From 5e470998a23e4c3d89ed24e8172cb22747e61efa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 26 Jun 2026 13:23:15 +0200 Subject: [PATCH 0001/3297] KVM: x86: Fix shadow paging use-after-free due to unexpected role commit 81ccda30b4e83d8f5cc4fd50503c44e3a33abfeb upstream. Commit 0cb2af2ea66ad ("KVM: x86: Fix shadow paging use-after-free due to unexpected GFN") fixed a shadow paging mismatch between stored and computed GFNs; the bug could be triggered by changing a PDE mapping from outside the guest, and then deleting a memslot. The rmap_remove() call would miss entries created after the PDE change because the GFN of the leaf SPTE does not match the GFN of the struct kvm_mmu_page. A similar hole however remains if the modified PDE points to a non-leaf page. In this case the gfn can be made to match, but the role does not match: the original large 2MB page creates a kvm_mmu_page with direct=1, while the new 4KB needs a kvm_mmu_page with direct=0. However, kvm_mmu_get_child_sp() does not compare the role, and therefore reuses the page. The next step is installing a leaf (4KB) SPTE on the new path which records an rmap entry under the gfn resolved by the walk. But when that child is zapped its parent kvm_mmu_page has direct=1 and kvm_mmu_page_get_gfn() computes the gfn for the 4KB page as sp->gfn + index instead of using sp->shadowed_translation[] (or sp->gfns[] in older kernels). It therefore fails to remove the recorded entry. When the memslot is dropped the shadow page is freed but the rmap entry survives, as in the scenario that was already fixed. Code that later walks that gfn (dirty logging, MMU notifier invalidation, and so on) dereferences an sptep that lies in the freed page, causing the use-after-free. Fixes: 2032a93d66fa ("KVM: MMU: Don't allocate gfns page for direct mmu pages") Reported-by: Hyunwoo Kim Signed-off-by: Paolo Bonzini Signed-off-by: Sasha Levin --- arch/x86/kvm/mmu/mmu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 0bd0cb8992c9f..541e199feb998 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2453,13 +2453,15 @@ static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu, u64 *sptep, gfn_t gfn, bool direct, unsigned int access) { - union kvm_mmu_page_role role; + union kvm_mmu_page_role role = kvm_mmu_child_role(sptep, direct, access); - if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep) && - spte_to_child_sp(*sptep) && spte_to_child_sp(*sptep)->gfn == gfn) + if (is_shadow_present_pte(*sptep) && + !is_large_pte(*sptep) && + spte_to_child_sp(*sptep) && + spte_to_child_sp(*sptep)->gfn == gfn && + spte_to_child_sp(*sptep)->role.word == role.word) return ERR_PTR(-EEXIST); - role = kvm_mmu_child_role(sptep, direct, access); return kvm_mmu_get_shadow_page(vcpu, gfn, role); } From 5dfcb15974e7d0f96aca278dd9f1b85df91523ef Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 26 Jun 2026 15:50:34 +0800 Subject: [PATCH 0002/3297] lsm: add backing_file LSM hooks [ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ] Stacked filesystems such as overlayfs do not currently provide the necessary mechanisms for LSMs to properly enforce access controls on the mmap() and mprotect() operations. In order to resolve this gap, a LSM security blob is being added to the backing_file struct and the following new LSM hooks are being created: security_backing_file_alloc() security_backing_file_free() security_mmap_backing_file() The first two hooks are to manage the lifecycle of the LSM security blob in the backing_file struct, while the third provides a new mmap() access control point for the underlying backing file. It is also expected that LSMs will likely want to update their security_file_mprotect() callback to address issues with their mprotect() controls, but that does not require a change to the security_file_mprotect() LSM hook. There are a three other small changes to support these new LSM hooks: * Pass the user file associated with a backing file down to alloc_empty_backing_file() so it can be included in the security_backing_file_alloc() hook. * Add getter and setter functions for the backing_file struct LSM blob as the backing_file struct remains private to fs/file_table.c. * Constify the file struct field in the LSM common_audit_data struct to better support LSMs that need to pass a const file struct pointer into the common LSM audit code. Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL() and supplying a fixup. Cc: stable@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org Cc: linux-unionfs@vger.kernel.org Cc: linux-erofs@lists.ozlabs.org Reviewed-by: Amir Goldstein Reviewed-by: Serge Hallyn Reviewed-by: Christian Brauner Signed-off-by: Paul Moore [Mainline declares lsm_backing_file_cache in security/lsm.h. Linux 6.18.y does not have security/lsm_init.c or security/lsm.h; the cache variable is defined locally as static struct kmem_cache *lsm_backing_file_cache in security/security.c.] Signed-off-by: Cai Xinchen Signed-off-by: Sasha Levin --- fs/backing-file.c | 17 ++++-- fs/file_table.c | 27 +++++++-- fs/fuse/passthrough.c | 2 +- fs/internal.h | 3 +- fs/overlayfs/dir.c | 2 +- fs/overlayfs/file.c | 2 +- include/linux/backing-file.h | 4 +- include/linux/fs.h | 13 ++++ include/linux/lsm_audit.h | 2 +- include/linux/lsm_hook_defs.h | 5 ++ include/linux/lsm_hooks.h | 1 + include/linux/security.h | 22 +++++++ security/security.c | 109 ++++++++++++++++++++++++++++++++++ 13 files changed, 194 insertions(+), 15 deletions(-) diff --git a/fs/backing-file.c b/fs/backing-file.c index 15a7f80310848..e049a627d78fb 100644 --- a/fs/backing-file.c +++ b/fs/backing-file.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "internal.h" @@ -29,14 +30,15 @@ * returned file into a container structure that also stores the stacked * file's path, which can be retrieved using backing_file_user_path(). */ -struct file *backing_file_open(const struct path *user_path, int flags, +struct file *backing_file_open(const struct file *user_file, int flags, const struct path *real_path, const struct cred *cred) { + const struct path *user_path = &user_file->f_path; struct file *f; int error; - f = alloc_empty_backing_file(flags, cred); + f = alloc_empty_backing_file(flags, cred, user_file); if (IS_ERR(f)) return f; @@ -52,15 +54,16 @@ struct file *backing_file_open(const struct path *user_path, int flags, } EXPORT_SYMBOL_GPL(backing_file_open); -struct file *backing_tmpfile_open(const struct path *user_path, int flags, +struct file *backing_tmpfile_open(const struct file *user_file, int flags, const struct path *real_parentpath, umode_t mode, const struct cred *cred) { struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt); + const struct path *user_path = &user_file->f_path; struct file *f; int error; - f = alloc_empty_backing_file(flags, cred); + f = alloc_empty_backing_file(flags, cred, user_file); if (IS_ERR(f)) return f; @@ -339,6 +342,12 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma, vma_set_file(vma, file); old_cred = override_creds(ctx->cred); + ret = security_mmap_backing_file(vma, file, user_file); + if (ret) { + revert_creds(old_cred); + return ret; + } + ret = vfs_mmap(vma->vm_file, vma); revert_creds(old_cred); diff --git a/fs/file_table.c b/fs/file_table.c index 762f03dcbcd77..987e01da99389 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -50,6 +50,9 @@ struct backing_file { struct path user_path; freeptr_t bf_freeptr; }; +#ifdef CONFIG_SECURITY + void *security; +#endif }; #define backing_file(f) container_of(f, struct backing_file, file) @@ -66,8 +69,21 @@ void backing_file_set_user_path(struct file *f, const struct path *path) } EXPORT_SYMBOL_GPL(backing_file_set_user_path); +#ifdef CONFIG_SECURITY +void *backing_file_security(const struct file *f) +{ + return backing_file(f)->security; +} + +void backing_file_set_security(struct file *f, void *security) +{ + backing_file(f)->security = security; +} +#endif /* CONFIG_SECURITY */ + static inline void backing_file_free(struct backing_file *ff) { + security_backing_file_free(&ff->file); path_put(&ff->user_path); kmem_cache_free(bfilp_cachep, ff); } @@ -288,10 +304,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) return f; } -static int init_backing_file(struct backing_file *ff) +static int init_backing_file(struct backing_file *ff, + const struct file *user_file) { memset(&ff->user_path, 0, sizeof(ff->user_path)); - return 0; + backing_file_set_security(&ff->file, NULL); + return security_backing_file_alloc(&ff->file, user_file); } /* @@ -301,7 +319,8 @@ static int init_backing_file(struct backing_file *ff) * This is only for kernel internal use, and the allocate file must not be * installed into file tables or such. */ -struct file *alloc_empty_backing_file(int flags, const struct cred *cred) +struct file *alloc_empty_backing_file(int flags, const struct cred *cred, + const struct file *user_file) { struct backing_file *ff; int error; @@ -318,7 +337,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) /* The f_mode flags must be set before fput(). */ ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; - error = init_backing_file(ff); + error = init_backing_file(ff, user_file); if (unlikely(error)) { fput(&ff->file); return ERR_PTR(error); diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 72de97c03d0ee..f2d08ac2459b7 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -167,7 +167,7 @@ struct fuse_backing *fuse_passthrough_open(struct file *file, int backing_id) goto out; /* Allocate backing file per fuse file to store fuse path */ - backing_file = backing_file_open(&file->f_path, file->f_flags, + backing_file = backing_file_open(file, file->f_flags, &fb->file->f_path, fb->cred); err = PTR_ERR(backing_file); if (IS_ERR(backing_file)) { diff --git a/fs/internal.h b/fs/internal.h index 9b2b4d1168802..51107fd515145 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -100,7 +100,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *); */ struct file *alloc_empty_file(int flags, const struct cred *cred); struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); -struct file *alloc_empty_backing_file(int flags, const struct cred *cred); +struct file *alloc_empty_backing_file(int flags, const struct cred *cred, + const struct file *user_file); void backing_file_set_user_path(struct file *f, const struct path *path); static inline void file_put_write_access(struct file *file) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index a5e9ddf3023b3..e924321b64025 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1355,7 +1355,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry, } ovl_path_upper(dentry->d_parent, &realparentpath); - realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath, + realfile = backing_tmpfile_open(file, flags, &realparentpath, mode, current_cred()); err = PTR_ERR_OR_ZERO(realfile); pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err); diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 7ab2c9daffd01..3fedfdddfa758 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -48,7 +48,7 @@ static struct file *ovl_open_realfile(const struct file *file, if (!inode_owner_or_capable(real_idmap, realinode)) flags &= ~O_NOATIME; - realfile = backing_file_open(file_user_path(file), + realfile = backing_file_open(file, flags, realpath, current_cred()); } ovl_revert_creds(old_cred); diff --git a/include/linux/backing-file.h b/include/linux/backing-file.h index 1476a6ed1bfd7..c939cd222730c 100644 --- a/include/linux/backing-file.h +++ b/include/linux/backing-file.h @@ -18,10 +18,10 @@ struct backing_file_ctx { void (*end_write)(struct kiocb *iocb, ssize_t); }; -struct file *backing_file_open(const struct path *user_path, int flags, +struct file *backing_file_open(const struct file *user_file, int flags, const struct path *real_path, const struct cred *cred); -struct file *backing_tmpfile_open(const struct path *user_path, int flags, +struct file *backing_tmpfile_open(const struct file *user_file, int flags, const struct path *real_parentpath, umode_t mode, const struct cred *cred); ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter, diff --git a/include/linux/fs.h b/include/linux/fs.h index 014cb04eefbe6..f3e798184a58e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2890,6 +2890,19 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode, const struct cred *cred); const struct path *backing_file_user_path(const struct file *f); +#ifdef CONFIG_SECURITY +void *backing_file_security(const struct file *f); +void backing_file_set_security(struct file *f, void *security); +#else +static inline void *backing_file_security(const struct file *f) +{ + return NULL; +} +static inline void backing_file_set_security(struct file *f, void *security) +{ +} +#endif /* CONFIG_SECURITY */ + /* * When mmapping a file on a stackable filesystem (e.g., overlayfs), the file * stored in ->vm_file is a backing file whose f_inode is on the underlying diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 382c56a97bba1..584db296e43b2 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -94,7 +94,7 @@ struct common_audit_data { #endif char *kmod_name; struct lsm_ioctlop_audit *op; - struct file *file; + const struct file *file; struct lsm_ibpkey_audit *ibpkey; struct lsm_ibendport_audit *ibendport; int reason; diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 8c42b4bde09c0..b4958167e3819 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -191,6 +191,9 @@ LSM_HOOK(int, 0, file_permission, struct file *file, int mask) LSM_HOOK(int, 0, file_alloc_security, struct file *file) LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file) LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) +LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file, + const struct file *user_file) +LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file) LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, unsigned long arg) LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, @@ -198,6 +201,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, LSM_HOOK(int, 0, mmap_addr, unsigned long addr) LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) +LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma, + struct file *backing_file, struct file *user_file) LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 79ec5a2bdcca7..ea4b0f5ca7f0f 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -104,6 +104,7 @@ struct security_hook_list { struct lsm_blob_sizes { int lbs_cred; int lbs_file; + int lbs_backing_file; int lbs_ib; int lbs_inode; int lbs_sock; diff --git a/include/linux/security.h b/include/linux/security.h index b64598e5d65d7..e540253624268 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -473,11 +473,17 @@ int security_file_permission(struct file *file, int mask); int security_file_alloc(struct file *file); void security_file_release(struct file *file); void security_file_free(struct file *file); +int security_backing_file_alloc(struct file *backing_file, + const struct file *user_file); +void security_backing_file_free(struct file *backing_file); int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int security_file_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg); int security_mmap_file(struct file *file, unsigned long prot, unsigned long flags); +int security_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file); int security_mmap_addr(unsigned long addr); int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot); @@ -1142,6 +1148,15 @@ static inline void security_file_release(struct file *file) static inline void security_file_free(struct file *file) { } +static inline int security_backing_file_alloc(struct file *backing_file, + const struct file *user_file) +{ + return 0; +} + +static inline void security_backing_file_free(struct file *backing_file) +{ } + static inline int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1161,6 +1176,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot, return 0; } +static inline int security_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file) +{ + return 0; +} + static inline int security_mmap_addr(unsigned long addr) { return cap_mmap_addr(addr); diff --git a/security/security.c b/security/security.c index 603c3c6d5635d..9285909908ab8 100644 --- a/security/security.c +++ b/security/security.c @@ -94,6 +94,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = { static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain); static struct kmem_cache *lsm_file_cache; +static struct kmem_cache *lsm_backing_file_cache; static struct kmem_cache *lsm_inode_cache; char *lsm_names; @@ -265,6 +266,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); + lsm_set_blob_size(&needed->lbs_backing_file, &blob_sizes.lbs_backing_file); lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib); /* * The inode blob gets an rcu_head in addition to @@ -470,6 +472,7 @@ static void __init ordered_lsm_init(void) init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); init_debug("file blob size = %d\n", blob_sizes.lbs_file); + init_debug("lsm_backing_file_cache = %d\n", blob_sizes.lbs_backing_file); init_debug("ib blob size = %d\n", blob_sizes.lbs_ib); init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); @@ -495,6 +498,11 @@ static void __init ordered_lsm_init(void) lsm_file_cache = kmem_cache_create("lsm_file_cache", blob_sizes.lbs_file, 0, SLAB_PANIC, NULL); + if (blob_sizes.lbs_backing_file) + lsm_backing_file_cache = kmem_cache_create( + "lsm_backing_file_cache", + blob_sizes.lbs_backing_file, + 0, SLAB_PANIC, NULL); if (blob_sizes.lbs_inode) lsm_inode_cache = kmem_cache_create("lsm_inode_cache", blob_sizes.lbs_inode, 0, @@ -671,6 +679,30 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_blocking_lsm_notifier); +/** + * lsm_backing_file_alloc - allocate a composite backing file blob + * @backing_file: the backing file + * + * Allocate the backing file blob for all the modules. + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +static int lsm_backing_file_alloc(struct file *backing_file) +{ + void *blob; + + if (!lsm_backing_file_cache) { + backing_file_set_security(backing_file, NULL); + return 0; + } + + blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL); + backing_file_set_security(backing_file, blob); + if (!blob) + return -ENOMEM; + return 0; +} + /** * lsm_blob_alloc - allocate a composite blob * @dest: the destination for the blob @@ -2965,6 +2997,57 @@ void security_file_free(struct file *file) } } +/** + * security_backing_file_alloc() - Allocate and setup a backing file blob + * @backing_file: the backing file + * @user_file: the associated user visible file + * + * Allocate a backing file LSM blob and perform any necessary initialization of + * the LSM blob. There will be some operations where the LSM will not have + * access to @user_file after this point, so any important state associated + * with @user_file that is important to the LSM should be captured in the + * backing file's LSM blob. + * + * LSM's should avoid taking a reference to @user_file in this hook as it will + * result in problems later when the system attempts to drop/put the file + * references due to a circular dependency. + * + * Return: Return 0 if the hook is successful, negative values otherwise. + */ +int security_backing_file_alloc(struct file *backing_file, + const struct file *user_file) +{ + int rc; + + rc = lsm_backing_file_alloc(backing_file); + if (rc) + return rc; + rc = call_int_hook(backing_file_alloc, backing_file, user_file); + if (unlikely(rc)) + security_backing_file_free(backing_file); + + return rc; +} + +/** + * security_backing_file_free() - Free a backing file blob + * @backing_file: the backing file + * + * Free any LSM state associate with a backing file's LSM blob, including the + * blob itself. + */ +void security_backing_file_free(struct file *backing_file) +{ + void *blob = backing_file_security(backing_file); + + call_void_hook(backing_file_free, backing_file); + + if (blob) { + backing_file_set_security(backing_file, NULL); + kmem_cache_free(lsm_backing_file_cache, blob); + } +} + /** * security_file_ioctl() - Check if an ioctl is allowed * @file: associated file @@ -3053,6 +3136,32 @@ int security_mmap_file(struct file *file, unsigned long prot, flags); } +/** + * security_mmap_backing_file - Check if mmap'ing a backing file is allowed + * @vma: the vm_area_struct for the mmap'd region + * @backing_file: the backing file being mmap'd + * @user_file: the user file being mmap'd + * + * Check permissions for a mmap operation on a stacked filesystem. This hook + * is called after the security_mmap_file() and is responsible for authorizing + * the mmap on @backing_file. It is important to note that the mmap operation + * on @user_file has already been authorized and the @vma->vm_file has been + * set to @backing_file. + * + * Return: Returns 0 if permission is granted. + */ +int security_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file) +{ + /* recommended by the stackable filesystem devs */ + if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING))) + return -EIO; + + return call_int_hook(mmap_backing_file, vma, backing_file, user_file); +} +EXPORT_SYMBOL_GPL(security_mmap_backing_file); + /** * security_mmap_addr() - Check if mmap'ing an address is allowed * @addr: address From d844702198395d3f80222777030f69db6be6b709 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 26 Jun 2026 15:50:35 +0800 Subject: [PATCH 0003/3297] selinux: fix overlayfs mmap() and mprotect() access checks [ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ] The existing SELinux security model for overlayfs is to allow access if the current task is able to access the top level file (the "user" file) and the mounter's credentials are sufficient to access the lower level file (the "backing" file). Unfortunately, the current code does not properly enforce these access controls for both mmap() and mprotect() operations on overlayfs filesystems. This patch makes use of the newly created security_mmap_backing_file() LSM hook to provide the missing backing file enforcement for mmap() operations, and leverages the backing file API and new LSM blob to provide the necessary information to properly enforce the mprotect() access controls. Cc: stable@vger.kernel.org Acked-by: Amir Goldstein Signed-off-by: Paul Moore Signed-off-by: Cai Xinchen Signed-off-by: Sasha Levin --- security/selinux/hooks.c | 242 ++++++++++++++++++++++-------- security/selinux/include/objsec.h | 11 ++ 2 files changed, 189 insertions(+), 64 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3da3017ad2ca0..f96ee8f372e3b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1739,49 +1739,72 @@ static inline int file_path_has_perm(const struct cred *cred, static int bpf_fd_pass(const struct file *file, u32 sid); #endif -/* Check whether a task can use an open file descriptor to - access an inode in a given way. Check access to the - descriptor itself, and then use dentry_has_perm to - check a particular permission to the file. - Access to the descriptor is implicitly granted if it - has the same SID as the process. If av is zero, then - access to the file is not checked, e.g. for cases - where only the descriptor is affected like seek. */ -static int file_has_perm(const struct cred *cred, - struct file *file, - u32 av) +static int __file_has_perm(const struct cred *cred, const struct file *file, + u32 av, bool bf_user_file) + { - struct file_security_struct *fsec = selinux_file(file); - struct inode *inode = file_inode(file); struct common_audit_data ad; - u32 sid = cred_sid(cred); + struct inode *inode; + u32 ssid = cred_sid(cred); + u32 tsid_fd; int rc; - ad.type = LSM_AUDIT_DATA_FILE; - ad.u.file = file; + if (bf_user_file) { + struct backing_file_security_struct *bfsec; + const struct path *path; - if (sid != fsec->sid) { - rc = avc_has_perm(sid, fsec->sid, - SECCLASS_FD, - FD__USE, - &ad); + if (WARN_ON(!(file->f_mode & FMODE_BACKING))) + return -EIO; + + bfsec = selinux_backing_file(file); + path = backing_file_user_path(file); + tsid_fd = bfsec->uf_sid; + inode = d_inode(path->dentry); + + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = *path; + } else { + struct file_security_struct *fsec = selinux_file(file); + + tsid_fd = fsec->sid; + inode = file_inode(file); + + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; + } + + if (ssid != tsid_fd) { + rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad); if (rc) - goto out; + return rc; } #ifdef CONFIG_BPF_SYSCALL - rc = bpf_fd_pass(file, cred_sid(cred)); + /* regardless of backing vs user file, use the underlying file here */ + rc = bpf_fd_pass(file, ssid); if (rc) return rc; #endif /* av is zero if only checking access to the descriptor. */ - rc = 0; if (av) - rc = inode_has_perm(cred, inode, av, &ad); + return inode_has_perm(cred, inode, av, &ad); -out: - return rc; + return 0; +} + +/* Check whether a task can use an open file descriptor to + access an inode in a given way. Check access to the + descriptor itself, and then use dentry_has_perm to + check a particular permission to the file. + Access to the descriptor is implicitly granted if it + has the same SID as the process. If av is zero, then + access to the file is not checked, e.g. for cases + where only the descriptor is affected like seek. */ +static inline int file_has_perm(const struct cred *cred, + const struct file *file, u32 av) +{ + return __file_has_perm(cred, file, av, false); } /* @@ -3799,6 +3822,17 @@ static int selinux_file_alloc_security(struct file *file) return 0; } +static int selinux_backing_file_alloc(struct file *backing_file, + const struct file *user_file) +{ + struct backing_file_security_struct *bfsec; + + bfsec = selinux_backing_file(backing_file); + bfsec->uf_sid = selinux_file(user_file)->sid; + + return 0; +} + /* * Check whether a task has the ioctl permission and cmd * operation to an inode. @@ -3916,42 +3950,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, static int default_noexec __ro_after_init; -static int file_map_prot_check(struct file *file, unsigned long prot, int shared) +static int __file_map_prot_check(const struct cred *cred, + const struct file *file, unsigned long prot, + bool shared, bool bf_user_file) { - const struct cred *cred = current_cred(); - u32 sid = cred_sid(cred); - int rc = 0; + struct inode *inode = NULL; + bool prot_exec = prot & PROT_EXEC; + bool prot_write = prot & PROT_WRITE; + + if (file) { + if (bf_user_file) + inode = d_inode(backing_file_user_path(file)->dentry); + else + inode = file_inode(file); + } + + if (default_noexec && prot_exec && + (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { + int rc; + u32 sid = cred_sid(cred); - if (default_noexec && - (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) || - (!shared && (prot & PROT_WRITE)))) { /* - * We are making executable an anonymous mapping or a - * private file mapping that will also be writable. - * This has an additional check. + * We are making executable an anonymous mapping or a private + * file mapping that will also be writable. */ - rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, - PROCESS__EXECMEM, NULL); + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM, + NULL); if (rc) - goto error; + return rc; } if (file) { - /* read access is always possible with a mapping */ + /* "read" always possible, "write" only if shared */ u32 av = FILE__READ; - - /* write access only matters if the mapping is shared */ - if (shared && (prot & PROT_WRITE)) + if (shared && prot_write) av |= FILE__WRITE; - - if (prot & PROT_EXEC) + if (prot_exec) av |= FILE__EXECUTE; - return file_has_perm(cred, file, av); + return __file_has_perm(cred, file, av, bf_user_file); } -error: - return rc; + return 0; +} + +static inline int file_map_prot_check(const struct cred *cred, + const struct file *file, + unsigned long prot, bool shared) +{ + return __file_map_prot_check(cred, file, prot, shared, false); } static int selinux_mmap_addr(unsigned long addr) @@ -3967,36 +4014,80 @@ static int selinux_mmap_addr(unsigned long addr) return rc; } -static int selinux_mmap_file(struct file *file, - unsigned long reqprot __always_unused, - unsigned long prot, unsigned long flags) +static int selinux_mmap_file_common(const struct cred *cred, struct file *file, + unsigned long prot, bool shared) { - struct common_audit_data ad; - int rc; - if (file) { + int rc; + struct common_audit_data ad; + ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; - rc = inode_has_perm(current_cred(), file_inode(file), - FILE__MAP, &ad); + rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad); if (rc) return rc; } - return file_map_prot_check(file, prot, - (flags & MAP_TYPE) == MAP_SHARED); + return file_map_prot_check(cred, file, prot, shared); +} + +static int selinux_mmap_file(struct file *file, + unsigned long reqprot __always_unused, + unsigned long prot, unsigned long flags) +{ + return selinux_mmap_file_common(current_cred(), file, prot, + (flags & MAP_TYPE) == MAP_SHARED); +} + +/** + * selinux_mmap_backing_file - Check mmap permissions on a backing file + * @vma: memory region + * @backing_file: stacked filesystem backing file + * @user_file: user visible file + * + * This is called after selinux_mmap_file() on stacked filesystems, and it + * is this function's responsibility to verify access to @backing_file and + * setup the SELinux state for possible later use in the mprotect() code path. + * + * By the time this function is called, mmap() access to @user_file has already + * been authorized and @vma->vm_file has been set to point to @backing_file. + * + * Return zero on success, negative values otherwise. + */ +static int selinux_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file __always_unused) +{ + unsigned long prot = 0; + + /* translate vma->vm_flags perms into PROT perms */ + if (vma->vm_flags & VM_READ) + prot |= PROT_READ; + if (vma->vm_flags & VM_WRITE) + prot |= PROT_WRITE; + if (vma->vm_flags & VM_EXEC) + prot |= PROT_EXEC; + + return selinux_mmap_file_common(backing_file->f_cred, backing_file, + prot, vma->vm_flags & VM_SHARED); } static int selinux_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot __always_unused, unsigned long prot) { + int rc; const struct cred *cred = current_cred(); u32 sid = cred_sid(cred); + const struct file *file = vma->vm_file; + bool backing_file; + bool shared = vma->vm_flags & VM_SHARED; + + /* check if we need to trigger the "backing files are awful" mode */ + backing_file = file && (file->f_mode & FMODE_BACKING); if (default_noexec && (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { - int rc = 0; /* * We don't use the vma_is_initial_heap() helper as it has * a history of problems and is currently broken on systems @@ -4010,11 +4101,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, vma->vm_end <= vma->vm_mm->brk) { rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECHEAP, NULL); - } else if (!vma->vm_file && (vma_is_initial_stack(vma) || + if (rc) + return rc; + } else if (!file && (vma_is_initial_stack(vma) || vma_is_stack_for_current(vma))) { rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECSTACK, NULL); - } else if (vma->vm_file && vma->anon_vma) { + if (rc) + return rc; + } else if (file && vma->anon_vma) { /* * We are making executable a file mapping that has * had some COW done. Since pages might have been @@ -4022,13 +4117,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, * modified content. This typically should only * occur for text relocations. */ - rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD); + rc = __file_has_perm(cred, file, FILE__EXECMOD, + backing_file); + if (rc) + return rc; + if (backing_file) { + rc = file_has_perm(file->f_cred, file, + FILE__EXECMOD); + if (rc) + return rc; + } } + } + + rc = __file_map_prot_check(cred, file, prot, shared, backing_file); + if (rc) + return rc; + if (backing_file) { + rc = file_map_prot_check(file->f_cred, file, prot, shared); if (rc) return rc; } - return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); + return 0; } static int selinux_file_lock(struct file *file, unsigned int cmd) @@ -7140,6 +7251,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = { .lbs_cred = sizeof(struct cred_security_struct), .lbs_task = sizeof(struct task_security_struct), .lbs_file = sizeof(struct file_security_struct), + .lbs_backing_file = sizeof(struct backing_file_security_struct), .lbs_inode = sizeof(struct inode_security_struct), .lbs_ipc = sizeof(struct ipc_security_struct), .lbs_key = sizeof(struct key_security_struct), @@ -7363,9 +7475,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), + LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc), LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), LSM_HOOK_INIT(mmap_file, selinux_mmap_file), + LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file), LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect), LSM_HOOK_INIT(file_lock, selinux_file_lock), diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 816fde5a5896c..fcb46793898f5 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -86,6 +86,10 @@ struct file_security_struct { u32 pseqno; /* Policy seqno at the time of file open */ }; +struct backing_file_security_struct { + u32 uf_sid; /* associated user file fsec->sid */ +}; + struct superblock_security_struct { u32 sid; /* SID of file system superblock */ u32 def_sid; /* default SID for labeling */ @@ -190,6 +194,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file) return file->f_security + selinux_blob_sizes.lbs_file; } +static inline struct backing_file_security_struct * +selinux_backing_file(const struct file *backing_file) +{ + void *blob = backing_file_security(backing_file); + return blob + selinux_blob_sizes.lbs_backing_file; +} + static inline struct inode_security_struct * selinux_inode(const struct inode *inode) { From 671ec2eabb874fcb593297c4dd885fc3dae54f32 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 27 Jun 2026 10:53:39 -0400 Subject: [PATCH 0004/3297] Revert "PCI: qcom: Advertise Hotplug Slot Capability with no Command Completion support" This reverts commit f176c47683bf6365e2f6d580d557fae49169a703. Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 43555ad9e5dcf..789cc0e3c10da 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -341,20 +341,15 @@ static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci) dw_pcie_dbi_ro_wr_dis(pci); } -static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci) +static void qcom_pcie_clear_hpc(struct dw_pcie *pci) { u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; dw_pcie_dbi_ro_wr_en(pci); - /* - * Qcom PCIe Root Ports do not support generating command completion - * notifications for the Hot-Plug commands. So set the NCCS field to - * avoid waiting for the completions. - */ val = readl(pci->dbi_base + offset + PCI_EXP_SLTCAP); - val |= PCI_EXP_SLTCAP_NCCS; + val &= ~PCI_EXP_SLTCAP_HPC; writel(val, pci->dbi_base + offset + PCI_EXP_SLTCAP); dw_pcie_dbi_ro_wr_dis(pci); @@ -554,7 +549,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) writel(CFG_BRIDGE_SB_INIT, pci->dbi_base + AXI_MSTR_RESP_COMP_CTRL1); - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_clear_hpc(pcie->pci); return 0; } @@ -634,7 +629,7 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie) writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT); } - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_clear_hpc(pcie->pci); return 0; } @@ -727,7 +722,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) val |= EN; writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_clear_hpc(pcie->pci); return 0; } @@ -1033,7 +1028,7 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) writel(WR_NO_SNOOP_OVERRIDE_EN | RD_NO_SNOOP_OVERRIDE_EN, pcie->parf + PARF_NO_SNOOP_OVERRIDE); - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_clear_hpc(pcie->pci); return 0; } From e055e74b80eb8858f98c736aa565173a917dcab5 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 26 Jun 2026 10:31:22 -0400 Subject: [PATCH 0005/3297] lockd: fix TEST handling when not all permissions are available. [ Upstream commit 0b474240327cebeff08ad429e8ed3cfc6c8ee816 ] The F_GETLK fcntl can work with either read access or write access or both. It can query F_RDLCK and F_WRLCK locks in either case. However lockd currently treats F_GETLK similar to F_SETLK in that read access is required to query an F_RDLCK lock and write access is required to query a F_WRLCK lock. This is wrong and can cause problems - e.g. when qemu accesses a read-only (e.g. iso) filesystem image over NFS (though why it queries if it can get a write lock - I don't know. But it does, and this works with local filesystems). So we need TEST requests to be handled differently. To do this: - change nlm_do_fopen() to accept O_RDWR as a mode and in that case succeed if either a O_RDONLY or O_WRONLY file can be opened. - change nlm_lookup_file() to accept a mode argument from caller, instead of deducing base on lock time, and pass that on to nlm_do_fopen() - change nlm4svc_retrieve_args() and nlmsvc_retrieve_args() to detect TEST requests and pass O_RDWR as a mode to nlm_lookup_file, passing the same mode as before for other requests. Also set lock->fl.c.flc_file to whichever file is available for TEST requests. - change nlmsvc_testlock() to also not calculate the mode, but to use whatever was stored in lock->fl.c.flc_file. This behaviour of lockd - requesting O_WRONLY access to TEST for exclusive locks - has been present at least since git history began. However it was hidden until recently because knfsd ignored the access requested by lockd and required only READ access for all locking requests (unless the underlying filesystem provided an f_op->open function which checked access permissions). The commit mentioned in Fixes: below changed nfsd_permission() to NOT override the access request for LOCK requests and this exposed the bug that we are now fixing. Note that there is another issue that this patch does not address. The flock(.., LOCK_EX) call is permitted on a read-only file descriptor. Linux NFS maps this to NLM locking as whole-file byte-range locks. nfsd will see this as though it were fcntl( F_SETLK (F_WRLCK)) and will now require write access, which it might not be able to get. It is not clear if this is a problem in practice, or what the best solution might be. So no attempt is made to address it. Reported-by: Tj Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1128861 Fixes: 4cc9b9f2bf4d ("nfsd: refine and rename NFSD_MAY_LOCK") Reviewed-by: Jeff Layton Signed-off-by: NeilBrown Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/lockd/svc4proc.c | 13 ++++++++++--- fs/lockd/svclock.c | 4 +--- fs/lockd/svcproc.c | 15 ++++++++++++--- fs/lockd/svcsubs.c | 35 +++++++++++++++++++++++++---------- include/linux/lockd/lockd.h | 2 +- 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 4b6f18d977343..75e020a8bfd07 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -26,6 +26,8 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, struct nlm_host *host = NULL; struct nlm_file *file = NULL; struct nlm_lock *lock = &argp->lock; + bool is_test = (rqstp->rq_proc == NLMPROC_TEST || + rqstp->rq_proc == NLMPROC_TEST_MSG); __be32 error = 0; /* nfsd callbacks must have been installed for this procedure */ @@ -46,15 +48,20 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, if (filp != NULL) { int mode = lock_to_openmode(&lock->fl); + if (is_test) + mode = O_RDWR; + lock->fl.c.flc_flags = FL_POSIX; - error = nlm_lookup_file(rqstp, &file, lock); + error = nlm_lookup_file(rqstp, &file, lock, mode); if (error) goto no_locks; *filp = file; - /* Set up the missing parts of the file_lock structure */ - lock->fl.c.flc_file = file->f_file[mode]; + if (is_test) + lock->fl.c.flc_file = nlmsvc_file_file(file); + else + lock->fl.c.flc_file = file->f_file[mode]; lock->fl.c.flc_pid = current->tgid; lock->fl.fl_start = (loff_t)lock->lock_start; lock->fl.fl_end = lock->lock_len ? diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index d66e828515999..c35ffa1b4b89d 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -611,7 +611,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_lock *conflock) { int error; - int mode; __be32 ret; dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", @@ -626,14 +625,13 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } - mode = lock_to_openmode(&lock->fl); locks_init_lock(&conflock->fl); /* vfs_test_lock only uses start, end, and owner, but tests flc_file */ conflock->fl.c.flc_file = lock->fl.c.flc_file; conflock->fl.fl_start = lock->fl.fl_start; conflock->fl.fl_end = lock->fl.fl_end; conflock->fl.c.flc_owner = lock->fl.c.flc_owner; - error = vfs_test_lock(file->f_file[mode], &conflock->fl); + error = vfs_test_lock(lock->fl.c.flc_file, &conflock->fl); if (error) { /* We can't currently deal with deferred test requests */ if (error == FILE_LOCK_DEFERRED) diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 5817ef272332d..d98e8d684376b 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -55,6 +55,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, struct nlm_host *host = NULL; struct nlm_file *file = NULL; struct nlm_lock *lock = &argp->lock; + bool is_test = (rqstp->rq_proc == NLMPROC_TEST || + rqstp->rq_proc == NLMPROC_TEST_MSG); int mode; __be32 error = 0; @@ -70,15 +72,22 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, /* Obtain file pointer. Not used by FREE_ALL call. */ if (filp != NULL) { - error = cast_status(nlm_lookup_file(rqstp, &file, lock)); + mode = lock_to_openmode(&lock->fl); + + if (is_test) + mode = O_RDWR; + + error = cast_status(nlm_lookup_file(rqstp, &file, lock, mode)); if (error != 0) goto no_locks; *filp = file; /* Set up the missing parts of the file_lock structure */ - mode = lock_to_openmode(&lock->fl); lock->fl.c.flc_flags = FL_POSIX; - lock->fl.c.flc_file = file->f_file[mode]; + if (is_test) + lock->fl.c.flc_file = nlmsvc_file_file(file); + else + lock->fl.c.flc_file = file->f_file[mode]; lock->fl.c.flc_pid = current->tgid; lock->fl.fl_lmops = &nlmsvc_lock_operations; nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 9103896164f68..7ea204eadfcac 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -82,18 +82,35 @@ int lock_to_openmode(struct file_lock *lock) * * We have to make sure we have the right credential to open * the file. + * + * mode can be O_RDONLY(0), O_WRONLY(1) or O_RDWR(2). The latter + * means success can be achieved with EITHER O_RDONLY or O_WRONLY. + * It does NOT mean both read and write are required. */ static __be32 nlm_do_fopen(struct svc_rqst *rqstp, struct nlm_file *file, int mode) { - struct file **fp = &file->f_file[mode]; - __be32 nfserr; + __be32 nfserr = nlm_lck_denied_nolocks; + __be32 deferred = 0; + struct file **fp; + int m; - if (*fp) - return 0; - nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode); - if (nfserr) - dprintk("lockd: open failed (error %d)\n", nfserr); + for (m = O_RDONLY ; m <= O_WRONLY ; m++) { + if (mode != O_RDWR && mode != m) + continue; + + fp = &file->f_file[m]; + if (*fp) + return 0; + nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m); + if (!nfserr) + return 0; + if (nfserr == nlm_drop_reply) + deferred = nfserr; + } + if (deferred) + return deferred; + dprintk("lockd: open failed (error %d)\n", ntohl(nfserr)); return nfserr; } @@ -103,17 +120,15 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp, */ __be32 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, - struct nlm_lock *lock) + struct nlm_lock *lock, int mode) { struct nlm_file *file; unsigned int hash; __be32 nfserr; - int mode; nlm_debug_print_fh("nlm_lookup_file", &lock->fh); hash = file_hash(&lock->fh); - mode = lock_to_openmode(&lock->fl); /* Lock file table */ mutex_lock(&nlm_file_mutex); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index c8f0f9458f2cc..d9930fc43ca54 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -293,7 +293,7 @@ void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t); * File handling for the server personality */ __be32 nlm_lookup_file(struct svc_rqst *, struct nlm_file **, - struct nlm_lock *); + struct nlm_lock *, int); void nlm_release_file(struct nlm_file *); void nlmsvc_put_lockowner(struct nlm_lockowner *); void nlmsvc_release_lockowner(struct nlm_lock *); From 1c5a1268418e8a2e5ab652a3adafac6c1e5e96b8 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:45 +0200 Subject: [PATCH 0006/3297] batman-adv: tp_meter: keep unacked list in ascending ordered commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. When batadv_tp_handle_out_of_order inserts a new entry in the list of unacked (out of order) packets, it searches from the entry with the newest sequence number towards oldest sequence number. If an entry is found which is older than the newly entry, the new entry has to be added after the found one to keep the ascending order. But for this operation list_add_tail() was used. But this function adds an entry _before_ another one. As result, the list would contain a lot of swapped sequence numbers. The consumer of this list (batadv_tp_ack_unordered()) would then fail to correctly ack packets. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index b1629e0ac8268..f222c5093b647 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, * one is attached _after_ it. In this way the list is kept in * ascending order */ - list_add_tail(&new->list, &un->list); + list_add(&new->list, &un->list); added = true; break; } From 696c4cae872cca59f51b1b5a0f8888d22ccb47a2 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:46 +0200 Subject: [PATCH 0007/3297] batman-adv: tp_meter: initialize dup_acks explicitly commit b2b68b32a715e0328662801576974aa37b942b00 upstream. When an ack with a sequence number equal to the last_acked is received, the dup_acks counter is increased to decide whether fast retransmit should be performed. Only when the sequence numbers are not equal, the dup_acks is set to the initial value (0). But if the initial packet would have the sequence number BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would operate on an undefined starting value. It is therefore required to have it explicitly initialized during the start of the sender session. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index f222c5093b647..fe9a447643074 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, tp_vars->icmp_uid = icmp_uid; tp_vars->last_sent = BATADV_TP_FIRST_SEQ; + atomic_set(&tp_vars->dup_acks, 0); atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ); tp_vars->fast_recovery = false; tp_vars->recover = BATADV_TP_FIRST_SEQ; From 7cb88d91d5f9f9a2842e0ec0622d01bdfaa0511a Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:47 +0200 Subject: [PATCH 0008/3297] batman-adv: tp_meter: initialize dec_cwnd explicitly commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd is only initialixed (to 0) when a duplicate Ack was received or when cwnd is below the ss_threshold. Just initialize the cwnd during the initialization to avoid any potential access of uninitialized data. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index fe9a447643074..473641d32dc68 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, * mesh_interface, hence its MTU */ tp_vars->cwnd = BATADV_TP_PLEN * 3; + tp_vars->dec_cwnd = 0; + /* at the beginning initialise the SS threshold to the biggest possible * window size, hence the AWND size */ From 8e77fe0414f5c7c956ea82615975d43eab018c25 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:48 +0200 Subject: [PATCH 0009/3297] batman-adv: tp_meter: avoid window underflow commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. In batadv_tp_avail(), win_left is calculated with 32-bit unsigned arithmetic: win_left = win_limit - tp_vars->last_sent; During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly shrunk win_limit is less than last_sent, the unsigned subtraction will underflow, wrapping to a massive positive value. Instead of returning that the window is full (unavailable), it returns that the sender can continue sending. To handle this situation, it must be checked whether the windows end sequence number (win_limit) has to be compared with the last sent sequence number. If it would be before the last sent sequence number, then more acks are needed before the transmission can be started again. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 473641d32dc68..71a4352cd78c7 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars, size_t payload_len) { + u32 last_sent = READ_ONCE(tp_vars->last_sent); u32 win_left, win_limit; win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd; - win_left = win_limit - tp_vars->last_sent; + + if (batadv_seq_before(last_sent, win_limit)) + win_left = win_limit - last_sent; + else + win_left = 0; return win_left >= payload_len; } From 7d2a44bc6bbe39aed03c68864aa0e54e04a50278 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:49 +0200 Subject: [PATCH 0010/3297] batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics. ((mss * 8) ** 2) / (cwnd * 8) In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around and be 0 - resulting in: ((mss * 8) ** 2) / 0 This is of course invalid and cannot be calculated. The calculation should must be simplified to avoid this overflow: (mss ** 2) * 8 / cwnd It will keep the precision enhancement from the scaling (by 8) but avoid the overflow in the divisor. In theory, there could still be an overflow in the dividend. It is at the moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an imminent problem. But allowing it to use the whole u32 bit range, would mean that it can still use up to 67 bits. To keep this calculation safe for 32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits - or in other words: must never be larger than 16383. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 71a4352cd78c7..00d8bb01611f9 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss) return; } + /* prevent overflow in (mss * mss) << 3 */ + mss = min_t(u32, mss, (1U << 14) - 1); + /* increment CWND at least of 1 (section 3.1 of RFC5681) */ tp_vars->dec_cwnd += max_t(u32, 1U << 3, - ((mss * mss) << 6) / (tp_vars->cwnd << 3)); + ((mss * mss) << 3) / tp_vars->cwnd); if (tp_vars->dec_cwnd < (mss << 3)) { spin_unlock_bh(&tp_vars->cwnd_lock); return; From 1db02f3e315da800720e2e14b4a9c8ffe14e8cbd Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:50 +0200 Subject: [PATCH 0011/3297] batman-adv: tp_meter: fix fast recovery precondition commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. The fast recovery precondition checks if the recover (initialized to BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is only updated when this check is successful, it will never enter the fast recovery mode. According to RFC6582 Section 3.2 step 2, the check should actually be different: > When the third duplicate ACK is received, the TCP sender first > checks the value of recover to see if the Cumulative > Acknowledgment field covers more than recover The precondition must therefore check if recover is smaller than the received ack - basically swapping the operands of the current check. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 00d8bb01611f9..a85622267ba65 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (atomic_read(&tp_vars->dup_acks) != 3) goto out; - if (recv_ack >= tp_vars->recover) + if (tp_vars->recover >= recv_ack) goto out; /* if this is the third duplicate ACK do Fast Retransmit */ From b8bf8400e50cbab595843241f63be3355deb1ca8 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:51 +0200 Subject: [PATCH 0012/3297] batman-adv: tp_meter: handle seqno wrap-around for fast recovery detection commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. The recover variable and the last_sent sequence number are initialized on purpose as a really high value which will wrap-around after the first 2000 bytes. The fast recovery precondition must therefore not use simple integer comparisons but use helpers which are aware of the sequence number wrap-arounds. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index a85622267ba65..0ebfc4462b8f1 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (atomic_read(&tp_vars->dup_acks) != 3) goto out; - if (tp_vars->recover >= recv_ack) + if (!batadv_seq_before(tp_vars->recover, recv_ack)) goto out; /* if this is the third duplicate ACK do Fast Retransmit */ From 23d085bd63086457ae82ab92e9e2d8ec895e2774 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:52 +0200 Subject: [PATCH 0013/3297] batman-adv: tp_meter: add only finished tp_vars to lists commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. When the receiver variables (aka "session") are initialized, then they are added to the list of sessions before the timer is set up. A RCU protected reader could therefore find the entry and run mod_setup before batadv_tp_init_recv() finished the timer initialization. The same is true for batadv_tp_start(), which must first initialize the finish_work and the test_length to avoid a similar problem. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 0ebfc4462b8f1..0444aa46b95c2 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, tp_vars->prerandom_offset = 0; spin_lock_init(&tp_vars->prerandom_lock); - kref_get(&tp_vars->refcount); - hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); - spin_unlock_bh(&bat_priv->tp_list_lock); - tp_vars->test_length = test_length; if (!tp_vars->test_length) tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH; + /* init work item for finished tp tests */ + INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + spin_unlock_bh(&bat_priv->tp_list_lock); + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: starting throughput meter towards %pM (length=%ums)\n", dst, test_length); - /* init work item for finished tp tests */ - INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); - /* start tp kthread. This way the write() call issued from userspace can * happily return and avoid to block */ @@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, INIT_LIST_HEAD(&tp_vars->unacked_list); kref_get(&tp_vars->refcount); - hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); kref_get(&tp_vars->refcount); - timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); batadv_tp_reset_receiver_timer(tp_vars); From 43733e5b525fbee8e60903b57d1a8cf6498e762d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:53 +0200 Subject: [PATCH 0014/3297] batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. The lasttime field for claim, backbone_gw, and loopdetect tracks the jiffies value of the most recent activity and is used to detect timeouts. These accesses are not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler optimizations. Cc: stable@kernel.org Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 3072f94275ac6..2c6e2b0d1ded4 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -513,7 +513,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, return NULL; entry->vid = vid; - entry->lasttime = jiffies; + WRITE_ONCE(entry->lasttime, jiffies); entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; spin_lock_init(&entry->crc_lock); @@ -581,7 +581,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, if (unlikely(!backbone_gw)) return; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_backbone_gw_put(backbone_gw); } @@ -715,7 +715,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, ether_addr_copy(claim->addr, mac); spin_lock_init(&claim->backbone_lock); claim->vid = vid; - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); kref_get(&backbone_gw->refcount); claim->backbone_gw = backbone_gw; kref_init(&claim->refcount); @@ -737,7 +737,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, return; } } else { - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); if (claim->backbone_gw == backbone_gw) /* no need to register a new backbone */ goto claim_free_ref; @@ -770,7 +770,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, spin_lock_bh(&backbone_gw->crc_lock); backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); spin_unlock_bh(&backbone_gw->crc_lock); - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); claim_free_ref: batadv_claim_put(claim); @@ -859,7 +859,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, return true; /* handle as ANNOUNCE frame */ - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); crc = ntohs(*((__force __be16 *)(&an_addr[4]))); batadv_dbg(BATADV_DBG_BLA, bat_priv, @@ -1254,7 +1254,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) head, hash_entry) { if (now) goto purge_now; - if (!batadv_has_timed_out(backbone_gw->lasttime, + if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime), BATADV_BLA_BACKBONE_TIMEOUT)) continue; @@ -1335,7 +1335,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, primary_if->net_dev->dev_addr)) goto skip; - if (!batadv_has_timed_out(claim->lasttime, + if (!batadv_has_timed_out(READ_ONCE(claim->lasttime), BATADV_BLA_CLAIM_TIMEOUT)) goto skip; @@ -1495,7 +1495,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) eth_random_addr(bat_priv->bla.loopdetect_addr); bat_priv->bla.loopdetect_addr[0] = 0xba; bat_priv->bla.loopdetect_addr[1] = 0xbe; - bat_priv->bla.loopdetect_lasttime = jiffies; + WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies); atomic_set(&bat_priv->bla.loopdetect_next, BATADV_BLA_LOOPDETECT_PERIODS); @@ -1516,7 +1516,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) primary_if->net_dev->dev_addr)) continue; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_bla_send_announce(bat_priv, backbone_gw); if (send_loopdetect) @@ -1934,7 +1934,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, /* If the packet came too late, don't forward it on the mesh * but don't consider that as loop. It might be a coincidence. */ - if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime, + if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime), BATADV_BLA_LOOPDETECT_TIMEOUT)) return true; @@ -2049,7 +2049,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, if (own_claim) { /* ... allow it in any case */ - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); goto allow; } @@ -2151,7 +2151,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, /* if yes, the client has roamed and we have * to unclaim it. */ - if (batadv_has_timed_out(claim->lasttime, 100)) { + if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) { /* only unclaim if the last claim entry is * older than 100 ms to make sure we really * have a roaming client here. @@ -2396,7 +2396,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, backbone_crc = backbone_gw->crc; spin_unlock_bh(&backbone_gw->crc_lock); - msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime); + msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime)); if (is_own) if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) { From 75612c100a9e2fe9df718056b03f544ec5b0623e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:54 +0200 Subject: [PATCH 0015/3297] batman-adv: prevent ELP transmission interval underflow commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts is randomly chosen between (elp_interval - BATADV_JITTER) and (elp_interval + BATADV_JITTER). The configured elp_interval must therefore be larger or equal to BATADV_JITTER to avoid that it causes an underflow of the unsigned integer. If this would happen, then a "fast" ELP interval would turn into a "day long" delay. At the same time, it must not be larger than the maximum value the variable can store. Cc: stable@kernel.org Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") [ Context ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/netlink.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 78c651f634cd3..1d144d8cc0928 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -917,9 +917,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, #ifdef CONFIG_BATMAN_ADV_BATMAN_V if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) { + u32 elp_interval; + attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; + elp_interval = nla_get_u32(attr); + + elp_interval = min_t(u32, elp_interval, INT_MAX); + elp_interval = max_t(u32, elp_interval, BATADV_JITTER); - atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr)); + atomic_set(&hard_iface->bat_v.elp_interval, elp_interval); } if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) { From 95a061f587b76a519ee17e3d406a37bc4eb63a50 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:55 +0200 Subject: [PATCH 0016/3297] batman-adv: tp_meter: initialize last_recv_time during init commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. The last_recv_time is the most important indicator for a receiver session to figure out whether a session timed out or not. But this information was only initialized after the session was added to the tp_receiver_list and after the timer was started. In the worst case, the timer (function) could have tried to access this information before the actual initialization was reached. Like rest of the variables of the tp_meter receiver session, this field has to be filled out before any other (parallel running) context has the chance to access it. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Context ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 0444aa46b95c2..10b8daca3a615 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, icmp->session, BATADV_TP_RECEIVER); - if (tp_vars) + if (tp_vars) { + tp_vars->last_recv_time = jiffies; goto out_unlock; + } if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, @@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, kref_get(&tp_vars->refcount); timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + tp_vars->last_recv_time = jiffies; + kref_get(&tp_vars->refcount); hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); @@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, icmp->orig); goto out; } - } - tp_vars->last_recv_time = jiffies; + tp_vars->last_recv_time = jiffies; + } /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK From 646b68639c06bc44593216fd7e9433d48d5e96eb Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:56 +0200 Subject: [PATCH 0017/3297] batman-adv: gw: don't deselect gateway with active hardif commit df97a7107b16375a10a36d7a63e9b4291a8ac680 upstream. The batadv_hardif_cnt() was previously checking if there is an batadv_hard_iface->mesh_iface which is has the same mesh_iface. And since batadv_hardif_disable_interface() was resetting the batadv_hard_iface->mesh_iface after this check, it had to verify whether *1* interface was still part of the mesh_iface before it started the gateway deselection. But after batadv_hardif_cnt() is now checking the lower interfaces of mesh_iface and batadv_hardif_disable_interface() already removed the interface via netdev_upper_dev_unlink() earlier in this function, the check must now make sure that *0* interfaces can be found by batadv_hardif_cnt() before selected gateway must be deselected. Otherwise the deselection would already happen one batadv_hard_iface too early. Because a 0 hardif count from batadv_hardif_cnt() is equal to an empty list, it is possible to replace the counting with a simple list_empty(). Cc: stable@kernel.org Fixes: 7dc284702bcd ("batman-adv: store hard_iface as iflink private data") Reviewed-by: Nora Schiffer Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/hard-interface.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 1c488049d5546..39b1ed813497d 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -786,30 +786,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, return ret; } -/** - * batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface - * @mesh_iface: mesh interface to check - * - * This function is only using RCU for locking - the result can therefore be - * off when another function is modifying the list at the same time. The - * caller can use the rtnl_lock to make sure that the count is accurate. - * - * Return: number of connected/enslaved hard interfaces - */ -static size_t batadv_hardif_cnt(struct net_device *mesh_iface) -{ - struct batadv_hard_iface *hard_iface; - struct list_head *iter; - size_t count = 0; - - rcu_read_lock(); - netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) - count++; - rcu_read_unlock(); - - return count; -} - /** * batadv_hardif_disable_interface() - Remove hard interface from mesh interface * @hard_iface: hard interface to be removed @@ -850,8 +826,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface) netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface); batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface); - /* nobody uses this interface anymore */ - if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1) + /* nobody uses this mesh interface anymore */ + if (list_empty(&hard_iface->mesh_iface->adj_list.lower)) batadv_gw_check_client_stop(bat_priv); hard_iface->mesh_iface = NULL; From 49bf27fcd7ee4cf643d766fc91fffbf2b0134363 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:57 +0200 Subject: [PATCH 0018/3297] batman-adv: ensure bcast is writable before modifying TTL commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. Before batman-adv is allowed to write to an skb, it either has to have its own copy of the skb or used skb_cow() to ensure that the data part is not shared. The old implementation used a shared queue and created copies before attempting to write to it. But with the new implementation, the broadcast packet is already modified when it gets received. Potentially writing to shared buffers in this process. Adding a skb_cow() right before this operation avoids this and can at the same time prepare it for the modifications required to rebroadcast the packet. Cc: stable@kernel.org Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/routing.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 12c16f81cc51d..0672dc30bed3b 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1191,6 +1191,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) goto free_skb; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto free_skb; + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + if (bcast_packet->ttl-- < 2) goto free_skb; From 0473ae882624ae309399c3fff10cedc6d0cffbfd Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:58 +0200 Subject: [PATCH 0019/3297] batman-adv: fix (m|b)cast csum after decrementing TTL commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. The broadcast and multicast packets can be received at the same time by the local system and forwarded to other nodes. Both are simply decrementing the TTL at the beginning of the receive path - independent of chosen paths (receive/forward). But such a modification of the data conflicts with the hw csum. This is not a problem when the packet is directly forwarded but can cause errors in the local receive path. Such a problem can then trigger a "hw csum failure". The receiver path must therefore ensure that the csum is fixed for each modification of the payload before batadv_interface_rx() is reached. Since all batman-adv packet types with a ttl have it as u8 at offset 2, a helper can be used for all of them. But it is only used at the moment for batadv_bcast_packet and batadv_mcast_packet because they are the only ones which deliver the packet locally but unconditionally modify the TTL. Cc: stable@kernel.org Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 0672dc30bed3b..cdcea90db6123 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb, return true; } +/** + * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe + * @skb: the received packet with @skb->data pointing to the batman-adv header + * + * Supports the following packet types, all of which carry the TTL at offset 2: + * + * - batadv_ogm_packet + * - batadv_ogm2_packet + * - batadv_icmp_header + * - batadv_icmp_packet + * - batadv_icmp_tp_packet + * - batadv_icmp_packet_rr + * - batadv_unicast_packet + * - batadv_frag_packet + * - batadv_bcast_packet + * - batadv_mcast_packet + * - batadv_coded_packet + * - batadv_unicast_tvlv_packet + * + * Return: true if the packet may be forwarded (ttl decremented), + * false if it must be dropped (ttl would expire) + */ +static bool batadv_skb_decrement_ttl(struct sk_buff *skb) +{ + static const size_t ttl_offset = 2; + u8 *ttl_pos; + + BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); + BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); + + ttl_pos = skb->data + ttl_offset; + + /* would expire on this hop -> drop, leave header + csum untouched */ + if (*ttl_pos < 2) + return false; + + skb_postpull_rcsum(skb, ttl_pos, 1); + (*ttl_pos)--; + skb_postpush_rcsum(skb, ttl_pos, 1); + + return true; +} + /** * batadv_recv_my_icmp_packet() - receive an icmp packet locally * @bat_priv: the bat priv with all the mesh interface information @@ -1197,7 +1251,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, bcast_packet = (struct batadv_bcast_packet *)skb->data; - if (bcast_packet->ttl-- < 2) + if (!batadv_skb_decrement_ttl(skb)) goto free_skb; orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); @@ -1304,7 +1358,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, goto free_skb; mcast_packet = (struct batadv_mcast_packet *)skb->data; - if (mcast_packet->ttl-- < 2) + if (!batadv_skb_decrement_ttl(skb)) goto free_skb; tvlv_buff = (unsigned char *)(skb->data + hdr_size); From 116e94025f0f48cbcfc167b6dc925551fad1b2bd Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:11:59 +0200 Subject: [PATCH 0020/3297] batman-adv: frag: ensure fragment is writable before modifying TTL commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. Before batman-adv is allowed to write to an skb, it either has to have its own copy of the skb or use skb_cow() to ensure that the data part is not shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. Adding a skb_cow() right before this operation avoids this and can at the same time prepare it for the modifications required to forward the fragment. Cc: stable@kernel.org Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/fragmentation.c | 15 ++++++++++++++- net/batman-adv/fragmentation.h | 3 ++- net/batman-adv/routing.c | 3 +-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 31395281692cb..ffa5d4b074095 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, * @skb: skb to forward * @recv_if: interface that the skb is received on * @orig_node_src: originator that the skb is received from + * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and + * NET_RX_DROP when it was dropped; only valid when true is returned * * Look up the next-hop of the fragments payload and check if the merged packet * will exceed the MTU towards the next-hop. If so, the fragment is forwarded @@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, */ bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_hard_iface *recv_if, - struct batadv_orig_node *orig_node_src) + struct batadv_orig_node *orig_node_src, + int *rx_result) { struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface); struct batadv_neigh_node *neigh_node = NULL; @@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, */ total_size = ntohs(packet->total_size); if (total_size > neigh_node->if_incoming->net_dev->mtu) { + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; + ret = true; + goto out; + } + + packet = (struct batadv_frag_packet *)skb->data; + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, skb->len + ETH_HLEN); packet->ttl--; batadv_send_unicast_skb(skb, neigh_node); + *rx_result = NET_RX_SUCCESS; ret = true; } diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h index dbf0871f87030..51e281027ab63 100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, bool (*check_cb)(struct batadv_frag_table_entry *)); bool batadv_frag_skb_fwd(struct sk_buff *skb, struct batadv_hard_iface *recv_if, - struct batadv_orig_node *orig_node_src); + struct batadv_orig_node *orig_node_src, + int *rx_result); bool batadv_frag_skb_buffer(struct sk_buff **skb, struct batadv_orig_node *orig_node); int batadv_frag_send_packet(struct sk_buff *skb, diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index cdcea90db6123..4483f8d9c7583 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1168,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, /* Route the fragment if it is not for us and too big to be merged. */ if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && - batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { + batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { /* skb was consumed */ skb = NULL; - ret = NET_RX_SUCCESS; goto put_orig_node; } From 44ae137a2aceff08cd226718ef077adca2638012 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:00 +0200 Subject: [PATCH 0021/3297] batman-adv: frag: avoid underflow of TTL commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. Packets with a TTL are using it to limit the amount of time this packet can be forwarded. But for batadv_frag_packet, the TTL was always only reduced but it was never evaluated. It could even underflow without any effect. Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for forwarding. This keeps it in sync with the not fragmented unicast packet. Cc: stable@kernel.org Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/fragmentation.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index ffa5d4b074095..4779741e7273e 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, */ total_size = ntohs(packet->total_size); if (total_size > neigh_node->if_incoming->net_dev->mtu) { + if (packet->ttl < 2) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; + ret = true; + goto out; + } + if (skb_cow(skb, ETH_HLEN) < 0) { kfree_skb(skb); *rx_result = NET_RX_DROP; From 3d4548c96d6f21ac1a9b06c5f82f3ef439c87023 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:01 +0200 Subject: [PATCH 0022/3297] batman-adv: v: prevent OGM aggregation on disabled hardif commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. When an interface gets disabled, the worker is correctly disabled by batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). In this process, the skb aggr_list is also freed. But batadv_v_ogm_send_meshif() can still queue new skbs (via batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all cores can no longer find the RCU protected list of hard interfaces. These queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. The batadv_v_ogm_iface_disable() function must block batadv_v_ogm_queue_on_if() to avoid leak of skbs. Cc: stable@kernel.org Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") [ Context ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/bat_v.c | 1 + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ net/batman-adv/types.h | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index de94447142642..17d2a1ccdce67 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) hard_iface->bat_v.aggr_len = 0; skb_queue_head_init(&hard_iface->bat_v.aggr_list); + hard_iface->bat_v.aggr_list_enabled = false; INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, batadv_v_ogm_aggr_work); } diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index d66ca77b1aaa3..6852bf5da8c55 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -252,11 +252,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, } spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + if (!hard_iface->bat_v.aggr_list_enabled) { + kfree_skb(skb); + goto unlock; + } + if (!batadv_v_ogm_queue_left(skb, 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); + +unlock: spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); } @@ -417,6 +424,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + hard_iface->bat_v.aggr_list_enabled = true; + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + batadv_v_ogm_start_queue_timer(hard_iface); batadv_v_ogm_start_timer(bat_priv); @@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + hard_iface->bat_v.aggr_list_enabled = false; batadv_v_ogm_aggr_list_free(hard_iface); spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index c9bd49d23547e..417d653021c7a 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { /** @aggr_list: queue for to be aggregated OGM packets */ struct sk_buff_head aggr_list; + /** + * @aggr_list_enabled: aggr_list is active and new skbs can be + * enqueued. Protected by aggr_list.lock after initialization + */ + bool aggr_list_enabled:1; + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ unsigned int aggr_len; From 2233787658db859f0a9b83cb397cf783bb8be865 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:02 +0200 Subject: [PATCH 0023/3297] batman-adv: tp_meter: restrict number of unacked list entries commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. When the unacked_list is unbound, an attacker could send messages with small lengths and appropriated seqno + gaps to force the receiver to allocate more and more unacked_list entries. And the end either causing an out-of-memory situation or increase the management overhead for the (large) list that significant portions of CPU cycles are wasted in searching through the list. When limiting the list to a specific number, it is important to still correctly add a new entry to the list. But if the list became larger than the limit, the last entry of the list (with the highest seqno) must be dropped to still allow the earlier seqnos to finish and therefore to continue the process. Otherwise, the process might get stuck with too high seqnos which are not handled by batadv_tp_ack_unordered(). Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Switch to pre-splitted tp_vars structure names ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- net/batman-adv/types.h | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 10b8daca3a615..e5387e8f33244 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -87,6 +87,11 @@ #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ sizeof(struct batadv_unicast_packet)) +/** + * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack + */ +#define BATADV_TP_MAX_UNACKED 100 + static u8 batadv_tp_prerandom[4096] __read_mostly; /** @@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { list_del(&un->list); kfree(un); + tp_vars->unacked_count--; } spin_unlock_bh(&tp_vars->unacked_lock); @@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->unacked_list)) { list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; goto out; } @@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, */ list_add(&new->list, &un->list); added = true; + tp_vars->unacked_count++; break; } /* received packet with smallest seqno out of order; add it to front */ - if (!added) + if (!added) { list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; + } + + /* remove the last (biggest) unacked seqno when list is too large */ + if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { + un = list_last_entry(&tp_vars->unacked_list, + struct batadv_tp_unacked, list); + list_del(&un->list); + kfree(un); + tp_vars->unacked_count--; + } out: spin_unlock_bh(&tp_vars->unacked_lock); @@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) list_del(&un->list); kfree(un); + tp_vars->unacked_count--; } spin_unlock_bh(&tp_vars->unacked_lock); } @@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, spin_lock_init(&tp_vars->unacked_lock); INIT_LIST_HEAD(&tp_vars->unacked_list); + tp_vars->unacked_count = 0; kref_get(&tp_vars->refcount); timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 417d653021c7a..8b180d8245b2a 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1426,6 +1426,9 @@ struct batadv_tp_vars { /** @unacked_lock: protect unacked_list */ spinlock_t unacked_lock; + /** @unacked_count: number of unacked entries */ + size_t unacked_count; + /** @last_recv_time: time (jiffies) a msg was received */ unsigned long last_recv_time; From 1dafdd0794be1a63d2c45f9a32e0a9cd89f68478 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:03 +0200 Subject: [PATCH 0024/3297] batman-adv: tp_meter: annotate last_recv_time access with READ/WRITE_ONCE commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. The last_recv_time field for batadv_tp_receiver tracks the jiffies value of the most recent activity and is used to detect timeouts. These accesses are not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler optimizations. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e5387e8f33244..e69bf10e66ac3 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) bat_priv = tp_vars->bat_priv; /* if there is recent activity rearm the timer */ - if (!batadv_has_timed_out(tp_vars->last_recv_time, + if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), BATADV_TP_RECV_TIMEOUT)) { /* reset the receiver shutdown timer */ batadv_tp_reset_receiver_timer(tp_vars); @@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, icmp->session, BATADV_TP_RECEIVER); if (tp_vars) { - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); goto out_unlock; } @@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, kref_get(&tp_vars->refcount); timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); kref_get(&tp_vars->refcount); hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); @@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, goto out; } - tp_vars->last_recv_time = jiffies; + WRITE_ONCE(tp_vars->last_recv_time, jiffies); } /* if the packet is a duplicate, it may be the case that an ACK has been From d511c72a83dd55adf90f318f4471285e605264f8 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:04 +0200 Subject: [PATCH 0025/3297] batman-adv: tp_meter: prevent parallel modifications of last_recv commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. When last_recv is updated to store the last receive sequence number, it is assuming that nothing is modifying in parallel while: * check for outdated packets is done * out of order check is performed (and packets are stored in out-of-order queue) * the out-of-order queue was searched for closed gaps * sequence number for next ack is calculated Nothing of that was actually protected. It could therefore happen that the last_recv was updated multiple times in parallel and the final sequence number was calculated with deltas which had no connection to the sequence number they were added to. Lock this whole region with the same lock which was already used to protect the unacked (out-of-order) list. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Switch to pre-splitted tp_vars structure names ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 22 +++++++++++++--------- net/batman-adv/types.h | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e69bf10e66ac3..629831ea9a58e 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, const struct sk_buff *skb) + __must_hold(&tp_vars->unacked_lock) { const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_unacked *un, *new; @@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, payload_len = skb->len - sizeof(struct batadv_unicast_packet); new->len = payload_len; - spin_lock_bh(&tp_vars->unacked_lock); /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->unacked_list)) { list_add(&new->list, &tp_vars->unacked_list); tp_vars->unacked_count++; - goto out; + return true; } /* otherwise loop over the list and either drop the packet because this @@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, tp_vars->unacked_count--; } -out: - spin_unlock_bh(&tp_vars->unacked_lock); - return true; } @@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, * @tp_vars: the private data of the current TP meter session */ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + __must_hold(&tp_vars->unacked_lock) { struct batadv_tp_unacked *un, *safe; u32 to_ack; @@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) /* go through the unacked packet list and possibly ACK them as * well */ - spin_lock_bh(&tp_vars->unacked_lock); list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { /* the list is ordered, therefore it is possible to stop as soon * there is a gap between the last acked seqno and the seqno of @@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) kfree(un); tp_vars->unacked_count--; } - spin_unlock_bh(&tp_vars->unacked_lock); } /** @@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_vars *tp_vars; size_t packet_size; + u32 to_ack; u32 seqno; icmp = (struct batadv_icmp_tp_packet *)skb->data; @@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, WRITE_ONCE(tp_vars->last_recv_time, jiffies); } + spin_lock_bh(&tp_vars->unacked_lock); + /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK */ @@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /* exit immediately (and do not send any ACK) if the packet has * not been enqueued correctly */ - if (!batadv_tp_handle_out_of_order(tp_vars, skb)) + if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { + spin_unlock_bh(&tp_vars->unacked_lock); goto out; + } /* send a duplicate ACK */ goto send_ack; @@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, batadv_tp_ack_unordered(tp_vars); send_ack: + to_ack = tp_vars->last_recv; + spin_unlock_bh(&tp_vars->unacked_lock); + /* send the ACK. If the received packet was out of order, the ACK that * is going to be sent is a duplicate (the sender will count them and * possibly enter Fast Retransmit as soon as it has reached 3) */ - batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, + batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, icmp->timestamp, icmp->session, icmp->uid); out: batadv_tp_vars_put(tp_vars); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 8b180d8245b2a..84de2570eac3e 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1423,7 +1423,7 @@ struct batadv_tp_vars { /** @unacked_list: list of unacked packets (meta-info only) */ struct list_head unacked_list; - /** @unacked_lock: protect unacked_list */ + /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ spinlock_t unacked_lock; /** @unacked_count: number of unacked entries */ From af5a069805f67957340cde27e349374be676e6d4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:05 +0200 Subject: [PATCH 0026/3297] batman-adv: tp_meter: handle overlapping packets commit cbde75c38b21f022891525078622587ad557b7c1 upstream. If the size of the packets would change during the transmission, it could happen that some retries of packets are overlapping. In this case, precise comparisons of sequence numbers by the receiver would be wrong. It is then necessary to check if the start sequence number to the end sequence number ("seqno + length") would contain a new range. If this is the case then this is enough to accept this packet. In all other cases, the packet still has to be dropped (and not acked). Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") [ Switch to pre-splitted tp_vars structure names ] Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tp_meter.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 629831ea9a58e..02af19aaaff29 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, /** * batadv_tp_handle_out_of_order() - store an out of order packet * @tp_vars: the private data of the current TP meter session - * @skb: the buffer containing the received packet + * @seqno: sequence number of new received packet + * @payload_len: length of the received packet * * Store the out of order packet in the unacked list for late processing. This * packets are kept in this list so that they can be ACKed at once as soon as @@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, * Return: true if the packed has been successfully processed, false otherwise */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, - const struct sk_buff *skb) + u32 seqno, u32 payload_len) __must_hold(&tp_vars->unacked_lock) { - const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_unacked *un, *new; - u32 payload_len; bool added = false; new = kmalloc(sizeof(*new), GFP_ATOMIC); if (unlikely(!new)) return false; - icmp = (struct batadv_icmp_tp_packet *)skb->data; - - new->seqno = ntohl(icmp->seqno); - payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->seqno = seqno; new->len = payload_len; /* if the list is empty immediately attach this new object */ @@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, { const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_vars *tp_vars; - size_t packet_size; + u32 payload_len; u32 to_ack; u32 seqno; @@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK */ - if (batadv_seq_before(seqno, tp_vars->last_recv)) + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + to_ack = seqno + payload_len; + if (batadv_seq_before(to_ack, tp_vars->last_recv)) goto send_ack; /* if the packet is out of order enqueue it */ - if (ntohl(icmp->seqno) != tp_vars->last_recv) { + if (batadv_seq_before(tp_vars->last_recv, seqno)) { /* exit immediately (and do not send any ACK) if the packet has * not been enqueued correctly */ - if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { + if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { spin_unlock_bh(&tp_vars->unacked_lock); goto out; } @@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, } /* if everything was fine count the ACKed bytes */ - packet_size = skb->len - sizeof(struct batadv_unicast_packet); - tp_vars->last_recv += packet_size; + tp_vars->last_recv = to_ack; /* check if this ordered message filled a gap.... */ batadv_tp_ack_unordered(tp_vars); From 3470d583fc652c40db2bfd350e981e0e4716e189 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:06 +0200 Subject: [PATCH 0027/3297] batman-adv: tt: don't merge change entries with different VIDs commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. batadv_tt_local_event() merges/cancels events for the same client which would conflict or be duplicates. The matching of the queued events only compares the MAC address - the VLAN ID stored in each event is ignored. If a MAC would now appear on multiple VID, the two ADD change events (for VID 1 and VID 2) would be merged to a single vid event. The remote can therefore not calculate the correct TT table and desync. A full translation table exchange is required to recover from this state. A check of VID is therefore necessary to avoid such wrong merges/cancels. Cc: stable@kernel.org Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/translation-table.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 9f6e67771ffa8..acd8af4446671 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -446,6 +446,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, if (!batadv_compare_eth(entry->change.addr, common->addr)) continue; + if (entry->change.vid != tt_change_node->change.vid) + continue; + del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL; if (del_op_requested != del_op_entry) { /* DEL+ADD in the same orig interval have no effect and From 3f82fc92cf523a007a423c21b04613acf32aeaa3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:07 +0200 Subject: [PATCH 0028/3297] batman-adv: tt: track roam count per VID commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. But TT entries are for a MAC + VID. The VID was completely missed and thus leads to incorrect detection of ROAM counts when a client MAC exists in multiple VLANs. Cc: stable@kernel.org Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/translation-table.c | 9 +++++++-- net/batman-adv/types.h | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index acd8af4446671..83dfd804a143a 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -3442,6 +3442,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) * batadv_tt_check_roam_count() - check if a client has roamed too frequently * @bat_priv: the bat priv with all the mesh interface information * @client: mac address of the roaming client + * @vid: VLAN identifier * * This function checks whether the client already reached the * maximum number of possible roaming phases. In this case the ROAMING_ADV @@ -3449,7 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) * * Return: true if the ROAMING_ADV can be sent, false otherwise */ -static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) +static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) { struct batadv_tt_roam_node *tt_roam_node; bool ret = false; @@ -3462,6 +3463,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) if (!batadv_compare_eth(tt_roam_node->addr, client)) continue; + if (tt_roam_node->vid != vid) + continue; + if (batadv_has_timed_out(tt_roam_node->first_time, BATADV_ROAMING_MAX_TIME)) continue; @@ -3483,6 +3487,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) atomic_set(&tt_roam_node->counter, BATADV_ROAMING_MAX_COUNT - 1); ether_addr_copy(tt_roam_node->addr, client); + tt_roam_node->vid = vid; list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); ret = true; @@ -3519,7 +3524,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, /* before going on we have to check whether the client has * already roamed to us too many times */ - if (!batadv_tt_check_roam_count(bat_priv, client)) + if (!batadv_tt_check_roam_count(bat_priv, client, vid)) goto out; batadv_dbg(BATADV_DBG_TT, bat_priv, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 84de2570eac3e..ef712ba4fff2c 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1912,6 +1912,9 @@ struct batadv_tt_roam_node { /** @addr: mac address of the client in the roaming phase */ u8 addr[ETH_ALEN]; + /** @vid: VLAN identifier */ + u16 vid; + /** * @counter: number of allowed roaming events per client within a single * OGM interval (changes are committed with each OGM) From 04e1a6557fbf8ad9385563ae79c5437cb5bf94c3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:08 +0200 Subject: [PATCH 0029/3297] batman-adv: dat: prevent false sharing between VLANs commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. The local hash of DAT entries is supposed to be VLAN (VID) aware. But the adding to the hash and the search in the hash were not checking the VID information of the hash entries. The entries would therefore only be correctly separated when batadv_hash_dat() didn't select the same buckets for different VIDs. Cc: stable@kernel.org Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/distributed-arp-table.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 031c295fff1b1..860db505e8697 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -214,10 +214,13 @@ static void batadv_dat_purge(struct work_struct *work) */ static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) { - const void *data1 = container_of(node, struct batadv_dat_entry, - hash_entry); + const struct batadv_dat_entry *entry1; + const struct batadv_dat_entry *entry2; - return memcmp(data1, data2, sizeof(__be32)) == 0; + entry1 = container_of(node, struct batadv_dat_entry, hash_entry); + entry2 = data2; + + return entry1->ip == entry2->ip && entry1->vid == entry2->vid; } /** @@ -344,6 +347,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, if (dat_entry->ip != ip) continue; + if (dat_entry->vid != vid) + continue; + if (!kref_get_unless_zero(&dat_entry->refcount)) continue; From 4cc9f7711bb898f7af34eef26f615e9465ae250c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:09 +0200 Subject: [PATCH 0030/3297] batman-adv: tvlv: enforce 2-byte alignment commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte alignment, so a following OGM must start at an even offset. As the header length is even, an odd tvlv_len would misalign it and trigger unaligned accesses on strict-alignment architectures. Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in the mesh. Therefore, reject such malformed packets. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- net/batman-adv/bat_v_ogm.c | 11 ++++++++++- net/batman-adv/routing.c | 6 ++++++ net/batman-adv/tvlv.c | 6 ++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index b8b1b997960a9..6e79f69c2fede 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, const struct batadv_ogm_packet *ogm_packet) { int next_buff_pos = 0; + u16 tvlv_len; /* check if there is enough space for the header */ next_buff_pos += buff_pos + sizeof(*ogm_packet); if (next_buff_pos > packet_len) return false; + tvlv_len = ntohs(ogm_packet->tvlv_len); + + /* the fields of an aggregated OGM are accessed assuming (at least) + * 2-byte alignment, so a following OGM must start at an even offset. + */ + if (tvlv_len & 1) + return false; + /* check if there is enough space for the optional TVLV */ - next_buff_pos += ntohs(ogm_packet->tvlv_len); + next_buff_pos += tvlv_len; return next_buff_pos <= packet_len; } diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 6852bf5da8c55..1f9b2d2b4831c 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -849,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, const struct batadv_ogm2_packet *ogm2_packet) { int next_buff_pos = 0; + u16 tvlv_len; /* check if there is enough space for the header */ next_buff_pos += buff_pos + sizeof(*ogm2_packet); if (next_buff_pos > packet_len) return false; + tvlv_len = ntohs(ogm2_packet->tvlv_len); + + /* the fields of an aggregated OGMv2 are accessed assuming (at least) + * 2-byte alignment, so a following OGMv2 must start at an even offset. + */ + if (tvlv_len & 1) + return false; + /* check if there is enough space for the optional TVLV */ - next_buff_pos += ntohs(ogm2_packet->tvlv_len); + next_buff_pos += tvlv_len; return next_buff_pos <= packet_len; } diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 4483f8d9c7583..41951c7a1c50b 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1366,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, if (tvlv_buff_len > skb->len - hdr_size) goto free_skb; + /* the fields of an multicast payload are accessed assuming (at least) + * 2-byte alignment, so a following packet must start at an even offset. + */ + if (tvlv_buff_len & 1) + goto free_skb; + ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb, tvlv_buff, tvlv_buff_len); if (ret >= 0) { diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index cde798c82dcf1..63fb54024d154 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (tvlv_value_cont_len > tvlv_value_len) break; + /* the next tvlv header is accessed assuming (at least) 2-byte + * alignment, so it must start at an even offset. + */ + if (tvlv_value_cont_len & 1) + break; + tvlv_handler = batadv_tvlv_handler_get(bat_priv, tvlv_hdr->type, tvlv_hdr->version); From 565ab66005b14e4d40f2ef7d36cc6baaf9725fb2 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 26 Jun 2026 18:12:10 +0200 Subject: [PATCH 0031/3297] batman-adv: tvlv: avoid race of cifsnotfound handler state commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to signal that the OGM handler should be called (with NULL for data) when the specific TVLV container was not found in the OGM. This is used by: * DAT * GW * Multicast (OGM + Tracker) The state whether the handler was executed was stored in the struct batadv_tvlv_handler. But the TVLV processing is started without any lock. Multiple parallel contexts processing TVLVs would therefore overwrite each others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared batadv_tvlv_handler. Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, per TVLV buffer, whether a matching container was present by scanning the packet's buffer. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Signed-off-by: Sven Eckelmann Signed-off-by: Sasha Levin --- net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- net/batman-adv/types.h | 7 ----- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 63fb54024d154..a91f1891747c0 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, tvlv_handler->ogm_handler(bat_priv, orig_node, BATADV_NO_FLAGS, tvlv_value, tvlv_value_len); - tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; break; case BATADV_UNICAST_TVLV: if (!skb) @@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, return NET_RX_SUCCESS; } +/** + * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + * @type: tvlv container type to look for + * @version: tvlv container version to look for + * + * Return: true if a container of the given type and version is present in the + * tvlv buffer, false otherwise. + */ +static bool batadv_tvlv_containers_contain(void *tvlv_value, + u16 tvlv_value_len, u8 type, + u8 version) +{ + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + + while (tvlv_value_len >= sizeof(*tvlv_hdr)) { + tvlv_hdr = tvlv_value; + tvlv_value_cont_len = ntohs(tvlv_hdr->len); + tvlv_value = tvlv_hdr + 1; + tvlv_value_len -= sizeof(*tvlv_hdr); + + if (tvlv_value_cont_len > tvlv_value_len) + break; + + /* the next tvlv header is accessed assuming (at least) 2-byte + * alignment, so it must start at an even offset. + */ + if (tvlv_value_cont_len & 1) + break; + + if (tvlv_hdr->type == type && tvlv_hdr->version == version) + return true; + + tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; + tvlv_value_len -= tvlv_value_cont_len; + } + + return false; +} + /** * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the * appropriate handlers @@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, struct sk_buff *skb, void *tvlv_value, u16 tvlv_value_len) { + u16 tvlv_value_start_len = tvlv_value_len; struct batadv_tvlv_handler *tvlv_handler; + void *tvlv_value_start = tvlv_value; struct batadv_tvlv_hdr *tvlv_hdr; u16 tvlv_value_cont_len; u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; @@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (!tvlv_handler->ogm_handler) continue; - if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && - !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) - tvlv_handler->ogm_handler(bat_priv, orig_node, - cifnotfound, NULL, 0); + if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) + continue; - tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; + /* if the corresponding container was present then the handler + * was already called from the loop above + */ + if (batadv_tvlv_containers_contain(tvlv_value_start, + tvlv_value_start_len, + tvlv_handler->type, + tvlv_handler->version)) + continue; + + tvlv_handler->ogm_handler(bat_priv, orig_node, + cifnotfound, NULL, 0); } rcu_read_unlock(); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index ef712ba4fff2c..ac4494f1b8e2a 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -2245,13 +2245,6 @@ enum batadv_tvlv_handler_flags { * will call this handler even if its type was not found (with no data) */ BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), - - /** - * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the - * API marks a handler as being called, so it won't be called if the - * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set - */ - BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), }; #endif /* _NET_BATMAN_ADV_TYPES_H_ */ From 6374fb9edf72c67a118a2c214a0dddd04c921e0a Mon Sep 17 00:00:00 2001 From: Wongi Lee Date: Tue, 16 Jun 2026 22:46:17 +0900 Subject: [PATCH 0032/3297] ipv6: account for fraggap on the paged allocation path commit 736b380e28d0480c7bc3e022f1950f31fe53a7c5 upstream. In __ip6_append_data(), when the paged-allocation branch is taken (MSG_MORE / NETIF_F_SG / large fraglen), alloclen and pagedlen are computed as alloclen = fragheaderlen + transhdrlen; pagedlen = datalen - transhdrlen; datalen already includes fraggap (datalen = length + fraggap). When fraggap is non-zero, this is not the first skb and transhdrlen is zero. The fraggap bytes carried over from the previous skb are copied just past the fragment headers in the new skb's linear area. The linear area is therefore undersized by fraggap bytes while pagedlen is overstated by the same amount, and the copy writes past skb->end into the trailing skb_shared_info. An unprivileged user can trigger this via a UDPv6 socket using MSG_MORE together with MSG_SPLICE_PAGES. The bad accounting was introduced by commit 773ba4fe9104 ("ipv6: avoid partial copy for zc"). Before commit ce650a166335 ("udp6: Fix __ip6_append_data()'s handling of MSG_SPLICE_PAGES"), the negative copy value caused -EINVAL to be returned. That later commit allowed MSG_SPLICE_PAGES to proceed in this case, making the corruption triggerable. The non-paged branch sets alloclen to fraglen, which already accounts for fraggap because datalen does. Bring the paged branch in line by adding fraggap to alloclen and subtracting it from pagedlen. After this adjustment, copy no longer collapses to -fraggap on the paged path, so remove the stale comment describing that old arithmetic. Since a negative copy is no longer expected for a valid MSG_SPLICE_PAGES case, remove the MSG_SPLICE_PAGES exception from the negative copy check. Fixes: 773ba4fe9104 ("ipv6: avoid partial copy for zc") Signed-off-by: Jungwoo Lee Signed-off-by: Wongi Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/ajFTqRljatR17fFy@DESKTOP-19IMU7U.localdomain Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_output.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index f5ca0267e7706..8f37c9cc868b0 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1648,8 +1648,8 @@ static int __ip6_append_data(struct sock *sk, !(rt->dst.dev->features & NETIF_F_SG))) alloclen = fraglen; else { - alloclen = fragheaderlen + transhdrlen; - pagedlen = datalen - transhdrlen; + alloclen = fragheaderlen + transhdrlen + fraggap; + pagedlen = datalen - transhdrlen - fraggap; } alloclen += alloc_extra; @@ -1664,10 +1664,7 @@ static int __ip6_append_data(struct sock *sk, fraglen = datalen + fragheaderlen; copy = datalen - transhdrlen - fraggap - pagedlen; - /* [!] NOTE: copy may be negative if pagedlen>0 - * because then the equation may reduces to -fraggap. - */ - if (copy < 0 && !(flags & MSG_SPLICE_PAGES)) { + if (copy < 0) { err = -EINVAL; goto error; } From 77798d7be6ef71e72fb6fc8a2901bf74ebc9706f Mon Sep 17 00:00:00 2001 From: Wongi Lee Date: Tue, 16 Jun 2026 22:38:29 +0900 Subject: [PATCH 0033/3297] ipv4: account for fraggap on the paged allocation path [ Upstream commit eca856950f7cb1a221e02b99d758409f2c5cec42 ] In __ip_append_data(), when the paged-allocation branch is taken, alloclen and pagedlen are computed as alloclen = fragheaderlen + transhdrlen; pagedlen = datalen - transhdrlen; datalen already includes fraggap, but the fraggap bytes carried over from the previous skb are copied into the new skb's linear area at offset transhdrlen by the subsequent skb_copy_and_csum_bits(). The linear area is therefore undersized by fraggap bytes while pagedlen is overstated by the same amount. The non-paged branch sets alloclen to fraglen, which already accounts for fraggap because datalen does. Bring the paged branch in line by adding fraggap to alloclen and subtracting it from pagedlen. After this adjustment, copy no longer collapses to -fraggap on the paged path, so remove the stale comment describing that old arithmetic. Fixes: 8eb77cc73977 ("ipv4: avoid partial copy for zc") Signed-off-by: Jungwoo Lee Signed-off-by: Wongi Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/ajFR1eLAIs42TN3g@DESKTOP-19IMU7U.localdomain Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/ip_output.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 7c005263262ff..7eaf35a6e24ba 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1117,8 +1117,8 @@ static int __ip_append_data(struct sock *sk, !(rt->dst.dev->features & NETIF_F_SG))) alloclen = fraglen; else { - alloclen = fragheaderlen + transhdrlen; - pagedlen = datalen - transhdrlen; + alloclen = fragheaderlen + transhdrlen + fraggap; + pagedlen = datalen - transhdrlen - fraggap; } alloclen += alloc_extra; @@ -1165,9 +1165,6 @@ static int __ip_append_data(struct sock *sk, } copy = datalen - transhdrlen - fraggap - pagedlen; - /* [!] NOTE: copy will be negative if pagedlen>0 - * because then the equation reduces to -fraggap. - */ if (copy > 0 && INDIRECT_CALL_1(getfrag, ip_generic_getfrag, from, data + transhdrlen, offset, From 5e658b9245a52d838ef93729a7bc07de8e19deb7 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Wed, 10 Jun 2026 12:31:01 +0200 Subject: [PATCH 0034/3297] ntfs3: reject direct userspace writes to reserved $LX* xattrs commit 5b08dccecf825cbf905f348bc6ccb497507e28e2 upstream. NTFS3 uses $LXUID, $LXGID, $LXMOD and $LXDEV as internal WSL permission metadata and reloads them into i_uid, i_gid and i_mode from ntfs_get_wsl_perm(). Because the empty-prefix xattr handler also lets file owners call setxattr() on these names directly, an unprivileged writer on a writable ntfs3 mount can plant root ownership and S_ISUID on their own file and gain euid 0 after inode reload. Reject direct userspace writes to the reserved $LX* names. Internal ntfs3 metadata updates are unchanged because ntfs_save_wsl_perm() writes them via ntfs_set_ea() directly. Signed-off-by: Zhen Yan [almaz.alexandrovich@paragon-software.com: added an additional check for non privileged users] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/xattr.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 142ecb3847e59..d719e1073dbb1 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -845,6 +845,12 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, return err; } +static bool ntfs_is_reserved_lxattr(const char *name) +{ + return !strcmp(name, "$LXUID") || !strcmp(name, "$LXGID") || + !strcmp(name, "$LXMOD") || !strcmp(name, "$LXDEV"); +} + /* * ntfs_setxattr - inode_operations::setxattr */ @@ -949,6 +955,12 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, goto out; } + /* Do not allow non privileged users to change $LXUID/$LXGID... */ + if (ntfs_is_reserved_lxattr(name) && !capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto out; + } + /* Deal with NTFS extended attribute. */ err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, 0, NULL); From 3c499851753a24d2e148d4e9ca51764c0c51554e Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Thu, 28 May 2026 11:38:14 +0800 Subject: [PATCH 0035/3297] wifi: mt76: add wcid publish check in mt76_sta_add commit 20b126920a259df4d7dcae19fcfe2c57a74d6b2e upstream. Since mt7925_mac_sta_add publishes wcid, add publish check in mt76_sta_add to avoid reinitializing the wcid->poll_list. Found dev->sta_poll_list corruption when using mt7925 and 7.1-rc4. According to the corruption information, prev->next was changed to itself. wlan0: disconnect from AP 90:fb:5d:94:8b:e3 for new auth to 90:fb:5d:94:8b:e2 wlan0: authenticate with 90:fb:5d:94:8b:e2 (local address=84:9e:56:9c:7e:6b) wlan0: send auth to 90:fb:5d:94:8b:e2 (try 1/3) slab kmalloc-8k start ffff8c80958a6000 pointer offset 4160 size 8192 list_add corruption. prev->next should be next (ffff8c808a7488f8), but was ffff8c80958a7040. (prev=ffff8c80958a7040). mt76_wcid_add_poll+0x95/0xd0 [mt76] mt7925_mac_add_txs.part.0+0xa5/0xe0 [mt7925_common] mt7925_rx_check+0xa7/0xc0 [mt7925_common] mt76_dma_rx_poll+0x50d/0x790 [mt76] mt792x_poll_rx+0x52/0xe0 [mt792x_lib] Signed-off-by: Jiajia Liu Link: https://patch.msgid.link/20260528033814.46418-1-liujiajia@kylinos.cn Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mac80211.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 76eb740428b8d..2d24f9fa51a50 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1568,6 +1568,7 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, { struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; struct mt76_dev *dev = phy->dev; + struct mt76_wcid *published; int ret; int i; @@ -1587,11 +1588,19 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, mtxq->wcid = wcid->idx; } - ewma_signal_init(&wcid->rssi); - rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + published = rcu_dereference_protected(dev->wcid[wcid->idx], + lockdep_is_held(&dev->mutex)); + if (published != wcid) { + WARN_ON_ONCE(published); + ewma_signal_init(&wcid->rssi); + rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + mt76_wcid_init(wcid, phy->band_idx); + } else { + wcid->phy_idx = phy->band_idx; + } + phy->num_sta++; - mt76_wcid_init(wcid, phy->band_idx); out: mutex_unlock(&dev->mutex); From 0cfa78c050662784fc8e3ab26dbfd1dc632b2082 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 1 Jul 2026 09:53:06 +0300 Subject: [PATCH 0036/3297] af_unix: Set gc_in_progress to true in unix_gc(). [ Upstream commit d82ba05263c69fa2437fe93e4e561cc40f4c03af ] Igor Ushakov reported that unix_gc() could run with gc_in_progress being false if the work is scheduled while running: Thread 1 Thread 2 Thread 3 -------- -------- -------- unix_schedule_gc() unix_schedule_gc() `- if (!gc_in_progress) `- if (!gc_in_progress) |- gc_in_progress = true | `- queue_work() | unix_gc() <----------------/ | | |- gc_in_progress = true ... `- queue_work() | | `- gc_in_progress = false | | unix_gc() <---------------------------------------------' | ... /* gc_in_progress == false */ | `- gc_in_progress = false unix_peek_fpl() relies on gc_in_progress not to confuse GC by MSG_PEEK. Let's set gc_in_progress to true in unix_gc(). Fixes: 8b90a9f819dc ("af_unix: Run GC on only one CPU.") Reported-by: Igor Ushakov Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260501073945.1884564-1-kuniyu@google.com Signed-off-by: Jakub Kicinski [ Add setting gc_in_progress in __unix_gc(). Keep the existing set in unix_gc() for wait_for_unix_gc() over-limit throttling. ] Signed-off-by: Igor Ushakov Signed-off-by: Sasha Levin --- net/unix/garbage.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 529b21d043d92..3986717090266 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -606,6 +606,8 @@ static void __unix_gc(struct work_struct *work) struct sk_buff_head hitlist; struct sk_buff *skb; + WRITE_ONCE(gc_in_progress, true); + spin_lock(&unix_gc_lock); if (unix_graph_state == UNIX_GRAPH_NOT_CYCLIC) { From bd968bdd568beacfdf98ec537a87527e85f1d0cf Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Tue, 26 May 2026 20:37:26 +0200 Subject: [PATCH 0037/3297] mac802154: llsec: add skb_cow_data() before in-place crypto commit 84a04eb5b210643bd67aab81ff805d32f62aa865 upstream. llsec_do_encrypt_unauth(), llsec_do_encrypt_auth(), llsec_do_decrypt_unauth(), and llsec_do_decrypt_auth() all perform in-place cryptographic transformations on skb data. They build a scatterlist with sg_init_one() pointing into the skb's linear data area and then pass the same scatterlist as both src and dst to the crypto API (e.g. crypto_skcipher_encrypt/decrypt, crypto_aead_encrypt/decrypt). On the RX path, __ieee802154_rx_handle_packet() clones the received skb before handing it to each subscriber via ieee802154_subif_frame(). The cloned skb shares the same underlying data buffer via reference counting. When llsec_do_decrypt() subsequently modifies this shared buffer in place, it corrupts data that other clones -- potentially belonging to other sockets or subsystems -- still reference. On the TX path, similar data sharing can occur when an skb's head has been cloned (skb_cloned() returns true). The fix is to call skb_cow_data() before performing any in-place crypto operation. skb_cow_data() ensures that the skb's data area is not shared: if the skb head is cloned or the data spans multiple fragments, it copies the data into a private buffer that can be safely modified in place. This is the same pattern used by: - ESP (net/ipv4/esp4.c, net/ipv6/esp6.c) - MACsec (drivers/net/macsec.c) - WireGuard (drivers/net/wireguard/receive.c) - TIPC (net/tipc/crypto.c) Without this guard, in-place crypto on shared skb data leads to: - Silent data corruption of other skb clones - Use-after-free when the crypto API scatterwalk writes through a page that has already been freed by another clone's kfree_skb() - Kernel crashes under concurrent 802.15.4 traffic with security enabled (KASAN/KMSAN reports slab-use-after-free) Found by 0sec (https://0sec.ai) using automated source analysis. Fixes: 4c14a2fb5d14 ("mac802154: add llsec decryption method") Fixes: 03556e4d0dbb ("mac802154: add llsec encryption method") Cc: stable@vger.kernel.org Reported-by: Doruk Tan Ozturk Closes: https://lore.kernel.org/linux-wpan/20260525161806.96158-1-doruk@0sec.ai/ Reviewed-by: Alexander Lobakin Signed-off-by: Doruk Tan Ozturk Closes: Link: https://lore.kernel.org/20260526183726.56100-1-doruk@0sec.ai Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- net/mac802154/llsec.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index f13b07ebfb98a..09a47104b5772 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -710,6 +710,7 @@ int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) { struct ieee802154_hdr hdr; int rc, authlen, hlen; + struct sk_buff *trailer; struct mac802154_llsec_key *key; u32 frame_ctr; @@ -769,6 +770,12 @@ int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) skb->mac_len = ieee802154_hdr_push(skb, &hdr); skb_reset_mac_header(skb); + rc = skb_cow_data(skb, 0, &trailer); + if (rc < 0) { + llsec_key_put(key); + return rc; + } + rc = llsec_do_encrypt(skb, sec, &hdr, key); llsec_key_put(key); @@ -908,6 +915,13 @@ llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec, const struct ieee802154_hdr *hdr, struct mac802154_llsec_key *key, __le64 dev_addr) { + struct sk_buff *trailer; + int err; + + err = skb_cow_data(skb, 0, &trailer); + if (err < 0) + return err; + if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr); else From 1acdd14c0990dd1cd4b6534f00366d2e6dfce05f Mon Sep 17 00:00:00 2001 From: Yiming Qian Date: Wed, 10 Jun 2026 06:21:36 +0000 Subject: [PATCH 0038/3297] net: skmsg: preserve sg.copy across SG transforms commit 406e8a651a7b854c41fecd5117bb282b3a6c2c6b upstream. The sk_msg sg.copy bitmap is part of the scatterlist entry ownership state. A set bit tells sk_msg_compute_data_pointers() not to expose the entry through writable BPF ctx->data. This protects entries backed by pages that are not private to the sk_msg, such as splice-backed file page-cache pages. Several sk_msg transform paths move, copy, split, or compact msg->sg.data[] entries without moving the matching sg.copy bit. This can make an externally backed entry arrive at a new slot with a clear copy bit. A later SK_MSG verdict can then expose sg_virt(sge) as writable ctx->data and BPF stores can modify the original page cache. Keep sg.copy synchronized with sg.data[] whenever entries are transferred, shifted, split, or copied into a new sk_msg. Clear the bit when an entry is replaced by a newly allocated private page or freed. This covers the BPF pull/push/pop helpers, sk_msg_shift_left/right(), sk_msg_xfer(), and tls_split_open_record(), including the partial tail entry created during TLS open-record splitting. Fixes: d3b18ad31f93 ("tls: add bpf support to sk_msg handling") Cc: stable@vger.kernel.org Reported-by: Yiming Qian Reported-by: Keenan Dong Signed-off-by: Yiming Qian Link: https://patch.msgid.link/20260610062137.49075-1-yimingqian591@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/linux/skmsg.h | 15 +++++++++++---- net/core/filter.c | 27 +++++++++++++++++++++++++++ net/core/skmsg.c | 2 ++ net/tls/tls_sw.c | 4 ++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 829b281d6c9c2..4ee7e4680a6e1 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -4,6 +4,7 @@ #ifndef _LINUX_SKMSG_H #define _LINUX_SKMSG_H +#include #include #include #include @@ -199,11 +200,14 @@ static inline void sk_msg_xfer(struct sk_msg *dst, struct sk_msg *src, int which, u32 size) { dst->sg.data[which] = src->sg.data[which]; + __assign_bit(which, dst->sg.copy, test_bit(which, src->sg.copy)); dst->sg.data[which].length = size; dst->sg.size += size; src->sg.size -= size; src->sg.data[which].length -= size; src->sg.data[which].offset += size; + if (!src->sg.data[which].length) + __clear_bit(which, src->sg.copy); } static inline void sk_msg_xfer_full(struct sk_msg *dst, struct sk_msg *src) @@ -273,16 +277,19 @@ static inline void sk_msg_page_add(struct sk_msg *msg, struct page *page, static inline void sk_msg_sg_copy(struct sk_msg *msg, u32 i, bool copy_state) { do { - if (copy_state) - __set_bit(i, msg->sg.copy); - else - __clear_bit(i, msg->sg.copy); + __assign_bit(i, msg->sg.copy, copy_state); sk_msg_iter_var_next(i); if (i == msg->sg.end) break; } while (1); } +static inline void sk_msg_sg_copy_assign(struct sk_msg *dst, u32 dst_i, + const struct sk_msg *src, u32 src_i) +{ + __assign_bit(dst_i, dst->sg.copy, test_bit(src_i, src->sg.copy)); +} + static inline void sk_msg_sg_copy_set(struct sk_msg *msg, u32 start) { sk_msg_sg_copy(msg, start, true); diff --git a/net/core/filter.c b/net/core/filter.c index 0b61945491054..6dd9bdbef1996 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2732,11 +2732,13 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, poffset += len; sge->length = 0; put_page(sg_page(sge)); + __clear_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); } while (i != last_sge); sg_set_page(&msg->sg.data[first_sge], page, copy, 0); + __clear_bit(first_sge, msg->sg.copy); /* To repair sg ring we need to shift entries. If we only * had a single entry though we can just replace it and @@ -2762,9 +2764,11 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, break; msg->sg.data[i] = msg->sg.data[move_from]; + sk_msg_sg_copy_assign(msg, i, msg, move_from); msg->sg.data[move_from].length = 0; msg->sg.data[move_from].page_link = 0; msg->sg.data[move_from].offset = 0; + __clear_bit(move_from, msg->sg.copy); sk_msg_iter_var_next(i); } while (1); @@ -2793,6 +2797,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, { struct scatterlist sge, nsge, nnsge, rsge = {0}, *psge; u32 new, i = 0, l = 0, space, copy = 0, offset = 0; + bool sge_copy, nsge_copy, nnsge_copy, rsge_copy = false; u8 *raw, *to, *from; struct page *page; @@ -2865,6 +2870,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, sk_msg_iter_var_prev(i); psge = sk_msg_elem(msg, i); rsge = sk_msg_elem_cpy(msg, i); + rsge_copy = test_bit(i, msg->sg.copy); psge->length = start - offset; rsge.length -= psge->length; @@ -2889,24 +2895,32 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, /* Shift one or two slots as needed */ sge = sk_msg_elem_cpy(msg, new); + sge_copy = test_bit(new, msg->sg.copy); sg_unmark_end(&sge); nsge = sk_msg_elem_cpy(msg, i); + nsge_copy = test_bit(i, msg->sg.copy); if (rsge.length) { sk_msg_iter_var_next(i); nnsge = sk_msg_elem_cpy(msg, i); + nnsge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_next(msg, end); } while (i != msg->sg.end) { msg->sg.data[i] = sge; + __assign_bit(i, msg->sg.copy, sge_copy); sge = nsge; + sge_copy = nsge_copy; sk_msg_iter_var_next(i); if (rsge.length) { nsge = nnsge; + nsge_copy = nnsge_copy; nnsge = sk_msg_elem_cpy(msg, i); + nnsge_copy = test_bit(i, msg->sg.copy); } else { nsge = sk_msg_elem_cpy(msg, i); + nsge_copy = test_bit(i, msg->sg.copy); } } @@ -2920,6 +2934,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, get_page(sg_page(&rsge)); sk_msg_iter_var_next(new); msg->sg.data[new] = rsge; + __assign_bit(new, msg->sg.copy, rsge_copy); } sk_msg_reset_curr(msg); @@ -2947,25 +2962,33 @@ static void sk_msg_shift_left(struct sk_msg *msg, int i) prev = i; sk_msg_iter_var_next(i); msg->sg.data[prev] = msg->sg.data[i]; + sk_msg_sg_copy_assign(msg, prev, msg, i); } while (i != msg->sg.end); sk_msg_iter_prev(msg, end); + __clear_bit(msg->sg.end, msg->sg.copy); } static void sk_msg_shift_right(struct sk_msg *msg, int i) { struct scatterlist tmp, sge; + bool tmp_copy, sge_copy; sk_msg_iter_next(msg, end); sge = sk_msg_elem_cpy(msg, i); + sge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); tmp = sk_msg_elem_cpy(msg, i); + tmp_copy = test_bit(i, msg->sg.copy); while (i != msg->sg.end) { msg->sg.data[i] = sge; + __assign_bit(i, msg->sg.copy, sge_copy); sk_msg_iter_var_next(i); sge = tmp; + sge_copy = tmp_copy; tmp = sk_msg_elem_cpy(msg, i); + tmp_copy = test_bit(i, msg->sg.copy); } } @@ -3025,6 +3048,8 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, struct scatterlist *nsge, *sge = sk_msg_elem(msg, i); int a = start - offset; int b = sge->length - pop - a; + u32 sge_i = i; + bool sge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); @@ -3037,6 +3062,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, sg_set_page(nsge, sg_page(sge), b, sge->offset + pop + a); + __assign_bit(i, msg->sg.copy, sge_copy); } else { struct page *page, *orig; u8 *to, *from; @@ -3053,6 +3079,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, memcpy(to, from, a); memcpy(to + a, from + a + pop, b); sg_set_page(sge, page, a + b, 0); + __clear_bit(sge_i, msg->sg.copy); put_page(orig); } pop = 0; diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 75ea4fdb27642..c1c315ae2b22b 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -66,6 +66,7 @@ int sk_msg_alloc(struct sock *sk, struct sk_msg *msg, int len, sge = &msg->sg.data[msg->sg.end]; sg_unmark_end(sge); sg_set_page(sge, pfrag->page, use, orig_offset); + __clear_bit(msg->sg.end, msg->sg.copy); get_page(pfrag->page); sk_msg_iter_next(msg, end); } @@ -186,6 +187,7 @@ static int sk_msg_free_elem(struct sock *sk, struct sk_msg *msg, u32 i, sk_mem_uncharge(sk, len); put_page(sg_page(sge)); } + __clear_bit(i, msg->sg.copy); memset(sge, 0, sizeof(*sge)); return len; } diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 034f322054e53..9949ae0270811 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -623,6 +623,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, struct scatterlist *sge, *osge, *nsge; u32 orig_size = msg_opl->sg.size; struct scatterlist tmp = { }; + u32 tmp_i = 0; struct sk_msg *msg_npl; struct tls_rec *new; int ret; @@ -644,6 +645,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, if (sge->length > apply) { u32 len = sge->length - apply; + tmp_i = i; get_page(sg_page(sge)); sg_set_page(&tmp, sg_page(sge), len, sge->offset + apply); @@ -675,6 +677,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, nsge = sk_msg_elem(msg_npl, j); if (tmp.length) { memcpy(nsge, &tmp, sizeof(*nsge)); + sk_msg_sg_copy_assign(msg_npl, j, msg_opl, tmp_i); sk_msg_iter_var_next(j); nsge = sk_msg_elem(msg_npl, j); } @@ -682,6 +685,7 @@ static int tls_split_open_record(struct sock *sk, struct tls_rec *from, osge = sk_msg_elem(msg_opl, i); while (osge->length) { memcpy(nsge, osge, sizeof(*nsge)); + sk_msg_sg_copy_assign(msg_npl, j, msg_opl, i); sg_unmark_end(nsge); sk_msg_iter_var_next(i); sk_msg_iter_var_next(j); From 1697957eb0971d420dde42862b88eb43506a1105 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:35 +0800 Subject: [PATCH 0039/3297] net: ip_gre: require CAP_NET_ADMIN in the device netns for changelink commit 8165f7ff57d9667d2bb477ef6af83ede7fed4ad7 upstream. A tunnel changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Add rtnl_dev_link_net_capable() next to rtnl_get_net_ns_capable() in net/core/rtnetlink.c. It requires CAP_NET_ADMIN in the link netns and is skipped when the link netns is dev_net(dev), where the rtnl path already checked it. The other patches in this series use the same helper. Gate ipgre_changelink() and erspan_changelink() with it, at the top of the op before any attribute is parsed, because the parsers update live tunnel fields first. ipgre_netlink_parms() sets t->collect_md before ip_tunnel_changelink() runs. Commit 8b484efd5cb4 ("ip6: vti: Use ip6_tnl.net in vti6_siocdevprivate().") added the same check on the ioctl path. This adds it on RTM_NEWLINK. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: b57708add314 ("gre: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-2-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/net/rtnetlink.h | 2 ++ net/core/rtnetlink.c | 8 ++++++++ net/ipv4/ip_gre.c | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index ec65a8cebb992..2bff41aacc987 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -256,6 +256,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer, struct netlink_ext_ack *exterr); struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid); +bool rtnl_dev_link_net_capable(const struct net_device *dev, + const struct net *link_net); #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 0a43c3881e3f8..4909f20ff4d63 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2414,6 +2414,14 @@ struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid) } EXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable); +bool rtnl_dev_link_net_capable(const struct net_device *dev, + const struct net *link_net) +{ + return net_eq(link_net, dev_net(dev)) || + ns_capable(link_net->user_ns, CAP_NET_ADMIN); +} +EXPORT_SYMBOL_GPL(rtnl_dev_link_net_capable); + static int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh, bool strict_check, struct nlattr **tb, struct netlink_ext_ack *extack) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 35f0baa99d409..879d37c557faf 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1456,6 +1456,9 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + err = ipgre_newlink_encap_setup(dev, data); if (err) return err; @@ -1485,6 +1488,9 @@ static int erspan_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + err = ipgre_newlink_encap_setup(dev, data); if (err) return err; From 4a69b83045d3195d5b9a9b053ad840ddb2998b4e Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 15:57:38 -0500 Subject: [PATCH 0040/3297] apparmor: mediate the implicit connect of TCP fast open sendmsg commit 4d587cd8a72155089a627130bbd4716ec0856e21 upstream. sendmsg()/sendto() with MSG_FASTOPEN is a combination of connect(2) and write(2): it opens the connection in the SYN. apparmor_socket_sendmsg() only checks AA_MAY_SEND, so a profile that grants send but denies connect lets a confined task open an outbound TCP/MPTCP connection that connect(2) would have refused, bypassing connect mediation. Mediate the implicit connect when MSG_FASTOPEN is set and a destination is supplied. Add it to apparmor_socket_sendmsg() (not the shared aa_sock_msg_perm() helper, which recvmsg also uses) and call aa_sk_perm() directly, mirroring the selinux and tomoyo fixes. sk_is_tcp() does not cover MPTCP fast open, so the SOCK_STREAM/IPPROTO_MPTCP arm is explicit. Fixes: cf60af03ca4e ("net-tcp: Fast Open client - sendmsg(MSG_FASTOPEN)") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: John Johansen Signed-off-by: Greg Kroah-Hartman --- security/apparmor/lsm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3a4ef7bd3b5d0..e47696a7eacf8 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1423,7 +1423,21 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, static int apparmor_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); + int error = aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); + + if (error) + return error; + + /* TCP fast open carries connect() semantics in sendmsg(); mediate + * the implicit connect so it cannot bypass the connect permission. + */ + if ((msg->msg_flags & MSG_FASTOPEN) && msg->msg_name && + (sk_is_tcp(sock->sk) || + (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM && + sock->sk->sk_protocol == IPPROTO_MPTCP))) + error = aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); + + return error; } static int apparmor_socket_recvmsg(struct socket *sock, From c3ca2631073b2cef06824fd2bfc452ff7a1023de Mon Sep 17 00:00:00 2001 From: Ruslan Valiyev Date: Tue, 26 May 2026 00:04:46 +0200 Subject: [PATCH 0041/3297] apparmor: fix use-after-free in rawdata dedup loop commit 6f060496d03e4dc560a40f73770bd08335cb7a27 upstream. aa_replace_profiles() walks ns->rawdata_list to dedup the incoming policy blob against entries already attached to existing profiles. Per the kernel-doc on struct aa_loaddata, list membership does not hold a reference: profiles hold pcount, and when the last pcount drops, do_ploaddata_rmfs() is queued on a workqueue that takes ns->lock and removes the entry. Between dropping the last pcount and the workqueue running, an entry remains on the list with pcount == 0. aa_get_profile_loaddata() is an unconditional kref_get() on pcount, so when the dedup loop hits such an entry, refcount hardening reports refcount_t: addition on 0; use-after-free. inside aa_replace_profiles(), and the poisoned counter then trips "saturated" and "underflow" warnings on the subsequent uses of the same loaddata. Before commit a0b7091c4de4 ("apparmor: fix race on rawdata dereference") the dedup path used a get_unless_zero-style helper on a single counter, so the existing "if (tmp)" guard was meaningful. The split-refcount refactor introduced aa_get_profile_loaddata(), which has plain kref_get() semantics, and the guard quietly became a no-op. Introduce aa_get_profile_loaddata_not0(), matching the existing _not0 convention used by aa_get_profile_not0(), and use it for the rawdata_list dedup lookup so dying entries are skipped. Reproduced on x86_64 with v7.1-rc5 in QEMU+KVM running Ubuntu 24.04 + stress-ng 0.17.06: stress-ng --apparmor 1 --klog-check --timeout 60s Without this patch the three refcount_t warnings fire within a few seconds. With it the same 60 s run is clean. Coverage is a smoke-test only; a longer soak with CONFIG_KASAN, CONFIG_KCSAN and CONFIG_PROVE_LOCKING would be welcome from anyone with the cycles. Fixes: a0b7091c4de4 ("apparmor: fix race on rawdata dereference") Reported-by: Colin Ian King Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221513 Cc: stable@vger.kernel.org Signed-off-by: Ruslan Valiyev Signed-off-by: John Johansen Signed-off-by: Greg Kroah-Hartman --- security/apparmor/include/policy_unpack.h | 19 +++++++++++++++++++ security/apparmor/policy.c | 8 ++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index e5a95dc4da1f7..b9de0fdf9ee52 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -163,6 +163,25 @@ aa_get_profile_loaddata(struct aa_loaddata *data) return data; } +/** + * aa_get_profile_loaddata_not0 - get a profile reference count if not zero + * @data: reference to get a count on + * + * Like aa_get_profile_loaddata(), but safe to call on an entry that may + * be on a list (e.g. ns->rawdata_list) where the last pcount has already + * dropped and the deferred cleanup has not yet run. + * + * Returns: pointer to reference, or %NULL if @data is NULL or its + * profile refcount has already reached zero. + */ +static inline struct aa_loaddata * +aa_get_profile_loaddata_not0(struct aa_loaddata *data) +{ + if (data && kref_get_unless_zero(&data->pcount)) + return data; + return NULL; +} + void __aa_loaddata_update(struct aa_loaddata *data, long revision); bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); void aa_loaddata_kref(struct kref *kref); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index b92db1b2f26e2..c474a55fed22c 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1206,8 +1206,12 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, if (aa_rawdata_eq(rawdata_ent, udata)) { struct aa_loaddata *tmp; - tmp = aa_get_profile_loaddata(rawdata_ent); - /* check we didn't fail the race */ + /* + * Entries remain on rawdata_list with + * pcount == 0 until do_ploaddata_rmfs() + * runs; only take a live profile ref. + */ + tmp = aa_get_profile_loaddata_not0(rawdata_ent); if (tmp) { aa_put_profile_loaddata(udata); udata = tmp; From 81371dbd23601f67f01372817fdbab42c5601e43 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 11:05:27 +0900 Subject: [PATCH 0042/3297] NTB: epf: Avoid pci_iounmap() with offset when PEER_SPAD and CONFIG share BAR commit d876153680e3d721d385e554def919bce3d18c74 upstream. When BAR_PEER_SPAD and BAR_CONFIG share one PCI BAR, the module teardown path ends up calling pci_iounmap() on the same iomem with some offset, which is unnecessary and triggers a kernel warning like the following: Trying to vunmap() nonexistent vm area (0000000069a5ffe8) WARNING: mm/vmalloc.c:3470 at vunmap+0x58/0x68, CPU#5: modprobe/2937 [...] Call trace: vunmap+0x58/0x68 (P) iounmap+0x34/0x48 pci_iounmap+0x2c/0x40 ntb_epf_pci_remove+0x44/0x80 [ntb_hw_epf] pci_device_remove+0x48/0xf8 device_remove+0x50/0x88 device_release_driver_internal+0x1c8/0x228 driver_detach+0x50/0xb0 bus_remove_driver+0x74/0x100 driver_unregister+0x34/0x68 pci_unregister_driver+0x34/0xa0 ntb_epf_pci_driver_exit+0x14/0xfe0 [ntb_hw_epf] [...] Fix it by unmapping only when PEER_SPAD and CONFIG use difference bars. Cc: stable@vger.kernel.org Fixes: e75d5ae8ab88 ("NTB: epf: Allow more flexibility in the memory BAR map method") Reviewed-by: Frank Li Signed-off-by: Koichiro Den Reviewed-by: Dave Jiang Signed-off-by: Jon Mason Signed-off-by: Greg Kroah-Hartman --- drivers/ntb/hw/epf/ntb_hw_epf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index d3ecf25a51625..9935da48a52ea 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -646,7 +646,8 @@ static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev) struct pci_dev *pdev = ndev->ntb.pdev; pci_iounmap(pdev, ndev->ctrl_reg); - pci_iounmap(pdev, ndev->peer_spad_reg); + if (ndev->barno_map[BAR_PEER_SPAD] != ndev->barno_map[BAR_CONFIG]) + pci_iounmap(pdev, ndev->peer_spad_reg); pci_iounmap(pdev, ndev->db_reg); pci_release_regions(pdev); From 0d35f9f194a858567a21017d69318a51e3a822b9 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Thu, 25 Jun 2026 23:50:48 -0500 Subject: [PATCH 0043/3297] fbdev: fix use-after-free in store_modes() commit 2c1c805c65fb7dc7524e20376d6987721e73a0b1 upstream. store_modes() replaces a framebuffer's modelist with modes from userspace. On success it frees the old modelist with fb_destroy_modelist(). Two fields still point into that freed list. One pointer is fb_display[i].mode, the mode a console is using. fbcon_new_modelist() moves these pointers to the new list. It only does so for consoles still mapped to the framebuffer. An unmapped console is skipped and keeps its stale pointer. Unbinding fbcon, for example, sets con2fb_map[i] to -1 but leaves fb_display[i].mode set. An FBIOPUT_VSCREENINFO ioctl with FB_ACTIVATE_INV_MODE later reaches fbcon_mode_deleted(). That function reads the stale fb_display[i].mode through fb_mode_is_equal(). The read is a use-after-free. The other pointer is fb_info->mode, the current mode. It is set through the mode sysfs attribute. store_modes() does not update fb_info->mode, so it is left pointing into the freed list. show_mode(), the attribute's read handler, dereferences the stale fb_info->mode through mode_string(). The read is a use-after-free. Clear both pointers before freeing the list. Commit a1f305893074 ("fbcon: Set fb_display[i]->mode to NULL when the mode is released") added the helper fbcon_delete_modelist(). It clears every fb_display[i].mode that points into a given list. So far it is called only from the unregister path. Call it from store_modes() too, and set fb_info->mode to NULL. Reported-by: syzbot+81c7c6b52649fd07299d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=81c7c6b52649fd07299d Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/ajjoDhAi2y4ArSlz@dev/ Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ian Bridges Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbsysfs.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index baa2bae0fb5b3..fe8bd33e64ab1 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -11,6 +11,7 @@ #include #include "fb_internal.h" +#include "fbcon.h" static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { @@ -111,8 +112,15 @@ static ssize_t store_modes(struct device *device, if (fb_new_modelist(fb_info)) { fb_destroy_modelist(&fb_info->modelist); list_splice(&old_list, &fb_info->modelist); - } else + } else { + /* + * fb_display[i].mode and fb_info->mode both point into the old + * list. Clear them before it is freed. + */ + fbcon_delete_modelist(&old_list); + fb_info->mode = NULL; fb_destroy_modelist(&old_list); + } unlock_fb_info(fb_info); console_unlock(); From 99e6c712cc300883b8cbf03347d5359ec1a4d6dd Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 16 Jun 2026 07:15:17 -0700 Subject: [PATCH 0044/3297] kernel/fork: clear PF_BLOCK_TS in copy_process() commit fd38b75c4b43295b10d69772a46d1c74dbd6fc81 upstream. PF_BLOCK_TS is only set in blk_time_get_ns() when current->plug is non-NULL, and blk_finish_plug() clears it via __blk_flush_plug() before NULLing the plug pointer. copy_process() breaks the invariant by inheriting PF_BLOCK_TS from the parent while resetting the child's plug to NULL. Clear PF_BLOCK_TS alongside that assignment so callers can rely on "PF_BLOCK_TS set implies current->plug != NULL" and dereference current->plug unguarded. Fixes: 06b23f92af87 ("block: update cached timestamp post schedule/preemption") Cc: stable@vger.kernel.org Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260616141604.328820-2-usama.arif@linux.dev Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- kernel/fork.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/fork.c b/kernel/fork.c index 1215d3f52c6d2..8b1238d692916 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2230,6 +2230,7 @@ __latent_entropy struct task_struct *copy_process( #ifdef CONFIG_BLOCK p->plug = NULL; + p->flags &= ~PF_BLOCK_TS; #endif futex_init_task(p); From 97e1044e79c5d6bdbc435e33980f52e6e1f5d65f Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Tue, 16 Jun 2026 07:15:18 -0700 Subject: [PATCH 0045/3297] block: invalidate cached plug timestamp after task switch commit fad156c2af227f42ca796cbb20ddc354a6dd9932 upstream. blk_time_get_ns() caches ktime_get_ns() in current->plug->cur_ktime and marks the task with PF_BLOCK_TS. That cache is only valid while the task keeps running; if the task is switched out, wall-clock time advances and the cached value must not be reused when the task runs again. The existing invalidation covers explicit plug flushes through __blk_flush_plug(), and the schedule() / rtmutex paths through sched_update_worker(). It does not cover in-kernel preemption paths such as preempt_schedule(), preempt_schedule_notrace(), and preempt_schedule_irq(), which enter __schedule(SM_PREEMPT) directly and return without calling sched_update_worker(). As a result, a task preempted while holding a plug with PF_BLOCK_TS set can reuse a stale plug->cur_ktime after it is scheduled back in. blk-iocost then consumes that stale timestamp through ioc_now(), producing stale vnow values for throttle decisions, and through ioc_rqos_done(), inflating on-queue time and feeding false missed-QoS samples into vrate adjustment. Move the schedule-side invalidation to finish_task_switch(), which runs for the scheduled-in task after every actual context switch regardless of which schedule entry point was used. Keep __blk_flush_plug() as the explicit flush/finish-plug invalidation path, and remove only the PF_BLOCK_TS handling from sched_update_worker(). Fixes: 06b23f92af87 ("block: update cached timestamp post schedule/preemption") Cc: stable@vger.kernel.org Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260616141604.328820-3-usama.arif@linux.dev Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- include/linux/blkdev.h | 16 ++++++---------- kernel/sched/core.c | 12 ++++++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 59e54550a053f..47b06e53fa2cb 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1187,16 +1187,12 @@ static inline void blk_flush_plug(struct blk_plug *plug, bool async) __blk_flush_plug(plug, async); } -/* - * tsk == current here - */ -static inline void blk_plug_invalidate_ts(struct task_struct *tsk) +static __always_inline void blk_plug_invalidate_ts(void) { - struct blk_plug *plug = tsk->plug; - - if (plug) - plug->cur_ktime = 0; - current->flags &= ~PF_BLOCK_TS; + if (unlikely(current->flags & PF_BLOCK_TS)) { + current->plug->cur_ktime = 0; + current->flags &= ~PF_BLOCK_TS; + } } int blkdev_issue_flush(struct block_device *bdev); @@ -1222,7 +1218,7 @@ static inline void blk_flush_plug(struct blk_plug *plug, bool async) { } -static inline void blk_plug_invalidate_ts(struct task_struct *tsk) +static inline void blk_plug_invalidate_ts(void) { } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 46fc94f2338e8..d089b1f211556 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5206,6 +5206,12 @@ static struct rq *finish_task_switch(struct task_struct *prev) */ kmap_local_sched_in(); + /* + * Any cached block-layer timestamp (plug->cur_ktime) is stale now, + * invalidate it. + */ + blk_plug_invalidate_ts(); + fire_sched_in_preempt_notifiers(current); /* * When switching through a kernel thread, the loop in @@ -7000,12 +7006,10 @@ static inline void sched_submit_work(struct task_struct *tsk) static void sched_update_worker(struct task_struct *tsk) { - if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER | PF_BLOCK_TS)) { - if (tsk->flags & PF_BLOCK_TS) - blk_plug_invalidate_ts(tsk); + if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER)) { if (tsk->flags & PF_WQ_WORKER) wq_worker_running(tsk); - else if (tsk->flags & PF_IO_WORKER) + else io_wq_worker_running(tsk); } } From 1fcca1260c6e74e2279661511cdaa0aa232e4f7e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 4 Jun 2026 17:11:56 +0200 Subject: [PATCH 0046/3297] KVM: arm64: Omit tag sync on stage-2 mappings of the zero page commit 2986a625740599fe6e7635b0586fed2a95bcd1f7 upstream. Commit f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged") removed the PG_mte_tagged flag from the zero page, but missed a KVM code path that may set this flag on the zero page when it is used in a stage-2 CoW mapping of anonymous memory. So disregard the zero page explicitly in sanitise_mte_tags(). Fixes: f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged") Cc: stable@vger.kernel.org # 5.10.x Suggested-by: Catalin Marinas Signed-off-by: Ard Biesheuvel Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/mmu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 0d38dc72dfc62..403a635483513 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1444,6 +1444,11 @@ static void sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn, if (!kvm_has_mte(kvm)) return; + if (is_zero_pfn(pfn)) { + WARN_ON_ONCE(nr_pages != 1); + return; + } + if (folio_test_hugetlb(folio)) { /* Hugetlb has MTE flags set on head page only */ if (folio_try_hugetlb_mte_tagging(folio)) { From 2b7ec72786094a4f4abd9bade1170021f026c5ff Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:18:41 +0200 Subject: [PATCH 0047/3297] err.h: use __always_inline on all error pointer helpers commit 94bfc7f3b0c7c33331ba4ff6cc64ff309dfcbce8 upstream. While testing randconfig builds on s390, I came across a link failure with CONFIG_DMA_SHARED_BUFFER disabled: ERROR: modpost: "dma_buf_put" [drivers/iommu/iommufd/iommufd.ko] undefined! The problem here is that IS_ERR() is not inlined and dead code elimination fails as a consequence. The err.h helpers all turn into a trivial assignment of a bit mask and should never result in a function call, so force them to always be inline. This should generally result in better object code aside from avoiding the link failure above. Link: https://lore.kernel.org/20260526101851.2495110-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Alexander Lobakin Reviewed-by: Nathan Chancellor Tested-by: Tamir Duberstein Cc: Alexander Gordeev Cc: Andriy Shevchenko Cc: Ansuel Smith Cc: Bjorn Andersson Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/err.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/err.h b/include/linux/err.h index 1d60aa86db53b..281115ff04252 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -36,7 +36,7 @@ * * Return: A pointer with @error encoded within its value. */ -static inline void * __must_check ERR_PTR(long error) +static __always_inline void * __must_check ERR_PTR(long error) { return (void *) error; } @@ -52,7 +52,7 @@ static inline void * __must_check ERR_PTR(long error) * @ptr: An error pointer. * Return: The error code within @ptr. */ -static inline long __must_check PTR_ERR(__force const void *ptr) +static __always_inline long __must_check PTR_ERR(__force const void *ptr) { return (long) ptr; } @@ -65,7 +65,7 @@ static inline long __must_check PTR_ERR(__force const void *ptr) * @ptr: The pointer to check. * Return: true if @ptr is an error pointer, false otherwise. */ -static inline bool __must_check IS_ERR(__force const void *ptr) +static __always_inline bool __must_check IS_ERR(__force const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } @@ -79,7 +79,7 @@ static inline bool __must_check IS_ERR(__force const void *ptr) * * Like IS_ERR(), but also returns true for a null pointer. */ -static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) +static __always_inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) { return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr); } @@ -91,7 +91,7 @@ static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) * Explicitly cast an error-valued pointer to another pointer type in such a * way as to make it clear that's what's going on. */ -static inline void * __must_check ERR_CAST(__force const void *ptr) +static __always_inline void * __must_check ERR_CAST(__force const void *ptr) { /* cast away the const */ return (void *) ptr; @@ -114,7 +114,7 @@ static inline void * __must_check ERR_CAST(__force const void *ptr) * * Return: The error code within @ptr if it is an error pointer; 0 otherwise. */ -static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) +static __always_inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) { if (IS_ERR(ptr)) return PTR_ERR(ptr); From 49d893b9cbcfc5802a32e53a64c6c6956670d65b Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Mon, 11 May 2026 12:50:52 +0200 Subject: [PATCH 0048/3297] gcov: use atomic counter updates to fix concurrent access crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 56cb9b7d96b28a1173a510ab25354b6599ad3a33 upstream. GCC's GCOV instrumentation can merge global branch counters with loop induction variables as an optimization. In inflate_fast(), the inner copy loops get transformed so that the GCOV counter value is loaded multiple times to compute the loop base address, start index, and end bound. Since GCOV counters are global (not per-CPU), concurrent execution on different CPUs causes the counter to change between loads, producing inconsistent values and out-of-bounds memory writes. The crash manifests during IPComp (IP Payload Compression) processing when inflate_fast() runs concurrently on multiple CPUs: BUG: unable to handle page fault for address: ffffd0a3c0902ffa RIP: inflate_fast+1431 Call Trace: zlib_inflate __deflate_decompress crypto_comp_decompress ipcomp_decompress [xfrm_ipcomp] ipcomp_input [xfrm_ipcomp] xfrm_input At the crash point, the compiler generated three loads from the same global GCOV counter (__gcov0.inflate_fast+216) to compute base, start, and end for an indexed loop. Another CPU modified the counter between loads, making the values inconsistent - the write went 3.4 MB past a 65 KB buffer. Add -fprofile-update=prefer-atomic to CFLAGS_GCOV at the global level in the top-level Makefile, guarded by a try-run compile test. The test compiles a minimal program with and without -fprofile-update=prefer-atomic using the full KBUILD_CFLAGS, then compares undefined symbols in the resulting object files. If prefer-atomic introduces new undefined references (such as __atomic_fetch_add_8 on i386 or __aarch64_ldadd8_relax on arm64 with outline-atomics), the flag is not added -- the kernel does not link against libatomic. On architectures where GCC inlines 64-bit atomic counter updates (x86_64, s390, ...) the test passes and the flag is enabled, preventing the compiler from merging counters with loop induction variables and fixing the observed concurrent-access crash. On architectures where the flag would introduce libatomic dependencies, it is silently omitted and behaviour is no worse than before this patch. Move the CFLAGS_GCOV block from its original position (before the arch Makefile include) to after the core KBUILD_CFLAGS assignments but before the scripts/Makefile.gcc-plugins include. This placement ensures the try-run test sees arch-specific flags (-m32, -march=, -mno-outline-atomics) while avoiding GCC plugin flags (-fplugin=) that would break the test on clean builds when plugin shared objects do not yet exist. Link: https://lore.kernel.org/20260511105052.417187-2-khorenko@virtuozzo.com Signed-off-by: Konstantin Khorenko Tested-by: Arnd Bergmann Tested-by: Peter Oberparleiter Reviewed-by: Peter Oberparleiter Cc: Masahiro Yamada Cc: Miguel Ojeda Cc: Mikhail Zaslonko Cc: Nathan Chancellor Cc: Pavel Tikhomirov Cc: Thomas Weißschuh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- Makefile | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 9c16001ccdcd1..74cbca8abc6cb 100644 --- a/Makefile +++ b/Makefile @@ -806,12 +806,6 @@ endif # KBUILD_EXTMOD # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -ifdef CONFIG_CC_IS_GCC -CFLAGS_GCOV += -fno-tree-loop-im -endif -export CFLAGS_GCOV - # The arch Makefiles can override CC_FLAGS_FTRACE. We may also append it later. ifdef CONFIG_FUNCTION_TRACER CC_FLAGS_FTRACE := -pg @@ -1082,6 +1076,27 @@ endif # Ensure compilers do not transform certain loops into calls to wcslen() KBUILD_CFLAGS += -fno-builtin-wcslen +CFLAGS_GCOV := -fprofile-arcs -ftest-coverage +ifdef CONFIG_CC_IS_GCC +CFLAGS_GCOV += -fno-tree-loop-im +# Use atomic counter updates to avoid concurrent-access crashes in GCOV. +# Only enable if -fprofile-update=prefer-atomic does not introduce new +# undefined symbols (e.g. libatomic calls that the kernel cannot link). +CFLAGS_GCOV += $(call try-run,\ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -x c - -c -o "$$TMP.base" && \ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -fprofile-update=prefer-atomic \ + -x c - -c -o "$$TMP" && \ + $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ + $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ + cmp -s "$$TMP.ubase" "$$TMP.utest",\ + -fprofile-update=prefer-atomic) +endif +export CFLAGS_GCOV + # change __FILE__ to the relative path to the source directory ifdef building_out_of_srctree KBUILD_CPPFLAGS += $(call cc-option,-fmacro-prefix-map=$(srcroot)/=) From b11c1fa32667692a2c0566e10163758e786e430c Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 1 Jun 2026 23:11:54 +0300 Subject: [PATCH 0049/3297] KEYS: fix overflow in keyctl_pkey_params_get_2() commit cb481e59ea6cae3b7796ac1d7a22b6b24c3f3c0b upstream. The length for the internal output buffer is calculated incorrectly, which can result overflow when a too small buffer is provided. Fix the bug by allocating internal output with the size of the maximum length of the cryptographic primitive instead of caller provided size. Link: https://lore.kernel.org/keyrings/20260531024914.3712130-1-jarkko@kernel.org/ Cc: stable@vger.kernel.org # v4.20+ Fixes: 00d60fd3b932 ("KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver #2]") Reported-by: Alessandro Groppo Tested-by: Alessandro Groppo Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- security/keys/keyctl_pkey.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c index 97bc27bbf0797..ba150ee2d4a37 100644 --- a/security/keys/keyctl_pkey.c +++ b/security/keys/keyctl_pkey.c @@ -138,28 +138,35 @@ static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_par if (uparams.in_len > info.max_dec_size || uparams.out_len > info.max_enc_size) return -EINVAL; + + params->out_len = info.max_enc_size; break; case KEYCTL_PKEY_DECRYPT: if (uparams.in_len > info.max_enc_size || uparams.out_len > info.max_dec_size) return -EINVAL; + + params->out_len = info.max_dec_size; break; case KEYCTL_PKEY_SIGN: if (uparams.in_len > info.max_data_size || uparams.out_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; case KEYCTL_PKEY_VERIFY: if (uparams.in_len > info.max_data_size || uparams.in2_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; default: BUG(); } params->in_len = uparams.in_len; - params->out_len = uparams.out_len; /* Note: same as in2_len */ return 0; } From 7216ce8cb12fee44e309503955bb83806b106129 Mon Sep 17 00:00:00 2001 From: Shaomin Chen Date: Wed, 10 Jun 2026 13:10:05 +0300 Subject: [PATCH 0050/3297] keys: Pin request_key_auth payload in instantiate paths commit fd15b457a86939c38aa12116adabd8ff686c5e51 upstream. A: request_key() B: KEYCTL_INSTANTIATE_IOV ================ ========================= create auth key store rka in auth key wait for helper get auth key load rka from auth key copy user payload sleep on #PF helper completed detach and free rka destroy auth key wake up use rka->target_key **USE-AFTER-FREE** Give request_key_auth payloads a refcount. Take a payload reference while authkey->sem stabilizes the payload and revocation state. Hold that reference across the instantiate and reject paths. Drop the auth key owning reference from revoke and destroy. [jarkko: Replaced the first two paragraphs of text with an actual concurrency scenario.] Cc: stable@vger.kernel.org # v5.10+ Fixes: b5f545c880a2 ("[PATCH] keys: Permit running process to instantiate keys") Reported-by: Shaomin Chen Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com Signed-off-by: Shaomin Chen Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- include/keys/request_key_auth-type.h | 2 ++ security/keys/internal.h | 2 ++ security/keys/keyctl.c | 24 +++++++++++++++----- security/keys/request_key_auth.c | 33 ++++++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/include/keys/request_key_auth-type.h b/include/keys/request_key_auth-type.h index 36b89a933310f..01e42ee5f4099 100644 --- a/include/keys/request_key_auth-type.h +++ b/include/keys/request_key_auth-type.h @@ -9,12 +9,14 @@ #define _KEYS_REQUEST_KEY_AUTH_TYPE_H #include +#include /* * Authorisation record for request_key(). */ struct request_key_auth { struct rcu_head rcu; + refcount_t usage; struct key *target_key; struct key *dest_keyring; const struct cred *cred; diff --git a/security/keys/internal.h b/security/keys/internal.h index 2cffa6dc82557..b7b622bc36a13 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(struct key *target, const void *callout_info, size_t callout_len, struct key *dest_keyring); +struct request_key_auth *request_key_auth_get(struct key *authkey); +void request_key_auth_put(struct request_key_auth *rka); extern struct key *key_get_instantiation_authkey(key_serial_t target_id); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ab927a142f515..11bedca4e1c6d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_common(key_serial_t id, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* pull the payload in if one was supplied */ payload = NULL; @@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_common(key_serial_t id, ret = -ENOMEM; payload = kvmalloc(plen, GFP_KERNEL); if (!payload) - goto error; + goto error_put_rka; ret = -EFAULT; if (!copy_from_iter_full(payload, plen, from)) @@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_common(key_serial_t id, error2: kvfree_sensitive(payload, plen); +error_put_rka: + request_key_auth_put(rka); error: return ret; } @@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* find the destination keyring if present (which must also be * writable) */ ret = get_instantiation_keyring(ringid, rka, &dest_keyring); if (ret < 0) - goto error; + goto error_put_rka; /* instantiate the key and link it into a keyring */ ret = key_reject_and_link(rka->target_key, timeout, error, @@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (ret == 0) keyctl_change_reqkey_auth(NULL); +error_put_rka: + request_key_auth_put(rka); error: return ret; } diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 8f33cd170e421..bd34317b58cc8 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -23,6 +23,7 @@ static void request_key_auth_describe(const struct key *, struct seq_file *); static void request_key_auth_revoke(struct key *); static void request_key_auth_destroy(struct key *); static long request_key_auth_read(const struct key *, char *, size_t); +static void request_key_auth_rcu_disposal(struct rcu_head *); /* * The request-key authorisation key type definition. @@ -115,6 +116,31 @@ static void free_request_key_auth(struct request_key_auth *rka) kfree(rka); } +/* + * Take a reference to the request-key authorisation payload so callers can + * drop authkey->sem before doing operations that may sleep. + */ +struct request_key_auth *request_key_auth_get(struct key *authkey) +{ + struct request_key_auth *rka; + + down_read(&authkey->sem); + rka = dereference_key_locked(authkey); + if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags)) + refcount_inc(&rka->usage); + else + rka = NULL; + up_read(&authkey->sem); + + return rka; +} + +void request_key_auth_put(struct request_key_auth *rka) +{ + if (rka && refcount_dec_and_test(&rka->usage)) + call_rcu(&rka->rcu, request_key_auth_rcu_disposal); +} + /* * Dispose of the request_key_auth record under RCU conditions */ @@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key) struct request_key_auth *rka = dereference_key_locked(key); kenter("{%d}", key->serial); + if (!rka) + return; rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } /* @@ -150,7 +178,7 @@ static void request_key_auth_destroy(struct key *key) kenter("{%d}", key->serial); if (rka) { rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } } @@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct key *target, const char *op, rka = kzalloc(sizeof(*rka), GFP_KERNEL); if (!rka) goto error; + refcount_set(&rka->usage, 1); rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL); if (!rka->callout_info) goto error_free_rka; From ec1c9e8962555b1766d059dc02760645c2ff62ff Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 13 May 2026 11:14:16 +0300 Subject: [PATCH 0051/3297] userfaultfd: ensure mremap_userfaultfd_fail() releases mmap_changing commit 0496a59745b0723ea74274db16fd5c8b1379b9a9 upstream. Sashiko says: mremap_userfaultfd_prep() increments ctx->mmap_changing to stall concurrent operations, but mremap_userfaultfd_fail() does not decrement it before dropping the context reference. If an mremap operation fails, ctx->mmap_changing remains elevated. This will causes subsequent userfaultfd operations like a UFFDIO_COPY to fail with -EAGAIN. Decrement ctx->mmap_changing in mremap_userfaultfd_fail(). Link: https://sashiko.dev/#/patchset/20260430113512.115938-1-rppt@kernel.org Link: https://lore.kernel.org/20260513081416.495963-1-rppt@kernel.org Fixes: df2cc96e7701 ("userfaultfd: prevent non-cooperative events vs mcopy_atomic races") Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Christian Brauner Cc: Jan Kara Cc: Peter Xu Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/userfaultfd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index df18fb4534037..a67afdbbf77af 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -767,6 +767,8 @@ void mremap_userfaultfd_fail(struct vm_userfaultfd_ctx *vm_ctx) if (!ctx) return; + atomic_dec(&ctx->mmap_changing); + VM_WARN_ON_ONCE(atomic_read(&ctx->mmap_changing) < 0); userfaultfd_ctx_put(ctx); } From 7e25b5e22c1f43f0d47ff487360e6aebd55d033a Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Tue, 7 Apr 2026 23:44:30 +0800 Subject: [PATCH 0052/3297] wifi: mt76: mt76x2u: Add support for ELECOM WDC-867SU3S commit f4ce0664e9f0387873b181777891741c33e19465 upstream. Add the ID 056e:400a to the table to support an additional MT7612U adapter: ELECOM WDC-867SU3S. Compile tested only. Cc: stable@vger.kernel.org # 5.10.x Signed-off-by: Zenm Chen Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260407154430.9184-1-zenmchen@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 96cecc576a986..427b294423d3c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -16,6 +16,7 @@ static const struct usb_device_id mt76x2u_device_table[] = { { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */ { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */ { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */ + { USB_DEVICE(0x056e, 0x400a) }, /* ELECOM WDC-867SU3S */ { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */ { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */ { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */ From a7cdc384c9c57506df62e7ff04058eff61bd1d0f Mon Sep 17 00:00:00 2001 From: ElXreno Date: Wed, 6 May 2026 04:39:16 +0300 Subject: [PATCH 0053/3297] wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer commit 37d65384aa6f9cbe45f4052b13b378af1aab3e95 upstream. On a STATION vif, removing a TDLS peer takes the mt7925_mac_sta_remove -> mt7925_mac_sta_remove_links path. The first loop in that function calls mt7925_mcu_add_bss_info(..., enable=false) for every link of the station being removed. For a non-MLO STATION vif there is exactly one link, link 0, whose bss_conf is the AP's. TDLS peers do not have their own bss_conf - they share the AP's BSS. The result is that every TDLS peer teardown sends a BSS_INFO_UPDATE with enable=0 for the AP's BSS to the firmware, which wipes the AP-side rate-control context. The connection stays associated and TX from the host still works at the negotiated rate, but the AP's downlink to us collapses to the lowest mandatory OFDM rate (HE-MCS 0 / 6 Mbit/s OFDM) and only slowly recovers as rate adaptation re-learns under sustained traffic. With brief or bursty traffic the link can stay at 6-72 Mbit/s indefinitely, requiring a manual reconnect. mt7925_mac_link_sta_remove() already guards its own mt7925_mcu_add_bss_info(..., false) call with "vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls". Add the equivalent guard at the top of the cleanup loop in mt7925_mac_sta_remove_links(), above the link_sta / link_conf / mlink / mconf lookups, so TDLS peer teardown skips the loop body entirely without doing the per-link work that would just be thrown away. Verified on mt7925e by triggering Samsung-S938B auto-TDLS via iperf3 and watching iw rx bitrate after teardown: Before: rx bitrate collapses to 6.0-72.0 Mbit/s, oscillates 17/72/ 137/288/432 Mbit/s for 30+ seconds, no full recovery without a manual reassoc. After: rx bitrate stays at 1200.9 Mbit/s HE-MCS 11 NSS 2 80 MHz across the entire TDLS lifecycle. bpftrace confirms a single mt7925_mcu_add_bss_info(enable=0) call per teardown before the fix; zero such calls after. Fixes: 3878b4333602 ("wifi: mt76: mt7925: update mt7925_mac_link_sta_[add, assoc, remove] for MLO") Cc: stable@vger.kernel.org Signed-off-by: ElXreno Assisted-by: Claude:claude-opus-4-7 bpftrace Link: https://patch.msgid.link/20260506-mt7925-tdls-fixes-v2-2-46aa826ba8bb@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 00126f563dfd9..9a8ef3449eea1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1144,6 +1144,9 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_AP) break; + if (vif->type == NL80211_IFTYPE_STATION && sta->tdls) + continue; + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); if (!link_sta) continue; From 40aa3c2b0cb8e34e0576fc94cc70e4e33db03c0a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Mon, 20 Apr 2026 13:01:29 +0200 Subject: [PATCH 0054/3297] wifi: ath11k: fix warning when unbinding commit 8b7a26b6681922a38cd5a7829ace61f8e54df9b7 upstream. If there is an error during some initialization related to firmware, the buffers dp->tx_ring[i].tx_status are released. However this is released again when the device is unbinded (ath11k_pci), and we get: WARNING: CPU: 0 PID: 6231 at mm/slub.c:4368 free_large_kmalloc+0x57/0x90 Call Trace: free_large_kmalloc ath11k_dp_free ath11k_core_deinit ath11k_pci_remove ... The issue is always reproducible from a VM because the MSI addressing initialization is failing. In order to fix the issue, just set the buffers to NULL after releasing in order to avoid the double free. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Cc: stable@vger.kernel.org Signed-off-by: Jose Ignacio Tornos Martinez Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260420110130.509670-1-jtornosm@redhat.com Signed-off-by: Jeff Johnson Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath11k/dp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index 56b1a657e0b0f..68c7ef190780a 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -1042,6 +1042,7 @@ void ath11k_dp_free(struct ath11k_base *ab) idr_destroy(&dp->tx_ring[i].txbuf_idr); spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); kfree(dp->tx_ring[i].tx_status); + dp->tx_ring[i].tx_status = NULL; } /* Deinit any SOC level resource */ From 0aeb4d3ff6ced464b342bca5c772e7502f3cad32 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 25 Apr 2026 22:32:58 +0300 Subject: [PATCH 0055/3297] wifi: rtlwifi: rtl8821ae: Fix C2H bit location in RX descriptor commit 83d38df6929118c3f996b9e3351c2d5014073d87 upstream. Bit 28 of double word 2 in the RX descriptor indicates if the packet is a normal 802.11 frame, or a message from the wifi firmware to the driver (Card 2 Host). Commit f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") mistakenly made the driver look for this bit in double word 1, causing packet loss and Bluetooth coexistence problems. Fixes: f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") Cc: Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/04da7398-cedb-425a-a810-5772ab10139d@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h index 1155365348f32..d5de09d75f451 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h @@ -291,7 +291,7 @@ static inline int get_rx_desc_paggr(__le32 *__pdesc) static inline int get_rx_status_desc_rpt_sel(__le32 *__pdesc) { - return le32_get_bits(*(__pdesc + 1), BIT(28)); + return le32_get_bits(*(__pdesc + 2), BIT(28)); } static inline int get_rx_desc_rxmcs(__le32 *__pdesc) From 73d427d271f7af6a738feab3368460331821ab6d Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:10 +0200 Subject: [PATCH 0056/3297] wifi: rtw88: increase TX report timeout to fix race condition commit c80788f7c5aed8d420366b821f867a8a353d83a5 upstream. The driver expects the firmware to report TX status within 500ms. However, a timeout can be triggered when the hardware performs background scans while under TX load. During these scans, the firmware stays off-channel for periods exceeding 500ms, delaying the delivery of TX reports back to the driver. When this occurs, the purge timer fires prematurely and drops the tracking skbs from the queue. This results in the host stack interpreting the missing status as packet loss, leading to TCP window collapse. In testing with iperf3, this causes throughput to drop from ~90 Mbps to near-zero for approximately 2 seconds until the connection recovers. Increase RTW_TX_PROBE_TIMEOUT to 2500ms for RTL8723DU. This duration is sufficient to accommodate off-channel dwell time during full background scans, ensuring the purge timer only trips during genuine firmware lockups and preventing unnecessary TCP retransmission cycles. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-1-luka.gejak@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw88/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 2ab440cb2d67b..9e24741b2cb15 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -196,6 +196,7 @@ void rtw_tx_report_purge_timer(struct timer_list *t) void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; + unsigned long timeout = RTW_TX_PROBE_TIMEOUT; unsigned long flags; u8 *drv_data; @@ -207,7 +208,11 @@ void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) __skb_queue_tail(&tx_report->queue, skb); spin_unlock_irqrestore(&tx_report->q_lock, flags); - mod_timer(&tx_report->purge_timer, jiffies + RTW_TX_PROBE_TIMEOUT); + if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D && + rtwdev->hci.type == RTW_HCI_TYPE_USB) + timeout = msecs_to_jiffies(2500); + + mod_timer(&tx_report->purge_timer, jiffies + timeout); } EXPORT_SYMBOL(rtw_tx_report_enqueue); From 200d58c851b8f63f77a05570072dd20f79bc3681 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:11 +0200 Subject: [PATCH 0057/3297] wifi: rtw88: usb: fix memory leaks on USB write failures commit 6b964941bbfe6e0f18b1a5e008486dbb62df440a upstream. When rtw_usb_write_port() fails to submit a USB Request Block (URB) (e.g., due to device disconnect or ENOMEM), the completion callback is never executed. Currently, the driver ignores the return value of rtw_usb_write_port() in rtw_usb_write_data() and rtw_usb_tx_agg_skb(). Because these functions rely on the completion callback to free the socket buffers (skbs) and the transaction control block (txcb), a submission failure results in: 1. A memory leak of the allocated skb in rtw_usb_write_data(). 2. A memory leak of the txcb structure and all aggregated skbs in rtw_usb_tx_agg_skb(). Fix this by checking the return value of rtw_usb_write_port(). If it fails, explicitly free the skb in rtw_usb_write_data(), and properly purge the tx_ack_queue and free the txcb in rtw_usb_tx_agg_skb(). The issue was discovered in practice during device disconnect/reconnect scenarios and memory pressure conditions. Tested by verifying normal TX operation continues after the fix without regressions. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-2-luka.gejak@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtw88/usb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 6e841a11c752b..c7f723c084338 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -399,6 +399,7 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list int agg_num = 0; unsigned int align_next = 0; u8 qsel; + int ret; if (skb_queue_empty(list)) return false; @@ -456,7 +457,13 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list tx_desc = (struct rtw_tx_desc *)skb_head->data; qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL); - rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb); + ret = rtw_usb_write_port(rtwdev, qsel, skb_head, + rtw_usb_write_port_tx_complete, txcb); + if (ret) { + ieee80211_purge_tx_queue(rtwdev->hw, &txcb->tx_ack_queue); + kfree(txcb); + return false; + } return true; } @@ -518,8 +525,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev, ret = rtw_usb_write_port(rtwdev, qsel, skb, rtw_usb_write_port_complete, skb); - if (unlikely(ret)) + if (unlikely(ret)) { rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); + dev_kfree_skb_any(skb); + } return ret; } From df626f284cb90b2566bd296d04e134a55c8d3012 Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Thu, 12 Feb 2026 20:50:34 +0800 Subject: [PATCH 0058/3297] wifi: iwlwifi: mvm: fix race condition in PTP removal commit 65150c9cc3e06ab54bc4e8134a47f6f5d095a4e3 upstream. iwl_mvm_ptp_remove() calls cancel_delayed_work_sync() only after ptp_clock_unregister() and clearing ptp_data state (ptp_clock, ptp_clock_info, last_gp2). This creates a race where the delayed work iwl_mvm_ptp_work() can execute between ptp_clock_unregister() and cancel_delayed_work_sync(), observing partially cleared PTP state. Move cancel_delayed_work_sync() before ptp_clock_unregister() to ensure the delayed work is fully stopped before any PTP cleanup begins. Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Reviewed-by: Vadim Fedorenko Signed-off-by: Junjie Cao Link: https://patch.msgid.link/20260212125035.1345718-1-junjie.cao@intel.com Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index ad156b82eaa94..efb291ceb0e58 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -323,11 +323,11 @@ void iwl_mvm_ptp_remove(struct iwl_mvm *mvm) mvm->ptp_data.ptp_clock_info.name, ptp_clock_index(mvm->ptp_data.ptp_clock)); + cancel_delayed_work_sync(&mvm->ptp_data.dwork); ptp_clock_unregister(mvm->ptp_data.ptp_clock); mvm->ptp_data.ptp_clock = NULL; memset(&mvm->ptp_data.ptp_clock_info, 0, sizeof(mvm->ptp_data.ptp_clock_info)); mvm->ptp_data.last_gp2 = 0; - cancel_delayed_work_sync(&mvm->ptp_data.dwork); } } From b0b07e04f0c7219bd1a3eb15e22bdf9109f0d393 Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Thu, 12 Feb 2026 20:50:35 +0800 Subject: [PATCH 0059/3297] wifi: iwlwifi: mld: fix race condition in PTP removal commit e1fc08598aa34b28359831e768076f56632720c1 upstream. iwl_mld_ptp_remove() calls cancel_delayed_work_sync() only after ptp_clock_unregister() and clearing ptp_data state (ptp_clock, last_gp2, wrap_counter). This creates a race where the delayed work iwl_mld_ptp_work() can execute between ptp_clock_unregister() and cancel_delayed_work_sync(), observing partially cleared PTP state. Move cancel_delayed_work_sync() before ptp_clock_unregister() to ensure the delayed work is fully stopped before any PTP cleanup begins. Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Reviewed-by: Vadim Fedorenko Signed-off-by: Junjie Cao Link: https://patch.msgid.link/20260212125035.1345718-2-junjie.cao@intel.com Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index 231920425c066..b40182320801d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -319,10 +319,10 @@ void iwl_mld_ptp_remove(struct iwl_mld *mld) mld->ptp_data.ptp_clock_info.name, ptp_clock_index(mld->ptp_data.ptp_clock)); + cancel_delayed_work_sync(&mld->ptp_data.dwork); ptp_clock_unregister(mld->ptp_data.ptp_clock); mld->ptp_data.ptp_clock = NULL; mld->ptp_data.last_gp2 = 0; mld->ptp_data.wrap_counter = 0; - cancel_delayed_work_sync(&mld->ptp_data.dwork); } } From 1de92789ce31e46fa7e7d8e89c90b19cdb1c103b Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 2 Apr 2026 14:48:07 +0800 Subject: [PATCH 0060/3297] wifi: iwlwifi: mld: validate sta_mask before ffs() in BA session handlers commit f056fc2b927448d37eca6b6cacc3d1b0f67b20d2 upstream. Three BA session handlers use ffs(ba_data->sta_mask) - 1 to derive a station ID without checking that sta_mask is non-zero. When sta_mask is zero, ffs() returns 0 and the subtraction wraps to 0xFFFFFFFF, causing an out-of-bounds access on fw_id_to_link_sta[]. Add WARN_ON_ONCE(!ba_data->sta_mask) guards before each ffs() call, consistent with the existing check in iwl_mld_ampdu_rx_start(). Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB788115C6CE873271A9A15A25AF51A@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mld/agg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index 3bf36f8f68742..e3627ad0321c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -64,6 +64,9 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld, } /* pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto out_unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) @@ -166,6 +169,9 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue, goto out_unlock; /* pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto out_unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) @@ -347,6 +353,9 @@ static void iwl_mld_rx_agg_session_expired(struct timer_list *t) } /* timer expired, pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]); From 1e48fefac682c5ee133add84d1f06d458fedf635 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 20 May 2026 20:07:05 +0800 Subject: [PATCH 0061/3297] f2fs: pass correct iostat type for single node writes commit fcb05c26c2a67953b420739b85f49386efc9b6c0 upstream. f2fs_write_single_node_folio() takes an io_type argument, but still passes FS_GC_NODE_IO to __write_node_folio() unconditionally. This was harmless while the helper was only used by f2fs_move_node_folio(), whose caller passes FS_GC_NODE_IO. However, commit fe9b8b30b971 ("f2fs: fix inline data not being written to disk in writeback path") made f2fs_inline_data_fiemap() call the helper with FS_NODE_IO for FIEMAP_FLAG_SYNC. Honor the caller supplied io_type so inline-data FIEMAP sync writeback is accounted as normal node IO instead of GC node IO, while the GC path continues to pass FS_GC_NODE_IO explicitly. Cc: stable@kernel.org Fixes: fe9b8b30b971 ("f2fs: fix inline data not being written to disk in writeback path") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index ca3dad7418b26..feb152c21d2eb 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1853,7 +1853,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode, } if (!__write_node_folio(node_folio, false, false, NULL, - &wbc, false, FS_GC_NODE_IO, NULL)) + &wbc, false, io_type, NULL)) err = -EAGAIN; goto release_folio; out_folio: From 8aad54746c251f2c2370118df766c0c82e2d2091 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Tue, 26 May 2026 13:35:57 +0800 Subject: [PATCH 0062/3297] f2fs: validate orphan inode entry count commit 846c499a65816d13f1186e3090e825e8bb8bcb8b upstream. f2fs_recover_orphan_inodes() trusts the orphan block entry_count when replaying orphan inodes from the checkpoint pack. A corrupted entry_count larger than F2FS_ORPHANS_PER_BLOCK makes the recovery loop read past the ino[] array and interpret footer or following data as inode numbers. On a crafted image, mounting an unpatched kernel can drive orphan recovery into f2fs_bug_on() and panic the kernel. Validate entry_count before consuming entries so corrupted checkpoint data fails the mount with -EFSCORRUPTED and requests fsck instead. Set ERROR_INCONSISTENT_ORPHAN as well, so the corruption reason can be recorded in the superblock s_errors[] field. This gives fsck a persistent hint even though mount-time orphan recovery failure may leave no chance to persist SBI_NEED_FSCK through a checkpoint. Cc: stable@kernel.org Fixes: 127e670abfa7 ("f2fs: add checkpoint operations") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/checkpoint.c | 14 +++++++++++++- include/linux/f2fs_fs.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index bbe07e3a6c75a..c27509d4b6ef4 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -745,6 +745,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi) for (i = 0; i < orphan_blocks; i++) { struct folio *folio; struct f2fs_orphan_block *orphan_blk; + unsigned int entry_count; folio = f2fs_get_meta_folio(sbi, start_blk + i); if (IS_ERR(folio)) { @@ -753,7 +754,18 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi) } orphan_blk = folio_address(folio); - for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { + entry_count = le32_to_cpu(orphan_blk->entry_count); + if (entry_count > F2FS_ORPHANS_PER_BLOCK) { + f2fs_err(sbi, "invalid orphan inode entry count %u", + entry_count); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN); + err = -EFSCORRUPTED; + f2fs_folio_put(folio, true); + goto out; + } + + for (j = 0; j < entry_count; j++) { nid_t ino = le32_to_cpu(orphan_blk->ino[j]); err = recover_orphan_inode(sbi, ino); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index dc41722fcc9dd..81c309b27467e 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -104,6 +104,7 @@ enum f2fs_error { ERROR_CORRUPTED_XATTR, ERROR_INVALID_NODE_REFERENCE, ERROR_INCONSISTENT_NAT, + ERROR_INCONSISTENT_ORPHAN, ERROR_MAX, }; From 77f216ff9ce5cde8eed9f6d12707e906dffdc9f7 Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Thu, 21 May 2026 11:16:18 +0800 Subject: [PATCH 0063/3297] f2fs: validate compress cache inode only when enabled commit 5073c66a96a9c23c0c2533ed4ed06e42f9021208 upstream. F2FS_COMPRESS_INO() uses NM_I(sbi)->max_nid as the synthetic inode number for the compressed page cache inode. That inode only exists when the compress_cache mount option is enabled. When compress_cache is disabled, max_nid is outside the valid inode range. A corrupted directory entry that points to ino == max_nid should therefore be rejected by f2fs_check_nid_range(). However, is_meta_ino() currently treats F2FS_COMPRESS_INO() as a meta inode unconditionally, so f2fs_iget() bypasses do_read_inode() and its nid range check, and instantiates a fake internal inode instead. Gate the compressed cache inode case on COMPRESS_CACHE, matching f2fs_init_compress_inode(). With compress_cache disabled, ino == max_nid now follows the normal inode path and is rejected as an out-of-range nid. Cc: stable@kernel.org Fixes: 6ce19aff0b8c ("f2fs: compress: add compress_inode to cache compressed blocks") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/inode.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 49470f4c9362a..171cd4172025f 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -555,8 +555,13 @@ static int do_read_inode(struct inode *inode) static bool is_meta_ino(struct f2fs_sb_info *sbi, unsigned int ino) { - return ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi) || - ino == F2FS_COMPRESS_INO(sbi); + if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi)) + return true; +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (test_opt(sbi, COMPRESS_CACHE) && ino == F2FS_COMPRESS_INO(sbi)) + return true; +#endif + return false; } struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) From 888d94cc9afbf1b81b76be1c783c3d9f8f338904 Mon Sep 17 00:00:00 2001 From: Sunmin Jeong Date: Mon, 22 Jun 2026 14:28:17 +0900 Subject: [PATCH 0064/3297] f2fs: fix to round down start offset of fallocate for pin file commit 4275b59673eb60b02eec3997816c83f1f4b909c4 upstream. Currently, the length of fallocate for pin file is section-aligned to keep allocated sections from being selected as victims of GC. However, for the case that the start offset of fallocate is not aligned in section, the allocated sections can't be fully utilized. It's because a new section is allocated by f2fs_allocate_pinning_section() after using blks_per_sec blocks regardless of the start offset. As a result, several unexpected dirty segments may be created, including blocks assigned to the pinned file. To address this issue, let's round down the start offset of fallocate to the length of section. The reproducing scenario is as below chunk=$(((2<<20)+4096)) # 2MB + 4KB touch test f2fs_io pinfile set test f2fs_io fallocate 0 0 $chunk test f2fs_io fallocate 0 $chunk $chunk test f2fs_io fallocate 0 $((chunk*2)) $chunk test f2fs_io fiemap 0 $((chunk*3)) test Fiemap: offset = 0 len = 12288 logical addr. physical addr. length flags 0 0000000000000000 000000068c600000 0000000000400000 00001088 1 0000000000400000 000000003d400000 0000000000001000 00001088 2 0000000000401000 00000003eb200000 0000000000200000 00001088 3 0000000000601000 00000005e4200000 0000000000001000 00001088 4 0000000000602000 0000000605400000 0000000000200000 00001089 Cc: stable@vger.kernel.org Fixes: f5a53edcf01e ("f2fs: support aligned pinned file") Reviewed-by: Yunji Kang Reviewed-by: Yeongjin Gil Reviewed-by: Sungjong Seo Signed-off-by: Sunmin Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/file.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6d42e2d28861c..271221485d66a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1895,8 +1895,15 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset, if (f2fs_is_pinned_file(inode)) { block_t sec_blks = CAP_BLKS_PER_SEC(sbi); - block_t sec_len = roundup(map.m_len, sec_blks); + block_t sec_len; + if (map.m_lblk % sec_blks) { + map.m_lblk = rounddown(map.m_lblk, sec_blks); + map.m_len = pg_end - map.m_lblk; + if (off_end) + map.m_len++; + } + sec_len = roundup(map.m_len, sec_blks); map.m_len = sec_blks; next_alloc: f2fs_down_write(&sbi->pin_sem); From ff83de56882cb8466184d322abece2589258ca56 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Mon, 15 Jun 2026 15:19:54 +0800 Subject: [PATCH 0065/3297] f2fs: validate ACL entry sizes in f2fs_acl_from_disk() commit c4810ada31e80cbe4011467c4f3b1e93f94134f3 upstream. f2fs_acl_count() only validates the aggregate ACL xattr length. A malformed ACL can still place ACL_USER or ACL_GROUP in a slot that only contains struct f2fs_acl_entry_short bytes, and f2fs_acl_from_disk() then reads entry->e_id before verifying that a full entry fits. Require a short entry before reading e_tag and e_perm, and require a full entry before reading e_id for ACL_USER and ACL_GROUP. Return -EFSCORRUPTED from these new truncated-entry checks, while keeping the pre-existing -EINVAL paths unchanged. Validation reproduced this kernel report: KASAN slab-out-of-bounds in __f2fs_get_acl+0x6fb/0x7e0 RIP: 0033:0x7f4b835ea7aa The buggy address belongs to the object at ffff888114589960 which belongs to the cache kmalloc-8 of size 8 The buggy address is located 0 bytes to the right of allocated 8-byte region [ffff888114589960, ffff888114589968) Read of size 4 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xce/0x630 (?:?) __f2fs_get_acl+0x6fb/0x7e0 (fs/f2fs/acl.c:169) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x224/0x430 (?:?) kasan_report+0xe0/0x110 (?:?) __f2fs_get_acl+0x5/0x7e0 (fs/f2fs/acl.c:169) __get_acl+0x281/0x380 (?:?) vfs_get_acl+0x10b/0x190 (?:?) do_get_acl+0x2a/0x410 (?:?) do_get_acl+0x9/0x410 (?:?) do_getxattr+0xe8/0x260 (?:?) filename_getxattr+0xd1/0x140 (?:?) do_getname+0x2d/0x2d0 (?:?) path_getxattrat+0x16c/0x200 (?:?) lock_release+0xc8/0x290 (?:?) cgroup_update_frozen+0x9d/0x320 (?:?) lockdep_hardirqs_on_prepare+0xea/0x1a0 (?:?) trace_hardirqs_on+0x1a/0x170 (?:?) _raw_spin_unlock_irq+0x28/0x50 (?:?) do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Cc: stable@kernel.org Fixes: af48b85b8cd3 ("f2fs: add xattr and acl functionalities") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/acl.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index d4d7f329d23f4..4d735c2a7d0b7 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -46,6 +46,7 @@ static inline int f2fs_acl_count(size_t size) static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) { int i, count; + int err = -EINVAL; struct posix_acl *acl; struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); @@ -69,8 +70,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) for (i = 0; i < count; i++) { - if ((char *)entry > end) + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry_short) > end)) { + err = -EFSCORRUPTED; goto fail; + } acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag); acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm); @@ -85,6 +89,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) break; case ACL_USER: + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry) > end)) { + err = -EFSCORRUPTED; + goto fail; + } acl->a_entries[i].e_uid = make_kuid(&init_user_ns, le32_to_cpu(entry->e_id)); @@ -92,6 +101,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) sizeof(struct f2fs_acl_entry)); break; case ACL_GROUP: + if (unlikely((char *)entry + + sizeof(struct f2fs_acl_entry) > end)) { + err = -EFSCORRUPTED; + goto fail; + } acl->a_entries[i].e_gid = make_kgid(&init_user_ns, le32_to_cpu(entry->e_id)); @@ -107,7 +121,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) return acl; fail: posix_acl_release(acl); - return ERR_PTR(-EINVAL); + return ERR_PTR(err); } static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi, From 20190e498057997532c7f186d081011f18e0a462 Mon Sep 17 00:00:00 2001 From: Yongpeng Yang Date: Mon, 27 Apr 2026 21:10:51 +0800 Subject: [PATCH 0066/3297] f2fs: fix incorrect FI_NO_EXTENT handling in __destroy_extent_node() commit 1f70ddb28a3c71df124da5fa4040c808116d6bb9 upstream. When __destroy_extent_node() sets the inode flag FI_NO_EXTENT, it does not reset the length of the largest extent to 0 and update the inode folio. Since modifications to the extent tree are disallowed afterward, the cached largest extent may become stale. This can trigger the following error in xfstests generic/388: F2FS-fs (dm-0): sanity_check_extent_cache: inode (ino=1761) extent info [220057, 57, 6] is incorrect, run fsck to fix In the f2fs_drop_inode path, __destroy_extent_node() does not need to guarantee that et->node_cnt is 0, because concurrency with writeback is expected in this path, and writeback may update the extent cache. This patch reverts commit ed78aeebef05 ("f2fs: fix node_cnt race between extent node destroy and writeback"), and remove the unnecessary zero check of et->node_cnt. Fixes: ed78aeebef05 ("f2fs: fix node_cnt race between extent node destroy and writeback") Cc: stable@vger.kernel.org Reported-by: Chao Yu Suggested-by: Chao Yu Signed-off-by: Yongpeng Yang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/extent_cache.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 87169fd29d897..129eed892a66f 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -119,10 +119,9 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type) if (!__init_may_extent_tree(inode, type)) return false; - if (is_inode_flag_set(inode, FI_NO_EXTENT)) - return false; - if (type == EX_READ) { + if (is_inode_flag_set(inode, FI_NO_EXTENT)) + return false; if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) && !f2fs_sb_has_readonly(F2FS_I_SB(inode))) return false; @@ -645,14 +644,10 @@ static unsigned int __destroy_extent_node(struct inode *inode, while (atomic_read(&et->node_cnt)) { write_lock(&et->lock); - if (!is_inode_flag_set(inode, FI_NO_EXTENT)) - set_inode_flag(inode, FI_NO_EXTENT); node_cnt += __free_extent_tree(sbi, et, nr_shrink); write_unlock(&et->lock); } - f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); - return node_cnt; } @@ -691,12 +686,12 @@ static void __update_extent_tree_range(struct inode *inode, write_lock(&et->lock); - if (is_inode_flag_set(inode, FI_NO_EXTENT)) { - write_unlock(&et->lock); - return; - } - if (type == EX_READ) { + if (is_inode_flag_set(inode, FI_NO_EXTENT)) { + write_unlock(&et->lock); + return; + } + prev = et->largest; dei.len = 0; From db2c5b9fb908715cab9976ee5966c8493ead34bd Mon Sep 17 00:00:00 2001 From: Wenjie Qi Date: Wed, 27 May 2026 20:06:28 +0800 Subject: [PATCH 0067/3297] f2fs: keep atomic write retry from zeroing original data commit 6d874b65aadce56ac78f76129dbcfc2599b638f8 upstream. A partial atomic write reserves a block in the COW inode before reading the original data page for the untouched bytes in that page. If that read fails, write_begin returns an error but leaves the COW inode entry as NEW_ADDR. A retry of the same partial write then finds the COW entry, treats it as existing COW data, and f2fs_write_begin() zeroes the whole folio because blkaddr is NEW_ADDR. If the retry is committed, the bytes outside the retried write range are committed as zeroes instead of preserving the original file contents. Only use the COW inode as the read source when it already has a real data block. If the COW entry is still NEW_ADDR, treat it as a reservation to reuse: keep reading the old data from the original inode and avoid reserving or accounting the same atomic block again. Cc: stable@kernel.org Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Signed-off-by: Wenjie Qi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/data.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f468cba89f303..89105b22a0243 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3537,6 +3537,7 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, pgoff_t index = folio->index; int err = 0; block_t ori_blk_addr = NULL_ADDR; + bool cow_has_reserved_block = false; /* If pos is beyond the end of file, reserve a new block in COW inode */ if ((pos & PAGE_MASK) >= i_size_read(inode)) @@ -3546,9 +3547,11 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, err = __find_data_block(cow_inode, index, blk_addr); if (err) { return err; - } else if (*blk_addr != NULL_ADDR) { + } else if (__is_valid_data_blkaddr(*blk_addr)) { *use_cow = true; return 0; + } else if (*blk_addr == NEW_ADDR) { + cow_has_reserved_block = true; } if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE)) @@ -3561,10 +3564,13 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi, reserve_block: /* Finally, we should reserve a new block in COW inode for the update */ - err = __reserve_data_block(cow_inode, index, blk_addr, node_changed); - if (err) - return err; - inc_atomic_write_cnt(inode); + if (!cow_has_reserved_block) { + err = __reserve_data_block(cow_inode, index, blk_addr, + node_changed); + if (err) + return err; + inc_atomic_write_cnt(inode); + } if (ori_blk_addr != NULL_ADDR) *blk_addr = ori_blk_addr; From 3804e6de30ae7b053d53341d9d6944356cf23b40 Mon Sep 17 00:00:00 2001 From: Denis Arefev Date: Thu, 21 May 2026 10:28:56 +0300 Subject: [PATCH 0068/3297] block: Avoid mounting the bdev pseudo-filesystem in userspace commit f73aa66dffcb8e61e78f01b56163ec16a15d06d2 upstream. The bdev pseudo-filesystem is an internal kernel filesystem with which userspace should not interfere. Unregister it so that userspace cannot even attempt to mount it. This fixes a bug [1] that occurs when attempting to access files, because the system call move_mount() uses pointers declared in the inode_operations structure, which for the bdev pseudo-filesystem are always equal to 0. `inode->i_op = &empty_iops;` [1] BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor instruction fetch in kernel mode #PF: error_code(0x0010) - not-present page PGD 23380067 P4D 23380067 PUD 23381067 PMD 0 Oops: 0010 [#1] PREEMPT SMP KASAN NOPTI CPU: 2 PID: 17125 Comm: syz-executor.0 Not tainted 6.1.155-syzkaller-00350-g84221fde2681 #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:0x0 Call Trace: lookup_open.isra.0+0x700/0x1180 fs/namei.c:3460 open_last_lookups fs/namei.c:3550 [inline] path_openat+0x953/0x2700 fs/namei.c:3780 do_filp_open+0x1c5/0x410 fs/namei.c:3810 do_sys_openat2+0x171/0x4d0 fs/open.c:1318 do_sys_open fs/open.c:1334 [inline] __do_sys_openat fs/open.c:1350 [inline] __se_sys_openat fs/open.c:1345 [inline] __x64_sys_openat+0x13c/0x1f0 fs/open.c:1345 do_syscall_x64 arch/x86/entry/common.c:51 [inline] do_syscall_64+0x35/0x80 arch/x86/entry/common.c:81 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://lore.kernel.org/all/20131010004732.GJ13318@ZenIV.linux.org.uk/T/# Cc: stable@vger.kernel.org Signed-off-by: Denis Arefev Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260521072857.5078-1-arefev@swemel.ru Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/bdev.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/block/bdev.c b/block/bdev.c index 638f0cd458ae4..57e78692084b1 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -438,15 +438,10 @@ EXPORT_SYMBOL_GPL(blockdev_superblock); void __init bdev_cache_init(void) { - int err; - bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode), 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| SLAB_ACCOUNT|SLAB_PANIC), init_once); - err = register_filesystem(&bd_type); - if (err) - panic("Cannot register bdev pseudo-fs"); blockdev_mnt = kern_mount(&bd_type); if (IS_ERR(blockdev_mnt)) panic("Cannot create bdev pseudo-fs"); From 65bd0c0afb0e1bf3287458e342429b069624f7d4 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 3 Jun 2026 18:53:16 +0800 Subject: [PATCH 0069/3297] bpf: use kvfree() for replaced sysctl write buffer commit 4c21b5927d4364bfe7365f2700da5fea0ed0d004 upstream. proc_sys_call_handler() allocates its temporary sysctl buffer with kvzalloc() and passes it to __cgroup_bpf_run_filter_sysctl(). Since kvzalloc() may fall back to vmalloc() for large allocations, freeing that buffer with kfree() is wrong and can corrupt memory. Use kvfree() to safely handle both kmalloc and kvzalloc()/vmalloc allocations. 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-rc5. Reproduced the bug based on v7.1-rc4 in a QEMU x86_64 guest booted with KASAN and CONFIG_FAILSLAB enabled. To exercise the replacement path, the test tree also included the accompanying fix for the stale ret == 1 check in __cgroup_bpf_run_filter_sysctl(). The reproducer confines failslab injections to the proc_sys_call_handler() range, uses stacktrace-depth=32, and injects fail-nth=1 while writing 8191 bytes to /proc/sys/kernel/domainname from a task in the target cgroup. Under that setup, fail-nth=1 triggered the fault: BUG: unable to handle page fault for address: ffffeb0200024d48 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: Oops: 0000 SMP KASAN NOPTI CPU: 2 UID: 0 PID: 209 Comm: repro_proc_sys_ Not tainted 7.1.0-rc4-00686-g97625979a5d4 PREEMPT(lazy) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 RIP: 0010:kfree+0x6e/0x510 ... Call Trace: ? __cgroup_bpf_run_filter_sysctl+0x626/0xc30 __cgroup_bpf_run_filter_sysctl+0x74d/0xc30 ? __pfx___cgroup_bpf_run_filter_sysctl+0x10/0x10 ? srso_return_thunk+0x5/0x5f ? __kvmalloc_node_noprof+0x345/0x870 ? proc_sys_call_handler+0x250/0x480 ? srso_return_thunk+0x5/0x5f proc_sys_call_handler+0x3a2/0x480 ? __pfx_proc_sys_call_handler+0x10/0x10 ? srso_return_thunk+0x5/0x5f ? selinux_file_permission+0x39f/0x500 ? srso_return_thunk+0x5/0x5f ? lock_is_held_type+0x9e/0x120 vfs_write+0x98e/0x1000 ... With this fix applied on top of the same test setup, rerunning the reproducer with fail-nth=1 yields no corresponding Oops reports. Fixes: 4508943794ef ("proc: use kvzalloc for our kernel buffer") Cc: stable@vger.kernel.org Reviewed-by: Emil Tsalapatis Reviewed-by: Jiayuan Chen Acked-by: Yonghong Song Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Link: https://lore.kernel.org/r/20260603105317.944304-3-dawei.feng@seu.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 248f517d66d04..159e29e809362 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1940,7 +1940,7 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, kfree(ctx.cur_val); if (ret == 1 && ctx.new_updated) { - kfree(*buf); + kvfree(*buf); *buf = ctx.new_val; *pcount = ctx.new_len; } else { From 6e61fc2e06e44b6d30248cc5bc47a58e75c2b43e Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:27 +0100 Subject: [PATCH 0070/3297] MIPS: DEC: Prevent initial console buffer from landing in XKPHYS commit 7fb13fd35110ebe95eb053faf79d018f51144d85 upstream. In 64-bit configurations calling the initial console output handler from a kernel thread other than the initial one will result in a situation where the stack has been placed in the XKPHYS 64-bit memory segment and consequently so has been the buffer allocated there that is used as the argument corresponding to the `%s' output conversion specifier for the firmware's printf() entry point. This 64-bit address will then be truncated by 32-bit firmware, resulting in an attempt to access the wrong memory location, which in turn will cause all kinds of unpredictable behaviour, such as a kernel crash: Console: colour dummy device 160x64 Calibrating delay loop... 49.36 BogoMIPS (lpj=192512) pid_max: default: 32768 minimum: 301 CPU 0 Unable to handle kernel paging request at virtual address 000000000203bd00, epc == ffffffffbfc08364, ra == ffffffffbfc08800 Oops[#1]: CPU: 0 PID: 0 Comm: swapper Not tainted 5.18.0-rc2-00254-gfb649bda6f56-dirty #121 $ 0 : 0000000000000000 0000000000000001 0000000000000023 ffffffff80684ba0 $ 4 : 000000000203bd00 ffffffffbfc0f3b4 ffffffffffffffff 0000000000000073 $ 8 : 0a303d7469000000 0000000000000000 0000000000000073 ffffffffbfc0f473 $12 : 0000000000000002 0000000000000000 ffffffff80684c1c 0000000000000000 $16 : 0000000000000000 ffffffff80596dc9 0000000000000000 ffffffffbfc09240 $20 : ffffffff80684c40 ffffffffbfc0f400 000000000000002d 000000000000002b $24 : ffffffffffffffbf 000000000203bd00 $28 : ffffffff805f0000 ffffffff80684b58 0000000000000030 ffffffffbfc08800 Hi : 0000000000000000 Lo : 0000000000000aa8 epc : ffffffffbfc08364 0xffffffffbfc08364 ra : ffffffffbfc08800 0xffffffffbfc08800 Status: 140120e2 KX SX UX KERNEL EXL Cause : 00000008 (ExcCode 02) BadVA : 000000000203bd00 PrId : 00000430 (R4000SC) Modules linked in: Process swapper (pid: 0, threadinfo=(____ptrval____), task=(____ptrval____), tls=0000000000000000) Stack : 0000000000000000 0000000000000000 0000000000000000 0000004d0000004d 80684cc0806a2a40 80596dc80000004d 8061000000000000 bfc0850c80684c38 0000000000000000 000000000203bd00 0000000000000000 0000000000000000 0000000000000000 00000000bfc0f3b4 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000002500000000 0000000000000000 0000000000000000 802c1a7400000000 0203bd0080596dc8 0203bd4d69000000 6c61632000000018 5f746567646e6172 6c616320625f6d6f 5f736e5f6d6f7266 206361323778302b 303d74696e726320 806a0a38806b0000 806a0a38806b0000 00000000806b0000 80683c58806b0000 ... Call Trace: Code: a082ffff 03e00008 00601021 <80820000> 00001821 10400005 24840001 80820000 24630001 ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Fatal exception in interrupt KN04 V2.1k (PC: 0xa0026768, SP: 0x806848e8) >> In this case the pointer in $4 was truncated from 0x980000000203bd00 to 0x000000000203bd00. This may happen when no final console driver has been enabled in the configuration and consequently the initial console continues being used late into bootstrap or with an upcoming change that will switch the zs driver to use a platform device, which in turn will make the console handover happen only after other kernel threads have already been started. Fix the issue by making the buffer static and initdata, and therefore placed in the CKSEG0 32-bit compatibility segment, observing that the console output handler is called with the console lock held, implying no need for this code to be reentrant. Add an assertion to verify the buffer actually has been placed in a compatibility segment. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.12+ Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/prom/console.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/mips/dec/prom/console.c b/arch/mips/dec/prom/console.c index 31a8441d84314..b4f0dba3fa20e 100644 --- a/arch/mips/dec/prom/console.c +++ b/arch/mips/dec/prom/console.c @@ -2,8 +2,9 @@ /* * DECstation PROM-based early console support. * - * Copyright (C) 2004, 2007 Maciej W. Rozycki + * Copyright (C) 2004, 2007, 2026 Maciej W. Rozycki */ +#include #include #include #include @@ -14,9 +15,11 @@ static void __init prom_console_write(struct console *con, const char *s, unsigned int c) { - char buf[81]; + static char buf[81] __initdata = { 0 }; unsigned int chunk = sizeof(buf) - 1; + BUG_ON((long)buf != (int)(long)buf); + while (c > 0) { if (chunk > c) chunk = c; From adfacfbaeae2cb760f492357cc36b41f84ef7f86 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 22 Apr 2026 11:58:44 -0400 Subject: [PATCH 0071/3297] exfat: fix potential use-after-free in exfat_find_dir_entry() commit 3f5f8ee9917cc2b9076ac533492d8a200edcabb8 upstream. In exfat_find_dir_entry(), the buffer_head obtained from exfat_get_dentry() is released with brelse(bh) before the fall-through TYPE_EXTEND branch reads the directory entry through ep (which points into bh->b_data): brelse(bh); if (entry_type == TYPE_EXTEND) { ... len = exfat_extract_uni_name(ep, entry_uniname); ... } After brelse() drops our reference, nothing guarantees that the underlying page backing bh->b_data remains valid for the subsequent exfat_extract_uni_name() read. This is the same pattern fixed in commit fc961522ddbd ("exfat: Fix potential use after free in exfat_load_upcase_table()"). Move brelse(bh) so it runs after ep is no longer dereferenced on each branch. Confirmed on QEMU x86_64 with CONFIG_KASAN=y + CONFIG_DEBUG_PAGEALLOC=y + CONFIG_PAGE_POISONING=y on linux-next, using a crafted exFAT image (long filename with same-hash collisions forcing the TYPE_EXTEND path). With a debug-only invalidate_bdev() inserted between brelse(bh) and the ep read to make the stale-deref window deterministic, the unpatched kernel faults: BUG: KASAN: use-after-free in exfat_find_dir_entry+0x133b/0x15a0 BUG: unable to handle page fault for address: ffff88801a5fa0c2 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN NOPTI RIP: 0010:exfat_find_dir_entry+0x1188/0x15a0 With this patch applied, the same instrumented harness completes cleanly under the same sanitizer stack. I have not reproduced a crash on an uninstrumented kernel under ordinary reclaim; the instrumented A/B establishes the lifetime violation and that the patch closes it, not an unaided triggerability claim. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/exfat/dir.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 7229146fe2bf6..97f2ff87220fe 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -1081,12 +1081,12 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } - brelse(bh); if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; if (step != DIRENT_STEP_NAME || name_len >= MAX_NAME_LENGTH) { + brelse(bh); step = DIRENT_STEP_FILE; continue; } @@ -1097,6 +1097,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); + brelse(bh); name_len += len; unichar = *(uniname+len); @@ -1115,6 +1116,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } + brelse(bh); if (entry_type & (TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) { if (step == DIRENT_STEP_SECD) { From 18587f9831612e24cd8f24be1ec15478feff7abc Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 29 Apr 2026 09:34:01 -0700 Subject: [PATCH 0072/3297] KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level commit ef057cbf825e03b63f6edf5980f96abf3c53089d upstream. When recovering hugepages in the shadow MMU, verify that the base gfn of the shadow page is actually contained within the target memslot, *before* querying the max mapping level given the shadow page's gfn. Failure to pre-check the validity of the gfn can lead to an out-of-bounds access to the slot's lpage_info (which typically manifests as a host #PF because the lpage_info is vmalloc'd) if the guest creates a hugepage mapping (in its PTEs) that extends "below" the bounds of a memslot. When faulting in memory for a guest, and the size of the guest mapping is greater than KVM's (current) max mapping, then KVM will create a "direct" shadow page (direct in that there are no gPTEs to shadow, and so the target gfn is a direct calculation given the base gfn of the shadow page). The hugepage recovery flow looks for such direct shadow pages, as forcing 4KiB mappings when dirty logging generates the guest > host mapping size case. When the 4KiB restriction is lifted, then KVM can replace the shadow page with a hugepage. But if KVM originally used a smaller mapping than the guest because the range of memory covered by the guest hugepage exceeds the bounds of a memslot, then KVM will link a direct shadow page with a gfn that is outside the bounds of the memslot being used to fault in memory. The rmap entry added for the leaf mapping is correct and within bounds, but the gfn of the leaf SPTE's parent shadow page will be out of bounds. BUG: unable to handle page fault for address: ffffc90000806ffc #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 100000067 P4D 100000067 PUD 1002a7067 PMD 10612f067 PTE 0 Oops: Oops: 0000 [#1] SMP CPU: 13 UID: 1000 PID: 757 Comm: mmu_stress_test Not tainted 7.1.0-rc1-48ce1e26eace-x86_pir_to_irr_comments-vm #341 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_mmu_max_mapping_level+0x79/0x2b0 [kvm] Call Trace: kvm_mmu_recover_huge_pages+0x21b/0x320 [kvm] kvm_set_memslot+0x1ee/0x590 [kvm] kvm_set_memory_region.part.0+0x3a1/0x4d0 [kvm] kvm_vm_ioctl+0x9bf/0x15d0 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb7/0xbb0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f21c0f1a9bf Don't bother pre-checking the bounds of the potential hugepage, i.e. don't check that e.g. sp->gfn + KVM_PAGES_PER_HPAGE(sp->role.level + 1) is also within the memslot, as the checks performed by kvm_mmu_max_mapping_level() are a superset of the basic bounds checks. I.e. pre-checking the full range would be a dubious micro-optimization. Fixes: 9eba50f8d7fc ("KVM: x86/mmu: Consult max mapping level when zapping collapsible SPTEs") Cc: stable@vger.kernel.org Cc: David Matlack Cc: James Houghton Cc: Alexander Bulekov Cc: Fred Griffoul Cc: Alexander Graf Cc: David Woodhouse Cc: Filippo Sironi Cc: Ivan Orlov Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/mmu/mmu.c | 18 ++++++++++++------ include/linux/kvm_host.h | 7 ++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 541e199feb998..eadeca9786f6f 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -7181,13 +7181,19 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm, sp = sptep_to_sp(sptep); /* - * We cannot do huge page mapping for indirect shadow pages, - * which are found on the last rmap (level = 1) when not using - * tdp; such shadow pages are synced with the page table in - * the guest, and the guest page table is using 4K page size - * mapping if the indirect sp has level = 1. + * Direct shadow page can be replaced by a hugepage if the host + * mapping level allows it and the memslot maps all of the host + * hugepage. Note! If the memslot maps only part of the + * hugepage, sp->gfn may be below slot->base_gfn, and querying + * the max mapping level would cause an out-of-bounds lpage_info + * access. So the gfn bounds check *must* be done first. + * + * Indirect shadow pages are created when the guest page tables + * are using 4K pages. Since the host mapping is always + * constrained by the page size in the guest, indirect shadow + * pages are never collapsible. */ - if (sp->role.direct && + if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) && sp->role.level < kvm_mmu_max_mapping_level(kvm, NULL, slot, sp->gfn)) { kvm_zap_one_rmap_spte(kvm, rmap_head, sptep); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 398e5695dc076..3c4e2401de072 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1793,6 +1793,11 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm, struct kvm_irq_ack_notifier *kian); bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args); +static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn) +{ + return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages; +} + /* * Returns a pointer to the memslot if it contains gfn. * Otherwise returns NULL. @@ -1803,7 +1808,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn) if (!slot) return NULL; - if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages) + if (is_gfn_in_memslot(slot, gfn)) return slot; else return NULL; From 5da9b1a87ec7cc3489c27016313524769f12d9e0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 12 Jun 2026 15:52:41 -0700 Subject: [PATCH 0073/3297] KVM: Replace guest-triggerable BUG_ON() in ioeventfd datamatch with get_unaligned() commit f1edbed787ba67988ed34e0132ca128b052b6ce8 upstream. Drop a BUG_ON() that has been reachable since it was first added, way back in 2009, and instead use get_unaligned() to perform potentially-unaligned accesses. For a given store, KVM x86's emulator tracks the entire value in the destination operand, x86_emulate_ctxt.dst. If the destination is memory, and the target splits multiple pages and/or is emulated MMIO, then KVM handles each fragment independently. E.g. on a page split starting at page offset 0xffc, KVM writes 4 bytes to the first page, then the remaining bytes to the second page, using ctxt->dst as the source for both (with appropriate offsets). If the destination splits a page *and* hits emulated MMIO on the second page, then KVM will complete the write to the first page, then emulate the MMIO access to the second page. If there is a datamatch-enabled ioeventfd at offset 0 of the second page, then KVM will process the remainder of the store as a potential ioeventfd signal. Putting it all together, if the guest emits a store that splits a page starting at page offset N, and the second page has a datamatch-enabled ioeventfd at offset 0, then KVM will check for datamatch using &dst.valptr[N] as the source. Due to dst (and thus dst.valptr) being 32-byte aligned, if N is not aligned to @len, the BUG_ON() fires. E.g. with a 16-byte store at page offset 0xffc, to an ioeventfd of len 8, all initial checks in ioeventfd_in_range() will succeed, and the BUG_ON() fires due to @val being 4-byte aligned, but not 8-byte aligned. ------------[ cut here ]------------ kernel BUG at arch/x86/kvm/../../../virt/kvm/eventfd.c:783! Oops: invalid opcode: 0000 [#1] SMP CPU: 0 UID: 1000 PID: 615 Comm: repro Not tainted 7.1.0-rc2-ff238429d1ea #365 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:ioeventfd_write+0x6c/0x70 [kvm] Call Trace: __kvm_io_bus_write+0x85/0xb0 [kvm] kvm_io_bus_write+0x53/0x80 [kvm] vcpu_mmio_write+0x66/0xf0 [kvm] emulator_read_write_onepage+0x12a/0x540 [kvm] emulator_read_write+0x109/0x2b0 [kvm] x86_emulate_insn+0x4f8/0xfb0 [kvm] x86_emulate_instruction+0x181/0x790 [kvm] kvm_mmu_page_fault+0x313/0x630 [kvm] vmx_handle_exit+0x18a/0x590 [kvm_intel] kvm_arch_vcpu_ioctl_run+0xc81/0x1c90 [kvm] kvm_vcpu_ioctl+0x2d5/0x970 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb7/0x890 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f19c931a9bf Modules linked in: kvm_intel kvm irqbypass ---[ end trace 0000000000000000 ]--- In a perfect world, the fix would be to simply delete the BUG_ON(), as KVM x86 doesn't perform alignment checks on "normal" memory accesses at CPL0. Sadly, C99 ruins all the fun; while the x86 architecture plays nice, dereferencing an unaligned pointer directly is undefined behavior in C, e.g. triggers splats when running with CONFIG_UBSAN_ALIGNMENT=y. Fixes: d34e6b175e61 ("KVM: add ioeventfd support") Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Message-ID: <20260612225241.678509-1-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- virt/kvm/eventfd.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 1a64266341b19..5bf90affcbb73 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -780,21 +781,18 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val) return true; /* otherwise, we have to actually compare the data */ - - BUG_ON(!IS_ALIGNED((unsigned long)val, len)); - switch (len) { case 1: - _val = *(u8 *)val; + _val = get_unaligned((u8 *)val); break; case 2: - _val = *(u16 *)val; + _val = get_unaligned((u16 *)val); break; case 4: - _val = *(u32 *)val; + _val = get_unaligned((u32 *)val); break; case 8: - _val = *(u64 *)val; + _val = get_unaligned((u64 *)val); break; default: return false; From 8d8507a457667f23477a15496b91908a5b5b7cf3 Mon Sep 17 00:00:00 2001 From: Sam James Date: Mon, 25 May 2026 08:56:19 +0100 Subject: [PATCH 0074/3297] crypto: nx - fix nx_crypto_ctx_exit argument commit 4e67f504ee9ded15e256b64f4fde150e917381d7 upstream. nx_crypto_ctx_shash_exit calls nx_crypto_ctx_exit with crypto_shash_ctx(...) but crypto_shash_ctx gives a nx_crypto_ctx *, not a crypto_tfm *. Fix the type in nx_crypto_ctx_exit and drop the bogus crypto_tfm_ctx call. This fixes the following oops: BUG: Unable to handle kernel data access at 0xc0403effffffffc8 Faulting instruction address: 0xc000000000396cb4 Oops: Kernel access of bad area, sig: 11 [#15] Call Trace: nx_crypto_ctx_shash_exit+0x24/0x60 crypto_shash_exit_tfm+0x28/0x40 crypto_destroy_tfm+0x98/0x140 crypto_exit_ahash_using_shash+0x20/0x40 crypto_destroy_tfm+0x98/0x140 hash_release+0x1c/0x30 alg_sock_destruct+0x38/0x60 __sk_destruct+0x48/0x2b0 af_alg_release+0x58/0xb0 __sock_release+0x68/0x150 sock_close+0x20/0x40 __fput+0x110/0x3a0 sys_close+0x48/0xa0 system_call_exception+0x140/0x2d0 system_call_common+0xf4/0x258 .. which came from hardlink(1) opportunistically using AF_ALG. The same problem exists with nx_crypto_ctx_skcipher_exit getting a context it wasn't expecting, but apparently nobody hit that for years. Cc: Eric Biggers Cc: stable@vger.kernel.org Fixes: bfd9efddf990 ("crypto: nx - convert AES-ECB to skcipher API") Fixes: 9420e628e7d8 ("crypto: nx - Use API partial block handling") Acked-by: Breno Leitao Reviewed-by: Eric Biggers Reported-by: Calvin Buckley Tested-by: Calvin Buckley Suggested-by: Brad Spengler Signed-off-by: Sam James Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/nx/nx.c | 6 ++---- drivers/crypto/nx/nx.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c index 78135fb13f5c2..1b7509e2ce443 100644 --- a/drivers/crypto/nx/nx.c +++ b/drivers/crypto/nx/nx.c @@ -714,15 +714,13 @@ int nx_crypto_ctx_aes_xcbc_init(struct crypto_shash *tfm) /** * nx_crypto_ctx_exit - destroy a crypto api context * - * @tfm: the crypto transform pointer for the context + * @nx_ctx: the crypto api context * * As crypto API contexts are destroyed, this exit hook is called to free the * memory associated with it. */ -void nx_crypto_ctx_exit(struct crypto_tfm *tfm) +void nx_crypto_ctx_exit(struct nx_crypto_ctx *nx_ctx) { - struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); - kfree_sensitive(nx_ctx->kmem); nx_ctx->csbcpb = NULL; nx_ctx->csbcpb_aead = NULL; diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h index 36974f08490a9..6dfabfbf8192c 100644 --- a/drivers/crypto/nx/nx.h +++ b/drivers/crypto/nx/nx.h @@ -153,7 +153,7 @@ int nx_crypto_ctx_aes_ctr_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_aes_cbc_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_aes_ecb_init(struct crypto_skcipher *tfm); int nx_crypto_ctx_sha_init(struct crypto_shash *tfm); -void nx_crypto_ctx_exit(struct crypto_tfm *tfm); +void nx_crypto_ctx_exit(struct nx_crypto_ctx *nx_ctx); void nx_crypto_ctx_skcipher_exit(struct crypto_skcipher *tfm); void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm); void nx_crypto_ctx_shash_exit(struct crypto_shash *tfm); From b85ef03f726b15047a6fa6d11b639bdf6c0ee4f0 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Fri, 1 May 2026 11:02:03 +0000 Subject: [PATCH 0075/3297] gfs2: fix use-after-free in gfs2_qd_dealloc commit f9c9ec2c319f843b70ecdf939d48b52d189bc081 upstream. gfs2_qd_dealloc(), called as an RCU callback from gfs2_qd_dispose(), accesses the superblock object sdp through qd->qd_sbd after freeing qd. It does so to decrement sd_quota_count and wake up sd_kill_wait. However, by the time the RCU callback runs, gfs2_put_super() may have already freed sdp via free_sbd(). This can happen when gfs2_quota_cleanup() is called during unmount: it disposes of quota objects via call_rcu() and then waits on sd_kill_wait with a 60-second timeout. If the timeout expires, or if gfs2_gl_hash_clear() triggers additional qd_put() calls that schedule more RCU callbacks after the wait completes, gfs2_put_super() will proceed to free the superblock while RCU callbacks referencing it are still pending. Add an rcu_barrier() before free_sbd() in gfs2_put_super() to ensure all pending RCU callbacks (including gfs2_qd_dealloc) have completed before the superblock is freed. Fixes: a475c5dd16e5 ("gfs2: Free quota data objects synchronously") Reported-by: syzbot+42a37bf8045847d8f9d2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=42a37bf8045847d8f9d2 Tested-by: syzbot+42a37bf8045847d8f9d2@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Signed-off-by: Andreas Gruenbacher Signed-off-by: Greg Kroah-Hartman --- fs/gfs2/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 54c6f2098f01e..7b5fcd78a3a1a 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -640,6 +640,7 @@ static void gfs2_put_super(struct super_block *sb) gfs2_delete_debugfs_file(sdp); gfs2_sys_fs_del(sdp); + rcu_barrier(); free_sbd(sdp); } From e91df6d273445c03f5aa302bfe147eda33d45794 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 16 Jun 2026 15:10:49 +0000 Subject: [PATCH 0076/3297] pwrseq: core: fix use-after-free in pwrseq_debugfs_seq_next() commit 257595adf9dac15ae1edd9d07753fbc576a7583d upstream. pwrseq_debugfs_seq_next() declares 'next' with __free(put_device), which causes put_device() to be called on the returned pointer when the variable goes out of scope. This results in a use-after-free since the seq_file framework receives a pointer whose reference has already been dropped. Simply removing __free(put_device) would fix the UAF but would leak the reference acquired by bus_find_next_device(), as stop() only calls up_read(&pwrseq_sem) and never releases the device reference. Fix this by making the reference counting consistent across all seq_file callbacks, matching the standard pattern used by PCI and SCSI: - start(): use get_device() so it returns a referenced pointer. - next(): explicitly put_device(curr) to release the previous device's reference (no NULL check needed - the seq_file framework only calls next() while the previous return was non-NULL). - stop(): put_device(data) to release the last iterated device's reference, with a NULL guard since stop() may be called with NULL when start() returned NULL or next() reached end-of-sequence. Cc: stable@vger.kernel.org Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260616151049.1705503-1-vulab@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/power/sequencing/core.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 1fcf0af7cc0bb..d5da006e1ee85 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -990,8 +990,9 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) ctx.index = *pos; /* - * We're holding the lock for the entire printout so no need to fiddle - * with device reference count. + * Hold the lock for the entire printout to prevent device removal. + * Reference counts are managed by start()/next()/stop() as required + * by the seq_file contract. */ down_read(&pwrseq_sem); @@ -999,7 +1000,7 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) if (!ctx.index) return NULL; - return ctx.dev; + return get_device(ctx.dev); } static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, @@ -1009,8 +1010,9 @@ static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, ++*pos; - struct device *next __free(put_device) = - bus_find_next_device(&pwrseq_bus, curr); + struct device *next = bus_find_next_device(&pwrseq_bus, curr); + + put_device(curr); return next; } @@ -1059,6 +1061,8 @@ static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data) static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data) { + if (data) + put_device(data); up_read(&pwrseq_sem); } From 5a84398101bf9f11e84b176343e4e3ba83e668c0 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Wed, 17 Jun 2026 02:05:18 +0000 Subject: [PATCH 0077/3297] hdlc_ppp: sync per-proto timers before freeing hdlc state commit c78a4e41ab5ead6193ad8a2dd92e8906bae659fa upstream. Each PPP control protocol (LCP/IPCP/IPV6CP) embedded in struct ppp registers a timer via timer_setup(). That struct ppp is the hdlc->state allocation, which detach_hdlc_protocol() frees with kfree() in both teardown paths: unregister_hdlc_device() and the re-attach inside attach_hdlc_protocol(). The ppp proto never registered a .detach callback, so detach_hdlc_protocol() performs no timer synchronization before the kfree(). The only cancel, timer_delete(&proto->timer) in ppp_cp_event(), is partial (it does not wait for a running callback) and only runs on the ->CLOSED transition; ppp_stop()/ppp_close() do not sync either. A ppp_timer callback already executing (blocked on ppp->lock) survives the kfree and then dereferences proto->state / ppp->lock in freed memory, leading to a use-after-free. Fix this by adding a .detach helper that calls timer_shutdown_sync() on every per-proto timer. detach_hdlc_protocol() invokes proto->detach(dev) before kfree(hdlc->state), so timer_shutdown_sync() now runs on both free paths. timer_shutdown_sync() is used instead of timer_delete_sync() because the keepalive path re-arms the timer through add_timer()/mod_timer() and shutdown blocks any re-activation during teardown. Initialize the per-protocol timers in ppp_ioctl() when the protocol is attached, and remove the now-redundant timer_setup() from ppp_start(), so that the timers are initialized exactly once at attach time and ppp_timer_release() never operates on uninitialized timer_list structures. attach_hdlc_protocol() uses kmalloc() (not kzalloc), so struct ppp's protos[i].timer is uninitialized garbage until the first timer_setup(); without this init-at-attach, attaching the PPP protocol without ever bringing the device up would leave timer_shutdown_sync() operating on uninitialized memory in .detach. Moving the init out of ppp_start() (which only runs on NETDEV_UP) into the attach path makes the initialization unconditional and avoids initializing the same timer_list twice. This bug was found by static analysis. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260617020518.116319-1-fanwu01@zju.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wan/hdlc_ppp.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index 7496a2e9a2820..22affdcb4ceae 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -621,7 +621,6 @@ static void ppp_start(struct net_device *dev) struct proto *proto = &ppp->protos[i]; proto->dev = dev; - timer_setup(&proto->timer, ppp_timer, 0); proto->state = CLOSED; } ppp->protos[IDX_LCP].pid = PID_LCP; @@ -641,6 +640,15 @@ static void ppp_close(struct net_device *dev) ppp_tx_flush(); } +static void ppp_timer_release(struct net_device *dev) +{ + struct ppp *ppp = get_ppp(dev); + int i; + + for (i = 0; i < IDX_COUNT; i++) + timer_shutdown_sync(&ppp->protos[i].timer); +} + static struct hdlc_proto proto = { .start = ppp_start, .stop = ppp_stop, @@ -649,6 +657,7 @@ static struct hdlc_proto proto = { .ioctl = ppp_ioctl, .netif_rx = ppp_rx, .module = THIS_MODULE, + .detach = ppp_timer_release, }; static const struct header_ops ppp_header_ops = { @@ -659,7 +668,7 @@ static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs) { hdlc_device *hdlc = dev_to_hdlc(dev); struct ppp *ppp; - int result; + int i, result; switch (ifs->type) { case IF_GET_PROTO: @@ -687,6 +696,8 @@ static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs) return result; ppp = get_ppp(dev); + for (i = 0; i < IDX_COUNT; i++) + timer_setup(&ppp->protos[i].timer, ppp_timer, 0); spin_lock_init(&ppp->lock); ppp->req_timeout = 2; ppp->cr_retries = 10; From 5e5b7f2ef854936e95dceb6a2fdfefcb7152d2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Koutn=C3=BD?= Date: Thu, 5 Feb 2026 23:54:23 +0800 Subject: [PATCH 0078/3297] blk-cgroup: fix UAF in __blkcg_rstat_flush() commit 0ab5ee5a1badb58cbb2242617cb01a4972b1f2a2 upstream. When multiple blkgs in the same blkcg are released concurrently, a use-after-free can occur. The race happens when one blkg's __blkcg_rstat_flush() removes another blkg's iostat entries via llist_del_all(). The second blkg sees an empty list and proceeds to free itself while the first is still iterating over its entries. Move the flush from __blkg_release() (RCU callback) to blkg_release() (before call_rcu). This ensures the RCU grace period waits for any concurrent flush's rcu_read_lock() section to complete before freeing. Cc: stable@vger.kernel.org Cc: Jay Shin Cc: Tejun Heo Cc: Waiman Long Fixes: 20cb1c2fb756 ("blk-cgroup: Flush stats before releasing blkcg_gq") Reported-by: coregee2000@gmail.com Closes: https://lore.kernel.org/linux-block/CAHPqNmwT9oRpem3J3erS_W0uSQND47LGGSBsNxP8E6uSUish1w@mail.gmail.com/ Signed-off-by: Ming Lei Tested-by: Jose Fernandez (Anthropic) Link: https://patch.msgid.link/20260205155425.342084-1-ming.lei@redhat.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-cgroup.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index f1ea69743c544..a2347b5795874 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -164,20 +164,10 @@ static void blkg_free(struct blkcg_gq *blkg) static void __blkg_release(struct rcu_head *rcu) { struct blkcg_gq *blkg = container_of(rcu, struct blkcg_gq, rcu_head); - struct blkcg *blkcg = blkg->blkcg; - int cpu; #ifdef CONFIG_BLK_CGROUP_PUNT_BIO WARN_ON(!bio_list_empty(&blkg->async_bios)); #endif - /* - * Flush all the non-empty percpu lockless lists before releasing - * us, given these stat belongs to us. - * - * blkg_stat_lock is for serializing blkg stat update - */ - for_each_possible_cpu(cpu) - __blkcg_rstat_flush(blkcg, cpu); /* release the blkcg and parent blkg refs this blkg has been holding */ css_put(&blkg->blkcg->css); @@ -195,6 +185,17 @@ static void __blkg_release(struct rcu_head *rcu) static void blkg_release(struct percpu_ref *ref) { struct blkcg_gq *blkg = container_of(ref, struct blkcg_gq, refcnt); + struct blkcg *blkcg = blkg->blkcg; + int cpu; + + /* + * Flush all the non-empty percpu lockless lists before releasing + * us, given these stat belongs to us. + * + * blkg_stat_lock is for serializing blkg stat update + */ + for_each_possible_cpu(cpu) + __blkcg_rstat_flush(blkcg, cpu); call_rcu(&blkg->rcu_head, __blkg_release); } From e18769616fd5a90ec1e12aabbba544c488284292 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Wed, 17 Jun 2026 09:58:18 +0200 Subject: [PATCH 0079/3297] tipc: fix slab-use-after-free Read in tipc_aead_decrypt_done commit bda3348872a2ef0d19f2df6aa8cb5025adce2f20 upstream. tipc_aead_decrypt() goes straight from tipc_bearer_hold(b) to crypto_aead_decrypt(req) without taking a reference on the netns, unlike the encrypt path. When crypto_aead_decrypt() is offloaded asynchronously (e.g. the SIMD aead wrapper queuing to cryptd), the cryptd worker runs tipc_aead_decrypt_done() later. If the bearer's netns is torn down in the meantime, cleanup_net() -> tipc_exit_net() -> tipc_crypto_stop() frees the per-netns tipc_crypto, and the completion then reads it: tipc_aead_decrypt_done() dereferences aead->crypto->stats and aead->crypto->net, and tipc_crypto_rcv_complete() dereferences aead->crypto->aead[] and the node table -- reading freed memory. Decoded KASAN splat (v7.1-rc7, CONFIG_KASAN_INLINE + TIPC + TIPC_CRYPTO): BUG: KASAN: slab-use-after-free in tipc_aead_decrypt_done (net/tipc/crypto.c:999) Read of size 8 at addr ffff8881056258a8 by task kworker/u16:2/51 Workqueue: events_unbound Call Trace: tipc_aead_decrypt_done (net/tipc/crypto.c:999) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397 kernel/workqueue.c:3478) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Allocated by task 169: __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415) tipc_crypto_start (net/tipc/crypto.c:1502) tipc_init_net (net/tipc/core.c:72) ops_init (net/core/net_namespace.c:137) setup_net (net/core/net_namespace.c:446) copy_net_ns (net/core/net_namespace.c:579) create_new_namespaces (kernel/nsproxy.c:132) __x64_sys_unshare (kernel/fork.c:3316) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Freed by task 8: kfree (mm/slub.c:6566) tipc_exit_net (net/tipc/core.c:119) cleanup_net (net/core/net_namespace.c:704) process_one_work (kernel/workqueue.c:3314) kthread (kernel/kthread.c:436) This is the same class of bug that commit e279024617134 ("net/tipc: fix slab-use-after-free Read in tipc_aead_encrypt_done") fixed for the encrypt side. The encrypt path takes maybe_get_net(aead->crypto->net) before crypto_aead_encrypt() and drops it with put_net() on the synchronous return paths and in tipc_aead_encrypt_done(); the -EINPROGRESS/-EBUSY return keeps the reference for the async callback to release. The decrypt path was left without the equivalent guard. Mirror the encrypt-side fix on the decrypt path: take a net reference before crypto_aead_decrypt() (failing with -ENODEV and the matching bearer put if it cannot be acquired), keep it across the -EINPROGRESS/-EBUSY async return, and drop it with put_net() on the synchronous success/error return and at the end of tipc_aead_decrypt_done(). Reproduced under KASAN on v7.1-rc7: a UDP bearer with a cluster key is flooded with crafted encrypted frames from an unknown peer (driving the cluster-key decrypt path) while the bearer's netns is repeatedly torn down. The completion must run asynchronously to outlive tipc_crypto_stop(); on x86 the stock aesni gcm(aes) now decrypts synchronously, so the async path was exercised via cryptd offload. The unguarded aead->crypto dereference in tipc_aead_decrypt_done() is the unpatched upstream path; tipc_aead_decrypt() still lacks maybe_get_net(aead->crypto->net), so the completion can outlive the free on any config where crypto_aead_decrypt() goes async. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: fc1b6d6de220 ("tipc: introduce TIPC encryption & authentication") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Alexander Lobakin Reviewed-by: Tung Nguyen Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260617075818.37431-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/tipc/crypto.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index a3f9ca28c3d53..d388fe8e477e2 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -950,12 +950,20 @@ static int tipc_aead_decrypt(struct net *net, struct tipc_aead *aead, goto exit; } + /* Get net to avoid freed tipc_crypto when delete namespace */ + if (!maybe_get_net(net)) { + tipc_bearer_put(b); + rc = -ENODEV; + goto exit; + } + /* Now, do decrypt */ rc = crypto_aead_decrypt(req); if (rc == -EINPROGRESS || rc == -EBUSY) return rc; tipc_bearer_put(b); + put_net(net); exit: kfree(ctx); @@ -993,6 +1001,7 @@ static void tipc_aead_decrypt_done(void *data, int err) } tipc_bearer_put(b); + put_net(net); } static inline int tipc_ehdr_size(struct tipc_ehdr *ehdr) From 90e254f18b8c224460082329dd5c42fd30995c2f Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 0080/3297] LoongArch: Report dying CPU to RCU in stop_this_cpu() commit f2539c56c74691e7a88af6372ba2b48c06ed2fe4 upstream. This is a port of MIPS commit 9f3f3bdc6d9dac1 ("MIPS: smp: report dying CPU to RCU in stop_this_cpu()"). smp_send_stop() parks all secondary CPUs in stop_this_cpu(). And the function marks the CPU offline for the scheduler via set_cpu_online(false) but never informs RCU, so RCU keeps expecting a quiescent state from CPUs that are now spinning forever with interrupts disabled. As long as nothing waits for an RCU grace period after smp_send_stop() this is harmless, which is why it went unnoticed. However, since commit 91840be8f710370 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT"), irq_work_sync() calls synchronize_rcu() on architectures without an irq_work self-IPI, i.e. where arch_irq_work_has_interrupt() returns false. Any irq_work_sync() issued in the reboot/shutdown/halt path after smp_send_stop() then blocks on a grace period that can never complete, hanging the reboot: WARNING: CPU: 0 PID: 15 at kernel/irq_work.c:144 irq_work_queue_on ... rcu: INFO: rcu_sched detected stalls on CPUs/tasks: rcu: Offline CPU 1 blocking current GP. rcu: Offline CPU 2 blocking current GP. rcu: Offline CPU 3 blocking current GP. This issue needs some hacks to reproduce, and it was not noticed on LoongArch because arch_irq_work_has_interrupt() usually returns true. Call rcutree_report_cpu_dead() once interrupts are disabled, mirroring the generic CPU-hotplug offline path, so RCU stops waiting on the parked CPUs and grace periods can still complete. LoongArch shuts down all CPUs here without going through the CPU-hotplug mechanism, so this report is not otherwise issued. Cc: Fixes: 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") Reviewed-by: Guo Ren Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/smp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 46036d98da75b..5d6a60294fb2d 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -688,6 +688,7 @@ static void stop_this_cpu(void *dummy) set_cpu_online(smp_processor_id(), false); calculate_cpu_foreign_map(); local_irq_disable(); + rcutree_report_cpu_dead(); while (true); } From 200e7637f4d6a1342987045eea72641524f909dc Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Mon, 18 May 2026 13:10:36 +0000 Subject: [PATCH 0081/3297] pNFS: Fix use-after-free in pnfs_update_layout() commit 13e198a90ca4050f4bee8a3f23680389a6563ccc upstream. When hitting the NFS_LAYOUT_RETURN branch in pnfs_update_layout(), the code calls pnfs_prepare_to_retry_layoutget(lo). If it succeeds, pnfs_put_layout_hdr(lo) is called before trace_pnfs_update_layout(), which still references 'lo'. This results in a use-after-free when the tracepoint accesses lo's fields. Fix this by moving the tracepoint call before pnfs_put_layout_hdr(lo). Fixes: 2c8d5fc37fe2 ("pNFS: Stricter ordering of layoutget and layoutreturn") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/pnfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index b3cb5ee9d821e..d3c1f49f39242 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -2217,11 +2217,11 @@ pnfs_update_layout(struct inode *ino, dprintk("%s wait for layoutreturn\n", __func__); lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo)); if (!IS_ERR(lseg)) { - pnfs_put_layout_hdr(lo); dprintk("%s retrying\n", __func__); trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, PNFS_UPDATE_LAYOUT_RETRY); + pnfs_put_layout_hdr(lo); goto lookup_again; } trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, From b3a3831b2eb884641906fc5e46207b205b6aea13 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Thu, 18 Jun 2026 10:13:52 +0800 Subject: [PATCH 0082/3297] irqchip/imgpdc: Fix resource leak, add missing chained handler cleanup on remove commit 37738fdf2ab1e504d1c63ce5bc0aeb6452d8f057 upstream. The driver allocates domain generic chips using irq_alloc_domain_generic_chips() during probe and sets up chained handlers using irq_set_chained_handler_and_data(). However, on driver removal, the generic chips are not freed and the chained handlers are not removed. The generic chips remain on the global gc_list and may later be accessed by generic interrupt chip suspend, resume, or shutdown callbacks after the driver has been removed, potentially resulting in a use-after-free and kernel crash. The chained handlers that were installed in probe for peripheral and syswake interrupts are also left dangling, which can lead to spurious interrupts accessing freed memory. Fix these issues by: - Setting IRQ_DOMAIN_FLAG_DESTROY_GC flag in domain->flags, so the core code automatically removes generic chips when irq_domain_remove() is called - Clearing all chained handlers with NULL in pdc_intc_remove() Fixes: b6ef9161e43a ("irq-imgpdc: add ImgTec PDC irqchip driver") Signed-off-by: Qingshuang Fu Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618021352.661773-1-fffsqian@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-imgpdc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c index e9ef2f5a7207e..4feef4ab5fecc 100644 --- a/drivers/irqchip/irq-imgpdc.c +++ b/drivers/irqchip/irq-imgpdc.c @@ -378,6 +378,7 @@ static int pdc_intc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot add IRQ domain\n"); return -ENOMEM; } + priv->domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC; /* * Set up 2 generic irq chips with 2 chip types. @@ -465,6 +466,11 @@ static void pdc_intc_remove(struct platform_device *pdev) { struct pdc_intc_priv *priv = platform_get_drvdata(pdev); + for (unsigned int i = 0; i < priv->nr_perips; ++i) + irq_set_chained_handler_and_data(priv->perip_irqs[i], NULL, NULL); + + irq_set_chained_handler_and_data(priv->syswake_irq, NULL, NULL); + irq_domain_remove(priv->domain); } From 369496d885b4cf6e8647cf4dc5cf3ac68fdf37a1 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 8 Apr 2026 15:45:34 +0000 Subject: [PATCH 0083/3297] fpga: region: fix use-after-free in child_regions_with_firmware() commit 54f3c5643ec523a04b6ec0e7c19eb10f5ebebdd3 upstream. Move of_node_put(child_region) after the error print to avoid accessing freed memory when pr_err() references child_region. Fixes: 0fa20cdfcc1f ("fpga: fpga-region: device tree control for FPGA") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang [ Yilun: Fix the Fixes tag ] Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260408154534.404327-1-vulab@iscas.ac.cn Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/of-fpga-region.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index caa091224dc54..9107a5b461d35 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -168,11 +168,10 @@ static int child_regions_with_firmware(struct device_node *overlay) fpga_region_of_match); } - of_node_put(child_region); - if (ret) pr_err("firmware-name not allowed in child FPGA region: %pOF", child_region); + of_node_put(child_region); return ret; } From 104d100212396801f1d9d388282f746e23e2bfd6 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 1 Jun 2026 14:32:47 -0400 Subject: [PATCH 0084/3297] rpmsg: char: Fix use-after-free on probe error path commit 1ff3f528e67d20e2b1483dcaba899dc7832b2e6b upstream. rpmsg_chrdev_probe() stores the newly allocated eptdev in the default endpoint's priv pointer before calling rpmsg_chrdev_eptdev_add(). If rpmsg_chrdev_eptdev_add() then fails, its error path frees eptdev while the default endpoint may still dispatch callbacks with the stale priv pointer. Avoid publishing eptdev through the default endpoint until rpmsg_chrdev_eptdev_add() succeeds. Messages received before the priv pointer is published should be ignored by rpmsg_ept_cb(). Flow-control updates can hit rpmsg_ept_flow_cb() in the same window, so make both callbacks return success when priv is NULL. Fixes: bc69d1066569 ("rpmsg: char: Introduce the "rpmsg-raw" channel") Signed-off-by: Yuho Choi Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260601183247.1962010-1-dbgh9129@gmail.com Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/rpmsg/rpmsg_char.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 96fcdd2d7093c..d918cb30db9b0 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -104,6 +104,9 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, struct rpmsg_eptdev *eptdev = priv; struct sk_buff *skb; + if (!eptdev) + return 0; + skb = alloc_skb(len, GFP_ATOMIC); if (!skb) return -ENOMEM; @@ -124,6 +127,9 @@ static int rpmsg_ept_flow_cb(struct rpmsg_device *rpdev, void *priv, bool enable { struct rpmsg_eptdev *eptdev = priv; + if (!eptdev) + return 0; + eptdev->remote_flow_restricted = enable; eptdev->remote_flow_updated = true; @@ -490,6 +496,7 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) struct rpmsg_channel_info chinfo; struct rpmsg_eptdev *eptdev; struct device *dev = &rpdev->dev; + int ret; memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); chinfo.src = rpdev->src; @@ -502,13 +509,17 @@ static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) /* Set the default_ept to the rpmsg device endpoint */ eptdev->default_ept = rpdev->ept; + ret = rpmsg_chrdev_eptdev_add(eptdev, chinfo); + + if (ret) + return ret; /* * The rpmsg_ept_cb uses *priv parameter to get its rpmsg_eptdev context. - * Storedit in default_ept *priv field. + * Stored it in default_ept *priv field. */ eptdev->default_ept->priv = eptdev; - return rpmsg_chrdev_eptdev_add(eptdev, chinfo); + return 0; } static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev) From 4cd57ebee395041099fcdfcabb00749ce38d8b27 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 24 May 2026 19:12:48 +0800 Subject: [PATCH 0085/3297] ocfs2: reject oversized group bitmap descriptors commit 9bd541e09dffff27e5bec0f9f45b0228173a5375 upstream. ocfs2_validate_gd_parent() only bounds bg_bits against the parent allocator's chain geometry. A malicious descriptor can still claim a bg_size/bg_bits pair that exceeds the bitmap bytes that physically fit in the group descriptor block, so later bitmap scans and bit updates can run past bg_bitmap. Add a physical-cap check based on ocfs2_group_bitmap_size() for the parent allocator type and reject descriptors whose bg_size or bg_bits exceed that capacity. Keep the existing chain geometry check so both the on-disk bitmap layout and the allocator metadata must agree before the descriptor is used. Validation reproduced this kernel report: KASAN use-after-free in _find_next_bit+0x7f/0xc0 Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) _find_next_bit+0x7f/0xc0 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) ocfs2_find_max_contig_free_bits+0x35/0x70 (fs/ocfs2/suballoc.c:1375) ocfs2_block_group_set_bits+0x472/0x4b0 (fs/ocfs2/suballoc.c:1457) ocfs2_cluster_group_search+0x16b/0x440 (fs/ocfs2/suballoc.c:86) ocfs2_bg_discontig_fix_result+0x1ef/0x230 (fs/ocfs2/suballoc.c:1786) ocfs2_search_chain+0x8f8/0x10a0 (fs/ocfs2/suballoc.c:1886) get_page_from_freelist+0x70e/0x2370 (?:?) lock_release+0xc6/0x290 (?:?) do_raw_spin_unlock+0x9a/0x100 (?:?) kasan_unpoison+0x27/0x60 (?:?) __bfs+0x147/0x240 (?:?) get_page_from_freelist+0x83d/0x2370 (?:?) ocfs2_claim_suballoc_bits+0x38c/0xe70 (fs/ocfs2/suballoc.c:96) sched_domains_numa_masks_clear+0x70/0xd0 (?:?) check_irq_usage+0xe8/0xb70 (?:?) __ocfs2_claim_clusters+0x18d/0x4c0 (fs/ocfs2/suballoc.c:2497) check_path+0x24/0x50 (?:?) rcu_is_watching+0x20/0x50 (?:?) check_prev_add+0xfd/0xd00 (?:?) ocfs2_add_clusters_in_btree+0x17d/0x810 (fs/ocfs2/suballoc.c:?) __folio_batch_add_and_move+0x1f5/0x3d0 (?:?) ocfs2_add_inode_data+0xd9/0x120 (fs/ocfs2/suballoc.c:?) filemap_add_folio+0x105/0x1f0 (?:?) ocfs2_write_begin_nolock+0x29f7/0x2f80 (fs/ocfs2/suballoc.c:3043) ocfs2_read_inode_block+0xb5/0x110 (fs/ocfs2/suballoc.c:?) down_write+0xf5/0x180 (?:?) ocfs2_write_begin+0x180/0x240 (fs/ocfs2/suballoc.c:?) __mark_inode_dirty+0x758/0x9a0 (?:?) inode_to_bdi+0x41/0x90 (?:?) balance_dirty_pages_ratelimited_flags+0xf8/0x1d0 (?:?) generic_perform_write+0x252/0x440 (?:?) mnt_put_write_access_file+0x16/0x70 (?:?) file_update_time_flags+0xe4/0x200 (?:?) ocfs2_file_write_iter+0x80a/0x1320 (fs/ocfs2/suballoc.c:?) lock_acquire+0x184/0x2f0 (?:?) ksys_write+0xd2/0x170 (?:?) apparmor_file_permission+0xf5/0x310 (?:?) read_zero+0x8d/0x140 (?:?) lock_is_held_type+0x8f/0x100 (?:?) Link: https://lore.kernel.org/20260524111248.1429884-1-rollkingzzc@gmail.com Fixes: ccd979bdbce9 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/suballoc.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index e93fc842bb203..a752ea59a1675 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -205,8 +205,16 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, int resize) { unsigned int max_bits; + unsigned int max_bitmap_bits; + unsigned int max_bitmap_size; + int suballocator; struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data; + suballocator = le64_to_cpu(di->i_blkno) != OCFS2_SB(sb)->bitmap_blkno; + max_bitmap_size = ocfs2_group_bitmap_size(sb, suballocator, + OCFS2_SB(sb)->s_feature_incompat); + max_bitmap_bits = max_bitmap_size * 8; + if (di->i_blkno != gd->bg_parent_dinode) { do_error("Group descriptor #%llu has bad parent pointer (%llu, expected %llu)\n", (unsigned long long)bh->b_blocknr, @@ -214,6 +222,20 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, (unsigned long long)le64_to_cpu(di->i_blkno)); } + if (le16_to_cpu(gd->bg_size) > max_bitmap_size) { + do_error("Group descriptor #%llu has bitmap size %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_size), + max_bitmap_size); + } + + if (le16_to_cpu(gd->bg_bits) > max_bitmap_bits) { + do_error("Group descriptor #%llu has bit count %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_bits), + max_bitmap_bits); + } + max_bits = le16_to_cpu(di->id2.i_chain.cl_cpg) * le16_to_cpu(di->id2.i_chain.cl_bpc); if (le16_to_cpu(gd->bg_bits) > max_bits) { do_error("Group descriptor #%llu has bit count of %u\n", From 6dbe9443d9f5f7fb6d319a7b77108853ae6c6bea Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 28 May 2026 13:39:16 +0800 Subject: [PATCH 0086/3297] 9p: avoid putting oldfid in p9_client_walk() error path commit 1a3860d46e3eb47dbd60339783cdad7904486b9f upstream. When p9_client_walk() is called with clone set to false, fid aliases oldfid. If the walk subsequently fails after the request has been sent, the error path jumps to clunk_fid, which currently calls p9_fid_put(fid) unconditionally. This drops a reference to oldfid even though ownership of oldfid remains with the caller. If this is the last reference, oldfid can be clunked and destroyed while the caller still expects it to be valid. A later use or put of oldfid can then trigger a use-after-free or refcount underflow. Fix this by only putting fid in the clunk_fid error path when it does not alias oldfid, matching the existing guard in the error path below. This can be triggered when a multi-component walk is split into multiple p9_client_walk() calls and a later non-cloning walk fails. A reproducer and refcount warning logs are available on request. Fixes: b48dbb998d70 ("9p fid refcount: add p9_fid_get/put wrappers") Cc: stable@vger.kernel.org Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: GLM 5.1 Signed-off-by: Yizhou Zhao Message-ID: <20260528053918.53550-1-zhaoyz24@mails.tsinghua.edu.cn> Signed-off-by: Dominique Martinet Signed-off-by: Greg Kroah-Hartman --- net/9p/client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/9p/client.c b/net/9p/client.c index 5c1ca57ccd285..9c9d249dabaea 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1215,7 +1215,8 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname, clunk_fid: kfree(wqids); - p9_fid_put(fid); + if (fid != oldfid) + p9_fid_put(fid); fid = NULL; error: From f9b57a0015c241274651f4b36627f56b1b5a8651 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Mon, 8 Jun 2026 09:37:29 +0000 Subject: [PATCH 0087/3297] MIPS: smp: report dying CPU to RCU in stop_this_cpu() commit 9f3f3bdc6d9dac1a5a8262ee7ad0f2ff1527a7e7 upstream. smp_send_stop() parks all secondary CPUs in stop_this_cpu(). The function marks the CPU offline for the scheduler via set_cpu_online(false) but never informs RCU, so RCU keeps expecting a quiescent state from CPUs that are now spinning forever with interrupts disabled. As long as nothing waits for an RCU grace period after smp_send_stop() this is harmless, which is why it went unnoticed. Since commit 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") however, irq_work_sync() calls synchronize_rcu() on architectures without an irq_work self-IPI, i.e. where arch_irq_work_has_interrupt() returns false. That is the asm-generic default used by MIPS. Any irq_work_sync() issued in the reboot/shutdown path after smp_send_stop() then blocks on a grace period that can never complete, hanging the reboot: WARNING: CPU: 0 PID: 15 at kernel/irq_work.c:144 irq_work_queue_on ... rcu: INFO: rcu_sched detected stalls on CPUs/tasks: rcu: Offline CPU 1 blocking current GP. rcu: Offline CPU 2 blocking current GP. rcu: Offline CPU 3 blocking current GP. This issue was noticed on several Realtek MIPS switch SoCs (MIPS interAptiv) and came up during kernel bump downstream in OpenWrt from 6.18.33 to 6.18.34, after the backport of the patch to the 6.18 stable branch. The patch also has been backported all the way back to 6.1. Call rcutree_report_cpu_dead() once interrupts are disabled, mirroring the generic CPU-hotplug offline path, so RCU stops waiting on the parked CPUs and grace periods can still complete. MIPS shuts down all CPUs here without going through the CPU-hotplug mechanism, so this report is not otherwise issued. Reporting a dying CPU to RCU outside the regular hotplug offline path is not unprecedented: arm64 does the same in cpu_die_early(). There it is an exception for a CPU that was coming online and is aborting bringup, rather than the default shutdown action as on MIPS. Fixes: 91840be8f710 ("irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT") CC: stable@vger.kernel.org Signed-off-by: Jonas Jelonek Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/smp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 4868e79f3b30e..0f28b4a62e72e 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -422,6 +423,7 @@ static void stop_this_cpu(void *dummy) set_cpu_online(smp_processor_id(), false); calculate_cpu_foreign_map(); local_irq_disable(); + rcutree_report_cpu_dead(); while (1); } From e36095d8d922bb26ce860231aacf0cd14edea07c Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sat, 6 Jun 2026 23:44:52 +0900 Subject: [PATCH 0088/3297] KVM: x86: hyper-v: Bound the bank index when querying sparse banks commit 4721f8160f17554b003e8928bb61e6c9b2fe92a3 upstream. When checking if a VP ID is included in a sparse bank set, explicitly check that the ID can actually be contained in a sparse bank (the TLFS allows for a maximum of 64 banks of 64 vCPUs each). When handling a paravirtual TLB flush for L2, the VP ID is copied verbatim from the enlightened VMCS, without any bounds check, i.e. isn't guaranteed to be under the limit of 4096. Failure to check the bounds of the VP ID leads to an out-of-bounds read when testing the sparse bank, and super strictly speaking could lead to KVM performing an unnecessary TLB flush for an L2 vCPU. ================================================================== BUG: KASAN: use-after-free in hv_is_vp_in_sparse_set+0x85/0x100 [kvm] Read of size 8 at addr ffff88811ba5f598 by task hyperv_evmcs/2802 CPU: 12 UID: 1000 PID: 2802 Comm: hyperv_evmcs Not tainted 7.1.0-rc2 #7 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 Call Trace: dump_stack_lvl+0x51/0x60 print_report+0xcb/0x5d0 kasan_report+0xb4/0xe0 kasan_check_range+0x35/0x1b0 hv_is_vp_in_sparse_set+0x85/0x100 [kvm] kvm_hv_flush_tlb+0xe9e/0x16c0 [kvm] kvm_hv_hypercall+0xe6b/0x1e60 [kvm] vmx_handle_exit+0x485/0x1b60 [kvm_intel] kvm_arch_vcpu_ioctl_run+0x22e3/0x5070 [kvm] kvm_vcpu_ioctl+0x5d0/0x10c0 [kvm] __x64_sys_ioctl+0x129/0x1a0 do_syscall_64+0xb9/0xcf0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x7f0e62d1a9bf The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffffffffffffffff pfn:0x11ba5f flags: 0x4000000000000000(zone=1) raw: 4000000000000000 0000000000000000 00000000ffffffff 0000000000000000 raw: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88811ba5f480: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88811ba5f500: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff >ffff88811ba5f580: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ^ ffff88811ba5f600: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88811ba5f680: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ================================================================== Disabling lock debugging due to kernel taint Opportunistically add a compile time assertion to ensure the maximum number of sparse banks exactly matches the number of possible bits in the passed in mask. Cc: stable@vger.kernel.org Fixes: c58a318f6090 ("KVM: x86: hyper-v: L2 TLB flush") Signed-off-by: Hyunwoo Kim Reviewed-by: Vitaly Kuznetsov Link: https://patch.msgid.link/aiQyZIJtO-2Aj_xN@v4bel [sean: add KASAN splat, drop comment, add assert, massage changelog] Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/hyperv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index f279561154bc9..4ebb3c40c6bbc 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -1839,6 +1839,11 @@ static bool hv_is_vp_in_sparse_set(u32 vp_id, u64 valid_bank_mask, u64 sparse_ba int valid_bit_nr = vp_id / HV_VCPUS_PER_SPARSE_BANK; unsigned long sbank; + BUILD_BUG_ON(BITS_PER_TYPE(valid_bank_mask) != HV_MAX_SPARSE_VCPU_BANKS); + + if (valid_bit_nr >= HV_MAX_SPARSE_VCPU_BANKS) + return false; + if (!test_bit(valid_bit_nr, (unsigned long *)&valid_bank_mask)) return false; From 720949ed666f34ff28ffdfe1471a5861d1e41fdf Mon Sep 17 00:00:00 2001 From: Ashutosh Desai Date: Fri, 1 May 2026 13:35:32 -0700 Subject: [PATCH 0089/3297] KVM: SVM: Fix page overflow in sev_dbg_crypt() for ENCRYPT path commit 78ee2d50185a037b3d2452a97f3dad69c3f7f389 upstream. In sev_dbg_crypt(), the per-iteration transfer length is bounded by the source page offset (PAGE_SIZE - s_off) but not by the destination page offset (PAGE_SIZE - d_off). When d_off > s_off, the encrypt path (__sev_dbg_encrypt_user) performs a read-modify-write using a single-page intermediate buffer (dst_tpage): 1. __sev_dbg_decrypt() expands the size to round_up(len + (d_off & 15), 16) before issuing the PSP command. If len + (d_off & 15) > PAGE_SIZE, the PSP writes beyond the end of the 4096-byte dst_tpage allocation. 2. The subsequent memcpy()/copy_from_user() into page_address(dst_tpage) + (d_off & 15) of 'len' bytes overflows by up to 15 bytes under the same condition. Trigger example: s_off = 0, d_off = 1, debug.len = PAGE_SIZE - the PSP is instructed to write round_up(4097, 16) = 4112 bytes to a 4096-byte buffer. Fix by also bounding len by (PAGE_SIZE - d_off), the same check that sev_send_update_data() already performs for its single-page guest region. ================================================================== BUG: KASAN: slab-use-after-free in sev_dbg_crypt+0x993/0xd10 [kvm_amd] Write of size 4095 at addr ff110062293bb009 by task sev_dbg_test/228214 CPU: 96 UID: 0 PID: 228214 Comm: sev_dbg_test Tainted: G U W 7.0.0-smp--5ce9b0c48211-dbg #156 PREEMPTLAZY Tainted: [U]=USER, [W]=WARN Hardware name: Google Astoria/astoria, BIOS 0.20250817.1-0 08/25/2025 Call Trace: dump_stack_lvl+0x54/0x70 print_report+0xbc/0x260 kasan_report+0xa2/0xd0 kasan_check_range+0x25f/0x2c0 __asan_memcpy+0x40/0x70 sev_dbg_crypt+0x993/0xd10 [kvm_amd] sev_mem_enc_ioctl+0x33c/0x450 [kvm_amd] kvm_vm_ioctl+0x65d/0x6d0 [kvm] __se_sys_ioctl+0xb2/0x100 do_syscall_64+0xe8/0x870 entry_SYSCALL_64_after_hwframe+0x4b/0x53 The buggy address belongs to the physical page: page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x7fe72b6a0 pfn:0x62293bb memcg:ff11000112827d82 flags: 0x1400000000000000(node=1|zone=1) raw: 1400000000000000 0000000000000000 dead000000000122 0000000000000000 raw: 00000007fe72b6a0 0000000000000000 00000001ffffffff ff11000112827d82 page dumped because: kasan: bad access detected Memory state around the buggy address: ff110062293bbf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff110062293bbf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ff110062293bc000: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ^ ff110062293bc080: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ff110062293bc100: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ================================================================== Disabling lock debugging due to kernel taint Fixes: 24f41fb23a39 ("KVM: SVM: Add support for SEV DEBUG_DECRYPT command") Fixes: 7d1594f5d94b ("KVM: SVM: Add support for SEV DEBUG_ENCRYPT command") Cc: stable@vger.kernel.org Signed-off-by: Ashutosh Desai [sean: add sample KASAN splat, Fixes, and stable@] Link: https://patch.msgid.link/20260501203537.2120074-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 6d4574eb8a4ce..8b807f8eb0672 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1340,6 +1340,7 @@ static int sev_dbg_crypt(struct kvm *kvm, struct kvm_sev_cmd *argp, bool dec) s_off = vaddr & ~PAGE_MASK; d_off = dst_vaddr & ~PAGE_MASK; len = min_t(size_t, (PAGE_SIZE - s_off), size); + len = min_t(size_t, len, PAGE_SIZE - d_off); if (dec) ret = __sev_dbg_decrypt_user(kvm, From 2205275be9be981e70ff29610b0117d8853fac70 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 7 Apr 2026 07:30:25 +0000 Subject: [PATCH 0090/3297] power: reset: linkstation-poweroff: fix use-after-free in the linkstation_poweroff_init() commit 8eec545cde69e46e9a1d2b7d915ce4f5df85b3bd upstream. Move of_node_put(dn) after the of_match_node() call, which still needs the node pointer. The node reference is correctly released after use. Fixes: e2f471efe1d6 ("power: reset: linkstation-poweroff: prepare for new devices") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260407073025.271865-1-vulab@iscas.ac.cn Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/reset/linkstation-poweroff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/reset/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c index 02f5fdb8ffc4c..e56d75bfcc43f 100644 --- a/drivers/power/reset/linkstation-poweroff.c +++ b/drivers/power/reset/linkstation-poweroff.c @@ -163,10 +163,10 @@ static int __init linkstation_poweroff_init(void) dn = of_find_matching_node(NULL, ls_poweroff_of_match); if (!dn) return -ENODEV; - of_node_put(dn); match = of_match_node(ls_poweroff_of_match, dn); cfg = match->data; + of_node_put(dn); dn = of_find_node_by_name(NULL, cfg->mdio_node_name); if (!dn) From 3b33dbb43e21a8d1248dbe906d9e27f640a6001a Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:45 +0800 Subject: [PATCH 0091/3297] riscv: mm: Extract helper mark_new_valid_map() commit 9ee25d0a70ff4494b4e1d266b962d0a574ef318a upstream. In preparation of a future patch using the same mechanism for non-vmalloc addresses, extract the mark_new_valid_map() helper from flush_cache_vmap(). No functional change intended. Cc: stable@vger.kernel.org Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-1-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/cacheflush.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 0092513c3376c..b1a2ac6657926 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -43,20 +43,23 @@ do { \ #ifdef CONFIG_64BIT extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; extern char _end[]; +static inline void mark_new_valid_map(void) +{ + int i; + + /* + * We don't care if concurrently a cpu resets this value since + * the only place this can happen is in handle_exception() where + * an sfence.vma is emitted. + */ + for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) + new_vmalloc[i] = -1ULL; +} #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) { - if (is_vmalloc_or_module_addr((void *)start)) { - int i; - - /* - * We don't care if concurrently a cpu resets this value since - * the only place this can happen is in handle_exception() where - * an sfence.vma is emitted. - */ - for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) - new_vmalloc[i] = -1ULL; - } + if (is_vmalloc_or_module_addr((void *)start)) + mark_new_valid_map(); } #define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end) #endif From 7643e5622994f9b207445f9cd89a02223ce6a99f Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:46 +0800 Subject: [PATCH 0092/3297] riscv: kfence: Call mark_new_valid_map() for kfence_unprotect() commit 8d6c8c40e733b3fcaf92fed0a078bba2f6941a3b upstream. In kfence_protect_page(), which kfence_unprotect() calls, we cannot send IPIs to other CPUs to ask them to flush TLB. This may lead to those CPUs spuriously faulting on a recently allocated kfence object despite it being valid, leading to false positive use-after-free reports. Fix this by calling mark_new_valid_map() so that the page fault handling code path notices the spurious fault and flushes TLB then retries the access. Update the comment in handle_exception to indicate that new_valid_map_cpus_check also handles kfence_unprotect() spurious faults. Note that kfence_protect() has the same stale TLB entries problem, but that leads to false negatives, which is fine with kfence. Cc: stable@vger.kernel.org Reported-by: Yanko Kaneti Fixes: b3431a8bb336 ("riscv: Fix IPIs usage in kfence_protect_page()") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-2-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/kfence.h | 7 +++++-- arch/riscv/kernel/entry.S | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/riscv/include/asm/kfence.h b/arch/riscv/include/asm/kfence.h index d08bf7fb3aee6..29cb3a6ee113d 100644 --- a/arch/riscv/include/asm/kfence.h +++ b/arch/riscv/include/asm/kfence.h @@ -6,6 +6,7 @@ #include #include #include +#include #include static inline bool arch_kfence_init_pool(void) @@ -17,10 +18,12 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect) { pte_t *pte = virt_to_kpte(addr); - if (protect) + if (protect) { set_pte(pte, __pte(pte_val(ptep_get(pte)) & ~_PAGE_PRESENT)); - else + } else { set_pte(pte, __pte(pte_val(ptep_get(pte)) | _PAGE_PRESENT)); + mark_new_valid_map(); + } preempt_disable(); local_flush_tlb_kernel_range(addr, addr + PAGE_SIZE); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 9b9dec6893b81..e7bd03a9ced53 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -107,8 +107,10 @@ SYM_CODE_START(handle_exception) #ifdef CONFIG_64BIT /* - * The RISC-V kernel does not eagerly emit a sfence.vma after each - * new vmalloc mapping, which may result in exceptions: + * The RISC-V kernel does not flush TLBs on all CPUS after each new + * vmalloc mapping or kfence_unprotect(), which may result in + * exceptions: + * * - if the uarch caches invalid entries, the new mapping would not be * observed by the page table walker and an invalidation is needed. * - if the uarch does not cache invalid entries, a reordered access From eea16b6f805c0b1fb2f72f0f771088ea45356956 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 24 Jun 2026 23:13:12 -0500 Subject: [PATCH 0093/3297] fbdev: Fix fb_new_modelist to prevent null-ptr-deref in fb_videomode_to_var commit 7f08fc10fa3d3366dc3af723970bd03d7d6d10e3 upstream. info->var, a framebuffer's current mode, is expected to have a matching entry in info->modelist. var_to_display() relies on this and treats a failed fb_match_mode() as "This should not happen". fb_set_var() keeps it true by adding the mode to the list on every change, and do_register_framebuffer() does the same at registration. store_modes() replaces the modelist from userspace. fb_new_modelist() validates the new modes but does not check that info->var still has a match. It relies on fbcon_new_modelist() to re-point consoles, but that only handles consoles mapped to the framebuffer. With fbcon unbound there are none, so info->var is left describing a mode that is no longer in the list. A later console takeover runs var_to_display(), where fb_match_mode() returns NULL and leaves fb_display[i].mode NULL. fbcon_switch() passes it to display_to_var(), and fb_videomode_to_var() dereferences the NULL mode. Keep the current mode in the list in fb_new_modelist(), the same way fb_set_var() does. Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ian Bridges Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbmem.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index eff757ebbed14..30a2c0d47e5c8 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -734,6 +734,18 @@ int fb_new_modelist(struct fb_info *info) if (list_empty(&info->modelist)) return 1; + /* + * The new modelist may not contain the current mode (info->var), and + * fbcon_new_modelist() below only re-points consoles mapped to this + * framebuffer. Add the current mode here so info->var keeps a match + * even when fbcon is unbound. + */ + if (!fb_match_mode(&info->var, &info->modelist)) { + fb_var_to_videomode(&mode, &info->var); + if (fb_add_videomode(&mode, &info->modelist)) + return 1; + } + fbcon_new_modelist(info); return 0; From 4d418cf8daf57e454b4d855bf9b2419fd8e6a540 Mon Sep 17 00:00:00 2001 From: Tuo Li Date: Wed, 10 Jun 2026 10:50:14 +0800 Subject: [PATCH 0094/3297] fbdev: modedb: fix a possible UAF in fb_find_mode() commit 85b6256469cebdac395e7447147e06b2e151014f upstream. If mode_option is NULL, it is assigned from mode_option_buf: if (!mode_option) { fb_get_options(NULL, &mode_option_buf); mode_option = mode_option_buf; } Later, name is assigned from mode_option: const char *name = mode_option; However, mode_option_buf is freed before name is no longer used: kfree(mode_option_buf); while name is still accessed by: if ((name_matches(db[i], name, namelen) || Since name aliases mode_option_buf, this may result in a use-after-free. Fix this by extending the lifetime of mode_option_buf until the end of the function by using scope-based resource management for cleanup. Signed-off-by: Tuo Li Cc: stable@vger.kernel.org # v6.5+ Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/modedb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 53a610948c4a5..25e8a83c571e1 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -626,7 +626,7 @@ int fb_find_mode(struct fb_var_screeninfo *var, const struct fb_videomode *default_mode, unsigned int default_bpp) { - char *mode_option_buf = NULL; + char *mode_option_buf __free(kfree) = NULL; int i; /* Set up defaults */ @@ -724,7 +724,6 @@ int fb_find_mode(struct fb_var_screeninfo *var, res_specified = 1; } done: - kfree(mode_option_buf); if (cvt) { struct fb_videomode cvt_mode; int ret; From fc6aa9bdbae60cdd8ae2358c7ee87c6fdb1132a4 Mon Sep 17 00:00:00 2001 From: Steffen Persvold Date: Fri, 12 Jun 2026 18:40:41 +0200 Subject: [PATCH 0095/3297] fbdev: modedb: Fix misaligned fields in the 1920x1080-60 mode commit d894c48a57d78206e4df9c90d4acfaf39394806a upstream. The 1920x1080@60 modedb entry has one too many initializers before its sync field: a stray "0" occupies the sync slot, which shifts the remaining values by one field. The entry therefore decodes as sync = 0, vmode = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT (0x3, i.e. FB_VMODE_INTERLACED | FB_VMODE_DOUBLE), and flag = FB_VMODE_NONINTERLACED, instead of the intended sync = positive H/V, vmode = non-interlaced. fb_find_mode() then returns a 1920x1080 mode flagged as interlaced + doublescan with active-low syncs. Drivers that honour var->vmode and var->sync when programming display timing enable doublescan and the wrong sync polarity, corrupting the output. Drop the stray initializer so sync and vmode hold their intended values (positive H/V sync, non-interlaced), matching the adjacent 1920x1200 entry. Fixes: c8902258b2b8 ("fbdev: modedb: Add 1920x1080 at 60 Hz video mode") Cc: stable@vger.kernel.org Signed-off-by: Steffen Persvold Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/modedb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 25e8a83c571e1..0b1f3c5bbcdb3 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -259,7 +259,7 @@ static const struct fb_videomode modedb[] = { FB_VMODE_DOUBLE }, /* 1920x1080 @ 60 Hz, 67.3 kHz hsync */ - { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, 0, + { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, From 6a946038f2a5a8c29048c6af369d4e391448a5c5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:12 +0200 Subject: [PATCH 0096/3297] i2c: core: fix adapter registration race commit ba14d7cf2fe7284610a29854bdff22b2537d3ce6 upstream. Adapters can be looked up based on their id using i2c_get_adapter() which takes a reference to the embedded struct device. Make sure that the adapter (including its struct device) has been initialised before adding it to the IDR to avoid accessing uninitialised data which could, for example, lead to NULL-pointer dereferences or use-after-free. Note that the i2c-dev chardev, which is registered from a bus notifier, currently uses i2c_get_adapter() so the adapter needs to be added to the IDR before registration. Fixes: 6e13e6418418 ("i2c: Add i2c_add_numbered_adapter()") Cc: stable@vger.kernel.org # 2.6.22 Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index ae7e9c8b65a65..05b315405639b 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1570,6 +1570,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, adap, adap->nr); + mutex_unlock(&core_lock); + res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); @@ -1628,7 +1632,7 @@ static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) int id; mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); + id = idr_alloc(&i2c_adapter_idr, NULL, adap->nr, adap->nr + 1, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) return id == -ENOSPC ? -EBUSY : id; @@ -1662,7 +1666,7 @@ int i2c_add_adapter(struct i2c_adapter *adapter) } mutex_lock(&core_lock); - id = idr_alloc(&i2c_adapter_idr, adapter, + id = idr_alloc(&i2c_adapter_idr, NULL, __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); mutex_unlock(&core_lock); if (WARN(id < 0, "couldn't get idr")) From c8a24effd96d4779e2ad779654682304491c55a5 Mon Sep 17 00:00:00 2001 From: Guannan Wang Date: Thu, 21 May 2026 16:03:32 +0800 Subject: [PATCH 0097/3297] NFSD: Fix SECINFO_NO_NAME decode error cleanup commit 9e18e83b8846a5c3fe13fc8a464b4865d33996c6 upstream. nfsd4_decode_secinfo_no_name() currently initializes sin_exp after decoding sin_style. If the XDR stream is truncated, the decoder returns nfserr_bad_xdr before sin_exp is initialized. Since commit 3fdc54646234 ("NFSD: Reduce amount of struct nfsd4_compoundargs that needs clearing"), the inline iops array is not cleared between RPC calls. A failed SECINFO_NO_NAME decode can therefore leave sin_exp holding stale union contents from a previous operation. The error response path still invokes nfsd4_secinfo_no_name_release(), which calls exp_put() on a non-NULL sin_exp. Initialize sin_exp before the first failable decode step, matching nfsd4_decode_secinfo(). Fixes: 3fdc54646234 ("NFSD: Reduce amount of struct nfsd4_compoundargs that needs clearing") Cc: stable@vger.kernel.org Signed-off-by: Guannan Wang Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4xdr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index b1b52c816ebdf..a96029c967e6b 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1871,10 +1871,11 @@ static __be32 nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) { struct nfsd4_secinfo_no_name *sin = &u->secinfo_no_name; + + sin->sin_exp = NULL; if (xdr_stream_decode_u32(argp->xdr, &sin->sin_style) < 0) return nfserr_bad_xdr; - sin->sin_exp = NULL; return nfs_ok; } From 136b416593f1349cf6f72c8e3d18f0f204ee8545 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 21 May 2026 13:51:43 -0400 Subject: [PATCH 0098/3297] nfsd: fix posix_acl leak on SETACL decode failure commit 0853ac544c590880d797b04daa33fcb72b6be0e1 upstream. nfsaclsvc_decode_setaclargs() and nfs3svc_decode_setaclargs() each call nfs_stream_decode_acl() twice, first for NFS_ACL and then for NFS_DFACL. Each successful call transfers ownership of a freshly allocated posix_acl into argp->acl_access or argp->acl_default. If the first call succeeds but the second fails, the decoder returns false and argp->acl_access is left dangling. ACLPROC2_SETACL.pc_release was wired to nfssvc_release_attrstat and ACLPROC3_SETACL.pc_release was wired to nfs3svc_release_fhandle. Both only call fh_put() and have no knowledge of the ACL fields on argp. The posix_acl_release() pairs sat at the out: labels inside nfsacld_proc_setacl() and nfsd3_proc_setacl(), but svc_process() skips pc_func when pc_decode returns false, so that cleanup is unreachable on decode failure: svc_process_common() pc_decode() /* decode_setaclargs: false */ /* pc_func skipped */ pc_release() /* fh_put only -- ACLs leaked */ The orphaned posix_acl is leaked for the lifetime of the server. Fix by adding nfsaclsvc_release_setacl() and nfs3svc_release_setacl(), which release both argp->acl_access and argp->acl_default in addition to fh_put(), and wiring them as pc_release for their respective SETACL procedures. pc_release runs on every path svc_process() takes after decode, including decode failure, so the posix_acl_release() pairs are removed from the proc functions' out: labels to keep ownership in one place. This matches the existing release_getacl() pattern used by the sibling GETACL procedures. Fixes: a257cdd0e217 ("[PATCH] NFSD: Add server support for NFSv3 ACLs.") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-7 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs2acl.c | 17 ++++++++++++----- fs/nfsd/nfs3acl.c | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 0ac538c761800..76305b86c1a95 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -131,10 +131,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) resp->status = fh_getattr(fh, &resp->stat); out: - /* argp->acl_{access,default} may have been allocated in - nfssvc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */ return rpc_success; out_drop_lock: @@ -310,6 +307,16 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp) fh_put(&resp->fh); } +static void nfsaclsvc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -343,7 +350,7 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = { .pc_func = nfsacld_proc_setacl, .pc_decode = nfsaclsvc_decode_setaclargs, .pc_encode = nfssvc_encode_attrstatres, - .pc_release = nfssvc_release_attrstat, + .pc_release = nfsaclsvc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd_attrstat), diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 7b5433bd30197..e87731380be85 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -118,10 +118,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp) out_errno: resp->status = nfserrno(error); out: - /* argp->acl_{access,default} may have been allocated in - nfs3svc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfs3svc_release_setacl. */ return rpc_success; } @@ -223,6 +220,16 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp) posix_acl_release(resp->acl_default); } +static void nfs3svc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd3_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -256,7 +263,7 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = { .pc_func = nfsd3_proc_setacl, .pc_decode = nfs3svc_decode_setaclargs, .pc_encode = nfs3svc_encode_setaclres, - .pc_release = nfs3svc_release_fhandle, + .pc_release = nfs3svc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd3_attrstat), From dba7da4835de7b17259fa23797d7dbfd36304bf0 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 21 May 2026 09:25:40 -0400 Subject: [PATCH 0099/3297] nfsd: fix inverted cp_ttl check in async copy reaper commit 0150459b05490b88b7e7378a31550a9e07b5517c upstream. nfsd4_async_copy_reaper() is supposed to keep completed async copy state around for NFSD_COPY_INITIAL_TTL (10) laundromat ticks so that OFFLOAD_STATUS can report the result, then reap the state once the countdown expires. The TTL predicate is inverted: `if (--copy->cp_ttl)` is true while ticks remain and false when the counter reaches zero. This causes the copy to be reaped on the very first tick (cp_ttl goes from 10 to 9, which is non-zero) instead of after all 10 ticks elapse. Once reaped, OFFLOAD_STATUS returns NFS4ERR_BAD_STATEID because the copy state has already been freed. Fix by negating the test so that cleanup runs when the TTL expires. Fixes: aa0ebd21df9c ("NFSD: Add nfsd4_copy time-to-live") Cc: stable@vger.kernel.org Reported-by: Chris Mason Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 8dada7ef97cb1..9d876f9d98be2 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1404,7 +1404,7 @@ void nfsd4_async_copy_reaper(struct nfsd_net *nn) list_for_each_safe(pos, next, &clp->async_copies) { copy = list_entry(pos, struct nfsd4_copy, copies); if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags)) { - if (--copy->cp_ttl) { + if (!--copy->cp_ttl) { list_del_init(©->copies); list_add(©->copies, &reaplist); } From 0f28337f54cfb7f9777bbc2018be52b7c76c620e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Wo=C5=BAniak?= Date: Thu, 21 May 2026 17:46:56 +0200 Subject: [PATCH 0100/3297] nfsd: check get_user() return when reading princhashlen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e186fa1c057f5eccb22afb1e83e34c0627085868 upstream. In __cld_pipe_inprogress_downcall(), the get_user() that reads princhashlen from the userspace cld_msg_v2 buffer does not check its return value. A failing copy leaves princhashlen with uninitialised stack contents, which are then used to drive memdup_user() and stored as princhash.len on the resulting reclaim record. The other get_user() calls in this function all check the return; only this one is missed, which is most likely a copy-paste oversight from when v2 upcalls were introduced. Mirror the existing pattern used a few lines above for namelen. namecopy is declared with __free(kfree) so the early return cleans up the already-allocated buffer automatically. Fixes: 6ee95d1c8991 ("nfsd: add support for upcall version 2") Cc: stable@vger.kernel.org Signed-off-by: Dominik Woźniak Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4recover.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index e2b9472e5c78c..3ea4b8a861631 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -800,7 +800,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, if (IS_ERR(name.data)) return PTR_ERR(name.data); name.len = namelen; - get_user(princhashlen, &ci->cc_princhash.cp_len); + if (get_user(princhashlen, &ci->cc_princhash.cp_len)) + return -EFAULT; if (princhashlen > 0) { princhash.data = memdup_user( &ci->cc_princhash.cp_data, From 017a6150106b054cc84d1b0582d97bd3a74d4281 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 22 May 2026 10:36:14 -0400 Subject: [PATCH 0101/3297] nfsd: avoid leaking pre-allocated openowner on unconfirmed retry race commit 57aee7a35bb12753057c5b65d72d1f46c0e95b07 upstream. When find_or_alloc_open_stateowner() encounters an unconfirmed owner, it calls release_openowner() and sets oo = NULL. Control then falls through past the `if (oo)` guard -- which would have freed any pre-allocated `new` -- and unconditionally executes `new = alloc_stateowner(...)`. If `new` was already allocated on a prior iteration, the pointer is silently overwritten and the previous allocation (slab object + owner name buffer) is leaked. This requires a race: two NFSv4.0 OPEN threads with the same owner string, where a concurrent thread inserts a new unconfirmed owner into the hash between retry iterations. The window is narrow but repeatable under adversarial conditions. Fix by adding `goto retry` after `oo = NULL` so the already-allocated `new` is reused on the next iteration rather than overwritten. Reported-by: Chris Mason Fixes: 23df17788c62 ("nfsd: perform all find_openstateowner_str calls in the one place.") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a9e95df2fdb68..b02fe9926b093 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -5176,6 +5176,7 @@ find_or_alloc_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, /* Replace unconfirmed owners without checking for replay. */ release_openowner(oo); oo = NULL; + goto retry; } if (oo) { if (new) From 4367afc119c51e17a616f6908772b7e2c2c4013f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 22 May 2026 12:44:19 -0400 Subject: [PATCH 0102/3297] nfsd: reset write verifier on deferred writeback errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2090b05803faab8a9fa62fbff871007862cac1b7 upstream. nfsd_vfs_write() and nfsd_commit() both call filemap_check_wb_err() to detect deferred writeback errors, but neither rotates the server's write verifier (nn->writeverf) when this check fails. Every other durable-storage-failure path in these functions calls commit_reset_write_verifier() before returning an error. The missing rotation means clients holding UNSTABLE write data under the current verifier will COMMIT, receive the unchanged verifier back, and conclude their data is durable — silently dropping data that failed writeback. This violates the UNSTABLE+COMMIT durability contract (RFC 1813 §3.3.7, RFC 8881 §18.32). Add commit_reset_write_verifier() calls at both filemap_check_wb_err() error sites, matching the pattern used by adjacent error paths in the same functions. The helper already filters -EAGAIN and -ESTALE internally, so the calls are unconditionally safe. Reported-by: Chris Mason Fixes: 555dbf1a9aac ("nfsd: Replace use of rwsem with errseq_t") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-6 Signed-off-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/vfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index e32a5fcd6ac85..2033037238780 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1264,8 +1264,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, nfsd_stats_io_write_add(nn, exp, *cnt); fsnotify_modify(file); host_err = filemap_check_wb_err(file->f_mapping, since); - if (host_err < 0) + if (host_err < 0) { + commit_reset_write_verifier(nn, rqstp, host_err); goto out_nfserr; + } if (stable && fhp->fh_use_wgather) { host_err = wait_for_concurrent_writes(file); @@ -1445,6 +1447,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf, nfsd_copy_write_verifier(verf, nn); err2 = filemap_check_wb_err(nf->nf_file->f_mapping, since); + if (err2 < 0) + commit_reset_write_verifier(nn, rqstp, err2); err = nfserrno(err2); break; case -EINVAL: From d8c90c7cc061265d5f2813a1f5c82ef2f4707e67 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 13 May 2026 12:26:56 -0400 Subject: [PATCH 0103/3297] NFSv4/flexfiles: reject zero filehandle version count commit 2c6bb3c40bc24f6aa8dfbe6fe98c3ad6389203f2 upstream. ff_layout_alloc_lseg() decodes the filehandle-version array count from the flexfiles layout body. The value is used as the count for kzalloc_objs(), and the current code only rejects NULL. A zero count yields ZERO_SIZE_PTR, which can be stored in dss_info->fh_versions even though later flexfiles paths assume that at least one filehandle version exists. Reject fh_count == 0 before the allocation, matching the existing zero version_count validation in the flexfiles GETDEVICEINFO parser. A QEMU/KASAN run with a malformed flexfiles layout hit: KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:ff_layout_encode_ff_layoutupdate.isra.0+0x15f/0x750 ff_layout_encode_layoutreturn+0x683/0x970 nfs4_xdr_enc_layoutreturn+0x278/0x3a0 Kernel panic - not syncing: Fatal exception The patched kernel rejects the malformed layout without KASAN/oops/panic, and a valid fh_count=1 regression still opens, reads, and unmounts cleanly. Cc: stable@vger.kernel.org Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/flexfilelayout/flexfilelayout.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 9056f05a67dc2..bb3f6c18630cb 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -552,6 +552,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, if (!p) goto out_err_free; fh_count = be32_to_cpup(p); + if (fh_count == 0) { + rc = -EINVAL; + goto out_err_free; + } dss_info->fh_versions = kcalloc(fh_count, sizeof(struct nfs_fh), From 012d37a568bfbb2c9686f03ade75560bc7139956 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 12:30:35 -0400 Subject: [PATCH 0104/3297] NFSv4/pNFS: reject zero-length r_addr in nfs4_decode_mp_ds_addr commit 41fe0f7b84f0cb822ae10ab08592996a592b2a25 upstream. nfs4_decode_mp_ds_addr() decodes the r_netid and r_addr opaques of a netaddr4 from a GETDEVICEINFO multipath-DS body, then immediately calls strrchr(buf, '.') to locate the port separator. Both decodes use xdr_stream_decode_string_dup(), and the current code checks only "nlen < 0" / "rlen < 0" before dereferencing the returned string. When the on-wire opaque has length zero, xdr_stream_decode_opaque_inline() returns 0 and xdr_stream_decode_string_dup() falls through to its "*str = NULL; return ret" tail, leaving buf NULL with a return value of 0. The "< 0" check does not catch this, and the next line is strrchr(NULL, '.'), a kernel NULL pointer dereference reachable from any pNFS-flexfile client mounted against a malicious or compromised metadata server. Reject the zero-length cases explicitly so the decoder fails with -EBADMSG (treated as a malformed GETDEVICEINFO body) instead of panicking the client. Cc: stable@vger.kernel.org Fixes: 6b7f3cf96364 ("nfs41: pull decode_ds_addr from file layout to generic pnfs") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/pnfs_nfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 9976cc16b6890..37cf4020f3d5d 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -1075,14 +1075,14 @@ nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags) /* r_netid */ nlen = xdr_stream_decode_string_dup(xdr, &netid, XDR_MAX_NETOBJ, gfp_flags); - if (unlikely(nlen < 0)) + if (unlikely(nlen <= 0)) goto out_err; /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */ /* port is ".ABC.DEF", 8 chars max */ rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8, gfp_flags); - if (unlikely(rlen < 0)) + if (unlikely(rlen <= 0)) goto out_free_netid; /* replace port '.' with '-' */ From 6919eb549e8f3caaef16d918749bf2b68d18532d Mon Sep 17 00:00:00 2001 From: Igor Raits Date: Wed, 29 Apr 2026 12:49:38 +0200 Subject: [PATCH 0105/3297] NFSv4: clear exception state on successful mkdir retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 238e9b51aa29f48b6243212a3b75c8e48d6b96fd upstream. After a server returns NFS4ERR_DELAY for an NFSv4 CREATE issued by mkdir(2), the client correctly waits and retries. When the retry succeeds, however, mkdir(2) can still surface -EEXIST to userspace even though the directory was just created on the server. Reproducer (random 16-hex names so collisions are not the cause) against an in-kernel Linux nfsd; reproduces under both NFSv4.0 and NFSv4.2: N=2000000; base=/var/gdc/export for ((i=1; i<=N; i++)); do d=$base/$(openssl rand -hex 8) mkdir "$d" 2>/dev/null || echo "$(date +%T) failed loop=$i $d" rmdir "$d" 2>/dev/null done Failures cluster at the cadence at which the server-side auth/export cache refresh path causes nfsd to return NFS4ERR_DELAY for CREATE. A wire trace of one failure (the three CREATE RPCs all come from a single mkdir(2), generated by the do-while in nfs4_proc_mkdir()): client -> server CREATE name=... -> NFS4ERR_DELAY ~100 ms later client -> server CREATE name=... -> NFS4_OK (dir created) ~80 us later client -> server CREATE name=... -> NFS4ERR_EXIST (correct) Since commit dd862da61e91 ("nfs: fix incorrect handling of large-number NFS errors in nfs4_do_mkdir()"), nfs4_handle_exception() is called only when _nfs4_proc_mkdir() returned an error. That gate breaks retry-state hygiene: nfs4_do_handle_exception() resets exception.{delay,recovering, retry} to 0 on entry, so calling it on success is what previously cleared the retry flag set by the preceding NFS4ERR_DELAY iteration. With the gate in place, exception.retry stays at 1 after the successful retry, the loop runs once more, and the resulting CREATE for an already-created name yields NFS4ERR_EXIST -> -EEXIST to userspace. Drop the conditional and call nfs4_handle_exception() unconditionally, matching every other do-while in fs/nfs/nfs4proc.c (nfs4_proc_symlink(), nfs4_proc_link(), etc.). The dentry/status separation introduced by that commit is preserved. Fixes: dd862da61e91 ("nfs: fix incorrect handling of large-number NFS errors in nfs4_do_mkdir()") Reported-and-tested-by: Jan Čípa Closes: https://lore.kernel.org/linux-nfs/CA+9S74hSp_tJu2Ffe2BPNC2T25gfkhgjjDkdgSsF5c2rnJq_wA@mail.gmail.com/ Reviewed-by: NeilBrown Cc: stable@vger.kernel.org Signed-off-by: Igor Raits Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4proc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 711812d839909..fc858b5c3f66d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5303,10 +5303,9 @@ static struct dentry *nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, do { alias = _nfs4_proc_mkdir(dir, dentry, sattr, label, &err); trace_nfs4_mkdir(dir, &dentry->d_name, err); + err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception); if (err) - alias = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), - err, - &exception)); + alias = ERR_PTR(err); } while (exception.retry); nfs4_label_release_security(label); From 62c26720121bfcb565f136d99bc40a7378a66fa0 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 14 Jun 2026 09:56:35 +0200 Subject: [PATCH 0106/3297] NFS: Prevent resource leak in nfs_alloc_server() commit d189f224308c8ac3feeea8e442c99922bd18f1b2 upstream. It was overlooked to call ida_free() after a failed nfs_alloc_iostats() call. Thus add the missed function call in an if branch. Fixes: 1c7251187dc067a6d460cf33ca67da9c1dd87807 ("NFS: add superblock sysfs entries") Cc: stable@vger.kernel.org Reported-by: Christophe Jaillet Closes: https://lore.kernel.org/linux-nfs/1c8e10c9-def7-4f0d-8aa1-23c8035a38c8@wanadoo.fr/ Signed-off-by: Markus Elfring Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 2aaea9c98c2cd..404e75b0444d7 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1070,6 +1070,7 @@ struct nfs_server *nfs_alloc_server(void) server->io_stats = nfs_alloc_iostats(); if (!server->io_stats) { + ida_free(&s_sysfs_ids, server->s_sysfs_id); kfree(server); return NULL; } From 7627ff8c4f9919f14de562b0160ab4ec9d80b1f7 Mon Sep 17 00:00:00 2001 From: Hem Parekh Date: Tue, 2 Jun 2026 16:56:46 -0700 Subject: [PATCH 0107/3297] ksmbd: fix out-of-bounds read in smb_check_perm_dacl() commit 1ef06004ed4bd6d3ed8c840d9d1a376b66d4935b upstream. The permission-check ACE walk in smb_check_perm_dacl() validates the ACE header size and caps sid.num_subauth at SID_MAX_SUB_AUTHORITIES, but it never checks that ace->size is actually large enough to contain num_subauth sub-authorities before compare_sids() dereferences them. CIFS_SID_BASE_SIZE covers the SID header up to but excluding the sub_auth[] array, and offsetof(struct smb_ace, sid) is the ACE header, so the existing guards only guarantee the 8-byte SID base, i.e. zero sub-authorities. compare_sids() then reads ace->sid.sub_auth[i] for i < min(local_sid->num_subauth, ace->sid.num_subauth). The local comparison SIDs (sid_everyone, sid_unix_NFS_mode, and the id_to_sid() result) always have at least one sub-authority, and an attacker controls the ACE revision and authority bytes (which lie within the in-bounds SID base), so they can match one of those SIDs and force the sub_auth read. A crafted ACE with size == 16 and num_subauth >= 1 placed at the tail of the security descriptor therefore causes a heap out-of-bounds read of up to SID_MAX_SUB_AUTHORITIES * sizeof(__le32) bytes past the pntsd allocation. The security descriptor is loaded by ksmbd_vfs_get_sd_xattr() into a buffer sized exactly to the on-disk data (kzalloc(sd_size) in ndr_decode_v4_ntacl()), so the read lands past the allocation. The malformed descriptor can be stored verbatim via SMB2_SET_INFO (the DACL is not normalised before being written to the security.NTACL xattr) and the read fires on a subsequent SMB2_CREATE access check, making this reachable by an authenticated client on a share that uses ACL xattrs. Add the missing num_subauth-versus-ace_size check, mirroring the identical guards already present in the sibling parsers parse_dacl() and smb_inherit_dacl(). Fixes: d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") Cc: stable@vger.kernel.org Signed-off-by: Hem Parekh Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smbacl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index e3c512675c632..f5864dd1dd5f1 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -1480,7 +1480,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, break; aces_size -= ace_size; - if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) + if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + ace_size < offsetof(struct smb_ace, sid) + CIFS_SID_BASE_SIZE + + sizeof(__le32) * ace->sid.num_subauth) break; if (!compare_sids(&sid, &ace->sid) || From 3d205fe80f2181f0109150ad1fa06ee5bc046935 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 19:37:45 +0500 Subject: [PATCH 0108/3297] serial: 8250_dw: unregister 8250 port if clk_notifier_register() fails commit 10fc708b4de7f86002d2d735a2dbf3b5b7f65692 upstream. dw8250_probe() registers the 8250 port via serial8250_register_8250_port() and then, if the device has a clock, registers a clock notifier. If clk_notifier_register() fails, probe returns the error but leaves the 8250 port registered. The matching serial8250_unregister_port() lives in dw8250_remove(), which is not called when probe fails, so the port slot stays occupied until the device is rebound or the system is rebooted. The devm-allocated driver data is freed while the port still references it (via the saved private_data and serial_in/serial_out callbacks), so any access to that port slot before a rebind is a use-after-free hazard. Unregister the port on the clk_notifier_register() error path. Fixes: cc816969d7b5 ("serial: 8250_dw: Fix common clocks usage race condition") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260514143746.23671-2-sozdayvek@gmail.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 4103178725e39..7a00083635439 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -846,8 +846,10 @@ static int dw8250_probe(struct platform_device *pdev) */ if (data->clk) { err = clk_notifier_register(data->clk, &data->clk_notifier); - if (err) + if (err) { + serial8250_unregister_port(data->data.line); return dev_err_probe(dev, err, "Failed to set the clock notifier\n"); + } queue_work(system_unbound_wq, &data->clk_work); } From e77fbefd1269b5c123e7c651a1ebdce1b87d19a0 Mon Sep 17 00:00:00 2001 From: HanQuan Date: Tue, 23 Jun 2026 01:52:08 +0000 Subject: [PATCH 0109/3297] net/tcp-ao: fix use-after-free of key in del_async path commit 5ba9950bc9078e19b69cca1e56d1553b125c6857 upstream. In tcp_ao_delete_key(), the del_async path skips the current_key and rnext_key validity checks present in the synchronous path, assuming these pointers are always NULL on LISTEN sockets. However, if a key was added with set_current=1/set_rnext=1 while the socket was in CLOSE state, current_key and rnext_key will be non-NULL after listen() transitions the socket to LISTEN. When such a key is deleted with del_async=1, hlist_del_rcu() and call_rcu() free the key without clearing the dangling pointers. After the RCU grace period, getsockopt(TCP_AO_INFO) dereferences current_key->sndid and rnext_key->rcvid from freed slab memory. Clear current_key and rnext_key in the del_async path when they reference the key being deleted. Fixes: d6732b95b6fb ("net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs)") Signed-off-by: HanQuan Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260623015208.1191687-1-eilaimemedsnaimel@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_ao.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index aa624434b5556..46eb5b4683bb5 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1776,6 +1776,10 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, * them and we can just free all resources in RCU fashion. */ if (del_async) { + if (ao_info->current_key == key) + WRITE_ONCE(ao_info->current_key, NULL); + if (ao_info->rnext_key == key) + WRITE_ONCE(ao_info->rnext_key, NULL); atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); call_rcu(&key->rcu, tcp_ao_key_free_rcu); return 0; From 92c63a5ef3c7a2136ee71324c28f1799769206a2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 22 Jun 2026 16:34:13 -0700 Subject: [PATCH 0110/3297] apparmor: advertise the tcp fast open fix is applied commit 2f6701a5ce6257ae7a64ddc6d89d0a08d2a034f8 upstream. The fix for tcp-fast-open ensures that the connect permission is being mediated correctly but it didn't add an artifact to the feature set to advertise the fix is available. Add an artifact so that the test suite can identify if the fix has not been properly applied or a new unexpected regression has occurred. Fixes: 4d587cd8a7215 ("apparmor: mediate the implicit connect of TCP fast open sendmsg") Signed-off-by: John Johansen Signed-off-by: Greg Kroah-Hartman --- security/apparmor/net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 44c04102062f3..1fc6145ccbb8d 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -22,12 +22,14 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1), { } }; struct aa_sfs_entry aa_sfs_entry_networkv9[] = { AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), AA_SFS_FILE_BOOLEAN("af_unix", 1), + AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1), { } }; From e46dc0adfe39724bcf52cea47b8f9c9aed86a394 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 4 Jul 2026 13:44:22 +0200 Subject: [PATCH 0111/3297] Linux 6.18.38 Link: https://lore.kernel.org/r/20260702155112.110058792@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Peter Schneider Tested-by: Miguel Ojeda Tested-by: Shung-Hsi Yu Link: https://lore.kernel.org/r/20260703072816.644513463@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Mark Brown Tested-by: Ron Economos Tested-by: Wentao Guan Tested-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 74cbca8abc6cb..7ee3beedcecaa 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 37 +SUBLEVEL = 38 EXTRAVERSION = NAME = Baby Opossum Posse From 501543207378f69778c66213759e5085ee023302 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 2 Jul 2026 16:07:56 +0800 Subject: [PATCH 0112/3297] bpf, arm64: Reject out-of-range B.cond targets commit 48d83d94930eb4db4c93d2de44838b9455cff626 upstream. aarch64_insn_gen_cond_branch_imm() calls label_imm_common() to compute a 19-bit signed byte offset for a conditional branch, but unlike its siblings aarch64_insn_gen_branch_imm() and aarch64_insn_gen_comp_branch_imm(), it does not check whether label_imm_common() returned its out-of-range sentinel (range) before feeding the value to aarch64_insn_encode_immediate(). aarch64_insn_encode_immediate() unconditionally masks the value with the 19-bit field mask, so an offset that was rejected by label_imm_common() gets silently truncated. With the sentinel value SZ_1M, the resulting field ends up with bit 18 (the sign bit of the 19-bit signed displacement) set, and the CPU decodes it as a ~1 MiB *backward* branch, producing an incorrectly targeted B.cond instruction. For code-gen locations like the emit_bpf_tail_call() this function is the only barrier between an overflowing displacement and a silently miscompiled branch. Fix it by returning AARCH64_BREAK_FAULT when the offset is out of range, so callers see a loud failure instead of a silently misencoded branch. validate_code() scans the generated image for any AARCH64_BREAK_FAULT and then lets the JIT fail. Fixes: 345e0d35ecdd ("arm64: introduce aarch64_insn_gen_cond_branch_imm()") Fixes: c94ae4f7c5ec ("arm64: insn: remove BUG_ON from codegen") Signed-off-by: Daniel Borkmann Reviewed-by: Puranjay Mohan Link: https://lore.kernel.org/r/20260415121403.639619-1-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- arch/arm64/lib/insn.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c index 4e298baddc2e5..e9e9a70ac155c 100644 --- a/arch/arm64/lib/insn.c +++ b/arch/arm64/lib/insn.c @@ -338,6 +338,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, long offset; offset = label_imm_common(pc, addr, SZ_1M); + if (offset >= SZ_1M) + return AARCH64_BREAK_FAULT; insn = aarch64_insn_get_bcond_value(); From 7c702bb4f8d83cc11ffcd01141b0cc2ea6e3a298 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Thu, 2 Jul 2026 16:24:07 -0400 Subject: [PATCH 0113/3297] nfsd: update mtime/ctime on CLONE in presense of delegated attributes commit 2863bac7f49c4acd80a048ce52506a2b9c8db015 upstream. When delegated attributes are given on open, the file is opened with NOCMTIME and modifying operations do not update mtime/ctime as to not get out-of-sync with the client's delegated view. However, for CLONE operation, the server should update its view of mtime/ctime and reflect that in any GETATTR queries. Fixes: e5e9b24ab8fa ("nfsd: freeze c/mtime updates with outstanding WRITE_ATTRS delegation") Cc: stable@vger.kernel.org Signed-off-by: Olga Kornievskaia Signed-off-by: Chuck Lever [ cel: 9be4b7e74eb7 and 3daab3112f03 are missing from linux-6.18.y ] Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4proc.c | 3 +++ fs/nfsd/nfs4state.c | 44 +++++++++++++++++++++++++++++--------------- fs/nfsd/state.h | 1 + 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9d876f9d98be2..5bfe49502a06f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1350,6 +1350,9 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, dst, clone->cl_dst_pos, clone->cl_count, EX_ISSYNC(cstate->current_fh.fh_export)); + if (!status && (READ_ONCE(dst->nf_file->f_mode) & FMODE_NOCMTIME) != 0) + nfsd_update_cmtime_attr(dst->nf_file, 0); + nfsd_file_put(dst); nfsd_file_put(src); out: diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index b02fe9926b093..a063a4a83ec22 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1226,10 +1226,6 @@ static void put_deleg_file(struct nfs4_file *fp) static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct file *f) { - struct iattr ia = { .ia_valid = ATTR_ATIME | ATTR_CTIME | ATTR_MTIME | ATTR_DELEG }; - struct inode *inode = file_inode(f); - int ret; - /* don't do anything if FMODE_NOCMTIME isn't set */ if ((READ_ONCE(f->f_mode) & FMODE_NOCMTIME) == 0) return; @@ -1247,17 +1243,7 @@ static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct f return; /* Stamp everything to "now" */ - inode_lock(inode); - ret = notify_change(&nop_mnt_idmap, f->f_path.dentry, &ia, NULL); - inode_unlock(inode); - if (ret) { - struct inode *inode = file_inode(f); - - pr_notice_ratelimited("Unable to update timestamps on inode %02x:%02x:%lu: %d\n", - MAJOR(inode->i_sb->s_dev), - MINOR(inode->i_sb->s_dev), - inode->i_ino, ret); - } + nfsd_update_cmtime_attr(f, ATTR_ATIME); } static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp) @@ -9434,3 +9420,31 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, nfs4_put_stid(&dp->dl_stid); return status; } + +/** + * nfsd_update_cmtime_attr - update file's delegated ctime/mtime, + * and optionally other attributes (ie ATTR_ATIME). + * @f: pointer to an opened file + * @flags: any additional flags that should be updated + * + * Given upon opening a file delegated attributes were issues, update + * @f attributes to current times. + */ +void nfsd_update_cmtime_attr(struct file *f, unsigned int flags) +{ + int ret; + struct inode *inode = file_inode(f); + struct iattr attr = { + .ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_DELEG | flags, + }; + + inode_lock(inode); + ret = notify_change(&nop_mnt_idmap, f->f_path.dentry, &attr, NULL); + inode_unlock(inode); + if (ret) + pr_notice_ratelimited("nfsd: Unable to update timestamps on " + "inode %02x:%02x:%lu: %d\n", + MAJOR(inode->i_sb->s_dev), + MINOR(inode->i_sb->s_dev), + inode->i_ino, ret); +} diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index aaf513ed91045..2eed2c5b3cd0c 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -831,6 +831,7 @@ extern void nfsd4_shutdown_callback(struct nfs4_client *); extern void nfsd4_shutdown_copy(struct nfs4_client *clp); void nfsd4_async_copy_reaper(struct nfsd_net *nn); bool nfsd4_has_active_async_copies(struct nfs4_client *clp); +void nfsd_update_cmtime_attr(struct file *f, unsigned int flags); extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, struct nfsd_net *nn); extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn); From 6f88ca186a984da0c1bfdc5163b949b96a707cc7 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Thu, 2 Jul 2026 16:24:08 -0400 Subject: [PATCH 0114/3297] nfsd: update mtime/ctime on COPY in presence of delegated attributes commit 4183cf383b6faec17a0882b84cd2d901dba62b16 upstream. When delegated attributes are given on open, the file is opened with NOCMTIME and modifying operations do not update mtime/ctime as to not get out-of-sync with the client's delegated view. However, for COPY operation, the server should update its view of mtime/ctime and reflect that in any GETATTR queries. Fixes: e5e9b24ab8fa ("nfsd: freeze c/mtime updates with outstanding WRITE_ATTRS delegation") Cc: stable@vger.kernel.org Signed-off-by: Olga Kornievskaia Signed-off-by: Chuck Lever Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4proc.c | 11 ++++++++++- fs/nfsd/xdr4.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 5bfe49502a06f..98086d217aa22 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1956,8 +1956,10 @@ static int nfsd4_do_async_copy(void *data) set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags); trace_nfsd_copy_async_done(copy); - nfsd4_send_cb_offload(copy); atomic_dec(©->cp_nn->pending_async_copies); + if (copy->cp_res.wr_bytes_written > 0 && copy->attr_update) + nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0); + nfsd4_send_cb_offload(copy); return 0; } @@ -2017,6 +2019,9 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, memcpy(&result->cb_stateid, ©->cp_stateid.cs_stid, sizeof(result->cb_stateid)); dup_copy_fields(copy, async_copy); + if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) & + FMODE_NOCMTIME) != 0) + async_copy->attr_update = true; memcpy(async_copy->cp_cb_offload.co_referring_sessionid.data, cstate->session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); @@ -2035,6 +2040,10 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } else { status = nfsd4_do_copy(copy, copy->nf_src->nf_file, copy->nf_dst->nf_file, true); + if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) & + FMODE_NOCMTIME) != 0 && + copy->cp_res.wr_bytes_written > 0) + nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0); } out: trace_nfsd_copy_done(copy, status); diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 1ce8e12ae3354..d0ef5e7f10778 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -745,6 +745,7 @@ struct nfsd4_copy { struct nfsd_file *nf_src; struct nfsd_file *nf_dst; + bool attr_update; copy_stateid_t cp_stateid; From 8dee7c278f1c2b5bb80e17a6281c3812fc8b0cdd Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 2 Jul 2026 16:24:09 -0400 Subject: [PATCH 0115/3297] nfsd: release layout stid on setlease failure commit 30d55c8aabb261bc3f427d6b9aae7ef6206063f9 upstream. nfs4_alloc_stid() publishes the new stid into cl->cl_stateids via idr_alloc_cyclic() under cl_lock before returning to nfsd4_alloc_layout_stateid(). When nfsd4_layout_setlease() then fails, the error path frees the layout stateid directly with kmem_cache_free() without ever calling idr_remove(), leaving the IDR slot pointing at freed slab memory. Any subsequent IDR walker (states_show, client teardown) dereferences the dangling pointer. The correct teardown for an IDR-published stid is nfs4_put_stid(), which removes the IDR slot under cl_lock, dispatches sc_free (nfsd4_free_layout_stateid) to release ls->ls_file via nfsd4_close_layout(), and drops the nfs4_file reference in its tail. A second issue blocks that switch: nfsd4_free_layout_stateid() unconditionally inspects ls->ls_fence_work via delayed_work_pending() under ls_lock, but INIT_DELAYED_WORK(&ls->ls_fence_work, ...) currently runs only after the setlease call. On the setlease-failure path the destructor would touch an uninitialized delayed_work. nfsd4_alloc_layout_stateid() nfs4_alloc_stid() /* idr_alloc_cyclic under cl_lock */ nfsd4_layout_setlease() /* fails */ nfs4_put_stid() nfsd4_free_layout_stateid() delayed_work_pending(&ls->ls_fence_work) /* needs INIT */ nfsd4_close_layout() /* nfsd_file_put(ls->ls_file) */ put_nfs4_file() Fix by hoisting the ls_fenced / ls_fence_delay / INIT_DELAYED_WORK initialization above the nfsd4_layout_setlease() call, and replace the manual nfsd_file_put + put_nfs4_file + kmem_cache_free cleanup with a single nfs4_put_stid(stp). Fixes: c5c707f96fc9 ("nfsd: implement pNFS layout recalls") Cc: stable@vger.kernel.org Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever [ cel: drop fence_work init hoist absent from 6.18.y ] Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4layouts.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index 683bd1130afe2..62762e43f810b 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -256,9 +256,7 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, BUG_ON(!ls->ls_file); if (nfsd4_layout_setlease(ls)) { - nfsd_file_put(ls->ls_file); - put_nfs4_file(fp); - kmem_cache_free(nfs4_layout_stateid_cache, ls); + nfs4_put_stid(stp); return NULL; } From 3603500c868a1af34283bfa64cf207c940beb033 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 9 Jun 2026 12:41:51 +0200 Subject: [PATCH 0116/3297] rust: str: use the "kernel vertical" imports style [ Upstream commit 724a93a9f6033800b02a3530dbcb464638448e7f ] Convert the imports to use the "kernel vertical" imports style [1]. No functional changes intended. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports [1] Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260609104152.261145-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda Stable-dep-of: 3fff4271809b ("rust: str: clean unused import for Rust >= 1.98") Signed-off-by: Sasha Levin --- rust/kernel/str.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 8992fedabaf0f..3a276bc32962d 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -3,14 +3,29 @@ //! String representations. use crate::{ - alloc::{flags::*, AllocError, KVec}, - error::{to_result, Result}, - fmt::{self, Write}, - prelude::*, + alloc::{ + flags::*, + AllocError, + KVec, // + }, + error::{ + to_result, + Result, // + }, + fmt::{ + self, + Write, // + }, + prelude::*, // }; use core::{ marker::PhantomData, - ops::{self, Deref, DerefMut, Index}, + ops::{ + self, + Deref, + DerefMut, + Index, // + }, // }; /// Byte string without UTF-8 validity guarantee. From 3436a7dd067c5109c98a2da3b25ec91c6825a4eb Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 9 Jun 2026 12:41:52 +0200 Subject: [PATCH 0117/3297] rust: str: clean unused import for Rust >= 1.98 [ Upstream commit 3fff4271809b57182c4011811e96556bdd4cb2f9 ] Starting with Rust 1.98.0 (expected 2026-08-20), the compiler has changed how the resolution algorithm works [1] in upstream commit c4d84db5f184 ("Resolver: Batched import resolution."), and it now spots: error: unused import: `flags::*` --> rust/kernel/str.rs:7:9 | 7 | flags::*, | ^^^^^^^^ | = note: `-D unused-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(unused_imports)]` It happens to not be needed because the `prelude::*` already provides the flags. Thus clean it up. Cc: stable@vger.kernel.org # Needed in 6.18.y and later (prelude added to `str`). Link: https://github.com/rust-lang/rust/pull/145108 [1] Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260609104152.261145-2-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Sasha Levin --- rust/kernel/str.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 3a276bc32962d..22cefbc985236 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -4,7 +4,6 @@ use crate::{ alloc::{ - flags::*, AllocError, KVec, // }, From 60d696a037eeeedfb57756dfe7ec08a1587c8631 Mon Sep 17 00:00:00 2001 From: Kiryl Shutsemau Date: Thu, 2 Jul 2026 16:49:07 +0100 Subject: [PATCH 0118/3297] userfaultfd: gate must_wait writability check on pte_present() [ Upstream commit 8e80af52db652fbc41320eee45a4f73bc029faf2 ] userfaultfd_must_wait() and userfaultfd_huge_must_wait() read the PTE without taking the page table lock and then apply pte_write() / huge_pte_write() to it. Those accessors decode bits from the present encoding only; on a swap or migration entry they read the offset bits that happen to share the same position and return an undefined result. The intent of the check is "is this fault still WP-blocked?". A non-marker swap entry means the page is in transit -- the userfault context the original fault delivered against is no longer the same, and the swap-in or migration completion path will re-deliver a fresh fault if userspace still needs to handle it. Worst case under the current code the garbage write bit says "wait", and the thread stays asleep until a UFFDIO_WAKE that may never arrive. Gate the writability check on pte_present() so the lockless re-check only inspects present-PTE bits when the entry is actually present. The non-present, non-marker case returns "don't wait" and lets the fault path retry. Link: https://lore.kernel.org/20260529172331.356655-6-kas@kernel.org Fixes: 369cd2121be4 ("userfaultfd: hugetlbfs: userfaultfd_huge_must_wait for hugepmd ranges") Fixes: 63b2d4174c4a ("userfaultfd: wp: add the writeprotect API to userfaultfd ioctl") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton [ kas: apply to fs/userfaultfd.c and fold the pte_present()/ huge_pte_present() gate into the existing writability checks; this tree predates the marker/return-style refactor of these functions ] Signed-off-by: Kiryl Shutsemau Signed-off-by: Sasha Levin --- fs/userfaultfd.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index a67afdbbf77af..7e40eb9131bd7 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -251,7 +251,12 @@ static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx, */ if (huge_pte_none_mostly(pte)) ret = true; - if (!huge_pte_write(pte) && (reason & VM_UFFD_WP)) + /* + * Gate the writability check on pte_present(): huge_pte_write() on a + * non-present migration entry decodes random offset bits. The + * migration completion path re-delivers the fault if still needed. + */ + if (pte_present(pte) && !huge_pte_write(pte) && (reason & VM_UFFD_WP)) ret = true; out: return ret; @@ -326,7 +331,12 @@ static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx, ptent = ptep_get(pte); if (pte_none_mostly(ptent)) ret = true; - if (!pte_write(ptent) && (reason & VM_UFFD_WP)) + /* + * Gate the writability check on pte_present(): pte_write() on a + * non-present swap/migration entry decodes random offset bits. The + * page-in path re-delivers the fault if it still needs userspace. + */ + if (pte_present(ptent) && !pte_write(ptent) && (reason & VM_UFFD_WP)) ret = true; pte_unmap(pte); From 9c86a1f930bb2ddb85f867b4736716e82a4a4683 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 2 Jul 2026 11:40:11 -0400 Subject: [PATCH 0119/3297] device property: initialize the remaining fields of fwnode_handle in fwnode_init() [ Upstream commit 7eba000621fff223dd7bab484d48918c7c77a307 ] 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 uninitialized 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. While at it: initialize the remaining fields of struct fwnode_handle too just to be sure. Cc: stable@vger.kernel.org Fixes: 01bb86b380a3 ("driver core: Add fwnode_init()") Reviewed-by: Sakari Ailus Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260511074927.9473-1-bartosz.golaszewski@oss.qualcomm.com [ Fix typo in commit message. - Danilo ] Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 31df7608737e7..c30a9baafc0d9 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -210,8 +210,10 @@ static inline void fwnode_init(struct fwnode_handle *fwnode, { fwnode->secondary = NULL; fwnode->ops = ops; + fwnode->dev = NULL; INIT_LIST_HEAD(&fwnode->consumers); INIT_LIST_HEAD(&fwnode->suppliers); + fwnode->flags = 0; } static inline void fwnode_set_flag(struct fwnode_handle *fwnode, From 5a4d3968cf820686400fc757a4512dcaddd84846 Mon Sep 17 00:00:00 2001 From: Yongpeng Yang Date: Fri, 3 Jul 2026 08:33:29 -0400 Subject: [PATCH 0120/3297] f2fs: remove non-uptodate folio from the page cache in move_data_block [ Upstream commit 9609dd704725a40cd63d915f2ab6c44248a44598 ] During data movement, move_data_block acquires file folio without triggering a file read. Such folio are typically not uptodate, they need to be removed from the page cache after gc complete. This patch marks folio with the PG_dropbehind flag and uses folio_end_dropbehind to remove folio from the page cache. Signed-off-by: Yunlei He Signed-off-by: Yongpeng Yang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Stable-dep-of: e0288584baa5 ("f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/gc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 3d9f326ae840a..c7f6312cbb101 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1456,7 +1456,11 @@ static int move_data_block(struct inode *inode, block_t bidx, put_out: f2fs_put_dnode(&dn); out: - f2fs_folio_put(folio, true); + if (!folio_test_uptodate(folio)) + __folio_set_dropbehind(folio); + folio_unlock(folio); + folio_end_dropbehind(folio); + folio_put(folio); return err; } From a499f77c06050a28c897bdbd86cd2f0721ae0743 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 3 Jul 2026 08:33:30 -0400 Subject: [PATCH 0121/3297] f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode [ Upstream commit e0288584baa5dc41df4a829a023c4c1b33fe53d7 ] - ioctl(F2FS_IOC_GARBAGE_COLLECT_RANGE) - shrink - f2fs_gc - gc_data_segment - ra_data_block(cow_inode) - mapping = F2FS_I(inode)->atomic_inode->i_mapping : f2fs_is_cow_file(cow_inode) is true - f2fs_evict_inode(atomic_inode) - clear_inode_flag(fi->cow_inode, FI_COW_FILE) - F2FS_I(fi->cow_inode)->atomic_inode = NULL ... - truncate_inode_pages_final(atomic_inode) - f2fs_grab_cache_folio(mapping) : create folio in atomic_inode->mapping - clear_inode(atomic_inode) - BUG_ON(atomic_inode->i_data.nrpages) We need to add a reference on fi->atomic_inode before using its mapping field during garbage collection, otherwise, it will cause UAF issue. Cc: stable@kernel.org Cc: Daeho Jeong Cc: Sunmin Jeong Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Fixes: f18d00769336 ("f2fs: use meta inode for GC of COW file") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/gc.c | 50 +++++++++++++++++++++++++++++++++++++++++-------- fs/f2fs/inode.c | 11 ++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c7f6312cbb101..7219a87955b20 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1208,8 +1208,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static int ra_data_block(struct inode *inode, pgoff_t index) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct address_space *mapping = f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping = inode->i_mapping; + struct inode *atomic_inode = NULL; struct dnode_of_data dn; struct folio *folio, *efolio; struct f2fs_io_info fio = { @@ -1224,9 +1224,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index) }; int err = 0; + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode = igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping = atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + folio = f2fs_grab_cache_folio(mapping, index, true); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out_iput; + } if (f2fs_lookup_read_extent_cache_block(inode, index, &dn.data_blkaddr)) { @@ -1287,11 +1300,16 @@ static int ra_data_block(struct inode *inode, pgoff_t index) f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE); f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE); + if (atomic_inode) + iput(atomic_inode); return 0; put_encrypted_page: f2fs_put_page(fio.encrypted_page, 1); put_folio: f2fs_folio_put(folio, true); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } @@ -1302,8 +1320,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index) static int move_data_block(struct inode *inode, block_t bidx, int gc_type, unsigned int segno, int off) { - struct address_space *mapping = f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping = inode->i_mapping; + struct inode *atomic_inode = NULL; struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .ino = inode->i_ino, @@ -1325,10 +1343,23 @@ static int move_data_block(struct inode *inode, block_t bidx, (fio.sbi->gc_mode != GC_URGENT_HIGH) ? CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode = igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping = atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + /* do not read out */ folio = f2fs_grab_cache_folio(mapping, bidx, false); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out_iput; + } if (!check_valid_map(F2FS_I_SB(inode), segno, off)) { err = -ENOENT; @@ -1461,6 +1492,9 @@ static int move_data_block(struct inode *inode, block_t bidx, folio_unlock(folio); folio_end_dropbehind(folio); folio_put(folio); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 171cd4172025f..c2aa52b025465 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -853,10 +853,15 @@ void f2fs_evict_inode(struct inode *inode) f2fs_abort_atomic_write(inode, true); if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { - clear_inode_flag(fi->cow_inode, FI_COW_FILE); - F2FS_I(fi->cow_inode)->atomic_inode = NULL; - iput(fi->cow_inode); + struct inode *cow_inode = fi->cow_inode; + + f2fs_down_write(&F2FS_I(cow_inode)->i_sem); + clear_inode_flag(cow_inode, FI_COW_FILE); + F2FS_I(cow_inode)->atomic_inode = NULL; fi->cow_inode = NULL; + f2fs_up_write(&F2FS_I(cow_inode)->i_sem); + + iput(cow_inode); } trace_f2fs_evict_inode(inode); From 4ce2d52f680c1d8bfdad7cce05b815ea7ca9790d Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 3 Jul 2026 10:09:34 -0400 Subject: [PATCH 0122/3297] f2fs: bound i_inline_xattr_size for non-inline-xattr inodes [ Upstream commit 378acf3cf19b6af6cba55e8dd1154c4e1504bae8 ] When the flexible_inline_xattr feature is enabled, do_read_inode() loads the on-disk i_inline_xattr_size unconditionally: if (f2fs_sb_has_flexible_inline_xattr(sbi)) fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); but sanity_check_inode() only range-checks it when the inode also has the FI_INLINE_XATTR flag set. An inode that carries an inline dentry or inline data but not FI_INLINE_XATTR -- the normal layout for an inline directory -- therefore keeps a fully attacker-controlled i_inline_xattr_size from a crafted image. get_inline_xattr_addrs() returns that value with no flag gating, so it feeds the inode geometry: MAX_INLINE_DATA() = 4 * (CUR_ADDRS_PER_INODE - i_inline_xattr_size - 1) NR_INLINE_DENTRY() = MAX_INLINE_DATA() * BITS_PER_BYTE / (...) addrs_per_page() = CUR_ADDRS_PER_INODE - i_inline_xattr_size A large i_inline_xattr_size drives MAX_INLINE_DATA() and NR_INLINE_DENTRY() negative, so make_dentry_ptr_inline() sets d->max (int) to a negative value. The inline directory walk then compares an unsigned long bit_pos against that negative d->max, which is promoted to a huge unsigned bound, and reads far past the inline area: while (bit_pos < d->max) /* fs/f2fs/dir.c */ ... test_bit_le(bit_pos, d->bitmap) / d->dentry[bit_pos] ... Mounting a crafted image and reading such a directory triggers an out-of-bounds read in f2fs_fill_dentries(); the same underflow also corrupts ADDRS_PER_INODE for regular files. Validate i_inline_xattr_size against MAX_INLINE_XATTR_SIZE whenever the flexible_inline_xattr feature is enabled -- i.e. whenever the value is loaded from disk and consumed -- and keep the lower MIN_INLINE_XATTR_SIZE bound gated on inodes that actually carry an inline xattr, so legitimate inodes with i_inline_xattr_size == 0 are still accepted. Cc: stable@vger.kernel.org Fixes: 6afc662e68b5 ("f2fs: support flexible inline xattr size") Signed-off-by: Bryam Vargas Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c2aa52b025465..90cc20232f760 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -318,9 +318,9 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio) } if (f2fs_sb_has_flexible_inline_xattr(sbi) && - f2fs_has_inline_xattr(inode) && - (fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE || - fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) { + (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE || + (f2fs_has_inline_xattr(inode) && + fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) { f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu", __func__, inode->i_ino, fi->i_inline_xattr_size, MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE); From 998536c96b6ad9d99cd85dea11452ab83dc7c88d Mon Sep 17 00:00:00 2001 From: Ruipeng Qi Date: Fri, 3 Jul 2026 14:03:16 -0400 Subject: [PATCH 0123/3297] f2fs: fix potential deadlock in f2fs_balance_fs() [ Upstream commit dd3114870771562036fdcf5abe813956f36d224d ] When the f2fs filesystem space is nearly exhausted, we encounter deadlock issues as below: INFO: task A:1890 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:A state:D stack:0 pid:1890 tgid:1626 ppid:1153 flags:0x00000204 Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 io_schedule+0x44/0x68 folio_wait_bit_common+0x174/0x370 folio_wait_bit+0x20/0x38 folio_wait_writeback+0x54/0xc8 truncate_inode_partial_folio+0x70/0x1e0 truncate_inode_pages_range+0x1b0/0x450 truncate_pagecache+0x54/0x88 f2fs_file_write_iter+0x3e8/0xb80 do_iter_readv_writev+0xf0/0x1e0 vfs_writev+0x138/0x2c8 do_writev+0x88/0x130 __arm64_sys_writev+0x28/0x40 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 INFO: task kworker/u8:11:2680853 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:11 state:D stack:0 pid:2680853 tgid:2680853 ppid:2 flags:0x00000208 Workqueue: writeback wb_workfn (flush-254:0) Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 io_schedule+0x44/0x68 folio_wait_bit_common+0x174/0x370 __filemap_get_folio+0x214/0x348 pagecache_get_page+0x20/0x70 f2fs_get_read_data_page+0x150/0x3e8 f2fs_get_lock_data_page+0x2c/0x160 move_data_page+0x50/0x478 do_garbage_collect+0xd38/0x1528 f2fs_gc+0x240/0x7e0 f2fs_balance_fs+0x1a0/0x208 f2fs_write_single_data_page+0x6e4/0x730 f2fs_write_cache_pages+0x378/0x9b0 f2fs_write_data_pages+0x2e4/0x388 do_writepages+0x8c/0x2c8 __writeback_single_inode+0x4c/0x498 writeback_sb_inodes+0x234/0x4a8 __writeback_inodes_wb+0x58/0x118 wb_writeback+0x2f8/0x3c0 wb_workfn+0x2c4/0x508 process_one_work+0x180/0x408 worker_thread+0x258/0x368 kthread+0x118/0x128 ret_from_fork+0x10/0x200 INFO: task kworker/u8:8:2641297 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:8 state:D stack:0 pid:2641297 tgid:2641297 ppid:2 flags:0x00000208 Workqueue: writeback wb_workfn (flush-254:0) Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 rt_mutex_schedule+0x30/0x60 __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 rwbase_write_lock+0x24c/0x378 down_write+0x1c/0x30 f2fs_balance_fs+0x184/0x208 f2fs_write_inode+0xf4/0x328 __writeback_single_inode+0x370/0x498 writeback_sb_inodes+0x234/0x4a8 __writeback_inodes_wb+0x58/0x118 wb_writeback+0x2f8/0x3c0 wb_workfn+0x2c4/0x508 process_one_work+0x180/0x408 worker_thread+0x258/0x368 kthread+0x118/0x128 ret_from_fork+0x10/0x20 INFO: task B:1902 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:B state:D stack:0 pid:1902 tgid:1626 ppid:1153 flags:0x0000020c Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 rt_mutex_schedule+0x30/0x60 __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 rwbase_write_lock+0x24c/0x378 down_write+0x1c/0x30 f2fs_balance_fs+0x184/0x208 f2fs_map_blocks+0x94c/0x1110 f2fs_file_write_iter+0x228/0xb80 do_iter_readv_writev+0xf0/0x1e0 vfs_writev+0x138/0x2c8 do_writev+0x88/0x130 __arm64_sys_writev+0x28/0x40 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 INFO: task sync:2769849 blocked for more than 120 seconds. Tainted: G O 6.12.41-g3fe07ddf05ab #1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:sync state:D stack:0 pid:2769849 tgid:2769849 ppid:736 flags:0x0000020c Call trace: __switch_to+0xf4/0x158 __schedule+0x27c/0x908 schedule+0x3c/0x118 wb_wait_for_completion+0xb0/0xe8 sync_inodes_sb+0xc8/0x2b0 sync_inodes_one_sb+0x24/0x38 iterate_supers+0xa8/0x138 ksys_sync+0x54/0xc8 __arm64_sys_sync+0x18/0x30 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0xc8/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x30/0xf8 el0t_64_sync_handler+0x120/0x130 el0t_64_sync+0x190/0x198 The root cause is a potential deadlock between the following tasks: kworker/u8:11 Thread A - f2fs_write_single_data_page - f2fs_do_write_data_page - folio_start_writeback(X) - f2fs_outplace_write_data - bio_add_folio(X) - folio_unlock(X) - truncate_inode_pages_range - __filemap_get_folio(X, FGP_LOCK) - truncate_inode_partial_folio(X) - folio_wait_writeback(X) - f2fs_balance_fs - f2fs_gc - do_garbage_collect - move_data_page - f2fs_get_lock_data_page - __filemap_get_folio(X, FGP_LOCK) Both threads try to access folio X. Thread A holds the lock but waits for writeback, while kworker waits for the lock. This causes a deadlock. Other threads also enter D state, waiting for locks such as gc_lock and writepages. OPU/IPU DATA folio are all affected by this issue. To avoid such potential deadlocks, always commit these cached folios before triggering f2fs_gc() in f2fs_balance_fs(). Suggested-by: Chao Yu Reviewed-by: Chao Yu Signed-off-by: Ruipeng Qi Signed-off-by: Jaegeuk Kim Stable-dep-of: 8b4468ec023d ("f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/data.c | 29 +++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 8 ++++++++ 3 files changed, 38 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 89105b22a0243..0f0f2d60fe699 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -898,6 +898,35 @@ void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, } } +void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi) +{ + struct bio_entry *be, *tmp; + struct f2fs_bio_info *io; + enum temp_type temp; + + for (temp = HOT; temp < NR_TEMP_TYPE; temp++) { + LIST_HEAD(list); + + io = sbi->write_io[DATA] + temp; + + /* A lockless list_empty() check is safe here: any bios from + * other kworkers that we miss will be submitted by those + * kworkers accordingly. + */ + if (list_empty(&io->bio_list)) + continue; + + f2fs_down_write(&io->bio_list_lock); + list_splice_init(&io->bio_list, &list); + f2fs_up_write(&io->bio_list_lock); + + list_for_each_entry_safe(be, tmp, &list, list) { + f2fs_submit_write_bio(sbi, be->bio, DATA); + del_bio_entry(be); + } + } +} + int f2fs_merge_page_bio(struct f2fs_io_info *fio) { struct bio *bio = *fio->bio; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a77858bd38779..ad6f08a30e9dc 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4084,6 +4084,7 @@ void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi, nid_t ino, enum page_type type); void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, struct bio **bio, struct folio *folio); +void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi); void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi); int f2fs_submit_page_bio(struct f2fs_io_info *fio); int f2fs_merge_page_bio(struct f2fs_io_info *fio); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a7cf8627d8881..7acfb52a5fa13 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -461,6 +461,14 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .should_migrate_blocks = false, .err_gc_skipped = false, .nr_free_secs = 1 }; + + /* + * Submit all cached OPU/IPU DATA bios before triggering + * foreground GC to avoid potential deadlocks. + */ + f2fs_submit_merged_write(sbi, DATA); + f2fs_submit_all_merged_ipu_writes(sbi); + f2fs_down_write(&sbi->gc_lock); stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); From 89479a27fa4e1e11f378b3724944eabceb84f114 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 3 Jul 2026 14:03:17 -0400 Subject: [PATCH 0124/3297] f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs() [ Upstream commit 8b4468ec023d0d1b4669dfb867588997cc03a06b ] When we mount device w/ gc_merge mount option, we may suffer below potential deadlock: Kworker GC trehad Truncator - f2fs_write_cache_pages - f2fs_write_single_data_page - f2fs_do_write_data_page - folio_start_writeback --- set writeback flag on folio - f2fs_outplace_write_data : cached folio in internal bio cache - f2fs_balance_fs - wake_up(gc_thread) : wake up gc thread to run foreground GC - finish_wait(fggc_wq) : wait on the waitqueue --- wait on GC thread to finish the work - truncate_inode_pages_range - __filemap_get_folio(, FGP_LOCK) --- lock folio - truncate_inode_partial_folio - folio_wait_writeback --- wait on writeback being cleared - do_garbage_collect - move_data_page - f2fs_get_lock_data_folio - lock on folio --- blocked on folio's lock In order to avoid such deadlock, let's call below functions to commit cached bios in GC_MERGE path of f2fs_balance_fs() as the same as we did in NOGC_MERGE path. - f2fs_submit_merged_write(sbi, DATA); - f2fs_submit_all_merged_ipu_writes(sbi); Cc: stable@kernel.org Fixes: 351df4b20115 ("f2fs: add segment operations") Cc: Ruipeng Qi Reported: Sandeep Dhavale Signed-off-by: Chao Yu Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/segment.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7acfb52a5fa13..0a080cb55c414 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -443,6 +443,13 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) if (has_enough_free_secs(sbi, 0, 0)) return; + /* + * Submit all cached OPU/IPU DATA bios before triggering + * foreground GC to avoid potential deadlocks. + */ + f2fs_submit_merged_write(sbi, DATA); + f2fs_submit_all_merged_ipu_writes(sbi); + if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && sbi->gc_thread->f2fs_gc_task) { DEFINE_WAIT(wait); @@ -462,13 +469,6 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .err_gc_skipped = false, .nr_free_secs = 1 }; - /* - * Submit all cached OPU/IPU DATA bios before triggering - * foreground GC to avoid potential deadlocks. - */ - f2fs_submit_merged_write(sbi, DATA); - f2fs_submit_all_merged_ipu_writes(sbi); - f2fs_down_write(&sbi->gc_lock); stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); From ec9f79c8d5b28a928e65b67cd138c841571cf502 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Fri, 3 Jul 2026 14:39:41 -0400 Subject: [PATCH 0125/3297] f2fs: fix listxattr handling of corrupted xattr entries [ Upstream commit 5ef5bc304f23c3fe255d4936472378dcb74d0e94 ] Validate the xattr entry before reading its fields in f2fs_listxattr(). Return -EFSCORRUPTED when the entry is outside the valid xattr storage area instead of returning a successful partial result. Fixes: 688078e7f36c ("f2fs: fix to avoid memory leakage in f2fs_listxattr") Cc: stable@kernel.org Reviewed-by: Chao Yu Signed-off-by: Keshav Verma Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/xattr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index b4e5c406632f8..8cb0de2a5bcf9 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -581,8 +581,6 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) size_t prefix_len; size_t size; - prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); - if ((void *)(entry) + sizeof(__u32) > last_base_addr || (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) { f2fs_err(F2FS_I_SB(inode), "list inode (%lu) has corrupted xattr", @@ -590,9 +588,11 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK); f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_XATTR); - break; + error = -EFSCORRUPTED; + goto cleanup; } + prefix = f2fs_xattr_prefix(entry->e_name_index, dentry); if (!prefix) continue; From 599d7d82eeecc0c2f0704831ed16055e8b8e9603 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 4 Jul 2026 09:01:02 -0400 Subject: [PATCH 0126/3297] f2fs: use memalloc_retry_wait() as much as possible [ Upstream commit 30a8496694f1a93328e5d7f19206380346918b5a ] memalloc_retry_wait() is recommended in memory allocation retry logic, use it as much as possible. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Stable-dep-of: 8712353ed80f ("f2fs: fix to do sanity check on f2fs_get_node_folio_ra()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/segment.c | 2 +- fs/f2fs/super.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0a080cb55c414..e1def471857a2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -234,7 +234,7 @@ static int __replace_atomic_write_block(struct inode *inode, pgoff_t index, err = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE); if (err) { if (err == -ENOMEM) { - f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); + memalloc_retry_wait(GFP_NOFS); goto retry; } return err; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9154f702e1706..fc4a2e915ca09 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3128,7 +3128,7 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type, &folio, &fsdata); if (unlikely(err)) { if (err == -ENOMEM) { - f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); + memalloc_retry_wait(GFP_NOFS); goto retry; } set_sbi_flag(F2FS_SB(sb), SBI_QUOTA_NEED_REPAIR); From 8dbc4c56868205bf41b0a3f0eb7e856017349b9c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 4 Jul 2026 09:01:03 -0400 Subject: [PATCH 0127/3297] f2fs: introduce f2fs_schedule_timeout() [ Upstream commit 76e780d88c771921ea643fb8a6c8d0b08c17cb7b ] In f2fs retry logic, we will call f2fs_io_schedule_timeout() to sleep as uninterruptible state (waiting for IO) for a while, however, in several paths below, we are not blocked by IO: - f2fs_write_single_data_page() return -EAGAIN due to racing on cp_rwsem. - f2fs_flush_device_cache() failed to submit preflush command. - __issue_discard_cmd_range() sleeps periodically in between two in batch discard submissions. So, in order to reveal state of task more accurate, let's introduce f2fs_schedule_timeout() and call it in above paths in where we are waiting for non-IO reasons. Then we can get real reason of uninterruptible sleep for a thread in tracepoint, perfetto, etc. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Stable-dep-of: 8712353ed80f ("f2fs: fix to do sanity check on f2fs_get_node_folio_ra()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/checkpoint.c | 4 ++-- fs/f2fs/compress.c | 4 ++-- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 22 +++++++++++++++------- fs/f2fs/segment.c | 4 ++-- fs/f2fs/super.c | 2 +- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c27509d4b6ef4..739f46eb866c8 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1330,7 +1330,7 @@ void f2fs_wait_on_all_pages(struct f2fs_sb_info *sbi, int type) f2fs_submit_merged_write(sbi, DATA); prepare_to_wait(&sbi->cp_wait, &wait, TASK_UNINTERRUPTIBLE); - io_schedule_timeout(DEFAULT_IO_TIMEOUT); + io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); } finish_wait(&sbi->cp_wait, &wait); } @@ -1986,7 +1986,7 @@ void f2fs_flush_ckpt_thread(struct f2fs_sb_info *sbi) /* Let's wait for the previous dispatched checkpoint. */ while (atomic_read(&cprc->queued_ckpt)) - io_schedule_timeout(DEFAULT_IO_TIMEOUT); + io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); } void f2fs_init_ckpt_req_control(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index ae36bb1f93519..0399db98fccfc 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1057,7 +1057,7 @@ static void cancel_cluster_writeback(struct compress_ctx *cc, f2fs_submit_merged_write(F2FS_I_SB(cc->inode), DATA); while (atomic_read(&cic->pending_pages) != (cc->valid_nr_cpages - submitted + 1)) - f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); + f2fs_io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); } /* Cancel writeback and stay locked. */ @@ -1582,7 +1582,7 @@ static int f2fs_write_raw_pages(struct compress_ctx *cc, */ if (IS_NOQUOTA(cc->inode)) goto out; - f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); + f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); goto retry_write; } goto out; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0f0f2d60fe699..872169b91913c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -3214,8 +3214,8 @@ static int f2fs_write_cache_pages(struct address_space *mapping, } else if (ret == -EAGAIN) { ret = 0; if (wbc->sync_mode == WB_SYNC_ALL) { - f2fs_io_schedule_timeout( - DEFAULT_IO_TIMEOUT); + f2fs_schedule_timeout( + DEFAULT_SCHEDULE_TIMEOUT); goto retry_write; } goto next; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ad6f08a30e9dc..b96e5bc66ef7d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -665,8 +665,8 @@ enum { #define DEFAULT_RETRY_IO_COUNT 8 /* maximum retry read IO or flush count */ -/* congestion wait timeout value, default: 20ms */ -#define DEFAULT_IO_TIMEOUT (msecs_to_jiffies(20)) +/* IO/non-IO congestion wait timeout value, default: 20ms */ +#define DEFAULT_SCHEDULE_TIMEOUT (msecs_to_jiffies(20)) /* timeout value injected, default: 1000ms */ #define DEFAULT_FAULT_TIMEOUT (msecs_to_jiffies(1000)) @@ -4940,22 +4940,30 @@ static inline bool f2fs_block_unit_discard(struct f2fs_sb_info *sbi) return F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_BLOCK; } -static inline void f2fs_io_schedule_timeout(long timeout) +static inline void __f2fs_schedule_timeout(long timeout, bool io) { set_current_state(TASK_UNINTERRUPTIBLE); - io_schedule_timeout(timeout); + if (io) + io_schedule_timeout(timeout); + else + schedule_timeout(timeout); } +#define f2fs_io_schedule_timeout(timeout) \ + __f2fs_schedule_timeout(timeout, true) +#define f2fs_schedule_timeout(timeout) \ + __f2fs_schedule_timeout(timeout, false) + static inline void f2fs_io_schedule_timeout_killable(long timeout) { while (timeout) { if (fatal_signal_pending(current)) return; set_current_state(TASK_UNINTERRUPTIBLE); - io_schedule_timeout(DEFAULT_IO_TIMEOUT); - if (timeout <= DEFAULT_IO_TIMEOUT) + io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); + if (timeout <= DEFAULT_SCHEDULE_TIMEOUT) return; - timeout -= DEFAULT_IO_TIMEOUT; + timeout -= DEFAULT_SCHEDULE_TIMEOUT; } } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e1def471857a2..5e2789cd96b5e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -758,7 +758,7 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi) do { ret = __submit_flush_wait(sbi, FDEV(i).bdev); if (ret) - f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); + f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); } while (ret && --count); if (ret) { @@ -3480,7 +3480,7 @@ static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, blk_finish_plug(&plug); mutex_unlock(&dcc->cmd_lock); trimmed += __wait_all_discard_cmd(sbi, NULL); - f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); + f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); goto next; } skip: diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index fc4a2e915ca09..f6b75ce11d1c1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2656,7 +2656,7 @@ static int f2fs_enable_checkpoint(struct f2fs_sb_info *sbi) /* we should flush all the data to keep data consistency */ while (get_pages(sbi, F2FS_DIRTY_DATA)) { writeback_inodes_sb_nr(sbi->sb, nr_pages, WB_REASON_SYNC); - f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); + f2fs_io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); if (f2fs_time_over(sbi, ENABLE_TIME)) break; From ed87e57558dc573439503da8247cd3a14f1d07f2 Mon Sep 17 00:00:00 2001 From: YH Lin Date: Sat, 4 Jul 2026 09:01:04 -0400 Subject: [PATCH 0128/3297] f2fs: optimize trace_f2fs_write_checkpoint with enums [ Upstream commit 8d1cb17aca466b361cca17834b8bb1cf3e3d1818 ] This patch optimizes the tracepoint by replacing these hardcoded strings with a new enumeration f2fs_cp_phase. 1.Defines enum f2fs_cp_phase with values for each checkpoint phase. 2.Updates trace_f2fs_write_checkpoint to accept a u16 phase argument instead of a string pointer. 3.Uses __print_symbolic in TP_printk to convert the enum values back to their corresponding strings for human-readable trace output. This change reduces the storage overhead for each trace event by replacing a variable-length string with a 2-byte integer, while maintaining the same readable output in ftrace. Signed-off-by: YH Lin Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Stable-dep-of: 8712353ed80f ("f2fs: fix to do sanity check on f2fs_get_node_folio_ra()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/checkpoint.c | 6 +++--- fs/f2fs/f2fs.h | 6 ++++++ include/trace/events/f2fs.h | 19 ++++++++++++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 739f46eb866c8..66d3714dda32a 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1685,7 +1685,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) goto out; } - trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops"); + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_START_BLOCK_OPS); err = block_operations(sbi); if (err) @@ -1693,7 +1693,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) stat_cp_time(cpc, CP_TIME_OP_LOCK); - trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops"); + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_FINISH_BLOCK_OPS); f2fs_flush_merged_writes(sbi); @@ -1759,7 +1759,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* update CP_TIME to trigger checkpoint periodically */ f2fs_update_time(sbi, CP_TIME); - trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint"); + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_FINISH_CHECKPOINT); out: if (cpc->reason != CP_RESIZE) f2fs_up_write(&sbi->cp_global_sem); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b96e5bc66ef7d..163ce8e2a8f52 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -319,6 +319,12 @@ struct cp_control { struct cp_stats stats; }; +enum f2fs_cp_phase { + CP_PHASE_START_BLOCK_OPS, + CP_PHASE_FINISH_BLOCK_OPS, + CP_PHASE_FINISH_CHECKPOINT, +}; + /* * indicate meta/data type */ diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index edbbd869078f0..abf5bc7dad9a0 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -50,6 +50,9 @@ TRACE_DEFINE_ENUM(CP_PAUSE); TRACE_DEFINE_ENUM(CP_RESIZE); TRACE_DEFINE_ENUM(EX_READ); TRACE_DEFINE_ENUM(EX_BLOCK_AGE); +TRACE_DEFINE_ENUM(CP_PHASE_START_BLOCK_OPS); +TRACE_DEFINE_ENUM(CP_PHASE_FINISH_BLOCK_OPS); +TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); #define show_block_type(type) \ __print_symbolic(type, \ @@ -175,6 +178,12 @@ TRACE_DEFINE_ENUM(EX_BLOCK_AGE); #define S_ALL_PERM (S_ISUID | S_ISGID | S_ISVTX | \ S_IRWXU | S_IRWXG | S_IRWXO) +#define show_cp_phase(phase) \ + __print_symbolic(phase, \ + { CP_PHASE_START_BLOCK_OPS, "start block_ops" }, \ + { CP_PHASE_FINISH_BLOCK_OPS, "finish block_ops" }, \ + { CP_PHASE_FINISH_CHECKPOINT, "finish checkpoint" }) + struct f2fs_sb_info; struct f2fs_io_info; struct extent_info; @@ -1541,26 +1550,26 @@ TRACE_EVENT(f2fs_readpages, TRACE_EVENT(f2fs_write_checkpoint, - TP_PROTO(struct super_block *sb, int reason, const char *msg), + TP_PROTO(struct super_block *sb, int reason, u16 phase), - TP_ARGS(sb, reason, msg), + TP_ARGS(sb, reason, phase), TP_STRUCT__entry( __field(dev_t, dev) __field(int, reason) - __string(dest_msg, msg) + __field(u16, phase) ), TP_fast_assign( __entry->dev = sb->s_dev; __entry->reason = reason; - __assign_str(dest_msg); + __entry->phase = phase; ), TP_printk("dev = (%d,%d), checkpoint for %s, state = %s", show_dev(__entry->dev), show_cpreason(__entry->reason), - __get_str(dest_msg)) + show_cp_phase(__entry->phase)) ); DECLARE_EVENT_CLASS(f2fs_discard, From 3f42fbd3c891dc05de20301903b85a181b5309f1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 4 Jul 2026 09:01:05 -0400 Subject: [PATCH 0129/3297] f2fs: detect more inconsistent cases in sanity_check_node_footer() [ Upstream commit 93ffb6c28ff180560d2d7313ac106efcd9e012b8 ] Let's enhance sanity_check_node_footer() to detect more inconsistent cases as below: Node Type Node Footer Info =================== ============================= NODE_TYPE_REGULAR inode = true and xnode = true NODE_TYPE_INODE inode = false or xnode = true NODE_TYPE_XATTR inode = true or xnode = false NODE_TYPE_NON_INODE inode = false Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Stable-dep-of: 8712353ed80f ("f2fs: fix to do sanity check on f2fs_get_node_folio_ra()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/node.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index feb152c21d2eb..9c28e8f451baf 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1504,20 +1504,29 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, struct folio *folio, pgoff_t nid, enum node_type ntype, bool in_irq) { + bool is_inode, is_xnode; + if (unlikely(nid != nid_of_node(folio))) goto out_err; + is_inode = IS_INODE(folio); + is_xnode = f2fs_has_xattr_block(ofs_of_node(folio)); + switch (ntype) { + case NODE_TYPE_REGULAR: + if (is_inode && is_xnode) + goto out_err; + break; case NODE_TYPE_INODE: - if (!IS_INODE(folio)) + if (!is_inode || is_xnode) goto out_err; break; case NODE_TYPE_XATTR: - if (!f2fs_has_xattr_block(ofs_of_node(folio))) + if (is_inode || !is_xnode) goto out_err; break; case NODE_TYPE_NON_INODE: - if (IS_INODE(folio)) + if (is_inode) goto out_err; break; default: From 406c28af75123432d38cf9bbaa6f1476f7b14770 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 4 Jul 2026 09:01:06 -0400 Subject: [PATCH 0130/3297] f2fs: fix to do sanity check on f2fs_get_node_folio_ra() [ Upstream commit 8712353ed80f87271d732297567dcdbe4b84e8c7 ] kernel BUG at fs/f2fs/file.c:845! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI CPU: 0 UID: 0 PID: 5336 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 RIP: 0010:f2fs_do_truncate_blocks+0x1115/0x1140 fs/f2fs/file.c:845 Code: fc fc 90 0f 0b e8 8b 9d 9a fd 90 0f 0b e8 83 9d 9a fd 48 89 df 48 c7 c6 60 d1 1a 8c e8 54 f1 fc fc 90 0f 0b e8 6c 9d 9a fd 90 <0f> 0b e8 64 9d 9a fd 90 0f 0b 90 e9 93 fd ff ff e8 56 9d 9a fd 90 RSP: 0018:ffffc9000e4474c0 EFLAGS: 00010283 RAX: ffffffff842b1d34 RBX: 0000000000000003 RCX: 0000000000100000 RDX: ffffc9000f03a000 RSI: 0000000000035503 RDI: 0000000000035504 RBP: ffffc9000e447608 R08: ffff8880123b0000 R09: 0000000000000002 R10: 00000000fffffffe R11: 0000000000000002 R12: 0000000000000001 R13: 0000000000000000 R14: 1ffff92001c88ea0 R15: 00000000ffff039c FS: 00007f7e02ee36c0(0000) GS:ffff88808c887000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff0305c4000 CR3: 0000000012d4c000 CR4: 0000000000352ef0 Call Trace: f2fs_truncate_blocks+0x10a/0x300 fs/f2fs/file.c:882 f2fs_truncate+0x471/0x7c0 fs/f2fs/file.c:940 f2fs_evict_inode+0xa3f/0x1ac0 fs/f2fs/inode.c:907 evict+0x61e/0xb10 fs/inode.c:841 f2fs_fill_super+0x5f43/0x78f0 fs/f2fs/super.c:5224 get_tree_bdev_flags+0x431/0x4f0 fs/super.c:1694 vfs_get_tree+0x92/0x2a0 fs/super.c:1754 fc_mount fs/namespace.c:1193 [inline] do_new_mount_fc fs/namespace.c:3758 [inline] do_new_mount+0x341/0xd30 fs/namespace.c:3834 do_mount fs/namespace.c:4167 [inline] __do_sys_mount fs/namespace.c:4383 [inline] __se_sys_mount+0x31d/0x420 fs/namespace.c:4360 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f count = ADDRS_PER_PAGE(dn.node_folio, inode); count -= dn.ofs_in_node; f2fs_bug_on(sbi, count < 0); The fuzz test will trigger above bug_on in f2fs. The root cause should be: in the corrupted inode, there is a direct node which has the same ino and nid in its footer, so in f2fs_do_truncate_blocks(), after f2fs_get_dnode_of_data() finds such dnode: 1) ADDRS_PER_PAGE(dn.node_folio, inode) will return 923 2) once dn.ofs_in_node points to addr[923, 1017] Then it will trigger the system panic. Let's introduce NODE_TYPE_NON_IXNODE to indicate current node should not be an inode or xattr node, and then use it in below path to detect inconsistent node chain in inode mapping table: - f2fs_do_truncate_blocks - f2fs_get_dnode_of_data - f2fs_get_node_folio_ra - __get_node_folio - f2fs_sanity_check_node_footer - case NODE_TYPE_NON_IXNODE -> check whether it is inode|xnode Cc: stable@kernel.org Reported-by: syzbot+2488d8d751b27f7ce268@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/69fa3697.170a0220.59368.0018.GAE@google.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 163ce8e2a8f52..f1d7a22293b37 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1533,6 +1533,7 @@ enum node_type { NODE_TYPE_INODE, NODE_TYPE_XATTR, NODE_TYPE_NON_INODE, + NODE_TYPE_NON_IXNODE, /* non inode and xnode */ }; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9c28e8f451baf..3918021553311 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1529,6 +1529,10 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, if (is_inode) goto out_err; break; + case NODE_TYPE_NON_IXNODE: + if (is_inode || is_xnode) + goto out_err; + break; default: break; } @@ -1622,7 +1626,7 @@ static struct folio *f2fs_get_node_folio_ra(struct folio *parent, int start) struct f2fs_sb_info *sbi = F2FS_F_SB(parent); nid_t nid = get_nid(parent, start, false); - return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_REGULAR); + return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_NON_IXNODE); } static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) From 076b1aa65f77a49bce5a48a4a55a397cfcafa2b8 Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Sat, 4 Jul 2026 11:19:43 -0400 Subject: [PATCH 0131/3297] fbdev: fbcon: fix out-of-bounds read in err_out of fbcon_do_set_font() [ Upstream commit 8fdc8c2057eea08d40ce2c8eed41ff9e451c65c2 ] When fbcon_do_set_font() fails (e.g., due to a memory allocation failure inside vc_resize() under heavy memory pressure), it jumps to the `err_out` label to roll back the console state. However, the current rollback logic forgets to restore the `hi_font` state, leading to a severe state machine corruption. Earlier in the function, `set_vc_hi_font()` might be called to change `vc->vc_hi_font_mask` and mutate the screen buffer. If `vc_resize()` subsequently fails, the `err_out` path restores `vc_font.charcount` but entirely skips rolling back the `vc_hi_font_mask` and the screen buffer. This mismatch leaves the terminal in a desynchronized state. Because `vc_hi_font_mask` remains set, the VT subsystem will still accept character indices greater than 255 from userspace and write them to the screen buffer. Subsequent rendering calls (e.g., `fbcon_putcs()`) will then use these inflated indices to access the reverted, 256-character font array, leading to a deterministic out-of-bounds read and potential kernel memory disclosure. Fix this by adding the missing rollback logic for the `hi_font` mask and screen buffer in the error path. Fixes: a5a923038d70 ("fbdev: fbcon: Properly revert changes when vc_resize() failed") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller [ Adjust context ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbcon.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 8baad7ec1b856..926905ad46a30 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2422,6 +2422,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, struct fbcon_display *p = &fb_display[vc->vc_num]; int resize, ret, old_userfont, old_width, old_height, old_charcount; u8 *old_data = vc->vc_font.data; + unsigned short old_hi_font_mask = vc->vc_hi_font_mask; resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); vc->vc_font.data = (void *)(p->fontdata = data); @@ -2475,6 +2476,12 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, vc->vc_font.height = old_height; vc->vc_font.charcount = old_charcount; + /* Restore the hi_font state and screen buffer */ + if (old_hi_font_mask && !vc->vc_hi_font_mask) + set_vc_hi_font(vc, true); + else if (!old_hi_font_mask && vc->vc_hi_font_mask) + set_vc_hi_font(vc, false); + return ret; } From c4b70c1512b8f9f33f23c2c8196dfd1210207681 Mon Sep 17 00:00:00 2001 From: Xingquan Liu Date: Fri, 19 Jun 2026 11:13:47 -0400 Subject: [PATCH 0132/3297] net/sched: dualpi2: fix GSO backlog accounting commit 05ed733b65ab977dd931e7f7ac0f62fdb81205c2 upstream. When DualPI2 splits a GSO skb into N segments, it propagates N additional packets to its parent before returning NET_XMIT_SUCCESS. The parent then accounts for the original skb once more, leaving its qlen one larger than the number of packets actually queued. With QFQ as the parent, after all real packets are dequeued, QFQ still has a non-zero qlen while its in-service aggregate has no active classes. qfq_choose_next_agg() returns NULL and qfq_dequeue() passes the result to qfq_peek_skb(), causing a NULL pointer dereference. Follow the same pattern used by tbf_segment() and taprio: count only successfully queued segments, propagate the difference between the original skb and those segments, and return NET_XMIT_SUCCESS whenever at least one segment was queued. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Cc: stable@vger.kernel.org Signed-off-by: Xingquan Liu Acked-by: Jamal Hadi Salim Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260619151447.223640-1-b1n@b1n.io Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_dualpi2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index efa32240c5a95..6beb6f5bbff1a 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -463,7 +463,7 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (IS_ERR_OR_NULL(nskb)) return qdisc_drop(skb, sch, to_free); - cnt = 1; + cnt = 0; byte_len = 0; orig_len = qdisc_pkt_len(skb); skb_list_walk_safe(nskb, nskb, next) { @@ -489,16 +489,15 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, byte_len += nskb->len; } } - if (cnt > 1) { + if (cnt > 0) { /* The caller will add the original skb stats to its * backlog, compensate this if any nskb is enqueued. */ - --cnt; - byte_len -= orig_len; + qdisc_tree_reduce_backlog(sch, 1 - cnt, + orig_len - byte_len); } - qdisc_tree_reduce_backlog(sch, -cnt, -byte_len); consume_skb(skb); - return err; + return cnt > 0 ? NET_XMIT_SUCCESS : err; } return dualpi2_enqueue_skb(skb, sch, to_free); } From 05e48af3bf58d0a73ab3e20a16c1a25a0546f379 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 15 Sep 2025 12:55:13 +1000 Subject: [PATCH 0133/3297] nfsd: move name lookup out of nfsd4_list_rec_dir() [ Upstream commit 89bd77cf436bf25e448817a662ebf76515f22863 ] nfsd4_list_rec_dir() is called with two different callbacks. One of the callbacks uses vfs_rmdir() to remove the directory. The other doesn't use the dentry at all, just the name. As only one callback needs the dentry, this patch moves the lookup into that callback. This prepares of changes to how directory operations are locked. Signed-off-by: NeilBrown Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Stable-dep-of: 4552f4e3f2c9 ("nfsd: change nfs4_client_to_reclaim() to allocate data") Signed-off-by: Sasha Levin --- fs/nfsd/nfs4recover.c | 54 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 3ea4b8a861631..8ab0b26c1f91a 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -237,7 +237,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) nfs4_reset_creds(original_cred); } -typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); +typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *); struct name_list { char name[HEXDIR_LEN]; @@ -291,24 +291,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) } status = iterate_dir(nn->rec_file, &ctx.ctx); - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); list_for_each_entry_safe(entry, tmp, &ctx.names, list) { - if (!status) { - struct dentry *dentry; - dentry = lookup_one(&nop_mnt_idmap, - &QSTR(entry->name), dir); - if (IS_ERR(dentry)) { - status = PTR_ERR(dentry); - break; - } - status = f(dir, dentry, nn); - dput(dentry); - } + if (!status) + status = f(dir, entry->name, nn); + list_del(&entry->list); kfree(entry); } - inode_unlock(d_inode(dir)); nfs4_reset_creds(original_cred); list_for_each_entry_safe(entry, tmp, &ctx.names, list) { @@ -406,18 +396,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) } static int -purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) +purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn) { int status; + struct dentry *child; struct xdr_netobj name; - if (child->d_name.len != HEXDIR_LEN - 1) { - printk("%s: illegal name %pd in recovery directory\n", - __func__, child); + if (strlen(cname) != HEXDIR_LEN - 1) { + printk("%s: illegal name %s in recovery directory\n", + __func__, cname); /* Keep trying; maybe the others are OK: */ return 0; } - name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); + name.data = kstrdup(cname, GFP_KERNEL); if (!name.data) { dprintk("%s: failed to allocate memory for name.data!\n", __func__); @@ -427,10 +418,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) if (nfs4_has_reclaimed_state(name, nn)) goto out_free; - status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); - if (status) - printk("failed to remove client recovery directory %pd\n", - child); + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + child = lookup_one(&nop_mnt_idmap, &QSTR(cname), parent); + if (!IS_ERR(child)) { + status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); + if (status) + printk("failed to remove client recovery directory %pd\n", + child); + dput(child); + } + inode_unlock(d_inode(parent)); + out_free: kfree(name.data); out: @@ -461,18 +459,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) } static int -load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) +load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) { struct xdr_netobj name; struct xdr_netobj princhash = { .len = 0, .data = NULL }; - if (child->d_name.len != HEXDIR_LEN - 1) { - printk("%s: illegal name %pd in recovery directory\n", - __func__, child); + if (strlen(cname) != HEXDIR_LEN - 1) { + printk("%s: illegal name %s in recovery directory\n", + __func__, cname); /* Keep trying; maybe the others are OK: */ return 0; } - name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL); + name.data = kstrdup(cname, GFP_KERNEL); if (!name.data) { dprintk("%s: failed to allocate memory for name.data!\n", __func__); From 2539f67b75461f21cb0ff66127b910988f97dc51 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 8 Sep 2025 11:38:33 +1000 Subject: [PATCH 0134/3297] nfsd: change nfs4_client_to_reclaim() to allocate data [ Upstream commit 4552f4e3f2c96597914f07b060d5c5db84420ddd ] The calling convention for nfs4_client_to_reclaim() is clumsy in that the caller needs to free memory if the function fails. It is much cleaner if the function frees its own memory. This patch changes nfs4_client_to_reclaim() to re-allocate the .data fields to be stored in the newly allocated struct nfs4_client_reclaim, and to free everything on failure. __cld_pipe_inprogress_downcall() needs to allocate the data anyway to copy it from user-space, so now that data is allocated twice. I think that is a small price to pay for a cleaner interface. Signed-off-by: NeilBrown Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4recover.c | 67 +++++++++++++++---------------------------- fs/nfsd/nfs4state.c | 22 ++++++++++++-- 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 8ab0b26c1f91a..8c5b5b8a5c880 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -147,24 +147,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error) static void __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp, - const char *dname, int len, struct nfsd_net *nn) + char *dname, struct nfsd_net *nn) { - struct xdr_netobj name; + struct xdr_netobj name = { .len = strlen(dname), .data = dname }; struct xdr_netobj princhash = { .len = 0, .data = NULL }; struct nfs4_client_reclaim *crp; - name.data = kmemdup(dname, len, GFP_KERNEL); - if (!name.data) { - dprintk("%s: failed to allocate memory for name.data!\n", - __func__); - return; - } - name.len = len; crp = nfs4_client_to_reclaim(name, princhash, nn); - if (!crp) { - kfree(name.data); - return; - } crp->cr_clp = clp; } @@ -223,8 +212,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) inode_unlock(d_inode(dir)); if (status == 0) { if (nn->in_grace) - __nfsd4_create_reclaim_record_grace(clp, dname, - HEXDIR_LEN, nn); + __nfsd4_create_reclaim_record_grace(clp, dname, nn); vfs_fsync(nn->rec_file, 0); } else { printk(KERN_ERR "NFSD: failed to write recovery record" @@ -461,7 +449,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) static int load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) { - struct xdr_netobj name; + struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname }; struct xdr_netobj princhash = { .len = 0, .data = NULL }; if (strlen(cname) != HEXDIR_LEN - 1) { @@ -470,16 +458,7 @@ load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn) /* Keep trying; maybe the others are OK: */ return 0; } - name.data = kstrdup(cname, GFP_KERNEL); - if (!name.data) { - dprintk("%s: failed to allocate memory for name.data!\n", - __func__); - goto out; - } - name.len = HEXDIR_LEN; - if (!nfs4_client_to_reclaim(name, princhash, nn)) - kfree(name.data); -out: + nfs4_client_to_reclaim(name, princhash, nn); return 0; } @@ -777,6 +756,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, { uint8_t cmd, princhashlen; struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; + char *namecopy __free(kfree) = NULL; + char *princhashcopy __free(kfree) = NULL; uint16_t namelen; if (get_user(cmd, &cmsg->cm_cmd)) { @@ -794,20 +775,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, dprintk("%s: invalid namelen (%u)", __func__, namelen); return -EINVAL; } - name.data = memdup_user(&ci->cc_name.cn_id, namelen); - if (IS_ERR(name.data)) - return PTR_ERR(name.data); + namecopy = memdup_user(&ci->cc_name.cn_id, namelen); + if (IS_ERR(namecopy)) + return PTR_ERR(namecopy); + name.data = namecopy; name.len = namelen; if (get_user(princhashlen, &ci->cc_princhash.cp_len)) return -EFAULT; if (princhashlen > 0) { - princhash.data = memdup_user( - &ci->cc_princhash.cp_data, - princhashlen); - if (IS_ERR(princhash.data)) { - kfree(name.data); - return PTR_ERR(princhash.data); - } + princhashcopy = memdup_user( + &ci->cc_princhash.cp_data, + princhashlen); + if (IS_ERR(princhashcopy)) + return PTR_ERR(princhashcopy); + princhash.data = princhashcopy; princhash.len = princhashlen; } else princhash.len = 0; @@ -821,9 +802,10 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, dprintk("%s: invalid namelen (%u)", __func__, namelen); return -EINVAL; } - name.data = memdup_user(&cnm->cn_id, namelen); - if (IS_ERR(name.data)) - return PTR_ERR(name.data); + namecopy = memdup_user(&cnm->cn_id, namelen); + if (IS_ERR(namecopy)) + return PTR_ERR(namecopy); + name.data = namecopy; name.len = namelen; } #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING @@ -831,15 +813,12 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, struct cld_net *cn = nn->cld_net; name.len = name.len - 5; - memmove(name.data, name.data + 5, name.len); + name.data = name.data + 5; cn->cn_has_legacy = true; } #endif - if (!nfs4_client_to_reclaim(name, princhash, nn)) { - kfree(name.data); - kfree(princhash.data); + if (!nfs4_client_to_reclaim(name, princhash, nn)) return -EFAULT; - } return nn->client_tracking_ops->msglen; } return -EFAULT; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a063a4a83ec22..a3e6076a3e645 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -8831,9 +8831,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) /* * failure => all reset bets are off, nfserr_no_grace... - * - * The caller is responsible for freeing name.data if NULL is returned (it - * will be freed in nfs4_remove_reclaim_record in the normal case). */ struct nfs4_client_reclaim * nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, @@ -8842,6 +8839,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, unsigned int strhashval; struct nfs4_client_reclaim *crp; + name.data = kmemdup(name.data, name.len, GFP_KERNEL); + if (!name.data) { + dprintk("%s: failed to allocate memory for name.data!\n", + __func__); + return NULL; + } + if (princhash.len) { + princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL); + if (!princhash.data) { + dprintk("%s: failed to allocate memory for princhash.data!\n", + __func__); + kfree(name.data); + return NULL; + } + } else + princhash.data = NULL; crp = alloc_reclaim(); if (crp) { strhashval = clientstr_hashval(name); @@ -8853,6 +8866,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, crp->cr_princhash.len = princhash.len; crp->cr_clp = NULL; nn->reclaim_str_hashtbl_size++; + } else { + kfree(name.data); + kfree(princhash.data); } return crp; } From 1c2672781b1b30533bdd7d4f50e4573256c9e7ff Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Wed, 8 Jul 2026 16:13:57 +0100 Subject: [PATCH 0135/3297] mm/khugepaged: write all dirty file folios when collapsing [There is no upstream commit, as this code was removed by upstream commit 044925f9b565 ("mm: fs: remove filemap_nr_thps*() functions and their users")] As-is, khugepaged and writable-file opening exclude each other. A file cannot be open writeable and have THPs (because the filesystem is not aware of them). khugepaged will never collapse file pages for files that are opened writeable. On an open(O_RDWR/O_WRONLY), the page cache for that particular file is dropped. This is fine because nothing could've been dirtied. However, there is an edge-case: collapse_file() might not be able to coexist with concurrent writers, but it can coexist with dirty folios (from previous writers). Therefore, the following can happen: open(file, O_RDWR) write(file) close(file) madvise(file_mapping, MADV_COLLAPSE, some non-dirty range) open(file, O_RDWR) nr_thps > 0 truncate_inode_pages() /* THPs are cleared out, but so are the dirty folios */ When this edge-case happens, there is data loss, as the dirty folios are fully discarded. Fix it by fully writing back the page cache (and waiting) when collapsing file THPs. Doing so provides the guarantee that no dirty folio will be observed while there are active THPs. To fully ensure this is safe, the invalidate_lock needs to be held while doing the writeout, so that do_dentry_open()'s page cache truncation excludes this write-and-wait. As a side effect, move the nr_thps counter bumping outside the i_pages lock. This is correct since the counter itself is an atomic_t and the producer <-> consumer correctness is provided by a full memory barrier: smp_mb() in collapse_file()/memory barrier implied by full ordering in get_write_access() -> atomic_inc_unless_negative(). Cc: stable@vger.kernel.org Cc: Alexander Viro Cc: Christian Brauner Cc: Jan Kara Cc: Matthew Wilcox Cc: Song Liu Cc: Eric Hagberg Cc: Zi Yan Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS") Reported-by: Gregg Leventhal Closes: https://lore.kernel.org/linux-mm/CAFN_u7H_0ECF3jixP=T=U7AH5=Q3wQNvJMo8an3VqUDMerQfUw@mail.gmail.com/ Tested-by: Zi Yan Tested-by: Lance Yang Signed-off-by: Pedro Falcato Acked-by: David Hildenbrand (Arm) Reviewed-by: Matthew Wilcox (Oracle) Signed-off-by: Sasha Levin --- mm/khugepaged.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 3dcd884c844e8..01a5d443ffc58 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2077,32 +2077,43 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr, goto xa_unlocked; } - if (!is_shmem) { +xa_locked: + xas_unlock_irq(&xas); +xa_unlocked: + + /* + * If collapse is successful, flush must be done now before copying. + * If collapse is unsuccessful, does flush actually need to be done? + * Do it anyway, to clear the state. + */ + try_to_unmap_flush(); + + if (result == SCAN_SUCCEED && !is_shmem && !mapping_large_folio_support(mapping)) { + /* + * invalidate_lock as shared excludes against concurrent opens + * in do_dentry_open() truncating the page cache. This is + * particularly important if there are dirty folios in transit. + */ + filemap_invalidate_lock_shared(mapping); filemap_nr_thps_inc(mapping); /* * Paired with the fence in do_dentry_open() -> get_write_access() * to ensure i_writecount is up to date and the update to nr_thps * is visible. Ensures the page cache will be truncated if the - * file is opened writable. + * file is opened writable. If collapse looks to be successful, + * flush any dirty pages out the page cache. With the nr_thps + * incremented, there won't be any new writers (nor new dirties). */ smp_mb(); - if (inode_is_open_for_write(mapping->host)) { + if (inode_is_open_for_write(mapping->host) || filemap_write_and_wait(mapping)) { result = SCAN_FAIL; filemap_nr_thps_dec(mapping); + filemap_invalidate_unlock_shared(mapping); + goto rollback; } + filemap_invalidate_unlock_shared(mapping); } -xa_locked: - xas_unlock_irq(&xas); -xa_unlocked: - - /* - * If collapse is successful, flush must be done now before copying. - * If collapse is unsuccessful, does flush actually need to be done? - * Do it anyway, to clear the state. - */ - try_to_unmap_flush(); - if (result == SCAN_SUCCEED && nr_none && !shmem_charge(mapping->host, nr_none)) result = SCAN_FAIL; From 2dca62902eb3501432ee9bd098fce878cfbb2c1a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Dec 2025 15:30:34 -0800 Subject: [PATCH 0136/3297] slab: Introduce kmalloc_flex() and family commit e4c8b46b924eb8de66c6f0accc9cdd0c2e8fa23b upstream. As done for kmalloc_obj*(), introduce a type-aware allocator for flexible arrays, which may also have "counted_by" annotations: ptr = kmalloc(struct_size(ptr, flex_member, count), gfp); becomes: ptr = kmalloc_flex(*ptr, flex_member, count, gfp); The internal use of __flex_counter() allows for automatically setting the counter member of a struct's flexible array member when it has been annotated with __counted_by(), avoiding any missed early size initializations while __counted_by() annotations are added to the kernel. Additionally, this also checks for "too large" allocations based on the type size of the counter variable. For example: if (count > type_max(ptr->flex_counter)) fail...; size = struct_size(ptr, flex_member, count); ptr = kmalloc(size, gfp); if (!ptr) fail...; ptr->flex_counter = count; becomes (n.b. unchanged from earlier example): ptr = kmalloc_flex(*ptr, flex_member, count, gfp); if (!ptr) fail...; ptr->flex_counter = count; Note that manual initialization of the flexible array counter is still required (at some point) after allocation as not all compiler versions support the __counted_by annotation yet. But doing it internally makes sure they cannot be missed when __counted_by _is_ available, meaning that the bounds checker will not trip due to the lack of "early enough" initializations that used to work before enabling the stricter bounds checking. For example: ptr = kmalloc_flex(*ptr, flex_member, count, gfp); fill(ptr->flex, count); ptr->flex_count = count; This works correctly before adding a __counted_by annotation (since nothing is checking ptr->flex accesses against ptr->flex_count). After adding the annotation, the bounds sanitizer would trip during fill() because ptr->flex_count wasn't set yet. But with kmalloc_flex() setting ptr->flex_count internally at allocation time, the existing code works without needing to move the ptr->flex_count assignment before the call to fill(). (This has been a stumbling block for __counted_by adoption.) Link: https://patch.msgid.link/20251203233036.3212363-4-kees@kernel.org Acked-by: Vlastimil Babka Signed-off-by: Kees Cook [Backport-notes: Removed the actual flex counter handling. That's a new feature, which isn't necessary for just adding the new allocation APIs to get backports to apply cleanly. Also, the allocation-time overflow check in the upstream commit was reverted upstream.] Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- Documentation/process/deprecated.rst | 7 +++++ include/linux/slab.h | 42 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst index 91c628fa2d59c..fed56864d0363 100644 --- a/Documentation/process/deprecated.rst +++ b/Documentation/process/deprecated.rst @@ -387,6 +387,7 @@ allocations. For example, these open coded assignments:: ptr = kzalloc(sizeof(*ptr), gfp); ptr = kmalloc_array(count, sizeof(*ptr), gfp); ptr = kcalloc(count, sizeof(*ptr), gfp); + ptr = kmalloc(struct_size(ptr, flex_member, count), gfp); ptr = kmalloc(sizeof(struct foo, gfp); become, respectively:: @@ -395,4 +396,10 @@ become, respectively:: ptr = kzalloc_obj(*ptr, gfp); ptr = kmalloc_objs(*ptr, count, gfp); ptr = kzalloc_objs(*ptr, count, gfp); + ptr = kmalloc_flex(*ptr, flex_member, count, gfp); __auto_type ptr = kmalloc_obj(struct foo, gfp); + +If `ptr->flex_member` is annotated with __counted_by(), the allocation +will automatically fail if `count` is larger than the maximum +representable value that can be stored in the counter member associated +with `flex_member`. diff --git a/include/linux/slab.h b/include/linux/slab.h index cbb64a2698f5d..67f5b9831c56b 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -982,6 +982,27 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); (TYPE *)KMALLOC(__obj_size, GFP); \ }) +/** + * __alloc_flex - Allocate an object that has a trailing flexible array + * @KMALLOC: kmalloc wrapper function to use for allocation. + * @GFP: GFP flags for the allocation. + * @TYPE: type of structure to allocate space for. + * @FAM: The name of the flexible array member of @TYPE structure. + * @COUNT: how many @FAM elements to allocate space for. + * + * Returns: Newly allocated pointer to @TYPE with @COUNT-many trailing + * @FAM elements, or NULL on failure or if @COUNT cannot be represented + * by the member of @TYPE that counts the @FAM elements (annotated via + * __counted_by()). + */ +#define __alloc_flex(KMALLOC, GFP, TYPE, FAM, COUNT) \ +({ \ + const size_t __count = (COUNT); \ + const size_t __obj_size = struct_size_t(TYPE, FAM, __count); \ + TYPE *__obj_ptr = KMALLOC(__obj_size, GFP); \ + __obj_ptr; \ +}) + /** * kmalloc_obj - Allocate a single instance of the given type * @VAR_OR_TYPE: Variable or type to allocate. @@ -1005,23 +1026,44 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); #define kmalloc_objs(VAR_OR_TYPE, COUNT, GFP) \ __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), COUNT) +/** + * kmalloc_flex - Allocate a single instance of the given flexible structure + * @VAR_OR_TYPE: Variable or type to allocate (with its flex array). + * @FAM: The name of the flexible array member of the structure. + * @COUNT: How many flexible array member elements are desired. + * @GFP: GFP flags for the allocation. + * + * Returns: newly allocated pointer to @VAR_OR_TYPE on success, NULL on + * failure. If @FAM has been annotated with __counted_by(), the allocation + * will immediately fail if @COUNT is larger than what the type of the + * struct's counter variable can represent. + */ +#define kmalloc_flex(VAR_OR_TYPE, FAM, COUNT, GFP) \ + __alloc_flex(kmalloc, GFP, typeof(VAR_OR_TYPE), FAM, COUNT) + /* All kzalloc aliases for kmalloc_(obj|objs|flex). */ #define kzalloc_obj(P, GFP) \ __alloc_objs(kzalloc, GFP, typeof(P), 1) #define kzalloc_objs(P, COUNT, GFP) \ __alloc_objs(kzalloc, GFP, typeof(P), COUNT) +#define kzalloc_flex(P, FAM, COUNT, GFP) \ + __alloc_flex(kzalloc, GFP, typeof(P), FAM, COUNT) /* All kvmalloc aliases for kmalloc_(obj|objs|flex). */ #define kvmalloc_obj(P, GFP) \ __alloc_objs(kvmalloc, GFP, typeof(P), 1) #define kvmalloc_objs(P, COUNT, GFP) \ __alloc_objs(kvmalloc, GFP, typeof(P), COUNT) +#define kvmalloc_flex(P, FAM, COUNT, GFP) \ + __alloc_flex(kvmalloc, GFP, typeof(P), FAM, COUNT) /* All kvzalloc aliases for kmalloc_(obj|objs|flex). */ #define kvzalloc_obj(P, GFP) \ __alloc_objs(kvzalloc, GFP, typeof(P), 1) #define kvzalloc_objs(P, COUNT, GFP) \ __alloc_objs(kvzalloc, GFP, typeof(P), COUNT) +#define kvzalloc_flex(P, FAM, COUNT, GFP) \ + __alloc_flex(kvzalloc, GFP, typeof(P), FAM, COUNT) #define kmem_buckets_alloc(_b, _size, _flags) \ alloc_hooks(__kmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, NUMA_NO_NODE)) From 50c26b461b8e7b547c7ee71deca9db3ecf4d1036 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 21 Feb 2026 16:14:11 -0800 Subject: [PATCH 0137/3297] add default_gfp() helper macro and use it in the new *alloc_obj() helpers commit e19e1b480ac73c3e62ffebbca1174f0f511f43e7 upstream. Most simple allocations use GFP_KERNEL, and with the new allocation helpers being introduced, let's just take advantage of that to simplify that default case. It's a numbers game: git grep 'alloc_obj(' | sed 's/.*\(GFP_[_A-Z]*\).*/\1/' | sort | uniq -c | sort -n | tail shows that about 90% of all those new allocator instances just use that standard GFP_KERNEL. Those helpers are already macros, and we can easily just make it be the default case when the gfp argument is missing. And yes, we could do that for all the legacy interfaces too, but let's keep it to just the new ones at least for now, since those all got converted recently anyway, so this is not any "extra" noise outside of that limited conversion. And, in fact, I want to do this before doing the -rc1 release, exactly so that we don't get extra merge conflicts. Signed-off-by: Linus Torvalds Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- include/linux/gfp.h | 4 ++++ include/linux/slab.h | 48 ++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index b155929af5b11..09559f7126ac7 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -13,6 +13,10 @@ struct vm_area_struct; struct mempolicy; +/* Helper macro to avoid gfp flags if they are the default one */ +#define __default_gfp(a,...) a +#define default_gfp(...) __default_gfp(__VA_ARGS__ __VA_OPT__(,) GFP_KERNEL) + /* Convert GFP flags to their corresponding migrate type */ #define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE) #define GFP_MOVABLE_SHIFT 3 diff --git a/include/linux/slab.h b/include/linux/slab.h index 67f5b9831c56b..976c781a78420 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1011,8 +1011,8 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); * Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL * on failure. */ -#define kmalloc_obj(VAR_OR_TYPE, GFP) \ - __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), 1) +#define kmalloc_obj(VAR_OR_TYPE, ...) \ + __alloc_objs(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), 1) /** * kmalloc_objs - Allocate an array of the given type @@ -1023,8 +1023,8 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); * Returns: newly allocated pointer to array of @VAR_OR_TYPE on success, * or NULL on failure. */ -#define kmalloc_objs(VAR_OR_TYPE, COUNT, GFP) \ - __alloc_objs(kmalloc, GFP, typeof(VAR_OR_TYPE), COUNT) +#define kmalloc_objs(VAR_OR_TYPE, COUNT, ...) \ + __alloc_objs(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), COUNT) /** * kmalloc_flex - Allocate a single instance of the given flexible structure @@ -1038,32 +1038,32 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); * will immediately fail if @COUNT is larger than what the type of the * struct's counter variable can represent. */ -#define kmalloc_flex(VAR_OR_TYPE, FAM, COUNT, GFP) \ - __alloc_flex(kmalloc, GFP, typeof(VAR_OR_TYPE), FAM, COUNT) +#define kmalloc_flex(VAR_OR_TYPE, FAM, COUNT, ...) \ + __alloc_flex(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), FAM, COUNT) /* All kzalloc aliases for kmalloc_(obj|objs|flex). */ -#define kzalloc_obj(P, GFP) \ - __alloc_objs(kzalloc, GFP, typeof(P), 1) -#define kzalloc_objs(P, COUNT, GFP) \ - __alloc_objs(kzalloc, GFP, typeof(P), COUNT) -#define kzalloc_flex(P, FAM, COUNT, GFP) \ - __alloc_flex(kzalloc, GFP, typeof(P), FAM, COUNT) +#define kzalloc_obj(P, ...) \ + __alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define kzalloc_objs(P, COUNT, ...) \ + __alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT) +#define kzalloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(kzalloc, default_gfp(__VA_ARGS__), typeof(P), FAM, COUNT) /* All kvmalloc aliases for kmalloc_(obj|objs|flex). */ -#define kvmalloc_obj(P, GFP) \ - __alloc_objs(kvmalloc, GFP, typeof(P), 1) -#define kvmalloc_objs(P, COUNT, GFP) \ - __alloc_objs(kvmalloc, GFP, typeof(P), COUNT) -#define kvmalloc_flex(P, FAM, COUNT, GFP) \ - __alloc_flex(kvmalloc, GFP, typeof(P), FAM, COUNT) +#define kvmalloc_obj(P, ...) \ + __alloc_objs(kvmalloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define kvmalloc_objs(P, COUNT, ...) \ + __alloc_objs(kvmalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT) +#define kvmalloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(kvmalloc, default_gfp(__VA_ARGS__), typeof(P), FAM, COUNT) /* All kvzalloc aliases for kmalloc_(obj|objs|flex). */ -#define kvzalloc_obj(P, GFP) \ - __alloc_objs(kvzalloc, GFP, typeof(P), 1) -#define kvzalloc_objs(P, COUNT, GFP) \ - __alloc_objs(kvzalloc, GFP, typeof(P), COUNT) -#define kvzalloc_flex(P, FAM, COUNT, GFP) \ - __alloc_flex(kvzalloc, GFP, typeof(P), FAM, COUNT) +#define kvzalloc_obj(P, ...) \ + __alloc_objs(kvzalloc, default_gfp(__VA_ARGS__), typeof(P), 1) +#define kvzalloc_objs(P, COUNT, ...) \ + __alloc_objs(kvzalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT) +#define kvzalloc_flex(P, FAM, COUNT, ...) \ + __alloc_flex(kvzalloc, default_gfp(__VA_ARGS__), typeof(P), FAM, COUNT) #define kmem_buckets_alloc(_b, _size, _flags) \ alloc_hooks(__kmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, NUMA_NO_NODE)) From 1776f29327a136aed6c80c720740489d0b6d015c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 23 Feb 2026 09:33:08 -0800 Subject: [PATCH 0138/3297] default_gfp(): avoid using the "newfangled" __VA_OPT__ trick commit 551d44200152cb26f75d2ef990aeb6185b7e37fd upstream. The default_gfp() helper that I added is not wrong, but it turns out that it causes unnecessary headaches for 'sparse' which doesn't support the use of __VA_OPT__ (introduced in C++20 and C23, and supported by gcc and clang for a long time). We do already use __VA_OPT__ in some other cases in the kernel (drm/xe and btrfs), but it has been fairly limited. Now it triggers for pretty much everything, and sparse ends up not working at all. We can use the traditional gcc ',##__VA_ARGS__' syntax instead: it may not be the "C standard" way and is slightly less natural in this context, but it is the traditional model for this and avoids the sparse problem. Reported-and-tested-by: Ricardo Ribalda Reported-and-tested-by: Richard Fitzgerald Reported-by: Ben Dooks Fixes: e19e1b480ac7 ("add default_gfp() helper macro and use it in the new *alloc_obj() helpers") Signed-off-by: Linus Torvalds Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- include/linux/gfp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 09559f7126ac7..647b3db8a7574 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -14,8 +14,8 @@ struct vm_area_struct; struct mempolicy; /* Helper macro to avoid gfp flags if they are the default one */ -#define __default_gfp(a,...) a -#define default_gfp(...) __default_gfp(__VA_ARGS__ __VA_OPT__(,) GFP_KERNEL) +#define __default_gfp(a,b,...) b +#define default_gfp(...) __default_gfp(,##__VA_ARGS__,GFP_KERNEL) /* Convert GFP flags to their corresponding migrate type */ #define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE) From 47e4c6e06e78ec7b38bcba41ef52fe08d1952b38 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 17 Jun 2026 09:31:25 -0700 Subject: [PATCH 0139/3297] slab: recognize @GFP parameter as optional in kernel-doc commit 7b5f5865fb11e60edd03c5e063e2d228b7062317 upstream. Since the @GFP parameter in kmalloc_obj() etc. is now optional, change the kernel-doc to indicate that it is optional. This avoids kernel-doc warnings: WARNING: include/linux/slab.h:1101 Excess function parameter 'GFP' description in 'kmalloc_obj' WARNING: include/linux/slab.h:1113 Excess function parameter 'GFP' description in 'kmalloc_objs' WARNING: include/linux/slab.h:1128 Excess function parameter 'GFP' description in 'kmalloc_flex' Fixes: e19e1b480ac7 ("add default_gfp() helper macro and use it in the new *alloc_obj() helpers") Signed-off-by: Randy Dunlap Acked-by: Harry Yoo (Oracle) Link: https://patch.msgid.link/20260617163125.2716279-1-rdunlap@infradead.org Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- include/linux/slab.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 976c781a78420..22daf3f34a76f 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1006,7 +1006,7 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); /** * kmalloc_obj - Allocate a single instance of the given type * @VAR_OR_TYPE: Variable or type to allocate. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to a @VAR_OR_TYPE on success, or NULL * on failure. @@ -1018,7 +1018,7 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); * kmalloc_objs - Allocate an array of the given type * @VAR_OR_TYPE: Variable or type to allocate an array of. * @COUNT: How many elements in the array. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to array of @VAR_OR_TYPE on success, * or NULL on failure. @@ -1031,7 +1031,7 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node); * @VAR_OR_TYPE: Variable or type to allocate (with its flex array). * @FAM: The name of the flexible array member of the structure. * @COUNT: How many flexible array member elements are desired. - * @GFP: GFP flags for the allocation. + * @...: optional GFP flags for the allocation (GFP_KERNEL when not specified). * * Returns: newly allocated pointer to @VAR_OR_TYPE on success, NULL on * failure. If @FAM has been annotated with __counted_by(), the allocation From 9baa2833e6bc83180da22c532b56555e1034b2d1 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 13 May 2026 12:23:46 -0700 Subject: [PATCH 0140/3297] perf trace beauty fcntl: Fix build with older kernel headers commit 7ee7f48413c42b90230de4a8e40898b757bc8e82 upstream. Toolchains with older kernel headers that do not include upstream commit c75b1d9421f80f41 ("fs: add fcntl() interface for setting/getting write life time hints") will now fail to build perf due to missing definitions for F_GET_RW_HINT/F_SET_RW_HINT/F_GET_FILE_RW_HINT/F_SET_FILE_RW_HINT. Provide a fallback definition for these when they are not already defined. Fixes: 9c47f66748381ecb ("perf trace beauty fcntl: Basic 'arg' beautifier") Reviewed-by: Ian Rogers Signed-off-by: Florian Fainelli Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Markus Mayer Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/trace/beauty/fcntl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/trace/beauty/fcntl.c b/tools/perf/trace/beauty/fcntl.c index d075904dccced..e1b99b8f55eb0 100644 --- a/tools/perf/trace/beauty/fcntl.c +++ b/tools/perf/trace/beauty/fcntl.c @@ -9,6 +9,22 @@ #include #include +#ifndef F_GET_RW_HINT +#define F_GET_RW_HINT (F_LINUX_SPECIFIC_BASE + 11) +#endif + +#ifndef F_SET_RW_HINT +#define F_SET_RW_HINT (F_LINUX_SPECIFIC_BASE + 12) +#endif + +#ifndef F_GET_FILE_RW_HINT +#define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13) +#endif + +#ifndef F_SET_FILE_RW_HINT +#define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14) +#endif + static size_t fcntl__scnprintf_getfd(unsigned long val, char *bf, size_t size, bool show_prefix) { return val ? scnprintf(bf, size, "%s", "0") : From 0d0187a46b16efc00e4a6bf682e8423814ee693f Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Jul 2026 09:22:45 +0200 Subject: [PATCH 0141/3297] KVM: x86: Move update_cr8_intercept() to lapic.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream Commit c7722e5e1daeeabbd9f969554d52bb7158120b27 ] Move update_cr8_intercept() to lapic.c so that it's globally visible in anticipation of extracting most of the register-specific code out of x86.c and into a new compilation unit. Opportunistically prefix the helper kvm_lapic_ to make its role/scope more obvious. No functional change intended. Reviewed-by: Kai Huang Reviewed-by: Yosry Ahmed Link: https://patch.msgid.link/20260529222223.870923-14-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Carlos López Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 26 ++++++++++++++++++++++++++ arch/x86/kvm/lapic.h | 1 + arch/x86/kvm/x86.c | 34 +++------------------------------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 0e842dad4194a..4a6a8d8c7626f 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2754,6 +2754,32 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu) return (tpr & 0xf0) >> 4; } +void kvm_lapic_update_cr8_intercept(struct kvm_vcpu *vcpu) +{ + int max_irr, tpr; + + if (!kvm_x86_ops.update_cr8_intercept) + return; + + if (!lapic_in_kernel(vcpu)) + return; + + if (vcpu->arch.apic->apicv_active) + return; + + if (!vcpu->arch.apic->vapic_addr) + max_irr = kvm_lapic_find_highest_irr(vcpu); + else + max_irr = -1; + + if (max_irr != -1) + max_irr >>= 4; + + tpr = kvm_lapic_get_cr8(vcpu); + + kvm_x86_call(update_cr8_intercept)(vcpu, tpr, max_irr); +} + static void __kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value) { u64 old_value = vcpu->arch.apic_base; diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index e5f5a222eced0..959380b4c067f 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -100,6 +100,7 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu); void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event); u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8); +void kvm_lapic_update_cr8_intercept(struct kvm_vcpu *vcpu); void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu); void kvm_apic_set_version(struct kvm_vcpu *vcpu); void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a21ebe04aa23a..28771be285a13 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -126,7 +126,6 @@ static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); KVM_X2APIC_ENABLE_SUPPRESS_EOI_BROADCAST | \ KVM_X2APIC_DISABLE_SUPPRESS_EOI_BROADCAST) -static void update_cr8_intercept(struct kvm_vcpu *vcpu); static void process_nmi(struct kvm_vcpu *vcpu); static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); static void store_regs(struct kvm_vcpu *vcpu); @@ -5357,7 +5356,7 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, r = kvm_apic_set_state(vcpu, s); if (r) return r; - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); return 0; } @@ -10565,33 +10564,6 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu) kvm_run->flags |= KVM_RUN_X86_GUEST_MODE; } -static void update_cr8_intercept(struct kvm_vcpu *vcpu) -{ - int max_irr, tpr; - - if (!kvm_x86_ops.update_cr8_intercept) - return; - - if (!lapic_in_kernel(vcpu)) - return; - - if (vcpu->arch.apic->apicv_active) - return; - - if (!vcpu->arch.apic->vapic_addr) - max_irr = kvm_lapic_find_highest_irr(vcpu); - else - max_irr = -1; - - if (max_irr != -1) - max_irr >>= 4; - - tpr = kvm_lapic_get_cr8(vcpu); - - kvm_x86_call(update_cr8_intercept)(vcpu, tpr, max_irr); -} - - int kvm_check_nested_events(struct kvm_vcpu *vcpu) { if (kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu)) { @@ -11298,7 +11270,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) kvm_x86_call(enable_irq_window)(vcpu); if (kvm_lapic_enabled(vcpu)) { - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); kvm_lapic_sync_to_vapic(vcpu); } } @@ -12424,7 +12396,7 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR); kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR); - update_cr8_intercept(vcpu); + kvm_lapic_update_cr8_intercept(vcpu); /* Older userspace won't unhalt the vcpu on reset. */ if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 && From 3dcfb04dd43b16fa1240fc6487fff578ad57264c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 9 Jul 2026 09:22:46 +0200 Subject: [PATCH 0142/3297] KVM: VMX: Grab vmcs12 on CR8 interception update iff vCPU is in guest mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7ef78d71ca713d8c00f7c34ddcf276c808143f77 ] When updating CR8 intercepts, get vmcs12 if and only if the vCPU is in guest mode so that a future change can have update CR8 intercepts during vCPU creation, without running afoul of get_vmcs12()'s lockdep assertion. ------------[ cut here ]------------ debug_locks && !(lock_is_held(&(&vcpu->mutex)->dep_map) || !refcount_read(&vcpu->kvm->users_count)) WARNING: arch/x86/kvm/vmx/nested.h:61 at get_vmcs12 arch/x86/kvm/vmx/nested.h:60 [inline], CPU#0: syz.2.19/5879 WARNING: arch/x86/kvm/vmx/nested.h:61 at vmx_update_cr8_intercept+0x3de/0x4e0 arch/x86/kvm/vmx/vmx.c:6879, CPU#0: syz.2.19/5879 Modules linked in: CPU: 0 UID: 0 PID: 5879 Comm: syz.2.19 Not tainted syzkaller #0 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 RIP: 0010:get_vmcs12 arch/x86/kvm/vmx/nested.h:60 [inline] RIP: 0010:vmx_update_cr8_intercept+0x3de/0x4e0 arch/x86/kvm/vmx/vmx.c:6879 Call Trace: apic_update_ppr arch/x86/kvm/lapic.c:984 [inline] kvm_lapic_reset+0x1c24/0x2980 arch/x86/kvm/lapic.c:3023 kvm_vcpu_reset+0x44c/0x1bf0 arch/x86/kvm/x86.c:12986 kvm_arch_vcpu_create+0x746/0x8b0 arch/x86/kvm/x86.c:12847 kvm_vm_ioctl_create_vcpu+0x428/0x930 virt/kvm/kvm_main.c:4201 kvm_vm_ioctl+0x893/0xd50 virt/kvm/kvm_main.c:5159 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f No functional change intended. Reported-by: syzbot ci Closes: https://lore.kernel.org/all/6a2adf3b.3b0a2d4e.8c8d1.0012.GAE@google.com Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Message-ID: <20260618174347.1981064-2-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Carlos López Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/vmx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index b7798ced7b505..d7a2b75b6c915 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6734,11 +6734,10 @@ static noinstr void vmx_l1d_flush(struct kvm_vcpu *vcpu) void vmx_update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr) { - struct vmcs12 *vmcs12 = get_vmcs12(vcpu); int tpr_threshold; if (is_guest_mode(vcpu) && - nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW)) + nested_cpu_has(get_vmcs12(vcpu), CPU_BASED_TPR_SHADOW)) return; tpr_threshold = (irr == -1 || tpr < irr) ? 0 : irr; From ff9c4c6428883182960cfe5c78928f0896d80ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Thu, 9 Jul 2026 09:22:47 +0200 Subject: [PATCH 0143/3297] KVM: x86: Unconditionally recompute CR8 intercept on PPR update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit bb365a506b1e6fb050c0fceaad354fe395385ef0 ] The TPR_THRESHOLD field in the VMCS is used by VMX to induce VM exits when the guest's virtual TPR falls under the specified threshold, allowing KVM to inject previously masked interrupts. KVM handles these VM exits in handle_tpr_below_threshold(). Commit eb90f3417a0c ("KVM: vmx: speed up TPR below threshold vmexits") optimized this function by calling apic_update_ppr() instead of raising KVM_REQ_EVENT. apic_update_ppr() then raises KVM_REQ_EVENT if there is a pending, deliverable interrupt. However, if there are no new interrupts pending, apic_update_ppr() does not issue the request. Thus, kvm_lapic_update_cr8_intercept() and vmx_update_cr8_intercept() are not called before VM entry, which results in a high, stale TPR_THRESHOLD. This is problematic due to the following sentence in 28.2.1.1 "VM-Execution Control Fields" in the SDM: The following check is performed if the “use TPR shadow” VM-execution control is 1 and the “virtualize APIC accesses” and “virtual-interrupt delivery” VM-execution controls are both 0: the value of bits 3:0 of the TPR threshold VM-execution control field should not be greater than the value of bits 7:4 of VTPR. This error condition is typically not observed when KVM runs on a bare metal system because modern processors support APICv, which enables virtual-interrupt delivery, and which KVM uses when possible. This causes the processor to no longer generate TPR-below-threshold exits and to no longer check TPR_THRESHOLD on entry. However, when running on older platforms, or under nested virtualization on a hypervisor that does not support virtual-interrupt delivery and enforces this check (like Hyper-V) this can cause a VM entry failure with hardware error 0x7, as seen in [1]. Call kvm_lapic_update_cr8_intercept() if apic_update_ppr() does not find a deliverable interrupt (and thus does not raise KVM_REQ_EVENT). Remove calls to kvm_lapic_update_cr8_intercept() on paths that end up in apic_update_ppr(), as they now become redundant. This ensures that any path that updates the guest's PPR also figures out if KVM needs to wait for a TPR change (using TPR_THRESHOLD on VMX or CR8 intercepts on SVM). Link: https://github.com/coconut-svsm/svsm/issues/1081 [1] Tested-by: Stefano Garzarella Cc: stable@vger.kernel.org Fixes: eb90f3417a0c ("KVM: vmx: speed up TPR below threshold vmexits") Signed-off-by: Carlos López Signed-off-by: Sean Christopherson Message-ID: <20260618174347.1981064-3-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 2 ++ arch/x86/kvm/x86.c | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 4a6a8d8c7626f..fe2806348c94a 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1001,6 +1001,8 @@ static void apic_update_ppr(struct kvm_lapic *apic) if (__apic_update_ppr(apic, &ppr) && apic_has_interrupt_for_ppr(apic, ppr) != -1) kvm_make_request(KVM_REQ_EVENT, apic->vcpu); + else + kvm_lapic_update_cr8_intercept(apic->vcpu); } void kvm_apic_update_ppr(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 28771be285a13..7be3ed9e2c7dc 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5356,7 +5356,6 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, r = kvm_apic_set_state(vcpu, s); if (r) return r; - kvm_lapic_update_cr8_intercept(vcpu); return 0; } @@ -12366,8 +12365,6 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, kvm_register_mark_dirty(vcpu, VCPU_EXREG_CR3); kvm_x86_call(post_set_cr3)(vcpu, sregs->cr3); - kvm_set_cr8(vcpu, sregs->cr8); - *mmu_reset_needed |= vcpu->arch.efer != sregs->efer; kvm_x86_call(set_efer)(vcpu, sregs->efer); @@ -12396,7 +12393,7 @@ static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs, kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR); kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR); - kvm_lapic_update_cr8_intercept(vcpu); + kvm_set_cr8(vcpu, sregs->cr8); /* Older userspace won't unhalt the vcpu on reset. */ if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 && From f29dc6132d4968e39d8fa575d1a12e2c718ce57b Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 1 Jun 2026 18:58:08 -0500 Subject: [PATCH 0144/3297] ACPI: CPPC: Suppress UBSAN warning caused by field misuse commit 1b1acf2dada0cc3931bb2cb9ff8832edfbee46a1 upstream. The definition of reg->access_width changes depending on the reg->space_id type. Type ACPI_ADR_SPACE_PLATFORM_COMM uses access_width to indicate the PCC region, which can result in a UBSAN if the value is greater than 4. For example: UBSAN: shift-out-of-bounds in drivers/acpi/cppc_acpi.c:1090:9 shift exponent 32 is too large for 32-bit type 'int' CPU: 61 UID: 0 PID: 1220 Comm: (udev-worker) Not tainted 7.0.10-201.fc44.aarch64 #1 PREEMPT(lazy) Hardware name: To be filled by O.E.M. Call trace: ...(trimming) ubsan_epilogue+0x10/0x48 __ubsan_handle_shift_out_of_bounds+0xdc/0x1e0 cpc_write+0x4d0/0x670 cppc_set_perf+0x18c/0x490 cppc_cpufreq_cpu_init+0x1c8/0x380 [cppc_cpufreq] ... (trimming) Lets fix this by validating the region type, as well as whether access_width has a value. Then since we are returning bit_width directly for ACPI_ADR_SPACE_PLATFORM_COMM, drop the code correcting the size. Fixes: 2f4a4d63a193 ("ACPI: CPPC: Use access_width over bit_width for system memory accesses") Signed-off-by: Jeremy Linton Tested-by: Jarred White Reviewed-by: Jarred White Reviewed-by: Easwar Hariharan Cc: All applicable Link: https://patch.msgid.link/20260601235808.1113137-1-jeremy.linton@arm.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/cppc_acpi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index ad763a5bf3af5..60d2548f0eea4 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -185,8 +185,13 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time); -/* Check for valid access_width, otherwise, fallback to using bit_width */ -#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width) +/* + * PCC reuses the access_width field as the subspace id, so only decode access + * size for non-PCC registers. Otherwise, use the bit_width. + */ +#define GET_BIT_WIDTH(reg) (((reg)->access_width && \ + (reg)->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) ? \ + (8 << ((reg)->access_width - 1)) : (reg)->bit_width) /* Shift and apply the mask for CPC reads/writes */ #define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \ @@ -1036,7 +1041,6 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val) * by the bit width field; the access size is used to indicate * the PCC subspace id. */ - size = reg->bit_width; vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); } else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) @@ -1109,7 +1113,6 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) * by the bit width field; the access size is used to indicate * the PCC subspace id. */ - size = reg->bit_width; vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); } else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) From 452945662fd8e9862a2d2043239c7ee1815d1ac4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 3 Jun 2026 19:56:21 +0200 Subject: [PATCH 0145/3297] ACPI: NFIT: core: Fix possible NULL pointer dereference commit 027e128abb82788189d6d45b68e3e8e7329b67be upstream. After commit 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before getting NFIT table"), acpi_nfit_probe() installs an ACPI notify handler for the NFIT device before checking the presence of the NFIT table. If that table is not there, 0 is returned without allocating the acpi_desc object and setting the driver data pointer of the NFIT device. If the platform firmware triggers an NFIT_NOTIFY_UC_MEMORY_ERROR notification on the NFIT device at that point, acpi_nfit_uc_error_notify() will dereference a NULL pointer. Prevent that from occurring by adding an acpi_desc check against NULL to acpi_nfit_uc_error_notify(). Fixes: 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before getting NFIT table") Signed-off-by: Rafael J. Wysocki Cc: All applicable Reviewed-by: Dave Jiang Link: https://patch.msgid.link/2418508.ElGaqSPkdT@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/nfit/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 3eb56b77cb6d9..2eb9989372ca5 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3447,6 +3447,9 @@ static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle) { struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev); + if (!acpi_desc) + return; + if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG); else From 86df6499dfd232fbc1c82c0d6eb9322ca67b8cd0 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 6 Jun 2026 02:49:05 +0900 Subject: [PATCH 0146/3297] platform/x86: intel-hid: Protect ACPI notify handler against recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c085d82613d5618814b84406c8b2d64f1bc305e7 upstream. Since commit e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") ACPI notify handlers like the intel-hid notify_handler() may run on multiple CPU cores racing with themselves. On convertibles and detachables (matched by DMI chassis-type 31 and 32 in dmi_auto_add_switch[]) the SW_TABLET_MODE input device is registered lazily from notify_handler() on the first tablet-mode event, via intel_hid_switches_setup(). When two such events race on different CPUs both can pass the !priv->switches check and register the priv->switches input device twice, resulting in a duplicate sysfs entry and a subsequent NULL pointer dereference. This is the same class of bug fixed by commit e075c3b13a0a ("platform/x86: intel-vbtn: Protect ACPI notify handler against recursion") for the sibling intel-vbtn driver. Protect intel-hid notify_handler() from racing with itself with a mutex to fix this. Fixes: e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") Cc: stable@vger.kernel.org Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260605174905.131095-1-sammiee5311@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/hid.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index f7e358be1af3a..0dcf3a68a3355 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -7,11 +7,13 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -224,6 +226,7 @@ static const struct dmi_system_id dmi_auto_add_switch[] = { }; struct intel_hid_priv { + struct mutex mutex; /* Avoid notify_handler() racing with itself */ struct input_dev *input_dev; struct input_dev *array; struct input_dev *switches; @@ -559,6 +562,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) struct key_entry *ke; int err; + guard(mutex)(&priv->mutex); + /* * Some convertible have unreliable VGBS return which could cause incorrect * SW_TABLET_MODE report, in these cases we enable support when receiving @@ -714,6 +719,10 @@ static int intel_hid_probe(struct platform_device *device) return -ENOMEM; dev_set_drvdata(&device->dev, priv); + err = devm_mutex_init(&device->dev, &priv->mutex); + if (err) + return err; + /* See dual_accel_detect.h for more info on the dual_accel check. */ if (enable_sw_tablet_mode == TABLET_SW_AUTO) { if (dmi_check_system(dmi_vgbs_allow_list)) From 30d5d4eef35a9edf00d0ec005710c785594eeca2 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 25 Jun 2026 13:03:47 +0800 Subject: [PATCH 0147/3297] LoongArch: Add PIO for early access before ACPI PCI root register commit 6061e65f95713b01f4313cda6637dfe3aa5412b4 upstream. For ACPI system we suppose the ISA/LPC PIO range is registered together with PCI root bridge. But the fact is there may be some early access to the ISA/LPC PIO range before ACPI PCI root register (most of them are due to abnormal BIOS). Unconditionally register the ISA/LPC PIO range usually causes ACPI PCI root register fail because of the address range confliction. So we add a pair of helpers: acpi_add_early_pio() to add PIO for early access, and acpi_remove_early_pio() to remove PIO before PCI root register. Since acpi_remove_early_pio() may be called multiple times, we add an acpi_pio flag to ensure PIO be removed only once. Cc: Tested-by: Yuanzhen Gan Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/include/asm/acpi.h | 2 ++ arch/loongarch/kernel/acpi.c | 28 ++++++++++++++++++++++++++++ arch/loongarch/kernel/setup.c | 2 ++ arch/loongarch/pci/acpi.c | 2 ++ 4 files changed, 34 insertions(+) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index 7376840fa9f78..32999f281d5be 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -38,6 +38,8 @@ static inline bool acpi_has_cpu_in_madt(void) extern struct list_head acpi_wakeup_device_list; extern struct acpi_madt_core_pic acpi_core_pic[MAX_CORE_PIC]; +extern void acpi_add_early_pio(void); +extern void acpi_remove_early_pio(void); extern int __init parse_acpi_topology(void); static inline u32 get_acpi_id_for_cpu(unsigned int cpu) diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 1367ca759468f..88ad24d1e729a 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,33 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) return ioremap_cache(phys, size); } +#define PIO_BASE (unsigned long)PCI_IOBASE +#define PIO_SIZE ALIGN(ISA_IOSIZE, PAGE_SIZE) + +static bool acpi_pio; + +/* Add PIO for early access */ +void acpi_add_early_pio(void) +{ + if (!acpi_disabled) { + acpi_pio = true; + vmap_page_range(PIO_BASE, PIO_BASE + PIO_SIZE, + LOONGSON_LIO_BASE, pgprot_device(PAGE_KERNEL)); + } +} + +/* Remove PIO for PCI register */ +void acpi_remove_early_pio(void) +{ + if (!acpi_pio) + return; + + if (!acpi_disabled) { + acpi_pio = false; + vunmap_range(PIO_BASE, PIO_BASE + PIO_SIZE); + } +} + #ifdef CONFIG_SMP static int set_processor_mask(u32 id, u32 pass) { diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 2b260d15b2e25..75e3dfa060553 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -510,6 +510,8 @@ static __init int arch_reserve_pio_range(void) { struct device_node *np; + acpi_add_early_pio(); + for_each_node_by_name(np, "isa") { struct of_range range; struct of_range_parser parser; diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c index a6d869e103c14..1a28fe847d472 100644 --- a/arch/loongarch/pci/acpi.c +++ b/arch/loongarch/pci/acpi.c @@ -65,6 +65,8 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) struct resource_entry *entry, *tmp; struct acpi_device *device = ci->bridge; + acpi_remove_early_pio(); + status = acpi_pci_probe_root_resources(ci); if (status > 0) { acpi_evaluate_integer(device->handle, "PCIH", NULL, &pci_h); From c1dd0b10710041b748fac58e29e91bbb0838375c Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 30 May 2026 11:58:09 +0200 Subject: [PATCH 0148/3297] rust: cpufreq: clean new `clippy::map_or_identity` lint for Rust 1.98.0 commit 3473e0a219fdb2cb013da0a5d917e66fef052325 upstream. Starting with Rust 1.98.0 (expected 2026-08-20), Clippy is likely introducing a new lint `clippy::map_or_identity` [1][2], which currently triggers in a single case: warning: expression can be simplified using `Result::unwrap_or()` --> rust/kernel/cpufreq.rs:1326:60 | 1326 | PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#map_or_identity = note: `-W clippy::map-or-identity` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::map_or_identity)]` help: consider using `unwrap_or` | 1326 - PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f)) 1326 + PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).unwrap_or(0)) | The suggestion is valid, thus clean it up. Cc: stable@vger.kernel.org # Needed in 6.18.y and later. Link: https://github.com/rust-lang/rust-clippy/issues/15801 [1] Link: https://github.com/rust-lang/rust-clippy/pull/16052 [2] Reviewed-by: Zhongqiu Han Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Acked-by: Viresh Kumar Link: https://patch.msgid.link/20260530095809.213611-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- rust/kernel/cpufreq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 19729ccb3f6d2..a509d09ca4523 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -1323,7 +1323,7 @@ impl Registration { // SAFETY: The C API guarantees that `cpu` refers to a valid CPU number. let cpu_id = unsafe { CpuId::from_u32_unchecked(cpu) }; - PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f)) + PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).unwrap_or(0)) } /// Driver's `update_limit` callback. From e7636f26f77070a529c26d65afd217514ce85ce4 Mon Sep 17 00:00:00 2001 From: Haoze Xie Date: Sat, 30 May 2026 14:11:54 +0800 Subject: [PATCH 0149/3297] rust: block: fix GenDisk cleanup paths commit 2957771379fa335103a4b539db57bb2271e12142 upstream. GenDiskBuilder::build() still has fallible work after __blk_mq_alloc_disk(), but its error path only recovers the foreign queue data. That leaks the temporary gendisk and request_queue until later teardown. If the caller moved the last Arc> into build(), the leaked queue can retain blk-mq state after the tag set is dropped. Fix the pre-registration failure path by dropping the temporary gendisk reference with put_disk() before recovering queue_data, so disk_release() can tear down the owned queue. Also pair GenDisk::drop() with put_disk() after del_gendisk(). Once a Rust GenDisk has been added with device_add_disk(), del_gendisk() only unregisters it; the final gendisk reference still has to be dropped to complete the release path. Fixes: 3253aba3408a ("rust: block: introduce `kernel::block::mq` module") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reviewed-by: Andreas Hindborg Signed-off-by: Haoze Xie Signed-off-by: Ren Wei Link: https://patch.msgid.link/b70aff9a920cc42110fe5cf454c3099561863519.1780063368.git.royenheart@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- rust/kernel/block/mq/gen_disk.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs index 1ce815c8cdab0..23c6a8929c347 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -153,6 +153,19 @@ impl GenDiskBuilder { // SAFETY: `gendisk` is a valid pointer as we initialized it above unsafe { (*gendisk).fops = &TABLE }; + let cleanup_failure = ScopeGuard::new_with_data((gendisk, data), |(gendisk, data)| { + // SAFETY: `gendisk` came from `__blk_mq_alloc_disk()` above and + // has not been added to the VFS on this cleanup path. + unsafe { bindings::put_disk(gendisk) }; + // SAFETY: `data` came from `into_foreign()` above and has not been + // converted back on this cleanup path. + drop(unsafe { T::QueueData::from_foreign(data) }); + }); + + // The failure guard now owns both pieces of cleanup; the early guard + // must not run on this path anymore. + recover_data.dismiss(); + let mut writer = NullTerminatedFormatter::new( // SAFETY: `gendisk` points to a valid and initialized instance. We // have exclusive access, since the disk is not added to the VFS @@ -175,7 +188,7 @@ impl GenDiskBuilder { }, )?; - recover_data.dismiss(); + cleanup_failure.dismiss(); // INVARIANT: `gendisk` was initialized above. // INVARIANT: `gendisk` was added to the VFS via `device_add_disk` above. @@ -218,6 +231,11 @@ impl Drop for GenDisk { // to the VFS. unsafe { bindings::del_gendisk(self.gendisk) }; + // SAFETY: By type invariant, `self.gendisk` was added to the VFS, so + // `put_disk()` must follow `del_gendisk()` to drop the final gendisk + // reference and trigger the remaining release path. + unsafe { bindings::put_disk(self.gendisk) }; + // SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build` with // a call to `ForeignOwnable::into_foreign` to create `queuedata`. // `ForeignOwnable::from_foreign` is only called here. From 70fe1ac8647b0f872b22bdb508f0e177abab25e0 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 16 Jun 2026 14:25:56 +0100 Subject: [PATCH 0150/3297] rust: doctest: fix incorrect pattern in replacement commit ac4d1caa82d487e7ed46d0597da1adc9c1a51c70 upstream. The `-> Result<(), impl core::fmt::Debug>` string is generated by rustdoc and by adding "::" into the string it no longer finds anything, making the line useless. Remove the "::" in the pattern. Omit it in the replacement too, for consistency with upstream rustdoc. Fixes: de7cd3e4d638 ("rust: use absolute paths in macros referencing core and kernel") Signed-off-by: Gary Guo Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616132559.2245814-1-gary@kernel.org [ Added link in code comment to `rustdoc`'s 1.87 PR that fully qualified it for context. Improved comments for consistency. Reworded to drop changelog and to fix typo. - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- scripts/rustdoc_test_builder.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs index f7540bcf595ac..df864437cef70 100644 --- a/scripts/rustdoc_test_builder.rs +++ b/scripts/rustdoc_test_builder.rs @@ -28,7 +28,7 @@ fn main() { // // ``` // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_28_0() { - // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl ::core::fmt::Debug> { + // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl core::fmt::Debug> { // ``` // // It should be unlikely that doctest code matches such lines (when code is formatted properly). @@ -47,12 +47,16 @@ fn main() { }) .expect("No test function found in `rustdoc`'s output."); - // Qualify `Result` to avoid the collision with our own `Result` coming from the prelude. + // Replicate `rustdoc` 1.87+ behaviour [1] by fully qualifying `Result` to avoid the collision + // with our own `Result` coming from the prelude. + // + // [1]: https://github.com/rust-lang/rust/pull/137807 + // + // TODO: Remove this when MSRV is bumped above 1.87. let body = body.replace( - &format!("{rustdoc_function_name}() -> Result<(), impl ::core::fmt::Debug> {{"), - &format!( - "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{" - ), + &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"), + // This intentionally does not use absolute paths to match `rustdoc` 1.87 behaviour. + &format!("{rustdoc_function_name}() -> core::result::Result<(), impl core::fmt::Debug> {{"), ); // For tests that get generated with `Result`, like above, `rustdoc` generates an `unwrap()` on From 1cc8f512cd90581d3ef9f743766a5cba34d454ff Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 16 Jun 2026 12:30:38 +0000 Subject: [PATCH 0151/3297] rust: Kbuild: set frame-pointer llvm module flag for CONFIG_FRAME_POINTER commit 191f49f1e38b1c10eb44b0f967c6175c884ef7db upstream. Due to a rustc bug, the -Cforce-frame-pointers=y flag only emits the frame-pointer annotation for functions, but not for the module. This means that functions generated by the LLVM backend such as 'asan.module_ctor' do not receive the frame-pointer annotation. This is likely to lead to broken backtraces and may also cause issues with ftrace if these features are used with functions generated by the LLVM backend. Thus, use -Zllvm_module_flag to work around this rustc bug if using a rustc without the fix. [ The fix [1] has landed for Rust 1.98.0 (expected release on 2026-08-20). - Miguel ] Cc: stable@vger.kernel.org # 6.12.y and later (flag not available in pinned Rust in older LTSs). Fixes: 2f7ab1267dc9 ("Kbuild: add Rust support") Link: https://github.com/rust-lang/rust/pull/156980 [1] Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260616-frame-ptr-fix-v1-1-dc6b29a631d9@google.com [ - Adjusted Cc: stable@ as discussed. - Added comment with link to the PR, similar to what we did in commit ac35b5580ace ("rust: arm64: set uwtable llvm module flag for CONFIG_UNWIND_TABLES"). - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 7ee3beedcecaa..9c602286d8e19 100644 --- a/Makefile +++ b/Makefile @@ -905,6 +905,9 @@ KBUILD_CFLAGS += $(stackp-flags-y) ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls KBUILD_RUSTFLAGS += -Cforce-frame-pointers=y +# Work around rustc bug on compilers without +# https://github.com/rust-lang/rust/pull/156980. +KBUILD_RUSTFLAGS += $(if $(call rustc-min-version,109800),,-Zllvm_module_flag=frame-pointer:u32:2:max) else # Some targets (ARM with Thumb2, for example), can't be built with frame # pointers. For those, we don't have FUNCTION_TRACER automatically From 94396fd93226a3e7ade1399f029841b2e8a1386e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 1 Jul 2026 15:11:50 +0200 Subject: [PATCH 0152/3297] futex/requeue: Revert "Prevent NULL pointer dereference in remove_waiter() on self-deadlock"" commit 39def6d250d370298f86c116f4ac60093cefadaa upstream. The commit cited below should not have been merged. It attemted to fix an existing problem ansd thereby introduced new problems by keeping the pi_state in state Q_REQUEUE_PI_IN_PROGRESS and leaking it. Based on the commit description the intention was to handle the case when task_blocks_on_rt_mutex() returns -EDEADLK and the following remove_waiter() dereferences the NULL pointer in waiter->task. That is already handled by Davidlohr in commit 40a25d59e85b3 ("locking/rtmutex: Skip remove_waiter() when waiter is not enqueued") and requires no further acting. Revert the commit breaking the "waiter == owner" case again. Fixes: 74e144274af39 ("futex/requeue: Prevent NULL pointer dereference in remove_waiter() on self-deadlock") Reported-by: Michael Bommarito Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260701131150.0Ijhq4Dw@linutronix.de Closes: https://lore.kernel.org/all/20260629020049.2082397-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman --- kernel/futex/requeue.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index 1d99a84dc9adb..b597cb3d17fc1 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -643,12 +643,6 @@ int futex_requeue(u32 __user *uaddr1, unsigned int flags1, continue; } - /* Self-deadlock: non-top waiter already owns the PI futex. */ - if (rt_mutex_owner(&pi_state->pi_mutex) == this->task) { - ret = -EDEADLK; - break; - } - ret = rt_mutex_start_proxy_lock(&pi_state->pi_mutex, this->rt_waiter, this->task); From a2d5d3ee7b6e3953114726b1521e62123ab5b043 Mon Sep 17 00:00:00 2001 From: Taeyang Lee <0wn@theori.io> Date: Sun, 14 Jun 2026 23:22:18 +0900 Subject: [PATCH 0153/3297] perf/core: Detach event groups during remove_on_exec [ Upstream commit 037a3c43edfb597665dd34457cd22b14692f2ba3 ] perf_event_remove_on_exec() removes events by calling perf_event_exit_event(). For top-level events, this removes the event from the context with DETACH_EXIT only. This can leave inconsistent group state when a removed event is a group leader and the group contains siblings without remove_on_exec. If the group was active, the surviving siblings can remain active and attached to the removed leader's sibling list, but are no longer represented by a valid group leader on the PMU context active lists. A later close of the removed leader uses DETACH_GROUP and can promote the still-active siblings from this stale group state. The next schedule-in can then add an already-linked active_list entry again, corrupting the PMU context active list. With DEBUG_LIST enabled, this is caught as a list_add double-add in merge_sched_in(). Fix this by detaching group relationships when remove_on_exec removes an event. This preserves the existing task-exit and revoke behavior, while ensuring surviving siblings are ungrouped before the removed event leaves the context. Fixes: 2e498d0a74e5 ("perf: Add support for event removal on exec") Signed-off-by: Taeyang Lee <0wn@theori.io> Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/ai65GgZcC0LAlWLG@Taeyangs-MacBook-Pro.local Signed-off-by: Sasha Levin --- kernel/events/core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 6b6fea8d33e04..449550b3bb286 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4586,7 +4586,7 @@ static void perf_remove_from_owner(struct perf_event *event); static void perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx, struct task_struct *task, - bool revoke); + unsigned long detach_flags); /* * Removes all events from the current task that have been marked @@ -4613,7 +4613,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx) modified = true; - perf_event_exit_event(event, ctx, ctx->task, false); + perf_event_exit_event(event, ctx, ctx->task, DETACH_GROUP); } raw_spin_lock_irqsave(&ctx->lock, flags); @@ -12511,7 +12511,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event, /* * De-schedule the event and mark it REVOKED. */ - perf_event_exit_event(event, ctx, ctx->task, true); + perf_event_exit_event(event, ctx, ctx->task, DETACH_REVOKE); /* * All _free_event() bits that rely on event->pmu: @@ -14095,12 +14095,13 @@ static void perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx, struct task_struct *task, - bool revoke) + unsigned long detach_flags) { struct perf_event *parent_event = event->parent; - unsigned long detach_flags = DETACH_EXIT; unsigned int attach_state; + detach_flags |= DETACH_EXIT; + if (parent_event) { /* * Do not destroy the 'original' grouping; because of the @@ -14123,8 +14124,8 @@ perf_event_exit_event(struct perf_event *event, sync_child_event(event, task); } - if (revoke) - detach_flags |= DETACH_GROUP | DETACH_REVOKE; + if (detach_flags & DETACH_REVOKE) + detach_flags |= DETACH_GROUP; perf_remove_from_context(event, detach_flags); /* @@ -14212,7 +14213,7 @@ static void perf_event_exit_task_context(struct task_struct *task, bool exit) perf_event_task(task, ctx, 0); list_for_each_entry_safe(child_event, next, &ctx->event_list, event_entry) - perf_event_exit_event(child_event, ctx, exit ? task : NULL, false); + perf_event_exit_event(child_event, ctx, exit ? task : NULL, 0); mutex_unlock(&ctx->mutex); From 20133754d46fe42dac8a56f590ea742645e6ba64 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 9 Jul 2026 19:59:43 +0200 Subject: [PATCH 0154/3297] rust: kasan: KASAN+RUST requires clang [ Upstream commit 5b271543d0f08e9733d4732721e960e285f6448f ] Kernel KASAN involves passing various llvm/gcc specific arguments to the C and Rust compiler. Since these arguments differ between llvm and gcc, it's not safe to mix an llvm-based rustc with a gcc build when kasan is enabled. Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Cc: stable@vger.kernel.org Fixes: e3117404b411 ("kbuild: rust: Enable KASAN support") Link: https://patch.msgid.link/20260408-kasan-rust-sw-tags-v3-1-e07964d14363@google.com Signed-off-by: Miguel Ojeda Signed-off-by: Sasha Levin --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index cab3ad28ca49e..0c25e78e10f47 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -2096,6 +2096,7 @@ config RUST depends on !CFI || HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC select CFI_ICALL_NORMALIZE_INTEGERS if CFI depends on !CALL_PADDING || RUSTC_VERSION >= 108100 + depends on !KASAN || CC_IS_CLANG depends on !KASAN_SW_TAGS depends on !(MITIGATION_RETHUNK && KASAN) || RUSTC_VERSION >= 108300 help From 85f8b440a09bad8d8f75156c89f72ac37c9d0d48 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 11:06:51 -0700 Subject: [PATCH 0155/3297] fscrypt: Fix key setup in edge case with multiple data unit sizes [ Upstream commit dd015b566d505d698386103e9c80b739c7336eb8 ] The addition of support for customizable data unit sizes introduced an edge case where a file's contents can be en/decrypted with the wrong data unit size. It occurs when there are multiple v2 policies that: - Have *different* data unit sizes, via the log2_data_unit_size field - Share the same master_key_identifier, contents_encryption_mode, and either FSCRYPT_POLICY_FLAG_DIRECT_KEY, FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32, or FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 - Are being used on the same filesystem, which also must be mounted with the "inlinecrypt" mount option. Fortunately this edge case doesn't actually occur in practice. I just found it via code review. But it needs to be fixed regardless. The bug is caused by the data unit size not being fully considered when blk_crypto_keys are cached in mk_direct_keys, mk_iv_ino_lblk_32_keys, and mk_iv_ino_lblk_64_keys. They're differentiated only by master key, encryption mode, and flag. However, each one actually has a data unit size too. Only the first data unit size that is cached is used. To fix this, start using the data unit size to differentiate the cached keys. For several reasons, including avoiding increasing the size of struct fscrypt_master_key, just replace all three arrays with a single linked list instead of changing them into two-dimensional arrays. This works well when considering that in practice at most 2 entries are used across all three arrays, so it was already mostly wasted space. For simplicity, make the list also take over the publish/subscribe of the prepared key itself. That is, create separate list nodes for blk_crypto_keys vs crypto_skciphers, and add nodes to the list only when their key is actually prepared. (Note that the legacy fscrypt_direct_keys table in fs/crypto/keysetup_v1.c already works this way.) This eliminates the need for the additional memory barriers when reading and writing the fields of struct fscrypt_prepared_key. Note that I technically should have included the data unit size in the HKDF info string as well. But it's too late to change that. Fixes: 5b1188847180 ("fscrypt: support crypto data unit size less than filesystem block size") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618180652.52742-1-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Sasha Levin --- fs/crypto/fscrypt_private.h | 52 +++++++++------- fs/crypto/inline_crypt.c | 8 +-- fs/crypto/keyring.c | 23 ++++--- fs/crypto/keysetup.c | 118 ++++++++++++++++++++++-------------- 4 files changed, 120 insertions(+), 81 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4e8e82a9ccf9a..3bcde4b59efb0 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -236,7 +236,7 @@ struct fscrypt_symlink_data { * @tfm: crypto API transform object * @blk_key: key for blk-crypto * - * Normally only one of the fields will be non-NULL. + * Only one of the fields is non-NULL. */ struct fscrypt_prepared_key { struct crypto_sync_skcipher *tfm; @@ -245,6 +245,15 @@ struct fscrypt_prepared_key { #endif }; +/* An entry in the linked list ->mk_mode_keys */ +struct fscrypt_mode_key { + struct fscrypt_prepared_key key; + struct list_head link; + u8 hkdf_context; + u8 mode_num; + u8 data_unit_bits; +}; + /* * fscrypt_inode_info - the "encryption key" for an inode * @@ -433,20 +442,12 @@ int fscrypt_derive_sw_secret(struct super_block *sb, * @prep_key, depending on which encryption implementation the file will use. */ static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - /* - * The two smp_load_acquire()'s here pair with the smp_store_release()'s - * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key(). - * I.e., in some cases (namely, if this prep_key is a per-mode - * encryption key) another task can publish blk_key or tfm concurrently, - * executing a RELEASE barrier. We need to use smp_load_acquire() here - * to safely ACQUIRE the memory the other task published. - */ if (fscrypt_using_inline_encryption(ci)) - return smp_load_acquire(&prep_key->blk_key) != NULL; - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->blk_key != NULL; + return prep_key->tfm != NULL; } #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -489,10 +490,10 @@ fscrypt_derive_sw_secret(struct super_block *sb, } static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->tfm != NULL; } #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -580,8 +581,8 @@ struct fscrypt_master_key { /* * Active and structural reference counts. An active ref guarantees * that the struct continues to exist, continues to be in the keyring - * ->s_master_keys, and that any embedded subkeys (e.g. - * ->mk_direct_keys) that have been prepared continue to exist. + * ->s_master_keys, and that any non-file-scoped subkeys (e.g. + * ->mk_mode_keys) that have been prepared continue to exist. * A structural ref only guarantees that the struct continues to exist. * * There is one active ref associated with ->mk_present being true, and @@ -635,12 +636,21 @@ struct fscrypt_master_key { spinlock_t mk_decrypted_inodes_lock; /* - * Per-mode encryption keys for the various types of encryption policies - * that use them. Allocated and derived on-demand. + * A list of 'struct fscrypt_mode_key' for the (hkdf_context, mode_num, + * data_unit_bits, inlinecrypt) combinations that are in use for this + * master key, for hkdf_context in [HKDF_CONTEXT_DIRECT_KEY, + * HKDF_CONTEXT_IV_INO_LBLK_32_KEY, HKDF_CONTEXT_IV_INO_LBLK_64_KEY]. + * + * This is a linked list and not a hash table because in practice + * there's just a single encryption policy per master key, using + * _at most_ 2 nodes in this list. Per-file keys don't use this at all. + * + * This list is append-only until the master key is fully removed, at + * which time the list is cleared. Before then, + * fscrypt_mode_key_setup_mutex synchronizes appends, and searches use + * the RCU read lock together with ->mk_sem held for read. */ - struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1]; + struct list_head mk_mode_keys; /* Hash key for inode numbers. Initialized only when needed. */ siphash_key_t mk_ino_hash_key; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index ed6e926226b51..645cc49360729 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -198,13 +198,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, goto fail; } - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->blk_key with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->blk_key, blk_key); + prep_key->blk_key = blk_key; return 0; fail: diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 3adbd7167055a..29bea5f3c9823 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -87,14 +87,14 @@ void fscrypt_put_master_key(struct fscrypt_master_key *mk) void fscrypt_put_master_key_activeref(struct super_block *sb, struct fscrypt_master_key *mk) { - size_t i; + struct fscrypt_mode_key *node, *tmp; if (!refcount_dec_and_test(&mk->mk_active_refs)) return; /* * No active references left, so complete the full removal of this * fscrypt_master_key struct by removing it from the keyring and - * destroying any subkeys embedded in it. + * destroying any non-file-scoped subkeys. */ if (WARN_ON_ONCE(!sb->s_master_keys)) @@ -110,13 +110,16 @@ void fscrypt_put_master_key_activeref(struct super_block *sb, WARN_ON_ONCE(mk->mk_present); WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); - for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { - fscrypt_destroy_prepared_key( - sb, &mk->mk_direct_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_64_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_32_keys[i]); + /* + * Destroy any non-file-scoped subkeys. Since ->mk_active_refs == 0, + * they're no longer referenced by any inodes. Nor can key setup run + * and use them again. So they're no longer needed. (This implies no + * concurrent readers, so we don't need list_del_rcu() for example.) + */ + list_for_each_entry_safe(node, tmp, &mk->mk_mode_keys, link) { + fscrypt_destroy_prepared_key(sb, &node->key); + list_del(&node->link); + kfree(node); } memzero_explicit(&mk->mk_ino_hash_key, sizeof(mk->mk_ino_hash_key)); @@ -445,6 +448,8 @@ static int add_new_master_key(struct super_block *sb, INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); + INIT_LIST_HEAD(&mk->mk_mode_keys); + if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { err = allocate_master_key_users_keyring(mk); if (err) diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 4bd3918f50e3f..f27997ef9a08c 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -163,13 +163,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); if (IS_ERR(tfm)) return PTR_ERR(tfm); - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->tfm with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->tfm, tfm); + prep_key->tfm = tfm; return 0; } @@ -190,9 +184,37 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_info *ci, return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci); } +/* + * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_context, + * mode_num, data_unit_bits, inlinecrypt) combination. + * + * The caller must hold ->mk_sem for reading and ->mk_present must be true, + * ensuring that ->mk_mode_keys is still append-only. + */ +static struct fscrypt_prepared_key * +fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context, + u8 mode_num, const struct fscrypt_inode_info *ci) +{ + struct fscrypt_mode_key *node; + + /* + * The RCU read lock here is used only to synchronize with concurrent + * list_add_tail_rcu(). Concurrent deletions are impossible here, so + * returning a pointer to a node without taking any refcount is safe. + */ + guard(rcu)(); + list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) { + if (node->hkdf_context == hkdf_context && + node->mode_num == mode_num && + node->data_unit_bits == ci->ci_data_unit_bits && + fscrypt_is_key_prepared(&node->key, ci)) + return &node->key; + } + return NULL; +} + static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_master_key *mk, - struct fscrypt_prepared_key *keys, u8 hkdf_context, bool include_fs_uuid) { const struct inode *inode = ci->ci_inode; @@ -200,7 +222,8 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_mode *mode = ci->ci_mode; const u8 mode_num = mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; - u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; + struct fscrypt_mode_key *new_node; + u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen = 0; bool use_hw_wrapped_key = false; @@ -223,48 +246,56 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, use_hw_wrapped_key = true; } - prep_key = &keys[mode_num]; - if (fscrypt_is_key_prepared(prep_key, ci)) { + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { ci->ci_enc_key = *prep_key; return 0; } - mutex_lock(&fscrypt_mode_key_setup_mutex); + guard(mutex)(&fscrypt_mode_key_setup_mutex); - if (fscrypt_is_key_prepared(prep_key, ci)) - goto done_unlock; + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { + ci->ci_enc_key = *prep_key; + return 0; + } + + new_node = kzalloc_obj(*new_node); + if (!new_node) + return -ENOMEM; + new_node->hkdf_context = hkdf_context; + new_node->mode_num = mode_num; + new_node->data_unit_bits = ci->ci_data_unit_bits; + prep_key = &new_node->key; if (use_hw_wrapped_key) { err = fscrypt_prepare_inline_crypt_key(prep_key, mk->mk_secret.bytes, mk->mk_secret.size, true, ci); - if (err) - goto out_unlock; - goto done_unlock; + } else { + static_assert(sizeof(mode_num) == 1); + static_assert(sizeof(sb->s_uuid) == 16); + static_assert(sizeof(hkdf_info) == 17); + hkdf_info[hkdf_infolen++] = mode_num; + if (include_fs_uuid) { + memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, + sizeof(sb->s_uuid)); + hkdf_infolen += sizeof(sb->s_uuid); + } + fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, + hkdf_info, hkdf_infolen, raw_mode_key, + mode->keysize); + err = fscrypt_prepare_key(prep_key, raw_mode_key, ci); + memzero_explicit(raw_mode_key, mode->keysize); } - - BUILD_BUG_ON(sizeof(mode_num) != 1); - BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); - BUILD_BUG_ON(sizeof(hkdf_info) != 17); - hkdf_info[hkdf_infolen++] = mode_num; - if (include_fs_uuid) { - memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, - sizeof(sb->s_uuid)); - hkdf_infolen += sizeof(sb->s_uuid); + if (err) { + kfree(new_node); + return err; } - fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info, - hkdf_infolen, mode_key, mode->keysize); - err = fscrypt_prepare_key(prep_key, mode_key, ci); - memzero_explicit(mode_key, mode->keysize); - if (err) - goto out_unlock; -done_unlock: + list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys); ci->ci_enc_key = *prep_key; - err = 0; -out_unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); - return err; + return 0; } /* @@ -311,8 +342,8 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_inode_info *ci, { int err; - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys, - HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY, + true); if (err) return err; @@ -364,8 +395,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * encryption key. This ensures that the master key is * consistently used only for HKDF, avoiding key reuse issues. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, - HKDF_CONTEXT_DIRECT_KEY, false); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY, + false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* @@ -374,9 +405,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * the IVs. This format is optimized for use with inline * encryption hardware compliant with the UFS standard. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, - HKDF_CONTEXT_IV_INO_LBLK_64_KEY, - true); + err = setup_per_mode_enc_key( + ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); From 9d1dc507b99ceb1c15036c9382fb001b919a614b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 18 Jun 2026 15:19:21 -0700 Subject: [PATCH 0156/3297] fscrypt: Replace mk_users keyring with simple list [ Upstream commit 696c030e1e3438955aba443b308ee8b6faa3983e ] Change mk_users (the set of user claims to an fscrypt master key) from a 'struct key' keyring to a simple linked list. It's still a collection of 'struct key' for quota tracking. It was originally thought to be natural that a collection of 'struct key' should be held in a 'struct key' keyring. In reality, it's just been causing problems, similar to how using 'struct key' for the filesystem keyring caused problems and was removed in commit d7e7b9af104c ("fscrypt: stop using keyrings subsystem for fscrypt_master_key"). Commit d3a7bd420076 ("fscrypt: clear keyring before calling key_put()") fixed mk_users cleanup to be synchronous. But that apparently wasn't enough: the keyring subsystem's redundant locking is still generating lockdep false positives due to the interaction with filesystem reclaim. With the simple list, the redundant locking and lockdep issue goes away. Of course, searching a linked list is linear-time whereas the 'struct key' keyring used a fancy constant-time associative array. But that's fine here, since in practice there's just one entry in the list. In fact the new code is much faster in practice, since it's much smaller and doesn't have to convert the kuid_t into a string to search for it. Reported-by: syzbot+f55b043dacf43776b50c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=f55b043dacf43776b50c Reported-by: Mohammed EL Kadiri Closes: https://lore.kernel.org/keyrings/20260614150041.21172-1-med08elkadiri@gmail.com/ Fixes: 23c688b54016 ("fscrypt: allow unprivileged users to add/remove keys for v2 policies") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618221921.87896-1-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Sasha Levin --- fs/crypto/fscrypt_private.h | 32 ++++-- fs/crypto/keyring.c | 216 +++++++++++++++--------------------- 2 files changed, 113 insertions(+), 135 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 3bcde4b59efb0..886ea39e924df 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -499,6 +499,19 @@ fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, /* keyring.c */ +/* + * fscrypt_master_key_user - a user's claim to a master key + */ +struct fscrypt_master_key_user { + struct list_head link; + kuid_t uid; + /* + * This 'struct key' contains no secret. It exists solely to charge the + * appropriate user's key quota. + */ + struct key *quota_key; +}; + /* * fscrypt_master_key_secret - secret key material of an in-use master key */ @@ -614,19 +627,18 @@ struct fscrypt_master_key { struct fscrypt_key_specifier mk_spec; /* - * Keyring which contains a key of type 'key_type_fscrypt_user' for each - * user who has added this key. Normally each key will be added by just - * one user, but it's possible that multiple users share a key, and in - * that case we need to keep track of those users so that one user can't - * remove the key before the others want it removed too. + * List of user claims to this key (struct fscrypt_master_key_user). + * Normally each key will be added by just one user, but it's possible + * that multiple users share a key, and in that case we need to keep + * track of those users so that one user can't remove the key before the + * others want it removed too. * - * This is NULL for v1 policy keys; those can only be added by root. + * Used only for v2 policy keys. v1 policy keys can be added only by + * root, so user tracking doesn't apply to them. * - * Locking: protected by ->mk_sem. (We don't just rely on the keyrings - * subsystem semaphore ->mk_users->sem, as we need support for atomic - * search+insert along with proper synchronization with other fields.) + * Locking: protected by ->mk_sem. */ - struct key *mk_users; + struct list_head mk_users; /* * List of inodes that were unlocked using this key. This allows the diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 29bea5f3c9823..4ea4ee5cfc64b 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -65,22 +65,19 @@ static void fscrypt_free_master_key(struct rcu_head *head) kfree_sensitive(mk); } +static void clear_mk_users(struct fscrypt_master_key *mk); + void fscrypt_put_master_key(struct fscrypt_master_key *mk) { if (!refcount_dec_and_test(&mk->mk_struct_refs)) return; /* - * No structural references left, so free ->mk_users, and also free the + * No structural references left, so clear ->mk_users, and also free the * fscrypt_master_key struct itself after an RCU grace period ensures * that concurrent keyring lookups can no longer find it. */ WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) != 0); - if (mk->mk_users) { - /* Clear the keyring so the quota gets released right away. */ - keyring_clear(mk->mk_users); - key_put(mk->mk_users); - mk->mk_users = NULL; - } + clear_mk_users(mk); call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); } @@ -165,8 +162,8 @@ static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) } /* - * Type of key in ->mk_users. Each key of this type represents a particular - * user who has added a particular master key. + * Type of fscrypt_master_key_user::quota_key. This contains no secret; it + * exists solely to charge a user's key quota. * * Note that the name of this key type really should be something like * ".fscrypt-user" instead of simply ".fscrypt". But the shorter name is chosen @@ -180,30 +177,9 @@ static struct key_type key_type_fscrypt_user = { .describe = fscrypt_user_key_describe, }; -#define FSCRYPT_MK_USERS_DESCRIPTION_SIZE \ - (CONST_STRLEN("fscrypt-") + 2 * FSCRYPT_KEY_IDENTIFIER_SIZE + \ - CONST_STRLEN("-users") + 1) - #define FSCRYPT_MK_USER_DESCRIPTION_SIZE \ (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + CONST_STRLEN(".uid.") + 10 + 1) -static void format_mk_users_keyring_description( - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - sprintf(description, "fscrypt-%*phN-users", - FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier); -} - -static void format_mk_user_description( - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - - sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE, - mk_identifier, __kuid_val(current_fsuid())); -} - /* Create ->s_master_keys if needed. Synchronized by fscrypt_add_key_mutex. */ static int allocate_filesystem_keyring(struct super_block *sb) { @@ -338,91 +314,94 @@ fscrypt_find_master_key(struct super_block *sb, return mk; } -static int allocate_master_key_users_keyring(struct fscrypt_master_key *mk) -{ - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE]; - struct key *keyring; - - format_mk_users_keyring_description(description, - mk->mk_spec.u.identifier); - keyring = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, - current_cred(), KEY_POS_SEARCH | - KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) - return PTR_ERR(keyring); - - mk->mk_users = keyring; - return 0; -} - -/* - * Find the current user's "key" in the master key's ->mk_users. - * Returns ERR_PTR(-ENOKEY) if not found. - */ -static struct key *find_master_key_user(struct fscrypt_master_key *mk) +/* Find the current user's claim in ->mk_users. ->mk_sem must be held. */ +static struct fscrypt_master_key_user * +find_master_key_user(struct fscrypt_master_key *mk) { - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - key_ref_t keyref; + struct fscrypt_master_key_user *mk_user; + kuid_t uid = current_fsuid(); - format_mk_user_description(description, mk->mk_spec.u.identifier); - - /* - * We need to mark the keyring reference as "possessed" so that we - * acquire permission to search it, via the KEY_POS_SEARCH permission. - */ - keyref = keyring_search(make_key_ref(mk->mk_users, true /*possessed*/), - &key_type_fscrypt_user, description, false); - if (IS_ERR(keyref)) { - if (PTR_ERR(keyref) == -EAGAIN || /* not found */ - PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */ - keyref = ERR_PTR(-ENOKEY); - return ERR_CAST(keyref); + list_for_each_entry(mk_user, &mk->mk_users, link) { + if (uid_eq(mk_user->uid, uid)) + return mk_user; } - return key_ref_to_ptr(keyref); + return NULL; } /* - * Give the current user a "key" in ->mk_users. This charges the user's quota + * Give the current user a claim in ->mk_users. This charges the user's quota * and marks the master key as added by the current user, so that it cannot be * removed by another user with the key. Either ->mk_sem must be held for * write, or the master key must be still undergoing initialization. */ static int add_master_key_user(struct fscrypt_master_key *mk) { + kuid_t uid = current_fsuid(); char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - struct key *mk_user; + struct key *quota_key; + struct fscrypt_master_key_user *mk_user; int err; - format_mk_user_description(description, mk->mk_spec.u.identifier); - mk_user = key_alloc(&key_type_fscrypt_user, description, - current_fsuid(), current_gid(), current_cred(), - KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); + snprintf(description, sizeof(description), "%*phN.uid.%u", + FSCRYPT_KEY_IDENTIFIER_SIZE, mk->mk_spec.u.identifier, + __kuid_val(uid)); + quota_key = key_alloc(&key_type_fscrypt_user, description, uid, + current_gid(), current_cred(), + KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(quota_key)) + return PTR_ERR(quota_key); + + err = key_instantiate_and_link(quota_key, NULL, 0, NULL, NULL); + if (err) { + key_put(quota_key); + return err; + } - err = key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL); - key_put(mk_user); - return err; + mk_user = kzalloc_obj(*mk_user); + if (!mk_user) { + key_put(quota_key); + return -ENOMEM; + } + mk_user->uid = uid; + mk_user->quota_key = quota_key; + list_add(&mk_user->link, &mk->mk_users); + return 0; +} + +static void unlink_and_free_mk_user(struct fscrypt_master_key_user *mk_user) +{ + list_del(&mk_user->link); + key_put(mk_user->quota_key); + kfree(mk_user); } /* - * Remove the current user's "key" from ->mk_users. + * Remove the current user's claim from ->mk_users. * ->mk_sem must be held for write. * - * Returns 0 if removed, -ENOKEY if not found, or another -errno code. + * Returns 0 if removed or -ENOKEY if not found. */ static int remove_master_key_user(struct fscrypt_master_key *mk) { - struct key *mk_user; - int err; + struct fscrypt_master_key_user *mk_user; mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - err = key_unlink(mk->mk_users, mk_user); - key_put(mk_user); - return err; + if (!mk_user) + return -ENOKEY; + unlink_and_free_mk_user(mk_user); + return 0; +} + +/* + * Clear ->mk_users. Either ->mk_sem must be held for write, or 'mk' must have + * no structural references left. + */ +static void clear_mk_users(struct fscrypt_master_key *mk) +{ + struct fscrypt_master_key_user *mk_user, *tmp; + + list_for_each_entry_safe(mk_user, tmp, &mk->mk_users, link) + unlink_and_free_mk_user(mk_user); } /* @@ -445,15 +424,14 @@ static int add_new_master_key(struct super_block *sb, refcount_set(&mk->mk_struct_refs, 1); mk->mk_spec = *mk_spec; + INIT_LIST_HEAD(&mk->mk_users); + INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); INIT_LIST_HEAD(&mk->mk_mode_keys); if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { - err = allocate_master_key_users_keyring(mk); - if (err) - goto out_put; err = add_master_key_user(mk); if (err) goto out_put; @@ -482,19 +460,13 @@ static int add_existing_master_key(struct fscrypt_master_key *mk, int err; /* - * If the current user is already in ->mk_users, then there's nothing to - * do. Otherwise, we need to add the user to ->mk_users. (Neither is - * applicable for v1 policy keys, which have NULL ->mk_users.) + * For v2 policy keys (FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER): If the current + * user is already in ->mk_users, then there's nothing to do. + * Otherwise, add the user to ->mk_users. */ - if (mk->mk_users) { - struct key *mk_user = find_master_key_user(mk); - - if (mk_user != ERR_PTR(-ENOKEY)) { - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - key_put(mk_user); + if (mk->mk_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + if (find_master_key_user(mk) != NULL) return 0; - } err = add_master_key_user(mk); if (err) return err; @@ -893,7 +865,6 @@ int fscrypt_verify_key_added(struct super_block *sb, { struct fscrypt_key_specifier mk_spec; struct fscrypt_master_key *mk; - struct key *mk_user; int err; mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; @@ -905,13 +876,10 @@ int fscrypt_verify_key_added(struct super_block *sb, goto out; } down_read(&mk->mk_sem); - mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) { - err = PTR_ERR(mk_user); - } else { - key_put(mk_user); + if (find_master_key_user(mk) != NULL) err = 0; - } + else + err = -ENOKEY; up_read(&mk->mk_sem); fscrypt_put_master_key(mk); out: @@ -1103,16 +1071,18 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) down_write(&mk->mk_sem); /* If relevant, remove current user's (or all users) claim to the key */ - if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree != 0) { - if (all_users) - err = keyring_clear(mk->mk_users); - else + if (!list_empty(&mk->mk_users)) { + if (all_users) { + clear_mk_users(mk); + err = 0; + } else { err = remove_master_key_user(mk); + } if (err) { up_write(&mk->mk_sem); goto out_put_key; } - if (mk->mk_users->keys.nr_leaves_on_tree != 0) { + if (!list_empty(&mk->mk_users)) { /* * Other users have still added the key too. We removed * the current user's claim to the key, but we still @@ -1198,6 +1168,8 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) struct super_block *sb = file_inode(filp)->i_sb; struct fscrypt_get_key_status_arg arg; struct fscrypt_master_key *mk; + kuid_t uid; + const struct fscrypt_master_key_user *mk_user; int err; if (copy_from_user(&arg, uarg, sizeof(arg))) @@ -1230,19 +1202,13 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } arg.status = FSCRYPT_KEY_STATUS_PRESENT; - if (mk->mk_users) { - struct key *mk_user; - arg.user_count = mk->mk_users->keys.nr_leaves_on_tree; - mk_user = find_master_key_user(mk); - if (!IS_ERR(mk_user)) { + uid = current_fsuid(); + list_for_each_entry(mk_user, &mk->mk_users, link) { + arg.user_count++; + if (uid_eq(mk_user->uid, uid)) arg.status_flags |= FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; - key_put(mk_user); - } else if (mk_user != ERR_PTR(-ENOKEY)) { - err = PTR_ERR(mk_user); - goto out_release_key; - } } err = 0; out_release_key: From e01e7814b4223560eab0513b7c15b8c82bdc83f3 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:58 +0200 Subject: [PATCH 0157/3297] usb: gadget: function: rndis: add length check to response query commit 95f90eea070837f7c72207d5520f805bdefc3bc5 upstream. Add variable representations for BufLength and BufOffset in rndis_query_response(), and perform a length check on them. This is identical to how rndis_set_response() handles these parameters. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260708-usb-gadget-rndis-v1-1-e77e026dcc6a@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/rndis.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index afd75d72412c9..1eb40ab326d65 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params, static int rndis_query_response(struct rndis_params *params, rndis_query_msg_type *buf) { + u32 BufLength, BufOffset; rndis_query_cmplt_type *resp; rndis_resp_t *r; @@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params, if (!params->dev) return -ENOTSUPP; + BufLength = le32_to_cpu(buf->InformationBufferLength); + BufOffset = le32_to_cpu(buf->InformationBufferOffset); + if ((BufLength > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE)) + return -EINVAL; + /* * we need more memory: * gen_ndis_query_resp expects enough space for @@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params, resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), - le32_to_cpu(buf->InformationBufferOffset) - + 8 + (u8 *)buf, - le32_to_cpu(buf->InformationBufferLength), - r)) { + BufOffset + 8 + (u8 *)buf, + BufLength, r)) { /* OID not supported */ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); resp->MessageLength = cpu_to_le32(sizeof *resp); From 7515a6d4a9e9e4838b833825882efa00e85f8901 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:59 +0200 Subject: [PATCH 0158/3297] usb: gadget: function: rndis: add length check for header commit 21b5bf155435008e0fb0736795289788e63d426f upstream. Add a length check for the rndis header in rndis_rm_hdr, to ensure that MessageType, MessageLength, DataOffset, and DataLength fields are present before they are accessed. Assisted-by: gkh_clanker_2000 Cc: stable Signed-off-by: Griffin Kroah-Hartman Link: https://patch.msgid.link/20260708-usb-gadget-rndis-v1-2-e77e026dcc6a@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/rndis.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 1eb40ab326d65..2a52de847d3fc 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -1080,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port, /* tmp points to a struct rndis_packet_msg_type */ __le32 *tmp = (void *)skb->data; + /* Need at least MessageType, MessageLength, DataOffset, DataLength */ + if (skb->len < 16) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + /* MessageType, MessageLength */ if (cpu_to_le32(RNDIS_MSG_PACKET) != get_unaligned(tmp++)) { From 3e766526827acd542bcd36c20c4d5f397e0f6521 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 13 Jun 2026 02:18:39 -0500 Subject: [PATCH 0159/3297] iio: accel: bmc150: clamp the device-reported FIFO frame count commit ce0e1cae26096fe959a0da5563a6d6d5a801d5fb upstream. __bmc150_accel_fifo_flush() copies the number of samples the device reports in its hardware FIFO into an on-stack buffer u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3]; which is sized for at most BMC150_ACCEL_FIFO_LENGTH (32) samples. The frame count is read from the FIFO_STATUS register and only masked to its 7 valid bits: count = val & 0x7F; so it can be 0..127. The only other limit applied to it is the optional caller-supplied sample budget: if (samples && count > samples) count = samples; which does not constrain count on the flush-all path (samples == 0), and leaves it well above 32 whenever samples is larger. count samples are then transferred into buffer[]: bmc150_accel_fifo_transfer(data, (u8 *)buffer, count); bmc150_accel_fifo_transfer() reads count * 6 bytes through regmap, so a malfunctioning, malicious or counterfeit accelerometer (or an attacker tampering with the I2C/SPI bus) that reports up to 127 frames writes up to 762 bytes into the 192-byte buffer: a stack out-of-bounds write of up to 570 bytes that clobbers the stack canary, saved registers and the return address. Clamp count to BMC150_ACCEL_FIFO_LENGTH, the number of samples buffer[] is sized for, before the transfer, mirroring the watermark clamp already done in bmc150_accel_set_watermark(). A well-formed flush reports at most BMC150_ACCEL_FIFO_LENGTH frames, so legitimate devices are unaffected. Fixes: 3bbec9773389 ("iio: bmc150_accel: add support for hardware fifo") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/bmc150-accel-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 42ccf0316ce59..f6c89e1657c89 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -991,6 +991,8 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, if (samples && count > samples) count = samples; + count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH); + ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count); if (ret) return ret; From 6293211d142605bec435229ef0aa3668b8964164 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:46 +0530 Subject: [PATCH 0160/3297] iio: accel: kxsd9: fix runtime PM imbalance on write_raw() error commit 44a5fd874bb6873bdaec59f722c1d57832fbc9df upstream. kxsd9_write_raw() takes a runtime PM reference with pm_runtime_get_sync() but returns -EINVAL directly when a scale with a non-zero integer part is requested, skipping the matching pm_runtime_put_autosuspend(). This leaks a runtime PM usage-counter reference on every such write, after which the device can no longer autosuspend. Set the error code and fall through to the existing put instead of returning early. Fixes: 9a9a369d6178 ("iio: accel: kxsd9: Deploy system and runtime PM") Signed-off-by: Biren Pandya Assisted-by: Claude:claude-opus-4-8 coccinelle Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/accel/kxsd9.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 4717d80fc24af..7ac885d94d7f4 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -147,8 +147,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev, if (mask == IIO_CHAN_INFO_SCALE) { /* Check no integer component */ if (val) - return -EINVAL; - ret = kxsd9_write_scale(indio_dev, val2); + ret = -EINVAL; + else + ret = kxsd9_write_scale(indio_dev, val2); } pm_runtime_put_autosuspend(st->dev); From e6ade81631d7621b9e95c3f8596b62ae90c89395 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 2 Jun 2026 16:45:39 +0000 Subject: [PATCH 0161/3297] iio: adc: ad7380: select REGMAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6697091b386a4e2830bdd38512c87a4befff2b32 upstream. The AD7380 driver uses generic regmap types and APIs. However, its Kconfig entry does not select REGMAP. As a result, AD7380 can be enabled from an allnoconfig-derived config with SPI_MASTER=y while REGMAP remains unset, causing ad7380.o to fail to build. Fixes: b095217c104b ("iio: adc: ad7380: new driver for AD7380 ADCs") Signed-off-by: Samuel Moelius Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 58a14e6833f60..427d21523b4ac 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -282,6 +282,7 @@ config AD7298 config AD7380 tristate "Analog Devices AD7380 ADC driver" depends on SPI_MASTER + select REGMAP select SPI_OFFLOAD select IIO_BUFFER select IIO_BUFFER_DMAENGINE From 24a9514b606e76499ec85e7cba95d3f146dfe5d0 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 11 Jan 2026 16:55:28 +0000 Subject: [PATCH 0162/3297] iio: adc: ad7768-1: Select GPIOLIB commit 5a9c90350be4f6f175bdd193e8bd60a7aedfb4d2 upstream. This driver provides a gpio chip a some related functions are not stubbed if GPIOLIB is not built. ad7768-1.c:420: undefined reference to `gpiochip_get_data' ad7768-1.c:498: undefined reference to `devm_gpiochip_add_data_with_key' Dual sign off reflects change of affiliation whilst this was under review. Fixes: d569ae0f052e ("iio: adc: ad7768-1: Add GPIO controller support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512271235.WwAmAbOa-lkp@intel.com/ Signed-off-by: Jonathan Cameron Cc: Stable@vger.kernel.org Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 427d21523b4ac..1a4caa0dee964 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -386,6 +386,7 @@ config AD7766 config AD7768_1 tristate "Analog Devices AD7768-1 ADC driver" depends on SPI + select GPIOLIB select REGULATOR select REGMAP_SPI select IIO_BUFFER From 46e93fcbe7c2caa80c83da260158c71ec915bc88 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 1 Jul 2026 21:21:47 +0200 Subject: [PATCH 0163/3297] iio: adc: ad7779: add missing 'select IIO_TRIGGERED_BUFFER' to Kconfig commit fd354554af1d2b33232ca6c8a3d79ed82413d715 upstream. The Kconfig entry for the AD7779 is missing a 'select IIO_TRIGGERED_BUFFER' parameter, causing build failures. Fixes: c9a3f8c7bfcb ("drivers: iio: adc: add support for ad777x family") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Tested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1a4caa0dee964..d4965c4b5d86f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -404,6 +404,7 @@ config AD7779 depends on SPI select CRC8 select IIO_BUFFER + select IIO_TRIGGERED_BUFFER select IIO_BACKEND help Say yes here to build support for Analog Devices AD777X family From 3394e0b3328422431cadaf314fa58d3717ed4936 Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 27 May 2026 12:38:39 +0300 Subject: [PATCH 0164/3297] iio: adc: ad_sigma_delta: fix clear_pending_event for registerless devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 91bc6767a4f55dc470d8a56b55b9f2ea09094efe upstream. ad_sigma_delta_clear_pending_event() falls through to the status register read path for devices with has_registers = false and no rdy_gpiod. For such devices, ad_sd_read_reg() skips the address byte entirely and clocks raw MISO bytes with no address phase — making it byte-for-byte identical to reading conversion data. If a pending conversion result is present, this partially consumes it and corrupts the data stream for the subsequent ad_sd_read_reg() call in ad_sigma_delta_single_conversion(). Furthermore, with num_resetclks = 0 on these devices, data_read_len evaluates to 0. If the clocked byte has bit 7 clear, pending_event is set and the code attempts memset(data + 2, 0xff, 0 - 1), overflowing to SIZE_MAX and corrupting the heap. Fix by returning 0 immediately when neither rdy_gpiod nor has_registers is set. This is safe for all current registerless devices: ad7191 and ad7780 (with powerdown GPIO) are reset between conversions by CS deassertion, so there is no stale result to drain; ad7780 (without powerdown GPIO) and max11205 are continuously-converting and cycle ~DRDY at the output data rate regardless of whether the previous result was read, so the next falling edge fires naturally. A future registerless device that holds ~DRDY asserted until data is read would be broken by this early return and would require either num_resetclks set or a rdy-gpio. The same heap corruption is reachable on any device with rdy_gpiod set but num_resetclks = 0: if the GPIO indicates a pending event, the drain path executes memset(data + 2, 0xff, 0 - 1) regardless of has_registers. Add an explicit data_read_len == 0 guard after the pending event check; the stale result is then consumed by the first ad_sd_read_reg() call in ad_sigma_delta_single_conversion(). Fixes: 132d44dc6966 ("iio: adc: ad_sigma_delta: Check for previous ready signals") Signed-off-by: Radu Sabau Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad_sigma_delta.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 7852884703b0b..dfe52eab88bce 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -260,11 +260,25 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta /* * Read R̅D̅Y̅ pin (if possible) or status register to check if there is an - * old event. + * old event. For devices with neither an RDY GPIO nor registers, + * ad_sd_read_reg() transmits no address byte and clocks raw MISO bytes, + * which is indistinguishable from reading conversion data and would + * partially consume a pending result. Skip the check for such devices. + * + * This is safe for all current registerless devices: ad7191 and ad7780 + * (with powerdown GPIO) are reset between conversions by CS deassertion, + * so there is no stale result to drain; ad7780 (without powerdown GPIO) + * and max11205 are continuously-converting and cycle ~DRDY at the output + * data rate regardless of whether the previous result was read, so the + * next falling edge fires naturally. + * + * A future registerless device that holds ~DRDY asserted until data is + * read would be broken by this early return and would need either + * num_resetclks set or a rdy-gpio. */ if (sigma_delta->rdy_gpiod) { pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); - } else { + } else if (sigma_delta->info->has_registers) { unsigned int status_reg; ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); @@ -272,11 +286,24 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta return ret; pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } else { + return 0; } if (!pending_event) return 0; + /* + * With num_resetclks = 0, data_read_len is 0 and the drain sequence + * below would compute memset(data + 2, 0xff, 0 - 1), underflowing to + * SIZE_MAX and corrupting the heap. There is no safe way to drain the + * stale result without knowing the data register size; it will be + * consumed by the first ad_sd_read_reg() call in + * ad_sigma_delta_single_conversion(). + */ + if (!data_read_len) + return 0; + /* * In general the size of the data register is unknown. It varies from * device to device, might be one byte longer if CONTROL.DATA_STATUS is From c313bb7c38855e94ceef939d152dd75e5a904b5f Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 27 May 2026 12:38:38 +0300 Subject: [PATCH 0165/3297] iio: adc: ad_sigma_delta: fix CS held asserted and state leaks commit c72da0688575e5ef39c36bb44fed53aa18f8ae65 upstream. In ad_sigma_delta_single_conversion(), set_mode(AD_SD_MODE_IDLE) and disable_one() were called from the out: block while keep_cs_asserted was still true. This caused any SPI transfer issued by those callbacks to carry cs_change=1, leaving CS permanently asserted after the conversion. Fix by moving both calls into the out_unlock: block, after keep_cs_asserted is cleared, matching the pattern already used in ad_sd_calibrate(). In the error path of ad_sd_buffer_postenable(), if an operation fails after set_mode(AD_SD_MODE_CONTINUOUS) has already succeeded (e.g. spi_offload_trigger_enable()), the device is left in continuous conversion mode with CS physically asserted. Additionally, bus_locked remaining true after spi_bus_unlock() causes subsequent SPI operations to call spi_sync_locked() without the bus lock actually held, allowing concurrent SPI access. Fix the error path by clearing keep_cs_asserted first, then calling set_mode(AD_SD_MODE_IDLE) to revert the device mode and deassert CS, then clearing bus_locked before releasing the bus. For devices that implement neither set_mode nor disable_one (such as MAX11205, which has no physical CS pin), no SPI transfer is issued during cleanup and the cs_change flag has no effect on any physical line. Fixes: 132d44dc6966 ("iio: adc: ad_sigma_delta: Check for previous ready signals") Signed-off-by: Radu Sabau Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad_sigma_delta.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index dfe52eab88bce..e2cf5f9136705 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -466,11 +466,10 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, out: ad_sd_disable_irq(sigma_delta); - ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); - ad_sigma_delta_disable_one(sigma_delta, chan->address); - out_unlock: sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, chan->address); sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); out_release: @@ -603,6 +602,9 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return 0; err_unlock: + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); spi_unoptimize_message(&sigma_delta->sample_msg); From 9e2e8b8cdfd37ae7c7a8a5c96c59e98a768731c4 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:10 -0500 Subject: [PATCH 0166/3297] iio: adc: lpc32xx: Initialize completion before requesting IRQ commit e561b35633f450ee607e87a6401d97f156a0cd54 upstream. In the report from Jaeyoung Chung: "lpc32xx_adc_probe() in drivers/iio/adc/lpc32xx_adc.c registers its interrupt handler with devm_request_irq() before it initializes st->completion with init_completion(). If an interrupt arrives after devm_request_irq() and before init_completion(), the handler calls complete() on an uninitialized completion, causing a kernel panic. The probe path, in lpc32xx_adc_probe(): iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); /* st kzalloc-zeroed */ ... retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, LPC32XXAD_NAME, st); /* register handler */ ... init_completion(&st->completion); /* initialize completion */ lpc32xx_adc_isr() calls complete(): complete(&st->completion); If the device raises an interrupt before init_completion() runs, complete() acquires the uninitialized wait.lock and walks the zeroed task_list in swake_up_locked(). The zeroed task_list makes list_empty() return false, so swake_up_locked() dereferences a NULL list entry, triggering a KASAN wild-memory-access." Fix the chance of a spurious IRQ causing an uninitialized pointer dereference by moving init_completion() above devm_request_irq(). Fixes: 7901b2a1453e ("staging:iio:adc:lpc32xx rename local state structure to _state") Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Reported-by: Jaeyoung Chung Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/ Signed-off-by: Maxwell Doose Reviewed-by: Vladimir Zapolskiy Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/lpc32xx_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c index 43a7bc8158b57..db3a602327ffe 100644 --- a/drivers/iio/adc/lpc32xx_adc.c +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -179,6 +179,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) if (irq < 0) return irq; + init_completion(&st->completion); + retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, LPC32XXAD_NAME, st); if (retval < 0) { @@ -197,8 +199,6 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, iodev); - init_completion(&st->completion); - iodev->name = LPC32XXAD_NAME; iodev->info = &lpc32xx_adc_iio_info; iodev->modes = INDIO_DIRECT_MODE; From bbfebae473ac2c8a194523b29ccb9b45f02f134c Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:11 -0500 Subject: [PATCH 0167/3297] iio: adc: spear: Initialize completion before requesting IRQ commit 3ee2128b6f0eb0be7b6cb8f6e0f1f113a65201a0 upstream. In the report from Jaeyoung Chung: "spear_adc_probe() in drivers/iio/adc/spear_adc.c registers its interrupt handler with devm_request_irq() before it initializes st->completion with init_completion(). If an interrupt arrives after devm_request_irq() and before init_completion(), the handler calls complete() on an uninitialized completion, causing a kernel panic. The probe path, in spear_adc_probe(): iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); /* st kzalloc-zeroed */ ... retval = devm_request_irq(&pdev->dev, irq, spear_adc_isr, 0, LPC32XXAD_NAME, st); /* register handler */ ... init_completion(&st->completion); /* initialize completion */ spear_adc_isr() calls complete(): complete(&st->completion); If the device raises an interrupt before init_completion() runs, complete() acquires the uninitialized wait.lock and walks the zeroed task_list in swake_up_locked(). The zeroed task_list makes list_empty() return false, so swake_up_locked() dereferences a NULL list entry, triggering a KASAN wild-memory-access." Fix the chance of a spurious IRQ causing an uninitialized pointer dereference by moving init_completion() above devm_request_irq(). Fixes: b586e5d9eee0 ("staging:iio:adc:spear rename device specific state structure to _state") Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Reported-by: Jaeyoung Chung Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/ Signed-off-by: Maxwell Doose Reviewed-by: Vladimir Zapolskiy Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/spear_adc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 50b0a607baeb7..79b0aa1df61df 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -280,6 +280,7 @@ static int spear_adc_probe(struct platform_device *pdev) st = iio_priv(indio_dev); st->dev = dev; + init_completion(&st->completion); mutex_init(&st->lock); /* @@ -326,8 +327,6 @@ static int spear_adc_probe(struct platform_device *pdev) spear_adc_configure(st); - init_completion(&st->completion); - indio_dev->name = SPEAR_ADC_MOD_NAME; indio_dev->info = &spear_adc_info; indio_dev->modes = INDIO_DIRECT_MODE; From ffb2195921c3d629194b9807de589578df9f9cb8 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 3 Jun 2026 20:16:40 +0800 Subject: [PATCH 0168/3297] iio: adc: ti-ads1119: fix PM reference leak in buffer preenable commit adf4bc07f814da8329278d32600147f5a150938c upstream. ads1119_triggered_buffer_preenable() resumes the device with pm_runtime_resume_and_get() before starting a conversion. If i2c_smbus_write_byte() fails, the function returns the error directly and leaves the runtime PM usage counter elevated. The matching postdisable callback is not called when preenable fails, so the reference is leaked and the device may remain runtime-active indefinitely. Store the I2C transfer result in ret and drop the runtime PM reference on failure before returning the error. Fixes: a9306887eba41 ("iio: adc: ti-ads1119: Add driver") Signed-off-by: Guangshuo Li Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ti-ads1119.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 79be71b4de963..9324408cb3b05 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -459,7 +459,11 @@ static int ads1119_triggered_buffer_preenable(struct iio_dev *indio_dev) if (ret) return ret; - return i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + if (ret) + pm_runtime_put_autosuspend(dev); + + return ret; } static int ads1119_triggered_buffer_postdisable(struct iio_dev *indio_dev) From 0f30e68dd6c1f5c6451de7e0f0b3bc79f9d12ba4 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:44:07 +0800 Subject: [PATCH 0169/3297] iio: adc: ti-ads124s08: Return reset GPIO lookup errors commit 7dc4de2aa6316f1d044cde21f5acfec5f3ec6b47 upstream. devm_gpiod_get_optional() returns NULL when the optional GPIO is absent, but returns an ERR_PTR when the GPIO provider lookup fails, including probe deferral. Probe currently logs the ERR_PTR case as if the reset GPIO were simply absent and keeps the error pointer in reset_gpio. Later ads124s_reset() treats any non-NULL reset_gpio as a valid descriptor and passes it to gpiod_set_value_cansleep(). Return the lookup error instead of retaining the ERR_PTR. Fixes: e717f8c6dfec ("iio: adc: Add the TI ads124s08 ADC code") Cc: stable@vger.kernel.org Reviewed-by: Joshua Crofts Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ti-ads124s08.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 8ea1269f74db0..57eed8554bd92 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -321,7 +321,8 @@ static int ads124s_probe(struct spi_device *spi) ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ads124s_priv->reset_gpio)) - dev_info(&spi->dev, "Reset GPIO not defined\n"); + return dev_err_probe(&spi->dev, PTR_ERR(ads124s_priv->reset_gpio), + "Failed to get reset GPIO\n"); ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; From c28835b8618ecb288605ba139c7d192f6d239cc4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:16:11 +0300 Subject: [PATCH 0170/3297] iio: backend: fix uninitialized data in debugfs commit a6e8b14a4897d0b6df9744f33d0a30e6b92368eb upstream. If the *ppos value is non-zero then simple_write_to_buffer() will not initialize the start of the buf[] buffer. Non-zero ppos values aren't going to work at all. Check for that at the start of the function and return -ENOSPC. Fixes: cdf01e0809a4 ("iio: backend: add debugFs interface") Signed-off-by: Dan Carpenter Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 23760652a046d..b8ffc0baa28ef 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -155,7 +155,7 @@ static ssize_t iio_backend_debugfs_write_reg(struct file *file, ssize_t rc; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -ENOSPC; rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, count); From 82accdd57404399eddf3d56fd9beda7c61307388 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Tue, 26 May 2026 17:55:24 -0500 Subject: [PATCH 0171/3297] iio: chemical: scd30: Cleanup initializations and fix sign-extension bug commit 60d877910a43c305b5165131b258a17b1d772d57 upstream. Include linux/bitfield.h for FIELD_GET(). Create new macros for bit manipulation in combination with manual bit manipulation being replaced with FIELD_GET(). The current variable declaration and initializations are barely readable and use comma separations across multiple lines. Refactor the initializations so that mantissa and exp have separate declarations and sign gets initialized later. In addition (and due to the nature of the cleanup), fix a sign-extension bug where, float32 would get bitwise anded with ~BIT(31) (which is 0xFFFFFFFF7FFFFFFF) which corrupted the exponent. Fixes: 64b3d8b1b0f5c ("iio: chemical: scd30: add core driver") Reported-by: sashiko Closes: https://sashiko.dev/#/patchset/20260524020309.18618-1-m32285159%40gmail.com Signed-off-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/chemical/scd30_core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 11d6bc1b63e67..d02f2de42327d 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -4,6 +4,8 @@ * * Copyright (c) 2020 Tomasz Duszynski */ + +#include #include #include #include @@ -43,6 +45,11 @@ #define SCD30_TEMP_OFFSET_MAX 655360 #define SCD30_EXTRA_TIMEOUT_PER_S 250 +/* Floating point arithmetic macros */ +#define SCD30_FLOAT_MANTISSA_MSK GENMASK(22, 0) +#define SCD30_FLOAT_EXP_MSK GENMASK(30, 23) +#define SCD30_FLOAT_SIGN_MSK BIT(31) + enum { SCD30_CONC, SCD30_TEMP, @@ -89,10 +96,14 @@ static int scd30_reset(struct scd30_state *state) /* simplified float to fixed point conversion with a scaling factor of 0.01 */ static int scd30_float_to_fp(int float32) { - int fraction, shift, - mantissa = float32 & GENMASK(22, 0), - sign = (float32 & BIT(31)) ? -1 : 1, - exp = (float32 & ~BIT(31)) >> 23; + int fraction, shift, sign; + int mantissa = FIELD_GET(SCD30_FLOAT_MANTISSA_MSK, float32); + int exp = FIELD_GET(SCD30_FLOAT_EXP_MSK, float32); + + if (float32 & SCD30_FLOAT_SIGN_MSK) + sign = -1; + else + sign = 1; /* special case 0 */ if (!exp && !mantissa) From b947bde73461ff523da014462f8de043e2cbd4e5 Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Tue, 16 Jun 2026 15:02:04 +0200 Subject: [PATCH 0172/3297] iio: common: st_sensors: honour channel endianness in read_axis_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 55052184ac9011db2ea983e54d6c21f0b1079a12 upstream. st_sensors_read_axis_data() unconditionally decoded multi-byte results with get_unaligned_le16() / get_unaligned_le24() regardless of the channel's declared scan_type.endianness. For every ST sensor that has used this helper since it was introduced this happened to be fine because the ST IMU/accel/gyro/pressure families publish their data registers as little-endian and the channel specs in those drivers declare IIO_LE accordingly. The LSM303DLH magnetometer however publishes its X/Y/Z output as a pair of big-endian bytes (the H register sits at the lower address, 0x03/0x05/0x07, and the L register immediately after), and its channel specs in st_magn_core.c correctly declare IIO_BE -- but read_axis_data() ignored that and decoded as little-endian, swapping the high and low bytes of every magnetometer sample. The LSM303DLHC and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and were therefore byte-swapped by the same bug; users of those parts will see different in_magn_*_raw values after this fix lands. The bug is most visible on a stationary chip: in earth's field the true X reading is small and the high byte sits at 0x00, so swapping the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0 = 240). Y and Z still appear "to vary" because their magnitudes are larger and the noise in the low byte produces big swings in the swapped high byte: before (LSM303DLH flat, sysfs in_magn_*_raw): X=240 (stuck), Y= 12032..23296, Z=-16128..-9728 after (direct i2c-dev big-endian decode, same chip same orientation): X≈-4096, Y≈210, Z≈80 (sensible values reflecting earth's ambient field at low gauss range) Fix read_axis_data() to dispatch on ch->scan_type.endianness and call get_unaligned_be16() / get_unaligned_be24() when the channel declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro, st_pressure, st_lsm6dsx and others) are unaffected because their channel specs already declare IIO_LE and the LE path is unchanged. While restructuring the branches, replace the previously implicit silent-success-with-uninitialised-*data fall-through for byte_for_channel outside 1..3 with an explicit return -EINVAL. No in-tree ST sensor publishes such a channel, but the new behaviour is strictly safer than handing userspace garbage. Fixes: 23491b513bcd ("iio:common: Add STMicroelectronics common library") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch Assisted-by: Sashiko:claude-opus-4-7 Signed-off-by: Herman van Hazendonk Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- .../iio/common/st_sensors/st_sensors_core.c | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index dac593be56958..88c0eb9aa8ebf 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -498,6 +498,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 *outdata; struct st_sensor_data *sdata = iio_priv(indio_dev); unsigned int byte_for_channel; + u32 tmp; byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits + ch->scan_type.shift, 8); @@ -510,12 +511,22 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, if (err < 0) goto st_sensors_free_memory; - if (byte_for_channel == 1) - *data = (s8)*outdata; - else if (byte_for_channel == 2) - *data = (s16)get_unaligned_le16(outdata); - else if (byte_for_channel == 3) - *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23); + if (byte_for_channel == 1) { + tmp = *outdata; + } else if (byte_for_channel == 2) { + if (ch->scan_type.endianness == IIO_BE) + tmp = get_unaligned_be16(outdata); + else + tmp = get_unaligned_le16(outdata); + } else if (byte_for_channel == 3) { + if (ch->scan_type.endianness == IIO_BE) + tmp = get_unaligned_be24(outdata); + else + tmp = get_unaligned_le24(outdata); + } else { + return -EINVAL; + } + *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1); st_sensors_free_memory: kfree(outdata); From e166a8cfb28a3d0da260dd70cae274eb8c7cec8d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:16:27 +0300 Subject: [PATCH 0173/3297] iio: core: fix uninitialized data in debugfs commit ab92ed206d41fd171ebd37bc46360d9f2140d043 upstream. If *ppos is non-zero then simple_write_to_buffer() will not initialize the start of buf[]. Non zero values for *ppos aren't going to work anyway. Test for them at the start of the function and return -EINVAL. Fixes: 6d5dd486c715 ("iio: core: make use of simple_write_to_buffer()") Signed-off-by: Dan Carpenter Reviewed-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 117ffad4f3769..be2b3259c8e71 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -418,7 +418,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file, char buf[80]; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, From 2358da87315d117e9e5134afc1d2a18fa2da72f8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:15:46 +0300 Subject: [PATCH 0174/3297] iio: dac: ad3552r-hs: fix uninitialized data ni ad3552r_hs_write_data_source() commit eaaa7eef181892ef7ba56c6295b81f0ae4492c13 upstream. If the *ppos value is non-zero then the simple_write_to_buffer() function won't initialized the start of the buf[] buffer. Non-zero values for *ppos won't work here generally, so just test for them and return -ENOSPC at the start of the function. Fixes: b1c5d68ea66e ("iio: dac: ad3552r-hs: add support for internal ramp") Signed-off-by: Dan Carpenter Reviewed-by: Angelo Dureghello Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad3552r-hs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index a9578afa7015d..6bc64f53bce91 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -549,7 +549,7 @@ static ssize_t ad3552r_hs_write_data_source(struct file *f, guard(mutex)(&st->lock); - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -ENOSPC; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, From 9edefd4c56bee3fe331e0355d1f10a533134999d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 6 Jul 2026 21:48:26 -0700 Subject: [PATCH 0175/3297] iio: event: Fix event FIFO reset race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit af791d295737ea6b6ff2c8d8488462a49c14af01 upstream. `iio_event_getfd()` creates the event file descriptor with `anon_inode_getfd()`, which allocates a new fd, creates the anonymous file and installs it in the process fd table before returning to the caller. The IIO code resets the event FIFO after `anon_inode_getfd()` has returned, but before `IIO_GET_EVENT_FD_IOCTL` has copied the fd number to userspace. But since fd tables are shared between threads, another thread can guess the newly allocated fd number and issue a `read()` on it as soon as the fd has been installed. This means the `kfifo_to_user()` in `iio_event_chrdev_read()` can run in parallel with the `kfifo_reset_out()` in `iio_event_getfd()`. The kfifo documentation says that `kfifo_reset_out()` is only safe when it is called from the reader thread and there is only one concurrent reader. Otherwise it is dangerous and must be handled in the same way as `kfifo_reset()`. If that happens, `kfifo_to_user()` can advance the FIFO `out` index based on state from before the reset, after the reset has already moved the `out` index to the current `in` index. That can leave the FIFO with an `out` index past the `in` index. A later `read()` can then see an underflowed FIFO length and copy more data than the event FIFO buffer contains. This can result in an out-of-bounds read and leak adjacent kernel memory to userspace. Move the FIFO reset before `anon_inode_getfd()`. At that point the event fd is marked busy, but the new fd has not been installed yet, so userspace cannot access it while the FIFO is reset. Fixes: b91accafbb10 ("iio:event: Fix and cleanup locking") Reported-by: Codex:gpt-5.5 Signed-off-by: Lars-Peter Clausen Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-event.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 06295cfc2da8b..1eee1ad2036f9 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -207,6 +207,8 @@ static int iio_event_getfd(struct iio_dev *indio_dev) goto unlock; } + kfifo_reset_out(&ev_int->det_events); + iio_device_get(indio_dev); fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, @@ -214,10 +216,7 @@ static int iio_event_getfd(struct iio_dev *indio_dev) if (fd < 0) { clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); iio_device_put(indio_dev); - } else { - kfifo_reset_out(&ev_int->det_events); } - unlock: mutex_unlock(&iio_dev_opaque->mlock); return fd; From 7bbf02b63961fc1768c9c654392c11f2077d4c59 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 10 May 2026 07:35:00 +0500 Subject: [PATCH 0176/3297] iio: gyro: bmg160: bail out when bandwidth/filter is not in table commit 8320c77e67382d5d55d77043a5f60a867d408a2b upstream. bmg160_get_filter() walks bmg160_samp_freq_table[] looking for the entry matching the bw_bits value read from the chip: for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { if (bmg160_samp_freq_table[i].bw_bits == bw_bits) break; } *val = bmg160_samp_freq_table[i].filter; If no entry matches, i ends up equal to the array size and the next line reads one slot past the end. bmg160_set_filter() has the same shape, driven by 'val' instead of bw_bits. smatch flags both: drivers/iio/gyro/bmg160_core.c:204 bmg160_get_filter() error: buffer overflow 'bmg160_samp_freq_table' 7 <= 7 drivers/iio/gyro/bmg160_core.c:222 bmg160_set_filter() error: buffer overflow 'bmg160_samp_freq_table' 7 <= 7 Return -EINVAL when no entry matches. The set_filter() path is reachable from userspace via the sysfs in_anglvel_filter_low_pass_3db_frequency interface, so userspace can trivially trigger the out-of-bounds read with a value that is not in bmg160_samp_freq_table[].filter. Fixes: 22b46c45fb9b ("iio:gyro:bmg160 Gyro Sensor driver") Signed-off-by: Stepan Ionichev Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/gyro/bmg160_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 38394b5f32755..58963f3ea186f 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -201,6 +201,9 @@ static int bmg160_get_filter(struct bmg160_data *data, int *val) break; } + if (i == ARRAY_SIZE(bmg160_samp_freq_table)) + return -EINVAL; + *val = bmg160_samp_freq_table[i].filter; return ret ? ret : IIO_VAL_INT; @@ -218,6 +221,9 @@ static int bmg160_set_filter(struct bmg160_data *data, int val) break; } + if (i == ARRAY_SIZE(bmg160_samp_freq_table)) + return -EINVAL; + ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bmg160_samp_freq_table[i].bw_bits); if (ret < 0) { From 001527e2382e31d948415818f425b2d4b7da012e Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 11:40:20 +0500 Subject: [PATCH 0177/3297] iio: gyro: bmg160: wait full startup time after mode change at probe commit 088fcb9b567f8723074ad9eb1bf5cb46f8a0096b upstream. bmg160_chip_init() calls bmg160_set_mode(BMG160_MODE_NORMAL) and then waits only 500-1000 us. Per the BMG160 datasheet (BST-BMG160-DS000-07 Rev. 1.0, May 2013), the start-up and wake-up times (tsu, twusm) are 30 ms. The same file already waits BMG160_MAX_STARTUP_TIME_MS (80 ms) in bmg160_runtime_resume() after the same set_mode(NORMAL) operation. The 500 us value at probe was likely a unit mix-up; the old comment said "500 ms" while the code used microseconds. Reuse the same constant via msleep() and add a code comment explaining the datasheet basis for the wait. Without this, register writes that follow the mode change can hit the chip before it is ready. Fixes: 22b46c45fb9b ("iio:gyro:bmg160 Gyro Sensor driver") Signed-off-by: Stepan Ionichev Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/gyro/bmg160_core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 58963f3ea186f..d611341a0e2ad 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -264,8 +264,14 @@ static int bmg160_chip_init(struct bmg160_data *data) if (ret < 0) return ret; - /* Wait upto 500 ms to be ready after changing mode */ - usleep_range(500, 1000); + /* + * Wait for the chip to be ready after switching to normal mode. + * The BMG160 datasheet (BST-BMG160-DS000-07 Rev. 1.0, May 2013) + * specifies a start-up / wake-up time (tsu, twusm) of 30 ms; use + * BMG160_MAX_STARTUP_TIME_MS (80 ms) as a safety margin, matching + * what bmg160_runtime_resume() already does. + */ + msleep(BMG160_MAX_STARTUP_TIME_MS); /* Set Bandwidth */ ret = bmg160_set_bw(data, BMG160_DEF_BW); From bdafd53ae671ed38ff2c1a4c88ce0fbc60e92f6f Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:46 +0800 Subject: [PATCH 0178/3297] iio: imu: adis: add IRQF_NO_THREAD to non-FIFO trigger IRQ commit 6e1b9bff1202da55c464e36bd34a2b6863d7fe30 upstream. devm_adis_probe_trigger() registers iio_trigger_generic_data_rdy_poll() through devm_request_irq() on the non-FIFO path, but it does not add IRQF_NO_THREAD to the IRQ flags. When the kernel is booted with forced IRQ threading, the parent IRQ can otherwise be threaded by the IRQ core and the subsequent IIO trigger child IRQ is then dispatched from irq/... thread context instead of hardirq context. Because iio_trigger_generic_data_rdy_poll() immediately drives iio_trigger_poll(), this violates the hardirq-only IIO trigger helper contract and can push downstream trigger consumers through the wrong execution context. Add IRQF_NO_THREAD on top of the existing adis->irq_flag value for the non-FIFO request_irq() path, while preserving the current trigger polarity and IRQF_NO_AUTOEN behavior. Fixes: fec86c6b8369 ("iio: imu: adis: Add Managed device functions") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/adis_trigger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index d76e13cbac680..3e6a7af6ab017 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -94,7 +94,7 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) else ret = devm_request_irq(&adis->spi->dev, adis->spi->irq, &iio_trigger_generic_data_rdy_poll, - adis->irq_flag, + adis->irq_flag | IRQF_NO_THREAD, indio_dev->name, adis->trig); if (ret) From 0522819228284eab9ec91fa4258c37fa8eb160a2 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:47 +0800 Subject: [PATCH 0179/3297] iio: imu: bmi160: add IRQF_NO_THREAD to data-ready trigger IRQ commit cd5a6a5096b246e10600da3ac47a1274ce9573c8 upstream. bmi160_probe_trigger() registers iio_trigger_generic_data_rdy_poll() through devm_request_irq(), but it passes only irq_type and does not add IRQF_NO_THREAD. When the kernel is booted with forced IRQ threading, the parent IRQ can otherwise be threaded by the IRQ core and the subsequent IIO trigger child IRQ is dispatched from irq/... thread context instead of hardirq context. Because the handler immediately pushes the event into iio_trigger_poll(), this violates the hardirq-only IIO trigger helper contract and can drive downstream trigger consumers through the wrong execution context. Add IRQF_NO_THREAD on top of irq_type when registering the BMI160 data- ready trigger handler. Fixes: 895bf81e6bbf ("iio:bmi160: add drdy interrupt support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/bmi160/bmi160_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 4abb83b75e2e8..86f6ecfd64aa2 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -788,7 +788,8 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type) ret = devm_request_irq(&indio_dev->dev, irq, &iio_trigger_generic_data_rdy_poll, - irq_type, "bmi160", data->trig); + irq_type | IRQF_NO_THREAD, + "bmi160", data->trig); if (ret) return ret; From 34656a59322e5951bb346728adf4e53ff9e74740 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Tue, 23 Jun 2026 16:22:15 +0200 Subject: [PATCH 0180/3297] iio: imu: inv_icm42600: fix timestamp clock period by using lower value commit 8b0b864c11a2e2ada470f9d5010e1c2bf1eceef2 upstream. Clock period value is used for computing periods of sampling. There is no need for it to be higher than the maximum odr, otherwise we are losing precision in the computation for nothing. Switch clock period value to maximum odr period (8kHz). Fixes: 0ecc363ccea7 ("iio: make invensense timestamp module generic") Cc: stable@vger.kernel.org Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 4 ++-- drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 0ab6eddf0543f..96a6213770d41 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -1170,10 +1170,10 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) accel_st->filter = INV_ICM42600_FILTER_AVG_16X; /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&accel_st->ts, &ts_chip); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 11339ddf1da36..a18dcac939298 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -755,10 +755,10 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) } /* - * clock period is 32kHz (31250ns) + * clock period is 8kHz (125000ns) * jitter is +/- 2% (20 per mille) */ - ts_chip.clock_period = 31250; + ts_chip.clock_period = 125000; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip); From 76e12a71ac0538560e1e497e1dc9bbf4b4451e9c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Mon, 29 Jun 2026 21:51:55 +0200 Subject: [PATCH 0181/3297] iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading commit affe3f077d7a4eeb25937f5323ff059a54b4712c upstream. Timestamps are made by measuring the chip clock using the watermark interrupts. If we read more than watermark samples as done today, we are reducing the period between interrupts and distort the time measurement. Fix that by reading only watermark samples in the interrupt case. Fixes: 7f85e42a6c54 ("iio: imu: inv_icm42600: add buffer support in iio devices") Cc: stable@vger.kernel.org Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 9 +++++---- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 68a3957580318..5c3840acf0857 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -248,6 +248,7 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) /* compute watermark value in bytes */ wm_size = watermark * packet_size; + st->fifo.watermark.value = watermark; /* changing FIFO watermark requires to turn off watermark interrupt */ ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0, @@ -454,11 +455,10 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, st->fifo.nb.accel = 0; st->fifo.nb.total = 0; - /* compute maximum FIFO read size */ + /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */ if (max == 0) - max_count = sizeof(st->fifo.data); - else - max_count = max * inv_icm42600_get_packet_size(st->fifo.en); + max = st->fifo.watermark.value; + max_count = max * inv_icm42600_get_packet_size(st->fifo.en); /* read FIFO count value */ raw_fifo_count = (__be16 *)st->buffer; @@ -574,6 +574,7 @@ int inv_icm42600_buffer_init(struct inv_icm42600_state *st) st->fifo.watermark.eff_gyro = 1; st->fifo.watermark.eff_accel = 1; + st->fifo.watermark.value = 1; /* * Default FIFO configuration (bits 7 to 5) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index ffca4da1e2493..88b8b9f780af3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -34,6 +34,7 @@ struct inv_icm42600_fifo { unsigned int accel; unsigned int eff_gyro; unsigned int eff_accel; + unsigned int value; } watermark; size_t count; struct { From 482b24660ec3b4b4a0991472508e5549410b5c08 Mon Sep 17 00:00:00 2001 From: Andreas Kempe Date: Thu, 2 Jul 2026 10:41:23 +0000 Subject: [PATCH 0182/3297] iio: imu: st_lsm6dsx: deselect shub page before reading whoami commit aede83625ff5d9539508582036df30c809d51058 upstream. As part of driver initialization, e.g. st_lsm6dsx_init_shub() selects the shub register page using st_lsm6dsx_set_page(). Selecting the shub register page shadows the regular register space so whoami, among other registers, is no longer accessible. In applications where the IMU is permanently powered separately from the processor, there is a window where a reset of the CPU leaves the IMU in the shub register page. Once this occurs, any subsequent probe attempt fails because of the register shadowing. Using the ism330dlc, the error typically looks like st_lsm6dsx_i2c 3-006a: unsupported whoami [10] with the unknown whoami read from a reserved register in the shub page. The reset register is also shadowed by the page select, preventing a reset from recovering the chip. Unconditionally clear the shub page before the whoami readout to ensure normal register access and allow the initialization to proceed. Place the fix in st_lsm6dsx_check_whoami() before the whoami check because hw->settings, which st_lsm6dsx_set_page() relies on, is first assigned in that function. Placing the fix in a more logical place than the whoami check would require a bigger restructuring of the code. Fixes: c91c1c844ebd ("iio: imu: st_lsm6dsx: add i2c embedded controller support") Signed-off-by: Andreas Kempe Acked-by: Lorenzo Bianconi Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 28ebb27d89249..2a826da915e0d 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1597,6 +1597,26 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, return -ENODEV; } + hw->settings = &st_lsm6dsx_sensor_settings[i]; + + if (hw->settings->shub_settings.page_mux.addr) { + /* + * If the IMU has the shub page selected on init, for example + * after a CPU watchdog reset while the page is selected, the + * regular register space is shadowed. While the regular + * register space is shadowed, the registers needed for + * initializing the IMU are not available. + * + * Unconditionally clear the shub page selection to ensure + * normal register access. + */ + err = st_lsm6dsx_set_page(hw, false); + if (err < 0) { + dev_err(hw->dev, "failed to clear shub page\n"); + return err; + } + } + err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data); if (err < 0) { dev_err(hw->dev, "failed to read whoami register\n"); @@ -1609,7 +1629,6 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, } *name = st_lsm6dsx_sensor_settings[i].id[j].name; - hw->settings = &st_lsm6dsx_sensor_settings[i]; return 0; } From 6afb69bb969ed1ea22612bf6cd44f16fe68e1d47 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:08 +0200 Subject: [PATCH 0183/3297] iio: light: al3000a: add missing REGMAP_I2C to Kconfig commit a2d30022b7c316ad845d1b696e724058b88e5a4e upstream. The KConfig entry for the al3000a is missing a `select REGMAP_I2C`, causing build failures. Fixes: d531b9f78949 ("iio: light: Add support for AL3000a illuminance sensor") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index ac1408d374c92..e8a136dc270f0 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -45,6 +45,7 @@ config ADUX1020 config AL3000A tristate "AL3000a ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3000a From 9fb4ff07d97e33eab5dd33dcb8188156215cff6b Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:09 +0200 Subject: [PATCH 0184/3297] iio: light: al3010: add missing REGMAP_I2C to Kconfig commit 84486e3bbda18a2df1ed74ca78e1e14bde9a941b upstream. The KConfig entry for the AL3010 is missing a `select REGMAP_I2C`, causing build failures. Fixes: 0e5e21e23dd6 ("iio: light: al3010: Implement regmap support") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index e8a136dc270f0..2e354a8d9f3d1 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -56,6 +56,7 @@ config AL3000A config AL3010 tristate "AL3010 ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3010 From cd278561640c716b2eff0b0143fd02cc9ee8b437 Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Sat, 4 Jul 2026 17:22:45 +0530 Subject: [PATCH 0185/3297] iio: light: al3010: fix incorrect scale for the highest gain range commit aa411adc6ce40ad1a55ebc965f255a4cfc0005f8 upstream. al3010_scales[] encodes the highest gain range as {0, 1187200}. For IIO_VAL_INT_PLUS_MICRO, the fractional part must be less than 1000000, so the scale 1.1872 should instead be represented as { 1, 187200 }. Since write_raw() compares the value from userspace against this table, writing the advertised 1.1872 scale never matches the malformed entry and returns -EINVAL. As a result, the highest gain range cannot be selected. Reading the scale in that state also reports the malformed value. Fixes: c36b5195ab70 ("iio: light: add Dyna-Image AL3010 driver") Signed-off-by: Vidhu Sarwal Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/al3010.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 0932fa2b49fa7..11eb68e889cb7 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -42,7 +42,7 @@ enum al3xxxx_range { }; static const int al3010_scales[][2] = { - {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600} + { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 }, }; static const struct regmap_config al3010_regmap_config = { From a00d471cf3580d033bdabc196b09c4ccabff64aa Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 23 May 2026 13:44:57 +0200 Subject: [PATCH 0186/3297] iio: light: al3010: read both ALS ADC registers again commit ee78fae068f52a5582aaf448d9414f826855c106 upstream. al3010_read_raw() used to read two adjacent registers until the driver was modernized using the regmap framework. That cleanup accidentally replaced the 16-bit word read with a single byte read. I'm reverting latter. Fixes: 0e5e21e23dd6 ("iio: light: al3010: Implement regmap support") Signed-off-by: Alexander A. Klimov Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/al3010.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c index 11eb68e889cb7..0e72906c637f0 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -111,7 +111,8 @@ static int al3010_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3010_data *data = iio_priv(indio_dev); - int ret, gain, raw; + int ret, gain; + __le16 raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -120,11 +121,12 @@ static int al3010_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3010_REG_DATA_LOW * - high byte of output is stored at AL3010_REG_DATA_LOW + 1 */ - ret = regmap_read(data->regmap, AL3010_REG_DATA_LOW, &raw); + ret = regmap_bulk_read(data->regmap, AL3010_REG_DATA_LOW, + &raw, sizeof(raw)); if (ret) return ret; - *val = raw; + *val = le16_to_cpu(raw); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: From a1dafc918d793274fd5b14cdf2eae65371dd1f2a Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:10 +0200 Subject: [PATCH 0187/3297] iio: light: al3320a: add missing REGMAP_I2C to Kconfig commit 9efcc9ba9b2e940cc01e63d132ae741e4c5d09c7 upstream. The Kconfig entry for the al3320a is missing a `select REGMAP_I2C`, causing build failures. Fixes: 1850e6ae7f91 ("iio: light: al3320a: Implement regmap support") Signed-off-by: Joshua Crofts Reviewed-by: Andy Shevchenko Reviewed-by: David Heidelberg Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 2e354a8d9f3d1..b567efdc7d506 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -67,6 +67,7 @@ config AL3010 config AL3320A tristate "AL3320A ambient light sensor" + select REGMAP_I2C depends on I2C help Say Y here if you want to build a driver for the Dyna Image AL3320A From a60bf629a760d176e22182302284f1314db8c76b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 23 May 2026 13:44:58 +0200 Subject: [PATCH 0188/3297] iio: light: al3320a: read both ALS ADC registers again commit 744bccc2647c6b2206290e8e40890a23812116b3 upstream. al3320a_read_raw() used to read two adjacent registers until the driver was modernized using the regmap framework. That cleanup accidentally replaced the 16-bit word read with a single byte read. I'm reverting latter. Fixes: 1850e6ae7f91 ("iio: light: al3320a: Implement regmap support") Signed-off-by: Alexander A. Klimov Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/al3320a.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index 63f5a85912fc9..b9076f4344179 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -135,7 +135,8 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3320a_data *data = iio_priv(indio_dev); - int ret, gain, raw; + int ret, gain; + __le16 raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -144,11 +145,12 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3320A_REG_DATA_LOW * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1 */ - ret = regmap_read(data->regmap, AL3320A_REG_DATA_LOW, &raw); + ret = regmap_bulk_read(data->regmap, AL3320A_REG_DATA_LOW, + &raw, sizeof(raw)); if (ret) return ret; - *val = raw; + *val = le16_to_cpu(raw); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: From 0c655d067ac69ee24e2e9d706c54179ea58a43db Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:49 +0530 Subject: [PATCH 0189/3297] iio: light: gp2ap002: fix runtime PM leak on read error commit 38b72267b7e22768a1f26d9935de4e1752a1dc85 upstream. gp2ap002_read_raw() calls pm_runtime_get_sync() before reading the lux value, but if gp2ap002_get_lux() fails, it returns directly. This skips the pm_runtime_put_autosuspend() call at the "out" label, permanently leaking a runtime PM reference and preventing the device from autosuspending. Replace the direct return with a "goto out" to ensure the reference is properly dropped on the error path. Fixes: f6dbf83c17cb ("iio: light: gp2ap002: Take runtime PM reference on light read") Signed-off-by: Biren Pandya Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/gp2ap002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index a0d8a58f27042..886accf5e8591 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -258,7 +258,7 @@ static int gp2ap002_read_raw(struct iio_dev *indio_dev, case IIO_LIGHT: ret = gp2ap002_get_lux(gp2ap002); if (ret < 0) - return ret; + goto out; *val = ret; ret = IIO_VAL_INT; goto out; From 9d421c2827ea176dab1ca472921510eadd74afdb Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 26 May 2026 13:15:29 +0200 Subject: [PATCH 0190/3297] iio: light: opt3001: fix missing state reset on timeout commit c123ca6ee26ad98f70a866ff428b08145c5a24fe upstream. Currently in the function opt3001_get_processed(), there is a check that directly returns -ETIMEDOUT if the conversion IRQ times out, completely bypassing the err label, leaving ok_to_ignore_lock permanently true, potentially breaking the device's falling threshold interrupt detection. Assign -ETIMEDOUT to the return variable and jump to the error label to ensure ok_to_ignore_lock is properly reset. Fixes: 26d90b559057 ("iio: light: opt3001: Fixed timeout error when 0 lux") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260525-opt3001-cleanup-v4-0-65b36a174f78%40gmail.com?part=1 Signed-off-by: Joshua Crofts Cc: stable@vger.kernel.org Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/opt3001.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 393a3d2fbe1d7..e3584457a167f 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -366,8 +366,10 @@ static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready, msecs_to_jiffies(OPT3001_RESULT_READY_LONG)); - if (ret == 0) - return -ETIMEDOUT; + if (ret == 0) { + ret = -ETIMEDOUT; + goto err; + } } else { /* Sleep for result ready time */ timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ? From ef6c2a521454fdd9aedba24fdce89f434283a304 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 18 May 2026 14:43:11 +0500 Subject: [PATCH 0191/3297] iio: light: tsl2591: return actual error from probe IRQ failure commit a00ffd15674bfaf8b906503c1600e3d8709af56c upstream. When devm_request_threaded_irq() fails, probe logs the error and then returns -EINVAL, dropping the real error code and breaking the deferred-probe flow for -EPROBE_DEFER. Return ret directly; the IRQ subsystem already prints on failure. Fixes: 2335f0d7c790 ("iio: light: Added AMS tsl2591 driver implementation") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/tsl2591.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c index c5557867ea43d..c5ccd833dde6f 100644 --- a/drivers/iio/light/tsl2591.c +++ b/drivers/iio/light/tsl2591.c @@ -1137,10 +1137,8 @@ static int tsl2591_probe(struct i2c_client *client) NULL, tsl2591_event_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "tsl2591_irq", indio_dev); - if (ret) { - dev_err_probe(&client->dev, ret, "IRQ request error\n"); - return -EINVAL; - } + if (ret) + return ret; indio_dev->info = &tsl2591_info; } else { indio_dev->info = &tsl2591_info_no_irq; From 0975e013179d30b87c4f0beabdf750565f5e345a Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 14 May 2026 14:01:11 +1300 Subject: [PATCH 0192/3297] iio: light: veml6030: fix channel type when pushing events commit c52bb33b641ebaae3e209f97714cb1758206f7d9 upstream. The events are registered for IIO_LIGHT and not for IIO_INTENSITY. Use the correct channel type. When at it, fix minor checkpatch code style warning (alignment). Cc: stable@vger.kernel.org Fixes: 7b779f573c48 ("iio: light: add driver for veml6030 ambient light sensor") Signed-off-by: Javier Carrasco Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/veml6030.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 6bcacae3863c0..da8c32cabfd68 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -875,9 +875,11 @@ static irqreturn_t veml6030_event_handler(int irq, void *private) else evtdir = IIO_EV_DIR_FALLING; - iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, - 0, IIO_EV_TYPE_THRESH, evtdir), - iio_get_time_ns(indio_dev)); + iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + 0, + IIO_EV_TYPE_THRESH, + evtdir), + iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } From f829d6c32f31bee77a1a3a0e368cab0b841d40b4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2026 10:27:55 +0200 Subject: [PATCH 0193/3297] iio: magnetometer: ak8975: Add missed pm_runtime_put_autosuspend() call commit e94944d7364d3ddb273539492f9bd9c9a622549a upstream. On the failure in the ak8975_read_axis() the PM runtime gets unbalanced. Balance it by calling pm_runtime_put_autosuspend() on error path as well. Fixes: cde4cb5dd422 ("iio: magn: ak8975: deploy runtime and system PM") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260505-magnetometer-fixes-v5-0-831b9b5550fc%40gmail.com Signed-off-by: Andy Shevchenko Reviewed-by: Joshua Crofts Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/magnetometer/ak8975.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index d30315ad85ded..cc1fb1e633339 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -784,6 +784,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) exit: mutex_unlock(&data->lock); + pm_runtime_put_autosuspend(&data->client->dev); dev_err(&client->dev, "Error in reading axis\n"); return ret; } From e2d5b9673bf712836186664dc18df6e63a5048e5 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 5 May 2026 18:34:55 +0100 Subject: [PATCH 0194/3297] iio: pressure: bmp280: zero-init bmp580 trigger handler buffer commit 1172160f2a2de7bade3bec64b8c5ecf945cde5ed upstream. bmp580_trigger_handler() builds an on-stack scan buffer containing two __le32 fields and an aligned_s64 timestamp, and pushes it to userspace via iio_push_to_buffers_with_ts(). However, only the low 3 bytes of each __le32 field are populated by the device data: memcpy(&buffer.comp_press, &data->buf[3], 3); memcpy(&buffer.comp_temp, &data->buf[0], 3); The high byte of each field is left uninitialised on the stack. The bmp580 channels declare storagebits = 32, so the IIO core transports all four bytes per sample to userspace as part of the scan element, leaking two bytes of kernel stack per scan. Zero-initialise the buffer before populating it, mirroring the fix applied to bme280_trigger_handler() in commit 018f50909e66 ("iio: bmp280: zero-init buffer"). Fixes: 872c8014e05e ("iio: pressure: bmp280: drop sensor_data array") Cc: stable@vger.kernel.org Signed-off-by: David Carlier 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 9b489766e457a..0ebe0b682caae 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1910,7 +1910,7 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) u32 comp_press; s32 comp_temp; aligned_s64 timestamp; - } buffer; + } buffer = { }; int ret; guard(mutex)(&data->lock); From b3f1af4ba8e9cf33aa08c4cdcaa5a17b140521ec Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:48 +0530 Subject: [PATCH 0195/3297] iio: pressure: mpl115: fix runtime PM leak on read error commit fbe67ff37a6fd855a6c097f84f3738bd13d0a898 upstream. mpl115_read_raw() takes a runtime PM reference with pm_runtime_get_sync() before reading the processed pressure or raw temperature, but on the read error path it returns without calling pm_runtime_put_autosuspend(). Each failed read therefore leaks a runtime PM reference and prevents the device from autosuspending. Drop the reference before checking the return value so both the success and error paths are balanced. Fixes: 0c3a333524a3 ("iio: pressure: mpl115: Implementing low power mode by shutdown gpio") Signed-off-by: Biren Pandya Assisted-by: Claude:claude-opus-4-8 coccinelle Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/pressure/mpl115.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 830a5065c008f..16e112b796ba9 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -106,18 +106,18 @@ static int mpl115_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: pm_runtime_get_sync(data->dev); ret = mpl115_comp_pressure(data, val, val2); + pm_runtime_put_autosuspend(data->dev); if (ret < 0) return ret; - pm_runtime_put_autosuspend(data->dev); return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_RAW: pm_runtime_get_sync(data->dev); /* temperature -5.35 C / LSB, 472 LSB is 25 C */ ret = mpl115_read_temp(data); + pm_runtime_put_autosuspend(data->dev); if (ret < 0) return ret; - pm_runtime_put_autosuspend(data->dev); *val = ret >> 6; return IIO_VAL_INT; From c6ca87c7bbb3f6d45413d35da0446a080269ca36 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 19:37:10 +0500 Subject: [PATCH 0196/3297] iio: proximity: vl53l0x: notify trigger and clear IRQ on error paths commit be843b0579f872ec7590d825e2c9a656d4790c4b upstream. vl53l0x_trigger_handler() returns directly on the I2C read failure paths without calling iio_trigger_notify_done() or vl53l0x_clear_irq(). A single transient i2c_smbus_read_i2c_block_data() failure (negative errno or a short read) therefore leaves two pieces of state behind: - iio_trigger_notify_done() never decrements the trigger's use_count, so iio_trigger_poll_nested() silently drops further dispatches (see industrialio-trigger.c, the !atomic_read(&trig->use_count) guard); - vl53l0x_clear_irq() never writes SYSTEM_INTERRUPT_CLEAR, so the chip keeps the DRDY interrupt asserted. The sensor's buffer mode stays wedged from then on, recoverable only by re-binding the driver. The sibling driver vl53l1x-i2c.c handles exactly the same case correctly by jumping to a "notify_and_clear_irq" label that always calls both helpers; mirror that here. The bogus negative-int return value cast to irqreturn_t also goes away as a side effect. Fixes: 762186c6e7b1 ("iio: proximity: vl53l0x-i2c: Added continuous mode support") Signed-off-by: Stepan Ionichev Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/proximity/vl53l0x-i2c.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index ad3e46d47fa8d..6c6e6dab045f7 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -87,15 +87,14 @@ static irqreturn_t vl53l0x_trigger_handler(int irq, void *priv) ret = i2c_smbus_read_i2c_block_data(data->client, VL_REG_RESULT_RANGE_STATUS, sizeof(buffer), buffer); - if (ret < 0) - return ret; - else if (ret != 12) - return -EREMOTEIO; + if (ret != 12) + goto done; scan.chan = get_unaligned_be16(&buffer[10]); iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), iio_get_time_ns(indio_dev)); +done: iio_trigger_notify_done(indio_dev->trig); vl53l0x_clear_irq(data); From 7d4d60f7c0541862f64df79d09cc978c4ec78049 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Fri, 15 May 2026 18:31:38 +0500 Subject: [PATCH 0197/3297] iio: resolver: ad2s1210: notify trigger and clear state on fault read error commit 70247658d0e783c93b48fcbc3b81d99e992ff478 upstream. ad2s1210_trigger_handler() walks several scan-mask branches and uses "goto error_ret" to land on the iio_trigger_notify_done() teardown at the bottom of the function for every I/O error -- except the MOD_CONFIG fault-register read, which uses a bare "return ret": if (st->fixed_mode == MOD_CONFIG) { unsigned int reg_val; ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) return ret; ... } Two problems on that path: - the handler returns a negative errno where the prototype expects an irqreturn_t (IRQ_HANDLED / IRQ_NONE), so the caller in the IIO core sees a value outside the enum; - iio_trigger_notify_done() is skipped, leaving the trigger busy-flag set. A single transient SPI/regmap error on the fault read then wedges the trigger so subsequent samples are dropped until the consumer is detached. Convert the error path to "goto error_ret" so the failure path goes through the same notify_done() teardown as every other error in the handler. Fixes: f9b9ff95be8c ("iio: resolver: ad2s1210: add support for adi,fixed-mode") Signed-off-by: Stepan Ionichev Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/resolver/ad2s1210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 9b028c8bb1db1..ca8f04c9fd426 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -1334,7 +1334,7 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - return ret; + goto error_ret; st->sample.fault = reg_val; } From b50344ab202f353231898fde9127aafc4f634588 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:42:59 +0800 Subject: [PATCH 0198/3297] iio: temperature: Build mlx90635 with CONFIG_MLX90635 commit 63a76e3a587c4143e8e24e8a6b0c232fa0676034 upstream. drivers/iio/temperature/Kconfig has a dedicated MLX90635 option, but the Makefile currently builds mlx90635.o under CONFIG_MLX90632. This means enabling CONFIG_MLX90635 alone does not carry its provider object into the build, while enabling CONFIG_MLX90632 unexpectedly also builds mlx90635.o. Gate mlx90635.o on the matching generated Kconfig symbol. Fixes: a1d1ba5e1c28 ("iio: temperature: mlx90635 MLX90635 IR Temperature sensor") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Andy Shevchenko Acked-by: Crt Mori Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 07d6e65709f7f..0850bf6918204 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MAX31865) += max31865.o obj-$(CONFIG_MCP9600) += mcp9600.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o -obj-$(CONFIG_MLX90632) += mlx90635.o +obj-$(CONFIG_MLX90635) += mlx90635.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TMP007) += tmp007.o obj-$(CONFIG_TMP117) += tmp117.o From e16258913be6a15e542410e894ab5976d64ef2ea Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:28 +0300 Subject: [PATCH 0199/3297] iio: temperature: ltc2983: Fix n_wires default bypassing rotation check commit 434c150752675f44dc52c384a7fa22e5176bc35a upstream. When adi,number-of-wires is absent, n_wires is left at 0. The binding documents a default of 2 wires, matching the hardware default. However the current-rotate validation checks n_wires == 2 || n_wires == 3, so with n_wires = 0 the guard is bypassed and adi,current-rotate is accepted for a 2-wire RTD. Initialize n_wires = 2 to match the binding default and ensure the rotation check fires correctly when the property is absent. Fixes: f110f3188e56 ("iio: temperature: Add support for LTC2983") Signed-off-by: Liviu Stan Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/ltc2983.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 7dd40d69cce68..a210a60f57f4a 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -741,7 +741,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, struct ltc2983_rtd *rtd; int ret = 0; struct device *dev = &st->spi->dev; - u32 excitation_current = 0, n_wires = 0; + u32 excitation_current = 0, n_wires = 2; rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); if (!rtd) From 62a0d75bedd4bce3c55b45fa360d4a5a2e8f2edd Mon Sep 17 00:00:00 2001 From: Liviu Stan Date: Mon, 25 May 2026 19:39:29 +0300 Subject: [PATCH 0200/3297] iio: temperature: ltc2983: Fix reinit_completion() called after conversion start commit 5cb9fdb446bfc3ae0524496f53fb68e67051701b upstream. reinit_completion() was called after regmap_write() initiated the hardware conversion, creating a race window where the interrupt could fire and call complete() before reinit_completion() reset the completion. Move reinit_completion() before the regmap_write() to close the race. ltc2983_eeprom_cmd() already does it in the correct order. Fixes: f110f3188e56 ("iio: temperature: Add support for LTC2983") Signed-off-by: Liviu Stan Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/ltc2983.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index a210a60f57f4a..cfdd1d6039f15 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1177,12 +1177,11 @@ static int ltc2983_chan_read(struct ltc2983_data *st, start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan); dev_dbg(&st->spi->dev, "Start conversion on chan:%d, status:%02X\n", sensor->chan, start_conversion); + reinit_completion(&st->completion); /* start conversion */ ret = regmap_write(st->regmap, LTC2983_STATUS_REG, start_conversion); if (ret) return ret; - - reinit_completion(&st->completion); /* * wait for conversion to complete. * 300 ms should be more than enough to complete the conversion. From a4f8491da9563ba6eb77969ac26fc7052114c476 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 17 May 2026 23:26:13 +0500 Subject: [PATCH 0201/3297] iio: temperature: tmp006: use devm_iio_trigger_register commit 3c5eed894efd93d68d7f6a359a81ddef0e928774 upstream. tmp006_probe() allocates the DRDY trigger with devm_iio_trigger_alloc() but registers it with plain iio_trigger_register(). The driver has no .remove() callback, so on module unload the trigger stays in the global trigger list while its memory is freed by devm, leaving a dangling entry. Switch to devm_iio_trigger_register() so the registration is undone in the same devm scope as the allocation. Fixes: 91f75ccf9f03 ("iio: temperature: tmp006: add triggered buffer support") Cc: stable@vger.kernel.org Signed-off-by: Stepan Ionichev Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/tmp006.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index 10bd3f221929d..cf59dda63f8b6 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -350,7 +350,7 @@ static int tmp006_probe(struct i2c_client *client) data->drdy_trig->ops = &tmp006_trigger_ops; iio_trigger_set_drvdata(data->drdy_trig, indio_dev); - ret = iio_trigger_register(data->drdy_trig); + ret = devm_iio_trigger_register(&client->dev, data->drdy_trig); if (ret) return ret; From c071df05bcda0e47aac581d4b564b2bebcb1ff60 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Wed, 1 Jul 2026 18:52:31 +0900 Subject: [PATCH 0202/3297] ALSA: usx2y: us144mkii: fix work UAF on disconnect commit 147996e7e7c9e8339c0e04f6fa7ccb3e4d448ff7 upstream. tascam_disconnect() cancels capture_work and midi_in_work before usb_kill_anchored_urbs() kills the capture/MIDI-in URBs. Those URBs self-resubmit, and their completion handlers reschedule the work. A URB that completes in the small window between cancel_work_sync() and usb_kill_anchored_urbs() therefore re-arms the work after its only cancel. Nothing cancels it again before snd_card_free() frees the card-private tascam structure, so the work handler then runs on freed memory. Kill the anchored URBs before cancelling the work; once the work is cancelled no remaining URB can complete to re-arm it. Fixes: c1bb0c13e430 ("ALSA: usb-audio: us144mkii: Implement audio capture and decoding") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260701095231.1020811-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/usx2y/us144mkii.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c index 4854144a72bf4..ecfb07186e73d 100644 --- a/sound/usb/usx2y/us144mkii.c +++ b/sound/usb/usx2y/us144mkii.c @@ -585,19 +585,24 @@ static void tascam_disconnect(struct usb_interface *intf) return; if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - /* Ensure all deferred work is complete before freeing resources */ snd_card_disconnect(tascam->card); - cancel_work_sync(&tascam->stop_work); - cancel_work_sync(&tascam->capture_work); - cancel_work_sync(&tascam->midi_in_work); - cancel_work_sync(&tascam->midi_out_work); - cancel_work_sync(&tascam->stop_pcm_work); + /* + * Kill the URBs before cancelling the work, so a late URB + * completion cannot re-arm a work that then runs after + * snd_card_free(). + */ usb_kill_anchored_urbs(&tascam->playback_anchor); usb_kill_anchored_urbs(&tascam->capture_anchor); usb_kill_anchored_urbs(&tascam->feedback_anchor); usb_kill_anchored_urbs(&tascam->midi_in_anchor); usb_kill_anchored_urbs(&tascam->midi_out_anchor); + + cancel_work_sync(&tascam->stop_work); + cancel_work_sync(&tascam->capture_work); + cancel_work_sync(&tascam->midi_in_work); + cancel_work_sync(&tascam->midi_out_work); + cancel_work_sync(&tascam->stop_pcm_work); timer_delete_sync(&tascam->error_timer); tascam_free_urbs(tascam); snd_card_free(tascam->card); From df0fe53a7104bc61e47775804bec289f29bcc277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 15 May 2026 10:32:25 -0300 Subject: [PATCH 0203/3297] ALSA: virtio: Add missing 384 kHz PCM rate mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 83fbbcb7935ec6d2c8ba3bc133e8a0ead2ab0b2d upstream. The VirtIO sound UAPI defines VIRTIO_SND_PCM_RATE_384000, and ALSA has SNDRV_PCM_RATE_384000. However, virtio-snd's rate conversion tables stop at 192 kHz. A device advertising only 384 kHz is rejected as having no supported PCM frame rates. A device advertising 384 kHz together with lower rates does not expose 384 kHz through the ALSA hardware constraints. The selected ALSA rate also needs a reverse mapping for SET_PARAMS. Add the missing 384 kHz entries to both conversion tables. Fixes: 29b96bf50ba9 ("ALSA: virtio: build PCM devices and substream hardware descriptors") Fixes: da76e9f3e43a ("ALSA: virtio: PCM substream operators") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260515-alsa-virtio-384k-rate-v1-1-35ecb5df835c@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/virtio/virtio_pcm.c | 3 ++- sound/virtio/virtio_pcm_ops.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/virtio/virtio_pcm.c b/sound/virtio/virtio_pcm.c index 3602b6690fcd9..67b8020d2d5a7 100644 --- a/sound/virtio/virtio_pcm.c +++ b/sound/virtio/virtio_pcm.c @@ -77,7 +77,8 @@ static const struct virtsnd_v2a_rate g_v2a_rate_map[] = { [VIRTIO_SND_PCM_RATE_88200] = { SNDRV_PCM_RATE_88200, 88200 }, [VIRTIO_SND_PCM_RATE_96000] = { SNDRV_PCM_RATE_96000, 96000 }, [VIRTIO_SND_PCM_RATE_176400] = { SNDRV_PCM_RATE_176400, 176400 }, - [VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 } + [VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 }, + [VIRTIO_SND_PCM_RATE_384000] = { SNDRV_PCM_RATE_384000, 384000 } }; /** diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c index 6297a9c61e70b..1105e7ff3523a 100644 --- a/sound/virtio/virtio_pcm_ops.c +++ b/sound/virtio/virtio_pcm_ops.c @@ -90,7 +90,8 @@ static const struct virtsnd_a2v_rate g_a2v_rate_map[] = { { 88200, VIRTIO_SND_PCM_RATE_88200 }, { 96000, VIRTIO_SND_PCM_RATE_96000 }, { 176400, VIRTIO_SND_PCM_RATE_176400 }, - { 192000, VIRTIO_SND_PCM_RATE_192000 } + { 192000, VIRTIO_SND_PCM_RATE_192000 }, + { 384000, VIRTIO_SND_PCM_RATE_384000 } }; static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream); From 5da9742de22db0dbaa8d414214ab5e1bedde00f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 7 May 2026 11:28:30 -0300 Subject: [PATCH 0204/3297] ALSA: virtio: Validate control metadata from the device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c77a6cbb36ff8cbc1f084d94f8dcda5250935271 upstream. virtio-snd control handling trusts the device-provided control type and value count returned by the device. That metadata is then used directly to index g_v2a_type_map[] in virtsnd_kctl_info(), and to size loops and memcpy() operations in virtsnd_kctl_get() and virtsnd_kctl_put() against fixed-size virtio_snd_ctl_value and snd_ctl_elem_value arrays. A buggy or malicious device can therefore trigger out-of-bounds access by advertising an invalid control type or an oversized value count. Validate control type and count once in virtsnd_kctl_parse_cfg(), before querying enumerated items or exposing the control to ALSA. Fixes: d6568e3de42d ("ALSA: virtio: add support for audio controls") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260507-alsa-virtio-validate-kctl-info-v1-1-7404fb12ec37@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/virtio/virtio_kctl.c | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/sound/virtio/virtio_kctl.c b/sound/virtio/virtio_kctl.c index ffb903d56297a..45f7b6a5b3080 100644 --- a/sound/virtio/virtio_kctl.c +++ b/sound/virtio/virtio_kctl.c @@ -18,6 +18,21 @@ static const snd_ctl_elem_type_t g_v2a_type_map[] = { [VIRTIO_SND_CTL_TYPE_IEC958] = SNDRV_CTL_ELEM_TYPE_IEC958 }; +/* Map for converting VirtIO types to maximum value counts. */ +static const unsigned int g_v2a_count_map[] = { + [VIRTIO_SND_CTL_TYPE_BOOLEAN] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer), + [VIRTIO_SND_CTL_TYPE_INTEGER] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer), + [VIRTIO_SND_CTL_TYPE_INTEGER64] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer64), + [VIRTIO_SND_CTL_TYPE_ENUMERATED] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.enumerated), + [VIRTIO_SND_CTL_TYPE_BYTES] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.bytes), + [VIRTIO_SND_CTL_TYPE_IEC958] = 1 +}; + /* Map for converting VirtIO access rights to ALSA access rights. */ static const unsigned int g_v2a_access_map[] = { [VIRTIO_SND_CTL_ACCESS_READ] = SNDRV_CTL_ELEM_ACCESS_READ, @@ -36,6 +51,37 @@ static const unsigned int g_v2a_mask_map[] = { [VIRTIO_SND_CTL_EVT_MASK_TLV] = SNDRV_CTL_EVENT_MASK_TLV }; +static int virtsnd_kctl_validate_info(struct virtio_snd *snd, u32 cid, + struct virtio_snd_ctl_info *kinfo) +{ + struct virtio_device *vdev = snd->vdev; + unsigned int type = le32_to_cpu(kinfo->type); + unsigned int count = le32_to_cpu(kinfo->count); + + if (type >= ARRAY_SIZE(g_v2a_type_map)) { + dev_err(&vdev->dev, "control #%u: unknown type %u\n", + cid, type); + return -EINVAL; + } + + if (count > g_v2a_count_map[type] || + (type == VIRTIO_SND_CTL_TYPE_IEC958 && count != 1)) { + dev_err(&vdev->dev, "control #%u: invalid count %u for type %u\n", + cid, count, type); + return -EINVAL; + } + + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED && + !le32_to_cpu(kinfo->value.enumerated.items)) { + dev_err(&vdev->dev, + "control #%u: no items for enumerated control\n", + cid); + return -EINVAL; + } + + return 0; +} + /** * virtsnd_kctl_info() - Returns information about the control. * @kcontrol: ALSA control element. @@ -385,6 +431,10 @@ int virtsnd_kctl_parse_cfg(struct virtio_snd *snd) struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[i]; unsigned int type = le32_to_cpu(kinfo->type); + rc = virtsnd_kctl_validate_info(snd, i, kinfo); + if (rc) + return rc; + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED) { rc = virtsnd_kctl_get_enum_items(snd, i); if (rc) From f6538a318947b627710b08a268bc80a48c23bde7 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:12 +0800 Subject: [PATCH 0205/3297] ALSA: ymfpci: check snd_ctl_new1() return value commit e64d170346d00b580c0043de3e5ccb3e331c47d4 upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_ymfpci_create_spdif_controls() does not check the return value before dereferencing kctl->id.device, which can lead to a NULL pointer dereference. Add NULL checks after snd_ctl_new1() calls and return -ENOMEM if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: c9b83ae4a160 ("ALSA: ymfpci: Fix kctl->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_4745C5DC2333325C0EDAB1EFC88A136E6809@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ymfpci/ymfpci_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index eb373d9395e35..044b5ce63fa13 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1781,16 +1781,22 @@ int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch) if (snd_BUG_ON(!chip->pcm_spdif)) return -ENXIO; kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip); + if (!kctl) + return -ENOMEM; kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip); + if (!kctl) + return -ENOMEM; kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip); + if (!kctl) + return -ENOMEM; kctl->id.device = chip->pcm_spdif->device; err = snd_ctl_add(chip->card, kctl); if (err < 0) From fd786466889e4a6e6de0f4462bd0068edea63960 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:14 +0800 Subject: [PATCH 0206/3297] ALSA: aoa: check snd_ctl_new1() return value commit 8df560fefe6fed6a20b7e06720eeaeccec349ac0 upstream. snd_ctl_new1() can return NULL when memory allocation fails. In layout.c, the function does not check the return value before dereferencing ctl->id.name or passing to aoa_snd_ctl_add(), which can lead to a NULL pointer dereference. Add NULL checks after snd_ctl_new1() calls and return early if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: f3d9478b2ce4 ("[ALSA] snd-aoa: add snd-aoa") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_35F3A25FEEBF190A2E15ED787754C57E3708@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/aoa/fabrics/layout.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index bb2a0ef3004bf..5cb6f5f4d83fc 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -948,6 +948,8 @@ static void layout_attached_codec(struct aoa_codec *codec) if (lineout == 1) ldev->gpio.methods->set_lineout(codec->gpio, 1); ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); + if (!ctl) + return; if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strscpy(ctl->id.name, "Headphone Switch"); ldev->lineout_ctrl = ctl; @@ -961,12 +963,16 @@ static void layout_attached_codec(struct aoa_codec *codec) if (ldev->have_lineout_detect) { ctl = snd_ctl_new1(&lineout_detect_choice, ldev); + if (!ctl) + return; if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strscpy(ctl->id.name, "Headphone Detect Autoswitch"); aoa_snd_ctl_add(ctl); ctl = snd_ctl_new1(&lineout_detected, ldev); + if (!ctl) + return; if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strscpy(ctl->id.name, "Headphone Detected"); From a5fd3122283bf75c04f6414bf610100beb0565b0 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 14:03:15 +0800 Subject: [PATCH 0207/3297] ALSA: caiaq: fix out-of-bounds read in the Traktor Kontrol S4 input parser commit f7f3f9fd81e7adbaa12c2e62ee07f0e094a543fd upstream. snd_usb_caiaq_tks4_dispatch() decodes the Traktor Kontrol S4 input stream in fixed 16-byte (TKS4_MSGBLOCK_SIZE) message blocks. On every iteration it advances buf and subtracts the block size while looping on "while (len)". len is urb->actual_length. That value is supplied by the device and is not guaranteed to be a multiple of 16. When a final short block leaves len between 1 and 15, the loop runs once more, reads up to buf[15], and then does "len -= TKS4_MSGBLOCK_SIZE". As len is unsigned this underflows to a huge value. The loop then keeps iterating and walking buf far past the end of the 512-byte ep4_in_buf, reading out of bounds until a bogus block id happens to be hit. Iterate only while a full message block is available. This stops the unsigned underflow and silently drops any trailing partial block, which carries no complete control value anyway. The sibling endpoint-4 parsers are not affected. The Traktor Kontrol X1 and Maschine arms in snd_usb_caiaq_ep4_reply_dispatch() floor urb->actual_length before dispatching. Fixes: 15c5ab607045 ("ALSA: snd-usb-caiaq: Add support for Traktor Kontrol S4") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178176259547.3343534.2724779296835237429@maoyixie.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/caiaq/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 5c70fdf61cc13..2db4d1332df1c 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -330,7 +330,7 @@ static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *cdev, { struct device *dev = caiaqdev_to_dev(cdev); - while (len) { + while (len >= TKS4_MSGBLOCK_SIZE) { unsigned int i, block_id = (buf[0] << 8) | buf[1]; switch (block_id) { From af2b009b773bc42995546507963e5e78970dc3ed Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:13 +0800 Subject: [PATCH 0208/3297] ALSA: cmipci: check snd_ctl_new1() return value commit c205bd1b28fb7e5f1061a4e78813fad7d315cb3e upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_cmipci_spdif_controls() does not check the return value before dereferencing kctl->id.device, which can lead to a NULL pointer dereference. Add NULL checks after snd_ctl_new1() calls and return -ENOMEM if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: f2f312ad88c6 ("ALSA: cmipci: Fix kctl->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_964433DCD132125D5EDA79EE068A2D6EFA09@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/cmipci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 0666be543474a..f7b4677ab6b82 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2637,16 +2637,22 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) } if (cm->can_ac3_hw) { kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm); + if (!kctl) + return -ENOMEM; kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm); + if (!kctl) + return -ENOMEM; kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm); + if (!kctl) + return -ENOMEM; kctl->id.device = pcm_spdif_device; err = snd_ctl_add(card, kctl); if (err < 0) From b27a75d42044d9d4709095617730b91b1c4af423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 15 Jun 2026 10:37:26 -0300 Subject: [PATCH 0209/3297] ALSA: compress: Fix task creation error unwind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4a60127debb9e370d6c0e22a307326b624a141f3 upstream. snd_compr_task_new() allocates the driver task before validating the returned DMA buffers and reserving file descriptors. When either of those later steps fails, the core frees its task wrapper and DMA-buffer references without calling the driver's task_free() callback. Any driver resources allocated by task_create() are therefore leaked. The dual-fd allocation path also jumps to cleanup without storing the negative get_unused_fd_flags() result in retval. Since retval still contains the successful task_create() return value, TASK_CREATE can incorrectly report success although the task was discarded. Preserve the fd allocation errors and call task_free() when failure occurs after a successful task_create() callback. Fixes: 04177158cf98 ("ALSA: compress_offload: introduce accel operation mode") Fixes: 3d3f43fab4cf ("ALSA: compress_offload: improve file descriptors installation for dma-buf") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260615-alsa-compress-task-unwind-v1-1-39e8ad3ddb27@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/compress_offload.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 2181442e3a1da..065cdfaef1eaa 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1070,15 +1070,18 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ file descriptors are allocated before fd_install() */ if (!task->input || !task->input->file || !task->output || !task->output->file) { retval = -EINVAL; - goto cleanup; + goto free_driver_task; } fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC); - if (fd_i < 0) - goto cleanup; + if (fd_i < 0) { + retval = fd_i; + goto free_driver_task; + } fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC); if (fd_o < 0) { + retval = fd_o; put_unused_fd(fd_i); - goto cleanup; + goto free_driver_task; } /* keep dmabuf reference until freed with task free ioctl */ get_dma_buf(task->input); @@ -1090,6 +1093,8 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ list_add_tail(&task->list, &stream->runtime->tasks); stream->runtime->total_tasks++; return 0; +free_driver_task: + stream->ops->task_free(stream, task); cleanup: snd_compr_task_free(task); return retval; From 9e53e99b6fa3cd82992d963cbff58dbbd1df8651 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:09 +0800 Subject: [PATCH 0210/3297] ALSA: es1938: check snd_ctl_new1() return value commit 1edd1f02dddd20aeb6066ded41017615766ea42f upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_es1938_mixer() does not check the return value before dereferencing the pointer, which can lead to a NULL pointer dereference. Add a NULL check after snd_ctl_new1() and return -ENOMEM if it fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_E0DC65165FDF2C8982BAFB6794B854B53B0A@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/es1938.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 280125eff3624..b6dcb721fffce 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1655,6 +1655,8 @@ static int snd_es1938_mixer(struct es1938 *chip) for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { struct snd_kcontrol *kctl; kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip); + if (!kctl) + return -ENOMEM; switch (idx) { case 0: chip->master_volume = kctl; From db25755e7629da9093f248f6a43a384ec1009b3e Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Tue, 23 Jun 2026 02:59:04 +0930 Subject: [PATCH 0211/3297] ALSA: FCP: Add Focusrite ISA C8X support commit e0ecb324246be9cf3a0689346a658e48a38546b2 upstream. Add USB PID 0x821e to the list of devices handled by the Focusrite Control Protocol (FCP) driver. Cc: stable@vger.kernel.org Signed-off-by: Geoffrey D. Bennett Link: https://patch.msgid.link/ajlw4HK+2RSW3nUl@m.b4.vu Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 7126a2cf9e79b..dd56f4bfb84d2 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -4346,6 +4346,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x821b): /* Focusrite Scarlett 16i16 4th Gen */ case USB_ID(0x1235, 0x821c): /* Focusrite Scarlett 18i16 4th Gen */ case USB_ID(0x1235, 0x821d): /* Focusrite Scarlett 18i20 4th Gen */ + case USB_ID(0x1235, 0x821e): /* Focusrite ISA C8X */ err = snd_fcp_init(mixer); break; From 8e48a29813df8dd71503800b7acf69c12c035045 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sun, 21 Jun 2026 23:09:07 +0800 Subject: [PATCH 0212/3297] ALSA: firewire: isight: bound the sample count to the packet payload commit 29b9667982e4df2ed7744f86b1144f8bb58eb698 upstream. isight_packet() takes the frame count from the device iso packet and checks it only against the device claimed iso length. count = be32_to_cpu(payload->sample_count); if (likely(count <= (length - 16) / 4)) isight_samples(isight, payload->samples, count); length is the iso header data_length. It can be up to 0xffff. So the gate allows a count up to about 16379. isight_samples() then copies count frames out of payload->samples into the PCM DMA buffer. payload->samples holds only 2 * MAX_FRAMES_PER_PACKET values. The device multiplexes two samples per frame. A count past MAX_FRAMES_PER_PACKET reads past the payload. A count past the buffer size writes past runtime->dma_area. The smallest PCM buffer is larger than MAX_FRAMES_PER_PACKET. Bounding the count to MAX_FRAMES_PER_PACKET keeps both the read and the write in range. A malicious or faulty Apple iSight on the FireWire bus reaches this during a normal capture. Add the MAX_FRAMES_PER_PACKET bound to the gate. Fixes: 3a691b28a0ca ("ALSA: add Apple iSight microphone driver") Suggested-by: Takashi Sakamoto Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178205454729.1900991.7807310178296762772@maoyixie.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/firewire/isight.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 2b7f071d593b9..33c9dd48b3b0b 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -179,7 +179,8 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, if (likely(length >= 16 && payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) { count = be32_to_cpu(payload->sample_count); - if (likely(count <= (length - 16) / 4)) { + if (likely(count <= (length - 16) / 4 && + count <= MAX_FRAMES_PER_PACKET)) { total = be32_to_cpu(payload->sample_total); if (unlikely(total != isight->total_samples)) { if (!isight->first_packet) From 5e74e5e8cb7cc25f7a89f59abaf3489bf0c6f4a0 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:10 +0800 Subject: [PATCH 0213/3297] ALSA: gus: check snd_ctl_new1() return value commit c7fa99d30c7a166a5e5db5a585ce7501ff68326b upstream. snd_ctl_new1() can return NULL when memory allocation fails. snd_gf1_pcm_volume_control() does not check the return value before dereferencing kctl->id.index, which can lead to a NULL pointer dereference. Add a NULL check after snd_ctl_new1() and return -ENOMEM if it fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: c5ae57b1bb99 ("ALSA: gus: Fix kctl->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_F644A3DCAD32945D62DB2FEEBE8A996F6809@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/isa/gus/gus_pcm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 9249cbff30f37..b724421ba5eeb 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -849,6 +849,8 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus); else kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus); + if (!kctl) + return -ENOMEM; kctl->id.index = control_index; err = snd_ctl_add(card, kctl); if (err < 0) From ce0a903d0591e3e2c790c5b628802b08d1b287cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 11 May 2026 01:29:34 -0300 Subject: [PATCH 0214/3297] ALSA: hda/cs35l41: Fix firmware load work teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b65020d5398f499c09498c9786dba6d67ae57664 upstream. cs35l41_hda creates ALSA controls whose private data points at the cs35l41_hda object. The firmware load control can also queue fw_load_work. Those controls are not removed on component unbind, and device remove only cancels fw_load_work through cs35l41_remove_dsp(). That helper is skipped when halo_initialized is false. With firmware_autostart disabled, a firmware load can be requested before the DSP has been initialized. If the component or device is removed before the queued work runs, the worker can run after teardown and dereference driver state that is no longer valid. Track the created controls and remove them on unbind so no new control callback can reach the driver data or queue more work. Then cancel fw_load_work to drain any request that was already queued. Also cancel the work unconditionally during device remove before runtime PM teardown. Fixes: 47ceabd99a28 ("ALSA: hda: cs35l41: Support Firmware switching and reloading") Fixes: 4c870513fbb0 ("ALSA: hda: cs35l41: Add read-only ALSA control for forced mute") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Reviewed-by: Stefan Binding Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260511-alsa-hda-cs35l41-fw-work-teardown-v1-1-1184e9bc4f25@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 77 +++++++++++++++------- sound/hda/codecs/side-codecs/cs35l41_hda.h | 5 ++ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 47263c5a021cb..438a19bd979bf 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1328,6 +1328,43 @@ static int cs35l41_fw_type_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ct return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(cs35l41_hda_fw_ids), cs35l41_hda_fw_ids); } +static void cs35l41_remove_controls(struct cs35l41_hda *cs35l41) +{ + if (!cs35l41->codec) + return; + + snd_ctl_remove(cs35l41->codec->card, cs35l41->mute_override_ctl); + cs35l41->mute_override_ctl = NULL; + + snd_ctl_remove(cs35l41->codec->card, cs35l41->fw_load_ctl); + cs35l41->fw_load_ctl = NULL; + + snd_ctl_remove(cs35l41->codec->card, cs35l41->fw_type_ctl); + cs35l41->fw_type_ctl = NULL; +} + +static int cs35l41_add_control(struct cs35l41_hda *cs35l41, + struct snd_kcontrol_new *ctl, + struct snd_kcontrol **kctl) +{ + int ret; + + *kctl = snd_ctl_new1(ctl, cs35l41); + if (!*kctl) + return -ENOMEM; + + ret = snd_ctl_add(cs35l41->codec->card, *kctl); + if (ret) { + dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", ctl->name, ret); + *kctl = NULL; + return ret; + } + + dev_dbg(cs35l41->dev, "Added Control %s\n", ctl->name); + + return 0; +} + static int cs35l41_create_controls(struct cs35l41_hda *cs35l41) { char fw_type_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; @@ -1363,32 +1400,23 @@ static int cs35l41_create_controls(struct cs35l41_hda *cs35l41) scnprintf(mute_override_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s Forced Mute Status", cs35l41->amp_name); - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_type_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_type_ctl.name, ret); - return ret; - } - - dev_dbg(cs35l41->dev, "Added Control %s\n", fw_type_ctl.name); - - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_load_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_load_ctl.name, ret); - return ret; - } - - dev_dbg(cs35l41->dev, "Added Control %s\n", fw_load_ctl.name); + ret = cs35l41_add_control(cs35l41, &fw_type_ctl, &cs35l41->fw_type_ctl); + if (ret) + goto err; - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&mute_override_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", mute_override_ctl.name, - ret); - return ret; - } + ret = cs35l41_add_control(cs35l41, &fw_load_ctl, &cs35l41->fw_load_ctl); + if (ret) + goto err; - dev_dbg(cs35l41->dev, "Added Control %s\n", mute_override_ctl.name); + ret = cs35l41_add_control(cs35l41, &mute_override_ctl, &cs35l41->mute_override_ctl); + if (ret) + goto err; return 0; + +err: + cs35l41_remove_controls(cs35l41); + return ret; } static bool cs35l41_dsm_supported(acpi_handle handle, unsigned int commands) @@ -1527,6 +1555,10 @@ static void cs35l41_hda_unbind(struct device *dev, struct device *master, void * device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev); unlock_system_sleep(sleep_flags); memset(comp, 0, sizeof(*comp)); + + cs35l41_remove_controls(cs35l41); + cancel_work_sync(&cs35l41->fw_load_work); + cs35l41->codec = NULL; } } @@ -2065,6 +2097,7 @@ void cs35l41_hda_remove(struct device *dev) struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); component_del(cs35l41->dev, &cs35l41_hda_comp_ops); + cancel_work_sync(&cs35l41->fw_load_work); pm_runtime_get_sync(cs35l41->dev); pm_runtime_dont_use_autosuspend(cs35l41->dev); diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.h b/sound/hda/codecs/side-codecs/cs35l41_hda.h index 7d003c598e93e..56ec07c0bb745 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.h +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.h @@ -57,6 +57,8 @@ enum control_bus { SPI }; +struct snd_kcontrol; + struct cs35l41_hda { struct device *dev; struct regmap *regmap; @@ -75,6 +77,9 @@ struct cs35l41_hda { int speaker_id; struct mutex fw_mutex; struct work_struct fw_load_work; + struct snd_kcontrol *fw_type_ctl; + struct snd_kcontrol *fw_load_ctl; + struct snd_kcontrol *mute_override_ctl; struct regmap_irq_chip_data *irq_data; bool firmware_running; From 4dd2552e559bd448c04b7474c69360954398c5cc Mon Sep 17 00:00:00 2001 From: Cameron Graham Date: Fri, 12 Jun 2026 21:46:01 +1200 Subject: [PATCH 0215/3297] ALSA: hda/hdmi: Add force-connect quirk for HP EliteDesk 800 G5 Mini commit 0ec17ee704615125c0b6e100c38129393a346bcc upstream. The HP EliteDesk 800 G5 Mini (PCI subsystem 103c:8595) uses Cannon Lake PCH cAVS HDA with DisplayPort audio pins 0x05 and 0x06 set to AC_JACK_PORT_NONE (N/A) in BIOS defaults, causing hdmi_add_pin() to skip them and the DP audio device to not appear in ALSA. Add the board to the existing force_connect_list alongside the similar HP EliteDesk 800 G4 entries. Signed-off-by: Cameron Graham Cc: Link: https://patch.msgid.link/20260612094601.1209845-1-cam.graham@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/hdmi/hdmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index c2e3adc7b3c00..df3b325d31484 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -1549,6 +1549,7 @@ static const struct snd_pci_quirk force_connect_list[] = { SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1), SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1), SND_PCI_QUIRK(0x103c, 0x845a, "HP EliteDesk 800 G4 DM 65W", 1), + SND_PCI_QUIRK(0x103c, 0x8595, "HP EliteDesk 800 G5 Mini", 1), SND_PCI_QUIRK(0x103c, 0x83f3, "HP ProDesk 400", 1), SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1), SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1), From 1933e6ee136b1dba697755b1be28453a7ae4671f Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 27 May 2026 22:08:41 +0800 Subject: [PATCH 0216/3297] ALSA: hda/hdmi: Use 'AC_PINSENSE_ELDV' to detect pinsense for Loongson commit 958e4450e961d75bd9d8f5bfe245fb15bc78e02a upstream. Due to a hardware defect, for Loongson PCI HDMI devices with a reversion ID of 2, the pin sense status must be determined via the ELD. Add a codec flag, eld_jack_detect, to indicate this case, and do special handlings in read_pin_sense(). Cc: stable@vger.kernel.org Signed-off-by: Baoqi Zhang Signed-off-by: Haowei Zheng Signed-off-by: Huacai Chen Link: https://patch.msgid.link/20260527140841.3407183-1-chenhuacai@loongson.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- include/sound/hda_codec.h | 1 + sound/hda/codecs/hdmi/hdmi.c | 8 +++++++- sound/hda/common/jack.c | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5d9f0ef228af1..3c9e64dc02524 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -259,6 +259,7 @@ struct hda_codec { unsigned int forced_resume:1; /* forced resume for jack */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ unsigned int ctl_dev_id:1; /* old control element id build behaviour */ + unsigned int eld_jack_detect:1; /* Machine jack-detection by ELD */ unsigned long power_on_acct; unsigned long power_off_acct; diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index df3b325d31484..1036d08d1431a 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -2286,6 +2286,7 @@ EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_init, "SND_HDA_CODEC_HDMI"); enum { MODEL_GENERIC, MODEL_GF, + MODEL_LOONGSON, }; static int generichdmi_probe(struct hda_codec *codec, @@ -2303,6 +2304,11 @@ static int generichdmi_probe(struct hda_codec *codec, if (id->driver_data == MODEL_GF) codec->no_sticky_stream = 1; + if (id->driver_data == MODEL_LOONGSON) { + if (codec->bus && codec->bus->pci->revision == 0x2) + codec->eld_jack_detect = 1; /* Jack-detection by ELD */ + } + return 0; } @@ -2320,7 +2326,7 @@ static const struct hda_codec_ops generichdmi_codec_ops = { /* */ static const struct hda_device_id snd_hda_id_generichdmi[] = { - HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_LOONGSON), HDA_CODEC_ID_MODEL(0x10951390, "SiI1390 HDMI", MODEL_GENERIC), HDA_CODEC_ID_MODEL(0x10951392, "SiI1392 HDMI", MODEL_GENERIC), HDA_CODEC_ID_MODEL(0x11069f84, "VX11 HDMI/DP", MODEL_GENERIC), diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c index 7d7786df60ea7..f921b10f39c93 100644 --- a/sound/hda/common/jack.c +++ b/sound/hda/common/jack.c @@ -58,6 +58,12 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id) AC_VERB_GET_PIN_SENSE, dev_id); if (codec->inv_jack_detect) val ^= AC_PINSENSE_PRESENCE; + if (codec->eld_jack_detect) { + if (val & AC_PINSENSE_ELDV) + val |= AC_PINSENSE_PRESENCE; + else + val &= ~AC_PINSENSE_PRESENCE; + } return val; } From 04dd2101805758fe9e443f3f0933a12b2000b167 Mon Sep 17 00:00:00 2001 From: Aaron Erhardt Date: Tue, 19 May 2026 17:49:48 +0200 Subject: [PATCH 0217/3297] ALSA: hda/realtek: Fix noisy mic for Clevo V6xxAW commit 493765b8e922a506e09e22e80b6cc9ff05e8295b upstream. Add a PCI quirk to reduce the volume of the internal microphone to prevent extremely noisy signal. Signed-off-by: Aaron Erhardt Signed-off-by: Werner Sembach Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260519155047.106096-1-wse@tuxedocomputers.com 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 c18c18f9e6521..04c2bbe9560f8 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7290,6 +7290,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x6480, "Clevo V6xxAW", ALC245_FIXUP_CLEVO_NOISY_MIC), SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), From 71b87108ad93d433cdb20704a8dc8852304cf2c2 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 27 May 2026 20:09:11 +0800 Subject: [PATCH 0218/3297] ALSA: ice1712: check snd_ctl_new1() return value commit 2b929b91b0f3bc6de8a844370049cd99ee8e31ff upstream. snd_ctl_new1() can return NULL when memory allocation fails. The ice1712 driver calls snd_ctl_new1() without checking the return value before dereferencing the pointer in multiple places (ice1712.c, ice1724.c, aureon.c), which can lead to NULL pointer dereferences. Add NULL checks after snd_ctl_new1() calls and return -ENOMEM if any fails. Assisted-by: Opencode:DeepSeek-V4-Flash Cc: stable@vger.kernel.org Fixes: b9a4efd61b6b ("ALSA: ice1712,ice1724: fix the kcontrol->id initialization") Signed-off-by: Zhao Dongdong Link: https://patch.msgid.link/tencent_42E5E2AB1B6A5101F7EE8C2117F1F687BB07@qq.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ice1712/aureon.c | 2 ++ sound/pci/ice1712/ice1712.c | 8 ++++++++ sound/pci/ice1712/ice1724.c | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index b4c9e7d116095..1b5532ddab8b8 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -1891,6 +1891,8 @@ static int aureon_add_controls(struct snd_ice1712 *ice) for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) { struct snd_kcontrol *kctl; kctl = snd_ctl_new1(&cs8415_controls[i], ice); + if (!kctl) + return -ENOMEM; if (i > 1) kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 1e39b985bef26..4cec56769c0f6 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2346,21 +2346,29 @@ int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice) if (snd_BUG_ON(!ice->pcm_pro)) return -EIO; kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm_pro->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index e2dbbbfbca9fa..7efb0bbcea550 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2379,16 +2379,22 @@ static int snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice) return err; kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) return err; kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice); + if (!kctl) + return -ENOMEM; kctl->id.device = ice->pcm->device; err = snd_ctl_add(ice->card, kctl); if (err < 0) From 651ba82fe2a144bc7356d940bfd235c3810b0549 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Wed, 24 Jun 2026 08:38:40 +0900 Subject: [PATCH 0219/3297] ALSA: seq: Fix uninitialised heap leak in snd_seq_event_dup() commit 435990e25bf1f4af3e6df12a6fbfd1f7ba4a97d4 upstream. snd_seq_event_dup() copies an incoming event into a pool cell and, in the UMP-enabled build, clears the trailing cell->ump.raw.extra word that the memcpy() did not cover. The guard deciding whether to clear it compares the copied size against sizeof(cell->event): memcpy(&cell->ump, event, size); if (size < sizeof(cell->event)) cell->ump.raw.extra = 0; For a legacy (non-UMP) event, size == sizeof(struct snd_seq_event) == sizeof(cell->event), so the condition is false and the extra word keeps stale data. The cell pool is allocated with kvmalloc() (not zeroed) and cells are reused via a free list, so that word holds uninitialised heap or leftover event data. When such a cell is delivered to a UMP client (client->midi_version > 0) that set SNDRV_SEQ_FILTER_NO_CONVERT -- so the legacy event reaches it unconverted -- snd_seq_read() reads it out as the larger struct snd_seq_ump_event and copies the stale word to user space, a 4-byte kernel heap infoleak to an unprivileged /dev/snd/seq client. Compare against sizeof(cell->ump) instead, so the trailing word is zeroed for every event shorter than the UMP cell. Fixes: 46397622a3fa ("ALSA: seq: Add UMP support") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260623233841.853326-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index ccde0ca3d2082..e7edea10027f3 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -364,7 +364,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, size = snd_seq_event_packet_size(event); memcpy(&cell->ump, event, size); #if IS_ENABLED(CONFIG_SND_SEQ_UMP) - if (size < sizeof(cell->event)) + if (size < sizeof(cell->ump)) cell->ump.raw.extra = 0; #endif From 16f14f55141d4c55c3f321f93c328fff7cd6860a Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Sat, 27 Jun 2026 12:29:49 +0800 Subject: [PATCH 0220/3297] ALSA: us144mkii: capture_urb_complete: redundant usb_anchor_urb corrupts anchor list on each resubmission commit 5cff1529a2f9b3461a7f5a6e36a86682fc290534 upstream. In capture_urb_complete(), usb_anchor_urb() is called on every completion callback, but the URB is already anchored from the initial submission in tascam_trigger_start(). Each redundant call corrupts the anchor's doubly-linked list and inflates the URB refcount. When usb_kill_anchored_urbs() traverses the list during stream stop / suspend / disconnect, the corrupted list leads to use-after-free. Remove the redundant usb_anchor_urb() from the resubmit path. Cc: stable@vger.kernel.org Fixes: c1bb0c13e430 ("ALSA: usb-audio: us144mkii: Implement audio capture and decoding") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260627042949.61767-1-vulab@iscas.ac.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/usx2y/us144mkii_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/usx2y/us144mkii_capture.c b/sound/usb/usx2y/us144mkii_capture.c index 00188ff6cd51c..6a45d2b6130ca 100644 --- a/sound/usb/usx2y/us144mkii_capture.c +++ b/sound/usb/usx2y/us144mkii_capture.c @@ -302,7 +302,6 @@ void capture_urb_complete(struct urb *urb) } usb_get_urb(urb); - usb_anchor_urb(urb, &tascam->capture_anchor); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { dev_err_ratelimited(tascam->card->dev, @@ -312,6 +311,7 @@ void capture_urb_complete(struct urb *urb) usb_put_urb(urb); atomic_dec( &tascam->active_urbs); /* Decrement on failed resubmission */ + return; } out: usb_put_urb(urb); From a263eb12cbe2e208e6e637df0f9b0be9a484158e Mon Sep 17 00:00:00 2001 From: Darvell Long Date: Wed, 24 Jun 2026 07:37:23 -0700 Subject: [PATCH 0221/3297] ALSA: usb-audio: avoid kobject path lookup in DualSense match commit 7693c0cc415f3a16a7a3355f245474a5e661be4e upstream. The DualSense jack-detection input handler verifies that a matching input device belongs to the same physical controller by building kobject path strings for both the input device and the USB audio device, then comparing the path prefix. This was observed when a weak physical connection caused the controller to rapidly disconnect and reconnect. During that repeated hotplug, snd_dualsense_ih_match() can run while the controller's USB device is being disconnected. kobject_get_path() walks ancestor kobjects and dereferences their names; if the USB device kobject name is no longer valid, this can fault in strlen(): RIP: 0010:strlen+0x10/0x30 Call Trace: kobject_get_path+0x34/0x150 snd_dualsense_ih_match+0x49/0xd0 [snd_usb_audio] input_register_device+0x566/0x6a0 ps_probe+0xb89/0x1590 [hid_playstation] The same ownership check can be done without building kobject path strings. The input device is parented below the HID device, USB interface and USB device, so walking the input device parent chain and comparing against the mixer USB device preserves the check without dereferencing kobject names during disconnect. Fixes: 79d561c4ec04 ("ALSA: usb-audio: Add mixer quirk for Sony DualSense PS5") Cc: Assisted-by: Cute:gpt-5.5 Signed-off-by: Darvell Long Link: https://patch.msgid.link/20260624143723.2986353-1-contact@darvell.me Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index dd56f4bfb84d2..e56f98615b4ce 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -567,46 +567,30 @@ static bool snd_dualsense_ih_match(struct input_handler *handler, { struct dualsense_mixer_elem_info *mei; struct usb_device *snd_dev; - char *input_dev_path, *usb_dev_path; - size_t usb_dev_path_len; - bool match = false; + struct device *parent; mei = container_of(handler, struct dualsense_mixer_elem_info, ih); snd_dev = mei->info.head.mixer->chip->dev; - input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); - if (!input_dev_path) { - dev_warn(&snd_dev->dev, "Failed to get input dev path\n"); - return false; - } - - usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL); - if (!usb_dev_path) { - dev_warn(&snd_dev->dev, "Failed to get USB dev path\n"); - goto free_paths; - } - /* * Ensure the VID:PID matched input device supposedly owned by the * hid-playstation driver belongs to the actual hardware handled by - * the current USB audio device, which implies input_dev_path being - * a subpath of usb_dev_path. + * the current USB audio device. * * This verification is necessary when there is more than one identical * controller attached to the host system. + * + * The input device is registered below the HID device, USB interface and + * USB device, so compare the parent chain directly instead of building + * kobject path strings. This avoids dereferencing kobject names while the + * USB device hierarchy is being torn down during disconnect. */ - usb_dev_path_len = strlen(usb_dev_path); - if (usb_dev_path_len >= strlen(input_dev_path)) - goto free_paths; - - usb_dev_path[usb_dev_path_len] = '/'; - match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1); - -free_paths: - kfree(input_dev_path); - kfree(usb_dev_path); + for (parent = dev->dev.parent; parent; parent = parent->parent) { + if (parent == &snd_dev->dev) + return true; + } - return match; + return false; } static int snd_dualsense_ih_connect(struct input_handler *handler, From bfd28b07541e510d64b937d28f3459e3e2500da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sun, 19 Apr 2026 17:30:30 -0300 Subject: [PATCH 0222/3297] ALSA: usb-audio: Propagate errors in scarlett_ctl_enum_put() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0f25cf1f02e3dba626791d949c759a48c0a44996 upstream. scarlett_ctl_enum_put() ignores the return value from snd_usb_set_cur_mix_value() and reports success whenever the requested enum value differs from the current one. If the SET_CUR request fails, the callback still returns success even though neither the hardware state nor the cached mixer value changed. Fixes: 76b188c4b370 ("ALSA: usb-audio: Scarlett mixer interface for 6i6, 18i6, 18i8 and 18i20") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260419-usb-write-error-propagation-v1-2-5a3bd4a673ae@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 8babfa3f7c458..c7f2ad6e657fe 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -439,7 +439,9 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, val = ucontrol->value.integer.value[0]; val = val + opt->start; if (val != oval) { - snd_usb_set_cur_mix_value(elem, 0, 0, val); + err = snd_usb_set_cur_mix_value(elem, 0, 0, val); + if (err < 0) + return err; return 1; } return 0; From 3061b6c114458d3570a1c48634a27d420a47c2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sun, 19 Apr 2026 17:30:31 -0300 Subject: [PATCH 0223/3297] ALSA: usb-audio: Propagate US-16x08 write errors in route/mix EQ-switch put callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3c06aec8abda6ba068b58a8b7119cdb2a48456b1 upstream. Several US-16x08 mixer put callbacks log failed control URBs but still return success to userspace. That hides device write failures even though the requested value was not applied. Return the negative write error instead in the route, master, bus, channel, and EQ switch put callbacks. Fixes: d2bb390a2081 ("ALSA: usb-audio: Tascam US-16x08 DSP mixer quirk") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260419-usb-write-error-propagation-v1-3-5a3bd4a673ae@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_us16x08.c | 49 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index f9df40730effd..72a240e497867 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -224,14 +224,14 @@ static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol, @@ -283,14 +283,14 @@ static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol, buf[5] = index + 1; err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set master, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol, @@ -324,14 +324,14 @@ static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol, break; } - if (err > 0) { - elem->cached |= 1; - elem->cache_val[0] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set bus parameter, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1; + elem->cache_val[0] = val; + return 1; } static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol, @@ -392,14 +392,14 @@ static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err); + return err; } - return err > 0 ? 1 : 0; + elem->cached |= 1 << index; + elem->cache_val[index] = val; + return 1; } static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol, @@ -529,13 +529,13 @@ static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol, msleep(15); } - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err); + return err; } + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } @@ -1418,4 +1418,3 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) return 0; } - From f3e8a6cca15b8b4cc7bf04282d57deff28defe85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 29 Apr 2026 10:20:01 -0300 Subject: [PATCH 0224/3297] ALSA: usb-audio: Roll back quirk control caches on write errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6380957fa24251856a532e48a46a4dc3d1ae26b6 upstream. Several mixer quirk callbacks cache the requested control value in kcontrol->private_value before issuing a single vendor or class write. Their paired get and resume paths consume that cache directly, so a failed write currently leaves software state changed even though the update did not succeed. That can make later reads report a value the device never accepted and can replay the stale cache on resume. Restore the previous cached value on failure in the Audigy2NX LED, Emu0204 channel switch, Xonar U1 output switch, Native Instruments controls, FTU effect program switch, and Sound Blaster E1 input source switch. Fixes: 9cf3689bfe07 ("ALSA: usb-audio: Add audigy2nx resume support") Fixes: 5f503ee9e270 ("ALSA: usb-audio: Add Emu0204 channel switch resume support") Fixes: 2bfb14c3b8fb ("ALSA: usb-audio: Add Xonar U1 resume support") Fixes: da6d276957ea ("ALSA: usb-audio: Add resume support for Native Instruments controls") Fixes: 0b4e9cfcef05 ("ALSA: usb-audio: Add resume support for FTU controls") Fixes: 388fdb8f882a ("ALSA: usb-audio: Support changing input on Sound Blaster E1") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260429-alsa-usb-quirks-cache-rollback-v1-1-01b35c688b80@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 45 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e56f98615b4ce..791670105f932 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -333,6 +333,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, int index = kcontrol->private_value & 0xff; unsigned int value = ucontrol->value.integer.value[0]; int old_value = kcontrol->private_value >> 8; + unsigned long old_pval = kcontrol->private_value; int err; if (value > 1) @@ -341,7 +342,11 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, return 0; kcontrol->private_value = (value << 8) | index; err = snd_audigy2nx_led_update(mixer, value, index); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list) @@ -487,6 +492,7 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct usb_mixer_interface *mixer = list->mixer; unsigned int value = ucontrol->value.enumerated.item[0]; + unsigned long old_pval = kcontrol->private_value; int err; if (value > 1) @@ -497,7 +503,11 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, kcontrol->private_value = value; err = snd_emu0204_ch_switch_update(mixer, value); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list) @@ -805,7 +815,11 @@ static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, kcontrol->private_value = new_status; err = snd_xonar_u1_switch_update(list->mixer, new_status); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_status; + return err; + } + return 1; } static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list) @@ -1143,7 +1157,8 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); - u8 oldval = (kcontrol->private_value >> 24) & 0xff; + unsigned long old_pval = kcontrol->private_value; + u8 oldval = (old_pval >> 24) & 0xff; u8 newval = ucontrol->value.integer.value[0]; int err; @@ -1153,7 +1168,11 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, kcontrol->private_value &= ~(0xff << 24); kcontrol->private_value |= (unsigned int)newval << 24; err = snd_ni_update_cur_val(list); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { @@ -1308,7 +1327,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); - unsigned int pval = list->kctl->private_value; + unsigned long old_pval = list->kctl->private_value; + unsigned int pval = old_pval; int cur_val, err, new_val; cur_val = pval >> 24; @@ -1319,7 +1339,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, kctl->private_value &= ~(0xff << 24); kctl->private_value |= new_val << 24; err = snd_ftu_eff_switch_update(list); - return err < 0 ? err : 1; + if (err < 0) { + kctl->private_value = old_pval; + return err; + } + return 1; } static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, @@ -2098,13 +2122,18 @@ static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol, { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); unsigned char value = !!ucontrol->value.integer.value[0]; + unsigned long old_pval = kcontrol->private_value; int err; if (kcontrol->private_value == value) return 0; kcontrol->private_value = value; err = snd_soundblaster_e1_switch_update(list->mixer, value); - return err < 0 ? err : 1; + if (err < 0) { + kcontrol->private_value = old_pval; + return err; + } + return 1; } static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list) From 54c448e4f26a7c86552d83edf715c5ca5ab31dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 29 Apr 2026 10:20:02 -0300 Subject: [PATCH 0225/3297] ALSA: usb-audio: Update Babyface Pro control caches only after successful writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d8f802ccf1fdbeb89d62748d6a0d0fbd442c8127 upstream. snd_bbfpro_ctl_put() and snd_bbfpro_vol_put() cache the requested packed control state in kcontrol->private_value before issuing the USB write. Their get and resume paths use that cached value directly, so a failed write can leave the driver reporting and later replaying a setting the hardware never accepted. Update the cached state only after a successful USB write. Fixes: 3e8f3bd04716 ("ALSA: usb-audio: RME Babyface Pro mixer patch") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260429-alsa-usb-quirks-cache-rollback-v1-2-01b35c688b80@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 791670105f932..02d9ede674670 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -2905,12 +2905,14 @@ static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol, if (val == old_value) return 0; + err = snd_bbfpro_ctl_update(mixer, reg, idx, val); + if (err < 0) + return err; + kcontrol->private_value = reg | ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT) | ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT); - - err = snd_bbfpro_ctl_update(mixer, reg, idx, val); - return err < 0 ? err : 1; + return 1; } static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list) @@ -3095,11 +3097,13 @@ static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol, new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK; + err = snd_bbfpro_vol_update(mixer, idx, new_val); + if (err < 0) + return err; + kcontrol->private_value = idx | (new_val << SND_BBFPRO_MIXER_VAL_SHIFT); - - err = snd_bbfpro_vol_update(mixer, idx, new_val); - return err < 0 ? err : 1; + return 1; } static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list) From afc90150551dd0d97a012dd51be2cdd7e0235eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Sun, 19 Apr 2026 17:30:32 -0300 Subject: [PATCH 0226/3297] ALSA: usb-audio: Update US-16x08 EQ/comp shadow state after successful writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a440c17869ecd71da0f295b62868fc742d09a8ba upstream. snd_us16x08_comp_put() and snd_us16x08_eq_put() update their software stores before sending the USB write. If the transfer fails, later get callbacks report a value the hardware never accepted. Build the outgoing message from the current store plus the pending value, then commit the store only after a successful write. Fixes: d2bb390a2081 ("ALSA: usb-audio: Tascam US-16x08 DSP mixer quirk") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260419-usb-write-error-propagation-v1-4-5a3bd4a673ae@gmail.com Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_us16x08.c | 78 ++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index 72a240e497867..e53f0808be1ae 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -435,6 +435,7 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol, int index = ucontrol->id.index; char buf[sizeof(comp_msg)]; int val_idx, val; + int threshold, ratio, attack, release, gain, switch_on; int err; val = ucontrol->value.integer.value[0]; @@ -447,36 +448,61 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol, /* new control value incl. bias*/ val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE; - store->val[val_idx][index] = ucontrol->value.integer.value[0]; + threshold = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)] + [index]; + ratio = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]; + attack = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index]; + release = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)] + [index]; + gain = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index]; + switch_on = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)] + [index]; + + switch (val_idx) { + case COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD): + threshold = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO): + ratio = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK): + attack = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE): + release = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN): + gain = val; + break; + case COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH): + switch_on = val; + break; + } /* prepare compressor URB message from template */ memcpy(buf, comp_msg, sizeof(comp_msg)); /* place comp values in message buffer watch bias! */ - buf[8] = store->val[ - COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index] - - SND_US16X08_COMP_THRESHOLD_BIAS; - buf[11] = ratio_map[store->val[ - COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]]; - buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index] - + SND_US16X08_COMP_ATTACK_BIAS; - buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index] - + SND_US16X08_COMP_RELEASE_BIAS; - buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index]; - buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index]; + buf[8] = threshold - SND_US16X08_COMP_THRESHOLD_BIAS; + buf[11] = ratio_map[ratio]; + buf[14] = attack + SND_US16X08_COMP_ATTACK_BIAS; + buf[17] = release + SND_US16X08_COMP_RELEASE_BIAS; + buf[20] = gain; + buf[26] = switch_on; /* place channel selector in message buffer */ buf[5] = index + 1; err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg)); - if (err > 0) { - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err); + return err; } + store->val[val_idx][index] = val; + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } @@ -578,11 +604,10 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, /* copy URB buffer from EQ template */ memcpy(buf, eqs_msq, sizeof(eqs_msq)); - store->val[b_idx][p_idx][index] = val; - buf[20] = store->val[b_idx][3][index]; - buf[17] = store->val[b_idx][2][index]; - buf[14] = store->val[b_idx][1][index]; - buf[11] = store->val[b_idx][0][index]; + buf[20] = p_idx == 3 ? val : store->val[b_idx][3][index]; + buf[17] = p_idx == 2 ? val : store->val[b_idx][2][index]; + buf[14] = p_idx == 1 ? val : store->val[b_idx][1][index]; + buf[11] = p_idx == 0 ? val : store->val[b_idx][0][index]; /* place channel index in URB buffer */ buf[5] = index + 1; @@ -592,14 +617,15 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq)); - if (err > 0) { - /* store new value in EQ band cache */ - elem->cached |= 1 << index; - elem->cache_val[index] = val; - } else { + if (err < 0) { usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err); + return err; } + store->val[b_idx][p_idx][index] = val; + /* store new value in EQ band cache */ + elem->cached |= 1 << index; + elem->cache_val[index] = val; return 1; } From a385d3435a7af36c4ac18e9536981b99d5d1c2a0 Mon Sep 17 00:00:00 2001 From: Raghavendra Rao Ananta Date: Thu, 14 May 2026 17:34:49 +0000 Subject: [PATCH 0227/3297] vfio/pci: Use a private flag to prevent power state change with VFs commit 40ef3edf151e184d021917a5c4c771cc0870844a upstream. The current implementation uses pci_num_vf() while holding the memory_lock to prevent changing the power state of a PF when VFs are enabled. This creates a lockdep circular dependency warning because memory_lock is held during device probing. [ 286.997167] ====================================================== [ 287.003363] WARNING: possible circular locking dependency detected [ 287.009562] 7.0.0-dbg-DEV #3 Tainted: G S [ 287.015074] ------------------------------------------------------ [ 287.021270] vfio_pci_sriov_/18636 is trying to acquire lock: [ 287.026942] ff45bea2294d4968 (&vdev->memory_lock){+.+.}-{4:4}, at: vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.036530] [ 287.036530] but task is already holding lock: [ 287.042383] ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4}, at: vfio_group_fops_unl_ioctl+0x44d/0x7b0 [ 287.051879] [ 287.051879] which lock already depends on the new lock. [ 287.051879] [ 287.060070] [ 287.060070] the existing dependency chain (in reverse order) is: [ 287.067568] [ 287.067568] -> #2 (&new_dev_set->lock){+.+.}-{4:4}: [ 287.073941] __mutex_lock+0x92/0xb80 [ 287.078058] vfio_assign_device_set+0x66/0x1b0 [ 287.083042] vfio_pci_core_register_device+0xd1/0x2a0 [ 287.088638] vfio_pci_probe+0xd2/0x100 [ 287.092933] local_pci_probe_callback+0x4d/0xa0 [ 287.098001] process_scheduled_works+0x2ca/0x680 [ 287.103158] worker_thread+0x1e8/0x2f0 [ 287.107452] kthread+0x10c/0x140 [ 287.111230] ret_from_fork+0x18e/0x360 [ 287.115519] ret_from_fork_asm+0x1a/0x30 [ 287.119983] [ 287.119983] -> #1 ((work_completion)(&arg.work)){+.+.}-{0:0}: [ 287.127219] __flush_work+0x345/0x490 [ 287.131429] pci_device_probe+0x2e3/0x490 [ 287.135979] really_probe+0x1f9/0x4e0 [ 287.140180] __driver_probe_device+0x77/0x100 [ 287.145079] driver_probe_device+0x1e/0x110 [ 287.149803] __device_attach_driver+0xe3/0x170 [ 287.154789] bus_for_each_drv+0x125/0x150 [ 287.159346] __device_attach+0xca/0x1a0 [ 287.163720] device_initial_probe+0x34/0x50 [ 287.168445] pci_bus_add_device+0x6e/0x90 [ 287.172995] pci_iov_add_virtfn+0x3c9/0x3e0 [ 287.177719] sriov_add_vfs+0x2c/0x60 [ 287.181838] sriov_enable+0x306/0x4a0 [ 287.186038] vfio_pci_core_sriov_configure+0x184/0x220 [ 287.191715] sriov_numvfs_store+0xd9/0x1c0 [ 287.196351] kernfs_fop_write_iter+0x13f/0x1d0 [ 287.201338] vfs_write+0x2be/0x3b0 [ 287.205286] ksys_write+0x73/0x100 [ 287.209233] do_syscall_64+0x14d/0x750 [ 287.213529] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 287.219120] [ 287.219120] -> #0 (&vdev->memory_lock){+.+.}-{4:4}: [ 287.225491] __lock_acquire+0x14c6/0x2800 [ 287.230048] lock_acquire+0xd3/0x2f0 [ 287.234168] down_write+0x3a/0xc0 [ 287.238019] vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.243436] __rpm_callback+0x8c/0x310 [ 287.247730] rpm_resume+0x529/0x6f0 [ 287.251765] __pm_runtime_resume+0x68/0x90 [ 287.256402] vfio_pci_core_enable+0x44/0x310 [ 287.261216] vfio_pci_open_device+0x1c/0x80 [ 287.265947] vfio_df_open+0x10f/0x150 [ 287.270148] vfio_group_fops_unl_ioctl+0x4a4/0x7b0 [ 287.275476] __se_sys_ioctl+0x71/0xc0 [ 287.279679] do_syscall_64+0x14d/0x750 [ 287.283975] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 287.289559] [ 287.289559] other info that might help us debug this: [ 287.289559] [ 287.297582] Chain exists of: [ 287.297582] &vdev->memory_lock --> (work_completion)(&arg.work) --> &new_dev_set->lock [ 287.297582] [ 287.310023] Possible unsafe locking scenario: [ 287.310023] [ 287.315961] CPU0 CPU1 [ 287.320510] ---- ---- [ 287.325059] lock(&new_dev_set->lock); [ 287.328917] lock((work_completion)(&arg.work)); [ 287.336153] lock(&new_dev_set->lock); [ 287.342523] lock(&vdev->memory_lock); [ 287.346382] [ 287.346382] *** DEADLOCK *** [ 287.346382] [ 287.352315] 2 locks held by vfio_pci_sriov_/18636: [ 287.357125] #0: ff45bea208ed3e18 (&group->group_lock){+.+.}-{4:4}, at: vfio_group_fops_unl_ioctl+0x3e3/0x7b0 [ 287.367048] #1: ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4}, at: vfio_group_fops_unl_ioctl+0x44d/0x7b0 [ 287.376976] [ 287.376976] stack backtrace: [ 287.381353] CPU: 191 UID: 0 PID: 18636 Comm: vfio_pci_sriov_ Tainted: G S 7.0.0-dbg-DEV #3 PREEMPTLAZY [ 287.381355] Tainted: [S]=CPU_OUT_OF_SPEC [ 287.381356] Call Trace: [ 287.381357] [ 287.381358] dump_stack_lvl+0x54/0x70 [ 287.381361] print_circular_bug+0x2e1/0x300 [ 287.381363] check_noncircular+0xf9/0x120 [ 287.381364] ? __lock_acquire+0x5b4/0x2800 [ 287.381366] __lock_acquire+0x14c6/0x2800 [ 287.381368] ? pci_mmcfg_read+0x4f/0x220 [ 287.381370] ? pci_mmcfg_write+0x57/0x220 [ 287.381371] ? lock_acquire+0xd3/0x2f0 [ 287.381373] ? pci_mmcfg_write+0x57/0x220 [ 287.381374] ? lock_release+0xef/0x360 [ 287.381376] ? vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381377] lock_acquire+0xd3/0x2f0 [ 287.381378] ? vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381379] ? lock_is_held_type+0x76/0x100 [ 287.381382] down_write+0x3a/0xc0 [ 287.381382] ? vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381383] vfio_pci_core_runtime_resume+0x1f/0xa0 [ 287.381384] ? __pfx_pci_pm_runtime_resume+0x10/0x10 [ 287.381385] __rpm_callback+0x8c/0x310 [ 287.381386] ? ktime_get_mono_fast_ns+0x3d/0xb0 [ 287.381389] ? __pfx_pci_pm_runtime_resume+0x10/0x10 [ 287.381390] rpm_resume+0x529/0x6f0 [ 287.381392] ? lock_is_held_type+0x76/0x100 [ 287.381394] __pm_runtime_resume+0x68/0x90 [ 287.381396] vfio_pci_core_enable+0x44/0x310 [ 287.381398] vfio_pci_open_device+0x1c/0x80 [ 287.381399] vfio_df_open+0x10f/0x150 [ 287.381401] vfio_group_fops_unl_ioctl+0x4a4/0x7b0 [ 287.381402] __se_sys_ioctl+0x71/0xc0 [ 287.381404] do_syscall_64+0x14d/0x750 [ 287.381405] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 287.381406] ? trace_irq_disable+0x25/0xd0 [ 287.381409] entry_SYSCALL_64_after_hwframe+0x77/0x7f Introduce a private flag 'sriov_active' in the vfio_pci_core_device struct. This allows the driver to track the SR-IOV power state requirement without relying on pci_num_vf() while holding the memory_lock. The lock is now only held to set the flag and ensure the device is in D0, after which pci_enable_sriov() can be called without the lock. Fixes: f4162eb1e2fc ("vfio/pci: Change the PF power state to D0 before enabling VFs") Cc: stable@vger.kernel.org Suggested-by: Jason Gunthorpe Suggested-by: Alex Williamson Signed-off-by: Raghavendra Rao Ananta Link: https://lore.kernel.org/r/20260514173449.3282188-1-rananta@google.com [promote bitfield to plain bool to avoid storage-unit races] Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/vfio_pci_core.c | 17 ++++++++++++++--- include/linux/vfio_pci_core.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 53a846d3e6758..1f5dde6b022d4 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -271,8 +271,11 @@ int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t stat int ret; /* Prevent changing power state for PFs with VFs enabled */ - if (pci_num_vf(pdev) && state > PCI_D0) - return -EBUSY; + if (state > PCI_D0) { + lockdep_assert_held_write(&vdev->memory_lock); + if (vdev->sriov_active) + return -EBUSY; + } if (vdev->needs_pm_restore) { if (pdev->current_state < PCI_D3hot && state >= PCI_D3hot) { @@ -2309,8 +2312,9 @@ int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev, down_write(&vdev->memory_lock); vfio_pci_set_power_state(vdev, PCI_D0); - ret = pci_enable_sriov(pdev, nr_virtfn); + vdev->sriov_active = true; up_write(&vdev->memory_lock); + ret = pci_enable_sriov(pdev, nr_virtfn); if (ret) { pm_runtime_put(&pdev->dev); goto out_del; @@ -2324,6 +2328,13 @@ int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev, } out_del: + /* + * Avoid taking the memory_lock intentionally. A race with a power + * state transition would at most result in an -EBUSY, leaving the + * device in PCI_D0. + */ + vdev->sriov_active = false; + mutex_lock(&vfio_pci_sriov_pfs_mutex); list_del_init(&vdev->sriov_pfs_item); out_unlock: diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 0ddc42732647e..1225db35aa73a 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -86,6 +86,7 @@ struct vfio_pci_core_device { bool needs_pm_restore:1; bool pm_intx_masked:1; bool pm_runtime_engaged:1; + bool sriov_active; struct pci_saved_state *pci_saved_state; struct pci_saved_state *pm_save; int ioeventfds_nr; From f6c67cf0051f96ba61d186731d3d9409b9927db2 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 13:12:29 -0600 Subject: [PATCH 0228/3297] vfio/pci: Latch disable_idle_d3 per device commit 4575e9aac5336d1365138c0284773bf8da4b1fa3 upstream. When disable_idle_d3 was introduced in vfio-pci, it directly manipulated the device power state with pci_set_power_state(). There were no refcounts to maintain or balanced operations, we could unconditionally bring the device to D0 and conditionally move it to D3hot. Therefore the module parameter was made writable. Later, in commit c61302aa48f7 ("vfio/pci: Move module parameters to vfio_pci.c"), as part of the vfio-pci-core split, the writable aspect of the module parameter was nullified. The parameter value could still be changed through sysfs, but the vfio-pci driver latched the values into vfio-pci-core globals at module init. Loading the vfio-pci module, or unloading and reloading, with non-default or different values could change the globals relative to existing devices bound to vfio-pci variant drivers. Runtime PM was introduced in commit 7ab5e10eda02 ("vfio/pci: Move the unused device into low power state with runtime PM"), which marks the point where power states became refcounted. PM get and put operations need to be balanced, but the same module operations noted above can change the global variables relative to those devices already bound to vfio-pci variant drivers. This introduces a window where PM operations can now become unbalanced. To resolve this with a narrow footprint for stable backports, the disable_idle_d3 flag is latched into the vfio_pci_core_device at the time of initialization, such that the device always operates with a consistent value. NB. vfio_pci_dev_set_try_reset() now unconditionally raises the runtime PM usage count around bus reset to account for disable_idle_d3 becoming a per-device rather than global flag. When this flag is set, the additional get/put pair is harmless and allows continued use of the shared vfio_pci_dev_set_pm_runtime_get() helper. Fixes: 7ab5e10eda02 ("vfio/pci: Move the unused device into low power state with runtime PM") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-2-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/vfio_pci_core.c | 19 ++++++++++--------- include/linux/vfio_pci_core.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 1f5dde6b022d4..c2562285b97be 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -506,7 +506,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) u16 cmd; u8 msix_pos; - if (!disable_idle_d3) { + if (!vdev->disable_idle_d3) { ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; @@ -584,7 +584,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) out_disable_device: pci_disable_device(pdev); out_power: - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_put(&pdev->dev); return ret; } @@ -720,7 +720,7 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev) vfio_pci_dev_set_try_reset(vdev->vdev.dev_set); /* Put the pm-runtime usage counter acquired during enable */ - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_put(&pdev->dev); } EXPORT_SYMBOL_GPL(vfio_pci_core_disable); @@ -2131,6 +2131,8 @@ int vfio_pci_core_init_dev(struct vfio_device *core_vdev) init_rwsem(&vdev->memory_lock); xa_init(&vdev->ctx); + vdev->disable_idle_d3 = disable_idle_d3; + return 0; } EXPORT_SYMBOL_GPL(vfio_pci_core_init_dev); @@ -2222,7 +2224,7 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev) dev->driver->pm = &vfio_pci_core_pm_ops; pm_runtime_allow(dev); - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_put(dev); ret = vfio_register_group_dev(&vdev->vdev); @@ -2231,7 +2233,7 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev) return 0; out_power: - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_get_noresume(dev); pm_runtime_forbid(dev); @@ -2250,7 +2252,7 @@ void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev) vfio_pci_vf_uninit(vdev); vfio_pci_vga_uninit(vdev); - if (!disable_idle_d3) + if (!vdev->disable_idle_d3) pm_runtime_get_noresume(&vdev->pdev->dev); pm_runtime_forbid(&vdev->pdev->dev); @@ -2578,7 +2580,7 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set) * state. Increment the usage count for all the devices in the dev_set * before reset and decrement the same after reset. */ - if (!disable_idle_d3 && vfio_pci_dev_set_pm_runtime_get(dev_set)) + if (vfio_pci_dev_set_pm_runtime_get(dev_set)) return; if (!pci_reset_bus(pdev)) @@ -2588,8 +2590,7 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set) if (reset_done) cur->needs_reset = false; - if (!disable_idle_d3) - pm_runtime_put(&cur->pdev->dev); + pm_runtime_put(&cur->pdev->dev); } } diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 1225db35aa73a..a303b1dff22c8 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -86,6 +86,7 @@ struct vfio_pci_core_device { bool needs_pm_restore:1; bool pm_intx_masked:1; bool pm_runtime_engaged:1; + bool disable_idle_d3:1; bool sriov_active; struct pci_saved_state *pci_saved_state; struct pci_saved_state *pm_save; From 52adb2dff7ce3d8430e2bdc5988b618a430def85 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 13:12:30 -0600 Subject: [PATCH 0229/3297] vfio/pci: Release the VGA arbiter client on register_device() failure commit daedde7f024ecf88bc8e832ed40cf2c795f0796a upstream. The re-order in the Fixes commit below displaced vfio_pci_vga_init() as the last failure point of what is now vfio_pci_core_register_device() without introducing an unwind for the VGA arbiter registration. In current kernels this is mostly benign because vfio_pci_set_decode() only uses pci_dev state, but the original failure path could leave a callback with a freed vdev cookie. The stale registration also becomes unsafe again once the callback follows drvdata to the vfio device. Add the required VGA unwind callout. Fixes: 4aeec3984ddc ("vfio/pci: Re-order vfio_pci_probe()") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-3-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/vfio_pci_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index c2562285b97be..a807a678277c6 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -2237,6 +2237,7 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev) pm_runtime_get_noresume(dev); pm_runtime_forbid(dev); + vfio_pci_vga_uninit(vdev); out_vf: vfio_pci_vf_uninit(vdev); return ret; From ba96666d991e6d6111f5dbdb7faad878485ceb22 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 13:12:31 -0600 Subject: [PATCH 0230/3297] vfio/pci: Fix racy bitfields and tighten struct layout commit e73638e55f861758d49f14d7bb5dba3035981cd7 upstream. Bitfield operations are not atomic, they use a read-modify-write pattern, therefore we should be careful not to pack bitfields that can be concurrently updated into the same storage unit. This split takes a binary approach: flags that are only modified pre/post open/close remain bitfields, flags modified from user action, including actions that reach across to another device (ex. reset) use dedicated storage units. Note that the virq_disabled and bardirty flags are relocated to fill an existing hole in the structure. Bitfield justifications: has_dyn_msix: written only in vfio_pci_core_enable() pci_2_3: written only in vfio_pci_core_enable() reset_works: written only in vfio_pci_core_enable() extended_caps: written only in vfio_cap_len() under vfio_config_init() has_vga: written only in vfio_pci_core_enable() nointx: written only in vfio_pci_core_enable() needs_pm_restore: written only in vfio_pci_probe_power_state() disable_idle_d3: written only at .init in vfio_pci_core_init_dev() Dedicated storage units: virq_disabled: written by guest INTx command writes in vfio_basic_config_write() while the device is open bardirty: written by guest BAR writes in vfio_basic_config_write() while the device is open pm_intx_masked: written in the runtime-PM suspend path. pm_runtime_engaged: written by low-power feature entry/exit paths needs_reset: set in vfio_pci_core_disable() and cleared for devices in the set by vfio_pci_dev_set_try_reset() sriov_active: written by vfio_pci_core_sriov_configure() via sysfs sriov_numvfs while bound. Fixes: 9cd0f6d5cbb6 ("vfio/pci: Use bitfield for struct vfio_pci_core_device flags") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-4-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- include/linux/vfio_pci_core.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index a303b1dff22c8..327cfd3946d4d 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -60,6 +60,9 @@ struct vfio_pci_core_device { struct pci_dev *pdev; void __iomem *barmap[PCI_STD_NUM_BARS]; bool bar_mmap_supported[PCI_STD_NUM_BARS]; + /* Flags modified at runtime - dedicated storage unit */ + bool virq_disabled; + bool bardirty; u8 *pci_config_map; u8 *vconfig; struct perm_bits *msi_perm; @@ -74,19 +77,19 @@ struct vfio_pci_core_device { u16 msix_size; u32 msix_offset; u32 rbar[7]; + /* Flags only modified on setup/release - bitfield ok */ bool has_dyn_msix:1; bool pci_2_3:1; - bool virq_disabled:1; bool reset_works:1; bool extended_caps:1; - bool bardirty:1; bool has_vga:1; - bool needs_reset:1; bool nointx:1; bool needs_pm_restore:1; - bool pm_intx_masked:1; - bool pm_runtime_engaged:1; bool disable_idle_d3:1; + /* Flags modified at runtime - dedicated storage unit */ + bool needs_reset; + bool pm_intx_masked; + bool pm_runtime_engaged; bool sriov_active; struct pci_saved_state *pci_saved_state; struct pci_saved_state *pm_save; From 7f2d6b31089e48db4653df832c9a6afdde9a1c29 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 2 Jun 2026 16:58:48 +0800 Subject: [PATCH 0231/3297] vfio: prevent infinite loop in vfio_mig_get_next_state() on blocked arc commit a26b499b757cfc8bbff1088bb1b844639e250893 upstream. vfio_mig_get_next_state() walks vfio_from_fsm_table[] one step at a time, looping to skip optional states the device does not support until *next_fsm is supported. A blocked transition is encoded as VFIO_DEVICE_STATE_ERROR, which the trailing return reports as -EINVAL. The skip loop does not account for the ERROR sentinel. state_flags_table[ERROR] is ~0U and vfio_from_fsm_table[ERROR][*] is ERROR, so once *next_fsm becomes ERROR the loop condition stays true and *next_fsm never changes. The blocked arcs STOP_COPY -> PRE_COPY and STOP_COPY -> PRE_COPY_P2P map to ERROR yet pass the support check on a precopy-capable device, causing the loop to spin forever while holding the driver state mutex. This can result in a soft lockup, and a panic with softlockup_panic set. Terminate the skip loop on the ERROR sentinel so a blocked transition falls through to the existing return and reports -EINVAL. Fixes: 4db52602a607 ("vfio: Extend the device migration protocol with PRE_COPY") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Reviewed-by: Kevin Tian Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/SYBPR01MB7881290BBDE79B61AE6A017FAF122@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/vfio_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c index 38c8e9350a60e..45ae63214b4b6 100644 --- a/drivers/vfio/vfio_main.c +++ b/drivers/vfio/vfio_main.c @@ -846,7 +846,8 @@ int vfio_mig_get_next_state(struct vfio_device *device, * logical state, as per the above comment. */ *next_fsm = vfio_from_fsm_table[cur_fsm][new_fsm]; - while ((state_flags_table[*next_fsm] & device->migration_flags) != + while (*next_fsm != VFIO_DEVICE_STATE_ERROR && + (state_flags_table[*next_fsm] & device->migration_flags) != state_flags_table[*next_fsm]) *next_fsm = vfio_from_fsm_table[*next_fsm][new_fsm]; From a53109ffb6b5148e11a27fb7670355b92db12dd3 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 15 Jun 2026 14:47:00 -0600 Subject: [PATCH 0232/3297] vfio: Remove device debugfs before releasing devres commit dc7fe87de492ea7f33a72b78d26650b75bf37f4f upstream. VFIO device debugfs files created with debugfs_create_devm_seqfile() store a devres allocated debugfs_devm_entry as inode private data. vfio_unregister_group_dev() currently calls vfio_device_del() before vfio_device_debugfs_exit(), but device_del() releases devres. This can leave debugfs entries visible with stale inode private data while unregister waits for userspace references to drain. Remove the per-device debugfs tree before vfio_device_del(). The debugfs view is diagnostic only, so losing it at the start of unregister is preferable to preserving entries whose backing storage may already have been released. Complete the teardown by clearing the per-device debugfs root after removal. This matches the global debugfs root cleanup and prevents future users from mistaking a removed dentry for a live debugfs tree during the remainder of unregister. Fixes: 2202844e4468 ("vfio/migration: Add debugfs to live migration driver") Reported-by: Sashiko AI Review Link: https://lore.kernel.org/r/20260615192725.6A2221F000E9@smtp.kernel.org Cc: stable@vger.kernel.org Cc: Longfang Liu Assisted-by: OpenAI Codex:gpt-5 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615204717.735302-1-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/debugfs.c | 1 + drivers/vfio/vfio_main.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/debugfs.c b/drivers/vfio/debugfs.c index 8b0ca7a090640..8a2f1b0cce3f1 100644 --- a/drivers/vfio/debugfs.c +++ b/drivers/vfio/debugfs.c @@ -97,6 +97,7 @@ void vfio_device_debugfs_init(struct vfio_device *vdev) void vfio_device_debugfs_exit(struct vfio_device *vdev) { debugfs_remove_recursive(vdev->debug_root); + vdev->debug_root = NULL; } void vfio_debugfs_create_root(void) diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c index 45ae63214b4b6..19c40fc596999 100644 --- a/drivers/vfio/vfio_main.c +++ b/drivers/vfio/vfio_main.c @@ -395,6 +395,13 @@ void vfio_unregister_group_dev(struct vfio_device *device) */ vfio_device_group_unregister(device); + /* + * Remove debugfs before device_del(), which releases devres. Some + * debugfs entries are created with debugfs_create_devm_seqfile() and + * therefore rely on devres-managed inode private data. + */ + vfio_device_debugfs_exit(device); + /* * Balances vfio_device_add() in register path, also prevents * new device opened by userspace in the cdev path. @@ -424,7 +431,6 @@ void vfio_unregister_group_dev(struct vfio_device *device) } } - vfio_device_debugfs_exit(device); /* Balances vfio_device_set_group in register path */ vfio_device_remove_group(device); } From 79f9e221dddecb86c982eed525960295c36c17b3 Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Tue, 26 May 2026 00:19:42 +0800 Subject: [PATCH 0233/3297] Bluetooth: btusb: Add USB ID 2c4e:0128 for Mercusys MA60XNB commit 88b4d528eda4ac71c2952b3458f2abbc80a91cd2 upstream. Add USB ID 2c4e:0128 for Mercusys MA60XNB, an RTL8851BU-based Wi-Fi + Bluetooth adapter. The information in /sys/kernel/debug/usb/devices about the Bluetooth device is listed as the below: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=2c4e ProdID=0128 Rev= 0.00 S: Manufacturer=Realtek S: Product=802.11ax WLAN Adapter S: SerialNumber=00e04c000001 C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=01 I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms I:* If#= 2 Alt= 0 #EPs= 8 Cls=ff(vend.) Sub=ff Prot=ff Driver=rtw89_8851bu E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=09(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0a(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0b(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0c(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Cc: stable@vger.kernel.org # 6.6.x Signed-off-by: Zenm Chen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 25236fd97aed5..7fa108893ed3d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -531,6 +531,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x7392, 0xe611), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2c4e, 0x0128), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8852AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK | From 8db0ce3de78367f61c2970c0f16d9adee8830a23 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 08:37:36 +0200 Subject: [PATCH 0234/3297] Bluetooth: btusb: fix use-after-free on registration failure commit eedc6867ebad73edbfaf9a0a65fbef7115cc4753 upstream. Make sure to release the sibling interfaces in case controller registration fails to avoid use-after-free and double-free when they are eventually disconnected. This issue was reported by Sashiko while reviewing a fix for a wakeup source leak in the btusb probe errors paths. Link: https://sashiko.dev/#/patchset/20260402092704.2346710-1-johan%40kernel.org Fixes: 9bfa35fe422c ("[Bluetooth] Add SCO support to btusb driver") Fixes: 9d08f50401ac ("Bluetooth: btusb: Add support for Broadcom LM_DIAG interface") Cc: stable@vger.kernel.org # 2.6.27 Reviewed-by: Paul Menzel Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7fa108893ed3d..f4a3824d8f080 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4381,7 +4381,7 @@ static int btusb_probe(struct usb_interface *intf, err = hci_register_dev(hdev); if (err < 0) - goto out_free_dev; + goto err_release_siblings; usb_set_intfdata(intf, data); @@ -4390,6 +4390,15 @@ static int btusb_probe(struct usb_interface *intf, return 0; +err_release_siblings: + if (data->diag) { + usb_set_intfdata(data->diag, NULL); + usb_driver_release_interface(&btusb_driver, data->diag); + } + if (data->isoc) { + usb_set_intfdata(data->isoc, NULL); + usb_driver_release_interface(&btusb_driver, data->isoc); + } out_free_dev: if (data->reset_gpio) gpiod_put(data->reset_gpio); From a7e941a395711791c7e98d9870c6562c2c9e9ef2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 08:37:37 +0200 Subject: [PATCH 0235/3297] Bluetooth: btusb: fix use-after-free on marvell probe failure commit c5b600a3c05b1a7a110d558df935a8fc8a471c79 upstream. Make sure to stop any TX URBs submitted during Marvell OOB wakeup configuration on later probe failures to avoid use-after-free in the completion callback. This issue was reported by Sashiko while reviewing a fix for a wakeup source leak in the btusb probe errors paths. Link: https://sashiko.dev/#/patchset/20260402092704.2346710-1-johan%40kernel.org Fixes: a4ccc9e33d2f ("Bluetooth: btusb: Configure Marvell to use one of the pins for oob wakeup") Cc: stable@vger.kernel.org # 4.11 Cc: Rajat Jain Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f4a3824d8f080..458b1e5809776 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4193,7 +4193,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_INTEL_COMBINED) { err = btintel_configure_setup(hdev, btusb_driver.name); if (err) - goto out_free_dev; + goto err_kill_tx_urbs; /* Transport specific configuration */ hdev->send = btusb_send_frame_intel; @@ -4355,7 +4355,7 @@ static int btusb_probe(struct usb_interface *intf, err = usb_set_interface(data->udev, 0, 0); if (err < 0) { BT_ERR("failed to set interface 0, alt 0 %d", err); - goto out_free_dev; + goto err_kill_tx_urbs; } } @@ -4363,7 +4363,7 @@ static int btusb_probe(struct usb_interface *intf, err = usb_driver_claim_interface(&btusb_driver, data->isoc, data); if (err < 0) - goto out_free_dev; + goto err_kill_tx_urbs; } if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) { @@ -4399,6 +4399,8 @@ static int btusb_probe(struct usb_interface *intf, usb_set_intfdata(data->isoc, NULL); usb_driver_release_interface(&btusb_driver, data->isoc); } +err_kill_tx_urbs: + usb_kill_anchored_urbs(&data->tx_anchor); out_free_dev: if (data->reset_gpio) gpiod_put(data->reset_gpio); From 17a2d3f903455f6b95539a58ef8ca5fa5a76e7cc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 08:37:38 +0200 Subject: [PATCH 0236/3297] Bluetooth: btusb: fix wakeup source leak on probe failure commit 3d93e1bb0fb881fe3ef961d1120556658e9cac4d upstream. Make sure to disable wakeup on probe failure to avoid leaking the wakeup source. Fixes: fd913ef7ce61 ("Bluetooth: btusb: Add out-of-band wakeup support") Cc: stable@vger.kernel.org # 4.11 Cc: Rajat Jain Signed-off-by: Johan Hovold Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 458b1e5809776..b9e5adecfed60 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2934,6 +2934,11 @@ static int marvell_config_oob_wake(struct hci_dev *hdev) return 0; } +#else +static inline int marvell_config_oob_wake(struct hci_dev *hdev) +{ + return 0; +} #endif static int btusb_set_bdaddr_marvell(struct hci_dev *hdev, @@ -3793,6 +3798,11 @@ static int btusb_config_oob_wake(struct hci_dev *hdev) bt_dev_info(hdev, "OOB Wake-on-BT configured at IRQ %u", irq); return 0; } +#else +static inline int btusb_config_oob_wake(struct hci_dev *hdev) +{ + return 0; +} #endif static void btusb_check_needs_reset_resume(struct usb_interface *intf) @@ -4147,7 +4157,6 @@ static int btusb_probe(struct usb_interface *intf, hdev->wakeup = btusb_wakeup; hdev->hci_drv = &btusb_hci_drv; -#ifdef CONFIG_PM err = btusb_config_oob_wake(hdev); if (err) goto out_free_dev; @@ -4156,9 +4165,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL && data->oob_wake_irq) { err = marvell_config_oob_wake(hdev); if (err) - goto out_free_dev; + goto err_disable_wakeup; } -#endif + if (id->driver_info & BTUSB_CW6622) hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY); @@ -4401,6 +4410,9 @@ static int btusb_probe(struct usb_interface *intf, } err_kill_tx_urbs: usb_kill_anchored_urbs(&data->tx_anchor); +err_disable_wakeup: + if (data->oob_wake_irq) + device_init_wakeup(&data->udev->dev, false); out_free_dev: if (data->reset_gpio) gpiod_put(data->reset_gpio); From ea02df466df60ecd758eb3b4df3f0cadc5c886ce Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:30 +0000 Subject: [PATCH 0237/3297] binder: fix UAF in binder_thread_release() commit 114a116aaa5f0295376cdf12da743c5bce3b20ce upstream. When a thread exits, binder_thread_release() walks its transaction stack to clear the t->from and t->to_proc that correspond with the exiting thread. However, a process dying in parallel might attempt to kfree some of these transactions. And if one of them has no associated t->to_proc, the t->to_proc->inner_lock will not be acquired. This means that transaction accesses in binder_thread_release() after t->to_proc has been cleared might race with binder_free_transaction() and cause a use-after-free error as reported by KASAN: ================================================================== BUG: KASAN: slab-use-after-free in binder_thread_release+0x5d0/0x798 Write of size 8 at addr ffff000016627500 by task X/715 CPU: 17 UID: 0 PID: 715 Comm: X Not tainted 7.1.0-rc5-00149-g8fde5d1d47f6 #30 PREEMPT Hardware name: linux,dummy-virt (DT) Call trace: binder_thread_release+0x5d0/0x798 binder_ioctl+0x12c0/0x299c [...] Allocated by task 717 on cpu 18 at 67.267803s: __kasan_kmalloc+0xa0/0xbc __kmalloc_cache_noprof+0x174/0x444 binder_transaction+0x554/0x8150 binder_thread_write+0xa30/0x4354 binder_ioctl+0x20f0/0x299c [...] Freed by task 202 on cpu 18 at 90.416221s: __kasan_slab_free+0x58/0x80 kfree+0x1a0/0x4a4 binder_free_transaction+0x150/0x294 binder_send_failed_reply+0x398/0x6d8 binder_release_work+0x3e4/0x4ec binder_deferred_func+0xbd8/0x104c [...] ================================================================== In order to avoid this, make sure that binder_free_transaction() reads the t->to_proc under the transaction lock. This will serialize the transaction release with the accesses in binder_thread_release(). Plus, it matches the documented locking rules for @to_proc. Cc: stable Fixes: 7a4408c6bd3e ("binder: make sure accesses to proc/thread are safe") Reviewed-by: Alice Ryhl Signed-off-by: Carlos Llamas Link: https://patch.msgid.link/20260619185233.2194678-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index a6b3bfe0d9b81..c39af7b63c024 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1658,7 +1658,11 @@ static void binder_txn_latency_free(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t) { - struct binder_proc *target_proc = t->to_proc; + struct binder_proc *target_proc; + + spin_lock(&t->lock); + target_proc = t->to_proc; + spin_unlock(&t->lock); if (target_proc) { binder_inner_proc_lock(target_proc); From 328ccf32acb87e8bbb1fe2b065068c574e4db2bf Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:31 +0000 Subject: [PATCH 0238/3297] binder: fix UAF in binder_free_transaction() commit f223d27a546c1e1f48d38fd67760e78f068fe8c4 upstream. In binder_free_transaction(), the t->to_proc is read under the t->lock. However, once the t->lock is dropped, the to_proc can die in parallel. This leads to a use-after-free error when we attempt to acquire its inner lock right afterwards: ================================================================== BUG: KASAN: slab-use-after-free in _raw_spin_lock+0xe4/0x1a0 Write of size 4 at addr ffff00001125da70 by task B/672 CPU: 20 UID: 0 PID: 672 Comm: B Not tainted 7.1.0-rc6-00284-g8e65320d91cd #4 PREEMPT Hardware name: linux,dummy-virt (DT) Call trace: _raw_spin_lock+0xe4/0x1a0 binder_free_transaction+0x8c/0x320 binder_send_failed_reply+0x21c/0x2f8 binder_thread_release+0x488/0x7e0 binder_ioctl+0x12c0/0x29a0 [...] Allocated by task 675: __kmalloc_cache_noprof+0x174/0x444 binder_open+0x118/0xb70 do_dentry_open+0x374/0x1040 vfs_open+0x58/0x3bc [...] Freed by task 212: __kasan_slab_free+0x58/0x80 kfree+0x1a0/0x4a4 binder_proc_dec_tmpref+0x32c/0x5e0 binder_deferred_func+0xc48/0x104c process_one_work+0x53c/0xbc0 [...] ================================================================== To prevent this, pin the target thread (t->to_thread) to guarantee the target process remains alive. Undelivered transactions without a target thread are already safe, as the target process can only be the current context in those paths. Cc: stable Reported-by: Alice Ryhl Closes: https://lore.kernel.org/all/aikJKVuny_eOivwN@google.com/ Fixes: a370003cc301 ("binder: fix possible UAF when freeing buffer") Signed-off-by: Carlos Llamas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260619185233.2194678-2-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index c39af7b63c024..558164f0918fb 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1658,10 +1658,19 @@ static void binder_txn_latency_free(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t) { + struct binder_thread *target_thread; struct binder_proc *target_proc; spin_lock(&t->lock); target_proc = t->to_proc; + target_thread = t->to_thread; + /* + * Pin target_thread to keep target_proc alive. Undelivered + * transactions with !target_thread are safe, as target_proc + * can only be the current context there. + */ + if (target_thread) + atomic_inc(&target_thread->tmp_ref); spin_unlock(&t->lock); if (target_proc) { @@ -1676,6 +1685,10 @@ static void binder_free_transaction(struct binder_transaction *t) t->buffer->transaction = NULL; binder_inner_proc_unlock(target_proc); } + + if (target_thread) + binder_thread_dec_tmpref(target_thread); + if (trace_binder_txn_latency_free_enabled()) binder_txn_latency_free(t); /* From 89b8cc948dce661af87527623b3a41cdd115e2f9 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 31 May 2026 22:29:24 +0900 Subject: [PATCH 0239/3297] rust_binder: use a u64 stride when cleaning up the offsets array commit 803c8a9502e9b97cd6ae937618ef4a8fd6274343 upstream. Allocation's Drop walks the offsets array (binder_size_t = u64 entries), cleaning up the objects, but it used usize instead of u64 for both the stride and the per-entry read. On 64-bit kernels (usize == u64) this is harmless, but on 32-bit kernels it walks the 8-byte entries in 4-byte steps, iterating an N-entry array 2N times, and reads the always-zero high word as offset 0, cleaning up the object at offset 0 N extra times. As a result the referenced node or handle ends up with a lower reference count than it actually has (a refcount over-decrement), and binder's reference accounting is corrupted; for example, the owner can be notified of a strong reference release (BR_RELEASE) even though references still remain. Change the stride to u64, and read each entry as a u64, narrowing it to usize with try_into(). On 32-bit ARM, when this over-decrement would drive a count below zero, the driver's existing refcount guard refuses it and fires: rust_binder: Failure: refcount underflow! Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Hyunwoo Kim Acked-by: Carlos Llamas Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/ahw3tFhLz9bMMJAO@v4bel Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index d9113e9b98b2b..a9475e136d76f 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -261,7 +261,7 @@ impl Drop for Allocation { if let Some(offsets) = info.offsets.clone() { let view = AllocationView::new(self, offsets.start); - for i in offsets.step_by(size_of::()) { + for i in offsets.step_by(size_of::()) { if view.cleanup_object(i).is_err() { pr_warn!("Error cleaning up object at offset {}\n", i) } @@ -419,7 +419,8 @@ impl<'a> AllocationView<'a> { } fn cleanup_object(&self, index_offset: usize) -> Result { - let offset = self.alloc.read(index_offset)?; + let offset = self.alloc.read::(index_offset)?; + let offset: usize = offset.try_into().map_err(|_| EINVAL)?; let header = self.read::(offset)?; match header.type_ { BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => { From 08e21d86d272243d54c6413cb19d1dcefe792272 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Thu, 25 Jun 2026 16:09:57 +0530 Subject: [PATCH 0240/3297] rust_binder: reject context manager self-transaction commit 6849cabfd30fb5727cfd31e8241e15801e17ebf9 upstream. Rust binder resolved handle 0 to the context manager node, but it does not reject the case where the caller owns the same node. The C binder driver rejects transactions from the context-manager process to handle 0 after resolving the target node. Match that behavior in Rust Binder by rejecting handle 0 transactions when the resolved context-manager node is owned by the calling process. This applies to both synchronous and oneway transactions because both paths resolve the target through Process::get_transaction_node(). Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Keshav Verma Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260625103957.730-1-iganschel@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 5e95aee95347d..b0e9516a5f7c0 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -873,7 +873,11 @@ impl Process { pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult { // When handle is zero, try to get the context manager. if handle == 0 { - Ok(self.ctx.get_manager_node(true)?) + let node_ref = self.ctx.get_manager_node(true)?; + if core::ptr::eq(self, &*node_ref.node.owner) { + return Err(EINVAL.into()); + } + Ok(node_ref) } else { Ok(self.get_node_from_handle(handle, true)?) } From 281335996ab21e0849047a9e20cf04aacce990ac Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Tue, 16 Jun 2026 02:47:43 +0530 Subject: [PATCH 0241/3297] rust_binder: synchronize Rust Binder stats with freeze commands commit eb1645bf10190e71f6f0316e37ff70755d719b53 upstream. Rust Binder stats use BC_COUNT and BR_COUNT to size the command and return counters, and use event string tables when printing debug statistics. The Binder protocol includes freeze-related commands and return codes, but the Rust Binder statistics code was not updated to cover them. As a result, those commands and return codes are not accounted for or printed by the stats debug output. Update the counts and event string tables so these commands and return codes are included in the debug statistics output. Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Cc: stable Acked-by: Carlos Llamas Reviewed-by: Alice Ryhl Signed-off-by: Keshav Verma Link: https://patch.msgid.link/20260615211743.734-1-iganschel@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.c | 7 ++++++- drivers/android/binder/stats.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder/rust_binder_events.c b/drivers/android/binder/rust_binder_events.c index 488b1470060cc..5792aa59cc825 100644 --- a/drivers/android/binder/rust_binder_events.c +++ b/drivers/android/binder/rust_binder_events.c @@ -28,6 +28,9 @@ const char * const binder_command_strings[] = { "BC_DEAD_BINDER_DONE", "BC_TRANSACTION_SG", "BC_REPLY_SG", + "BC_REQUEST_FREEZE_NOTIFICATION", + "BC_CLEAR_FREEZE_NOTIFICATION", + "BC_FREEZE_NOTIFICATION_DONE", }; const char * const binder_return_strings[] = { @@ -51,7 +54,9 @@ const char * const binder_return_strings[] = { "BR_FAILED_REPLY", "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", - "BR_TRANSACTION_PENDING_FROZEN" + "BR_TRANSACTION_PENDING_FROZEN", + "BR_FROZEN_BINDER", + "BR_CLEAR_FREEZE_NOTIFICATION_DONE", }; #define CREATE_TRACE_POINTS diff --git a/drivers/android/binder/stats.rs b/drivers/android/binder/stats.rs index a83ec111d2cb5..f7ee24dd387a9 100644 --- a/drivers/android/binder/stats.rs +++ b/drivers/android/binder/stats.rs @@ -8,8 +8,8 @@ use crate::defs::*; use core::sync::atomic::{AtomicU32, Ordering::Relaxed}; use kernel::{ioctl::_IOC_NR, seq_file::SeqFile, seq_print}; -const BC_COUNT: usize = _IOC_NR(BC_REPLY_SG) as usize + 1; -const BR_COUNT: usize = _IOC_NR(BR_TRANSACTION_PENDING_FROZEN) as usize + 1; +const BC_COUNT: usize = _IOC_NR(BC_FREEZE_NOTIFICATION_DONE) as usize + 1; +const BR_COUNT: usize = _IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) as usize + 1; pub(crate) static GLOBAL_STATS: BinderStats = BinderStats::new(); From 91b27f8172cdbf265240104772fd042a461a7767 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 3 Jul 2026 11:25:12 +0000 Subject: [PATCH 0242/3297] rust_binder: clear freeze listener on node removal commit bc4a9828897871ff3e5a1f8a1d346decbf4ee95e upstream. Generally userspace is supposed to explicitly clear freeze listeners before they drop the refcount on the node ref to zero, but there's nothing forcing that. Currently, in this scenario the freeze listener remains in the freeze_listeners rbtree and in the remote node's freeze listener list, even though the ref for which the listener is registered is gone. This could potentially lead to a memory leak due to a refcount cycle. Thus, remove the freeze listener in this scenario. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260703-remove-freeze-on-remove-node-v3-1-6e0c4547af46@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/freeze.rs | 11 +++++++++-- drivers/android/binder/node.rs | 10 ++++++---- drivers/android/binder/process.rs | 12 +++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 220de35ae85ac..e3b7aba2279f8 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -154,10 +154,17 @@ impl DeliverToRead for FreezeMessage { } impl FreezeListener { - pub(crate) fn on_process_exit(&self, proc: &Arc) { + /// Called when this freeze listener is cleared abnormally. + /// + /// This occurs either because the process exited or because the process dropped its last + /// refcount on the node ref without explicitly removing the freeze listener first. + /// + /// The returned `KVVec` is just a value that should be dropped outside of the lock. + pub(crate) fn on_process_cleanup(&self, proc: &Process) -> KVVec> { if !self.is_clearing { - self.node.remove_freeze_listener(proc); + return self.node.remove_freeze_listener(proc); } + KVVec::new() } } diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index c26d113ede966..b76ecff80f911 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -674,12 +674,13 @@ impl Node { } } - pub(crate) fn remove_freeze_listener(&self, p: &Arc) { - let _unused_capacity; + pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec> { let mut guard = self.owner.inner.lock(); let inner = self.inner.access_mut(&mut guard); let len = inner.freeze_list.len(); - inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p)); + inner + .freeze_list + .retain(|proc| !core::ptr::eq::(&**proc, p)); if len == inner.freeze_list.len() { pr_warn!( "Could not remove freeze listener for {}\n", @@ -687,8 +688,9 @@ impl Node { ); } if inner.freeze_list.is_empty() { - _unused_capacity = mem::take(&mut inner.freeze_list); + return mem::take(&mut inner.freeze_list); } + KVVec::new() } pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc] { diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index b0e9516a5f7c0..8ccc9ffbcd90e 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -919,6 +919,8 @@ impl Process { // To preserve original binder behaviour, we only fail requests where the manager tries to // increment references on itself. + let _to_free_freeze_listener; + let _to_free_freeze_listener_cleanup; let mut refs = self.node_refs.lock(); if let Some(info) = refs.by_handle.get_mut(&handle) { if info.node_ref().update(inc, strong) { @@ -934,6 +936,14 @@ impl Process { unsafe { info.node_ref2().node.remove_node_info(info) }; let id = info.node_ref().node.global_id(); + + if let Some(freeze) = *info.freeze() { + if let Some(fl) = refs.freeze_listeners.remove(&freeze) { + _to_free_freeze_listener_cleanup = fl.on_process_cleanup(&self); + _to_free_freeze_listener = fl; + } + } + refs.by_handle.remove(&handle); refs.by_node.remove(&id); } @@ -1348,7 +1358,7 @@ impl Process { // Clean up freeze listeners. let freeze_listeners = take(&mut self.node_refs.lock().freeze_listeners); for listener in freeze_listeners.values() { - listener.on_process_exit(&self); + listener.on_process_cleanup(&self); } drop(freeze_listeners); From f90586129cf9e1fbdb718ef602eea3f15dc1c31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E8=BF=9E=E5=8B=A4?= Date: Fri, 3 Jul 2026 17:40:33 +0300 Subject: [PATCH 0243/3297] usb: xhci: Fix sleep in atomic context in xhci_free_streams() commit 42c37c4b75d38b51d84f31a8e29427f5e06a7c2a upstream. When a USB device with active stream endpoints is disconnected, xhci_free_streams() is called from the hub_event workqueue to free the stream resources. It calls xhci_free_stream_info() while holding xhci->lock with irqs disabled. xhci_free_stream_info() invokes xhci_free_stream_ctx(), which calls dma_free_coherent() for large stream context arrays. dma_free_coherent() can sleep (e.g. via vunmap), triggering a BUG when called from atomic context. Call trace: dma_free_attrs+0x174/0x220 xhci_free_stream_info+0xd0/0x11c xhci_free_streams+0x278/0x37c usb_free_streams+0x98/0xc0 usb_unbind_interface+0x1b8/0x2f8 device_release_driver_internal+0x1d4/0x2cc device_release_driver+0x18/0x28 bus_remove_device+0x160/0x1a4 device_del+0x1ec/0x350 usb_disable_device+0x98/0x214 usb_disconnect+0xf0/0x35c hub_event+0xab4/0x19ec process_one_work+0x278/0x63c Fix this by saving the stream_info pointers and clearing the ep references under the lock, then calling xhci_free_stream_info() outside the lock where sleeping is allowed. Fixes: 8df75f42f8e6 ("USB: xhci: Add memory allocation for USB3 bulk streams.") Cc: stable Signed-off-by: Lianqin Hu Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260703144033.483286-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a8dee373a485e..a06758addbf82 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3774,6 +3774,7 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_virt_device *vdev; struct xhci_command *command; struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_stream_info *stream_info[EP_CTX_PER_DEV]; unsigned int ep_index; unsigned long flags; u32 changed_ep_bitmask; @@ -3834,10 +3835,15 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, if (ret < 0) return ret; + /* + * dma_free_coherent() called by xhci_free_stream_info() may sleep, + * so save stream_info pointers and clear references under lock, + * then free the memory outside lock. + */ spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < num_eps; i++) { ep_index = xhci_get_endpoint_index(&eps[i]->desc); - xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info); + stream_info[i] = vdev->eps[ep_index].stream_info; vdev->eps[ep_index].stream_info = NULL; /* FIXME Unset maxPstreams in endpoint context and * update deq ptr to point to normal string ring. @@ -3847,6 +3853,9 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, } spin_unlock_irqrestore(&xhci->lock, flags); + for (i = 0; i < num_eps; i++) + xhci_free_stream_info(xhci, stream_info[i]); + return 0; } From 99d00a9e35e311a91d258029d5bb584377296c34 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 3 Jul 2026 17:40:32 +0300 Subject: [PATCH 0244/3297] xhci: sideband: fix ring sg table pages leak commit 49f6e3c3ef19f04f6657ed8dce550e36c763abb8 upstream. xhci_ring_to_sgtable() allocates a temporary pages array and uses it to build the returned sg_table with sg_alloc_table_from_pages(). The error paths free the pages array, but the success path returns the sg_table without freeing it. This leaks the temporary array every time a sideband client gets an endpoint or event ring buffer. Free the pages array after sg_alloc_table_from_pages() succeeds. The returned sg_table has its own scatterlist entries and does not depend on the temporary array after construction. Fixes: de66754e9f80 ("xhci: sideband: add initial api to register a secondary interrupter entity") Cc: stable Signed-off-by: Xu Rao Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260703144033.483286-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-sideband.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index 6519736061376..ceca614d3a662 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -58,6 +58,8 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring) if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) goto err; + kvfree(pages); + /* * Save first segment dma address to sg dma_address field for the sideband * client to have access to the IOVA of the ring. From e5406c8fb71cd2f89a46300a746f6e7972e621e8 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Mon, 6 Jul 2026 23:53:12 +0900 Subject: [PATCH 0245/3297] usb: typec: tcpci_rt1711h: unregister TCPCI port with devres commit e8da46d99d3710106e7c44db14566bf9b57386b5 upstream. rt1711h_probe() registers the TCPCI port before requesting the interrupt and enabling alert interrupts. If either of those later steps fails, the probe function returns without unregistering the TCPCI port. The explicit unregister currently only happens from the remove callback. Register a devres action immediately after tcpci_register_port() succeeds, so tcpci_unregister_port() runs on later probe failures and on driver detach. Drop the remove callback to avoid unregistering the same port twice. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 302c570bf36e ("usb: typec: tcpci_rt1711h: avoid screaming irq causing boot hangs") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260706145312.37260-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_rt1711h.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 88c50b984e8a3..cd74cb9350fa7 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -330,6 +330,8 @@ static int rt1711h_check_revision(struct i2c_client *i2c, struct rt1711h_chip *c return ret; } +static void rt1711h_unregister_tcpci_port(void *tcpci); + static int rt1711h_probe(struct i2c_client *client) { int ret; @@ -381,6 +383,10 @@ static int rt1711h_probe(struct i2c_client *client) if (IS_ERR_OR_NULL(chip->tcpci)) return PTR_ERR(chip->tcpci); + ret = devm_add_action_or_reset(chip->dev, rt1711h_unregister_tcpci_port, chip->tcpci); + if (ret) + return ret; + ret = devm_request_threaded_irq(chip->dev, client->irq, NULL, rt1711h_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW, @@ -398,11 +404,9 @@ static int rt1711h_probe(struct i2c_client *client) return 0; } -static void rt1711h_remove(struct i2c_client *client) +static void rt1711h_unregister_tcpci_port(void *tcpci) { - struct rt1711h_chip *chip = i2c_get_clientdata(client); - - tcpci_unregister_port(chip->tcpci); + tcpci_unregister_port(tcpci); } static const struct rt1711h_chip_info rt1711h = { @@ -435,7 +439,6 @@ static struct i2c_driver rt1711h_i2c_driver = { .of_match_table = rt1711h_of_match, }, .probe = rt1711h_probe, - .remove = rt1711h_remove, .id_table = rt1711h_id, }; module_i2c_driver(rt1711h_i2c_driver); From d666c5aec822d42e8a26dce29eddfdf6d154b169 Mon Sep 17 00:00:00 2001 From: Ziyao Li Date: Sun, 12 Apr 2026 18:17:31 +0800 Subject: [PATCH 0246/3297] PCI: loongson: Override PCIe bridge supported speeds for Loongson-3C6000 series commit e373c789bac0ad73b472d8b44714df3bd18a4edf upstream. Older steppings of the Loongson-3C6000 series incorrectly report the supported link speeds on their PCIe bridges (device IDs 0x3c19, 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds from 2.5 GT/s up to 16 GT/s. As a result, since commit 774c71c52aa4 ("PCI/bwctrl: Enable only if more than one speed is supported"), bwctrl will be disabled if there's only one 2.5 GT/s value in vector 'supported_speeds'. Manually override the 'supported_speeds' field for affected PCIe bridges with those found on the upstream bus to correctly reflect the supported link speeds. Updating the speeds to reflect what the hardware actually supports avoids quirks in drivers consuming the speed information. This commit was originally found from AOSC OS[1]. Fixes: cd89edda4002 ("PCI: loongson: Add ACPI init support") Signed-off-by: Ayden Meng Signed-off-by: Mingcong Bai [Ziyao Li: move from drivers/pci/quirks.c to drivers/pci/controller/pci-loongson.c] Signed-off-by: Ziyao Li [Xi Ruoyao: Fixed falling through logic, added debug log, Fixes tag and rebased to 7.0-rc7] Signed-off-by: Xi Ruoyao Signed-off-by: Manivannan Sadhasivam [bhelgaas: commit log, https://lore.kernel.org/all/9d815df3b33a63223112b97440c01247935363c1.camel@xry111.site] Signed-off-by: Bjorn Helgaas Tested-by: Lain Fearyncess Yang Tested-by: Ayden Meng Tested-by: Mingcong Bai Reviewed-by: Huacai Chen Cc: stable@vger.kernel.org Link: https://github.com/AOSC-Tracking/linux/commit/4392f441363abdf6fa0a0433d73175a17f493454 Link: https://github.com/AOSC-Tracking/linux/pull/2 #1 Link: https://patch.msgid.link/20260412101731.107059-1-xry111@xry111.site Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-loongson.c | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index bc630ab8a2831..9609e6f50b981 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -176,6 +176,42 @@ static void loongson_pci_msi_quirk(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT5, loongson_pci_msi_quirk); +/* + * Older steppings of the Loongson-3C6000 series incorrectly report the + * supported link speeds on their PCIe bridges (device IDs 0x3c19, + * 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds + * from 2.5 GT/s up to 16 GT/s. + */ +static void loongson_pci_bridge_speed_quirk(struct pci_dev *pdev) +{ + u8 old_supported_speeds = pdev->supported_speeds; + + switch (pdev->bus->max_bus_speed) { + case PCIE_SPEED_16_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_16_0GB; + fallthrough; + case PCIE_SPEED_8_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_8_0GB; + fallthrough; + case PCIE_SPEED_5_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_5_0GB; + fallthrough; + case PCIE_SPEED_2_5GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_2_5GB; + break; + default: + pci_warn(pdev, "unexpected max bus speed"); + + return; + } + + if (pdev->supported_speeds != old_supported_speeds) + pci_info(pdev, "fixed up supported link speeds: 0x%x => 0x%x", + old_supported_speeds, pdev->supported_speeds); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c19, loongson_pci_bridge_speed_quirk); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c29, loongson_pci_bridge_speed_quirk); + static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; From 5e42a981887d24580c19bee1ba99a5292e572713 Mon Sep 17 00:00:00 2001 From: Mahesh Vaidya Date: Thu, 30 Apr 2026 13:43:29 -0700 Subject: [PATCH 0247/3297] PCI: altera: Do not dispose parent IRQ mapping commit 5ef4bac02189bee0b7c170e352d7a38e13fe9678 upstream. altera_pcie_irq_teardown() calls irq_dispose_mapping() on pcie->irq. However, pcie->irq is the parent IRQ returned by platform_get_irq(), not the mapping created by Altera INTx irq_domain. The Altera driver only sets the chained handler on the parent IRQ. It should detach that handler during teardown, but it should not dispose the parent IRQ mapping, which belongs to the parent interrupt controller's irq_domain. Drop irq_dispose_mapping(pcie->irq) from the teardown path. Note that during irqchip remove(), the child IRQs should've disposed. But since the chained handler itself is removed, there is no way the stale child IRQs (if exists) could fire. So it is safe here. Fixes: ec15c4d0d5d2 ("PCI: altera: Allow building as module") Signed-off-by: Mahesh Vaidya [mani: added a note about IRQ disposal] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Subhransu S. Prusty Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260430204330.3121003-2-mahesh.vaidya@altera.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pcie-altera.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3dbb7adc421c4..3d3519b8d88f0 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -868,7 +868,6 @@ static void altera_pcie_irq_teardown(struct altera_pcie *pcie) { irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); irq_domain_remove(pcie->irq_domain); - irq_dispose_mapping(pcie->irq); } static int altera_pcie_parse_dt(struct altera_pcie *pcie) From 09c43b7b7d29c6fadb27f32cdf7f3bb6598befa9 Mon Sep 17 00:00:00 2001 From: Mahesh Vaidya Date: Thu, 30 Apr 2026 13:43:30 -0700 Subject: [PATCH 0248/3297] PCI: altera: Fix resource leaks on probe failure commit 7a94138caeb27f3c49c1dbd93bf422098925bb28 upstream. The chained IRQ handler is set during probe, but is only removed during the driver remove(). If pci_host_probe() fails, the handler and INTx IRQ domain remain set even though the devm-managed host bridge storage containing struct altera_pcie will be released, leaving the handler with a stale data pointer. Interrupts are also enabled before pci_host_probe() is called. If probe fails after that point, the controller interrupt source should be disabled before the chained handler and INTx domain are removed. So set the chained handler only after the INTx domain has been created. Disable controller interrupts during IRQ teardown, and tear the IRQ setup down if pci_host_probe() fails. Fixes: c63aed7334c2 ("PCI: altera: Use pci_host_probe() to register host") Signed-off-by: Mahesh Vaidya [mani: commit log] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Subhransu S. Prusty Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260430204330.3121003-3-mahesh.vaidya@altera.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pcie-altera.c | 35 ++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3d3519b8d88f0..76f3823d96139 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -864,8 +864,23 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) return 0; } +static void altera_pcie_disable_irq(struct altera_pcie *pcie) +{ + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || + pcie->pcie_data->version == ALTERA_PCIE_V2) { + /* Disable all P2A interrupts */ + cra_writel(pcie, 0, P2A_INT_ENABLE); + } else if (pcie->pcie_data->version == ALTERA_PCIE_V3) { + /* Disable port-level interrupts (CFG_AER, etc.) */ + writel(0, pcie->hip_base + + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_enable_offset); + } +} + static void altera_pcie_irq_teardown(struct altera_pcie *pcie) { + altera_pcie_disable_irq(pcie); irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); irq_domain_remove(pcie->irq_domain); } @@ -890,7 +905,6 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) if (pcie->irq < 0) return pcie->irq; - irq_set_chained_handler_and_data(pcie->irq, pcie->pcie_data->ops->rp_isr, pcie); return 0; } @@ -1019,6 +1033,14 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } + /* + * The chained handler uses pcie->irq_domain, so set it only after the + * INTx domain has been created. + */ + irq_set_chained_handler_and_data(pcie->irq, + pcie->pcie_data->ops->rp_isr, + pcie); + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || pcie->pcie_data->version == ALTERA_PCIE_V2) { /* clear all interrupts */ @@ -1036,7 +1058,16 @@ static int altera_pcie_probe(struct platform_device *pdev) bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; - return pci_host_probe(bridge); + ret = pci_host_probe(bridge); + if (ret) + goto err_teardown_irq; + + return 0; + +err_teardown_irq: + altera_pcie_irq_teardown(pcie); + + return ret; } static void altera_pcie_remove(struct platform_device *pdev) From 9c698af5c2a12035d093244ca37e3c0971fd05e4 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 8 Dec 2025 19:24:29 +0000 Subject: [PATCH 0249/3297] PCI: Always lift 2.5GT/s restriction in PCIe failed link retraining commit 72780f7964684939d7d2f69c348876213b184484 upstream. Discard Vendor:Device ID matching in the PCIe failed link retraining quirk and ignore the link status for the removal of the 2.5GT/s speed clamp, whether applied by the quirk itself or the firmware earlier on. Revert to the original target link speed if this final link retraining has failed. This is so that link training noise in hot-plug scenarios does not make a link remain clamped to the 2.5GT/s speed where an event race has led the quirk to apply the speed clamp for one device, only to leave it in place for a subsequent device to be plugged in. Refer to the Link Capabilities register directly for the maximum link speed determination so as to streamline backporting. Fixes: a89c82249c37 ("PCI: Work around PCIe link training failures") Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas Tested-by: Alok Tiwari Cc: stable@vger.kernel.org # v6.5+ Link: https://patch.msgid.link/alpine.DEB.2.21.2512080331530.49654@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 51 ++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d32a47e81fcf3..f6001be94955e 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -79,11 +79,10 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) * Restrict the speed to 2.5GT/s then with the Target Link Speed field, * request a retrain and check the result. * - * If this turns out successful and we know by the Vendor:Device ID it is - * safe to do so, then lift the restriction, letting the devices negotiate - * a higher speed. Also check for a similar 2.5GT/s speed restriction the - * firmware may have already arranged and lift it with ports that already - * report their data link being up. + * If this turns out successful, or where a 2.5GT/s speed restriction has + * been previously arranged by the firmware and the port reports its link + * already being up, lift the restriction, in a hope it is safe to do so, + * letting the devices negotiate a higher speed. * * Otherwise revert the speed to the original setting and request a retrain * again to remove any residual state, ignoring the result as it's supposed @@ -94,51 +93,37 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) */ int pcie_failed_link_retrain(struct pci_dev *dev) { - static const struct pci_device_id ids[] = { - { PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */ - {} - }; - u16 lnksta, lnkctl2; + u16 lnksta, lnkctl2, oldlnkctl2; int ret = -ENOTTY; + u32 lnkcap; if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) || !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) return ret; pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) { - u16 oldlnkctl2; - pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n"); - - pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false); - if (ret) { - pci_info(dev, "retraining failed\n"); - pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), - true); - return ret; - } - - pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if (ret) + goto err; } pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); - - if ((lnksta & PCI_EXP_LNKSTA_DLLLA) && - (lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && - pci_match_id(ids, dev)) { - u32 lnkcap; - + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && + (lnkcap & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); - pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false); - if (ret) { - pci_info(dev, "retraining failed\n"); - return ret; - } + if (ret) + goto err; } + return ret; +err: + pci_info(dev, "retraining failed\n"); + pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), true); return ret; } From 1d2e66a4bc0dd6c1f6f2f28247450e4d4ce7f5ba Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Tue, 14 Apr 2026 13:47:30 +0530 Subject: [PATCH 0250/3297] PCI: host-common: Request bus reassignment when not probe-only commit fda8749ba73638f5bbca3ffb39bc6861eb3b23fa upstream. pci_host_common_init() is used by several generic ECAM host drivers. After PCI core changes around pci_flags and preserve_config, these hosts no longer opted into full bus number reassignment the way they did before, which broke enumeration of devices on a Marvell CN106XX board. When PCI_PROBE_ONLY is not set, add PCI_REASSIGN_ALL_BUS so pci_scan_bridge_extend() takes the reassignment path: bus numbers can be assigned from firmware EA data (e.g. pci_ea_fixed_busnrs()). Skip the flag in probe-only mode so existing assignments are not overridden. Fixes: 7246a4520b4b ("PCI: Use preserve_config in place of pci_flags") Closes: https://lore.kernel.org/all/abkqm_LCd9zAM8cW@rkannoth-OptiPlex-7090/ Signed-off-by: Ratheesh Kannoth [mani: added stable tag] Signed-off-by: Manivannan Sadhasivam [bhelgaas: add problem report link] Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Cc: Vidya Sagar Link: https://patch.msgid.link/20260414081730.3864372-1-rkannoth@marvell.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pci-host-common.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 810d1c8de24e9..422c2c4600173 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -72,6 +72,10 @@ int pci_host_common_init(struct platform_device *pdev, if (IS_ERR(cfg)) return PTR_ERR(cfg); + /* Do not reassign bus numbers if probe only */ + if (!pci_has_flag(PCI_PROBE_ONLY)) + pci_add_flags(PCI_REASSIGN_ALL_BUS); + bridge->sysdata = cfg; bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->enable_device = ops->enable_device; From 69416a5308675ab6d4e22a83f14048a5cc02e9bb Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 19 Mar 2026 17:08:44 +0800 Subject: [PATCH 0251/3297] PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling commit aad953fb4eed0df5486cd54ccad80ac197678e01 upstream. The IMX6SX_GPR12_PCIE_TEST_POWERDOWN bit does not control the PCIe reference clock on i.MX6SX. Instead, it is part of i.MX6SX PCIe core reset sequence. Move the IMX6SX_GPR12_PCIE_TEST_POWERDOWN assertion/deassertion into the core reset functions to properly reflect its purpose. Remove the .enable_ref_clk() callback for i.MX6SX since it was incorrectly manipulating this bit. Fixes: e3c06cd063d6 ("PCI: imx6: Add initial imx6sx support") Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260319090844.444987-1-hongxing.zhu@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pci-imx6.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 9083658a4287f..d7de9790fe9fd 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -659,14 +659,6 @@ static int imx_pcie_attach_pd(struct device *dev) return 0; } -static int imx6sx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) -{ - regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_TEST_POWERDOWN, - enable ? 0 : IMX6SX_GPR12_PCIE_TEST_POWERDOWN); - return 0; -} - static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { if (enable) { @@ -765,6 +757,9 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) if (assert) regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_TEST_POWERDOWN); /* Force PCIe PHY reset */ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET, @@ -1852,7 +1847,6 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .init_phy = imx6sx_pcie_init_phy, - .enable_ref_clk = imx6sx_pcie_enable_ref_clk, .core_reset = imx6sx_pcie_core_reset, .ops = &imx_pcie_host_ops, }, From 6e6a529d6f779413379b4404c9ef6a36c0337225 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 21 May 2026 23:16:17 +0530 Subject: [PATCH 0252/3297] PCI: mediatek: Fix IRQ domain leak when port fails to enable commit f865a57896bd92d7662eb2818d8f48872e2cbbc7 upstream. When mtk_pcie_enable_port() fails, mtk_pcie_port_free() removes the port from pcie->ports and frees the port structure. However, the IRQ domains set up earlier by mtk_pcie_init_irq_domain() are never freed. Fix this by refactoring mtk_pcie_irq_teardown() into a per-port helper, mtk_pcie_irq_teardown_port(), and calling it from mtk_pcie_setup() when mtk_pcie_enable_port() fails. Since the IRQ teardown must only happen in the probe error path (during resume, child devices may have active MSI mappings and the NOIRQ context prohibits sleeping locks), mtk_pcie_enable_port() is changed to return an error code so callers can distinguish the two paths and act accordingly. This issue was reported by Sashiko while reviewing the EcoNet EN7528 SoC support series. Fixes: b099631df160 ("PCI: mediatek: Add controller support for MT2712 and MT7622") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org # 5.10 Cc: Caleb James DeLisle Link: https://patch.msgid.link/20260521174617.17692-1-mani@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/pcie-mediatek.c | 63 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index e0bf667c2b4c6..d779baf1ae6d8 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -519,23 +519,27 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) writel(val, port->base + PCIE_INT_MASK); } -static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) +static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port) { - struct mtk_pcie_port *port, *tmp; + irq_set_chained_handler_and_data(port->irq, NULL, NULL); - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { - irq_set_chained_handler_and_data(port->irq, NULL, NULL); + if (port->irq_domain) + irq_domain_remove(port->irq_domain); - if (port->irq_domain) - irq_domain_remove(port->irq_domain); + if (IS_ENABLED(CONFIG_PCI_MSI)) { + if (port->inner_domain) + irq_domain_remove(port->inner_domain); + } - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (port->inner_domain) - irq_domain_remove(port->inner_domain); - } + irq_dispose_mapping(port->irq); +} - irq_dispose_mapping(port->irq); - } +static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) +{ + struct mtk_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + mtk_pcie_irq_teardown_port(port); } static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, @@ -823,7 +827,7 @@ static int mtk_pcie_startup_port(struct mtk_pcie_port *port) return 0; } -static void mtk_pcie_enable_port(struct mtk_pcie_port *port) +static int mtk_pcie_enable_port(struct mtk_pcie_port *port) { struct mtk_pcie *pcie = port->pcie; struct device *dev = pcie->dev; @@ -832,7 +836,7 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) err = clk_prepare_enable(port->sys_ck); if (err) { dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot); - goto err_sys_clk; + return err; } err = clk_prepare_enable(port->ahb_ck); @@ -880,11 +884,15 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) goto err_phy_on; } - if (!pcie->soc->startup(port)) - return; + err = pcie->soc->startup(port); + if (err) { + dev_info(dev, "Port%d link down\n", port->slot); + goto err_soc_startup; + } - dev_info(dev, "Port%d link down\n", port->slot); + return 0; +err_soc_startup: phy_power_off(port->phy); err_phy_on: phy_exit(port->phy); @@ -900,8 +908,8 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) clk_disable_unprepare(port->ahb_ck); err_ahb_clk: clk_disable_unprepare(port->sys_ck); -err_sys_clk: - mtk_pcie_port_free(port); + + return err; } static int mtk_pcie_parse_port(struct mtk_pcie *pcie, @@ -1067,8 +1075,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) return err; /* enable each port, and then check link status */ - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) { + mtk_pcie_irq_teardown_port(port); + mtk_pcie_port_free(port); + } + } /* power down PCIe subsys if slots are all empty (link down) */ if (list_empty(&pcie->ports)) @@ -1167,14 +1180,18 @@ static int mtk_pcie_resume_noirq(struct device *dev) { struct mtk_pcie *pcie = dev_get_drvdata(dev); struct mtk_pcie_port *port, *tmp; + int err; if (list_empty(&pcie->ports)) return 0; clk_prepare_enable(pcie->free_ck); - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) + mtk_pcie_port_free(port); + } /* In case of EP was removed while system suspend. */ if (list_empty(&pcie->ports)) From 1115680bca1d748ce887ec4b0ad1012714231479 Mon Sep 17 00:00:00 2001 From: Yadu M G Date: Thu, 4 Jun 2026 17:54:18 +0530 Subject: [PATCH 0253/3297] PCI: qcom: Initialize DWC MSI lock for firmware-managed ECAM hosts commit e0779713a1e2f891aeec53e629dbbd33f423c629 upstream. A lockdep warning is observed during boot on a Qcom firmware-managed platform: INFO: trying to register non-static key. The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator. ... Call trace: register_lock_class+0x128/0x4d8 __lock_acquire+0x110/0x1db0 lock_acquire+0x278/0x3d8 _raw_spin_lock_irq+0x6c/0xc0 dw_pcie_irq_domain_alloc+0x48/0x190 irq_domain_alloc_irqs_parent+0x2c/0x48 msi_domain_alloc+0x90/0x160 ... dw_pcie_irq_domain_alloc() takes pp->lock while allocating MSI interrupts. pp->lock is normally initialized by dw_pcie_host_init(), but Qcom firmware-managed hosts use the ECAM init path instead: pci_host_common_ecam_create() pci_ecam_create() qcom_pcie_ecam_host_init() dw_pcie_msi_host_init() dw_pcie_allocate_domains() That path constructs a fresh struct dw_pcie_rp and calls dw_pcie_msi_host_init() directly, without going through dw_pcie_host_init(). As a result, pp->lock was not initialized, which triggers the warning. Initialize pp->lock in qcom_pcie_ecam_host_init() before registering the MSI domains so the firmware-managed ECAM path matches the normal DWC host initialization sequence. Fixes: 7d944c0f1469 ("PCI: qcom: Add support for Qualcomm SA8255p based PCIe Root Complex") Signed-off-by: Yadu M G [mani: added fixes tag and CCed stable] Signed-off-by: Manivannan Sadhasivam Cc: stable@kernel.org Link: https://patch.msgid.link/20260604122418.727274-1-yadu.mg@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pcie-qcom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 789cc0e3c10da..a70911a956753 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1626,6 +1626,12 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg) pci->dbi_base = cfg->win; pp->num_vectors = MSI_DEF_NUM_VECTORS; + /* + * dw_pcie_msi_host_init() is called directly here, bypassing + * dw_pcie_host_init() where pp->lock is normally initialized. + */ + raw_spin_lock_init(&pp->lock); + ret = dw_pcie_msi_host_init(pp); if (ret) return ret; From b77524621250407386f44c6eea7e5e4619ada1ce Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Fri, 17 Apr 2026 15:24:37 +0200 Subject: [PATCH 0254/3297] PCI/IOV: Skip VF Resizable BAR restore on read error commit f34f1712229d71ce4286440fef12526fd4590b37 upstream. sriov_restore_vf_rebar_state() uses the VF Resizable BAR Control register to decide how many VF BARs to restore (nbars) and which VF BAR each iteration addresses (bar_idx). bar_idx indexes into dev->sriov->barsz[], which has only PCI_SRIOV_NUM_BARS (6) entries. When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7. The barsz[] access then goes out of bounds. UBSAN reports this as: UBSAN: array-index-out-of-bounds in drivers/pci/iov.c:948:51 index 7 is out of range for type 'resource_size_t [6]' Observed on an NVIDIA RTX PRO 1000 GPU (GB207GLM) that stopped responding during a failed GC6 power state exit. The subsequent pci_restore_state() invoked sriov_restore_vf_rebar_state() while config reads returned 0xffffffff, triggering the splat. Bail out if any VF Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further VF BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless. This mirrors the guard in pci_restore_rebar_state(). Fixes: 5a8f77e24a30 ("PCI/IOV: Restore VF resizable BAR state after reset") Signed-off-by: Marco Nenciarini Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/44a4ae53ec2825816b816c85cd378430d9a95cc6.1776429882.git.mnencia@kcore.it Signed-off-by: Greg Kroah-Hartman --- drivers/pci/iov.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index c6dc1b44bf602..8bda4e202f56d 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -938,12 +938,18 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev) return; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { int bar_idx, size; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl); size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]); ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE; From 657646c08c94ef7b9dbe468fe7828032216f9841 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 25 Jun 2026 19:21:39 +0100 Subject: [PATCH 0255/3297] tcp: restore RCU grace period in tcp_ao_destroy_sock commit 8bc4d43bccbd60efe85d0a44d5bf41762f2f0c30 upstream. Commit 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") removed the call_rcu() callback from tcp_ao_destroy_sock(), arguing that "the destruction of info/keys is delayed until the socket destructor" and therefore "no one can discover it anymore". That argument does not hold for the call site in tcp_connect() (net/ipv4/tcp_output.c:4327-4332). At that point the socket is in TCP_SYN_SENT, has already been inserted into the inet ehash by inet_hash_connect() in tcp_v4_connect(), and is therefore very much discoverable: any softirq running tcp_v4_rcv() on another CPU can take the socket out of the ehash, walk into tcp_inbound_hash(), and load tp->ao_info via implicit RCU before bh_lock_sock_nested() is taken on the destroying CPU. The reader path then enters __tcp_ao_do_lookup() (net/ipv4/tcp_ao.c:208) which re-loads tp->ao_info via rcu_dereference_check(); the re-load can still observe the (about-to-be-freed) pointer because there is no synchronize_rcu() between rcu_assign_pointer(tp->ao_info, NULL) and tcp_ao_info_free() in tcp_ao_destroy_sock(). The captured pointer is then walked at line 223: hlist_for_each_entry_rcu(key, &ao->head, node, ...) The writer's synchronous kfree() is free to complete between the line 218 re-fetch and the line 223 hlist iteration. The slab is reused (or simply LIST_POISON1-stamped if not yet reused) and the iteration walks attacker-controlled or poison memory in softirq context. Reproducer (no debug shim, stock x86_64 v7.1-rc2 SMP+KASAN, QEMU+KVM): an unprivileged uid=1000 process inside CLONE_NEWUSER|CLONE_NEWNET installs TCP_MD5SIG + TCP_AO_ADD_KEY on a TCP socket, sprays forged TCP-AO segments toward its eventual 4-tuple via raw sockets, then calls connect(). The md5-wins reconciliation in tcp_connect() fires tcp_ao_destroy_sock(); the softirq backlog reader on the loopback NAPI path crashes on the freed ao->head.first walk: Oops: general protection fault, probably for non-canonical address 0xfbd59c000000002f KASAN: maybe wild-memory-access in range [0xdead000000000178-0xdead00000000017f] CPU: 0 UID: 1000 PID: 100 Comm: repro_userns RIP: 0010:__tcp_ao_do_lookup+0x107/0x1c0 Call Trace: __tcp_ao_do_lookup+0x107/0x1c0 tcp_ao_inbound_lookup.constprop.0+0x12a/0x200 tcp_inbound_ao_hash+0x5ea/0x1520 tcp_inbound_hash+0x7ce/0x1240 tcp_v4_rcv+0x1e7a/0x3e10 ... Restore the RCU grace period: re-add struct rcu_head to tcp_ao_info and replace the synchronous tcp_ao_info_free() with a call_rcu() callback. Readers that captured tp->ao_info before rcu_assign_pointer NULLed it now see the object remain valid until rcu_read_unlock(). With the patch applied the reproducer runs cleanly for 2000 iterations on the same kernel build. Fixes: 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") Cc: stable@vger.kernel.org # v6.18+ Reviewed-by: Dmitry Safonov Signed-off-by: Michael Bommarito Reviewed-by: Eric Dumazet Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-1-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/net/tcp_ao.h | 1 + net/ipv4/tcp_ao.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 1e9e27d6e06ba..df655ce6987d3 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -130,6 +130,7 @@ struct tcp_ao_info { u32 snd_sne; u32 rcv_sne; refcount_t refcnt; /* Protects twsk destruction */ + struct rcu_head rcu; }; #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 46eb5b4683bb5..a7b1fe194fb1e 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -270,8 +270,9 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head) kfree_sensitive(key); } -static void tcp_ao_info_free(struct tcp_ao_info *ao) +static void tcp_ao_info_free_rcu(struct rcu_head *head) { + struct tcp_ao_info *ao = container_of(head, struct tcp_ao_info, rcu); struct tcp_ao_key *key; struct hlist_node *n; @@ -311,7 +312,7 @@ void tcp_ao_destroy_sock(struct sock *sk, bool twsk) if (!twsk) tcp_ao_sk_omem_free(sk, ao); - tcp_ao_info_free(ao); + call_rcu(&ao->rcu, tcp_ao_info_free_rcu); } void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) From 9c8f31eaae6140ecadec0c07320498a944556de2 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 23 Jun 2026 06:58:31 -0700 Subject: [PATCH 0256/3297] mm/damon/ops-common: handle extreme intervals in damon_hot_score() commit 35d4a3cf70a855b50e53189ac2f8463e20a02046 upstream. Fix three issues in damon_hot_score() that comes from wrong handling of extreme (zero or too high) monitoring intervals user setup. When the user sets sampling interval zero, damon_max_nr_accesses(), which is called from damon_hot_score(), causes a divide-by-zero. Needless to say, it is a problem. When the user sets the aggregation interval zero, the function returns zero. It is wrong, since the real maximum nr_acceses in the setup should be one. Worse yet, it can cause another divide-by-zero from its caller, damon_hot_score(), since it uses damon_max_nr_accesses() return value as a denominator. When the user sets the aggregation interval very high, damon_hot_score() could return a value out of [0, DAMOS_MAX_SCORE] range. Since the return value is used as an index to the regions_score_histogram array, which is DAMOS_MAX_SCORE+1 size, it causes out of bounds array access. The issues can be relatively easily reproduced like below. The sysfs write permission is required, though. # ./damo start --damos_action lru_prio --damos_quota_space 100M \ --damos_quota_interval 1s # cd /sys/kernel/mm/damon/admin/kdamonds/0 # echo 0 > contexts/0/monitoring_attrs/intervals/sample_us # echo 0 > contexts/0/monitoring_attrs/intervals/aggr_us # echo commit > state # dmesg [...] [ 131.329762] Oops: divide error: 0000 [#1] SMP NOPTI [...] [ 131.336089] RIP: 0010:damon_hot_score+0x27/0xd0 [...] Fix the divide-by-zero intervals problems by explicitly handling the zero intervals in damon_max_nr_accesses(). Fix the out-of-bound array access by applying [0, DAMOS_MAX_SCORE] bounds before returning from damon_hot_score(). The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260623135834.67189-1-sj@kernel.org Link: https://lore.kernel.org/20260619202459.145010-1-sj@kernel.org [1] Fixes: 198f0f4c58b9 ("mm/damon/vaddr,paddr: support pageout prioritization") Signed-off-by: SeongJae Park Cc: # 5.16.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/damon.h | 8 ++++++-- mm/damon/ops-common.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index d902381950908..bb4dd13984d39 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -939,9 +939,13 @@ static inline bool damon_target_has_pid(const struct damon_ctx *ctx) static inline unsigned int damon_max_nr_accesses(const struct damon_attrs *attrs) { - /* {aggr,sample}_interval are unsigned long, hence could overflow */ - return min(attrs->aggr_interval / attrs->sample_interval, + unsigned long sample_interval; + unsigned long max_nr_accesses; + + sample_interval = attrs->sample_interval ? : 1; + max_nr_accesses = min(attrs->aggr_interval / sample_interval, (unsigned long)UINT_MAX); + return max_nr_accesses ? : 1; } diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 37dba4276d44e..512ea62e29abd 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -128,6 +128,7 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r, * Transform it to fit in [0, DAMOS_MAX_SCORE] */ hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE; + hotness = max(min(hotness, DAMOS_MAX_SCORE), 0); return hotness; } From 81d54c766337b923eec26da0a13406760b091093 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 24 Jun 2026 18:00:06 -0700 Subject: [PATCH 0257/3297] netfilter: ipset: fix race between dump and ip_set_list resize commit 7cd9103283b26b917360ec99d7d2f2d761bcf1ab upstream. The release path of ip_set_dump_do() and ip_set_dump_done() read inst->ip_set_list via ip_set_ref_netlink(), a plain rcu_dereference_raw() of the array pointer. These run from netlink_recvmsg() without the nfnl mutex and without an RCU read-side critical section. A concurrent ip_set_create() can grow the array: it publishes the new array, calls synchronize_net() and then kvfree()s the old one. Since the dump paths read the array outside any RCU reader, synchronize_net() does not wait for them and the old array can be freed while they still index into it, causing a use-after-free. The dumped set itself stays pinned via set->ref_netlink, so only the array load needs protecting. Take rcu_read_lock() around it, matching ip_set_get_byname() and __ip_set_put_byindex(). BUG: KASAN: slab-use-after-free in ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697) Read of size 8 at addr ffff88800b5c4018 by task exploit/150 Call Trace: ... kasan_report (mm/kasan/report.c:595) ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697) netlink_dump (net/netlink/af_netlink.c:2325) netlink_recvmsg (net/netlink/af_netlink.c:1976) sock_recvmsg (net/socket.c:1159) __sys_recvfrom (net/socket.c:2315) ... Oops: general protection fault, probably for non-canonical address ... KASAN NOPTI KASAN: maybe wild-memory-access in range [0x02d6...d0-0x02d6...d7] RIP: 0010:ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1698) Kernel panic - not syncing: Fatal exception Fixes: 8a02bdd50b2e ("netfilter: ipset: Fix calling ip_set() macro at dumping") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Acked-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipset/ip_set_core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index a4e1d7951b2c6..94157104d85a9 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callback *cb) struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX]; - struct ip_set *set = ip_set_ref_netlink(inst, index); + struct ip_set *set; + + rcu_read_lock(); + set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); @@ -1685,7 +1689,9 @@ ip_set_dump_do(struct sk_buff *skb, struct netlink_callback *cb) release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[IPSET_CB_ARG0]) { + rcu_read_lock(); set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); pr_debug("release set %s\n", set->name); From 075bc3c779e1ea7294afabdcb7e0a49536959b28 Mon Sep 17 00:00:00 2001 From: Ammar Faizi Date: Sun, 15 Mar 2026 21:18:08 +0700 Subject: [PATCH 0258/3297] virtio_pci: fix vq info pointer lookup via wrong index commit f7d380fb525c13bdd114369a1979c80c346e6abc upstream. Unbinding a virtio balloon device: echo virtio0 > /sys/bus/virtio/drivers/virtio_balloon/unbind triggers a NULL pointer dereference. The dmesg says: BUG: kernel NULL pointer dereference, address: 0000000000000008 [...] RIP: 0010:__list_del_entry_valid_or_report+0x5/0xf0 Call Trace: vp_del_vqs+0x121/0x230 remove_common+0x135/0x150 virtballoon_remove+0xee/0x100 virtio_dev_remove+0x3b/0x80 device_release_driver_internal+0x187/0x2c0 unbind_store+0xb9/0xe0 kernfs_fop_write_iter.llvm.11660790530567441834+0xf6/0x180 vfs_write+0x2a9/0x3b0 ksys_write+0x5c/0xd0 do_syscall_64+0x54/0x230 entry_SYSCALL_64_after_hwframe+0x29/0x31 [...] The virtio_balloon device registers 5 queues (inflate, deflate, stats, free_page, reporting) but only the first two are unconditional. The stats, free_page and reporting queues are each conditional on their respective feature bits. When any of these features are absent, the corresponding vqs_info entry has name == NULL, creating holes in the array. The root cause is an indexing mismatch introduced when vq info storage was changed to be passed as an argument. vp_find_vqs_msix() and vp_find_vqs_intx() store the info pointer at vp_dev->vqs[i], where 'i' is the caller's sparse array index. However, the virtqueue itself gets vq->index assigned from queue_idx, a dense index that skips NULL entries. When holes exist, 'i' and queue_idx diverge. Later, vp_del_vqs() looks up info via vp_dev->vqs[vq->index] using the dense index into the sparsely-populated array, and hits NULL. Fix this by storing info at vp_dev->vqs[queue_idx] instead of vp_dev->vqs[i], so the store index matches the lookup index (vq->index). Apply the fix to both the MSIX and INTX paths. Cc: Yichun Zhang Cc: Jiri Pirko Cc: stable@vger.kernel.org # v6.11+ Tested-by: Yuka Fixes: 89a1c435aec2 ("virtio_pci: pass vq info as an argument to vp_setup_vq()") Signed-off-by: Ammar Faizi Message-Id: <20260315141808.547081-1-ammarfaizi2@openresty.com> Signed-off-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_pci_common.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index d6d79af44569b..30c6627a5ce4d 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -426,10 +426,11 @@ static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs, vqs[i] = NULL; continue; } - vqs[i] = vp_find_one_vq_msix(vdev, queue_idx++, vqi->callback, + vqs[i] = vp_find_one_vq_msix(vdev, queue_idx, vqi->callback, vqi->name, vqi->ctx, false, &allocated_vectors, vector_policy, - &vp_dev->vqs[i]); + &vp_dev->vqs[queue_idx]); + queue_idx++; if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto error_find; @@ -488,9 +489,10 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs, vqs[i] = NULL; continue; } - vqs[i] = vp_setup_vq(vdev, queue_idx++, vqi->callback, + vqs[i] = vp_setup_vq(vdev, queue_idx, vqi->callback, vqi->name, vqi->ctx, - VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[i]); + VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[queue_idx]); + queue_idx++; if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto out_del_vqs; From 65e93ec592f5b1eb0e9dec47d44011c3fd4938e6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 27 Apr 2026 16:37:10 +0200 Subject: [PATCH 0259/3297] virtio-mmio: fix device release warning on module unload commit c687bc35694698ec4c7f92bf929c3d659f0cecb8 upstream. Driver core expects devices to be allocated dynamically and complains loudly when a device that lacks a release function is freed. Use __root_device_register() to allocate and register the root device instead of open coding using a static device. Note that root_device_register(), which also creates a link to the module, cannot be used as the device is registered when parsing the module parameters which happens before the module kobject has been set up. Fixes: 81a054ce0b46 ("virtio-mmio: Devices parameter parsing") Cc: stable@vger.kernel.org # 3.5 Cc: Pawel Moll Signed-off-by: Johan Hovold Signed-off-by: Michael S. Tsirkin Message-ID: <20260427143710.14702-1-johan@kernel.org> Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_mmio.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index b152a1eca05ad..4832c9835dff9 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -662,9 +662,7 @@ static void virtio_mmio_remove(struct platform_device *pdev) #if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES) -static struct device vm_cmdline_parent = { - .init_name = "virtio-mmio-cmdline", -}; +static struct device *vm_cmdline_parent; static int vm_cmdline_parent_registered; static int vm_cmdline_id; @@ -672,7 +670,6 @@ static int vm_cmdline_id; static int vm_cmdline_set(const char *device, const struct kernel_param *kp) { - int err; struct resource resources[2] = {}; char *str; long long base, size; @@ -704,11 +701,10 @@ static int vm_cmdline_set(const char *device, resources[1].start = resources[1].end = irq; if (!vm_cmdline_parent_registered) { - err = device_register(&vm_cmdline_parent); - if (err) { - put_device(&vm_cmdline_parent); + vm_cmdline_parent = __root_device_register("virtio-mmio-cmdline", NULL); + if (IS_ERR(vm_cmdline_parent)) { pr_err("Failed to register parent device!\n"); - return err; + return PTR_ERR(vm_cmdline_parent); } vm_cmdline_parent_registered = 1; } @@ -719,7 +715,7 @@ static int vm_cmdline_set(const char *device, (unsigned long long)resources[0].end, (int)resources[1].start); - pdev = platform_device_register_resndata(&vm_cmdline_parent, + pdev = platform_device_register_resndata(vm_cmdline_parent, "virtio-mmio", vm_cmdline_id++, resources, ARRAY_SIZE(resources), NULL, 0); @@ -743,8 +739,12 @@ static int vm_cmdline_get_device(struct device *dev, void *data) static int vm_cmdline_get(char *buffer, const struct kernel_param *kp) { buffer[0] = '\0'; - device_for_each_child(&vm_cmdline_parent, buffer, - vm_cmdline_get_device); + + if (vm_cmdline_parent_registered) { + device_for_each_child(vm_cmdline_parent, buffer, + vm_cmdline_get_device); + } + return strlen(buffer) + 1; } @@ -766,9 +766,9 @@ static int vm_unregister_cmdline_device(struct device *dev, static void vm_unregister_cmdline_devices(void) { if (vm_cmdline_parent_registered) { - device_for_each_child(&vm_cmdline_parent, NULL, + device_for_each_child(vm_cmdline_parent, NULL, vm_unregister_cmdline_device); - device_unregister(&vm_cmdline_parent); + root_device_unregister(vm_cmdline_parent); vm_cmdline_parent_registered = 0; } } From 285e17c44e3873a73460f294acbd64018ff64385 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 31 May 2026 10:22:51 -0400 Subject: [PATCH 0260/3297] hwrng: virtio: clamp device-reported used.len at copy_data() commit e3046eeada299f917a8ad883af4434bfb86556b1 upstream. random_recv_done() stores the device-reported used.len directly into vi->data_avail. copy_data() then indexes vi->data[] using vi->data_idx (advanced by previous copy_data() calls) and issues a memcpy() without re-validating either value against the posted buffer size sizeof(vi->data) (SMP_CACHE_BYTES bytes, typically 32 or 64). A malicious or buggy virtio-rng backend can set used.len beyond sizeof(vi->data), steering the memcpy() past the end of the inline array into adjacent kmalloc-1k slab bytes. hwrng_fillfn() mixes those bytes into the guest RNG, and guest root can also observe them directly via /dev/hwrng. Concrete impact is inside the guest: - Memory-safety / hardening: any virtio-rng backend that over-reports used.len causes the driver to read past vi->data into unrelated slab contents. hwrng_fillfn() is a kernel thread that runs as soon as the device is probed; no guest userspace interaction is required to first-trigger the OOB. - Cross-boundary leak (confidential-compute threat model): a malicious hypervisor cooperating with a malicious or compromised guest root userspace can use /dev/hwrng as a leak channel for guest-kernel heap data. The host sets a large used.len, guest root reads /dev/hwrng, and the returned bytes contain guest kernel slab contents that were adjacent to vi->data. In practice, confidential-compute guests (SEV-SNP, TDX) usually disable virtio-rng entirely, so this path is narrow, but the fix is still worth carrying because the underlying memory-safety bug contaminates the guest RNG on any host. KASAN confirms the OOB on a 7.1-rc4 guest whose virtio-rng backend has been patched to report used.len = 0x10000: BUG: KASAN: slab-out-of-bounds in virtio_read+0x394/0x5d0 Read of size 64 at addr ffff88800ae0ba20 by task hwrng/52 Call Trace: __asan_memcpy+0x23/0x60 virtio_read+0x394/0x5d0 hwrng_fillfn+0xb2/0x470 kthread+0x2cc/0x3a0 Allocated by task 1: probe_common+0xa5/0x660 virtio_dev_probe+0x549/0xbc0 The buggy address belongs to the object at ffff88800ae0b800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes to the right of allocated 544-byte region [ffff88800ae0b800, ffff88800ae0ba20) Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"), which hardened usb9pfs_rx_complete() against unchecked device-reported length in the USB 9p transport. With the clamp at point of use and array_index_nospec() in place, the same harness boots cleanly: copy_data() returns zero for the bogus report, the device-supplied bytes after data_idx are discarded, and the driver issues a fresh request. Fixes: f7f510ec1957 ("virtio: An entropy device, as suggested by hpa.") Cc: stable@vger.kernel.org Suggested-by: Michael S. Tsirkin Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael S. Tsirkin Message-ID: <20260531142251.2792061-1-michael.bommarito@gmail.com> Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/virtio-rng.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index dd998f4fe4f23..2933f8cb769e4 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -66,8 +67,26 @@ static void request_entropy(struct virtrng_info *vi) static unsigned int copy_data(struct virtrng_info *vi, void *buf, unsigned int size) { - size = min_t(unsigned int, size, vi->data_avail); - memcpy(buf, vi->data + vi->data_idx, size); + unsigned int idx, avail; + + /* + * vi->data_avail was set from the device-reported used.len and + * vi->data_idx was advanced by previous copy_data() calls. A + * malicious or buggy virtio-rng backend can drive either past + * sizeof(vi->data). Clamp at point of use and harden the index + * with array_index_nospec() so the memcpy() below cannot be + * steered into adjacent slab memory, including under + * speculation. + */ + avail = min_t(unsigned int, vi->data_avail, sizeof(vi->data)); + if (vi->data_idx >= avail) { + vi->data_avail = 0; + request_entropy(vi); + return 0; + } + size = min_t(unsigned int, size, avail - vi->data_idx); + idx = array_index_nospec(vi->data_idx, sizeof(vi->data)); + memcpy(buf, vi->data + idx, size); vi->data_idx += size; vi->data_avail -= size; if (vi->data_avail == 0) From 2a52d55c86a429dac47886b8424e67f90b001e67 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 9 Jun 2026 13:37:36 -0400 Subject: [PATCH 0261/3297] USB: chaoskey: Fix slab-use-after-free in chaoskey_release() commit abf76d3239dee97b66e7241ad04811f1ce562e28 upstream. The chaoskey driver has a use-after-free bug in its release routine. If the user closes the device file after the USB device has been unplugged, a debugging log statement will try to access the usb_interface structure after it has been deallocated: BUG: KASAN: slab-use-after-free in dev_driver_string (drivers/base/core.c:2406) Read of size 8 at addr ffff888168e8a0b8 by task chaoskey_raw_re/10106 Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) print_report (mm/kasan/report.c:378 mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:595) dev_driver_string (drivers/base/core.c:2406) __dynamic_dev_dbg (lib/dynamic_debug.c:906) chaoskey_release (drivers/usb/misc/chaoskey.c:323) __fput (fs/file_table.c:510) fput_close_sync (fs/file_table.c:615) __x64_sys_close (fs/open.c:1507 fs/open.c:1492 fs/open.c:1492) 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) The driver's last reference to the interface structure is dropped in the chaoskey_free() routine, so the code must not use the interface -- even in a debugging statement -- after that routine returns. (Exception: If we know that another reference is held by someone else, such as the device core while the disconnect routine runs, there's no problem. Thanks to Johan Hovold for pointing this out.) Since the bad access is part of an unimportant debugging statement, we can fix the problem simply by removing the whole statement. Reported-by: Shuangpeng Bai Closes: https://lore.kernel.org/linux-usb/20EC9664-054E-438B-B411-2145D347F97B@gmail.com/ Tested-by: Shuangpeng Bai Signed-off-by: Alan Stern Fixes: 66e3e591891d ("usb: Add driver for Altus Metrum ChaosKey device (v2)") Cc: stable Reviewed-by: Johan Hovold Link: https://patch.msgid.link/bb5b1dc6-eb59-43e1-8d26-51e658e88bbe@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/chaoskey.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 45cff32656c6e..1524e4770a46c 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -320,7 +320,6 @@ static int chaoskey_release(struct inode *inode, struct file *file) mutex_unlock(&dev->lock); destruction: mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "release success"); return rv; } From c4e232bd07fe2b69a6e5c380db41dd36b95e0524 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 13:20:05 +0800 Subject: [PATCH 0262/3297] usb: dwc3: run gadget disconnect from sleepable suspend context commit 010382937fb69892b3469ac4d30af072262f59e8 upstream. dwc3_gadget_suspend() takes dwc->lock with IRQs disabled and then calls dwc3_disconnect_gadget(). For async callbacks that helper only uses plain spin_unlock()/spin_lock(), so the gadget ->disconnect() callback still runs with IRQs disabled and any sleepable callback trips Lockdep. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the dwc3_gadget_suspend() -> dwc3_disconnect_gadget() -> gadget_driver->disconnect() chain, and Lockdep reported: BUG: sleeping function called from invalid context gadget_disconnect+0x21/0x39 [vuln_msv] dwc3_gadget_suspend.constprop.0+0x2b/0x42 [vuln_msv] Keep the disconnect callback selection in one common helper, but add a sleepable suspend-side wrapper which snapshots the callback under dwc->lock and then runs it after spin_unlock_irqrestore(). The regular event path still uses the existing spin_unlock()/spin_lock() window. Fixes: c8540870af4c ("usb: dwc3: gadget: Improve dwc3_gadget_suspend() and dwc3_gadget_resume()") Cc: stable Signed-off-by: Runyu Xiao Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260612052005.3849659-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 43 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 89963cd77daaa..ea8487d4bbb3c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, } } +static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc, + struct usb_gadget_driver **driver, + struct usb_gadget **gadget) +{ + if (!dwc->async_callbacks || !dwc->gadget_driver || + !dwc->gadget_driver->disconnect) + return false; + + *driver = dwc->gadget_driver; + *gadget = dwc->gadget; + + return true; +} + static void dwc3_disconnect_gadget(struct dwc3 *dwc) { - if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + + if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(dwc->gadget); + driver->disconnect(gadget); spin_lock(&dwc->lock); } } +static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc) +{ + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + driver->disconnect(gadget); +} + static void dwc3_suspend_gadget(struct dwc3 *dwc) { if (dwc->async_callbacks && dwc->gadget_driver->suspend) { @@ -4836,7 +4869,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc) int dwc3_gadget_suspend(struct dwc3 *dwc) { - unsigned long flags; int ret; ret = dwc3_gadget_soft_disconnect(dwc); @@ -4850,10 +4882,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) return -EAGAIN; } - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) - dwc3_disconnect_gadget(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_disconnect_gadget_sleepable(dwc); return 0; } From c40090f8d19b415e2925b22be964b5d1f695666f Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 18 Jun 2026 20:40:29 +0800 Subject: [PATCH 0263/3297] usb: misc: usbio: fix disconnect UAF in client teardown commit 0bfeec21984fedd32987f4e4c0cde34b445af404 upstream. usbio_disconnect() walks usbio->cli_list in reverse and uninitializes each auxiliary device. auxiliary_device_uninit() drops the device reference, and for an unbound child that can run usbio_auxdev_release() and free the containing struct usbio_client. list_for_each_entry_reverse() advances after the loop body by reading client->link.prev. If the current client is freed by auxiliary_device_uninit(), the iterator dereferences freed memory. Use list_for_each_entry_safe_reverse() so the previous client is cached before the body can drop the final reference. This preserves reverse teardown order while keeping the next iterator cursor independent of the current client's lifetime. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in usbio_disconnect+0x12e/0x150 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? usbio_disconnect+0x12e/0x150 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? usbio_disconnect+0x12e/0x150 kasan_report+0xe0/0x110 ? usbio_disconnect+0x12e/0x150 usbio_disconnect+0x12e/0x150 usb_unbind_interface+0xf3/0x400 really_probe+0x316/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x44/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xea/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 ? usb_enable_lpm+0x3c/0x260 usb_set_configuration+0xb64/0xf20 usb_generic_driver_probe+0x5f/0x90 usb_probe_device+0x71/0x1b0 really_probe+0x46b/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? add_device_randomness+0xb7/0xf0 usb_new_device+0x492/0x870 hub_event+0x1b10/0x29c0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x187/0x300 ? process_one_work+0x475/0xb90 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Reviewed-by: Hans de Goede Acked-by: Sakari Ailus Link: https://patch.msgid.link/20260618124029.3704089-1-zzzccc427@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 64815f8410acd..759af25313b0f 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -518,7 +518,7 @@ static int usbio_resume(struct usb_interface *intf) static void usbio_disconnect(struct usb_interface *intf) { struct usbio_device *usbio = usb_get_intfdata(intf); - struct usbio_client *client; + struct usbio_client *client, *next; /* Wakeup any clients waiting for a reply */ usbio->rxdat_len = 0; @@ -535,7 +535,7 @@ static void usbio_disconnect(struct usb_interface *intf) usb_kill_urb(usbio->urb); usb_free_urb(usbio->urb); - list_for_each_entry_reverse(client, &usbio->cli_list, link) { + list_for_each_entry_safe_reverse(client, next, &usbio->cli_list, link) { auxiliary_device_delete(&client->auxdev); auxiliary_device_uninit(&client->auxdev); } From 0beccbcf50de125be5520d0ffc59af4bb8655482 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Tue, 9 Jun 2026 16:00:52 +0800 Subject: [PATCH 0264/3297] 6lowpan: fix NHC entry use-after-free on error path commit 1720db928e5a58ca7d75ac1d514c3b73fd7061a7 upstream. lowpan_nhc_do_uncompression() looks up an NHC descriptor while holding lowpan_nhc_lock. If the descriptor has no uncompress callback, the error path drops the lock before printing nhc->name. lowpan_nhc_del() removes descriptors under the same lock and then relies on synchronize_net() before the owning module can be unloaded. That only waits for net RX RCU readers. lowpan_header_decompress() is also exported and can be reached from callers that are not necessarily covered by the net core RX critical section, for example the Bluetooth 6LoWPAN L2CAP receive path. This leaves a race where one task drops lowpan_nhc_lock in the error path, another task unregisters and frees the matching descriptor after synchronize_net() returns, and the first task then dereferences nhc->name for the warning. With the post-unlock window widened, KASAN reports: BUG: KASAN: slab-use-after-free in lowpan_nhc_do_uncompression+0x1f4/0x220 Read of size 8 lowpan_nhc_do_uncompression lowpan_header_decompress Fix this by printing the warning before dropping lowpan_nhc_lock, so the descriptor name is read while unregister is still excluded. The malformed packet is still rejected with -ENOTSUPP. Fixes: 92aa7c65d295 ("6lowpan: add generic nhc layer interface") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Signed-off-by: Yizhou Zhao Acked-by: Alexander Aring Link: https://patch.msgid.link/20260609080054.4541-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/6lowpan/nhc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c index 7b374595328de..a4dde85664f2f 100644 --- a/net/6lowpan/nhc.c +++ b/net/6lowpan/nhc.c @@ -117,9 +117,9 @@ int lowpan_nhc_do_uncompression(struct sk_buff *skb, return ret; } } else { - spin_unlock_bh(&lowpan_nhc_lock); netdev_warn(dev, "received nhc id for %s which is not implemented.\n", nhc->name); + spin_unlock_bh(&lowpan_nhc_lock); return -ENOTSUPP; } } else { From 2de42e268174766cb2e2b90721afdfdff70e0d8d Mon Sep 17 00:00:00 2001 From: Samuel Page Date: Thu, 25 Jun 2026 15:38:15 +0100 Subject: [PATCH 0265/3297] tipc: fix out-of-bounds read in broadcast Gap ACK blocks commit 2b66974a1b6134a4bbc3bfed181f7418f688eb54 upstream. A broadcast PROTOCOL/STATE_MSG can carry a Gap ACK blocks record in its data area. tipc_get_gap_ack_blks() only verifies that the record's len field is self-consistent with its ugack_cnt/bgack_cnt counts (sz == struct_size(p, gacks, ugack_cnt + bgack_cnt)); it does not check that the record actually fits in the message data area, msg_data_sz(). The unicast caller tipc_link_proto_rcv() bounds it ("if (glen > dlen) break;"), but the broadcast caller tipc_bcast_sync_rcv() discards the returned size, so tipc_link_advance_transmq() copies the record off the receive skb with an attacker-controlled count: this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt), GFP_ATOMIC); A TIPC neighbour that negotiated TIPC_GAP_ACK_BLOCK triggers it with one ordinary broadcast STATE_MSG (msg_bc_ack_invalid() clear), sized so its data area is short, carrying a Gap ACK record with len = 0x400, bgack_cnt = 0xff and ugack_cnt = 0. len then equals struct_size(p, gacks, 255), so the consistency check passes and ga is non-NULL; kmemdup() reads struct_size(ga, gacks, 255) = 1024 bytes out of the much smaller skb: BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x48/0x60 Read of size 1024 at addr ffff0000c7030d38 by task poc864/69 Call trace: kmemdup_noprof+0x48/0x60 tipc_link_advance_transmq+0x86c/0xb80 tipc_link_bc_ack_rcv+0x19c/0x1e0 tipc_bcast_sync_rcv+0x1c4/0x2c4 tipc_rcv+0x85c/0x1340 tipc_l2_rcv_msg+0xac/0x104 The buggy address belongs to the object at ffff0000c7030d00 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 56 bytes inside of allocated 704-byte region [ffff0000c7030d00, ffff0000c7030fc0) The copied-out bytes are subsequently consumed as gap/ack values, but the read is already out of bounds at the kmemdup() regardless of how they are used. The unicast STATE path drops such a message: "if (glen > dlen) break;" skips the rest of STATE_MSG handling and the skb is freed. Make the broadcast path drop it too. tipc_bcast_sync_rcv() now bounds the record against msg_data_sz() and, when it does not fit, reports it back through tipc_node_bc_sync_rcv() to tipc_rcv() so the skb is discarded rather than processed. ga is not cleared on this path: ga == NULL already means "legacy peer without Selective ACK", a distinct legitimate state. Fixes: d7626b5acff9 ("tipc: introduce Gap ACK blocks for broadcast link") Cc: stable@vger.kernel.org Signed-off-by: Samuel Page Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260625143815.1525412-1-sam@bynar.io Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/tipc/bcast.c | 22 ++++++++++++++-------- net/tipc/bcast.h | 2 +- net/tipc/node.c | 15 ++++++++++++--- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 114fef65f92ea..4ae097cfcd2dc 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -497,12 +497,13 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, */ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq) + struct sk_buff_head *retrq, bool *valid) { struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq; struct tipc_gap_ack_blks *ga; struct sk_buff_head xmitq; int rc = 0; + u16 glen; __skb_queue_head_init(&xmitq); @@ -510,13 +511,18 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, if (msg_type(hdr) != STATE_MSG) { tipc_link_bc_init_rcv(l, hdr); } else if (!msg_bc_ack_invalid(hdr)) { - tipc_get_gap_ack_blks(&ga, l, hdr, false); - if (!sysctl_tipc_bc_retruni) - retrq = &xmitq; - rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), - msg_bc_gap(hdr), ga, &xmitq, - retrq); - rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + glen = tipc_get_gap_ack_blks(&ga, l, hdr, false); + if (glen > msg_data_sz(hdr)) { + /* Malformed Gap ACK blocks; caller drops the msg */ + *valid = false; + } else { + if (!sysctl_tipc_bc_retruni) + retrq = &xmitq; + rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), + msg_bc_gap(hdr), ga, &xmitq, + retrq); + rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + } } tipc_bcast_unlock(net); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 2d9352dc7b0e6..55d17b5413e16 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -97,7 +97,7 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr); int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq); + struct sk_buff_head *retrq, bool *valid); int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, struct tipc_link *bcl); int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]); diff --git a/net/tipc/node.c b/net/tipc/node.c index a07fb073368ca..8dca9b1f4168a 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1831,12 +1831,15 @@ static void tipc_node_mcast_rcv(struct tipc_node *n) } static void tipc_node_bc_sync_rcv(struct tipc_node *n, struct tipc_msg *hdr, - int bearer_id, struct sk_buff_head *xmitq) + int bearer_id, struct sk_buff_head *xmitq, + bool *valid) { struct tipc_link *ucl; int rc; - rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq); + rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq, valid); + if (!*valid) + return; if (rc & TIPC_LINK_DOWN_EVT) { tipc_node_reset_links(n); @@ -2140,12 +2143,18 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b) /* Ensure broadcast reception is in synch with peer's send state */ if (unlikely(usr == LINK_PROTOCOL)) { + bool valid = true; + if (unlikely(skb_linearize(skb))) { tipc_node_put(n); goto discard; } hdr = buf_msg(skb); - tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq); + tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq, &valid); + if (!valid) { + tipc_node_put(n); + goto discard; + } } else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack)) { tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr); } From e99f2df433c63c86c93de1e5f08f16e404388756 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 18 Jun 2026 13:47:09 +0200 Subject: [PATCH 0266/3297] staging: vme_user: bound slave read/write to the kern_buf size commit 9f32f38265014fac7f5dc9490fb01a638ce6e121 upstream. The SLAVE-path helpers buffer_to_user() and buffer_from_user() copy 'count' bytes into/out of the fixed-size kern_buf (size_buf == PCI_BUF_SIZE == 0x20000, 128 KiB) using *ppos as the offset, without bounding *ppos + count against size_buf. vme_user_write()/vme_user_read() only clamp count to the VME window size (image_size = vme_get_size(resource)), which VME_SET_SLAVE sets from the user-supplied slave.size -- validated against the VME address space (up to VME_A32_MAX = 4 GiB), not against PCI_BUF_SIZE. When the window exceeds 128 KiB, a write()/read() copies past the kern_buf allocation. Clamp count against size_buf in both helpers, with an early return when *ppos is already at/after the buffer end. *ppos is >= 0 here (the caller rejects negative offsets), so size_buf - *ppos cannot wrap. This mirrors the existing clamp in the MASTER-path helpers resource_to_user() / resource_from_user(), and matches the read()/write() convention of a short transfer at end-of-buffer. Found by static analysis (CodeQL taint tracking + CBMC bounded model checking) and confirmed dynamically under KASAN with the vme_fake bridge: BUG: KASAN: slab-out-of-bounds in _copy_from_user+0x2d/0x80 Write of size 262144 at addr ffff888004100000 by task trigger/68 _copy_from_user+0x2d/0x80 vme_user_write+0x13e/0x240 [vme_user] vfs_write+0x1b8/0x7a0 ksys_write+0xb8/0x150 Fixes: f00a86d98a1e ("Staging: vme: add VME userspace driver") Cc: stable Signed-off-by: Michael Tautschnig Link: https://patch.msgid.link/20260618114709.72499-1-tautschn@amazon.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_user.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/staging/vme_user/vme_user.c b/drivers/staging/vme_user/vme_user.c index 5829a4141561e..97400b4a12596 100644 --- a/drivers/staging/vme_user/vme_user.c +++ b/drivers/staging/vme_user/vme_user.c @@ -156,6 +156,17 @@ static ssize_t buffer_to_user(unsigned int minor, char __user *buf, { void *image_ptr; + /* + * The slave window (image_size) can exceed the fixed kern_buf + * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. + * *ppos is >= 0 here (checked by the caller), so the + * subtraction below cannot wrap. + */ + if (*ppos >= image[minor].size_buf) + return 0; + if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos; if (copy_to_user(buf, image_ptr, (unsigned long)count)) return -EFAULT; @@ -168,6 +179,17 @@ static ssize_t buffer_from_user(unsigned int minor, const char __user *buf, { void *image_ptr; + /* + * The slave window (image_size) can exceed the fixed kern_buf + * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. + * *ppos is >= 0 here (checked by the caller), so the + * subtraction below cannot wrap. + */ + if (*ppos >= image[minor].size_buf) + return 0; + if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos; if (copy_from_user(image_ptr, buf, (unsigned long)count)) return -EFAULT; From ceb875a375dedbf51c9425c1d13a2d7a8435c08c Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Tue, 7 Jul 2026 20:23:58 +0900 Subject: [PATCH 0267/3297] smb: client: restrict implied bcc[0] exemption to responses without data area commit 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f upstream. smb2_check_message() has a long-standing quirk that accepts a response whose calculated length is one byte larger than the bytes actually received ("server can return one byte more due to implied bcc[0]"). This was introduced to accommodate servers that omit the trailing bcc[0] overlap byte when no data area is present. However, the exemption is applied unconditionally, regardless of whether the command actually carries a data area (has_smb2_data_area[]). When a response with a data area is subject to the +1 exemption, the reported data can extend one byte beyond the bytes actually received, yet smb2_check_message() still accepts it. The subsequent decoder then reads past the end of the receive buffer. This is reachable during NEGOTIATE and SESSION_SETUP, before the session is established. The resulting out-of-bounds reads are visible under KASAN when mounting against a non-conforming server; both the SPNEGO/negTokenInit and the NTLMSSP challenge decoders are affected: BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00 Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81 CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1 Call Trace: dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 asn1_ber_decoder+0x16a7/0x1b00 decode_negTokenInit+0x19/0x30 SMB2_negotiate+0x31d9/0x4c90 cifs_negotiate_protocol+0x1f2/0x3f0 cifs_get_smb_ses+0x93f/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 85: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 0 bytes to the right of allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0) which belongs to the cache cifs_small_rq of size 448 BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50 Read of size 329 at addr ffff88800726c678 by task mount.cifs/89 CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G B 7.1.0-rc6 #1 Call Trace: dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x36/0x50 decode_ntlmssp_challenge+0x457/0x680 SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0 SMB2_sess_setup+0x219/0x4f0 cifs_setup_session+0x248/0xaf0 cifs_get_smb_ses+0xf79/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 93: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 120 bytes inside of allocated 448-byte region [ffff88800726c600, ffff88800726c7c0) which belongs to the cache cifs_small_rq of size 448 Restrict the +1 exemption to responses that have no data area, so that it still covers the bcc[0] omission it was meant for. When a data area is present, the +1 discrepancy instead means the reported data length overruns the received buffer, so the response must be rejected. Fixes: 093b2bdad322 ("CIFS: Make demultiplex_thread work with SMB2 code") Cc: stable@vger.kernel.org Signed-off-by: Shoichiro Miyamoto Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2misc.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 0b37bc2a7f1c3..005a820946da1 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -19,6 +19,8 @@ #include "nterr.h" #include "cached_dir.h" +static unsigned int __smb2_calc_size(void *buf, bool *have_data); + static int check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) { @@ -144,6 +146,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) int command; __u32 calc_len; /* calculated length */ __u64 mid; + bool have_data; /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; @@ -227,7 +230,8 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) } } - calc_len = smb2_calc_size(buf); + have_data = false; + calc_len = __smb2_calc_size(buf, &have_data); /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */ @@ -246,8 +250,13 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) /* Windows 7 server returns 24 bytes more */ if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) return 0; - /* server can return one byte more due to implied bcc[0] */ - if (calc_len == len + 1) + /* + * Server can return one byte more due to implied bcc[0]. + * Allow it only when there is no data area; if data_length > 0 + * the +1 gap indicates an overreported data length rather than + * the bcc[0] omission. + */ + if (calc_len == len + 1 && !have_data) return 0; /* @@ -408,14 +417,17 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) /* * Calculate the size of the SMB message based on the fixed header * portion, the number of word parameters and the data portion of the message. + * If have_data is non-NULL, it is set to true when a non-empty data area was + * found (data_length > 0), allowing callers to distinguish the implied bcc[0] + * case (no data area) from an overreported data length. */ -unsigned int -smb2_calc_size(void *buf) +static unsigned int +__smb2_calc_size(void *buf, bool *have_data) { struct smb2_pdu *pdu = buf; struct smb2_hdr *shdr = &pdu->hdr; int offset; /* the offset from the beginning of SMB to data area */ - int data_length; /* the length of the variable length data area */ + int data_length = 0; /* the length of the variable length data area */ /* Structure Size has already been checked to make sure it is 64 */ int len = le16_to_cpu(shdr->StructureSize); @@ -448,9 +460,17 @@ smb2_calc_size(void *buf) } calc_size_exit: cifs_dbg(FYI, "SMB2 len %d\n", len); + if (have_data) + *have_data = (data_length > 0); return len; } +unsigned int +smb2_calc_size(void *buf) +{ + return __smb2_calc_size(buf, NULL); +} + /* Note: caller must free return buffer */ __le16 * cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) From a9214863139751c291f691a64161cd11b1d8af0b Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:15 +0800 Subject: [PATCH 0268/3297] staging: vme_user: fix location monitor leak in fake bridge commit e8422d89e8af41d87f0e9db564be8e2634f4c602 upstream. fake_init() allocates a location monitor resource and links it into fake_bridge->lm_resources. The init error path frees this list, but fake_exit() only frees the slave and master resource lists. Loading and unloading the module therefore triggers a kmemleak warning: unreferenced object 0xffff8b8b82aebe40 (size 64): comm "init", pid 1, jiffies 4294894572 backtrace (crc c1e013ef): kmemleak_alloc+0x4e/0x90 __kmalloc_cache_noprof+0x338/0x430 0xffffffffc0602246 do_one_initcall+0x4f/0x320 do_init_module+0x68/0x270 load_module+0x2a3b/0x2d90 Free the lm_resources list in fake_exit() as well, before fake_bridge is freed. Fixes: 658bcdae9c67 ("vme: Adding Fake VME driver") Cc: stable Cc: Martyn Welch Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Link: https://patch.msgid.link/20260704065817.403111-1-alvinhuang0603@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_fake.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/vme_user/vme_fake.c b/drivers/staging/vme_user/vme_fake.c index c6a7c0c69f16e..d0e7ba204c3e1 100644 --- a/drivers/staging/vme_user/vme_fake.c +++ b/drivers/staging/vme_user/vme_fake.c @@ -1239,6 +1239,7 @@ static void __exit fake_exit(void) { struct list_head *pos = NULL; struct list_head *tmplist; + struct vme_lm_resource *lm; struct vme_master_resource *master_image; struct vme_slave_resource *slave_image; int i; @@ -1268,6 +1269,13 @@ static void __exit fake_exit(void) vme_unregister_bridge(fake_bridge); fake_crcsr_exit(fake_bridge); + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &fake_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } + /* resources are stored in link list */ list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) { slave_image = list_entry(pos, struct vme_slave_resource, list); From e3ceafa6d8ee6b3a0f7fabe7a551fda909edbd46 Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:16 +0800 Subject: [PATCH 0269/3297] staging: vme_user: fix location monitor leak in tsi148 bridge commit 151edde741f8bc7f2931c5f44ab376d32b0c8beb upstream. tsi148_probe() allocates a location monitor resource and links it into tsi148_bridge->lm_resources. The probe error path frees this list, but tsi148_remove() only frees the dma, slave and master resource lists, so the location monitor resource is leaked on device unbind or module unload. Free the lm_resources list in tsi148_remove() as well, before tsi148_bridge is freed. Fixes: d22b8ed9a3b0 ("Staging: vme: add Tundra TSI148 VME-PCI Bridge driver") Cc: stable Cc: Martyn Welch Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Link: https://patch.msgid.link/20260704065817.403111-2-alvinhuang0603@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vme_user/vme_tsi148.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/vme_user/vme_tsi148.c b/drivers/staging/vme_user/vme_tsi148.c index 733594dde9ae9..58f9286c13540 100644 --- a/drivers/staging/vme_user/vme_tsi148.c +++ b/drivers/staging/vme_user/vme_tsi148.c @@ -2536,6 +2536,7 @@ static void tsi148_remove(struct pci_dev *pdev) { struct list_head *pos = NULL; struct list_head *tmplist; + struct vme_lm_resource *lm; struct vme_master_resource *master_image; struct vme_slave_resource *slave_image; struct vme_dma_resource *dma_ctrlr; @@ -2592,6 +2593,13 @@ static void tsi148_remove(struct pci_dev *pdev) tsi148_crcsr_exit(tsi148_bridge, pdev); + /* resources are stored in link list */ + list_for_each_safe(pos, tmplist, &tsi148_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); + list_del(pos); + kfree(lm); + } + /* resources are stored in link list */ list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) { dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); From b4ba13dafa13ca54d8a5ca8ce6f9ee95ce59c6c5 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:10 +0000 Subject: [PATCH 0270/3297] media: staging: ipu3-imgu: Add range check for imgu_css_cfg_acc_stripe commit c32fe4c4918c9aa49f61359e3b42619c4d8686de upstream. If the driver's stripe information is invalid it can result in an integer underflow. Add a range check to avoid this kind of error. This patch fixes the following smatch error: drivers/staging/media/ipu3/ipu3-css-params.c:1792 imgu_css_cfg_acc_stripe() warn: 'acc->stripe.bds_out_stripes[0]->width - 2 * f' 4294967168 can't fit into 65535 'acc->stripe.bds_out_stripes[1]->offset' Cc: stable@vger.kernel.org Fixes: e11110a5b744 ("media: staging/intel-ipu3: css: Compute and program ccs") Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/ipu3/ipu3-css-params.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 2c48d57a3180f..92cce31e35c5d 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[0].width = ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f); } else { + u32 offset; + /* Image processing is divided into two stripes */ acc->stripe.bds_out_stripes[0].width = acc->stripe.bds_out_stripes[1].width = @@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[1].width += f; } /* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */ - acc->stripe.bds_out_stripes[1].offset = - acc->stripe.bds_out_stripes[0].width - 2 * f; + offset = acc->stripe.bds_out_stripes[0].width - 2 * f; + if (offset > 65535) + return -EINVAL; + acc->stripe.bds_out_stripes[1].offset = offset; } acc->stripe.effective_stripes[0].height = From 1ca4f310c6b1fba9ac1fc791323d073a3b961ba8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 25 Mar 2026 13:59:43 +0100 Subject: [PATCH 0271/3297] staging: media: atomisp: reduce load_primary_binaries() stack usage commit f4d51e55dd47ef467fbe37d8575e20eee41b092d upstream. The load_primary_binaries() function is overly complex and has som large variables on the stack, which can cause warnings depending on CONFIG_FRAME_WARN setting: drivers/staging/media/atomisp/pci/sh_css.c: In function 'load_primary_binaries': drivers/staging/media/atomisp/pci/sh_css.c:5260:1: error: the frame size of 1560 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] Half of the stack usage is for the prim_descr[] array, but only one member of the array is used at any given time. Reduce the stack usage by turning the array into a single structure. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/atomisp/pci/sh_css.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 73bd87f43a8c2..cd5719dc16467 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5021,7 +5021,6 @@ static int load_primary_binaries( struct ia_css_capture_settings *mycs; unsigned int i; bool need_extra_yuv_scaler = false; - struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES]; IA_CSS_ENTER_PRIVATE(""); assert(pipe); @@ -5191,15 +5190,16 @@ static int load_primary_binaries( /* Primary */ for (i = 0; i < mycs->num_primary_stage; i++) { + struct ia_css_binary_descr prim_descr; struct ia_css_frame_info *local_vf_info = NULL; if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && (i == mycs->num_primary_stage - 1)) local_vf_info = &vf_info; - ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], + ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr, &prim_in_info, &prim_out_info, local_vf_info, i); - err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]); + err = ia_css_binary_find(&prim_descr, &mycs->primary_binary[i]); if (err) { IA_CSS_LEAVE_ERR_PRIVATE(err); return err; From b5ddc7257bee71f5b8cf9083e2b0ac0427e9fbb3 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 13 Apr 2026 17:12:44 +0200 Subject: [PATCH 0272/3297] staging: media: ipu7: fix double-free and use-after-free in error paths commit d3a9a8cf2d7fd61a2f63df61f6cbc0a9bb007cc0 upstream. In both ipu7_isys_init() and ipu7_psys_init(), pdata is allocated and then passed to ipu7_bus_initialize_device(), which stores it in adev->pdata. The ipu7_bus_release() function frees adev->pdata when the device's reference count drops to zero. Two error paths incorrectly call kfree(pdata) after the device teardown has already freed it: 1. When ipu7_mmu_init() fails: put_device() is called, which drops the reference count to zero and triggers ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is a double-free. 2. When ipu7_bus_add_device() fails: it calls auxiliary_device_uninit() internally, which calls put_device() -> ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is again a double-free. Note that the kfree(pdata) when ipu7_bus_initialize_device() itself fails is correct, because in that case auxiliary_device_init() failed and the release function was never set up, so pdata must be freed manually. Additionally, the error code was not saved before calling put_device(), causing ERR_CAST() to dereference the already-freed adev pointer when constructing the return value. Fix this by saving the error from dev_err_probe() before put_device() and returning ERR_PTR() instead. Remove the redundant kfree(pdata) calls and fix the use-after-free in the return values of the two affected error paths. Fixes: b7fe4c0019b1 ("media: staging/ipu7: add Intel IPU7 PCI device driver") Cc: stable@vger.kernel.org Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Signed-off-by: Sakari Ailus Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/ipu7/ipu7.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index fa5a1867626f8..8ec571c6bd07f 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2169,21 +2169,18 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, &ipdata->hw_variant); if (IS_ERR(isys_adev->mmu)) { - dev_err_probe(dev, PTR_ERR(isys_adev->mmu), - "ipu7_mmu_init(isys_adev->mmu) failed\n"); + ret = dev_err_probe(dev, PTR_ERR(isys_adev->mmu), + "ipu7_mmu_init(isys_adev->mmu) failed\n"); put_device(&isys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(isys_adev->mmu); + return ERR_PTR(ret); } isys_adev->mmu->dev = &isys_adev->auxdev.dev; isys_adev->subsys = IPU_IS; ret = ipu7_bus_add_device(isys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return isys_adev; } @@ -2216,21 +2213,18 @@ ipu7_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, &ipdata->hw_variant); if (IS_ERR(psys_adev->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), - "ipu7_mmu_init(psys_adev->mmu) failed\n"); + ret = dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), + "ipu7_mmu_init(psys_adev->mmu) failed\n"); put_device(&psys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(psys_adev->mmu); + return ERR_PTR(ret); } psys_adev->mmu->dev = &psys_adev->auxdev.dev; psys_adev->subsys = IPU_PS; ret = ipu7_bus_add_device(psys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return psys_adev; } From b9c4bf133c3c47e23baf4f5403b98a953bf58606 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:27 +0200 Subject: [PATCH 0273/3297] staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie() commit 5a752a616e756844388a1a45404db9fc29fec655 upstream. supplicant_ie is a 256-byte array in struct security_priv. The WPA and WPA2 IE copy paths use: memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2); where wpa_ielen is the raw IE length field (u8, 0-255). When a local user supplies a connect request via nl80211 with a crafted WPA IE of length 255, wpa_ielen + 2 equals 257, overflowing the 256-byte buffer by one byte into the adjacent last_mic_err_time field. rtw_parse_wpa_ie() does not prevent this: its length consistency check compares *(wpa_ie+1) against (u8)(wpa_ie_len-2), which is (u8)(255) == 255 when wpa_ie_len = 257, so the check passes silently. Add explicit bounds checks for both the WPA and WPA2 paths before the memcpy, rejecting any IE whose total size (wpa_ielen + 2) exceeds the supplicant_ie buffer. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-4-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 5d28dbf8b50ef..73fc1d4f92e6c 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -1446,6 +1446,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen); if (pwpa && wpa_ielen > 0) { + if (wpa_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL; + goto exit; + } if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; @@ -1455,6 +1459,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen); if (pwpa2 && wpa2_ielen > 0) { + if (wpa2_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL; + goto exit; + } if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; From 64ec4192d9c10e96922245d4a6747304cc76b19d Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:46:05 +0200 Subject: [PATCH 0274/3297] staging: rtl8723bs: fix WEP length underflow and OOB read in OnAuth() commit a1fc19d61f661d47204f095b593de507884849f7 upstream. OnAuth() has two bugs in the shared-key authentication path. When the Privacy bit is set, rtw_wep_decrypt() is called without verifying that the frame is long enough to contain a valid WEP IV and ICV. Inside rtw_wep_decrypt(), length is computed as: length = len - WLAN_HDR_A3_LEN - iv_len and then passed as (length - 4) to crc32_le(). If len is less than WLAN_HDR_A3_LEN + iv_len + icv_len (32 bytes), length - 4 is negative and, after the implicit cast to size_t, causes crc32_le() to read far beyond the frame buffer. Add a minimum length check before accessing the IV field and calling the decryption path. When processing a seq=3 response, rtw_get_ie() stores the Challenge Text IE length in ie_len, but the subsequent memcmp() always reads 128 bytes regardless of ie_len. IEEE 802.11 mandates a challenge text of exactly 128 bytes; reject any IE whose length field differs, matching the check already applied to OnAuthClient(). Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004605.1039209-1-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 72eb48d554a35..df9a5dc887013 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -703,6 +703,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE) return _FAIL; + if (len < WLAN_HDR_A3_LEN) + return _FAIL; + sa = GetAddr2Ptr(pframe); auth_mode = psecuritypriv->dot11AuthAlgrthm; @@ -714,6 +717,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) prxattrib->hdrlen = WLAN_HDR_A3_LEN; prxattrib->encrypt = _WEP40_; + if (len < WLAN_HDR_A3_LEN + 8) + return _FAIL; + iv = pframe+prxattrib->hdrlen; prxattrib->key_index = ((iv[3]>>6)&0x3); @@ -818,7 +824,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&ie_len, len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4); - if (!p || ie_len <= 0) { + if (!p || ie_len != 128) { status = WLAN_STATUS_CHALLENGE_FAIL; goto auth_fail; } From 04f612dc03427e0b1ac80a2611b5ac0ba93ac446 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:29 +0200 Subject: [PATCH 0275/3297] staging: rtl8723bs: fix OOB read in OnAssocRsp() IE loop commit f9654207e92283e0acac5d64fe5f8835383b5a23 upstream. The IE parsing loop in OnAssocRsp() advances by (pIE->length + 2) each iteration but only guards on i < pkt_len. When a malicious AP sends an AssocResponse whose last IE has only one byte remaining in the frame (the element_id byte lands at pkt_len-1), the loop reads pIE->length from pframe[pkt_len], which is one byte past the allocated receive buffer. Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond pkt_len, silently passing a truncated IE to the handler functions. Add two guards at the top of the loop body: 1. Break if fewer than sizeof(*pIE) bytes remain (can't read header). 2. Break if the IE's declared data extends past pkt_len. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260522004531.1038924-6-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index df9a5dc887013..dd6a707d7c3a7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -1430,7 +1430,11 @@ unsigned int OnAssocRsp(struct adapter *padapter, union recv_frame *precv_frame) /* to handle HT, WMM, rate adaptive, update MAC reg */ /* for not to handle the synchronous IO in the tasklet */ for (i = (6 + WLAN_HDR_A3_LEN); i < pkt_len;) { + if (i + sizeof(*pIE) > pkt_len) + break; pIE = (struct ndis_80211_var_ie *)(pframe + i); + if (i + sizeof(*pIE) + pIE->length > pkt_len) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: From 69f174a0673b6b7a29b851adb60bc450cdc0ecc4 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:25 +0200 Subject: [PATCH 0276/3297] staging: rtl8723bs: fix OOB read in update_beacon_info() IE loop commit ed51de4a86e173c3b0ef78e039c2e49e08b11f16 upstream. The IE parsing loop in update_beacon_info() advances by (pIE->length + 2) each iteration but only guards on i < len. When a malicious AP sends a Beacon whose last IE has only one byte remaining in the frame (the element_id byte lands at len-1), the loop reads pIE->length from one byte past the allocated receive buffer. Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond len, passing a truncated IE to the handler functions. Add two guards at the top of the loop body: 1. Break if fewer than sizeof(*pIE) bytes remain (can't read header). 2. Break if the IE's declared data extends past len. Also replace i += (pIE->length + 2) with i += sizeof(*pIE) + pIE->length for consistency with the sizeof(*pIE) guards added above. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-2-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 1def9758852cd..2ac90dcd08259 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1288,7 +1288,11 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru len = pkt_len - (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN); for (i = 0; i < len;) { + if (i + sizeof(*pIE) > len) + break; pIE = (struct ndis_80211_var_ie *)(pframe + (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN) + i); + if (i + sizeof(*pIE) + pIE->length > len) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: @@ -1313,7 +1317,7 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru break; } - i += (pIE->length + 2); + i += sizeof(*pIE) + pIE->length; } } From c38d16b1ffac385c9e4b38447cd5c46af1114b58 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:26 +0200 Subject: [PATCH 0277/3297] staging: rtl8723bs: fix OOB reads in IE loops in issue_assocreq() and join_cmd_hdl() commit ef61d628dfad38fead1fd2e08979ae9126d011d5 upstream. Two IE parsing loops are missing the header bounds checks before they dereference pIE->length: - issue_assocreq() walks pmlmeinfo->network.ies to build the association request. If the stored IE data ends with only an element_id byte and no length byte, pIE->length is read one byte past the end of the buffer. - join_cmd_hdl() walks pnetwork->ies during station join and has the same problem under the same conditions. Both buffers are filled from AP beacon and probe-response frames, so a malicious AP that sends a truncated final IE can trigger the issue. Apply the two-guard pattern established in update_beacon_info(): 1. Break if fewer than sizeof(*pIE) bytes remain. 2. Break if the IE's declared data extends past the buffer end. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Reviewed-by: Luka Gejak Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-3-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index dd6a707d7c3a7..9060b63eb424d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -2972,7 +2972,11 @@ void issue_assocreq(struct adapter *padapter) /* vendor specific IE, such as WPA, WMM, WPS */ for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) { + if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length) + break; pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); + if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: @@ -5370,7 +5374,11 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf) /* sizeof(struct ndis_802_11_fix_ie) */ for (i = _FIXED_IE_LENGTH_; i < pnetwork->ie_length;) { + if (i + sizeof(*pIE) > pnetwork->ie_length) + break; pIE = (struct ndis_80211_var_ie *)(pnetwork->ies + i); + if (i + sizeof(*pIE) + pIE->length > pnetwork->ie_length) + break; switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC:/* Get WMM IE. */ From a6105ea8ca6ebbc04beaf3bcbf7dbb5985f5d395 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:30 +0200 Subject: [PATCH 0278/3297] staging: rtl8723bs: fix OOB reads in is_ap_in_tkip() IE loop commit 3bf39f711ff27c64be8680a8938bcc5001982e81 upstream. The loop in is_ap_in_tkip() iterates over IEs without verifying that enough bytes remain before dereferencing the IE header or its payload: - pIE->element_id and pIE->length are read without checking that i + sizeof(*pIE) <= ie_length, so a truncated IE at the end of the buffer causes an OOB read. - For WLAN_EID_VENDOR_SPECIFIC the code compares pIE->data + 12, which requires pIE->length >= 16. For WLAN_EID_RSN it compares pIE->data + 8, requiring pIE->length >= 12. Neither requirement is checked. Add the missing IE header and payload bounds checks and guard each data access with an explicit pIE->length minimum, matching the pattern established in update_beacon_info(). Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-7-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 2ac90dcd08259..c1fdc5d677354 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -1333,15 +1333,23 @@ unsigned int is_ap_in_tkip(struct adapter *padapter) for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) { pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); + if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length) + break; + if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length) + break; + switch (pIE->element_id) { case WLAN_EID_VENDOR_SPECIFIC: - if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) && (!memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4))) + if (pIE->length >= 16 && + !memcmp(pIE->data, RTW_WPA_OUI, 4) && + !memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4)) return true; break; case WLAN_EID_RSN: - if (!memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4)) + if (pIE->length >= 12 && + !memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4)) return true; break; @@ -1349,7 +1357,7 @@ unsigned int is_ap_in_tkip(struct adapter *padapter) break; } - i += (pIE->length + 2); + i += sizeof(*pIE) + pIE->length; } return false; From 6f91621fc45025ad3c0be796b70e6e4cee22fc69 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:28 +0200 Subject: [PATCH 0279/3297] staging: rtl8723bs: fix OOB write in HT_caps_handler() commit f8001e1a516ba3b495728c65b61f799cbfad6bd0 upstream. HT_caps_handler() iterates pIE->length bytes and writes into HT_caps.u.HT_cap[], which is a fixed 26-byte array (sizeof struct HT_caps_element). Because pIE->length is a raw u8 from an over-the-air 802.11 AssocResponse frame and is never validated, a malicious AP can set it up to 255, causing up to 229 bytes of out-of-bounds writes into adjacent fields of struct mlme_ext_info. Truncate the iteration count to the size of HT_caps.u.HT_cap using umin() so that data from a longer-than-expected IE is silently ignored rather than written out of bounds, preserving interoperability with APs that pad the element. An early return on oversized IEs was considered but rejected: it would bypass the pmlmeinfo->HT_caps_enable = 1 assignment that precedes the loop, silently disabling HT mode for APs that append extra bytes to the HT Capabilities IE. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Reviewed-by: Luka Gejak Link: https://patch.msgid.link/20260522004531.1038924-5-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index c1fdc5d677354..656a1b49ad20c 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -935,7 +935,8 @@ void HT_caps_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE) pmlmeinfo->HT_caps_enable = 1; - for (i = 0; i < (pIE->length); i++) { + for (i = 0; i < umin(pIE->length, + sizeof(pmlmeinfo->HT_caps.u.HT_cap)); i++) { if (i != 2) { /* Commented by Albert 2010/07/12 */ /* Got the endian issue here. */ From 84a00be9b736aa5dce902a290f62cbbbdcfab9ed Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Fri, 8 May 2026 12:24:16 +0800 Subject: [PATCH 0280/3297] crypto: amlogic - avoid double cleanup in meson_crypto_probe() commit 6d827ade51a24e18d81afb9f32756d339520a14c upstream. When meson_allocate_chanlist() fails after a partial allocation, it already unwinds the allocated chanlist state through its local error path. meson_crypto_probe() then jump to error_flow and calls meson_free_chanlist() again, causing the same per-flow resources to be torn down twice. In the reproduced failure path, the second teardown re-entered crypto_engine_exit() on an already destroyed worker and KASAN reported a slab-use-after-free in kthread_destroy_worker(). Prevent double-free by handling partial allocation failures locally within meson_allocate_chanlist() and skipping the outer cleanup path. 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. The bug was reproduced in a QEMU x86_64 guest booted with KASAN on v7.1, using the reproducer under tools/testing/meson_crypto_probe. The reproducer forces the second dma_alloc_attrs() call in the gxl-crypto probe path to return NULL, making meson_allocate_chanlist() fail after partial initialization. On the unpatched kernel this reliably triggered a slab-use-after-free. With this fix applied, the same reproducer no longer emits any KASAN report and the probe fails cleanly with -ENOMEM. ================================================================== BUG: KASAN: slab-use-after-free in kthread_destroy_worker+0xb2/0xd0 Read of size 8 at addr ff1100010c057a68 by task insmod/265 CPU: 1 UID: 0 PID: 265 Comm: insmod Tainted: G O 7.1.0-rc2-00376-g810af9adc907-dirty #10 PREEMPT(lazy) Tainted: [O]=OOT_MODULE Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 Call Trace: dump_stack_lvl+0x68/0xa0 print_report+0xcb/0x5e0 ? __virt_addr_valid+0x21d/0x3f0 ? kthread_destroy_worker+0xb2/0xd0 ? kthread_destroy_worker+0xb2/0xd0 kasan_report+0xca/0x100 ? kthread_destroy_worker+0xb2/0xd0 kthread_destroy_worker+0xb2/0xd0 meson_crypto_probe+0x4d0/0xc10 [amlogic_gxl_crypto] platform_probe+0x99/0x140 really_probe+0x1c6/0x6a0 ? __pfx___device_attach_driver+0x10/0x10 __driver_probe_device+0x248/0x310 ? acpi_driver_match_device+0xb0/0x100 driver_probe_device+0x48/0x210 ? __pfx___device_attach_driver+0x10/0x10 __device_attach_driver+0x160/0x320 bus_for_each_drv+0x104/0x190 ? __pfx_bus_for_each_drv+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x2c/0x50 __device_attach+0x19d/0x3b0 ? __pfx___device_attach+0x10/0x10 ? do_raw_spin_unlock+0x53/0x220 device_initial_probe+0x78/0xa0 bus_probe_device+0x5b/0x130 device_add+0xcfd/0x1430 ? __pfx_device_add+0x10/0x10 ? insert_resource+0x34/0x50 ? lock_release+0xc9/0x290 platform_device_add+0x24e/0x590 ? __pfx_meson_crypto_probe_repro_init+0x10/0x10 [meson_crypto_probe_repro] meson_crypto_probe_repro_init+0x330/0xff0 [meson_crypto_probe_repro] do_one_initcall+0xc0/0x450 ? __pfx_do_one_initcall+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x2c/0x50 ? __create_object+0x59/0x80 ? kasan_unpoison+0x27/0x60 do_init_module+0x27b/0x7d0 ? __pfx_do_init_module+0x10/0x10 ? kasan_quarantine_put+0x84/0x1d0 ? kfree+0x32c/0x510 ? load_module+0x561e/0x5ff0 load_module+0x54fe/0x5ff0 ? __pfx_load_module+0x10/0x10 ? security_file_permission+0x20/0x40 ? kernel_read_file+0x23d/0x6e0 ? mmap_region+0x235/0x4a0 ? __pfx_kernel_read_file+0x10/0x10 ? __file_has_perm+0x2c0/0x3e0 init_module_from_file+0x158/0x180 ? __pfx_init_module_from_file+0x10/0x10 ? __lock_acquire+0x45a/0x1ba0 ? idempotent_init_module+0x315/0x610 ? lock_release+0xc9/0x290 ? lockdep_init_map_type+0x4b/0x220 ? do_raw_spin_unlock+0x53/0x220 idempotent_init_module+0x330/0x610 ? __pfx_idempotent_init_module+0x10/0x10 ? __pfx_cred_has_capability.isra.0+0x10/0x10 ? ksys_mmap_pgoff+0x385/0x520 __x64_sys_finit_module+0xbe/0x120 do_syscall_64+0x115/0x690 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f7d6d31690d Code: 5b 41 5c c3 66 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 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 8b 0d f3 b4 0f 00 f7 d8 > RSP: 002b:00007fffc027ac68 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 RAX: ffffffffffffffda RBX: 000055f7b81967c0 RCX: 00007f7d6d31690d RDX: 0000000000000000 RSI: 000055f79a0d6cd2 RDI: 0000000000000003 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000003 R11: 0000000000000246 R12: 000055f79a0d6cd2 R13: 000055f7b8196790 R14: 000055f79a0d5888 R15: 000055f7b81968e0 Fixes: 48fe583fe541 ("crypto: amlogic - Add crypto accelerator for amlogic GXL") Cc: stable@vger.kernel.org Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/amlogic/amlogic-gxl-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/amlogic/amlogic-gxl-core.c b/drivers/crypto/amlogic/amlogic-gxl-core.c index 1c18a5b8470e0..6cb33949915fe 100644 --- a/drivers/crypto/amlogic/amlogic-gxl-core.c +++ b/drivers/crypto/amlogic/amlogic-gxl-core.c @@ -291,8 +291,8 @@ static int meson_crypto_probe(struct platform_device *pdev) return 0; error_alg: meson_unregister_algs(mc); -error_flow: meson_free_chanlist(mc, MAXFLOW - 1); +error_flow: clk_disable_unprepare(mc->busclk); return err; } From ef6feb77e2d91761427c5b773edc9c97e1b706ad Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:24:55 -0400 Subject: [PATCH 0281/3297] crypto: krb5 - filter out async aead implementations at alloc commit 6c9dddeb582fde005360f4fe02c760d45ca05fb5 upstream. krb5_aead_encrypt(), krb5_aead_decrypt() in rfc3961_simplified.c and rfc8009_encrypt(), rfc8009_decrypt() in rfc8009_aes2.c set a NULL completion callback and treat any negative return from crypto_aead_{encrypt,decrypt}() as terminal, falling through to kfree_sensitive(buffer). When the encrypt_name resolves to an async AEAD instance the request returns -EINPROGRESS, the buffer is freed while the backend's worker still holds a pointer, and the worker dereferences the freed slab on completion. KASAN report under UML+SLUB with a synthetic async aead backend bound to krb5->encrypt_name: BUG: KASAN: slab-use-after-free in t5_stub_complete+0x7d/0xc7 The helpers were written synchronously, so filter the async instances out at allocation time instead of plumbing crypto_wait_req() through every call site. Reachable via net/rxrpc/rxgk.c, fs/afs/cm_security.c and net/ceph/crypto.c on systems with an async AEAD provider bound to the krb5 enctype name. Fixes: 00244da40f78 ("crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions") Fixes: 6c3c0e86c2ac ("crypto/krb5: Implement the AES enctypes from rfc8009") Cc: stable@vger.kernel.org Suggested-by: Herbert Xu Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/krb5/krb5_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c index c7ea40f900a77..03395b89cc61e 100644 --- a/crypto/krb5/krb5_api.c +++ b/crypto/krb5/krb5_api.c @@ -207,7 +207,7 @@ struct crypto_aead *krb5_prepare_encryption(const struct krb5_enctype *krb5, struct crypto_aead *ci = NULL; int ret = -ENOMEM; - ci = crypto_alloc_aead(krb5->encrypt_name, 0, 0); + ci = crypto_alloc_aead(krb5->encrypt_name, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(ci)) { ret = PTR_ERR(ci); if (ret == -ENOENT) From 94083db751930b1540ddff2b54d4677549c57f81 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Wed, 10 Jun 2026 19:53:14 +0900 Subject: [PATCH 0282/3297] ksmbd: fix use-after-free of a deferred file_lock on SMB2_CLOSE then SMB2_CANCEL commit 10f293a07f9e10e988b0ae44e2e99c631f5a68e0 upstream. Commit f580d27e8928 ("ksmbd: fix use-after-free of a deferred file_lock on double SMB2_CANCEL") made smb2_cancel() skip a work whose state is KSMBD_WORK_CANCELLED, so its cancel_fn cannot be fired a second time. But KSMBD_WORK has three states (ACTIVE, CANCELLED, CLOSED), and the same freeing producer path is reached for CLOSED too: SMB2_CLOSE on the locking handle -> set_close_state_blocked_works() sets the deferred work's state to KSMBD_WORK_CLOSED and wakes the smb2_lock() worker. The worker takes the non-ACTIVE early-exit, locks_free_lock()s the file_lock and, because the state is not KSMBD_WORK_CANCELLED, takes the STATUS_RANGE_NOT_LOCKED branch with "goto out2" -- which, like the cancelled branch, skips release_async_work(). The work stays on conn->async_requests with a live cancel_fn = smb2_remove_blocked_lock pointing at the freed file_lock. A subsequent SMB2_CANCEL for the same AsyncId then passes the KSMBD_WORK_CANCELLED-only guard (its state is KSMBD_WORK_CLOSED), so smb2_cancel() fires cancel_fn again over the freed file_lock -- the same use-after-free fixed, via SMB2_CLOSE instead of a first SMB2_CANCEL: BUG: KASAN: slab-use-after-free in __locks_delete_block __locks_delete_block locks_delete_block ksmbd_vfs_posix_lock_unblock smb2_remove_blocked_lock smb2_cancel <- 2nd SMB2_CANCEL fires cancel_fn handle_ksmbd_work Allocated by ...: locks_alloc_lock <- smb2_lock Freed by ...: locks_free_lock <- smb2_lock (non-ACTIVE early-exit) ... cache file_lock_cache of size 192 Reproduced on mainline 7.1-rc7 (which already contains f580d27e8928) with KASAN by an authenticated SMB client; the double-SMB2_CANCEL control is silent on that kernel, so the splat is attributable to the CLOSE trigger. Only an ACTIVE deferred work may have its cancel_fn fired: both terminal states (CANCELLED and CLOSED) reach the smb2_lock() early-exit that frees the file_lock and skips release_async_work(). Guard on KSMBD_WORK_ACTIVE so any non-active work is skipped. Fixes: f580d27e8928 ("ksmbd: fix use-after-free of a deferred file_lock on double SMB2_CANCEL") Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c012c5a93cc61..fd1c3a7d21601 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7334,14 +7334,14 @@ int smb2_cancel(struct ksmbd_work *work) continue; /* - * A cancelled deferred byte-range lock frees its - * file_lock and takes the smb2_lock() early-exit that - * skips release_async_work(), so the work stays on - * conn->async_requests with a live cancel_fn pointing - * at the freed file_lock. Re-firing it on a second - * SMB2_CANCEL is a use-after-free. + * Only an ACTIVE deferred work may have its cancel_fn + * fired. A CANCELLED or CLOSED work already took the + * smb2_lock() non-ACTIVE early-exit that frees the + * file_lock and skips release_async_work(), so it is + * still on conn->async_requests with a live cancel_fn + * pointing at the freed file_lock. */ - if (iter->state == KSMBD_WORK_CANCELLED) + if (iter->state != KSMBD_WORK_ACTIVE) break; ksmbd_debug(SMB, From e8417353cbd078d10531ba3928e609c84ab09e6b Mon Sep 17 00:00:00 2001 From: Zijing Yin Date: Mon, 8 Jun 2026 07:44:41 -0700 Subject: [PATCH 0283/3297] net: af_key: initialize alg_key_len for IPComp states commit d129c3177d7b1138fd5066fcc63a698b3ba415b0 upstream. pfkey_msg2xfrm_state() handles the IPComp (SADB_X_SATYPE_IPCOMP) case by allocating x->calg and copying only the algorithm name: x->calg = kmalloc_obj(*x->calg); if (!x->calg) { err = -ENOMEM; goto out; } strcpy(x->calg->alg_name, a->name); x->props.calgo = sa->sadb_sa_encrypt; Unlike the authentication (x->aalg) and encryption (x->ealg) branches of the same function, the compression branch never initializes calg->alg_key_len. IPComp carries no key and the allocation only reserves sizeof(struct xfrm_algo) (i.e. no room for a key), so the field is left containing uninitialized slab data. calg->alg_key_len is later used as a length by xfrm_algo_clone() when an IPComp state is cloned during XFRM_MSG_MIGRATE: xfrm_state_migrate() xfrm_state_clone_and_setup() x->calg = xfrm_algo_clone(orig->calg); kmemdup(orig, xfrm_alg_len(orig)); where xfrm_alg_len() returns sizeof(*alg) + (alg_key_len + 7) / 8. With a non-zero garbage alg_key_len, kmemdup() reads past the end of the 68-byte calg object. Adding an IPComp SA via PF_KEY and then migrating it triggers (net-next, KASAN, init_on_alloc=0): BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x44/0x60 Read of size 4164 at addr ff11000025a74980 by task diag2/9287 CPU: 3 UID: 0 PID: 9287 Comm: diag2 7.1.0-rc6-g903db046d557 #1 Call Trace: dump_stack_lvl+0x10e/0x1f0 print_report+0xf7/0x600 kasan_report+0xe4/0x120 kasan_check_range+0x105/0x1b0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x44/0x60 xfrm_state_migrate+0x70a/0x1da0 xfrm_migrate+0x753/0x18a0 xfrm_do_migrate+0xb47/0xf10 xfrm_user_rcv_msg+0x411/0xb50 netlink_rcv_skb+0x158/0x420 xfrm_netlink_rcv+0x71/0x90 netlink_unicast+0x584/0x850 netlink_sendmsg+0x8b0/0xdc0 ____sys_sendmsg+0x9f7/0xb90 ___sys_sendmsg+0x134/0x1d0 __sys_sendmsg+0x16d/0x220 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 9287: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 pfkey_add+0x2652/0x2ea0 pfkey_process+0x6d0/0x830 pfkey_sendmsg+0x42c/0x850 __sys_sendto+0x461/0x4b0 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ff11000025a74980 which belongs to the cache kmalloc-96 of size 96 The buggy address is located 0 bytes inside of allocated 68-byte region [ff11000025a74980, ff11000025a749c4) Depending on the uninitialized value the same field can instead request an oversized kmemdup() allocation and make the migration clone fail. The XFRM netlink path is not affected: verify_one_alg() rejects an XFRMA_ALG_COMP attribute shorter than xfrm_alg_len(), so a calg added via XFRM_MSG_NEWSA is always self-consistent. Initialize calg->alg_key_len to 0, matching the aalg/ealg branches. Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint address(es)") Cc: stable@vger.kernel.org Signed-off-by: Zijing Yin Reviewed-by: Sabrina Dubroca Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/key/af_key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/key/af_key.c b/net/key/af_key.c index 96fdeadf46e31..a2534e1ad6861 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1218,6 +1218,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, goto out; } strcpy(x->calg->alg_name, a->name); + x->calg->alg_key_len = 0; x->props.calgo = sa->sadb_sa_encrypt; } else { int keysize = 0; From fe997a84a385f840b593ead92e575503a5046cee Mon Sep 17 00:00:00 2001 From: Chi Wang Date: Fri, 19 Jun 2026 15:42:44 +0800 Subject: [PATCH 0284/3297] audit: Fix data races of skb_queue_len() readers on audit_queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c9a71daaecb2fb1d8c704545cc0b1c920b9bf5d7 upstream. Multiple readers access audit_queue.qlen via skb_queue_len() without holding the queue lock or using READ_ONCE(), while kauditd writes to this field via the skb_dequeue() → __skb_unlink() path with WRITE_ONCE() protected by a spinlock. This constitutes data races. All affected skb_queue_len(&audit_queue) call sites: - kauditd_thread() wait_event_freezable() condition - audit_receive_msg() AUDIT_GET handler (s.backlog assignment) - audit_receive() backlog check - audit_log_start() backlog check and pr_warn() KCSAN reports the following conflicting access pattern (one example): ================================================================== BUG: KCSAN: data-race in audit_log_start / skb_dequeue write (marked) to 0xffffffff8512ee20 of 4 bytes by task 661 on cpu 57: skb_dequeue+0x70/0xf0 kauditd_send_queue+0x71/0x220 kauditd_thread+0x1cb/0x430 kthread+0x1c2/0x210 ret_from_fork+0x162/0x1a0 ret_from_fork_asm+0x1a/0x30 read to 0xffffffff8512ee20 of 4 bytes by task 36586 on cpu 1: audit_log_start+0x2a0/0x6b0 audit_core_dumps+0x64/0xa0 do_coredump+0x14b/0x1260 get_signal+0xeb2/0xf70 arch_do_signal_or_restart+0x41/0x170 exit_to_user_mode_loop+0xa2/0x1c0 do_syscall_64+0x1a3/0x1c0 entry_SYSCALL_64_after_hwframe+0x76/0xe0 value changed: 0x00000001 -> 0x00000000 ================================================================== Resolve the race by switching to lockless helper skb_queue_len_lockless(), which internally uses READ_ONCE() and properly pairs with the WRITE_ONCE() write accesses already present on the writer side. Cc: stable@vger.kernel.org Fixes: 3197542482df ("audit: rework audit_log_start()") Signed-off-by: Chi Wang Reviewed-by: Ricardo Robaina [PM: line length tweak] Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/audit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 7bbd05cc3d6a5..0594897ce1595 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -946,7 +946,7 @@ static int kauditd_thread(void *dummy) * do the multicast send and rotate records from the * main queue to the retry/hold queues */ wait_event_freezable(kauditd_wait, - (skb_queue_len(&audit_queue) ? 1 : 0)); + (skb_queue_len_lockless(&audit_queue) ? 1 : 0)); } return 0; @@ -1279,7 +1279,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh, s.rate_limit = audit_rate_limit; s.backlog_limit = audit_backlog_limit; s.lost = atomic_read(&audit_lost); - s.backlog = skb_queue_len(&audit_queue); + s.backlog = skb_queue_len_lockless(&audit_queue); s.feature_bitmap = AUDIT_FEATURE_BITMAP_ALL; s.backlog_wait_time = audit_backlog_wait_time; s.backlog_wait_time_actual = atomic_read(&audit_backlog_wait_time_actual); @@ -1622,7 +1622,7 @@ static void audit_receive(struct sk_buff *skb) /* can't block with the ctrl lock, so penalize the sender now */ if (audit_backlog_limit && - (skb_queue_len(&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) { DECLARE_WAITQUEUE(wait, current); /* wake kauditd to try and flush the queue */ @@ -1928,7 +1928,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, long stime = audit_backlog_wait_time; while (audit_backlog_limit && - (skb_queue_len(&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) { /* wake kauditd to try and flush the queue */ wake_up_interruptible(&kauditd_wait); @@ -1948,7 +1948,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, } else { if (audit_rate_check() && printk_ratelimit()) pr_warn("audit_backlog=%d > audit_backlog_limit=%d\n", - skb_queue_len(&audit_queue), + skb_queue_len_lockless(&audit_queue), audit_backlog_limit); audit_log_lost("backlog limit exceeded"); return NULL; From d3b739db5dc6f688a60d56da872fabaf65246032 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 5 Jun 2026 16:23:35 +0200 Subject: [PATCH 0285/3297] Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref commit b66774b48dd98f07254951f74ea6f513efe7ff8b upstream. l2cap_chan_timeout() runs asynchronously and accesses chan->conn. If the connection is torn down while the timer is running or pending, chan->conn can be freed, leading to a use-after-free when the timer worker attempts to lock conn->lock: | BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline] | BUG: KASAN: slab-use-after-free in atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline] | BUG: KASAN: slab-use-after-free in __mutex_trylock_fast kernel/locking/mutex.c:161 [inline] | BUG: KASAN: slab-use-after-free in mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318 | Write of size 8 at addr ffff8881298d9550 by task kworker/2:1/83 | | CPU: 2 UID: 0 PID: 83 Comm: kworker/2:1 Not tainted 7.1.0-rc6-next-20260601-dirty #6 PREEMPT(full) | Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014 | Workqueue: events l2cap_chan_timeout | Call Trace: | | instrument_atomic_read_write include/linux/instrumented.h:112 [inline] | atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline] | __mutex_trylock_fast kernel/locking/mutex.c:161 [inline] | mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318 | l2cap_chan_timeout+0x5d/0x1b0 net/bluetooth/l2cap_core.c:422 | process_one_work kernel/workqueue.c:3326 [inline] | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409 | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490 | kthread+0x346/0x430 kernel/kthread.c:436 | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158 | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 | | | Allocated by task 320: | l2cap_conn_add+0xa7/0x820 net/bluetooth/l2cap_core.c:7075 | l2cap_connect_cfm+0xdb/0xd70 net/bluetooth/l2cap_core.c:7452 | hci_connect_cfm include/net/bluetooth/hci_core.h:2139 [inline] | hci_remote_features_evt+0x52f/0x9f0 net/bluetooth/hci_event.c:3760 | hci_event_func net/bluetooth/hci_event.c:7796 [inline] | hci_event_packet+0x561/0xa70 net/bluetooth/hci_event.c:7847 | hci_rx_work+0x370/0x890 net/bluetooth/hci_core.c:4040 | process_one_work kernel/workqueue.c:3326 [inline] | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409 | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490 | kthread+0x346/0x430 kernel/kthread.c:436 | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158 | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 | | Freed by task 322: | hci_disconn_cfm include/net/bluetooth/hci_core.h:2154 [inline] | hci_conn_hash_flush+0x101/0x1f0 net/bluetooth/hci_conn.c:2736 | hci_dev_close_sync+0x889/0xde0 net/bluetooth/hci_sync.c:5405 | hci_dev_do_close net/bluetooth/hci_core.c:502 [inline] | hci_unregister_dev+0x1f7/0x370 net/bluetooth/hci_core.c:2679 | vhci_release+0x12a/0x180 drivers/bluetooth/hci_vhci.c:690 | __fput+0x369/0x890 fs/file_table.c:510 | task_work_run+0x160/0x1d0 kernel/task_work.c:233 | get_signal+0xf5b/0x1120 kernel/signal.c:2810 | arch_do_signal_or_restart+0x4d/0x600 arch/x86/kernel/signal.c:337 | __exit_to_user_mode_loop kernel/entry/common.c:64 [inline] | exit_to_user_mode_loop+0x85/0x510 kernel/entry/common.c:98 | do_syscall_64+0x263/0x3d0 arch/x86/entry/syscall_64.c:100 | entry_SYSCALL_64_after_hwframe+0x77/0x7f | | The buggy address belongs to the object at ffff8881298d9400 | which belongs to the cache kmalloc-512 of size 512 | The buggy address is located 336 bytes inside of | freed 512-byte region [ffff8881298d9400, ffff8881298d9600) Fix it by having chan->conn hold a reference to l2cap_conn (via l2cap_conn_get) when the channel is added to the connection, and releasing it in the channel destructor. This ensures the l2cap_conn remains alive as long as the channel exists. A new FLAG_DEL channel flag is introduced to indicate that the channel has been deleted from its connection. l2cap_chan_del() atomically sets this flag using test_and_set_bit() instead of setting chan->conn to NULL. All asynchronous workers (l2cap_chan_timeout, l2cap_ack_timeout, l2cap_monitor_timeout, l2cap_retrans_timeout) and l2cap_chan_send() check FLAG_DEL to determine whether the channel has been torn down, rather than testing chan->conn for NULL. Fixes: 8c8e620467a7 ("Bluetooth: L2CAP: use chan timer to close channels in cleanup_listen()") Cc: Cc: Siwei Zhang Cc: Luiz Augusto von Dentz Assisted-by: Gemini:gemini-3.1-pro-preview Reported-by: https://sashiko.dev/#/patchset/20260521021249.3258069-1-oss%40fourdim.xyz Signed-off-by: Marco Elver Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e0a1f2293679a..6578da0048fb4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -748,6 +748,7 @@ enum { FLAG_ECRED_CONN_REQ_SENT, FLAG_PENDING_SECURITY, FLAG_HOLD_HCI_CONN, + FLAG_DEL, }; /* Lock nesting levels for L2CAP channels. We need these because lockdep diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 58c05a9aa96dd..26fbf61f6e332 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -411,7 +411,7 @@ static void l2cap_chan_timeout(struct work_struct *work) BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); - if (!conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_put(chan); return; } @@ -422,6 +422,9 @@ static void l2cap_chan_timeout(struct work_struct *work) */ l2cap_chan_lock(chan); + if (test_bit(FLAG_DEL, &chan->flags)) + goto unlock; + if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; else if (chan->state == BT_CONNECT && @@ -434,10 +437,10 @@ static void l2cap_chan_timeout(struct work_struct *work) chan->ops->close(chan); +unlock: l2cap_chan_unlock(chan); - l2cap_chan_put(chan); - mutex_unlock(&conn->lock); + l2cap_chan_put(chan); } struct l2cap_chan *l2cap_chan_create(void) @@ -490,6 +493,9 @@ static void l2cap_chan_destroy(struct kref *kref) list_del(&chan->global_l); write_unlock(&chan_list_lock); + if (chan->conn) + l2cap_conn_put(chan->conn); + kfree(chan); } @@ -593,7 +599,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; - chan->conn = conn; + chan->conn = l2cap_conn_get(conn); switch (chan->chan_type) { case L2CAP_CHAN_CONN_ORIENTED: @@ -648,30 +654,26 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) void l2cap_chan_del(struct l2cap_chan *chan, int err) { - struct l2cap_conn *conn = chan->conn; - __clear_chan_timer(chan); - BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err, + BT_DBG("chan %p, err %d, state %s", chan, err, state_to_string(chan->state)); chan->ops->teardown(chan, err); - if (conn) { + if (!test_and_set_bit(FLAG_DEL, &chan->flags)) { /* Delete from channel list */ list_del(&chan->list); l2cap_chan_put(chan); - chan->conn = NULL; - /* Reference was only held for non-fixed channels or * fixed channels that explicitly requested it using the * FLAG_HOLD_HCI_CONN flag. */ if (chan->chan_type != L2CAP_CHAN_FIXED || test_bit(FLAG_HOLD_HCI_CONN, &chan->flags)) - hci_conn_drop(conn->hcon); + hci_conn_drop(chan->conn->hcon); } if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) @@ -1903,7 +1905,7 @@ static void l2cap_monitor_timeout(struct work_struct *work) l2cap_chan_lock(chan); - if (!chan->conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; @@ -1924,7 +1926,7 @@ static void l2cap_retrans_timeout(struct work_struct *work) l2cap_chan_lock(chan); - if (!chan->conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; @@ -2565,7 +2567,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, int err; struct sk_buff_head seg_queue; - if (!chan->conn) + if (test_bit(FLAG_DEL, &chan->flags)) return -ENOTCONN; /* Connectionless channel */ @@ -3160,12 +3162,16 @@ static void l2cap_ack_timeout(struct work_struct *work) l2cap_chan_lock(chan); + if (test_bit(FLAG_DEL, &chan->flags)) + goto unlock; + frames_to_ack = __seq_offset(chan, chan->buffer_seq, chan->last_acked_seq); if (frames_to_ack) l2cap_send_rr_or_rnr(chan, 0); +unlock: l2cap_chan_unlock(chan); l2cap_chan_put(chan); } From b346efa825b5e4386f19bc63f81141652d496ec4 Mon Sep 17 00:00:00 2001 From: Samuel Page Date: Mon, 15 Jun 2026 16:09:22 +0100 Subject: [PATCH 0286/3297] Bluetooth: MGMT: Fix UAF of hci_conn_params in add_device_complete commit fa85d985f614bc3feb343000f14a1072e99b0df1 upstream. add_device_complete() runs from the hci_cmd_sync_work kworker, which holds only hci_req_sync_lock and *not* hci_dev_lock. It calls hci_conn_params_lookup() and then dereferences the returned object (params->flags) without taking hci_dev_lock: params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); ... device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); hci_conn_params_lookup() walks hdev->le_conn_params and is documented to require hdev->lock. A concurrent MGMT_OP_REMOVE_DEVICE (remove_device()), which does run under hci_dev_lock, can call hci_conn_params_free() to list_del() and kfree() the very object the lookup returned, so the subsequent params->flags read touches freed memory [0]. Hold hci_dev_lock() across the hci_conn_params_lookup() and the read of params->flags (and the matching event emission) so the lookup result cannot be freed by a concurrent remove_device() before it is used, honouring the locking contract of hci_conn_params_lookup(). [0]: (trailing page/memory-state dump trimmed) BUG: KASAN: slab-use-after-free in add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 Read of size 1 at addr ffff000017ab26c1 by task kworker/u9:8/388 CPU: 1 UID: 0 PID: 388 Comm: kworker/u9:8 Not tainted 7.0.11 #20 PREEMPT Hardware name: linux,dummy-virt (DT) Workqueue: hci0 hci_cmd_sync_work Call trace: show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:499 (C) __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xb4/0xd4 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0x118/0x5d8 mm/kasan/report.c:482 kasan_report+0xb0/0xf4 mm/kasan/report.c:595 __asan_report_load1_noabort+0x20/0x2c mm/kasan/report_generic.c:378 add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 hci_cmd_sync_work+0x14c/0x240 net/bluetooth/hci_sync.c:334 process_one_work+0x628/0xd38 kernel/workqueue.c:3289 process_scheduled_works kernel/workqueue.c:3372 [inline] worker_thread+0x7a8/0xac0 kernel/workqueue.c:3453 kthread+0x39c/0x444 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:860 Allocated by task 3401: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_alloc_info+0x40/0x54 mm/kasan/generic.c:570 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0xd4/0xd8 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x1b0/0x458 mm/slub.c:5385 kmalloc_noprof include/linux/slab.h:950 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] hci_conn_params_add+0x10c/0x4b0 net/bluetooth/hci_core.c:2279 hci_conn_params_set net/bluetooth/mgmt.c:5162 [inline] add_device+0x5b4/0xa54 net/bluetooth/mgmt.c:7755 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Freed by task 3740: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_free_info+0x4c/0x74 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x88/0xb8 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2685 [inline] slab_free mm/slub.c:6170 [inline] kfree+0x14c/0x458 mm/slub.c:6488 hci_conn_params_free+0x288/0x484 net/bluetooth/hci_core.c:2312 remove_device+0x4b0/0x968 net/bluetooth/mgmt.c:7919 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Fixes: 1e2e3044c1bc ("Bluetooth: MGMT: Fix MGMT_OP_ADD_DEVICE invalid device flags") Cc: stable@vger.kernel.org Assisted-by: Bynario AI Signed-off-by: Samuel Page Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c87ec0138c430..90da285a00cdf 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7641,6 +7641,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) if (!err) { struct hci_conn_params *params; + hci_dev_lock(hdev); + params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); @@ -7649,6 +7651,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); + hci_dev_unlock(hdev); } mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE, From cbb684ef39e9f292464da133be622f9734bb6f49 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 29 May 2026 00:52:01 +0800 Subject: [PATCH 0287/3297] coresight: etb10: restore atomic_t for shared reading state commit fa09f08ede3db3050ae16ae1ed92c902d0cada23 upstream. The etb10 miscdevice uses drvdata->reading as a shared exclusivity gate for userspace buffer access. etb_open() claims that gate with local_cmpxchg(), and etb_release() clears it with local_set(). That gate is shared per-device state rather than CPU-local state. A running system can reach it whenever /dev/ is opened, closed, and reopened by different tasks while the device remains registered, so the same drvdata->reading variable may be claimed on one CPU and later cleared on another. This code used to use atomic_t for the same gate, but commit 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") changed it to local_t even though the access pattern remained cross-task and cross-CPU. Restore atomic_t together with atomic_cmpxchg() and atomic_set() so the exclusivity gate again uses a primitive intended for shared state. The issue was found on Linux v6.18.21 by our static analysis tool while scanning surviving local_t-on-shared-state sites, and then manually reviewed against the live etb10 file-op path. It was runtime-validated with a reproducible QEMU no-device KCSAN PoC that kept the same report-local contract: 1. use one shared struct etb_drvdata carrier and its drvdata->reading gate; 2. call etb_open() and etb_release() sequentially on that gate to confirm the original claim/clear path; 3. bind the open side to CPU0 and the release side to CPU1 for the same gate to show cross-CPU ownership; 4. run bound workers that repeatedly race etb_open() and etb_release() on the same gate until KCSAN reports a target hit. The harness recorded: L1 passed open=1 release=1 reading_after_open=1 reading_after_release=0 L2 passed open_cpu=0 release_cpu=1 cross_cpu_release=1 reading_after=0 open_ret=0 Representative KCSAN excerpt from the no-device validation run: BUG: KCSAN: data-race in etb_open.constprop.0.isra.0 [vuln_msv] write to 0xffffffffc0003810 of 4 bytes by task 216 on cpu 1: etb_open.constprop.0.isra.0+0x38/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30 read to 0xffffffffc0003810 of 4 bytes by task 215 on cpu 0: etb_open.constprop.0.isra.0+0x18/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30 value changed: 0x00000000 -> 0x00000001 Reported by Kernel Concurrency Sanitizer on: CPU: 0 PID: 215 Comm: etb10_l3_a Tainted: G O 6.1.66 #2 This no-device harness is not a real ETB10 hardware end-to-end run, but it preserves the same shared drvdata->reading gate and the same etb_open()/etb_release() claim/clear contract. No real ETB10 hardware was available for runtime testing. Build-tested with: make olddefconfig make -j"$(nproc)" drivers/hwtracing/coresight/coresight-etb10.o Fixes: 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260528165201.319452-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 35db1b6093d15..98269ea6f7ae3 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -85,7 +85,7 @@ struct etb_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; raw_spinlock_t spinlock; - local_t reading; + atomic_t reading; pid_t pid; u8 *buf; u32 buffer_depth; @@ -603,7 +603,7 @@ static int etb_open(struct inode *inode, struct file *file) struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - if (local_cmpxchg(&drvdata->reading, 0, 1)) + if (atomic_cmpxchg(&drvdata->reading, 0, 1)) return -EBUSY; dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); @@ -641,7 +641,7 @@ static int etb_release(struct inode *inode, struct file *file) { struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - local_set(&drvdata->reading, 0); + atomic_set(&drvdata->reading, 0); dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); return 0; From c00164c9e7fa6145886ad666806cb5347895de5c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 21 Jun 2026 16:47:44 +0200 Subject: [PATCH 0288/3297] debugobjects: Plug race against a concurrent OOM disable commit b81dde13cc163450dcb402dcc915ef13ba241e01 upstream. syzbot reported a puzzling splat: WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20 stub_timer() is installed as timer callback function in hrtimer_fixup_assert_init(), which is invoked when debug_object_assert_init() can't find a shadow object. In that case debug objects emits a warning about it before invoking the fixup. Though the provided console log lacks this warning and instead has the following a few seconds before the splat: ODEBUG: Out of memory. ODEBUG disabled So the object was looked up in debug_object_assert_init() and the lookup failed due a concurrent out of memory situation which disabled debug objects and freed the shadow objects: debug_object_assert_init() if (!debug_objects_enabled) return; obj = alloc(); if (!obj) { // Out of memory debug_objects_enabled = false; free_objects(); obj = lookup_or_alloc(); // The lookup failed because the other side // removed the objects, so this returns // an error code as the object in question // is not statically initialized if (!IS_ERR_OR_NULL(obj)) return; if (!obj) { debug_oom(); return; } print(...) if (!debug_objects_enabled) return; fixup(...) The debug object splat is skipped because debug_objects_enabled is false, but the fixup callback is invoked unconditionally, which makes the timer disfunctional. This is only a problem in debug_object_assert_init() and debug_object_activate() as both have to handle statically initialized objects and therefore must handle the error pointer return case gracefully. All other places only handle the found/not found case and the NULL pointer return is a signal for OOM. Otherwise they get a valid shadow object. Plug the hole by checking whether debug objects are still enabled before invoking the print and fixup function in those two places. Fixes: b84d435cc228 ("debugobjects: Extend to assert that an object is initialized") Reported-by: syzbot+5e8dda76ca21dae314b6@syzkaller.appspotmail.com Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/874iiwlzlb.ffs@fw13 Signed-off-by: Greg Kroah-Hartman --- lib/debugobjects.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 17f116247bb42..0149411096fa4 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -894,6 +894,14 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr) } raw_spin_unlock_irqrestore(&db->lock, flags); + + /* + * lookup_object_or_alloc() might have raced with a concurrent + * allocation failure which disabled debug objects. + */ + if (!debug_objects_enabled) + return 0; + debug_print_object(&o, "activate"); switch (o.state) { @@ -1071,6 +1079,15 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr) return; } + /* + * lookup_object_or_alloc() might have raced with a concurrent + * allocation failure which disabled debug objects. Don't run the fixup + * as it might turn a valid object useless. See for example + * hrtimer_fixup_assert_init(). + */ + if (!debug_objects_enabled) + return; + /* Object is neither tracked nor static. It's not initialized. */ debug_print_object(&o, "assert_init"); debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE); From cf28fc1658463d768657cf1c27a83980d4ba7ef2 Mon Sep 17 00:00:00 2001 From: Yunpeng Tian Date: Mon, 4 May 2026 07:19:43 -0700 Subject: [PATCH 0289/3297] fs/ntfs3: validate Dirty Page Table capacity in log_replay copy_lcns commit 57382ec6ac63b63dce2789e835fded28b698ae79 upstream. In the analysis pass of $LogFile journal replay, log_replay() copies LCNs from each action log record into an existing Dirty Page Table (DPT) entry without bounding the destination index. A crafted NTFS image with DPT entry lcns_follow=1 and an action log record with lcns_follow=2 produces a kernel slab out-of-bounds write at mount time: BUG: KASAN: slab-out-of-bounds in log_replay+0x654c/0xdb60 Write of size 8 at addr ffff8880095e1040 by task mount Two attacker-controlled fields can drive j+i past the allocated page_lcns[] array: 1. dp->lcns_follow (capacity) can be smaller than lrh->lcns_follow. 2. lrh->target_vcn may be smaller than dp->vcn, making the u64 subtraction wrap to a huge size_t. Validate target VCN delta and per-record LCN count against the DPT entry capacity, bail via the existing out: cleanup label with -EINVAL. This mirrors the bounds-check pattern added in commit b2bc7c44ed17 ("fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot") and commit 0ca0485e4b2e ("fs/ntfs3: validate rec->used in journal-replay file record check"). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Reported-by: Yunpeng Tian Reported-by: Mingda Zhang Reported-by: Gongming Wang Reported-by: Peiyuan Xu Reported-by: Qinrun Dai Cc: stable@vger.kernel.org Signed-off-by: Yunpeng Tian Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 90d1de22fee68..5d9a2e1159af9 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -4546,11 +4546,21 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } + + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; From f71e8d9875069fa73e335f63f02ec6e52e3aaa51 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 17:30:28 +0900 Subject: [PATCH 0290/3297] NTB: epf: Avoid calling pci_irq_vector() from hardirq context commit 4dcddc1c794d1c65eda68f1f8dd04a0fecc0870f upstream. ntb_epf_vec_isr() calls pci_irq_vector() in hardirq context to derive the vector number. pci_irq_vector() calls msi_get_virq() that takes a mutex and can therefore trigger "scheduling while atomic" splats: BUG: scheduling while atomic: kworker/u33:0/55/0x00010001 ... Call trace: ... schedule+0x38/0x110 schedule_preempt_disabled+0x28/0x50 __mutex_lock.constprop.0+0x848/0x908 __mutex_lock_slowpath+0x18/0x30 mutex_lock+0x4c/0x60 msi_domain_get_virq+0xe8/0x138 pci_irq_vector+0x2c/0x60 ntb_epf_vec_isr+0x28/0x120 [ntb_hw_epf] __handle_irq_event_percpu+0x70/0x3a8 handle_irq_event+0x48/0x100 handle_edge_irq+0x100/0x1c8 ... Cache the Linux IRQ number for vector 0 when vectors are allocated and use it as a base in the ISR. Running the ISR in a threaded IRQ handler would also avoid the problem, but that would be unnecessary here. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Cc: stable@vger.kernel.org # v5.12+ Link: https://patch.msgid.link/20260304083028.1391068-3-den@valinux.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/ntb/hw/epf/ntb_hw_epf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 9935da48a52ea..8aaf8f9bc69c9 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -92,6 +92,7 @@ struct ntb_epf_dev { int db_val; u64 db_valid_mask; + int irq_base; }; #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) @@ -318,7 +319,7 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) struct ntb_epf_dev *ndev = dev; int irq_no; - irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0); + irq_no = irq - ndev->irq_base; ndev->db_val = irq_no + 1; if (irq_no == 0) @@ -350,6 +351,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument &= ~MSIX_ENABLE; } + ndev->irq_base = pci_irq_vector(pdev, 0); for (i = 0; i < irq; i++) { ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, 0, "ntb_epf", ndev); From 4750909a40da9016185e0ac991510a278cecb1e7 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:35 +0800 Subject: [PATCH 0291/3297] gpio: eic-sprd: use raw_spinlock_t in the irq startup path commit 90f0109019e6817eb40a486671b7722d1544ae29 upstream. sprd_eic_irq_unmask() enables the GPIO IRQ and then updates controller state through sprd_eic_update(), which takes sprd_eic->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sprd_eic_irq_unmask() -> sprd_eic_update() carrier and used the original spin_lock_irqsave(&sprd_eic->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sprd_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sprd_eic_update.constprop.0+0x48/0x90 [vuln_msv] sprd_eic_irq_unmask.constprop.0+0x35/0x50 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the Spreadtrum EIC controller lock to raw_spinlock_t. The locked section only serializes MMIO register updates and does not contain sleepable operations, so keeping it non-sleeping is appropriate for the irqchip callbacks. Fixes: 25518e024e3a ("gpio: Add Spreadtrum EIC driver support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260617154035.1199948-3-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-eic-sprd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 50fafeda8d7e2..3b7ebcf12fe7f 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -95,7 +95,7 @@ struct sprd_eic { struct notifier_block irq_nb; void __iomem *base[SPRD_EIC_MAX_BANK]; enum sprd_eic_type type; - spinlock_t lock; + raw_spinlock_t lock; int irq; }; @@ -149,7 +149,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, unsigned long flags; u32 tmp; - spin_lock_irqsave(&sprd_eic->lock, flags); + raw_spin_lock_irqsave(&sprd_eic->lock, flags); tmp = readl_relaxed(base + reg); if (val) @@ -158,7 +158,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset, tmp &= ~BIT(SPRD_EIC_BIT(offset)); writel_relaxed(tmp, base + reg); - spin_unlock_irqrestore(&sprd_eic->lock, flags); + raw_spin_unlock_irqrestore(&sprd_eic->lock, flags); } static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg) @@ -628,7 +628,7 @@ static int sprd_eic_probe(struct platform_device *pdev) if (!sprd_eic) return -ENOMEM; - spin_lock_init(&sprd_eic->lock); + raw_spin_lock_init(&sprd_eic->lock); sprd_eic->type = pdata->type; sprd_eic->irq = platform_get_irq(pdev, 0); From 4508366ab7dd0c2917a51a9c2e23cc1b9d35157a Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:40:34 +0800 Subject: [PATCH 0292/3297] gpio: sch: use raw_spinlock_t in the irq startup path commit 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b upstream. sch_irq_unmask() enables the GPIO IRQ and then updates the controller state through sch_irq_mask_unmask(), which takes sch->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and used the original spin_lock_irqsave(&sch->lock) edge. Lockdep reported: BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv] Convert the SCH controller lock to raw_spinlock_t. The same lock is also used by the GPIO direction and value callbacks, but those critical sections only update MMIO-backed GPIO registers and do not contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. Fixes: 7a81638485c1 ("gpio: sch: Add edge event support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Sebastian Andrzej Siewior Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260617154035.1199948-2-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-sch.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 966d16a6d5157..5e361742a11ad 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -39,7 +39,7 @@ struct sch_gpio { struct gpio_chip chip; void __iomem *regs; - spinlock_t lock; + raw_spinlock_t lock; unsigned short resume_base; /* GPE handling */ @@ -104,9 +104,9 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -122,9 +122,9 @@ static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GLV, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -135,9 +135,9 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GIO, 0); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); /* * according to the datasheet, writing to the level register has no @@ -196,14 +196,14 @@ static int sch_irq_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTPE, rising); sch_gpio_reg_set(sch, gpio_num, GTNE, falling); irq_set_handler_locked(d, handle_edge_irq); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); return 0; } @@ -215,9 +215,9 @@ static void sch_irq_ack(struct irq_data *d) irq_hw_number_t gpio_num = irqd_to_hwirq(d); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GTS, 1); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val) @@ -225,9 +225,9 @@ static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GGPE, val); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); } static void sch_irq_mask(struct irq_data *d) @@ -268,12 +268,12 @@ static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) int offset; u32 ret; - spin_lock_irqsave(&sch->lock, flags); + raw_spin_lock_irqsave(&sch->lock, flags); core_status = ioread32(sch->regs + CORE_BANK_OFFSET + GTS); resume_status = ioread32(sch->regs + RESUME_BANK_OFFSET + GTS); - spin_unlock_irqrestore(&sch->lock, flags); + raw_spin_unlock_irqrestore(&sch->lock, flags); pending = (resume_status << sch->resume_base) | core_status; for_each_set_bit(offset, &pending, sch->chip.ngpio) @@ -343,7 +343,7 @@ static int sch_gpio_probe(struct platform_device *pdev) sch->regs = regs; - spin_lock_init(&sch->lock); + raw_spin_lock_init(&sch->lock); sch->chip = sch_gpio_chip; sch->chip.label = dev_name(dev); sch->chip.parent = dev; From 722869fcff598fad20d5ab79c305897a7534708b Mon Sep 17 00:00:00 2001 From: Vasileios Almpanis Date: Mon, 15 Jun 2026 16:45:57 +0200 Subject: [PATCH 0293/3297] io_uring/nop: fix file reference leak with IOSQE_FIXED_FILE commit 2564ca2e31bd8ee8348362941af2ee4671e487ca upstream. NOP file-acquisition support choses between a fixed (registered) file and a normal fget()'d file based on its own IORING_NOP_FIXED_FILE flag in sqe->nop_flags. However, a request's REQ_F_FIXED_FILE is set independently from the generic IOSQE_FIXED_FILE sqe flag during request init, before the issue handler runs. If a NOP is submitted with IOSQE_FIXED_FILE set (so REQ_F_FIXED_FILE is set) but without IORING_NOP_FIXED_FILE, io_nop() takes the normal path and grabs a real reference via io_file_get_normal(). On completion, io_put_file() only drops the reference when REQ_F_FIXED_FILE is clear, so the fget()'d file is never released and leaks: BUG: memory leak unreferenced object 0xffff88800f42c240 (size 176): kmem_cache_alloc_noprof+0x358/0x440 alloc_empty_file+0x57/0x180 path_openat+0x44/0x1e50 do_file_open+0x121/0x200 do_sys_openat2+0xa7/0x150 __x64_sys_openat+0x82/0xf0 Decide between fixed and normal file acquisition from REQ_F_FIXED_FILE, the same way io_assign_file() does for every other opcode, and fold IORING_NOP_FIXED_FILE into REQ_F_FIXED_FILE at prep time. Cc: stable@vger.kernel.org Fixes: a85f31052bce ("io_uring/nop: add support for testing registered files and buffers") Reported-by: syzbot+2cd473471e77bda12b0e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?id=879092631b98f73a28ea405adacfa5bb34a14a25 Signed-off-by: Vasileios Almpanis Link: https://patch.msgid.link/20260615144619.482749-1-vasilisalmpanis@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/nop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/io_uring/nop.c b/io_uring/nop.c index f5c9969e7f64a..2f9aa48832619 100644 --- a/io_uring/nop.c +++ b/io_uring/nop.c @@ -41,6 +41,8 @@ int io_nop_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) nop->fd = READ_ONCE(sqe->fd); else nop->fd = -1; + if (nop->flags & IORING_NOP_FIXED_FILE) + req->flags |= REQ_F_FIXED_FILE; if (nop->flags & IORING_NOP_FIXED_BUFFER) req->buf_index = READ_ONCE(sqe->buf_index); if (nop->flags & IORING_NOP_CQE32) { @@ -60,12 +62,10 @@ int io_nop(struct io_kiocb *req, unsigned int issue_flags) int ret = nop->result; if (nop->flags & IORING_NOP_FILE) { - if (nop->flags & IORING_NOP_FIXED_FILE) { + if (req->flags & REQ_F_FIXED_FILE) req->file = io_file_get_fixed(req, nop->fd, issue_flags); - req->flags |= REQ_F_FIXED_FILE; - } else { + else req->file = io_file_get_normal(req, nop->fd); - } if (!req->file) { ret = -EBADF; goto done; From 1636d85dc139b07c0449308f2bb5e0c7a2e0da99 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 28 May 2026 01:22:03 +0800 Subject: [PATCH 0294/3297] io_uring/io-wq: re-check IO_WQ_BIT_EXIT for each linked work item commit 29bef9934b2521f787bb15dd1985d4c0d12ae02a upstream. commit 10dc95939817 ("io_uring/io-wq: check IO_WQ_BIT_EXIT inside work run loop") fixed the obvious case where io_worker_handle_work() took one exit-bit snapshot before draining pending work, but the fix stops one level too early. io_worker_handle_work() now re-checks IO_WQ_BIT_EXIT in its outer work run loop, yet it still snapshots that bit once before processing a whole dependent linked-work chain. If io_wq_exit_start() sets IO_WQ_BIT_EXIT after the first linked item has started, the remaining linked items can still reuse stale do_kill = false, skip IO_WQ_WORK_CANCEL, and continue running after exit has begun. Move the check further inside, so it covers linked items too. Note: this is a syzbot special as it loves setting up tons of slow linked work on weird devices like msr that take forever to read, and immediately close the ring. Exit then takes a long time. Fixes: 10dc95939817 ("io_uring/io-wq: check IO_WQ_BIT_EXIT inside work run loop") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260527172203.2043962-1-runyu.xiao@seu.edu.cn Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/io-wq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index bd47d32f71c25..af00f942a75fa 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -601,7 +601,6 @@ static void io_worker_handle_work(struct io_wq_acct *acct, struct io_wq *wq = worker->wq; do { - bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); struct io_wq_work *work; /* @@ -637,6 +636,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct, /* handle a whole dependent link */ do { + bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); struct io_wq_work *next_hashed, *linked; unsigned int work_flags = atomic_read(&work->flags); unsigned int hash = __io_wq_is_hashed(work_flags) From f090acf881a262a51a3347f7e98991011c329ec4 Mon Sep 17 00:00:00 2001 From: Michael Wigham Date: Sat, 13 Jun 2026 23:52:16 +0100 Subject: [PATCH 0295/3297] io_uring/rw: preserve partial result for iopoll commit c554246ff4c68abf71b61a89c6e39d3cf94f523e upstream. A partial read will store the completed byte count in io->bytes_done. The regular completion path applies io_fixup_rw_res() so that, when the following operation reaches EOF, the number of bytes already read is returned. The iopoll completion path does not apply this fixup to the return value and can return zero instead. Use the fixup result when updating the CQE, and the raw result for the reissue check. Cc: stable@vger.kernel.org Fixes: 4d9cb92ca41d ("io_uring/rw: fix short rw error handling") Signed-off-by: Michael Wigham Link: https://patch.msgid.link/20260613225240.34032-1-michael@wigham.net Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/rw.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/io_uring/rw.c b/io_uring/rw.c index d7388a4a3ea5e..1a5dee8fe619f 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -611,15 +611,15 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res) { struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb); struct io_kiocb *req = cmd_to_io_kiocb(rw); + int final_res = io_fixup_rw_res(req, res); if (kiocb->ki_flags & IOCB_WRITE) io_req_end_write(req); - if (unlikely(res != req->cqe.res)) { - if (res == -EAGAIN && io_rw_should_reissue(req)) - req->flags |= REQ_F_REISSUE | REQ_F_BL_NO_RECYCLE; - else - req->cqe.res = res; - } + + if (res == -EAGAIN && io_rw_should_reissue(req)) + req->flags |= REQ_F_REISSUE | REQ_F_BL_NO_RECYCLE; + else if (unlikely(final_res != req->cqe.res)) + req->cqe.res = final_res; /* order with io_iopoll_complete() checking ->iopoll_completed */ smp_store_release(&req->iopoll_completed, 1); From a33f37f8d079da7236ed7b7e2aed2a34ab81e7cf Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 25 Jun 2026 05:03:18 -0700 Subject: [PATCH 0296/3297] netpoll: fix a use-after-free on shutdown path commit 45f1458a85017a023f138b22ac5c76abd477db42 upstream. There is a use-after-free error on netpoll, which is clearly detected by KASAN. BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x3b/0x80 Read of size 1 at addr ... by task kworker/9:1 Workqueue: events queue_process Call Trace: skb_dequeue+0x1e/0xb0 queue_process+0x2c/0x600 process_scheduled_works+0x4b6/0x850 worker_thread+0x414/0x5a0 Allocated by task 242: __netpoll_setup+0x201/0x4a0 netpoll_setup+0x249/0x550 enabled_store+0x32f/0x380 Freed by task 0: kfree+0x1b7/0x540 rcu_core+0x3f8/0x7a0 The problem happens when there is a pending TX worker running in parallel with the cleanup path. This is what happens on netpoll shutdown path: 1) __netpoll_cleanup() is called 2) set dev->npinfo to NULL 3) call_rcu() with rcu_cleanup_netpoll_info() 3.1) rcu_cleanup_netpoll_info() tries to cancel all workers with cancel_delayed_work(), but doesn't wait for the worker to finish 4) and kfree(npinfo); Because 3.1) doesn't really cancel the work, as the comment says "we can't call cancel_delayed_work_sync here, as we are in softirq", the TX worker can run after 4). Tl;DR: queue_process() is not an RCU reader, it reaches npinfo through the work item via container_of(). Use disable_delayed_work_sync() to ensure the worker is completely stopped and prevent any future re-arming attempts. Once npinfo is set to NULL, senders will bail out and not queue new work. The disable flag ensures any in-flight re-arming attempts also fail silently. In the future, we can do the cleanup inline here without needing the npinfo->rcu rcu_head, but that is net-next material. Cc: stable@vger.kernel.org Fixes: 38e6bc185d95 ("netpoll: make __netpoll_cleanup non-block") Reviewed-by: Pavan Chebbi Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20260625-netpoll_rcu_fix-v2-1-0748ffac1e98@debian.org Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/netpoll.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 854e37c41653b..9ef344a6932f9 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -812,14 +812,6 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) container_of(rcu_head, struct netpoll_info, rcu); skb_queue_purge(&npinfo->txq); - - /* we can't call cancel_delayed_work_sync here, as we are in softirq */ - cancel_delayed_work(&npinfo->tx_work); - - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - /* now cancel it again */ - cancel_delayed_work(&npinfo->tx_work); kfree(npinfo); } @@ -843,6 +835,7 @@ static void __netpoll_cleanup(struct netpoll *np) ops->ndo_netpoll_cleanup(np->dev); RCU_INIT_POINTER(np->dev->npinfo, NULL); + disable_delayed_work_sync(&npinfo->tx_work); call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info); } From 2ca18df1c2611f70eb3eb487e02ae85eb703b284 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Thu, 2 Jul 2026 08:50:14 +0900 Subject: [PATCH 0297/3297] ipv4: igmp: remove multicast group from hash table on device destruction commit 7993211bde166471dffac074dc965489f86531f8 upstream. When a device is destroyed under RTNL, ip_mc_destroy_dev() iterates through the multicast list and calls ip_ma_put() on each membership, scheduling them for RCU reclamation. However, they are not unlinked from the device's multicast hash table (mc_hash). Since the device remains published in dev->ip_ptr until after ip_mc_destroy_dev() completes, concurrent RCU readers traversing mc_hash can still locate and access the multicast group after its refcount is decremented. If the RCU callback runs and frees the group while a reader is accessing it, a use-after-free occurs. Fix this by unlinking the multicast group from mc_hash using ip_mc_hash_remove() before scheduling it for reclamation. BUG: KASAN: slab-use-after-free in ip_check_mc_rcu+0x149/0x3f0 Read of size 4 at addr ffff888009bf1408 by task mausezahn/2276 Call Trace: dump_stack_lvl+0x67/0x90 print_report+0x175/0x7c0 kasan_report+0x147/0x180 ip_check_mc_rcu+0x149/0x3f0 udp_v4_early_demux+0x36d/0x12d0 ip_rcv_finish_core+0xb8b/0x1390 ip_rcv_finish+0x54/0x120 NF_HOOK+0x213/0x2b0 __netif_receive_skb+0x126/0x340 process_backlog+0x4f2/0xf00 __napi_poll+0x92/0x2c0 net_rx_action+0x583/0xc60 handle_softirqs+0x236/0x7f0 do_softirq+0x57/0x80 Allocated by task 2239: kasan_save_track+0x3e/0x80 __kasan_kmalloc+0x72/0x90 ____ip_mc_inc_group+0x31a/0xa40 __ip_mc_join_group+0x334/0x3f0 do_ip_setsockopt+0x16fa/0x2010 ip_setsockopt+0x3f/0x90 do_sock_setsockopt+0x1ad/0x300 Freed by task 0: kasan_save_track+0x3e/0x80 kasan_save_free_info+0x40/0x50 __kasan_slab_free+0x3a/0x60 __rcu_free_sheaf_prepare+0xd4/0x220 rcu_free_sheaf+0x36/0x190 rcu_core+0x8d9/0x12f0 handle_softirqs+0x236/0x7f0 Fixes: e9897071350b ("igmp: hash a hash table to speedup ip_check_mc_rcu()") Cc: stable@vger.kernel.org Signed-off-by: Yuyang Huang Reviewed-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260701235014.73505-1-yuyanghuang@google.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/ipv4/igmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 0adc993c211d7..449cb4ba6153e 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1904,6 +1904,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev) #endif while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { + ip_mc_hash_remove(in_dev, i); in_dev->mc_list = i->next_rcu; in_dev->mc_count--; ip_mc_clear_src(i); From bbae351c0f32f7c200249e4aa6561b2b419dcf69 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Mon, 15 Jun 2026 18:31:18 +0800 Subject: [PATCH 0298/3297] net: ipv4: bound TCP reordering sysctl writes and MTU probe sizes commit efb8763d7bbb40cff4cc55a6b62c3095a038149c upstream. Reject invalid `net.ipv4.tcp_reordering` values before they reach TCP socket state. The sysctl is stored as an `int` but copied into the `u32` `tp->reordering` field for new sockets, so negative writes wrap to large values. With `tcp_mtu_probing=2`, the wrapped value can overflow the `tcp_mtu_probe()` size calculation and drive the MTU probing path into an out-of-bounds read. Route `tcp_reordering` writes through `proc_dointvec_minmax()` and require it to be at least 1. Also require `tcp_max_reordering` to be at least 1 so the configured maximum cannot become negative either. When registering the table for a non-init network namespace, relocate `extra2` pointers that refer into `init_net.ipv4` so the `tcp_reordering` upper bound follows that namespace's `tcp_max_reordering`. Harden `tcp_mtu_probe()` itself by computing `size_needed` as `u64`. This keeps the send queue and window checks from being bypassed through signed integer overflow. Fixes: 91cc17c0e5e5 ("[TCP]: MTUprobe: receiver window & data available checks fixed") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Suggested-by: Eric Dumazet Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/1a5b7e1ef4d70fbad8c8ee0b82d8405f3c964a3d.1781395200.git.bronzed_45_vested@icloud.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/sysctl_net_ipv4.c | 10 ++++++++-- net/ipv4/tcp_output.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index ce9b077343cac..afcbb9d48cfa2 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1040,7 +1040,9 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_tcp_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, + .extra2 = &init_net.ipv4.sysctl_tcp_max_reordering, }, { .procname = "tcp_retries1", @@ -1275,7 +1277,8 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_tcp_max_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, }, { .procname = "tcp_dsack", @@ -1640,6 +1643,9 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) */ table[i].mode &= ~0222; } + if (table[i].extra2 >= (void *)&init_net.ipv4 && + table[i].extra2 < (void *)(&init_net.ipv4 + 1)) + table[i].extra2 += (void *)net - (void *)&init_net; } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 6937b2b03ef93..318d548b606e5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2604,7 +2604,7 @@ static int tcp_mtu_probe(struct sock *sk) struct sk_buff *skb, *nskb, *next; struct net *net = sock_net(sk); int probe_size; - int size_needed; + u64 size_needed; int copy, len; int mss_now; int interval; @@ -2628,7 +2628,7 @@ static int tcp_mtu_probe(struct sock *sk) mss_now = tcp_current_mss(sk); probe_size = tcp_mtu_to_mss(sk, (icsk->icsk_mtup.search_high + icsk->icsk_mtup.search_low) >> 1); - size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache; + size_needed = probe_size + (tp->reordering + 1) * (u64)tp->mss_cache; interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low; /* When misfortune happens, we are reprobing actively, * and then reprobe timer has expired. We stick with current From b5f41d5bf08e7b1b14fa0bd640975e6d78dc006d Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 27 Apr 2026 13:17:21 +0000 Subject: [PATCH 0299/3297] mfd: cros_ec: Delay dev_set_drvdata() until probe success commit 8b2c1d41bc36c100b38ce5ee6def246c527eaf8a upstream. If ec_device_probe() fails, cros_ec_class_release releases memory for the cros_ec_dev structure. However, because the drvdata was already set, sub-drivers like cros_ec_typec can still retrieve the stale pointer via the platform device. This leads to a use-after-free when cros_ec_typec attempts to access &typec->ec->ec->dev on a device that has already been released. Move dev_set_drvdata() to ensure that the pointer is only made available once all initialization steps have succeeded. sysfs: cannot create duplicate filename '/class/chromeos/cros_ec' Call trace: sysfs_do_create_link_sd+0x94/0xdc sysfs_create_link+0x30/0x44 device_add_class_symlinks+0x90/0x13c device_add+0xf0/0x50c ec_device_probe+0x150/0x4f0 platform_probe+0xa0/0xe0 ... BUG: KASAN: invalid-access in __memcpy+0x44/0x230 Write at addr f5ffff809e2d33ac by task kworker/u32:5/125 Pointer tag: [f5], memory tag: [fe] Tainted : [W]=WARN, [O]=OOT_MODULE Hardware name: Google Navi unprovisioned 0x7FFFFFFF/sku0 board/sku3 Workqueue: events_unbound deferred_probe_work_func Call trace: __memcpy+0x44/0x230 cros_ec_check_features+0x60/0xcc [cros_ec_proto] cros_typec_probe+0xe8/0x6e0 [cros_ec_typec] platform_probe+0xa0/0xe0 Cc: stable@vger.kernel.org Fixes: 1c1d152cc5ac ("platform/chrome: cros_ec_dev - utilize new cdev_device_add helper function") Co-developed-by: Sergey Senozhatsky Signed-off-by: Sergey Senozhatsky Signed-off-by: Andrei Kuchynski Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260427131721.1165078-1-akuchynski@chromium.org Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/cros_ec_dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index dc80a272726bb..afd5e33647f07 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -195,7 +195,6 @@ static int ec_device_probe(struct platform_device *pdev) if (!ec) return retval; - dev_set_drvdata(dev, ec); ec->ec_dev = dev_get_drvdata(dev->parent); ec->dev = dev; ec->cmd_offset = ec_platform->cmd_offset; @@ -237,6 +236,8 @@ static int ec_device_probe(struct platform_device *pdev) if (retval) goto failed; + dev_set_drvdata(dev, ec); + /* check whether this EC is a sensor hub. */ if (cros_ec_get_sensor_count(ec) > 0) { retval = mfd_add_hotplug_devices(ec->dev, From 560e21e8ccff813e84d05f6500907c549a3d6985 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Wed, 10 Jun 2026 16:20:48 -0700 Subject: [PATCH 0300/3297] mm/shrinker: do not hold RCU lock in shrinker_debugfs_count_show() commit b902890c62d200b3509cb5e09cf1e0a66553c128 upstream. Reading the debugfs "count" file of a memcg-aware shrinker can sleep inside an RCU read-side critical section: BUG: sleeping function called from invalid context at kernel/cgroup/rstat.c:421 RCU nest depth: 1, expected: 0 css_rstat_flush mem_cgroup_flush_stats zswap_shrinker_count shrinker_debugfs_count_show shrinker_debugfs_count_show() invokes the ->count_objects() callback under rcu_read_lock(). The zswap callback flushes memcg stats via css_rstat_flush(), which may sleep, so it must not run under RCU. The RCU lock is not needed here. mem_cgroup_iter() takes RCU internally and returns a memcg holding a css reference (dropped on the next iteration or by mem_cgroup_iter_break()), so the memcg stays alive without it. The shrinker is kept alive by the open debugfs file: shrinker_free() removes the debugfs entries via debugfs_remove_recursive(), which waits for in-flight readers to drain, before call_rcu(..., shrinker_free_rcu_cb). The sibling "scan" handler already invokes the sleeping ->scan_objects() callback with no RCU section. Drop the rcu_read_lock()/rcu_read_unlock(). Link: https://lore.kernel.org/20260610232048.62930-1-shakeel.butt@linux.dev Fixes: 5035ebc644ae ("mm: shrinkers: introduce debugfs interface for memory shrinkers") Signed-off-by: Shakeel Butt Reported-by: Zenghui Yu Closes: https://lore.kernel.org/all/c052a064-cddb-494f-a0d8-f8a10b4b1c4d@linux.dev/ Suggested-by: Nhat Pham Reviewed-by: SeongJae Park Reviewed-by: Qi Zheng Tested-by: Zenghui Yu (Huawei) Reviewed-by: Nhat Pham Acked-by: Muchun Song Reviewed-by: Roman Gushchin Cc: Dave Chinner Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/shrinker_debug.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index 20eaee3e97f7a..5fa71476b1ee8 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -57,8 +57,6 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v) if (!count_per_node) return -ENOMEM; - rcu_read_lock(); - memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE; memcg = mem_cgroup_iter(NULL, NULL, NULL); @@ -88,8 +86,6 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v) } } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL); - rcu_read_unlock(); - kfree(count_per_node); return ret; } From 6465ff3ce65131c774a312d450abe10f4b9f3875 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 16:56:58 +0800 Subject: [PATCH 0301/3297] mm: shrinker: fix shrinker_info teardown race with expansion commit 65476d31d8056e859c48580f82295ce159196ffe upstream. expand_shrinker_info() iterates all visible memcgs under shrinker_mutex, including memcgs that have not finished ->css_online() yet. Once pn->shrinker_info has been published, teardown must stay serialized with expand_shrinker_info() until that memcg is either fully online or no longer visible to iteration. Today alloc_shrinker_info() breaks that rule by dropping shrinker_mutex before freeing a partially initialized shrinker_info array, which may cause the following race: CPU0 CPU1 ==== ==== css_create --> list_add_tail_rcu(&css->sibling, &parent_css->children); online_css --> mem_cgroup_css_online --> alloc_shrinker_info --> alloc node0 info rcu_assign_pointer(C->node0->shrinker_info, old0) alloc node1 info -> FAIL -> goto err mutex_unlock(shrinker_mutex) shrinker_alloc() --> shrinker_memcg_alloc --> mutex_lock(shrinker_mutex) expand_shrinker_info --> mem_cgroup_iter see the memcg expand_one_shrinker_info --> old0 = C->node0->shrinker_info memcpy(new->unit, old0->unit, ...); free_shrinker_info --> kvfree(old0); /* double free !! */ kvfree_rcu(old0, rcu); The same problem exists later in mem_cgroup_css_online(). If alloc_shrinker_info() succeeds but a subsequent objcg allocation fails, the free_objcg -> free_shrinker_info() unwind path tears down the already published pn->shrinker_info arrays without shrinker_mutex. The expand_one_shrinker_info() can race with that teardown in the same way, leading to use-after-free or double-free of the old shrinker_info. Fix this by serializing shrinker_info teardown with shrinker_mutex, and by keeping alloc_shrinker_info() error cleanup inside the locked section. Link: https://lore.kernel.org/20260617085658.27096-1-qi.zheng@linux.dev Fixes: 307bececcd12 ("mm: shrinker: add a secondary array for shrinker_info::{map, nr_deferred}") Signed-off-by: Qi Zheng Acked-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/shrinker.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mm/shrinker.c b/mm/shrinker.c index 4a93fd433689a..cf730374fbc0c 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -59,12 +59,14 @@ static inline int shrinker_unit_alloc(struct shrinker_info *new, return 0; } -void free_shrinker_info(struct mem_cgroup *memcg) +static void __free_shrinker_info(struct mem_cgroup *memcg) { struct mem_cgroup_per_node *pn; struct shrinker_info *info; int nid; + lockdep_assert_held(&shrinker_mutex); + for_each_node(nid) { pn = memcg->nodeinfo[nid]; info = rcu_dereference_protected(pn->shrinker_info, true); @@ -74,6 +76,13 @@ void free_shrinker_info(struct mem_cgroup *memcg) } } +void free_shrinker_info(struct mem_cgroup *memcg) +{ + mutex_lock(&shrinker_mutex); + __free_shrinker_info(memcg); + mutex_unlock(&shrinker_mutex); +} + int alloc_shrinker_info(struct mem_cgroup *memcg) { int nid, ret = 0; @@ -98,8 +107,8 @@ int alloc_shrinker_info(struct mem_cgroup *memcg) return ret; err: + __free_shrinker_info(memcg); mutex_unlock(&shrinker_mutex); - free_shrinker_info(memcg); return -ENOMEM; } From 006467ab932698612398f853344a7405164541f4 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 17:00:52 +0800 Subject: [PATCH 0302/3297] mm: shrinker: fix NULL pointer dereference in debugfs commit e30453c61e185e914fde83c650e268067b140218 upstream. shrinker_debugfs_add() creates both "count" and "scan" debugfs files unconditionally. That assumes every shrinker implements both count_objects() and scan_objects(), which is not guaranteed. For example, the xen-backend shrinker sets count_objects() but leaves scan_objects() NULL, so writing to its scan file calls through a NULL function pointer and panics the kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. Call Trace: shrinker_debugfs_scan_write+0x12e/0x270 full_proxy_write+0x5f/0x90 vfs_write+0xde/0x420 ? filp_flush+0x75/0x90 ? filp_close+0x1d/0x30 ? do_dup2+0xb8/0x120 ksys_write+0x68/0xf0 ? filp_flush+0x75/0x90 do_syscall_64+0xb3/0x5b0 entry_SYSCALL_64_after_hwframe+0x76/0x7e The count path has the same issue in principle if a shrinker omits count_objects(). To fix it, only create "count" and "scan" debugfs files when the corresponding callbacks are present. Link: https://lore.kernel.org/20260617090052.27325-1-qi.zheng@linux.dev Fixes: bbf535fd6f06 ("mm: shrinkers: add scan interface for shrinker debugfs") Signed-off-by: Qi Zheng Reviewed-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/shrinker_debug.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index 5fa71476b1ee8..5de59e853b070 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -182,10 +182,12 @@ int shrinker_debugfs_add(struct shrinker *shrinker) } shrinker->debugfs_entry = entry; - debugfs_create_file("count", 0440, entry, shrinker, - &shrinker_debugfs_count_fops); - debugfs_create_file("scan", 0220, entry, shrinker, - &shrinker_debugfs_scan_fops); + if (shrinker->count_objects) + debugfs_create_file("count", 0440, entry, shrinker, + &shrinker_debugfs_count_fops); + if (shrinker->scan_objects) + debugfs_create_file("scan", 0220, entry, shrinker, + &shrinker_debugfs_scan_fops); return 0; } From b415c00bf23df577a4a95673d00ae76687bcc1d4 Mon Sep 17 00:00:00 2001 From: "Jose Fernandez (Anthropic)" Date: Mon, 4 May 2026 12:55:17 +0000 Subject: [PATCH 0303/3297] mm: swap_cgroup: fix NULL deref in lookup_swap_cgroup_id on swapless host commit 63b02a9409cb5180398491b093e48bcb5315f5fb upstream. lookup_swap_cgroup_id() passes swap_cgroup_ctrl[type].map to __swap_cgroup_id_lookup() without checking that the type was ever registered via swap_cgroup_swapon(). On a swapless host every ctrl->map is NULL, so __swap_cgroup_id_lookup() dereferences NULL + a scaled swp_offset(). Since commit bea67dcc5eea ("mm: attempt to batch free swap entries for zap_pte_range()"), zap_pte_range() -> swap_pte_batch() calls lookup_swap_cgroup_id() on any non-present, non-none PTE that decodes as a real swap entry, without first validating it against swap_info[]. A single PTE corrupted into a type-0 swap entry takes the host down at process exit. We hit this in production on a swapless 6.12.58 host: ~1s of "get_swap_device: Bad swap file entry 3f800204222bb" (do_swap_page() being correctly defensive about the same entry) followed by BUG: unable to handle page fault for address: 000003f800204220 RIP: 0010:lookup_swap_cgroup_id+0x2b/0x60 Call Trace: swap_pte_batch+0xbf/0x230 zap_pte_range+0x4c8/0x780 unmap_page_range+0x190/0x3e0 exit_mmap+0xd9/0x3c0 do_exit+0x20c/0x4b0 syzbot has reported the identical stack. The source of the PTE corruption is a separate bug; this change makes the teardown path as robust as the fault path already is. Every other caller of lookup_swap_cgroup_id() is downstream of a get_swap_device() that has already validated the entry, so the new branch is cold. Link: https://lore.kernel.org/20260504-swap-cgroup-fix-7-0-v1-1-f53ff41ee553@linux.dev Fixes: bea67dcc5eea ("mm: attempt to batch free swap entries for zap_pte_range()") Signed-off-by: Jose Fernandez (Anthropic) Reported-by: syzbot+e12bd9ca48157add237a@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/69859728.050a0220.3b3015.0033.GAE@google.com Assisted-by: Claude:unspecified Cc: Barry Song Cc: David Hildenbrand Cc: Hugh Dickins Cc: Johannes Weiner Cc: Kairui Song Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Shakeel Butt Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/swap_cgroup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/swap_cgroup.c b/mm/swap_cgroup.c index de779fed8c210..95c38e54dd587 100644 --- a/mm/swap_cgroup.c +++ b/mm/swap_cgroup.c @@ -124,6 +124,8 @@ unsigned short lookup_swap_cgroup_id(swp_entry_t ent) return 0; ctrl = &swap_cgroup_ctrl[swp_type(ent)]; + if (unlikely(!ctrl->map)) + return 0; return __swap_cgroup_id_lookup(ctrl->map, swp_offset(ent)); } From 69c0e6246575b780ae0d3f411c749bcf13c221f3 Mon Sep 17 00:00:00 2001 From: Zijiang Huang Date: Wed, 6 May 2026 21:09:19 +0800 Subject: [PATCH 0304/3297] mm/swap: add cond_resched() in swap_reclaim_full_clusters to prevent softlockup commit 66366d291f666ddeda5f8c84f253e308de3e6b55 upstream. We hit a real softlockup in an internal stress test environment. The workload was LTP memory/swap stress on a large arm64 machine, with 320 CPUs, about 1TB memory and an 8.6GB swap device. The system was under heavy load and the swap device had a large number of full clusters. The softlockup was triggered during a stress test after about 3 days. So, add periodic cond_resched() calls during large full_clusters reclaim operations to prevent softlockup issues. Detailed call trace as follow: PID: 3817773 TASK: ffff0883bb28b780 CPU: 48 COMMAND: "kworker/48:7" #0 [ffff800080183d10] __crash_kexec at ffffa4c1361e5de4 #1 [ffff800080183d90] panic at ffffa4c1360d5e9c #2 [ffff800080183e20] watchdog_timer_fn at ffffa4c136231fa8 ... #16 [ffff8000c4ad3cb0] swap_cache_del_folio at ffffa4c1363e1614 #17 [ffff8000c4ad3ce0] __try_to_reclaim_swap at ffffa4c1363e4bfc #18 [ffff8000c4ad3d40] swap_reclaim_full_clusters at ffffa4c1363e5474 #19 [ffff8000c4ad3da0] swap_reclaim_work at ffffa4c1363e550c #20 [ffff8000c4ad3dc0] process_one_work at ffffa4c136102edc #21 [ffff8000c4ad3e10] worker_thread at ffffa4c136103398 #22 [ffff8000c4ad3e70] kthread at ffffa4c13610d95c Link: https://lore.kernel.org/20260506130919.2298807-1-kerayhuang@tencent.com Fixes: 5168a68eb78f ("mm, swap: avoid over reclaim of full clusters") Signed-off-by: Zijiang Huang Reviewed-by: Kairui Song Reviewed-by: Hao Peng Reviewed-by: albinwyang Reviewed-by: Baoquan He Acked-by: Chris Li Cc: Barry Song Cc: Kairui Song Cc: Kemeng Shi Cc: Nhat Pham Cc: Youngjun Park Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/swapfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/swapfile.c b/mm/swapfile.c index ca0298a840cdd..28e522e72c3cb 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1008,6 +1008,7 @@ static void swap_reclaim_full_clusters(struct swap_info_struct *si, bool force) swap_cluster_unlock(ci); if (to_scan <= 0) break; + cond_resched(); } } From a8f03a3793289337eba31dba3a5f263f0d8ea3bf Mon Sep 17 00:00:00 2001 From: Pratham Gupta Date: Mon, 4 May 2026 22:11:57 -0700 Subject: [PATCH 0305/3297] netfilter: ctnetlink: use nf_ct_exp_net() in expectation dump commit a7f57320bbbc67e347bf5fff4b4a9bab980d5956 upstream. Commit 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") introduced exp->net so RCU-only expectation paths no longer need to dereference exp->master for netns lookups. Commit 3db5647984de ("netfilter: nf_conntrack_expect: skip expectations in other netns via proc") updated the proc path accordingly, but ctnetlink_exp_dump_table() still compares against nf_ct_net(exp->master). Use nf_ct_exp_net(exp) here as well so the netlink dump path matches the rest of the March 2026 expectation netns/RCU cleanup. Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") Cc: stable@vger.kernel.org Signed-off-by: Pratham Gupta Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index fbe9e3f1036f8..c8fe284b86db4 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3159,7 +3159,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) if (l3proto && exp->tuple.src.l3num != l3proto) continue; - if (!net_eq(nf_ct_net(exp->master), net)) + if (!net_eq(nf_ct_exp_net(exp), net)) continue; if (cb->args[1]) { From fc5bfe63bacf8a3ae307b62b34206406ca733354 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 5 Jul 2026 15:29:13 +0200 Subject: [PATCH 0306/3297] netfilter: handle unreadable frags commit da5b58478a9c1b85608c9e40a3b8432d071b409e upstream. sashiko reports: When an skb with unreadable fragments (such as from devmem TCP, where skb_frags_readable(skb) returns false) is processed by the u32 module, skb_copy_bits() will safely return a negative error code [..] xt_u32: bail out with hotdrop in this case. gather_frags: return -1, just as if we had no fragment header. nfnetlink_queue: restrict to the linear part. nfnetlink_log: restrict to the linear part. v2: - skb_zerocopy helpers don't copy readable flag, i.e. nfnetlink_queue is broken too xt_u32 shouldn't return true if hotdrop was set. Fixes: 65249feb6b3d ("net: add support for skbs with unreadable frags") Cc: stable@vger.kernel.org Acked-by: Mina Almasry Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/netfilter/nfnetlink_log.c | 26 ++++++++++++++++--------- net/netfilter/nfnetlink_queue.c | 16 +++++++++++---- net/netfilter/xt_u32.c | 16 ++++++++++----- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 64ab23ff559bb..7edca05dbfc93 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -418,7 +418,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) return -1; } if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) - BUG(); + return -1; if (nexthdr == NEXTHDR_AUTH) hdrlen = ipv6_authlen(&hdr); else diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 25a30bf722c69..a6989966d80d5 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -668,7 +668,7 @@ __build_packet_message(struct nfnl_log_net *log, goto nla_put_failure; if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) - BUG(); + goto nla_put_failure; } nlh->nlmsg_len = inst->skb->tail - old_tail; @@ -690,6 +690,21 @@ static const struct nf_loginfo default_loginfo = { }, }; +static unsigned int nfulnl_get_copy_len(const struct nf_loginfo *li, + const struct sk_buff *skb, + unsigned int copy_len) +{ + unsigned int len = skb->len; + + if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && + li->u.ulog.copy_len < copy_len) + copy_len = li->u.ulog.copy_len; + if (!skb_frags_readable(skb)) + len = skb_headlen(skb); + + return min(len, copy_len); +} + /* log handler for internal netfilter logging api */ static void nfulnl_log_packet(struct net *net, @@ -782,14 +797,7 @@ nfulnl_log_packet(struct net *net, break; case NFULNL_COPY_PACKET: - data_len = inst->copy_range; - if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && - (li->u.ulog.copy_len < data_len)) - data_len = li->u.ulog.copy_len; - - if (data_len > skb->len) - data_len = skb->len; - + data_len = nfulnl_get_copy_len(li, skb, inst->copy_range); size += nla_total_size(data_len); break; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 0a5aa6b90fc27..aa870e29b5004 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -676,6 +676,17 @@ static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr, } #endif +static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb, + unsigned int copy_range) +{ + unsigned int data_len = entskb->len; + + if (!skb_frags_readable(entskb)) + data_len = skb_headlen(entskb); + + return min(data_len, copy_range); +} + static struct sk_buff * nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry, @@ -741,10 +752,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, nf_queue_checksum_help(entskb)) return NULL; - data_len = READ_ONCE(queue->copy_range); - if (data_len > entskb->len) - data_len = entskb->len; - + data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range)); hlen = skb_zerocopy_headlen(entskb); hlen = min_t(unsigned int, hlen, data_len); size += sizeof(struct nlattr) + hlen; diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index 117d4615d6684..1ae7f926b0df7 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -14,8 +14,8 @@ #include #include -static bool u32_match_it(const struct xt_u32 *data, - const struct sk_buff *skb) +static int u32_match_it(const struct xt_u32 *data, + const struct sk_buff *skb) { const struct xt_u32_test *ct; unsigned int testind; @@ -40,7 +40,8 @@ static bool u32_match_it(const struct xt_u32 *data, return false; if (skb_copy_bits(skb, pos, &n, sizeof(n)) < 0) - BUG(); + return -1; + val = ntohl(n); nnums = ct->nnums; @@ -68,7 +69,7 @@ static bool u32_match_it(const struct xt_u32 *data, if (skb_copy_bits(skb, at + pos, &n, sizeof(n)) < 0) - BUG(); + return -1; val = ntohl(n); break; } @@ -90,9 +91,14 @@ static bool u32_match_it(const struct xt_u32 *data, static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_u32 *data = par->matchinfo; - bool ret; + int ret; ret = u32_match_it(data, skb); + if (ret < 0) { + par->hotdrop = true; + return false; + } + return ret ^ data->invert; } From 9f74d28e903fa4fdf82f870d0aeadddc8196e41c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 10:23:31 +0200 Subject: [PATCH 0307/3297] netfilter: ebtables: zero chainstack array commit cbfe53599eebffd188938ab6774cc41794f6f9d5 upstream. sashiko reports: looking at ebtables table translation, could a sparse cpu_possible_mask lead to an uninitialized pointer free? If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible, but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at CPU 2, the cleanup loop will blindly decrement and call vfree() on newinfo->chainstack[1]. Not a real-world bug, such allocation isn't expected to fail in the first place. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index ecd7baa25d726..f0fffb6a18d09 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -921,8 +921,7 @@ static int translate_table(struct net *net, const char *name, * if an error occurs */ newinfo->chainstack = - vmalloc_array(nr_cpu_ids, - sizeof(*(newinfo->chainstack))); + vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack))); if (!newinfo->chainstack) return -ENOMEM; for_each_possible_cpu(i) { From 13a5f532e3a4fc75c33060a026def1572c208643 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 12:05:15 +0200 Subject: [PATCH 0308/3297] netfilter: ebtables: module names must be null-terminated commit 084d23f818321390509e9738a0b08bbf46df6425 upstream. We need to explicitly check the length, else we may pass non-null terminated string to request_module(). Cc: stable@vger.kernel.org Fixes: bcf493428840 ("netfilter: ebtables: Fix extension lookup with identical name") Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f0fffb6a18d09..d0aa6a28f7a77 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, left - sizeof(struct ebt_entry_match) < m->match_size) return -EINVAL; + if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN) + return -EINVAL; + match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision); if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) { if (!IS_ERR(match)) From 6fe8d3cecd20bfaaaf440db3a06ba674d2f2e322 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 14:58:00 -0700 Subject: [PATCH 0309/3297] netfilter: ebtables: terminate table name before find_table_lock() commit a622d2e9608c9dff47fc2e5759ac7aa3a836b45d upstream. update_counters() and compat_update_counters() forward a user-supplied 32-byte table name to find_table_lock() without NUL-terminating it. On a lookup miss, find_inlist_lock() calls try_then_request_module(..., "%s%s", "ebtable_", name), and vsnprintf() reads past the name field and the stack object until it hits a zero byte. BUG: KASAN: stack-out-of-bounds in string (lib/vsprintf.c:648 lib/vsprintf.c:730) Read of size 1 at addr ffff8880119dfb20 by task exploit/147 Call Trace: ... string (lib/vsprintf.c:648 lib/vsprintf.c:730) vsnprintf (lib/vsprintf.c:2945) __request_module (kernel/module/kmod.c:150) do_update_counters.isra.0 (net/bridge/netfilter/ebtables.c:371 net/bridge/netfilter/ebtables.c:380) update_counters (net/bridge/netfilter/ebtables.c:1440) do_ebt_set_ctl (net/bridge/netfilter/ebtables.c:2573) nf_setsockopt (net/netfilter/nf_sockopt.c:101) ip_setsockopt (net/ipv4/ip_sockglue.c:1424) raw_setsockopt (net/ipv4/raw.c:847) __sys_setsockopt (net/socket.c:2393) ... compat_do_replace() shares the same unterminated name via compat_copy_ebt_replace_from_user(); terminate it there too so all find_table_lock() callers behave alike. The other callers already terminate the name after the copy. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index d0aa6a28f7a77..96176aa881117 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1436,6 +1436,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len) if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return -EINVAL; @@ -2275,6 +2277,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl, memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry)); + repl->name[sizeof(repl->name) - 1] = '\0'; + /* starting with hook_entry, 32 vs. 64 bit structures are different */ for (i = 0; i < NF_BR_NUMHOOKS; i++) repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]); @@ -2397,6 +2401,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg, if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + /* try real handler in case userland supplied needed padding */ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return update_counters(net, arg, len); From 81a5971cbe18b474b49c651e32f2194b2fb171fe Mon Sep 17 00:00:00 2001 From: Adrian Bente Date: Thu, 28 May 2026 10:08:51 +0300 Subject: [PATCH 0310/3297] netfilter: flowtable: fix offloaded ct timeout never being extended commit 53b3e60edb674b442b2b3bbdba484667b0f47a5d upstream. OpenWrt has recently migrated many platforms to kernel 6.18. On the MediaTek platform, which supports hardware network offloading, WiFi connections accelerated via the WED path were observed to drop after roughly 300 seconds. After several debugging sessions, assisted by the Claude LLM, the problem was narrowed down as follows: nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded flows using: cmpxchg(&ct->timeout, expires, new_timeout); 'expires' comes from nf_ct_expires(ct) and is a relative value, while ct->timeout holds an absolute timestamp. The two are never equal, so the cmpxchg always fails and the timeout is never extended. This goes unnoticed for most flows, but a long-lived hardware (WED) offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to zero, the conntrack entry is reaped and the connection breaks. Open-code the relative value from a single READ_ONCE(ct->timeout) snapshot and compare against that same absolute snapshot in the cmpxchg, so the timeout extension actually takes effect while the datapath remains authoritative if it updates ct->timeout concurrently. Fixes: 03428ca5cee9 ("netfilter: conntrack: rework offload nf_conn timeout extension logic") Cc: stable@vger.kernel.org Suggested-by: Florian Westphal Signed-off-by: Adrian Bente Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_flow_table_core.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 9441ac3d8c1a2..d3104816b7f71 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -500,8 +500,13 @@ static u32 nf_flow_table_tcp_timeout(const struct nf_conn *ct) */ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) { - static const u32 min_timeout = 5 * 60 * HZ; - u32 expires = nf_ct_expires(ct); + static const s32 min_timeout = 5 * 60 * HZ; + u32 ct_timeout = READ_ONCE(ct->timeout); + s32 expires; + + expires = ct_timeout - nfct_time_stamp; + if (expires <= 0) /* already expired */ + return; /* normal case: large enough timeout, nothing to do. */ if (likely(expires >= min_timeout)) @@ -519,7 +524,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) if (nf_ct_is_confirmed(ct) && test_bit(IPS_OFFLOAD_BIT, &ct->status)) { u8 l4proto = nf_ct_protonum(ct); - u32 new_timeout = true; + u32 new_timeout = 1; switch (l4proto) { case IPPROTO_UDP: @@ -544,7 +549,7 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) */ if (new_timeout) { new_timeout += nfct_time_stamp; - cmpxchg(&ct->timeout, expires, new_timeout); + cmpxchg(&ct->timeout, ct_timeout, new_timeout); } } From 0039bdde36b23ccf1196635f1d52c5490481544d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 9 Jun 2026 21:10:06 +0900 Subject: [PATCH 0311/3297] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work() commit a257407e2bbbb099ed427719a50563f67fa366d8 upstream. Every once in a while we see a hung btmtksdio_flush() task: INFO: task kworker/u17:0:189 blocked for more than 122 seconds. __cancel_work_timer+0x3f4/0x460 cancel_work_sync+0x1c/0x2c btmtksdio_flush+0x2c/0x40 hci_dev_open_sync+0x10c4/0x2190 [..] It all boils down to incorrect time_is_before_jiffies() usage in btmtksdio_txrx_work(). The btmtksdio_txrx_work() loop is expected to be terminated if running for longer than 5*HZ. However the timeout check is twisted: time_is_before_jiffies(old_jiffies + 5*HZ) evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a timeout has occurred. Using OR with time_is_before_jiffies(txrx_timeout) means that: - before the 5-second timeout: the condition is `int_status || false`, so it loops as long as there are pending interrupts. - after the 5-second timeout: the condition becomes `int_status || true`, which is always true. When the loop becomes infinite btmtksdio_txrx_work() loop never terminates and never releases the SDIO host. Fix loop termination condition to actually enforce a 5*HZ timeout. Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work") Cc: stable@vger.kernel.org Signed-off-by: Sergey Senozhatsky Reviewed-by: Sean Wang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btmtksdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 62db31bd65928..d2583bc742632 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -607,7 +607,7 @@ static void btmtksdio_txrx_work(struct work_struct *work) if (btmtksdio_rx_packet(bdev, rx_size) < 0) bdev->hdev->stat.err_rx++; } - } while (int_status || time_is_before_jiffies(txrx_timeout)); + } while (int_status && time_is_after_jiffies(txrx_timeout)); /* Enable interrupt */ if (bdev->func->irq_handler) From a6b22dbd80926556290ad2243be25218d6956a19 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:50:58 +0200 Subject: [PATCH 0312/3297] Bluetooth: bnep: pin L2CAP connection during netdev registration commit bb067a99a0356196c0b89a95721985485ebce5a5 upstream. bnep_add_connection() reads the L2CAP connection without holding the channel lock, then passes its HCI device to register_netdev(). Controller teardown can clear and release that connection concurrently, leaving the network device registration path to dereference a freed parent device. Take a reference to the L2CAP connection while holding the channel lock. Retain it until register_netdev() has taken the parent device reference. Fixes: 65f53e9802db ("Bluetooth: Access BNEP session addresses through L2CAP channel") Reported-by: syzbot+fed5dce4553262f3b35c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fed5dce4553262f3b35c Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/bnep/core.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 5c5f53ff30e8e..ed3ca38d8a6e1 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -562,14 +562,18 @@ static int bnep_session(void *arg) return 0; } -static struct device *bnep_get_device(struct bnep_session *session) +static struct l2cap_conn *bnep_get_conn(struct bnep_session *session) { - struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn; + struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan; + struct l2cap_conn *conn; - if (!conn || !conn->hcon) - return NULL; + l2cap_chan_lock(chan); + conn = chan->conn; + if (conn) + l2cap_conn_get(conn); + l2cap_chan_unlock(chan); - return &conn->hcon->dev; + return conn; } static const struct device_type bnep_type = { @@ -581,6 +585,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) u32 valid_flags = BIT(BNEP_SETUP_RESPONSE); struct net_device *dev; struct bnep_session *s, *ss; + struct l2cap_conn *conn = NULL; u8 dst[ETH_ALEN], src[ETH_ALEN]; int err; @@ -640,10 +645,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) bnep_set_default_proto_filter(s); #endif - SET_NETDEV_DEV(dev, bnep_get_device(s)); + conn = bnep_get_conn(s); + if (!conn) { + err = -ENOTCONN; + goto failed; + } + + SET_NETDEV_DEV(dev, &conn->hcon->dev); SET_NETDEV_DEVTYPE(dev, &bnep_type); err = register_netdev(dev); + l2cap_conn_put(conn); + conn = NULL; if (err) goto failed; @@ -665,6 +678,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) return 0; failed: + if (conn) + l2cap_conn_put(conn); up_write(&bnep_session_sem); free_netdev(dev); return err; From 2a68a773089204af1c8581dc79668b775418c5ee Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 16:36:52 +0800 Subject: [PATCH 0313/3297] Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() commit badff6c3bed8923a1257a853f137d447976eec30 upstream. During the v3 firmware download the controller sends a v3_data_req with a 32 bit offset and a 16 bit len. nxp_recv_fw_req_v3() checks only the lower bound of the offset and then sends firmware from that offset. nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); Nothing checks that fw_dnld_v3_offset + len stays within nxpdev->fw->size, so a controller that asks for an offset or length past the firmware image makes the driver read past the end of nxpdev->fw->data and send that memory back over UART. nxp_recv_fw_req_v1() already bounds the same write. Add the equivalent check to the v3 path, reject the request when it falls outside the firmware image, and zero len on the error path so the fw_v3_prev_sent bookkeeping at free_skb stays consistent. Fixes: 689ca16e5232 ("Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets") Suggested-by: Neeraj Sanjay Kale Reviewed-by: Neeraj Sanjay Kale Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btnxpuart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 3b1e9224e9657..970da4f84e60f 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; + if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size || + len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) { + bt_dev_err(hdev, "FW download out of bounds, ignoring request"); + len = 0; + goto free_skb; + } serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); From 26168db1ce5a9766cde021b18e590a101c056614 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:23:05 +0200 Subject: [PATCH 0314/3297] Bluetooth: fix UAF in bt_accept_dequeue() commit 4bd0b274054f2679f28b70222b607bb0afc3ab9a upstream. bt_accept_get() takes a temporary reference before dropping the accept queue lock. bt_accept_dequeue() currently drops that reference before bt_accept_unlink(), leaving only the queue reference. bt_accept_unlink() drops the queue reference. The subsequent sock_hold() therefore accesses freed memory if it was the final reference, as observed by KASAN during listening L2CAP socket cleanup. Retain the temporary queue-walk reference through unlink and hand it to the caller on success. Drop it explicitly on the closed and not-yet-connected paths. Fixes: ab1513597c6c ("Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del()") Reported-by: syzbot+674ff7e4d7fdfd572afc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=674ff7e4d7fdfd572afc Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/af_bluetooth.c | 17 +++-------------- net/bluetooth/l2cap_sock.c | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 70e35e1980753..6a72ddcd3d704 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -308,7 +308,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) restart: for (sk = bt_accept_get(parent, NULL); sk; sk = next) { - /* Prevent early freeing of sk due to unlink and sock_kill */ + /* The reference from bt_accept_get() keeps sk alive. */ lock_sock(sk); /* Check sk has not already been unlinked via @@ -324,13 +324,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) next = bt_accept_get(parent, sk); - /* sk is safely in the parent list so reduce reference count */ - sock_put(sk); - /* FIXME: Is this check still needed */ if (sk->sk_state == BT_CLOSED) { bt_accept_unlink(sk); release_sock(sk); + sock_put(sk); continue; } @@ -340,16 +338,6 @@ 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); if (next) sock_put(next); @@ -357,6 +345,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) } release_sock(sk); + sock_put(sk); } return NULL; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 506c8c0b4d75d..f2fa3130fd41b 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1464,8 +1464,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) /* 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() + * bt_accept_dequeue() returns sk with its temporary queue-walk + * reference held, so a concurrent l2cap_conn_del() * -> l2cap_sock_kill() cannot free sk under us. * * cleanup_listen() runs under the parent sk lock, so unlike From 61701912c58a05f6a043f097cc177a964abef348 Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Mon, 15 Jun 2026 11:33:05 -0400 Subject: [PATCH 0315/3297] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() commit 12917f591cea1af36087dba5b9ec888652f0b42a upstream. hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection was pending, but hdev->sent_cmd can be NULL while req_status is still HCI_REQ_PEND, leading to a NULL pointer dereference and a general protection fault from the hci_rx_work() receive path. Instead of inspecting hdev->sent_cmd, track the in-flight create connection command with a new per-connection HCI_CONN_CREATE flag and route all cancellation through hci_cancel_connect_sync(), which dispatches to a dedicated per-type cancel function. The create command is in exactly one of two states: still queued, or in flight. The cancel function holds cmd_sync_work_lock across the whole decision: the worker takes this lock to dequeue every entry, so while it is held a queued command cannot start running and an in-flight command cannot complete and let the next command become pending. This keeps the flag test and hci_cmd_sync_cancel() atomic with respect to the worker, so a queued command is simply dequeued, and an in-flight command owned by this connection is cancelled without the risk of cancelling an unrelated command that became pending in the meantime. CIS uses the same flag mechanism via HCI_CONN_CREATE_CIS but cannot be dequeued per-connection. hci_acl_create_conn_sync() and hci_le_create_conn_sync() clear HCI_CONN_CREATE after the create command completes, but the command status handler can free conn via hci_conn_del() (for example when the controller rejects the connection) while the worker is still blocked on the connection complete event. Hold a reference on conn across the create command so the flag can be cleared without a use-after-free. Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections") Cc: stable@vger.kernel.org Suggested-by: XIAO WU Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 21 +---- net/bluetooth/hci_sync.c | 133 +++++++++++++++++++++++++++---- 3 files changed, 123 insertions(+), 32 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0cb87687837f7..b5640980fbf4e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -984,6 +984,7 @@ enum { HCI_CONN_AUTH_FAILURE, HCI_CONN_PER_ADV, HCI_CONN_BIG_CREATED, + HCI_CONN_CREATE, HCI_CONN_CREATE_CIS, HCI_CONN_CREATE_BIG_SYNC, HCI_CONN_BIG_SYNC, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3be7ee6438059..ceeb19158a36c 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2979,26 +2979,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) conn->abort_reason = reason; - /* If the connection is pending check the command opcode since that - * might be blocking on hci_cmd_sync_work while waiting its respective - * event so we need to hci_cmd_sync_cancel to cancel it. - * - * hci_connect_le serializes the connection attempts so only one - * connection can be in BT_CONNECT at time. + /* Cancel the connect attempt. A return of 0 means the create command + * was still queued and got dequeued, so there is nothing to disconnect. */ - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) { - switch (hci_skb_event(hdev->sent_cmd)) { - case HCI_EV_CONN_COMPLETE: - case HCI_EV_LE_CONN_COMPLETE: - case HCI_EV_LE_ENHANCED_CONN_COMPLETE: - case HCI_EVT_LE_CIS_ESTABLISHED: - hci_cmd_sync_cancel(hdev, ECANCELED); - break; - } - /* Cancel connect attempt if still queued/pending */ - } else if (!hci_cancel_connect_sync(hdev, conn)) { + if (!hci_cancel_connect_sync(hdev, conn)) return 0; - } /* Run immediately if on cmd_sync_work since this may be called * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 10b8c24a3fbed..a8404027724de 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6556,6 +6556,11 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) bt_dev_dbg(hdev, "conn %p", conn); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() at done. + */ + hci_conn_get(conn); + clear_bit(HCI_CONN_SCANNING, &conn->flags); conn->state = BT_CONNECT; @@ -6568,6 +6573,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) hdev->le_scan_type == LE_SCAN_ACTIVE && !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { hci_conn_del(conn); + hci_conn_put(conn); return -EBUSY; } @@ -6613,6 +6619,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) &own_addr_type); if (err) goto done; + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + /* Send command LE Extended Create Connection if supported */ if (use_ext_conn(hdev)) { err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type); @@ -6648,11 +6660,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) conn->conn_timeout, NULL); done: + clear_bit(HCI_CONN_CREATE, &conn->flags); + if (err == -ETIMEDOUT) hci_le_connect_cancel_sync(hdev, conn, 0x00); /* Re-enable advertising after the connection attempt is finished. */ hci_resume_advertising_sync(hdev); + hci_conn_put(conn); return err; } @@ -6927,10 +6942,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) else cp.role_switch = 0x00; - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, - sizeof(cp), &cp, - HCI_EV_CONN_COMPLETE, - conn->conn_timeout, NULL); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() below. + */ + hci_conn_get(conn); + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, + sizeof(cp), &cp, + HCI_EV_CONN_COMPLETE, + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); + hci_conn_put(conn); + + return err; } int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) @@ -6976,22 +7006,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) create_le_conn_complete); } -int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) { - if (conn->state != BT_OPEN) - return -EINVAL; + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; + + /* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel + * atomic against the worker, which takes this lock to dequeue every + * entry: while it is held no other command can become pending, so + * hci_cmd_sync_cancel() cannot cancel an unrelated command. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + /* In flight: this connection owns the pending request, cancel it. */ + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; + } + + /* Still queued: a successful dequeue means it never started, so there + * is nothing to disconnect. + */ + entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn, + NULL); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; + /* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel + * atomic against the cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; + } + + entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn, + create_le_conn_complete); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + /* LE Create CIS is shared by the whole CIG and cannot be dequeued + * per-connection, so only an in-flight command can be cancelled. + * cmd_sync_work_lock keeps the test and the cancel atomic against the + * cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) + hci_cmd_sync_cancel(hdev, ECANCELED); + + mutex_unlock(&hdev->cmd_sync_work_lock); + return -EBUSY; +} + +int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +{ switch (conn->type) { case ACL_LINK: - return !hci_cmd_sync_dequeue_once(hdev, - hci_acl_create_conn_sync, - conn, NULL); + return hci_acl_cancel_create_conn_sync(hdev, conn); case LE_LINK: - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync, - conn, create_le_conn_complete); + return hci_le_cancel_create_conn_sync(hdev, conn); + case CIS_LINK: + return hci_cis_cancel_create_conn_sync(hdev, conn); + default: + return -ENOENT; } - - return -ENOENT; } int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, From b9dd39cf1667e378b25a082ca796d495d578c5d3 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 13 Jun 2026 21:43:37 +0300 Subject: [PATCH 0316/3297] Bluetooth: hci_uart: clear HCI_UART_SENDING when write_work is canceled commit 1b0d946d6f08bd39211385bc703a440911b41e46 upstream. HCI_UART_SENDING bit in tx_state means write_work is pending and blocks queueing it again. Currently this bit is not cleared when canceling the work in hci_uart_close(), which blocks future writes when device is reopened later if write_work was pending. Fix by clearing HCI_UART_SENDING when canceling the work. Also make clearing of tx_skb safe by using disable_work_sync + enable_work instead of just cancel_work_sync. hci_uart_flush() purges the proto tx queue so we can cancel the pending write_work there, instead of doing it just in hci_uart_close(). Re-enable and possibly requeue the work after queue flush. Fixes: c1bb9336ae6b ("Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths") Link: https://lore.kernel.org/linux-bluetooth/07e0a28650773abec711ee492fdb1bf5d21a6c98.camel@iki.fi/ Cc: stable@vger.kernel.org Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_ldisc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 10e936b87cc8e..c27413a371c2d 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev *hdev) BT_DBG("hdev %p tty %p", hdev, tty); + disable_work_sync(&hu->write_work); + if (hu->tx_skb) { kfree_skb(hu->tx_skb); hu->tx_skb = NULL; } @@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev *hdev) percpu_up_read(&hu->proto_lock); + /* Resume TX. Also reschedule in case work was queued concurrently; + * this may schedule write_work although there's nothing to do. + */ + enable_work(&hu->write_work); + clear_bit(HCI_UART_SENDING, &hu->tx_state); + if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) + hci_uart_tx_wakeup(hu); + return 0; } @@ -271,12 +281,8 @@ 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; From b84eeb7636d6962dd882d5e0b31475e4f404313c Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 21:23:05 +0500 Subject: [PATCH 0317/3297] Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync() commit d5541eb148da72d5e0a1bca8ecd171f9fc8b366f upstream. iso_conn_big_sync() drops the socket lock to call hci_get_route() and then re-acquires it, but dereferences iso_pi(sk)->conn->hcon afterwards without re-checking that conn is still valid. While the lock is dropped, the connection can be torn down under the same socket lock: iso_disconn_cfm() -> iso_conn_del() -> iso_chan_del() sets iso_pi(sk)->conn to NULL (and the broadcast teardown path can also clear conn->hcon on its own). When iso_conn_big_sync() re-acquires the lock and reads conn->hcon, conn may be NULL, causing a NULL pointer dereference (hcon is the first member of struct iso_conn). This path is reached from iso_sock_recvmsg() for a PA-sync broadcast sink socket (BT_SK_DEFER_SETUP | BT_SK_PA_SYNC), so the dropped-lock window can race with connection teardown driven by controller events. Re-validate iso_pi(sk)->conn and its hcon after re-acquiring the socket lock and bail out if the connection went away, as already done in the sibling iso_sock_rebind_bc(). Fixes: 7a17308c17880d ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync") 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 | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index c1e3015a6630d..f2ff10ae76e22 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1515,6 +1515,7 @@ static void iso_conn_big_sync(struct sock *sk) { int err; struct hci_dev *hdev; + struct iso_conn *conn; bdaddr_t src, dst; u8 src_type; @@ -1537,8 +1538,17 @@ static void iso_conn_big_sync(struct sock *sk) hci_dev_lock(hdev); lock_sock(sk); + /* The socket lock was dropped for hci_get_route(), so the connection + * may have been torn down meanwhile: iso_chan_del() clears conn and + * the broadcast teardown path can clear conn->hcon on its own. Check + * both before dereferencing conn->hcon. + */ + conn = iso_pi(sk)->conn; + if (!conn || !conn->hcon) + goto unlock; + if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { - err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon, + err = hci_conn_big_create_sync(hdev, conn->hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis, @@ -1547,6 +1557,7 @@ static void iso_conn_big_sync(struct sock *sk) bt_dev_err(hdev, "hci_big_create_sync: %d", err); } +unlock: release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); From d5616beb3355b5fca2280d796c1cf7ada4ee6551 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:36:13 +0800 Subject: [PATCH 0318/3297] Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock commit 2641a9e0a1dd4af2e21995470a21d55dd35e5203 upstream. l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for pending_rx_work. process_pending_rx() takes the same mutex, so teardown can deadlock against the worker it is flushing. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., &conn->pending_rx_work) submit path, the l2cap_conn_del() -> cancel_work_sync(&conn->pending_rx_work) teardown path, and the process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep reported: WARNING: possible circular locking dependency detected process_pending_rx+0x21/0x2a [vuln_msv] l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] *** DEADLOCK *** Cancel pending_rx_work before taking conn->lock, matching the existing lock-before-drain ordering used for the two delayed works in the same teardown path. The pending_rx queue is still purged after the work has been cancelled and conn->lock has been acquired. Fixes: 7ab56c3a6ecc ("Bluetooth: Fix deadlock in l2cap_conn_del()") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap_core.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 26fbf61f6e332..4e54bdde4b6e5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1778,19 +1778,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) disable_delayed_work_sync(&conn->info_timer); disable_delayed_work_sync(&conn->id_addr_timer); + cancel_work_sync(&conn->pending_rx_work); + mutex_lock(&conn->lock); kfree_skb(conn->rx_skb); skb_queue_purge(&conn->pending_rx); - - /* We can not call flush_work(&conn->pending_rx_work) here since we - * might block if we are running on a worker from the same workqueue - * pending_rx_work is waiting on. - */ - if (work_pending(&conn->pending_rx_work)) - cancel_work_sync(&conn->pending_rx_work); - ida_destroy(&conn->tx_ida); l2cap_unregister_all_users(conn); From 73abbaf91aa33da87c008fb62c148ade561bb606 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 00:56:35 +0500 Subject: [PATCH 0319/3297] Bluetooth: L2CAP: validate option length before reading conf opt value commit 687617555cedfb74c9e3cb85d759b908dcb17856 upstream. l2cap_get_conf_opt() derives the option length from the attacker-controlled opt->len field and immediately dereferences opt->val (as u8, get_unaligned_le16() or get_unaligned_le32(), or a raw pointer for the default case) before any caller has confirmed that opt->len bytes are present in the buffer. The callers (l2cap_parse_conf_req(), l2cap_parse_conf_rsp() and l2cap_conf_rfc_get()) only detect a malformed option afterwards, once the running length has gone negative, by which point the out-of-bounds read has already executed. An existing post-hoc length check keeps the garbage value from being consumed, so this is not a data leak in the current control flow. It is still a validate-after-use ordering bug: up to 4 bytes are read past the end of the buffer before it is known to contain them, and it is fragile to future changes in the callers. Fix it at the source. Pass the end of the buffer into l2cap_get_conf_opt() and refuse to touch opt->val unless the full option (header + value) fits. Each caller computes an end pointer once before the loop and checks the return value directly instead of inferring the error from a negative length. Fixes: 7c9cbd0b5e38 ("Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer") 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/l2cap_core.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4e54bdde4b6e5..908198959e3bc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3048,13 +3048,24 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, return NULL; } -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, - unsigned long *val) +static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type, + int *olen, unsigned long *val) { struct l2cap_conf_opt *opt = *ptr; int len; + /* opt->len is attacker-controlled. Validate that the full option + * (header + value) actually fits in the buffer before touching + * opt->val, otherwise the switch below reads past the end of the + * caller's buffer. + */ + if (end - *ptr < L2CAP_CONF_OPT_SIZE) + return -EINVAL; + len = L2CAP_CONF_OPT_SIZE + opt->len; + if (end - *ptr < len) + return -EINVAL; + *ptr += len; *type = opt->type; @@ -3426,6 +3437,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data void *ptr = rsp->data; void *endptr = data + data_size; void *req = chan->conf_req; + void *req_end = req + chan->conf_len; int len = chan->conf_len; int type, hint, olen; unsigned long val; @@ -3439,9 +3451,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data BT_DBG("chan %p", chan); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&req, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; hint = type & L2CAP_CONF_HINT; type &= L2CAP_CONF_MASK; @@ -3669,6 +3683,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, struct l2cap_conf_req *req = data; void *ptr = req->data; void *endptr = data + size; + void *rsp_end = rsp + len; int type, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; @@ -3677,9 +3692,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_MTU: @@ -3930,6 +3947,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) { int type, olen; unsigned long val; + void *rsp_end = rsp + len; /* Use sane default values in case a misbehaving remote device * did not send an RFC or extended window size option. */ @@ -3948,9 +3966,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) return; while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_RFC: From 4718007870547e1efebbdd6745d9fce58f008fef Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Thu, 22 Jan 2026 09:48:56 +0800 Subject: [PATCH 0320/3297] iommu/vt-d: Fix race condition during PASID entry replacement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c3b1edea3791fa91ab7032faa90355913ad9451b upstream. The Intel VT-d PASID table entry is 512 bits (64 bytes). When replacing an active PASID entry (e.g., during domain replacement), the current implementation calculates a new entry on the stack and copies it to the table using a single structure assignment. struct pasid_entry *pte, new_pte; pte = intel_pasid_get_entry(dev, pasid); pasid_pte_config_first_level(iommu, &new_pte, ...); *pte = new_pte; Because the hardware may fetch the 512-bit PASID entry in multiple 128-bit chunks, updating the entire entry while it is active (Present bit set) risks a "torn" read. In this scenario, the IOMMU hardware could observe an inconsistent state — partially new data and partially old data — leading to unpredictable behavior or spurious faults. Fix this by removing the unsafe "replace" helpers and following the "clear-then-update" flow, which ensures the Present bit is cleared and the required invalidation handshake is completed before the new configuration is applied. Fixes: 7543ee63e811 ("iommu/vt-d: Add pasid replace helpers") Signed-off-by: Lu Baolu Reviewed-by: Samiullah Khawaja Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260120061816.2132558-4-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel [vineeth: adjusted for 6.18.y: the removed intel_pasid_replace_second_level() in 6.18 still computed pgd/pgd_val locally and used the older pasid_pte_config_second_level() signature; since the function is deleted entirely, the resolution only affects removed lines. The prerequisite "Clear Present bit" patches are already in 6.18.y. Built and tested on 6.18.38: PASID replace, iommufd RID replace and DMA sanity on QEMU VT-d in scalable mode (FS and SS), no regressions.] Signed-off-by: Vineeth Pillai (Google) Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/intel/iommu.c | 29 +++--- drivers/iommu/intel/nested.c | 9 +- drivers/iommu/intel/pasid.c | 190 ----------------------------------- drivers/iommu/intel/pasid.h | 14 --- 4 files changed, 16 insertions(+), 226 deletions(-) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 79676188f60f3..db0cf436c8395 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -1734,12 +1734,10 @@ int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev, ioasid_t pasid, u16 did, phys_addr_t fsptptr, int flags, struct iommu_domain *old) { - if (!old) - return intel_pasid_setup_first_level(iommu, dev, fsptptr, pasid, - did, flags); - return intel_pasid_replace_first_level(iommu, dev, fsptptr, pasid, did, - iommu_domain_did(old, iommu), - flags); + if (old) + intel_pasid_tear_down_entry(iommu, dev, pasid, false); + + return intel_pasid_setup_first_level(iommu, dev, fsptptr, pasid, did, flags); } static int domain_setup_second_level(struct intel_iommu *iommu, @@ -1747,23 +1745,20 @@ static int domain_setup_second_level(struct intel_iommu *iommu, struct device *dev, ioasid_t pasid, struct iommu_domain *old) { - if (!old) - return intel_pasid_setup_second_level(iommu, domain, - dev, pasid); - return intel_pasid_replace_second_level(iommu, domain, dev, - iommu_domain_did(old, iommu), - pasid); + if (old) + intel_pasid_tear_down_entry(iommu, dev, pasid, false); + + return intel_pasid_setup_second_level(iommu, domain, dev, pasid); } static int domain_setup_passthrough(struct intel_iommu *iommu, struct device *dev, ioasid_t pasid, struct iommu_domain *old) { - if (!old) - return intel_pasid_setup_pass_through(iommu, dev, pasid); - return intel_pasid_replace_pass_through(iommu, dev, - iommu_domain_did(old, iommu), - pasid); + if (old) + intel_pasid_tear_down_entry(iommu, dev, pasid, false); + + return intel_pasid_setup_pass_through(iommu, dev, pasid); } static int domain_setup_first_level(struct intel_iommu *iommu, diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c index e91b07475fb84..a909ab1a665a3 100644 --- a/drivers/iommu/intel/nested.c +++ b/drivers/iommu/intel/nested.c @@ -141,11 +141,10 @@ static int domain_setup_nested(struct intel_iommu *iommu, struct device *dev, ioasid_t pasid, struct iommu_domain *old) { - if (!old) - return intel_pasid_setup_nested(iommu, dev, pasid, domain); - return intel_pasid_replace_nested(iommu, dev, pasid, - iommu_domain_did(old, iommu), - domain); + if (old) + intel_pasid_tear_down_entry(iommu, dev, pasid, false); + + return intel_pasid_setup_nested(iommu, dev, pasid, domain); } static int intel_nested_set_dev_pasid(struct iommu_domain *domain, diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c index 7a64a55fb5887..787897e61efa3 100644 --- a/drivers/iommu/intel/pasid.c +++ b/drivers/iommu/intel/pasid.c @@ -417,50 +417,6 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu, struct device *dev, return 0; } -int intel_pasid_replace_first_level(struct intel_iommu *iommu, - struct device *dev, phys_addr_t fsptptr, - u32 pasid, u16 did, u16 old_did, - int flags) -{ - struct pasid_entry *pte, new_pte; - - if (!ecap_flts(iommu->ecap)) { - pr_err("No first level translation support on %s\n", - iommu->name); - return -EINVAL; - } - - if ((flags & PASID_FLAG_FL5LP) && !cap_fl5lp_support(iommu->cap)) { - pr_err("No 5-level paging support for first-level on %s\n", - iommu->name); - return -EINVAL; - } - - pasid_pte_config_first_level(iommu, &new_pte, fsptptr, did, flags); - - spin_lock(&iommu->lock); - pte = intel_pasid_get_entry(dev, pasid); - if (!pte) { - spin_unlock(&iommu->lock); - return -ENODEV; - } - - if (!pasid_pte_is_present(pte)) { - spin_unlock(&iommu->lock); - return -EINVAL; - } - - WARN_ON(old_did != pasid_get_domain_id(pte)); - - *pte = new_pte; - spin_unlock(&iommu->lock); - - intel_pasid_flush_present(iommu, dev, pasid, old_did, pte); - intel_iommu_drain_pasid_prq(dev, pasid); - - return 0; -} - /* * Set up the scalable mode pasid entry for second only translation type. */ @@ -528,57 +484,6 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu, return 0; } -int intel_pasid_replace_second_level(struct intel_iommu *iommu, - struct dmar_domain *domain, - struct device *dev, u16 old_did, - u32 pasid) -{ - struct pasid_entry *pte, new_pte; - struct dma_pte *pgd; - u64 pgd_val; - u16 did; - - /* - * If hardware advertises no support for second level - * translation, return directly. - */ - if (!ecap_slts(iommu->ecap)) { - pr_err("No second level translation support on %s\n", - iommu->name); - return -EINVAL; - } - - pgd = domain->pgd; - pgd_val = virt_to_phys(pgd); - did = domain_id_iommu(domain, iommu); - - pasid_pte_config_second_level(iommu, &new_pte, pgd_val, - domain->agaw, did, - domain->dirty_tracking); - - spin_lock(&iommu->lock); - pte = intel_pasid_get_entry(dev, pasid); - if (!pte) { - spin_unlock(&iommu->lock); - return -ENODEV; - } - - if (!pasid_pte_is_present(pte)) { - spin_unlock(&iommu->lock); - return -EINVAL; - } - - WARN_ON(old_did != pasid_get_domain_id(pte)); - - *pte = new_pte; - spin_unlock(&iommu->lock); - - intel_pasid_flush_present(iommu, dev, pasid, old_did, pte); - intel_iommu_drain_pasid_prq(dev, pasid); - - return 0; -} - /* * Set up dirty tracking on a second only or nested translation type. */ @@ -691,38 +596,6 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu, return 0; } -int intel_pasid_replace_pass_through(struct intel_iommu *iommu, - struct device *dev, u16 old_did, - u32 pasid) -{ - struct pasid_entry *pte, new_pte; - u16 did = FLPT_DEFAULT_DID; - - pasid_pte_config_pass_through(iommu, &new_pte, did); - - spin_lock(&iommu->lock); - pte = intel_pasid_get_entry(dev, pasid); - if (!pte) { - spin_unlock(&iommu->lock); - return -ENODEV; - } - - if (!pasid_pte_is_present(pte)) { - spin_unlock(&iommu->lock); - return -EINVAL; - } - - WARN_ON(old_did != pasid_get_domain_id(pte)); - - *pte = new_pte; - spin_unlock(&iommu->lock); - - intel_pasid_flush_present(iommu, dev, pasid, old_did, pte); - intel_iommu_drain_pasid_prq(dev, pasid); - - return 0; -} - /* * Set the page snoop control for a pasid entry which has been set up. */ @@ -853,69 +726,6 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev, return 0; } -int intel_pasid_replace_nested(struct intel_iommu *iommu, - struct device *dev, u32 pasid, - u16 old_did, struct dmar_domain *domain) -{ - struct iommu_hwpt_vtd_s1 *s1_cfg = &domain->s1_cfg; - struct dmar_domain *s2_domain = domain->s2_domain; - u16 did = domain_id_iommu(domain, iommu); - struct pasid_entry *pte, new_pte; - - /* Address width should match the address width supported by hardware */ - switch (s1_cfg->addr_width) { - case ADDR_WIDTH_4LEVEL: - break; - case ADDR_WIDTH_5LEVEL: - if (!cap_fl5lp_support(iommu->cap)) { - dev_err_ratelimited(dev, - "5-level paging not supported\n"); - return -EINVAL; - } - break; - default: - dev_err_ratelimited(dev, "Invalid stage-1 address width %d\n", - s1_cfg->addr_width); - return -EINVAL; - } - - if ((s1_cfg->flags & IOMMU_VTD_S1_SRE) && !ecap_srs(iommu->ecap)) { - pr_err_ratelimited("No supervisor request support on %s\n", - iommu->name); - return -EINVAL; - } - - if ((s1_cfg->flags & IOMMU_VTD_S1_EAFE) && !ecap_eafs(iommu->ecap)) { - pr_err_ratelimited("No extended access flag support on %s\n", - iommu->name); - return -EINVAL; - } - - pasid_pte_config_nestd(iommu, &new_pte, s1_cfg, s2_domain, did); - - spin_lock(&iommu->lock); - pte = intel_pasid_get_entry(dev, pasid); - if (!pte) { - spin_unlock(&iommu->lock); - return -ENODEV; - } - - if (!pasid_pte_is_present(pte)) { - spin_unlock(&iommu->lock); - return -EINVAL; - } - - WARN_ON(old_did != pasid_get_domain_id(pte)); - - *pte = new_pte; - spin_unlock(&iommu->lock); - - intel_pasid_flush_present(iommu, dev, pasid, old_did, pte); - intel_iommu_drain_pasid_prq(dev, pasid); - - return 0; -} - /* * Interfaces to setup or teardown a pasid table to the scalable-mode * context table entry: diff --git a/drivers/iommu/intel/pasid.h b/drivers/iommu/intel/pasid.h index 637373995be80..0296ca55ea5e6 100644 --- a/drivers/iommu/intel/pasid.h +++ b/drivers/iommu/intel/pasid.h @@ -315,20 +315,6 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu, struct device *dev, u32 pasid); int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev, u32 pasid, struct dmar_domain *domain); -int intel_pasid_replace_first_level(struct intel_iommu *iommu, - struct device *dev, phys_addr_t fsptptr, - u32 pasid, u16 did, u16 old_did, int flags); -int intel_pasid_replace_second_level(struct intel_iommu *iommu, - struct dmar_domain *domain, - struct device *dev, u16 old_did, - u32 pasid); -int intel_pasid_replace_pass_through(struct intel_iommu *iommu, - struct device *dev, u16 old_did, - u32 pasid); -int intel_pasid_replace_nested(struct intel_iommu *iommu, - struct device *dev, u32 pasid, - u16 old_did, struct dmar_domain *domain); - void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev, u32 pasid, bool fault_ignore); From 38cbb1feebcf56ed67c8683e6746ed5cdd16bbac Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Mon, 8 Dec 2025 22:57:46 +0300 Subject: [PATCH 0321/3297] fs/ntfs3: rename ni_readpage_cmpr into ni_read_folio_cmpr [ Upstream commit 4248f563f0b76f3fb74b2a28ee068bf66fcbbedf ] The old "readpage" naming is still used in ni_readpage_cmpr(), even though the vfs has transitioned to the folio-based read_folio() API. This patch performs a straightforward renaming of the helper: ni_readpage_cmpr() -> ni_read_folio_cmpr(). Signed-off-by: Konstantin Komarov Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()") Signed-off-by: Sasha Levin --- fs/ntfs3/frecord.c | 8 ++++---- fs/ntfs3/inode.c | 2 +- fs/ntfs3/ntfs_fs.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 295f6936a3e58..480f2ed518a78 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -2046,18 +2046,18 @@ static struct page *ntfs_lock_new_page(struct address_space *mapping, } /* - * ni_readpage_cmpr + * ni_read_folio_cmpr * * When decompressing, we typically obtain more than one page per reference. * We inject the additional pages into the page cache. */ -int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio) +int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio) { int err; struct ntfs_sb_info *sbi = ni->mi.sbi; struct address_space *mapping = folio->mapping; - pgoff_t index = folio->index; - u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT; + pgoff_t index; + u64 frame_vbo, vbo = folio_pos(folio); struct page **pages = NULL; /* Array of at most 16 pages. stack? */ u8 frame_bits; CLST frame; diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 205baa791f998..0cf2898334f33 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -736,7 +736,7 @@ static int ntfs_read_folio(struct file *file, struct folio *folio) if (is_compressed(ni)) { ni_lock(ni); - err = ni_readpage_cmpr(ni, folio); + err = ni_read_folio_cmpr(ni, folio); ni_unlock(ni); return err; } diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 2649fbe16669d..a4466e9641f65 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -567,7 +567,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint); #define _ni_write_inode(i, w) ni_write_inode(i, w, __func__) int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, __u64 vbo, __u64 len); -int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio); +int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio); int ni_decompress_file(struct ntfs_inode *ni); int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, u32 pages_per_frame); From 764e6f76fdbd495bf383639495db50e604772139 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 12 Dec 2025 14:12:18 +0300 Subject: [PATCH 0322/3297] fs/ntfs3: fsync files by syncing parent inodes [ Upstream commit dcd9d6a47199565d83d61a11bbf91fa2ade4d676 ] Some xfstests expect fsync() on a file or directory to also persist directory metadata up the parent chain. Using generic_file_fsync() is not sufficient for ntfs, because parent directories are not explicitly written out. Signed-off-by: Konstantin Komarov Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()") Signed-off-by: Sasha Levin --- fs/ntfs3/dir.c | 2 +- fs/ntfs3/file.c | 30 ++++++++++++++++++++++++--- fs/ntfs3/frecord.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ fs/ntfs3/ntfs_fs.h | 2 ++ 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index 460df046482c7..d41d02372c747 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -624,7 +624,7 @@ const struct file_operations ntfs_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = ntfs_readdir, - .fsync = generic_file_fsync, + .fsync = ntfs_file_fsync, .open = ntfs_file_open, .unlocked_ioctl = ntfs_ioctl, #ifdef CONFIG_COMPAT diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 5042eb321eb89..6a19bb0422080 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -1378,13 +1378,37 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe, /* * ntfs_file_fsync - file_operations::fsync */ -static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) +int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) { struct inode *inode = file_inode(file); - if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) + struct super_block *sb = inode->i_sb; + struct ntfs_sb_info *sbi = sb->s_fs_info; + int err, ret; + + if (unlikely(ntfs3_forced_shutdown(sb))) return -EIO; - return generic_file_fsync(file, start, end, datasync); + ret = file_write_and_wait_range(file, start, end); + if (ret) + return ret; + + ret = write_inode_now(inode, !datasync); + + if (!ret) { + ret = ni_write_parents(ntfs_i(inode), !datasync); + } + + if (!ret) { + ntfs_set_state(sbi, NTFS_DIRTY_CLEAR); + ntfs_update_mftmirr(sbi, false); + } + + err = sync_blockdev(sb->s_bdev); + if (unlikely(err && !ret)) + ret = err; + if (!ret) + blkdev_issue_flush(sb->s_bdev); + return ret; } // clang-format off diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 480f2ed518a78..e904c5da91922 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -3090,6 +3090,57 @@ bool ni_is_dirty(struct inode *inode) return false; } +/* + * ni_write_parents + * + * Helper function for ntfs_file_fsync. + */ +int ni_write_parents(struct ntfs_inode *ni, int sync) +{ + int err = 0; + struct ATTRIB *attr = NULL; + struct ATTR_LIST_ENTRY *le = NULL; + struct ntfs_sb_info *sbi = ni->mi.sbi; + struct super_block *sb = sbi->sb; + + while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL, + NULL))) { + struct inode *dir; + struct ATTR_FILE_NAME *fname; + + fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); + if (!fname) + continue; + + /* Check simple case when parent inode equals current inode. */ + if (ino_get(&fname->home) == ni->vfs_inode.i_ino) { + if (MFT_REC_ROOT != ni->vfs_inode.i_ino) { + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + err = -EINVAL; + } + continue; + } + + dir = ntfs_iget5(sb, &fname->home, NULL); + if (IS_ERR(dir)) { + ntfs_inode_warn( + &ni->vfs_inode, + "failed to open parent directory r=%lx to write", + (long)ino_get(&fname->home)); + continue; + } + + if (!is_bad_inode(dir)) { + int err2 = write_inode_now(dir, sync); + if (!err) + err = err2; + } + iput(dir); + } + + return err; +} + /* * ni_update_parent * diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index a4466e9641f65..7fb08bff02549 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -510,6 +510,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, int ntfs_file_open(struct inode *inode, struct file *file); int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len); +int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync); long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg); long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg); extern const struct inode_operations ntfs_special_inode_operations; @@ -587,6 +588,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de); bool ni_is_dirty(struct inode *inode); +int ni_write_parents(struct ntfs_inode *ni, int sync); /* Globals from fslog.c */ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes); From 6dd58c56ab864431eb292de1507615c4e19b5857 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 12 Dec 2025 14:38:10 +0300 Subject: [PATCH 0323/3297] fs/ntfs3: zero-fill folios beyond i_valid in ntfs_read_folio() [ Upstream commit 356fa248168be90109b66f32a61b8eaedc98424a ] Handle ntfs_read_folio() early when the folio offset is beyond i_valid by zero-filling the folio and marking it uptodate. This avoids needless I/O and locking, improves read performance. Signed-off-by: Konstantin Komarov Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()") Signed-off-by: Sasha Levin --- fs/ntfs3/inode.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 0cf2898334f33..1b7bb924fc002 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -723,6 +723,19 @@ static int ntfs_read_folio(struct file *file, struct folio *folio) struct address_space *mapping = folio->mapping; struct inode *inode = mapping->host; struct ntfs_inode *ni = ntfs_i(inode); + loff_t vbo = folio_pos(folio); + + if (unlikely(is_bad_ni(ni))) { + folio_unlock(folio); + return -EIO; + } + + if (ni->i_valid <= vbo) { + folio_zero_range(folio, 0, folio_size(folio)); + folio_mark_uptodate(folio); + folio_unlock(folio); + return 0; + } if (is_resident(ni)) { ni_lock(ni); From 08fad5d5a26cc55c0ecb68ac59ba3be5e31f4147 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Thu, 19 Mar 2026 13:15:46 +0530 Subject: [PATCH 0324/3297] fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked() [ Upstream commit d7ea8495fd307b58f8867acd81a1b40075b1d3ba ] When a compressed or sparse attribute has its clusters frame-aligned, vcn is rounded down to the frame start using cmask, which can result in vcn != vcn0. In this case, vcn and vcn0 may reside in different attribute segments. The code already handles the case where vcn is in a different segment by loading its runs before allocation. However, it fails to load runs for vcn0 when vcn0 resides in a different segment than vcn. This causes run_lookup_entry() to return SPARSE_LCN for vcn0 since its segment was never loaded into the in-memory run list, triggering the WARN_ON(1). Fix this by adding a missing check for vcn0 after the existing vcn segment check. If vcn0 falls outside the current segment range [svcn, evcn1), find and load the attribute segment containing vcn0 before performing the run lookup. The following scenario triggers the bug: attr_data_get_block_locked() vcn = vcn0 & cmask <- vcn != vcn0 after frame alignment load runs for vcn segment <- vcn0 segment not loaded! attr_allocate_clusters() <- allocation succeeds run_lookup_entry(vcn0) <- vcn0 not in run -> SPARSE_LCN WARN_ON(1) <- bug fires here! Reported-by: syzbot+c1e9aedbd913fadad617@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c1e9aedbd913fadad617 Fixes: c380b52f6c57 ("fs/ntfs3: Change new sparse cluster processing") Signed-off-by: Deepanshu Kartikey Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/attrib.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 5a7675112e7b8..501f2ec153ab8 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -1045,6 +1045,20 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, if (err) goto out; } + + if (vcn0 < svcn || evcn1 <= vcn0) { + struct ATTRIB *attr2; + + attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL, + 0, &vcn0, &mi); + if (!attr2) { + err = -EINVAL; + goto out; + } + err = attr_load_runs(attr2, ni, run, NULL); + if (err) + goto out; + } } if (vcn + to_alloc > asize) From 661a019ac0413ecec9e5d1dfcc12fbca8e78d5fb Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 4 Jun 2026 15:34:25 +0800 Subject: [PATCH 0325/3297] coresight: ultrasoc-smb: Fix OOB write in smb_sync_perf_buffer() commit 98495b5a4d77dd22e106f462b76e1093a55b29a7 upstream. When the SMB sink is used as a perf AUX sink, smb_update_buffer() calls smb_sync_perf_buffer() to copy hardware trace data into the perf AUX ring buffer pages. It derives pg_idx = head >> PAGE_SHIFT from @head, which is handle->head, and indexes dst_pages[pg_idx]. The pg_idx %= nr_pages normalization is only applied after the first loop iteration. This leaves the initial page index underived from the buffer size, which can result in an out-of-bounds write past dst_pages[] when head exceeds the AUX buffer size. Normalize head modulo the AUX buffer size before deriving the page index and offset, mirroring tmc_etr_sync_perf_buffer(). Fixes: 06f5c2926aaa ("drivers/coresight: Add UltraSoc System Memory Buffer driver") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/SYBPR01MB788156B3380A36835DB22290AF102@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 26cfc939e5bd8..780329250aca6 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -338,6 +338,7 @@ static void smb_sync_perf_buffer(struct smb_drv_data *drvdata, unsigned long to_copy; long pg_idx, pg_offset; + head %= (unsigned long)buf->nr_pages << PAGE_SHIFT; pg_idx = head >> PAGE_SHIFT; pg_offset = head & (PAGE_SIZE - 1); From 91b8a58c6ac15c7db6518f696389933282f88da7 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 20:11:49 -0400 Subject: [PATCH 0326/3297] smb: client: resolve SWN tcon from live registrations commit ec457f9afe5ae9538bdcd58fd4cb442b9787e183 upstream. cifs_swn_notify() looks up a witness registration by id under cifs_swnreg_idr_mutex, drops the mutex, and then uses the registration's cached tcon pointer. That pointer is not a lifetime reference, and it is not a stable representative once cifs_get_swn_reg() lets multiple tcons for the same net/share name share one registration id. A same-share second mount can keep the cifs_swn_reg alive after the first tcon unregisters and is freed. The registration then still points at the freed first tcon, so taking tc_lock or incrementing tc_count through swnreg->tcon only moves the use-after-free earlier. Taking tc_lock while holding cifs_swnreg_idr_mutex also violates the documented CIFS lock order. Fix this by making the registration store only the stable witness identity: id, net name, share name, and notify flags. When a notify arrives, copy that identity under cifs_swnreg_idr_mutex, drop the mutex, then find and pin a live witness tcon that currently matches the net/share pair under the normal cifs_tcp_ses_lock -> tc_lock order. The notification path uses that pinned tcon directly and drops the reference when done. Registration and unregister messages now use the live tcon passed by the caller instead of a cached tcon in the registration. The final unregister send is folded into cifs_swn_unregister() while the registration is still protected by cifs_swnreg_idr_mutex. This removes the previous find/drop/reacquire raw-pointer window. The release path only removes the idr entry and frees the stable identity strings. This preserves the intended one-registration/many-tcon behavior: a registration id represents a net/share pair, and notify handling acts on a live representative selected at use time. It also preserves CLIENT_MOVE ordering for the representative tcon because the old-IP unregister is sent before cifs_swn_register() sends the new-IP register. 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/cifs_swn.c | 314 ++++++++++++++++++++++++++++++++------- fs/smb/client/trace.h | 2 + 2 files changed, 262 insertions(+), 54 deletions(-) diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c index 68a1f87c446d0..12b0420d9bdc1 100644 --- a/fs/smb/client/cifs_swn.c +++ b/fs/smb/client/cifs_swn.c @@ -28,10 +28,54 @@ struct cifs_swn_reg { bool net_name_notify; bool share_name_notify; bool ip_notify; +}; - struct cifs_tcon *tcon; +struct cifs_swn_reg_info { + int id; + unsigned int ref_count; + const char *net_name; + const char *share_name; + bool net_name_notify; + bool share_name_notify; + bool ip_notify; }; +static void cifs_swn_snapshot_reg(struct cifs_swn_reg *swnreg, + struct cifs_swn_reg_info *info) +{ + info->id = swnreg->id; + info->ref_count = kref_read(&swnreg->ref_count); + info->net_name = swnreg->net_name; + info->share_name = swnreg->share_name; + info->net_name_notify = swnreg->net_name_notify; + info->share_name_notify = swnreg->share_name_notify; + info->ip_notify = swnreg->ip_notify; +} + +static int cifs_swn_dup_reg(struct cifs_swn_reg *swnreg, + struct cifs_swn_reg_info *info) +{ + cifs_swn_snapshot_reg(swnreg, info); + + info->net_name = kstrdup(swnreg->net_name, GFP_KERNEL); + if (!info->net_name) + return -ENOMEM; + + info->share_name = kstrdup(swnreg->share_name, GFP_KERNEL); + if (!info->share_name) { + kfree(info->net_name); + return -ENOMEM; + } + + return 0; +} + +static void cifs_swn_free_reg_info(struct cifs_swn_reg_info *info) +{ + kfree(info->net_name); + kfree(info->share_name); +} + static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) { int ret; @@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) * The authentication information to connect to the witness service is bundled * into the message. */ -static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) +static int cifs_swn_send_register_message(struct cifs_swn_reg_info *swnreg, + struct cifs_tcon *tcon) { struct sk_buff *skb; struct genlmsghdr *hdr; @@ -109,10 +154,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) * told to switch to it (client move message). In these cases we unregister from the * server address and register to the new address when we receive the notification. */ - if (swnreg->tcon->ses->server->use_swn_dstaddr) - addr = &swnreg->tcon->ses->server->swn_dstaddr; + if (tcon->ses->server->use_swn_dstaddr) + addr = &tcon->ses->server->swn_dstaddr; else - addr = &swnreg->tcon->ses->server->dstaddr; + addr = &tcon->ses->server->dstaddr; ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); if (ret < 0) @@ -136,10 +181,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) goto nlmsg_fail; } - authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); + authtype = cifs_select_sectype(tcon->ses->server, tcon->ses->sectype); switch (authtype) { case Kerberos: - ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); + ret = cifs_swn_auth_info_krb(tcon, skb); if (ret < 0) { cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); goto nlmsg_fail; @@ -147,7 +192,7 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) break; case NTLMv2: case RawNTLMSSP: - ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); + ret = cifs_swn_auth_info_ntlm(tcon, skb); if (ret < 0) { cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); goto nlmsg_fail; @@ -176,7 +221,8 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) /* * Sends an uregister message to the userspace daemon based on the registration */ -static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) +static int cifs_swn_send_unregister_message(struct cifs_swn_reg_info *swnreg, + struct cifs_tcon *tcon) { struct sk_buff *skb; struct genlmsghdr *hdr; @@ -205,7 +251,7 @@ static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) goto nlmsg_fail; ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), - &swnreg->tcon->ses->server->dstaddr); + &tcon->ses->server->dstaddr); if (ret < 0) goto nlmsg_fail; @@ -241,6 +287,88 @@ static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) return ret; } +/* + * Allocation-free mirror of extract_hostname() + extract_sharename() from + * fs/smb/client/unc.c. Those helpers kmalloc(GFP_KERNEL); this runs under + * cifs_tcp_ses_lock and tcon->tc_lock, both spinlocks, so we mirror their + * parsing in place against the caller's stable net_name/share_name strings. + * Keep in sync with unc.c. + */ +static bool cifs_swn_tcon_matches(struct cifs_tcon *tcon, + const char *net_name, + const char *share_name) +{ + const char *unc = tcon->tree_name; + const char *host, *share, *delim; + size_t host_len, share_len; + + if (!tcon->use_witness) + return false; + + /* extract_hostname: require strlen(unc) >= 3 */ + if (strnlen(unc, 3) < 3) + return false; + /* extract_hostname: skip all leading '\' characters */ + for (host = unc; *host == '\\'; host++) + ; + if (!*host) + return false; + delim = strchr(host, '\\'); + if (!delim) + return false; + host_len = delim - host; + if (strlen(net_name) != host_len || + strncasecmp(host, net_name, host_len)) + return false; + + /* extract_sharename: start at unc + 2, then first '\' onward */ + share = unc + 2; + delim = strchr(share, '\\'); + if (!delim) + return false; + share = delim + 1; + share_len = strlen(share); + + return strlen(share_name) == share_len && + !strncasecmp(share, share_name, share_len); +} + +/* + * One SWN registration id represents one net/share name pair. Multiple + * mounted tcons can therefore share the id. Pick a live representative at + * use time instead of caching the first tcon pointer in the registration. + */ +static struct cifs_tcon *cifs_swn_get_tcon(struct cifs_swn_reg_info *swnreg) +{ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_EXITING || + !cifs_swn_tcon_matches(tcon, swnreg->net_name, + swnreg->share_name)) { + spin_unlock(&tcon->tc_lock); + continue; + } + ++tcon->tc_count; + trace_smb3_tcon_ref(tcon->debug_id, + tcon->tc_count, + netfs_trace_tcon_ref_get_swn_notify); + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return tcon; + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + /* * Try to find a matching registration for the tcon's server name and share name. * Calls to this function must be protected by cifs_swnreg_idr_mutex. @@ -347,8 +475,6 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) reg->net_name_notify = true; reg->share_name_notify = true; reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); - - reg->tcon = tcon; unlock: mutex_unlock(&cifs_swnreg_idr_mutex); @@ -368,11 +494,6 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) static void cifs_swn_reg_release(struct kref *ref) { struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); - int ret; - - ret = cifs_swn_send_unregister_message(swnreg); - if (ret < 0) - cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); idr_remove(&cifs_swnreg_idr, swnreg->id); kfree(swnreg->net_name); @@ -380,23 +501,33 @@ static void cifs_swn_reg_release(struct kref *ref) kfree(swnreg); } -static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) +static void cifs_put_swn_reg_locked(struct cifs_swn_reg *swnreg, + struct cifs_tcon *tcon) { - mutex_lock(&cifs_swnreg_idr_mutex); + if (kref_read(&swnreg->ref_count) == 1) { + struct cifs_swn_reg_info swnreg_info; + int ret; + + cifs_swn_snapshot_reg(swnreg, &swnreg_info); + ret = cifs_swn_send_unregister_message(&swnreg_info, tcon); + if (ret < 0) + cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", + __func__, ret); + } + kref_put(&swnreg->ref_count, cifs_swn_reg_release); - mutex_unlock(&cifs_swnreg_idr_mutex); } -static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) +static int cifs_swn_resource_state_changed(struct cifs_tcon *tcon, const char *name, int state) { switch (state) { case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); break; case CIFS_SWN_RESOURCE_STATE_AVAILABLE: cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); break; case CIFS_SWN_RESOURCE_STATE_UNKNOWN: cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name); @@ -502,7 +633,7 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a return ret; } -static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) +static int cifs_swn_client_move(struct cifs_tcon *tcon, struct sockaddr_storage *addr) { struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; @@ -512,14 +643,17 @@ static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_sto else if (addr->ss_family == AF_INET6) cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); - return cifs_swn_reconnect(swnreg->tcon, addr); + return cifs_swn_reconnect(tcon, addr); } int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) { struct cifs_swn_reg *swnreg; + struct cifs_swn_reg_info swnreg_info; + struct cifs_tcon *tcon; char name[256]; int type; + int ret = 0; if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { int swnreg_id; @@ -527,21 +661,34 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); mutex_lock(&cifs_swnreg_idr_mutex); swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); - mutex_unlock(&cifs_swnreg_idr_mutex); if (swnreg == NULL) { + mutex_unlock(&cifs_swnreg_idr_mutex); cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); return -EINVAL; } + ret = cifs_swn_dup_reg(swnreg, &swnreg_info); + mutex_unlock(&cifs_swnreg_idr_mutex); + if (ret) + return ret; } else { cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); return -EINVAL; } + tcon = cifs_swn_get_tcon(&swnreg_info); + if (!tcon) { + cifs_dbg(FYI, "%s: registration id %d has no live tcon\n", + __func__, swnreg_info.id); + ret = -ENODEV; + goto free_info; + } + if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); } else { cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } switch (type) { @@ -553,15 +700,18 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) sizeof(name)); } else { cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); } else { cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } - return cifs_swn_resource_state_changed(swnreg, name, state); + ret = cifs_swn_resource_state_changed(tcon, name, state); + break; } case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { struct sockaddr_storage addr; @@ -570,28 +720,36 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); } else { cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto out; } - return cifs_swn_client_move(swnreg, &addr); + ret = cifs_swn_client_move(tcon, &addr); + break; } default: cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); break; } - return 0; +out: + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: + cifs_swn_free_reg_info(&swnreg_info); + return ret; } int cifs_swn_register(struct cifs_tcon *tcon) { struct cifs_swn_reg *swnreg; + struct cifs_swn_reg_info swnreg_info; int ret; swnreg = cifs_get_swn_reg(tcon); if (IS_ERR(swnreg)) return PTR_ERR(swnreg); - ret = cifs_swn_send_register_message(swnreg); + cifs_swn_snapshot_reg(swnreg, &swnreg_info); + ret = cifs_swn_send_register_message(&swnreg_info, tcon); if (ret < 0) { cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); /* Do not put the swnreg or return error, the echo task will retry */ @@ -612,35 +770,68 @@ int cifs_swn_unregister(struct cifs_tcon *tcon) return PTR_ERR(swnreg); } + cifs_put_swn_reg_locked(swnreg, tcon); mutex_unlock(&cifs_swnreg_idr_mutex); - cifs_put_swn_reg(swnreg); - return 0; } -void cifs_swn_dump(struct seq_file *m) +/* + * Snapshot one registration under cifs_swnreg_idr_mutex and return. Callers + * intentionally do the per-registration network/genlmsg work without the + * mutex held, both to keep the critical section short and to avoid nesting + * cifs_swnreg_idr_mutex inside the higher tc_lock when a live tcon is then + * pinned for the send. + */ +static int cifs_swn_get_next_reg_info(int *id, struct cifs_swn_reg_info *info) { struct cifs_swn_reg *swnreg; + int ret = 0; + + mutex_lock(&cifs_swnreg_idr_mutex); + swnreg = idr_get_next(&cifs_swnreg_idr, id); + if (swnreg) { + ret = cifs_swn_dup_reg(swnreg, info); + if (!ret) { + *id = swnreg->id + 1; + ret = 1; + } + } + mutex_unlock(&cifs_swnreg_idr_mutex); + + return ret; +} + +void cifs_swn_dump(struct seq_file *m) +{ + struct cifs_swn_reg_info swnreg_info; + struct cifs_tcon *tcon; struct sockaddr_in *sa; struct sockaddr_in6 *sa6; - int id; + int id = 0; + int ret; seq_puts(m, "Witness registrations:"); - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) { seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", - id, kref_read(&swnreg->ref_count), - swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", - swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); - switch (swnreg->tcon->ses->server->dstaddr.ss_family) { + swnreg_info.id, swnreg_info.ref_count, + swnreg_info.net_name, swnreg_info.net_name_notify ? "(y)" : "(n)", + swnreg_info.share_name, swnreg_info.share_name_notify ? "(y)" : "(n)"); + + tcon = cifs_swn_get_tcon(&swnreg_info); + if (!tcon) { + seq_puts(m, "(no live tcon)"); + goto next; + } + + switch (tcon->ses->server->dstaddr.ss_family) { case AF_INET: - sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; + sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr; seq_printf(m, "%pI4", &sa->sin_addr.s_addr); break; case AF_INET6: - sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; + sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr; seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); if (sa6->sin6_scope_id) seq_printf(m, "%%%u", sa6->sin6_scope_id); @@ -648,23 +839,38 @@ void cifs_swn_dump(struct seq_file *m) default: seq_puts(m, "(unknown)"); } - seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); +next: + seq_printf(m, "%s", swnreg_info.ip_notify ? "(y)" : "(n)"); + cifs_swn_free_reg_info(&swnreg_info); } - mutex_unlock(&cifs_swnreg_idr_mutex); + if (ret < 0) + seq_printf(m, "\nFailed to snapshot witness registration: %d", ret); seq_puts(m, "\n"); } void cifs_swn_check(void) { - struct cifs_swn_reg *swnreg; - int id; + struct cifs_swn_reg_info swnreg_info; + struct cifs_tcon *tcon; + int id = 0; int ret; - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - ret = cifs_swn_send_register_message(swnreg); + while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) { + tcon = cifs_swn_get_tcon(&swnreg_info); + if (!tcon) { + cifs_dbg(FYI, "%s: registration id %d has no live tcon\n", + __func__, swnreg_info.id); + goto free_info; + } + + ret = cifs_swn_send_register_message(&swnreg_info, tcon); if (ret < 0) cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); + cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: + cifs_swn_free_reg_info(&swnreg_info); } - mutex_unlock(&cifs_swnreg_idr_mutex); + if (ret < 0) + cifs_dbg(FYI, "%s: Failed to snapshot registration: %d\n", __func__, ret); } diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index 79a246af664d2..dbed7c082af32 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -52,6 +52,7 @@ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \ EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \ + EM(netfs_trace_tcon_ref_get_swn_notify, "GET SwnNot") \ EM(netfs_trace_tcon_ref_new, "NEW ") \ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \ EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \ @@ -63,6 +64,7 @@ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ + EM(netfs_trace_tcon_ref_put_swn_notify, "PUT SwnNot") \ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \ EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \ From a187883cc1dc784a4d32537f5d316f1b7b9ad76f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 11 Jun 2026 10:35:28 +0300 Subject: [PATCH 0327/3297] smb/client: Fix error code in smb2_aead_req_alloc() commit 61f28012e5650c619223decdb7970e0d3162e949 upstream. The "*num_sgs" variable is a u32 so "ERR_PTR(*num_sgs)" doesn't work. We would have to do something similar to the previous line where it's cast to int and then long. However, it's simpler to store the return in an int ret variable. This bug would eventually result in a crash when dereference the invalid error pointer. Fixes: d08089f649a0 ("cifs: Change the I/O paths to use an iterator rather than a page list") Cc: stable@kernel.org Signed-off-by: Dan Carpenter Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2ops.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index e1a9e89cb85fe..0b52f07003338 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4314,11 +4314,13 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); unsigned int iv_size = crypto_aead_ivsize(tfm); unsigned int len; + int ret; u8 *p; - *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); - if (IS_ERR_VALUE((long)(int)*num_sgs)) - return ERR_PTR(*num_sgs); + ret = cifs_get_num_sgs(rqst, num_rqst, sig); + if (ret < 0) + return ERR_PTR(ret); + *num_sgs = ret; len = iv_size; len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); From baae7b39673ec21073a25e3d14f8feaada01d5df Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 12 Jun 2026 07:15:38 +0900 Subject: [PATCH 0328/3297] ksmbd: add permission checks for FSCTL_DUPLICATE_EXTENTS_TO_FILE commit 388e4139db27a9e3612c9d356b826f5b1ff6a9e3 upstream. The FSCTL_DUPLICATE_EXTENTS_TO_FILE arm of smb2_ioctl() overwrites the destination file's data via vfs_clone_file_range() with neither the share-level KSMBD_TREE_CONN_FLAG_WRITABLE check nor a per-handle fp->daccess check that the other write-bearing arms carry. A client can overwrite destination data on a read-only share, or from a handle opened with only FILE_WRITE_ATTRIBUTES (which still yields an FMODE_WRITE filp). FILE_WRITE_ATTRIBUTES-only destination handle overwrote the file's data via the clone. Add both checks, matching the FSCTL_SET_SPARSE permission fix; require FILE_WRITE_DATA since this writes data. Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- 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 fd1c3a7d21601..30fffe3f31a00 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8563,6 +8563,17 @@ int smb2_ioctl(struct ksmbd_work *work) goto dup_ext_out; } + if (!test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ret = -EACCES; + goto dup_ext_out; + } + + if (!(fp_out->daccess & FILE_WRITE_DATA_LE)) { + ret = -EACCES; + goto dup_ext_out; + } + src_off = le64_to_cpu(dup_ext->SourceFileOffset); dst_off = le64_to_cpu(dup_ext->TargetFileOffset); length = le64_to_cpu(dup_ext->ByteCount); From 57f2042fd87d7ce8fc3ac8b6c176e554df68b1a7 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Wed, 10 Jun 2026 20:07:04 +0900 Subject: [PATCH 0329/3297] ksmbd: add a permission check for FSCTL_SET_ZERO_DATA commit 3320ba068198adc144c89d6661b805acce01735b upstream. FSCTL_SET_ZERO_DATA in smb2_ioctl() destroys file data via ksmbd_vfs_zero_data() -> vfs_fallocate(PUNCH_HOLE/ZERO_RANGE) after checking only the share-level KSMBD_TREE_CONN_FLAG_WRITABLE, with no per-handle access check. A handle opened with only FILE_WRITE_ATTRIBUTES still yields an FMODE_WRITE filp (FILE_WRITE_ATTRIBUTES is part of FILE_WRITE_DESIRE_ACCESS_LE, so smb2_create_open_flags() opens it O_WRONLY), so the vfs_fallocate FMODE_WRITE check does not stop it; only the missing fp->daccess gate would. Reproduced on mainline 7.1-rc7 with KASAN by an authenticated SMB client: a FILE_WRITE_ATTRIBUTES-only handle zeroed 4096 bytes of file data it had no FILE_WRITE_DATA right to (6/6; a FILE_READ_DATA-only handle was correctly denied). This is the unfixed sibling of commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE"). Because SET_ZERO_DATA writes data (not an attribute), require FILE_WRITE_DATA. Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 30fffe3f31a00..b173700774fd3 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8488,6 +8488,12 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } + if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + ksmbd_fd_put(work, fp); + ret = -EACCES; + goto out; + } + ret = ksmbd_vfs_zero_data(work, fp, off, len); ksmbd_fd_put(work, fp); if (ret < 0) From a1d5d31cad593ea5e1b637f2f39c9ef6d09d1199 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 12 Jun 2026 08:00:00 +0900 Subject: [PATCH 0330/3297] ksmbd: serialize QUERY_DIRECTORY requests per file commit be6d26bf27499977c746abc163659915082348d8 upstream. smb2_query_dir() stores a pointer to its stack-allocated private data in the ksmbd_file readdir_data. Concurrent QUERY_DIRECTORY requests using the same file handle can overwrite this pointer while an iterate_dir() callback is still using it, resulting in a stack use-after-free. Add a per-file mutex and hold it while accessing the shared directory enumeration state. The lock covers scan restart, dot entry state, readdir_data setup and iteration, and response construction. This prevents another request from replacing readdir_data.private before the current request has finished using it and also serializes the shared file position. Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-30527 Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 4 ++++ fs/smb/server/vfs_cache.c | 1 + fs/smb/server/vfs_cache.h | 2 ++ 3 files changed, 7 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index b173700774fd3..6f80796f884b6 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -4472,6 +4472,8 @@ int smb2_query_dir(struct ksmbd_work *work) ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); } + mutex_lock(&dir_fp->readdir_lock); + if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { ksmbd_debug(SMB, "Restart directory scan\n"); generic_file_llseek(dir_fp->filp, 0, SEEK_SET); @@ -4576,6 +4578,7 @@ int smb2_query_dir(struct ksmbd_work *work) goto err_out; } + mutex_unlock(&dir_fp->readdir_lock); kfree(srch_ptr); ksmbd_fd_put(work, dir_fp); ksmbd_revert_fsids(work); @@ -4583,6 +4586,7 @@ int smb2_query_dir(struct ksmbd_work *work) err_out: pr_err("error while processing smb2 query dir rc = %d\n", rc); + mutex_unlock(&dir_fp->readdir_lock); kfree(srch_ptr); err_out2: diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 7293b7effbc14..58dc9a1383e81 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -686,6 +686,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) INIT_LIST_HEAD(&fp->node); INIT_LIST_HEAD(&fp->lock_list); spin_lock_init(&fp->f_lock); + mutex_init(&fp->readdir_lock); atomic_set(&fp->refcount, 1); fp->filp = filp; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 866f32c10d4dd..6fcb7791f275f 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -113,6 +114,7 @@ struct ksmbd_file { /* if ls is happening on directory, below is valid*/ struct ksmbd_readdir_data readdir_data; + struct mutex readdir_lock; int dot_dotdot[2]; unsigned int f_state; bool reserve_lease_break; From 5aa1cb01155f96824003baf7997cdf1f150caba3 Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Sat, 6 Jun 2026 16:11:04 +0900 Subject: [PATCH 0331/3297] ksmbd: fix UAF of struct file_lock in SMB2_LOCK deferred-lock cancellation commit d20d1c8ba5765d1d12eefc0aee6385ab3f240e1e upstream. When a blocking byte-range lock request is deferred in the FILE_LOCK_DEFERRED path, ksmbd registers the asynchronous work into the connection's async_requests list via setup_async_work(). The cancel callback smb2_remove_blocked_lock() holds a reference to the flock. If the lock waiter is subsequently woken up but the work state is no longer KSMBD_WORK_ACTIVE (e.g., due to a concurrent cancellation), the cleanup path calls locks_free_lock(flock) without dequeuing the work from the async_requests list. Concurrently, smb2_cancel() walks the list under conn->request_lock and invokes the cancel callback, which then dereferences the already freed 'flock'. This leads to a slab-use-after-free inside __wake_up_common. Fix this by restructuring the cleanup logic after the worker returns from ksmbd_vfs_posix_lock_wait(). Move list_del(&smb_lock->llist) and release_async_work(work) to the top of the cleanup block. This guarantees that the async work is completely dequeued and serialized under conn->request_lock before locks_free_lock(flock) is called, rendering the flock unreachable for any concurrent smb2_cancel(). Cc: stable@vger.kernel.org Signed-off-by: Davide Ornaghi Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 6f80796f884b6..933a1e42db4b6 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7747,29 +7747,27 @@ int smb2_lock(struct ksmbd_work *work) list_del(&work->fp_entry); spin_unlock(&fp->f_lock); - if (work->state != KSMBD_WORK_ACTIVE) { - list_del(&smb_lock->llist); - locks_free_lock(flock); + list_del(&smb_lock->llist); + release_async_work(work); - if (work->state == KSMBD_WORK_CANCELLED) { - rsp->hdr.Status = - STATUS_CANCELLED; - kfree(smb_lock); - smb2_send_interim_resp(work, - STATUS_CANCELLED); - work->send_no_response = 1; - goto out; - } + if (work->state == KSMBD_WORK_ACTIVE) + goto retry; + + locks_free_lock(flock); - rsp->hdr.Status = - STATUS_RANGE_NOT_LOCKED; + if (work->state == KSMBD_WORK_CANCELLED) { + rsp->hdr.Status = STATUS_CANCELLED; kfree(smb_lock); - goto out2; + smb2_send_interim_resp(work, + STATUS_CANCELLED); + work->send_no_response = 1; + goto out; } - list_del(&smb_lock->llist); - release_async_work(work); - goto retry; + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); + goto out2; } else if (!rc) { list_add(&smb_lock->llist, &rollback_list); spin_lock(&work->conn->llist_lock); From db231af842868268839f9f9619c68cb27830d8be Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:02 +0900 Subject: [PATCH 0332/3297] ksmbd: require source read access for duplicate extents commit cedff600f1642aa982178503552f0d007bc829c8 upstream. FSCTL_DUPLICATE_EXTENTS_TO_FILE passes the source file directly to vfs_clone_file_range() or vfs_copy_file_range() without checking the SMB access mask granted to the source handle. A handle opened with attribute access can consequently be used to copy file contents into an attacker-readable destination. Require FILE_READ_DATA on the source handle before either VFS operation, matching other ksmbd data-copy paths. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 933a1e42db4b6..bf4488182dfa7 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8581,6 +8581,10 @@ int smb2_ioctl(struct ksmbd_work *work) ret = -EACCES; goto dup_ext_out; } + if (!(fp_in->daccess & FILE_READ_DATA_LE)) { + ret = -EACCES; + goto dup_ext_out; + } src_off = le64_to_cpu(dup_ext->SourceFileOffset); dst_off = le64_to_cpu(dup_ext->TargetFileOffset); From f56535db508ead8dec1c481ad93d7d8acd8f8f1e Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Tue, 9 Jun 2026 00:00:00 +0000 Subject: [PATCH 0333/3297] ksmbd: add a WRITE_DAC/WRITE_OWNER check to SMB2 SET_INFO SECURITY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 44df157a1183a7f746caa970c169255da5ac61f8 upstream. commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE") added a fp->daccess gate to fsctl_set_sparse and noted that "similar handle-level checks exist in other functions but are missing here." The SMB2 SET_INFO SECURITY arm is one of the missing ones, and the most security-relevant: smb2_set_info_sec() calls set_info_sec() with no per-handle access check. set_info_sec() (fs/smb/server/smbacl.c) re-permissions the file: it rewrites owner/group/mode via notify_change(), rewrites the POSIX ACL via set_posix_acl(), and on KSMBD_SHARE_FLAG_ACL_XATTR shares removes and rewrites the Windows security descriptor via ksmbd_vfs_set_sd_xattr(). Every other persistent-mutation arm of the sibling handler smb2_set_info_file() checks fp->daccess first (FILE_WRITE_DATA / FILE_DELETE / FILE_WRITE_EA / FILE_WRITE_ATTRIBUTES); the SECURITY arm — which mutates the access control itself — is the only one with no gate. A client can therefore open a handle with FILE_WRITE_ATTRIBUTES only (no FILE_WRITE_DAC / FILE_WRITE_OWNER) and use SMB2_SET_INFO with InfoType SMB2_O_INFO_SECURITY to rewrite the file's DACL and owner, granting itself access the handle's daccess never carried. Unlike the FSCTL data arms this is a metadata/xattr operation, so there is no FMODE_WRITE VFS backstop — the missing fp->daccess check is the entire gate. Setting a security descriptor is the WRITE_DAC / WRITE_OWNER operation, so require at least one of those on the handle before re-permissioning the file. -EACCES is mapped to STATUS_ACCESS_DENIED by smb2_set_info(). Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index bf4488182dfa7..5f847d28746fb 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6623,6 +6623,9 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, fp->saccess |= FILE_SHARE_DELETE_LE; + if (!(fp->daccess & (FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE))) + return -EACCES; + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, buf_len, false, true); } From 20ee516a62989a8d505ee432f9e59525ea23984e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:01 +0900 Subject: [PATCH 0334/3297] ksmbd: run set info with opener credentials commit b383bcad3d2fe634b26efbce53e22bbb5753a520 upstream. SMB2 SET_INFO handlers call path-based VFS helpers after checking the access mask granted to the SMB handle. Those helpers perform their owner, inode permission and LSM checks using the current ksmbd worker credentials. Run the complete SET_INFO dispatch with the credentials captured when the handle was opened. This also removes the separate security information credential setup and keeps all SET_INFO classes under one credential scope. Direct override_creds() is used because it can nest with the request credential overrides already used by rename and link helpers. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 5f847d28746fb..3b69b28950770 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6638,6 +6638,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, */ int smb2_set_info(struct ksmbd_work *work) { + const struct cred *saved_cred; struct smb2_set_info_req *req; struct smb2_set_info_rsp *rsp; struct ksmbd_file *fp = NULL; @@ -6679,6 +6680,7 @@ int smb2_set_info(struct ksmbd_work *work) goto err_out; } + saved_cred = override_creds(fp->filp->f_cred); switch (req->InfoType) { case SMB2_O_INFO_FILE: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); @@ -6686,19 +6688,15 @@ int smb2_set_info(struct ksmbd_work *work) break; case SMB2_O_INFO_SECURITY: ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - if (ksmbd_override_fsids(work)) { - rc = -ENOMEM; - goto err_out; - } rc = smb2_set_info_sec(fp, le32_to_cpu(req->AdditionalInformation), (char *)req + le16_to_cpu(req->BufferOffset), le32_to_cpu(req->BufferLength)); - ksmbd_revert_fsids(work); break; default: rc = -EOPNOTSUPP; } + revert_creds(saved_cred); if (rc < 0) goto err_out; From 2ca82bfff49c84709d8736fb5040e00867bf3028 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Thu, 11 Jun 2026 22:59:51 +0900 Subject: [PATCH 0335/3297] ksmbd: enforce FILE_READ_ATTRIBUTES on SMB_FIND_FILE_POSIX_INFORMATION commit 20c8442dc1003f9f7bb522d3dcd81d09ea59a79e upstream. find_file_posix_info() in smb2_query_info() returns file metadata (owner uid, group gid, mode, inode, size, allocation size, hard-link count and all four timestamps) but performs no per-handle access check. Every sibling query handler gates on the handle's granted access first -- get_file_basic_info(), get_file_all_info(), get_file_network_open_info() and get_file_attribute_tag_info() all reject a handle lacking FILE_READ_ATTRIBUTES_LE with -EACCES. The POSIX handler is gated only by the connection-scoped tcon->posix_extensions flag, which is not a per-handle authorization, so a handle opened with only FILE_WRITE_DATA is correctly denied FileBasicInformation yet is allowed the strict-superset POSIX info. Mirror the FILE_READ_ATTRIBUTES_LE gate the sibling info handlers already use. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 3b69b28950770..d2173a308adb9 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -5339,6 +5339,12 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; int ret; + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); if (ret) From df501c0f320b5e2446712f339e2729f750906ee7 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Wed, 10 Jun 2026 20:13:51 +0900 Subject: [PATCH 0336/3297] ksmbd: add per-handle permission check to FILE_LINK_INFORMATION commit 13f3942f2bf45856bb751faed2f0c4618f41ca20 upstream. The FILE_LINK_INFORMATION arm of smb2_set_info_file() calls smb2_create_link() with no per-handle fp->daccess check. On the ReplaceIfExists path smb2_create_link() unlinks an existing file at the target name (ksmbd_vfs_remove_file) and creates a hardlink (ksmbd_vfs_link); neither helper checks daccess. A handle opened with FILE_READ_DATA only (no FILE_DELETE, no FILE_WRITE_DATA) can therefore delete an arbitrary file in the share and plant a hardlink over its name. The sibling delete/move arms in the same switch already gate: FILE_RENAME_INFORMATION and FILE_DISPOSITION_INFORMATION both require FILE_DELETE_LE; FILE_FULL_EA_INFORMATION requires FILE_WRITE_EA_LE. Gate the link arm the same way as its closest analogue (rename), since it mutates the namespace and, on replace, deletes an existing entry. This is a sibling of commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE"). Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d2173a308adb9..50e36050f9ea3 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6572,6 +6572,11 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } case FILE_LINK_INFORMATION: { + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + if (buf_len < sizeof(struct smb2_file_link_info)) return -EINVAL; From e72c15085b6d86f45d224d98aa75b5cace4aaab9 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:00 +0900 Subject: [PATCH 0337/3297] ksmbd: use opener credentials for delete-on-close commit 52e2f21911158ec961cd5aae19c56460db382af0 upstream. Delete-on-close can be completed by deferred or durable handle teardown, where no request work is available. Both the base-file unlink and the ADS xattr removal consequently run with the ksmbd worker credentials and can bypass filesystem permission checks. Run both operations with the credentials captured in struct file when the handle was opened. This preserves the authenticated user's fsuid, fsgid, supplementary groups and capability restrictions at final close. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/vfs.c | 7 +++++-- fs/smb/server/vfs_cache.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 90e693e4e4149..db7ce7671363d 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1078,13 +1078,15 @@ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, int ksmbd_vfs_unlink(struct file *filp) { + const struct cred *saved_cred; int err = 0; struct dentry *dir, *dentry = filp->f_path.dentry; struct mnt_idmap *idmap = file_mnt_idmap(filp); + saved_cred = override_creds(filp->f_cred); err = mnt_want_write(filp->f_path.mnt); if (err) - return err; + goto out_revert; dir = dget_parent(dentry); err = ksmbd_vfs_lock_parent(dir, dentry); @@ -1104,7 +1106,8 @@ int ksmbd_vfs_unlink(struct file *filp) out: dput(dir); mnt_drop_write(filp->f_path.mnt); - +out_revert: + revert_creds(saved_cred); return err; } diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 58dc9a1383e81..a1f59a8e16e28 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -292,10 +292,14 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) up_write(&ci->m_lock); if (remove_stream_xattr) { + const struct cred *saved_cred; + + saved_cred = override_creds(filp->f_cred); err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp), &filp->f_path, fp->stream.name, true); + revert_creds(saved_cred); if (err) pr_err("remove xattr failed : %s\n", fp->stream.name); From 2b4592cea214683de0f2ce6f8c22c097fb0ea1ab Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 13 Jun 2026 22:00:03 +0900 Subject: [PATCH 0338/3297] ksmbd: use opener credentials for ADS I/O commit baa5e094886fffa7e6272edcb5e08be5ce28262c upstream. Alternate data streams are stored as xattrs. Unlike regular file I/O, their read and write paths therefore call VFS xattr helpers which recheck inode permissions and LSM policy using the current task credentials. Run ADS I/O with the credentials captured when the SMB handle was opened. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/vfs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index db7ce7671363d..babdecc5a01ff 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -278,17 +278,20 @@ static ssize_t ksmbd_vfs_getcasexattr(struct mnt_idmap *idmap, static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, size_t count) { + const struct cred *saved_cred; ssize_t v_len; char *stream_buf = NULL; ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", *pos, count); + saved_cred = override_creds(fp->filp->f_cred); v_len = ksmbd_vfs_getcasexattr(file_mnt_idmap(fp->filp), fp->filp->f_path.dentry, fp->stream.name, fp->stream.size, &stream_buf); + revert_creds(saved_cred); if ((int)v_len <= 0) return (int)v_len; @@ -411,6 +414,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, size_t count) { + const struct cred *saved_cred; char *stream_buf = NULL, *wbuf; struct mnt_idmap *idmap = file_mnt_idmap(fp->filp); size_t size; @@ -431,6 +435,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, count = XATTR_SIZE_MAX - *pos; } + saved_cred = override_creds(fp->filp->f_cred); v_len = ksmbd_vfs_getcasexattr(idmap, fp->filp->f_path.dentry, fp->stream.name, @@ -439,14 +444,14 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, if (v_len < 0) { pr_err("not found stream in xattr : %zd\n", v_len); err = v_len; - goto out; + goto out_revert; } if (v_len < size) { wbuf = kvzalloc(size, KSMBD_DEFAULT_GFP); if (!wbuf) { err = -ENOMEM; - goto out; + goto out_revert; } if (v_len > 0) @@ -464,6 +469,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, size, 0, true); +out_revert: + revert_creds(saved_cred); if (err < 0) goto out; else From 00b0fa425941438b664950a8ee65dfba2def4336 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:38 -0300 Subject: [PATCH 0339/3297] smb: client: fix query directory replay double-free commit 9647492b5e41954be59d5157eddbcd4cdc1656f7 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_directory_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index fa77dd31a39cc..ba488138e0498 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5668,6 +5668,8 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From f1add4acb656f5a82806a1ab0e63fed3d8b1bfca Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:36 -0300 Subject: [PATCH 0340/3297] smb: client: fix query_info() replay double-free commit 2a88561d66eb855813cf004a0abe648bbb17de5e upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_info_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index ba488138e0498..d9b1f38ddc754 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3821,6 +3821,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; allocated = false; server = cifs_pick_channel(ses); From 276c8efbc49f9303ac76d0d4deab7128581b0f3b Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:34 -0300 Subject: [PATCH 0341/3297] smb: client: fix double-free in SMB2_ioctl() replay commit f9bbadb6c94583e3b4af1afc449bfceb1d1ddec9 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_ioctl_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index d9b1f38ddc754..486aa302ad0cd 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3425,6 +3425,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 52af1975f0dfae990c5a0e85872cc41be0e88a68 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:37 -0300 Subject: [PATCH 0342/3297] smb: client: fix change notify replay double-free commit 145f820dcbb2cced374f2532f8a61a44dce4a615 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_notify_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 486aa302ad0cd..e6c4b5498f9e6 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4010,6 +4010,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 3407240cde132a4b72d6429a2625a09a2f78adaf Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Thu, 18 Jun 2026 23:28:05 +0800 Subject: [PATCH 0343/3297] smb: client: fix double-free in SMB2_flush() replay commit 4be31c943a3a27a5a0251dbb8f5cb89059ec3d5a upstream. SMB2_flush() keeps its response buffer bookkeeping across replay attempts. If a replayable flush response is received and the retry then fails before cifs_send_recv() stores a replacement response, flush_exit will free the stale response pointer a second time. Reinitialize resp_buftype and rsp_iov at the top of the replay loop so cleanup only acts on response state produced by the current attempt. This fixes a double-free without changing replay handling for successful requests. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Acked-by: Henrique Carvalho Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index e6c4b5498f9e6..9534923f35d63 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4351,6 +4351,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); From 14498ff5ce0f272ce0ef988721413e06b7038972 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:33 -0300 Subject: [PATCH 0344/3297] smb: client: fix double-free in SMB2_open() replay commit b55e182f2324bc6a604c21a47aa6c448f719a532 upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_open_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 9534923f35d63..d30b1470013dc 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3205,6 +3205,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); oparms->replay = !!(retries); From d15d83125007f673aec4323e1bbbaaffbe87ea13 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 18 Jun 2026 17:34:35 -0300 Subject: [PATCH 0345/3297] smb: client: fix double-free in SMB2_close() replay commit f96e1cdcb63ed3321142ff2fcdf784e32cda8fee upstream. A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_close_init() fails before the next send, cleanup retains the previous buffer type and frees that response again. Reset response bookkeeping before each attempt to prevent the stale free. Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set") Cc: stable@vger.kernel.org Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index d30b1470013dc..063f4623ec8a1 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3617,6 +3617,8 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; query_attrs = false; server = cifs_pick_channel(ses); From 297243e365fc9fe2f8e9b7dd535a65d922cd108b Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 09:45:38 +0800 Subject: [PATCH 0346/3297] smb: client: Fix next buffer leak in receive_encrypted_standard() commit 1c6267a1d5cf4c73b656f8181b310cbbb3e4767b upstream. receive_encrypted_standard() allocates next_buffer before checking whether the number of compound PDUs already reached MAX_COMPOUND. If the limit check fails, the function returns immediately and the newly allocated next_buffer is not assigned to server->smallbuf/server->bigbuf, making it leaked. Move the MAX_COMPOUND check before allocating next_buffer. Fixes: b24df3e30cbf ("cifs: update receive_encrypted_standard to handle compounded responses") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2ops.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 0b52f07003338..9c2a6fb423fc4 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -5096,6 +5096,12 @@ receive_encrypted_standard(struct TCP_Server_Info *server, one_more: shdr = (struct smb2_hdr *)buf; next_cmd = le32_to_cpu(shdr->NextCommand); + + if (*num_mids >= MAX_COMPOUND) { + cifs_server_dbg(VFS, "too many PDUs in compound\n"); + return -1; + } + if (next_cmd) { if (WARN_ON_ONCE(next_cmd > pdu_length)) return -1; @@ -5119,10 +5125,6 @@ receive_encrypted_standard(struct TCP_Server_Info *server, mid_entry->resp_buf_size = server->pdu_size; } - if (*num_mids >= MAX_COMPOUND) { - cifs_server_dbg(VFS, "too many PDUs in compound\n"); - return -1; - } bufs[*num_mids] = buf; mids[(*num_mids)++] = mid_entry; From 3d89ae65ef78a7958ff5efab6ad7a46eb4b38333 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Wed, 1 Jul 2026 18:23:21 +0800 Subject: [PATCH 0347/3297] smb: client: use unaligned reads in parse_posix_ctxt() commit b86467cd2691192ad4809a5a6e922fc24b8e9839 upstream. The server controls create-context DataOffset, so the POSIX context data pointer may be misaligned on strict-alignment architectures. Use get_unaligned_le32() when reading nlink, reparse_tag, and mode. Fixes: 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data") Cc: stable@vger.kernel.org Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 063f4623ec8a1..8b74025acda37 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -2297,9 +2297,9 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, memset(posix, 0, sizeof(*posix)); - posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); - posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); - posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); + posix->nlink = get_unaligned_le32(beg); + posix->reparse_tag = get_unaligned_le32(beg + 4); + posix->mode = get_unaligned_le32(beg + 8); sid = beg + 12; sid_len = posix_info_sid_size(sid, end); From 86c5d470f5d42e61123b2f4b4f0b91f4eee5b980 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 28 Jun 2026 17:19:24 +0800 Subject: [PATCH 0348/3297] smb: client: harden POSIX SID length parsing commit 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 upstream. posix_info_sid_size() reads sid[1] to obtain the subauthority count, but its existing boundary check still accepts buffers with only one remaining byte. Require two bytes before reading sid[1] so all client paths that reuse the helper reject truncated POSIX SIDs safely. Fixes: 349e13ad30b4 ("cifs: add smb2 POSIX info level") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Signed-off-by: Ren Wei 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 8b74025acda37..4828cff55fb7f 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -5230,7 +5230,7 @@ int posix_info_sid_size(const void *beg, const void *end) size_t subauth; int total; - if (beg + 1 > end) + if (beg + 2 > end) return -1; subauth = *(u8 *)(beg+1); From 157c67a657a7da1a31ab22c1fd0b2d9e5968ebd5 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:30:17 +0800 Subject: [PATCH 0349/3297] smb: client: fix atime clamp check in read completion commit 0b043279e73880bee21d3b1f221bafda5af1b27e upstream. cifs_rreq_done() updates the inode atime to current_time(inode) after a netfs read. It then preserves the CIFS rule that atime should not be older than mtime, because some applications break if atime is less than mtime. That rule only requires clamping when atime < mtime. The current check uses the raw non-zero result of timespec64_compare(). It therefore takes the clamp path for both atime < mtime and atime > mtime. The latter is the normal case when reading an older file: the newly recorded atime is newer than the file mtime. The completion handler then immediately moves atime back to mtime, losing the access time that was just recorded. Userspace tools that rely on atime, such as stat, find -atime, backup tools or cold-data classifiers, can therefore see a recently read CIFS file as not recently accessed. This is easy to miss because the bug is silent: read I/O still succeeds, no error is reported, and many systems either do not check atime after reads or mount with policies such as relatime/noatime. It becomes visible when a CIFS file has an mtime older than the current time, the file is read, and the local inode atime is inspected before a later revalidation replaces the cached timestamps. Clamp only when atime is actually older than mtime. This matches the same atime/mtime rule used when applying CIFS inode attributes. Fixes: 69c3c023af25 ("cifs: Implement netfslib hooks") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index f0e7a8f69e8b8..b0dc071a9de4f 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -302,7 +302,7 @@ static void cifs_rreq_done(struct netfs_io_request *rreq) /* we do not want atime to be less than mtime, it broke some apps */ atime = inode_set_atime_to_ts(inode, current_time(inode)); mtime = inode_get_mtime(inode); - if (timespec64_compare(&atime, &mtime)) + if (timespec64_compare(&atime, &mtime) < 0) inode_set_atime_to_ts(inode, inode_get_mtime(inode)); } From c6c484a7d5bff6b929a86d7ed5130f29834c6a0d Mon Sep 17 00:00:00 2001 From: Norbert Manthey Date: Thu, 9 Jul 2026 15:54:39 +0000 Subject: [PATCH 0350/3297] smb: client: mask server-provided mode to 07777 in modefromsid commit e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 upstream. When modefromsid is active, parse_dacl() applies the server-provided sub_auth[2] value from the NFS mode SID to cf_mode without masking to 07777. Apply the correct masking, same as in the read path. Fixes: e2f8fbfb8d09c ("cifs: get mode bits from special sid on stat") Signed-off-by: Norbert Manthey Assisted-by: Kiro:claude-opus-4.6 Cc: stable@vger.kernel.org Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/cifsacl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index d17912beb28ec..92ee03cd01b39 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -889,7 +889,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, */ fattr->cf_mode &= ~07777; fattr->cf_mode |= - le32_to_cpu(ppace[i]->sid.sub_auth[2]); + le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777; break; } else { if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { From 685fc15a410885b6d4dee64de0dce721b9428b12 Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Thu, 21 May 2026 17:50:14 +0800 Subject: [PATCH 0351/3297] writeback: fix race between cgroup_writeback_umount() and inode_switch_wbs() commit cba38ec4cbd3a7b8b942a8d52531a05be8a9ff0d upstream. When a container exits, the following BUG_ON() is occasionally triggered: ================================================================== VFS: Busy inodes after unmount of sdb (ext4) ------------[ cut here ]------------ kernel BUG at fs/super.c:695! CPU: 3 PID: 6 Comm: containerd-shim Tainted: G OE K 6.6 #1 pstate: 63400009 (nZCv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--) pc : generic_shutdown_super+0xf0/0x100 lr : generic_shutdown_super+0xf0/0x100 Call trace: generic_shutdown_super+0xf0/0x100 kill_block_super+0x20/0x48 ext4_kill_sb+0x28/0x60 deactivate_locked_super+0x54/0x130 deactivate_super+0x84/0xa0 cleanup_mnt+0xa4/0x140 __cleanup_mnt+0x18/0x28 task_work_run+0x78/0xe0 do_notify_resume+0x204/0x240 ================================================================== The root cause is a race between cgroup_writeback_umount() and inode_switch_wbs()/cleanup_offline_cgwb(). There is a window between inode_prepare_wbs_switch() returning true and the subsequent wb_queue_isw() call. Following is the process that triggers the issue: CPU A (umount) | CPU B (writeback) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ inode_switch_wbs/cleanup_offline_cgwb atomic_inc(&isw_nr_in_flight) inode_prepare_wbs_switch -> passes SB_ACTIVE check __iget(inode) generic_shutdown_super sb->s_flags &= ~SB_ACTIVE cgroup_writeback_umount(sb) smp_mb() atomic_read(&isw_nr_in_flight) rcu_barrier() -> no pending RCU callbacks flush_workqueue(isw_wq) -> nothing queued, returns evict_inodes(sb) -> Inode skipped as isw still holds a ref. sop->put_super(sb) /* destroys percpu counters */ -> VFS: Busy inodes after unmount! wb_queue_isw() queue_work(isw_wq, ...) /* later in work function */ inode_switch_wbs_work_fn process_inode_switch_wbs iput() -> evict percpu_counter_dec() // UAF! Fix this by extending the RCU read-side critical section in inode_switch_wbs() and cleanup_offline_cgwb() to cover from inode_prepare_wbs_switch() through wb_queue_isw(). Since there is no sleep in this window, rcu_read_lock() can be used. Then add a synchronize_rcu() in cgroup_writeback_umount() before the existing rcu_barrier(), so that all in-flight switchers that have passed the SB_ACTIVE check have completed queue_work() before flush_workqueue() is called. The existing rcu_barrier() is intentionally retained so this fix can be backported unchanged to stable kernels (5.10.y, 6.6.y, ...) that still queue switches via queue_rcu_work(). It is a no-op on current mainline (since commit e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes")) and is removed in a follow-up patch. Fixes: a1a0e23e4903 ("writeback: flush inode cgroup wb switches instead of pinning super_block") Cc: stable@vger.kernel.org Suggested-by: Jan Kara Link: https://lore.kernel.org/all/mxnjq2l6guusfchvauxr3v7c4bwjasybxlleqbbh4efloeqspz@iqylk76ohufz Reviewed-by: Jan Kara Signed-off-by: Baokun Li Link: https://patch.msgid.link/20260521095016.2791354-2-libaokun@linux.alibaba.com Acked-by: Tejun Heo Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/fs-writeback.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 5d43bbaf37b16..99d0658b49a22 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -650,12 +650,19 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) atomic_inc(&isw_nr_in_flight); - /* find and pin the new wb */ + /* + * Paired with synchronize_rcu() in cgroup_writeback_umount(): + * holding rcu_read_lock across inode_prepare_wbs_switch() + * (covering the SB_ACTIVE check and the inode grab) and + * wb_queue_isw() ensures synchronize_rcu() cannot return until + * the work is queued, so the subsequent flush_workqueue() will + * wait for the switch. + */ rcu_read_lock(); + /* find and pin the new wb */ memcg_css = css_from_id(new_wb_id, &memory_cgrp_subsys); if (memcg_css && !css_tryget(memcg_css)) memcg_css = NULL; - rcu_read_unlock(); if (!memcg_css) goto out_free; @@ -671,9 +678,11 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) trace_inode_switch_wbs_queue(inode->i_wb, new_wb, 1); wb_queue_isw(new_wb, isw); + rcu_read_unlock(); return; out_free: + rcu_read_unlock(); atomic_dec(&isw_nr_in_flight); if (new_wb) wb_put(new_wb); @@ -732,6 +741,14 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb) new_wb = &wb->bdi->wb; /* wb_get() is noop for bdi's wb */ nr = 0; + /* + * Paired with synchronize_rcu() in cgroup_writeback_umount(). + * Holding rcu_read_lock across the SB_ACTIVE check, the inode grab + * and wb_queue_isw() ensures synchronize_rcu() cannot return until + * the work is queued, so the subsequent flush_workqueue() will wait + * for the switch. + */ + rcu_read_lock(); spin_lock(&wb->list_lock); /* * In addition to the inodes that have completed writeback, also switch @@ -749,6 +766,7 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb) /* no attached inodes? bail out */ if (nr == 0) { + rcu_read_unlock(); atomic_dec(&isw_nr_in_flight); wb_put(new_wb); kfree(isw); @@ -757,6 +775,7 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb) trace_inode_switch_wbs_queue(wb, new_wb, nr); wb_queue_isw(new_wb, isw); + rcu_read_unlock(); return restart; } @@ -1210,6 +1229,14 @@ void cgroup_writeback_umount(struct super_block *sb) smp_mb(); if (atomic_read(&isw_nr_in_flight)) { + /* + * Paired with rcu_read_lock() in inode_switch_wbs() and + * cleanup_offline_cgwb(). synchronize_rcu() waits for any + * in-flight switcher that already passed the SB_ACTIVE check + * to finish queueing its work, so flush_workqueue() below + * will then drain it. + */ + synchronize_rcu(); /* * Use rcu_barrier() to wait for all pending callbacks to * ensure that all in-flight wb switches are in the workqueue. From a277489337c7d4630a277f125fd824d9a90b2500 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 11 May 2026 12:12:11 +0530 Subject: [PATCH 0352/3297] OPP: of: Fix potential memory leak in opp_parse_supplies() commit 69f888381d2ecbe18ed9f112c096f8fd3623db98 upstream. The memory allocated for microvolt, microamp and microwatt is not freed in one of the paths in opp_parse_supplies() which returns directly. Fix that by adding a goto to the error unwind ladder. Fixes: 2eedf62e66c2 ("OPP: decouple dt properties in opp_parse_supplies()") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/opp/of.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 505d79821584e..7b4b7deb5ac6f 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -669,7 +669,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, */ if (unlikely(opp_table->regulator_count == -1)) { opp_table->regulator_count = 0; - return 0; + goto free_microwatt; } for (i = 0, j = 0; i < opp_table->regulator_count; i++) { @@ -692,6 +692,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, opp->supplies[i].u_watt = microwatt[i]; } +free_microwatt: kfree(microwatt); free_microamp: kfree(microamp); From e904961332801c87355f5d11c65bb433e717c489 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 2 May 2026 03:00:05 +0800 Subject: [PATCH 0353/3297] cpufreq: qcom-cpufreq-hw: Fix possible double free commit bcb8889c4981fdde42d4fd2c29a77d510fe21da2 upstream. qcom_cpufreq.data is allocated with devm_kzalloc() in probe() as an array of per-domain data. qcom_cpufreq_hw_cpu_init() stores a pointer to one element of this array in policy->driver_data. qcom_cpufreq_hw_cpu_exit() currently calls kfree() on policy->driver_data. This is not valid because the memory is devm-managed. For the first domain, this can free the devm-managed allocation while the devres entry is still active, leading to a possible double free when the platform device is later detached. For other domains, the pointer may refer to an element inside the array rather than the allocation base. Remove the kfree(data) call and let devres release qcom_cpufreq.data. This issue was found by a static analysis tool I am developing. Fixes: 054a3ef683a1 ("cpufreq: qcom-hw: Allocate qcom_cpufreq_data during probe") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Zhongqiu Han Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/qcom-cpufreq-hw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 8422704a3b10f..4f14573e88a4d 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -578,7 +578,6 @@ static void qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); qcom_cpufreq_hw_lmh_exit(data); kfree(policy->freq_table); - kfree(data); } static void qcom_cpufreq_ready(struct cpufreq_policy *policy) From 92f41769e5fd16bcd9ba97500d0517332e0a5b45 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Tue, 5 May 2026 17:12:31 +0800 Subject: [PATCH 0354/3297] firmware_loader: fix device reference leak in firmware_upload_register() commit 896df22ee57648b0c505bd76ddbc6b2341834696 upstream. firmware_upload_register() -> fw_create_instance() -> device_initialize() After fw_create_instance() succeeds, the lifetime of the embedded struct device is expected to be managed through the device core reference counting, since fw_create_instance() has already called device_initialize(). In firmware_upload_register(), if alloc_lookup_fw_priv() fails after fw_create_instance() succeeds, the code reaches free_fw_sysfs and frees fw_sysfs directly instead of releasing the device reference with put_device(). This 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(fw_dev) in the failure path and letting fw_dev_release() handle the final cleanup, instead of freeing the instance directly from the error path. Fixes: 97730bbb242c ("firmware_loader: Add firmware-upload support") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260505091231.607089-1-lgs201920130244@gmail.com Signed-off-by: Danilo Krummrich Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/sysfs_upload.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c index 829270067d163..cab79e7fcfcf8 100644 --- a/drivers/base/firmware_loader/sysfs_upload.c +++ b/drivers/base/firmware_loader/sysfs_upload.c @@ -341,7 +341,6 @@ firmware_upload_register(struct module *module, struct device *parent, goto free_fw_upload_priv; } fw_upload->priv = fw_sysfs; - fw_sysfs->fw_upload_priv = fw_upload_priv; fw_dev = &fw_sysfs->dev; ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, NULL, 0, 0, @@ -349,10 +348,12 @@ firmware_upload_register(struct module *module, struct device *parent, if (ret != 0) { if (ret > 0) ret = -EINVAL; - goto free_fw_sysfs; + put_device(fw_dev); + goto free_fw_upload_priv; } fw_priv->is_paged_buf = true; fw_sysfs->fw_priv = fw_priv; + fw_sysfs->fw_upload_priv = fw_upload_priv; ret = device_add(fw_dev); if (ret) { @@ -363,9 +364,6 @@ firmware_upload_register(struct module *module, struct device *parent, return fw_upload; -free_fw_sysfs: - kfree(fw_sysfs); - free_fw_upload_priv: kfree(fw_upload_priv); From b9d45d328fcda4f0d3d281b7d6f12d3f181d9381 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Wed, 3 Jun 2026 19:53:14 -0700 Subject: [PATCH 0355/3297] libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo() commit 6de2aeffabaafaeda819e60ec8d04f199711e11a upstream. Since commit 1e7ab6f67824 ("anon_inode: rework assertions"), path_noexec() warns when an anonymous-inode file is mmap'd from a superblock that has not set SB_I_NOEXEC. dma-buf backs its files this way and never set the flag, so mmap of any exported buffer trips the warning on a CONFIG_DEBUG_VFS=y kernel: WARNING: CPU: 11 PID: 121813 at fs/exec.c:118 path_noexec+0x47/0x50 do_mmap+0x2b5/0x680 vm_mmap_pgoff+0x129/0x210 ksys_mmap_pgoff+0x177/0x240 __x64_sys_mmap+0x33/0x70 init_pseudo() sets up internal SB_NOUSER mounts that are never path-reachable. Set both flags here so every pseudo filesystem gets them by default instead of each caller setting them. SB_I_NODEV is inert for unreachable mounts. SB_I_NOEXEC has one visible effect: an executable mapping of a pseudo-fs fd, such as a dma-buf, now fails with -EPERM, which is the invariant the assertion enforces. No in-tree caller maps these executable. Reproduce on CONFIG_DEBUG_VFS=y: make -C tools/testing/selftests/dmabuf-heaps sudo ./tools/testing/selftests/dmabuf-heaps/dmabuf-heap -t system Fixes: 1e7ab6f67824 ("anon_inode: rework assertions") Suggested-by: Christoph Hellwig Cc: stable@vger.kernel.org Signed-off-by: John Hubbard Link: https://patch.msgid.link/20260604025315.245910-2-jhubbard@nvidia.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/libfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/libfs.c b/fs/libfs.c index 6be233c787fd8..96551deff8e50 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -721,6 +721,7 @@ struct pseudo_fs_context *init_pseudo(struct fs_context *fc, fc->fs_private = ctx; fc->ops = &pseudo_fs_context_ops; fc->sb_flags |= SB_NOUSER; + fc->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; fc->global = true; } return ctx; From 59626d0d29217a5a0b89609725e7bc3453f6b802 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Tue, 2 Jun 2026 07:49:05 -0700 Subject: [PATCH 0356/3297] perf/x86/intel/uncore: Defer ADL global PMON enable to enable_box() commit 9a0bb848a37150aeccc10088e141339917d995dc upstream. On some Raptor Cove CPUs, enabling uncore PMON globally at driver init may increase power consumption even when no perf events are in use. Drop adl_uncore_msr_init_box() and defer programming the global control register to enable_box(), so it is only set when a box is actually used. IMC and IMC freerunning counters use a separate control path and are unaffected. Fixes: 772ed05f3c5c ("perf/x86/intel/uncore: Add Alder Lake support") Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260602144908.263680-5-zide.chen@intel.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/events/intel/uncore_snb.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c index 807e582b8f17d..600328e30367c 100644 --- a/arch/x86/events/intel/uncore_snb.c +++ b/arch/x86/events/intel/uncore_snb.c @@ -539,12 +539,6 @@ void tgl_uncore_cpu_init(void) skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box; } -static void adl_uncore_msr_init_box(struct intel_uncore_box *box) -{ - if (box->pmu->pmu_idx == 0) - wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN); -} - static void adl_uncore_msr_enable_box(struct intel_uncore_box *box) { wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN); @@ -563,7 +557,6 @@ static void adl_uncore_msr_exit_box(struct intel_uncore_box *box) } static struct intel_uncore_ops adl_uncore_msr_ops = { - .init_box = adl_uncore_msr_init_box, .enable_box = adl_uncore_msr_enable_box, .disable_box = adl_uncore_msr_disable_box, .exit_box = adl_uncore_msr_exit_box, From f77e55baeeb8c8bd63f8a6e3f4f9674d1c4a4e54 Mon Sep 17 00:00:00 2001 From: Fushuai Wang Date: Wed, 20 May 2026 11:21:19 +0800 Subject: [PATCH 0357/3297] cpufreq: intel_pstate: Sync policy->cur during CPU offline commit bcbdaa1086c25a8a5d48e04e1b82fdfb0682b681 upstream. When a CPU goes offline with HWP disabled, intel_pstate_set_min_pstate() sets the MSR_IA32_PERF_CTL to minimum frequency to prevent SMT siblings from being restricted. However, the policy->cur value was not updated, leaving it at the previous value. When the CPU comes back online, governor->limits() checks if target_freq equals policy->cur and skips the frequency adjustment if they match. Since policy->cur still holds the previous value, the governor does not call cpufreq_driver->target to update MSR_IA32_PERF_CTL. Fix this by synchronizing policy->cur with the hardware state when setting minimum pstate during CPU offline. Fixes: bb18008f8086 ("intel_pstate: Set core to min P state during core offline") Cc: stable@vger.kernel.org # 3.15+ Suggested-by: Srinivas Pandruvada Signed-off-by: Fushuai Wang [ rjw: Subject refinement ] Link: https://patch.msgid.link/20260520032119.30615-1-fushuai.wang@linux.dev Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/intel_pstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 7fb6412b5f193..532e85ab0619b 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -3016,10 +3016,12 @@ static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) * from getting to lower performance levels, so force the minimum * performance on CPU offline to prevent that from happening. */ - if (hwp_active) + if (hwp_active) { intel_pstate_hwp_offline(cpu); - else + } else { intel_pstate_set_min_pstate(cpu); + policy->cur = cpu->pstate.min_freq; + } intel_pstate_exit_perf_limits(policy); From 4bd0da48fbc1dbef6774175129107fbbdd353e26 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 15 May 2026 10:37:40 -0400 Subject: [PATCH 0358/3297] sched/rt: Have RT_PUSH_IPI be default off for non PREEMPT_RT commit dd29c017aed628076e915fe4cdfb5392fd4c5cab upstream. RT migration is done aggressively. When a CPU schedules out a high priority RT task for a lower priority task, it will look to see if there's any RT tasks that are waiting to run on another CPU that is of higher priority than the task this CPU is about to run. If it finds one, it will pull that task over to the CPU and allow it to run there instead. Normally, this pulling is done by looking at the RT overloaded mask (rto) which contains all the CPUs in the scheduler domain with RT tasks that are waiting to run due to a higher priority RT task currently running on their CPU. The CPU that is about to schedule a lower priority task will grab the rq lock of the overloaded CPU and move the RT task from that CPU's runqueue to the local one and schedule the higher priority RT task. This caused issues when a lot of CPUs would schedule a lower priority task at the same time. They would all try to grab the same runqueue lock of the CPU with the overloaded RT tasks. Only the first CPU that got in will get that task. All the others would wait until they got the runqueue lock and see there's nothing to pull and do nothing. On systems with lots of CPUs, this caused a large latency (up to 500us) which is beyond what PREEMPT_RT is to allow. The solution to that was to create an RT_PUSH_IPI logic. When any CPU wanted to pull a task, instead of grabbing the runqueue lock of the overloaded CPU, it would start by sending an IPI to the overloaded CPU, and that IPI handler would have the CPU with the waiting RT task do a push instead. Then that handler would send an IPI to the next CPU with overloaded RT tasks, and so on. Note, after the first CPU starts this process, if another CPU wanted to do a pull, it would see that the process has already begun and would only increment a counter to have the IPIs continue again. The RT_PUSH_IPI solved the latency problem with PREEMPT_RT but could cause a new issue with non PREEMPT_RT. Namely, softirqs run in a threaded context on PREEMPT_RT but they can run in an interrupt context in non-RT. If an IPI lands on a CPU that has just woken up multiple RT tasks and the current CPU is running a non RT or a low priority RT task, instead of doing a push, it would simply do a schedule on that CPU. But if a softirq was also executing on this CPU, the schedule would need to wait until the softirq finished. Until then, the CPU would still be considered overloaded as there are RT tasks still waiting to run on it. A live lock occurred on a workload that was doing heavy networking traffic on a large machine where the softirqs would run 500us out of 750us. And it would also be waking up RT tasks, causing the RT pull logic to be constantly executed. When a softirq triggered on a CPU with RT tasks queued but not running yet, and the other CPUs would see this CPU as being overloaded, they would send an IPI over to it. The CPU would notice that the waiting RT tasks are of higher priority than the currently running task and simply schedule that CPU instead. But because the softirq was executing, before it could schedule, it would receive another IPI to do the same. The amount of IPIs would slow down the currently running softirq so much that before it could return back to task context, it would execute another softirq never allowing the CPU to schedule. This live locked that CPU. As RT_PUSH_IPI was created to help PREEMPT_RT, make it default off if PREEMPT_RT is not enabled. Fixes: b6366f048e0c ("sched/rt: Use IPI to trigger RT task push migration instead of pulling") Closes: https://lore.kernel.org/all/20260506235716.2530720-1-tj@kernel.org/ Reported-by: Tejun Heo Signed-off-by: Steven Rostedt Signed-off-by: Peter Zijlstra (Intel) Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260515103740.25ccbed8@gandalf.local.home Signed-off-by: Greg Kroah-Hartman --- kernel/sched/features.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 136a6584be797..659c0970cf2cd 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -103,8 +103,16 @@ SCHED_FEAT(WARN_DOUBLE_CLOCK, false) * rq lock and possibly create a large contention, sending an * IPI to that CPU and let that CPU push the RT task to where * it should go may be a better scenario. + * + * This is best for PREEMPT_RT, but for non-RT it can cause issues + * when preemption is disabled for long periods of time. Have + * it only default enabled for PREEMPT_RT. */ +# ifdef CONFIG_PREEMPT_RT SCHED_FEAT(RT_PUSH_IPI, true) +# else +SCHED_FEAT(RT_PUSH_IPI, false) +# endif #endif SCHED_FEAT(RT_RUNTIME_SHARE, false) From 6e175c00c62dca3d91b987015808b5d52e8db2b4 Mon Sep 17 00:00:00 2001 From: Tianxiang Chen Date: Wed, 8 Apr 2026 22:19:14 +0800 Subject: [PATCH 0359/3297] cpufreq: Fix hotplug-suspend race during reboot commit a9029dd55696c651ee46912afa2a166fa456bb3e upstream. During system reboot, cpufreq_suspend() is called via the kernel_restart() -> device_shutdown() path. Unlike the normal system suspend path, the reboot path does not call freeze_processes(), so userspace processes and kernel threads remain active. This allows CPU hotplug operations to run concurrently with cpufreq_suspend(). The original code has no synchronization with CPU hotplug, leading to a race condition where governor_data can be freed by the hotplug path while cpufreq_suspend() is still accessing it, resulting in a null pointer dereference: Unable to handle kernel NULL pointer dereference Call Trace: do_kernel_fault+0x28/0x3c cpufreq_suspend+0xdc/0x160 device_shutdown+0x18/0x200 kernel_restart+0x40/0x80 arm64_sys_reboot+0x1b0/0x200 Fix this by adding cpus_read_lock()/cpus_read_unlock() to cpufreq_suspend() to block CPU hotplug operations while suspend is in progress. Fixes: 65650b35133f ("cpufreq: Avoid cpufreq_suspend() deadlock on system shutdown") Signed-off-by: Tianxiang Chen Reviewed-by: Zhongqiu Han Cc: All applicable [ rjw: Changelog edits ] Link: https://patch.msgid.link/20260408141914.35281-1-nanmu@xiaomi.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1ed528e34bab4..a70df77f9b7d1 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1973,6 +1973,7 @@ void cpufreq_suspend(void) if (!cpufreq_driver) return; + cpus_read_lock(); if (!has_target() && !cpufreq_driver->suspend) goto suspend; @@ -1992,6 +1993,7 @@ void cpufreq_suspend(void) suspend: cpufreq_suspended = true; + cpus_read_unlock(); } /** From 6ba6f6783be2ffeb2cbcdc9321c4b9f708f796f7 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 16 Apr 2026 10:46:21 -0400 Subject: [PATCH 0360/3297] cpufreq: pcc: fix use-after-free and double free in _OSC evaluation commit 266d3dd8b757b48a576e90f018b51f7b7563cc32 upstream. pcc_cpufreq_do_osc() calls acpi_evaluate_object() twice for the two-phase _OSC negotiation. Between the two calls it freed output.pointer but left output.length unchanged. Since acpi_evaluate_object() treats a non-zero length with a non-NULL pointer as an existing buffer to write into, the second call wrote into freed memory (use-after-free). The subsequent kfree(output.pointer) at out_free then freed the same pointer a second time (double free). Reset output.pointer to NULL and output.length to ACPI_ALLOCATE_BUFFER after freeing the first result, so ACPICA allocates a fresh buffer for each phase independently. Fixes: 0f1d683fb35d ("[CPUFREQ] Processor Clocking Control interface driver") Signed-off-by: Yuho Choi Reviewed-by: Zhongqiu Han Cc: All applicable Link: https://patch.msgid.link/20260416144621.93964-1-dbgh9129@gmail.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/pcc-cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index ac2e90a65f0c4..a355ec4f3dd4c 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -352,6 +352,8 @@ static int __init pcc_cpufreq_do_osc(acpi_handle *handle) } kfree(output.pointer); + output.pointer = NULL; + output.length = ACPI_ALLOCATE_BUFFER; capabilities[0] = 0x0; capabilities[1] = 0x1; From 7776f9226e99eb49d97492b0b445027cfcb189da Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Fri, 12 Jun 2026 00:17:38 +0800 Subject: [PATCH 0361/3297] posix-cpu-timers: Fix pid refcount leak in do_cpu_nanosleep() error path commit 87bd2ad568e15b90d5f7d4bcd70342d05dad649c upstream. In do_cpu_nanosleep(), posix_cpu_timer_create() takes a pid reference via get_pid() and stores it in timer.it.cpu.pid. If the subsequent posix_cpu_timer_set() call fails, the function returns immediately without calling posix_cpu_timer_del() to release the pid reference, causing a leak. Fix it by calling posix_cpu_timer_del() before the unlock-and-return on the error path, consistent with the other exit paths in the same function. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: WenTao Liang Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611161738.97043-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- kernel/time/posix-cpu-timers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 2e5b89d7d8660..d6ccd9fbf5b96 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -1504,6 +1504,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, spin_lock_irq(&timer.it_lock); error = posix_cpu_timer_set(&timer, flags, &it, NULL); if (error) { + posix_cpu_timer_del(&timer); spin_unlock_irq(&timer.it_lock); return error; } From cd25e9819620aa1325897912cfb4dd89303325fe Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 9 Jun 2026 17:14:45 +0200 Subject: [PATCH 0362/3297] time/jiffies: Register jiffies clocksource before usage commit f24df84cbe05e4471c04ac4b921fc0340bbc7752 upstream. Teddy reported that a XEN HVM has a long boot delay, which was bisected to the recent enhancements to the negative motion detection. It turned out that the jiffies clocksource is used in early boot before it is registered, which leaves the max_delta_raw field at zero. That causes the read out to be clamped to the max delta of 0, which means time is not making progress. Cure it by ensuring that it is initialized before its first usage in timekeeping_init(). Fixes: 76031d9536a0 ("clocksource: Make negative motion detection more robust") Reported-by: Teddy Astie Signed-off-by: Thomas Gleixner Tested-by: Teddy Astie Cc: stable@vger.kernel.org Link: https://patch.msgid.link/87y0gn3fve.ffs@fw13 Closes: https://lore.kernel.org/all/1780914594.8631fc262581453bbf619ec5b2062170.19ea6c8227b000701b@vates.tech Signed-off-by: Greg Kroah-Hartman --- kernel/time/jiffies.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index 34eeacac22539..e34a0623e7ada 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -61,15 +61,14 @@ EXPORT_SYMBOL(get_jiffies_64); EXPORT_SYMBOL(jiffies); -static int __init init_jiffies_clocksource(void) -{ - return __clocksource_register(&clocksource_jiffies); -} - -core_initcall(init_jiffies_clocksource); +static bool cs_jiffies_registered __initdata; struct clocksource * __init __weak clocksource_default_clock(void) { + if (!cs_jiffies_registered) { + __clocksource_register(&clocksource_jiffies); + cs_jiffies_registered = true; + } return &clocksource_jiffies; } From 2421a7b24f9c8f3c3677fd5dacc9ddcfb3554637 Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Thu, 7 May 2026 21:15:54 +0530 Subject: [PATCH 0363/3297] clocksource/drivers/timer-tegra186: Fix support for multiple watchdog instances commit ca57bf46e7a94f8c53d05c376df9fcfdcb482100 upstream. Tegra SoCs support multiple watchdogs; currently only one (WDT0) is used. When multiple watchdogs are registered, tegra186_wdt_enable() overwrites the TKEIE(x) register, discarding any existing watchdog interrupt enable bits. As a result, enabling one watchdog inadvertently disables interrupts for the others. Fix this by preserving the existing TKEIE(x) value and updating it using a read-modify-write sequence. Fixes: 42cee19a9f83 ("clocksource: Add Tegra186 timers support") Cc: stable@vger.kernel.org Signed-off-by: Kartik Rajput Signed-off-by: Daniel Lezcano Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260507154557.2082697-2-kkartik@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/clocksource/timer-tegra186.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index 355558893e5f3..bfe16d2d5104e 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -149,7 +149,8 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt) u32 value; /* unmask hardware IRQ, this may have been lost across powergate */ - value = TKEIE_WDT_MASK(wdt->index, 1); + value = readl(tegra->regs + TKEIE(wdt->tmr->hwirq)); + value |= TKEIE_WDT_MASK(wdt->index, 1); writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq)); /* clear interrupt */ From be79d285bea70d0edd5015bd487311bfa8cbebc9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 11 Jun 2026 17:37:46 +0200 Subject: [PATCH 0364/3297] s390: Revert support for DCACHE_WORD_ACCESS commit 37540b8c287fc817bdbd0c62bb75ad6eab0e5d03 upstream. load_unaligned_zeropad() reads eight bytes from unaligned addresses and may cross page boundaries. It handles exceptions which may happen if reading from the second page results in an exception. For pages which are donated to the Ultravisor for secure execution purposes the do_secure_storage_access() exception handler however does not handle such exceptions correctly. Such an exception may result in an endless exception loop which will never be resolved. An attempt to fix this [1] turned out to be not sufficient. For now revert load_unaligned_zeropad() until this problem has been resolved in a proper way. Note that the implementation of load_unaligned_zeropad() itself is correct. The revert is just a temporary workaround until there is complete fix for secure storage access exceptions. [1] commit b00be77302d7 ("s390/mm: Add missing secure storage access fixups for donated memory") Fixes: 802ba53eefc5 ("s390: add support for DCACHE_WORD_ACCESS") Cc: stable@vger.kernel.org Acked-by: Christian Borntraeger Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- arch/s390/Kconfig | 1 - arch/s390/include/asm/asm-extable.h | 4 ---- arch/s390/include/asm/word-at-a-time.h | 22 ---------------------- arch/s390/mm/extable.c | 18 ------------------ 4 files changed, 45 deletions(-) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index e60d2b823e091..f43a6570fd6f7 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -161,7 +161,6 @@ config S390 select ARCH_WANTS_THP_SWAP select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS2 - select DCACHE_WORD_ACCESS if !KMSAN select DYNAMIC_FTRACE if FUNCTION_TRACER select FUNCTION_ALIGNMENT_8B if CC_IS_GCC select FUNCTION_ALIGNMENT_16B if !CC_IS_GCC diff --git a/arch/s390/include/asm/asm-extable.h b/arch/s390/include/asm/asm-extable.h index d23ea0c94e4ea..99748c20e7671 100644 --- a/arch/s390/include/asm/asm-extable.h +++ b/arch/s390/include/asm/asm-extable.h @@ -12,7 +12,6 @@ #define EX_TYPE_UA_FAULT 3 #define EX_TYPE_UA_LOAD_REG 5 #define EX_TYPE_UA_LOAD_REGPAIR 6 -#define EX_TYPE_ZEROPAD 7 #define EX_TYPE_FPC 8 #define EX_TYPE_UA_MVCOS_TO 9 #define EX_TYPE_UA_MVCOS_FROM 10 @@ -80,9 +79,6 @@ #define EX_TABLE_UA_LOAD_REGPAIR(_fault, _target, _regerr, _regzero) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REGPAIR, _regerr, _regzero, 0) -#define EX_TABLE_ZEROPAD(_fault, _target, _regdata, _regaddr) \ - __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_ZEROPAD, _regdata, _regaddr, 0) - #define EX_TABLE_FPC(_fault, _target) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_FPC, __stringify(%%r0), __stringify(%%r0), 0) diff --git a/arch/s390/include/asm/word-at-a-time.h b/arch/s390/include/asm/word-at-a-time.h index eaa19dee76994..e9287036392d0 100644 --- a/arch/s390/include/asm/word-at-a-time.h +++ b/arch/s390/include/asm/word-at-a-time.h @@ -4,7 +4,6 @@ #include #include -#include #include struct word_at_a_time { @@ -41,25 +40,4 @@ static inline unsigned long zero_bytemask(unsigned long data) return ~1UL << data; } -/* - * Load an unaligned word from kernel space. - * - * In the (very unlikely) case of the word being a page-crosser - * and the next page not being mapped, take the exception and - * return zeroes in the non-existing part. - */ -static inline unsigned long load_unaligned_zeropad(const void *addr) -{ - unsigned long data; - - asm_inline volatile( - "0: lg %[data],0(%[addr])\n" - "1: nopr %%r7\n" - EX_TABLE_ZEROPAD(0b, 1b, %[data], %[addr]) - EX_TABLE_ZEROPAD(1b, 1b, %[data], %[addr]) - : [data] "=d" (data) - : [addr] "a" (addr), "m" (*(unsigned long *)addr)); - return data; -} - #endif /* _ASM_WORD_AT_A_TIME_H */ diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c index 7498e858c4019..063b4346742d9 100644 --- a/arch/s390/mm/extable.c +++ b/arch/s390/mm/extable.c @@ -50,22 +50,6 @@ static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, return true; } -static bool ex_handler_zeropad(const struct exception_table_entry *ex, struct pt_regs *regs) -{ - unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data); - unsigned int reg_data = FIELD_GET(EX_DATA_REG_ERR, ex->data); - unsigned long data, addr, offset; - - addr = regs->gprs[reg_addr]; - offset = addr & (sizeof(unsigned long) - 1); - addr &= ~(sizeof(unsigned long) - 1); - data = *(unsigned long *)addr; - data <<= BITS_PER_BYTE * offset; - regs->gprs[reg_data] = data; - regs->psw.addr = extable_fixup(ex); - return true; -} - static bool ex_handler_fpc(const struct exception_table_entry *ex, struct pt_regs *regs) { fpu_sfpc(0); @@ -134,8 +118,6 @@ bool fixup_exception(struct pt_regs *regs) return ex_handler_ua_load_reg(ex, false, regs); case EX_TYPE_UA_LOAD_REGPAIR: return ex_handler_ua_load_reg(ex, true, regs); - case EX_TYPE_ZEROPAD: - return ex_handler_zeropad(ex, regs); case EX_TYPE_FPC: return ex_handler_fpc(ex, regs); case EX_TYPE_UA_MVCOS_TO: From 28390912740a2d23119ae7d3ec60ce69e11d1778 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Fri, 29 May 2026 15:33:45 +0100 Subject: [PATCH 0365/3297] perf/arm-cmn: Fix DVM node events commit 5936245125f78d896fdb1bbc2ae79213e28a6579 upstream. The new DVM node events added in CMN-700 also apply to CMN S3; fix the model encoding so that we can expose the aliases and handle occupancy filtering on newer CMNs too. Cc: stable@vger.kernel.org Fixes: 0dc2f4963f7e ("perf/arm-cmn: Support CMN S3") Signed-off-by: Robin Murphy Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- drivers/perf/arm-cmn.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 4fbafc4b79843..8924f482806ba 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -197,13 +197,14 @@ enum cmn_model { CMN600 = 1, CMN650 = 2, - CMN700 = 4, - CI700 = 8, + CI700 = 4, + CMN700 = 8, CMNS3 = 16, /* ...and then we can use bitmap tricks for commonality */ CMN_ANY = -1, NOT_CMN600 = -2, - CMN_650ON = CMN650 | CMN700 | CMNS3, + CMN_700ON = ~(CMN700 - 1), + CMN_650ON = CMN_700ON | CMN650, }; /* Actual part numbers and revision IDs defined by the hardware */ @@ -919,14 +920,14 @@ static struct attribute *arm_cmn_event_attrs[] = { CMN_EVENT_DVM(NOT_CMN600, txsnp_stall, 0x0a), CMN_EVENT_DVM(NOT_CMN600, trkfull, 0x0b), CMN_EVENT_DVM_OCC(NOT_CMN600, trk_occupancy, 0x0c), - CMN_EVENT_DVM_OCC(CMN700, trk_occupancy_cxha, 0x0d), - CMN_EVENT_DVM_OCC(CMN700, trk_occupancy_pdn, 0x0e), - CMN_EVENT_DVM(CMN700, trk_alloc, 0x0f), - CMN_EVENT_DVM(CMN700, trk_cxha_alloc, 0x10), - CMN_EVENT_DVM(CMN700, trk_pdn_alloc, 0x11), - CMN_EVENT_DVM(CMN700, txsnp_stall_limit, 0x12), - CMN_EVENT_DVM(CMN700, rxsnp_stall_starv, 0x13), - CMN_EVENT_DVM(CMN700, txsnp_sync_stall_op, 0x14), + CMN_EVENT_DVM_OCC(CMN_700ON, trk_occupancy_cxha, 0x0d), + CMN_EVENT_DVM_OCC(CMN_700ON, trk_occupancy_pdn, 0x0e), + CMN_EVENT_DVM(CMN_700ON, trk_alloc, 0x0f), + CMN_EVENT_DVM(CMN_700ON, trk_cxha_alloc, 0x10), + CMN_EVENT_DVM(CMN_700ON, trk_pdn_alloc, 0x11), + CMN_EVENT_DVM(CMN_700ON, txsnp_stall_limit, 0x12), + CMN_EVENT_DVM(CMN_700ON, rxsnp_stall_starv, 0x13), + CMN_EVENT_DVM(CMN_700ON, txsnp_sync_stall_op, 0x14), CMN_EVENT_HNF(CMN_ANY, cache_miss, 0x01), CMN_EVENT_HNF(CMN_ANY, slc_sf_cache_access, 0x02), From 18d90dc05d98bd5579ee38dba37c1e4132e765db Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 14 May 2026 08:55:58 +0200 Subject: [PATCH 0366/3297] X.509: Fix validation of ASN.1 certificate header commit 3b626ba431c4501512ad07549310685e07fe4706 upstream. x509_load_certificate_list() seeks to enforce that a certificate starts with 0x30 0x82 (ASN.1 SEQUENCE tag followed by a length of more than 256 and less than 65535 bytes). But it only enforces that *either* of those two byte values are present, instead of checking for the *conjunction* of the two values. Fix it. Fixes: 631cc66eb9ea ("MODSIGN: Provide module signing public keys to the kernel") Reported-by: Sashiko Closes: https://lore.kernel.org/r/20260508033917.B5873C2BCB0@smtp.kernel.org/ Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org # v3.7+ Reviewed-by: Ignat Korchagin Reviewed-by: Alistair Francis Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/asymmetric_keys/x509_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/x509_loader.c index a417413269989..0d516c77cc26a 100644 --- a/crypto/asymmetric_keys/x509_loader.c +++ b/crypto/asymmetric_keys/x509_loader.c @@ -20,7 +20,7 @@ int x509_load_certificate_list(const u8 cert_list[], */ if (end - p < 4) goto dodgy_cert; - if (p[0] != 0x30 && + if (p[0] != 0x30 || p[1] != 0x82) goto dodgy_cert; plen = (p[2] << 8) | p[3]; From 2382971aaaef5bf85a651234c64906f59580b8be Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Wed, 10 Jun 2026 17:40:03 +0200 Subject: [PATCH 0367/3297] mm/slab: do not limit zeroing to orig_size when only red zoning is enabled commit 648927ceb84021a25a0fbd5673740956f318d534 upstream. When init (zeroing) on allocation is requested, for kmalloc() we generally have to zero the full object size even if a smaller size is requested, in order to provide krealloc()'s __GFP_ZERO guarantees. But if we track the requested size, krealloc() uses that information to do the right thing, so we can zero only the requested size. With red zoning also enabled, any extra size became part of the red zone, so it must not be zeroed and thus we must zero only the requested size. However the current check is imprecise, and will trigger also when only SLAB_RED_ZONE is enabled without SLAB_STORE_USER (which enables tracking the requested size). This means enabling red zoning alone can compromise krealloc()'s __GFP_ZERO contract. Fix this by using slub_debug_orig_size() instead, which is the exact check for whether the requested size is tracked. We don't need to care if red zoning is also enabled or not. Also update and expand the comment accordingly. Fixes: 9ce67395f5a0 ("mm/slub: only zero requested size of buffer for kzalloc when debug enabled") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260610-slab_alloc_flags-v2-1-7190909db118@kernel.org Reviewed-by: Harry Yoo (Oracle) Reviewed-by: Hao Li Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Greg Kroah-Hartman --- mm/slub.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 5fdec3b837060..71e09c675047d 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5004,15 +5004,17 @@ bool slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, gfp_t init_flags = flags & gfp_allowed_mask; /* - * For kmalloc object, the allocated memory size(object_size) is likely - * larger than the requested size(orig_size). If redzone check is - * enabled for the extra space, don't zero it, as it will be redzoned - * soon. The redzone operation for this extra space could be seen as a - * replacement of current poisoning under certain debug option, and - * won't break other sanity checks. + * For kmalloc object, the allocated size (object_size) can be larger + * than the requested size (orig_size). We however need to zero the + * whole object_size to handle possible later krealloc() with + *__GFP_ZERO properly. + * + * But if we keep track of the requested size, krealloc() uses that + * information. Additionally if red zoning is enabled, the extra space + * is also red zone, so we should not overwrite it. So limit zeroing to + * orig_size if we track it. */ - if (kmem_cache_debug_flags(s, SLAB_STORE_USER | SLAB_RED_ZONE) && - (s->flags & SLAB_KMALLOC)) + if (slub_debug_orig_size(s)) zero_size = orig_size; /* From e0eec7497bcc7f61d70cb37c69d1382f76e2db74 Mon Sep 17 00:00:00 2001 From: Xuewen Wang Date: Mon, 18 May 2026 14:21:57 +0800 Subject: [PATCH 0368/3297] tools/mm/slabinfo: Fix trace disable logic inversion commit 235ab68d67eadbef1fdbfb771f21f5bacc77a2ae upstream. The disable trace path in slab_debug() had a logic error where it would set trace=1 instead of trace=0. This made trace functionality permanently enabled once turned on for any slab cache. Fixes: a87615b8f9e2 ("SLUB: slabinfo upgrade") Cc: stable@vger.kernel.org Reviewed-by: SeongJae Park Signed-off-by: Xuewen Wang WARNING: From:/Signed-off-by: email address mismatch: 'From: wangxuewen <18810879172@163.com>' != 'Signed-off-by: wangxuewen ' Link: https://patch.msgid.link/20260518062159.80664-2-wangxuewen@kylinos.cn Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Greg Kroah-Hartman --- tools/mm/slabinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c index 80cdbd3db82df..7c51d283504d9 100644 --- a/tools/mm/slabinfo.c +++ b/tools/mm/slabinfo.c @@ -798,7 +798,7 @@ static void slab_debug(struct slabinfo *s) fprintf(stderr, "%s can only enable trace for one slab at a time\n", s->name); } if (!tracing && s->trace) - set_obj(s, "trace", 1); + set_obj(s, "trace", 0); } static void totals(void) From abf07f5c3584f453e81b3d85a3b2712a4319d373 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Fri, 12 Jun 2026 15:13:59 +0800 Subject: [PATCH 0369/3297] tools/mm/slabinfo: fix total_objects attribute name commit 892a7864730775c3dbee2a39e9ead4fa8d4256e7 upstream. SLUB exports the total_objects sysfs attribute, but slabinfo tries to read objects_total. As a result, the lookup fails and the field remains zero. Use the correct attribute name and rename the corresponding structure member to match. Fixes: 205ab99dd103 ("slub: Update statistics handling for variable order slabs") Signed-off-by: Yichong Chen Cc: Reviewed-by: SeongJae Park Link: https://patch.msgid.link/96556748872BB47E+20260612071359.649946-1-chenyichong@uniontech.com Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Greg Kroah-Hartman --- tools/mm/slabinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c index 7c51d283504d9..2c1c052067c6f 100644 --- a/tools/mm/slabinfo.c +++ b/tools/mm/slabinfo.c @@ -33,7 +33,7 @@ struct slabinfo { unsigned int hwcache_align, object_size, objs_per_slab; unsigned int sanity_checks, slab_size, store_user, trace; int order, poison, reclaim_account, red_zone; - unsigned long partial, objects, slabs, objects_partial, objects_total; + unsigned long partial, objects, slabs, objects_partial, total_objects; unsigned long alloc_fastpath, alloc_slowpath; unsigned long free_fastpath, free_slowpath; unsigned long free_frozen, free_add_partial, free_remove_partial; @@ -1263,7 +1263,7 @@ static void read_slab_dir(void) slab->object_size = get_obj("object_size"); slab->objects = get_obj("objects"); slab->objects_partial = get_obj("objects_partial"); - slab->objects_total = get_obj("objects_total"); + slab->total_objects = get_obj("total_objects"); slab->objs_per_slab = get_obj("objs_per_slab"); slab->order = get_obj("order"); slab->partial = get_obj("partial"); From dae1d000ddfd5c2140b036e47fff0c497ae9c64b Mon Sep 17 00:00:00 2001 From: Tianchu Chen Date: Fri, 29 May 2026 13:42:47 +0000 Subject: [PATCH 0370/3297] HID: hid-goodix-spi: validate report size to prevent stack buffer overflow commit db0a0768d09273aadadeb76730cd658d720333a4 upstream. goodix_hid_set_raw_report() builds a protocol frame in a 128-byte stack buffer (tmp_buf), writing an 11-12 byte header followed by the caller-supplied report data. The HID core caps report size at HID_MAX_BUFFER_SIZE (16384) by default, while the driver does not set hid_ll_driver.max_buffer_size and performs no bounds checking before copying the payload: memcpy(tmp_buf + tx_len, buf, len); A hidraw SET_REPORT ioctl with a report larger than ~116 bytes overflows the stack buffer. Add a size check after constructing the header, rejecting reports that would exceed the buffer capacity. Discovered by Atuin - Automated Vulnerability Discovery Engine. Fixes: 75e16c8ce283 ("HID: hid-goodix: Add Goodix HID-over-SPI driver") Cc: stable@vger.kernel.org Signed-off-by: Tianchu Chen Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-goodix-spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-goodix-spi.c b/drivers/hid/hid-goodix-spi.c index 80c0288a3a38b..288cb827e9d6d 100644 --- a/drivers/hid/hid-goodix-spi.c +++ b/drivers/hid/hid-goodix-spi.c @@ -520,6 +520,9 @@ static int goodix_hid_set_raw_report(struct hid_device *hid, memcpy(tmp_buf + tx_len, args, args_len); tx_len += args_len; + if (tx_len + len > sizeof(tmp_buf)) + return -EINVAL; + memcpy(tmp_buf + tx_len, buf, len); tx_len += len; From 7ce2c7dd28ab8586cfa494e166ddb397f153562b Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Sat, 6 Jun 2026 18:15:52 +0000 Subject: [PATCH 0371/3297] HID: uhid: convert to hid_safe_input_report() commit 63a694c51bf120a37550890b8e7736b4888985e9 upstream. Commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()"), added a check in hid_report_raw_event() to reject reports if the received data size is smaller than expected. This was intended to prevent OOB errors by no longer allowing zeroing-out of shorter reports due to the lack of buffer size information. However, this leads to regressions in hid_report_raw_event(), where shorter than expected reports are rejected, even though their buffers are sufficiently large to be zero-padded. To solve this issue, Benjamin introduced a safer alternative in commit 206342541fc8 ("HID: core: introduce hid_safe_input_report()"), which forwards the buffer size and allows hid_report_raw_event() to safely zero-pad the data. Convert uhid to use hid_safe_input_report() and pass UHID_DATA_MAX as the buffer size. This prevents the reported regressions [1], allowing hid core to zero-pad the shorter reports safely as expected. Cc: stable@vger.kernel.org Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()") Closes: https://lore.kernel.org/all/ahsh0UtTX6e0ZeHa@google.com/ [1] Signed-off-by: Carlos Llamas Reviewed-by: Lee Jones Closes: https://lore.kernel.org/all/ahsh0UtTX6e0ZeHa@google.com/ Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/uhid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 21a70420151e4..d16667207b9f9 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -595,8 +595,8 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) if (!READ_ONCE(uhid->running)) return -EINVAL; - hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, - min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + hid_safe_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, UHID_DATA_MAX, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); return 0; } @@ -606,8 +606,8 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev) if (!READ_ONCE(uhid->running)) return -EINVAL; - hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, - min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); + hid_safe_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, UHID_DATA_MAX, + min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); return 0; } From 416095e9a6037b4b39fcadd0d2bd77a8852211ec Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Thu, 4 Jun 2026 13:56:58 +0900 Subject: [PATCH 0372/3297] HID: wacom: stop hardware after post-start probe failures commit ec2612b8ad9e642596db011dd8b6568ef1edeaa1 upstream. wacom_parse_and_register() starts HID hardware before registering inputs and initializing pad LEDs/remotes. Those later steps can fail, but their error paths currently release Wacom resources without stopping the HID hardware. Route post-hid_hw_start() failures through hid_hw_stop() before releasing driver resources. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: c1d6708bf0d3 ("HID: wacom: Do not register input devices until after hid_hw_start") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_sys.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 63b5fed9415d8..e9bead4f61878 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -2462,16 +2462,16 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) error = wacom_register_inputs(wacom); if (error) - goto fail; + goto fail_hw_stop; if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) { error = wacom_initialize_leds(wacom); if (error) - goto fail; + goto fail_hw_stop; error = wacom_initialize_remotes(wacom); if (error) - goto fail; + goto fail_hw_stop; } if (!wireless) { @@ -2485,14 +2485,14 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) cancel_delayed_work_sync(&wacom->init_work); _wacom_query_tablet_data(wacom); error = -ENODEV; - goto fail_quirks; + goto fail_hw_stop; } if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) { error = hid_hw_open(hdev); if (error) { hid_err(hdev, "hw open failed\n"); - goto fail_quirks; + goto fail_hw_stop; } } @@ -2501,7 +2501,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) return 0; -fail_quirks: +fail_hw_stop: hid_hw_stop(hdev); fail: wacom_release_resources(wacom); From cb90a01e478c220c7fa710fd883377750d0e1d9c Mon Sep 17 00:00:00 2001 From: Oleg Makarenko Date: Tue, 9 Jun 2026 19:00:27 +0300 Subject: [PATCH 0373/3297] HID: pidff: Use correct effect type in effect update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b251598b8bf37300510868f739a79e07800d41ce upstream. When updating an existing effect, the effect type from the last created effect was sent to the device instead of the updated one. This caused incorrect reports when a game creates multiple different effects and updates only one that is not the last created. Fixes FFB in multiple games that create multiple simultaneous effects (Forza Horizon 5/6). Fixes: 224ee88fe395 ("Input: add force feedback driver for PID devices") Cc: stable@vger.kernel.org Tested-by: Oliver Roundtree Co-developed-by: Ryno Kotzé Signed-off-by: Ryno Kotzé Signed-off-by: Oleg Makarenko Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/usbhid/hid-pidff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index c06b1913f5baf..7eb9e28c0d909 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -521,7 +521,7 @@ static void pidff_set_effect_report(struct pidff_device *pidff, pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; pidff->set_effect_type->value[0] = - pidff->create_new_effect_type->value[0]; + pidff_get_effect_type_id(pidff, effect); pidff_set_duration(&pidff->set_effect[PID_DURATION], effect->replay.length); From ca899a926c11a59211b764b0155d9a1cdcc32b81 Mon Sep 17 00:00:00 2001 From: Jinmo Yang Date: Fri, 29 May 2026 02:59:45 +0900 Subject: [PATCH 0374/3297] HID: wacom: fix slab-out-of-bounds write in wacom_wac_queue_insert commit 6b3014ec0e9a390ca563030b2d7689921f0daef5 upstream. wacom_wac_queue_insert() calls kfifo_skip() in a loop when the kfifo doesn't have enough space for the incoming report. If the kfifo is empty, kfifo_skip() reads stale data left in the kmalloc'd buffer via __kfifo_peek_n() and interprets it as a record length, advancing fifo->out by that garbage value. This corrupts the internal kfifo state, causing kfifo_unused() to return a value much larger than the actual buffer size, which bypasses __kfifo_in_r()'s guard: if (len + recsize > kfifo_unused(fifo)) return 0; kfifo_copy_in() then performs an out-of-bounds memcpy, writing up to 3842 bytes past the 256-byte buffer. Add a !kfifo_is_empty() condition to the while loop so kfifo_skip() is never called on an empty fifo, and check the return value of kfifo_in() to reject reports that are too large for the fifo. Suggested-by: Dmitry Torokhov Fixes: 5e013ad20689 ("HID: wacom: Remove static WACOM_PKGLEN_MAX limit") Cc: stable@vger.kernel.org Signed-off-by: Jinmo Yang Reviewed-by: Dmitry Torokhov Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_sys.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index e9bead4f61878..61ad3e40a7477 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -54,7 +54,7 @@ static void wacom_wac_queue_insert(struct hid_device *hdev, { bool warned = false; - while (kfifo_avail(fifo) < size) { + while (kfifo_avail(fifo) < size && !kfifo_is_empty(fifo)) { if (!warned) hid_warn(hdev, "%s: kfifo has filled, starting to drop events\n", __func__); warned = true; @@ -62,7 +62,9 @@ static void wacom_wac_queue_insert(struct hid_device *hdev, kfifo_skip(fifo); } - kfifo_in(fifo, raw_data, size); + if (!kfifo_in(fifo, raw_data, size)) + hid_warn_ratelimited(hdev, "%s: report is too large (%d)\n", + __func__, size); } static void wacom_wac_queue_flush(struct hid_device *hdev, From bbe1e55629bfaabd4b2e8125b48dd3503d74ac8b Mon Sep 17 00:00:00 2001 From: Jinmo Yang Date: Mon, 1 Jun 2026 22:41:23 +0900 Subject: [PATCH 0375/3297] HID: wacom: use GFP_ATOMIC in wacom_wac_queue_flush() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 55f1ad573e34abf9a0443c34bc5a63d74edba7d7 upstream. wacom_wac_queue_flush() is called via the .raw_event callback (wacom_raw_event → wacom_wac_pen_serial_enforce → wacom_wac_queue_flush). For USB HID devices, this callback is invoked from hid_irq_in(), which is a URB completion handler running in atomic context. Using GFP_KERNEL in this path can sleep, leading to a "scheduling while atomic" bug. Use GFP_ATOMIC instead. The existing code already handles allocation failure by skipping the fifo entry and continuing. Reported-by: Sashiko-bot Fixes: 5e013ad20689 ("HID: wacom: Remove static WACOM_PKGLEN_MAX limit") Cc: stable@vger.kernel.org Reviewed-by: Dmitry Torokhov Signed-off-by: Jinmo Yang Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 61ad3e40a7477..e298ed9b502af 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -76,7 +76,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev, unsigned int count; int err; - buf = kzalloc(size, GFP_KERNEL); + buf = kzalloc(size, GFP_ATOMIC); if (!buf) { kfifo_skip(fifo); continue; From 3eca1a8165b5e7996e699e9df76cb4645e184d42 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 22:27:00 +0545 Subject: [PATCH 0376/3297] HID: letsketch: fix UAF on inrange_timer at driver unbind commit 46c8beeccd8ab2c863827254a85ea877654a3534 upstream. letsketch_driver does not provide a .remove callback, but letsketch_probe() arms a per-device timer: timer_setup(&data->inrange_timer, letsketch_inrange_timeout, 0); The timer is re-armed from letsketch_raw_event() with a 100 ms timeout on every pen-in-range report, and its callback dereferences data->input_tablet to deliver a synthetic BTN_TOOL_PEN release. letsketch_data is allocated with devm_kzalloc(), and its input_dev fields are devm-allocated via letsketch_setup_input_tablet(). On device unbind (USB unplug or rmmod), the HID core runs its default teardown and devm cleanup frees both letsketch_data and the input devices. Because no .remove callback exists, nothing drains the timer first: if raw_event armed it within ~100 ms of the unbind, the pending timer fires on freed memory. This is a UAF read of data and of data->input_tablet, followed by input_report_key() / input_sync() into the freed input_dev. The same problem can occur on the probe error path: if hid_hw_start() enabled I/O on an always-poll-quirk device and then failed, raw_event may have armed the timer before devm releases data. Fix by adding a .remove callback that calls hid_hw_stop() first. hid_hw_stop() synchronously kills the URBs that deliver raw_event(), so once it returns no path can re-arm the timer. timer_shutdown_sync() then drains any in-flight callback and permanently disables further mod_timer() calls. Apply the same timer_shutdown_sync() in the probe error path so the timer is guaranteed not to outlive data. Fixes: 33a5c2793451 ("HID: Add new Letsketch tablet driver") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-letsketch.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c index 11e21f9887232..b52e93a91ae54 100644 --- a/drivers/hid/hid-letsketch.c +++ b/drivers/hid/hid-letsketch.c @@ -296,13 +296,42 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id * ret = letsketch_setup_input_tablet(data); if (ret) - return ret; + goto err_shutdown_timer; ret = letsketch_setup_input_tablet_pad(data); if (ret) - return ret; + goto err_shutdown_timer; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + goto err_shutdown_timer; - return hid_hw_start(hdev, HID_CONNECT_HIDRAW); + return 0; + +err_shutdown_timer: + /* + * Drain any pending callback and permanently disable the timer + * before devm releases data: if hid_hw_start() enabled I/O on an + * always-poll-quirk device and then failed, raw_event may have + * armed the timer already. + */ + timer_shutdown_sync(&data->inrange_timer); + return ret; +} + +static void letsketch_remove(struct hid_device *hdev) +{ + struct letsketch_data *data = hid_get_drvdata(hdev); + + /* + * hid_hw_stop() synchronously kills the URBs that deliver + * raw_event(), so once it returns no path can re-arm + * inrange_timer. timer_shutdown_sync() then drains any + * in-flight callback and permanently disables further + * mod_timer() calls before devm releases data. + */ + hid_hw_stop(hdev); + timer_shutdown_sync(&data->inrange_timer); } static const struct hid_device_id letsketch_devices[] = { @@ -315,6 +344,7 @@ static struct hid_driver letsketch_driver = { .name = "letsketch", .id_table = letsketch_devices, .probe = letsketch_probe, + .remove = letsketch_remove, .raw_event = letsketch_raw_event, }; module_hid_driver(letsketch_driver); From 37daa8c96bd563d03150e23f094cb60703594a6d Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 2 Jul 2026 00:13:19 +0700 Subject: [PATCH 0377/3297] HID: multitouch: fix out-of-bounds bit access on mt_io_flags commit 8813b0612275cc61fe9e6603d0ee019247ade6be upstream. mt_io_flags is a single unsigned long, but mt_process_slot(), mt_release_pending_palms() and mt_release_contacts() use it as a per-slot bitmap indexed by the slot number. That slot number is only bounded by td->maxcontacts, which is taken from the device's ContactCountMaximum feature report and can be up to 255, not by BITS_PER_LONG. As a result, a multitouch device that advertises a large contact count makes set_bit()/clear_bit() operate past the mt_io_flags word and corrupt the adjacent members of struct mt_device. The sticky-fingers release timer is the easiest way to reach this. mt_release_contacts() runs for (i = 0; i < mt->num_slots; i++) clear_bit(i, &td->mt_io_flags); with num_slots == maxcontacts. For maxcontacts around 250 the loop clears the bits that overlap td->applications.next, zeroing that list head, and the list_for_each_entry() that immediately follows then dereferences NULL. The kernel panics from timer (softirq) context. On a KASAN build this shows up as a general protection fault in mt_release_contacts() with a null-ptr-deref at offset 0x58, which is offsetof(struct mt_application, num_received). The state is reachable from an untrusted USB or Bluetooth HID multitouch device; no local privileges are required. Store the per-slot active state in a separately allocated bitmap sized for maxcontacts, the same pattern already used for pending_palm_slots, and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two "mt_io_flags & MT_IO_SLOTS_MASK" arming checks become bitmap_empty(td->active_slots, td->maxcontacts). Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the same commit to leave the low byte for the slot bits; with the slot bits gone it fits in bit 0 again, which also keeps it within the unsigned long on 32-bit. Fixes: 46f781e0d151 ("HID: multitouch: fix sticky fingers") Cc: stable@vger.kernel.org Signed-off-by: Trung Nguyen Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a543d48221070..1959481dc7820 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -31,6 +31,7 @@ * [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ +#include #include #include #include @@ -96,8 +97,7 @@ enum report_mode { TOUCHPAD_REPORT_ALL = TOUCHPAD_REPORT_BUTTONS | TOUCHPAD_REPORT_CONTACTS, }; -#define MT_IO_SLOTS_MASK GENMASK(7, 0) /* reserve first 8 bits for slot tracking */ -#define MT_IO_FLAGS_RUNNING 32 +#define MT_IO_FLAGS_RUNNING 0 static const bool mtrue = true; /* default for true */ static const bool mfalse; /* default for false */ @@ -173,10 +173,9 @@ struct mt_device { struct timer_list release_timer; /* to release sticky fingers */ struct hid_haptic_device *haptic; /* haptic related configuration */ struct hid_device *hdev; /* hid_device we're attached to */ - unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) - * first 8 bits are reserved for keeping the slot - * states, this is fine because we only support up - * to 250 slots (MT_MAX_MAXCONTACT) + unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) */ + unsigned long *active_slots; /* bitmap of slots with an active + * contact, sized for maxcontacts */ __u8 inputmode_value; /* InputMode HID feature value */ __u8 maxcontacts; @@ -1025,7 +1024,7 @@ static void mt_release_pending_palms(struct mt_device *td, for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) { clear_bit(slotnum, app->pending_palm_slots); - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); input_mt_slot(input, slotnum); input_mt_report_slot_inactive(input); @@ -1236,9 +1235,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); - set_bit(slotnum, &td->mt_io_flags); + set_bit(slotnum, td->active_slots); } else { - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); } return 0; @@ -1373,7 +1372,7 @@ static void mt_touch_report(struct hid_device *hid, * defect. */ if (app->quirks & MT_QUIRK_STICKY_FINGERS) { - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); else @@ -1430,6 +1429,15 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (td->is_buttonpad) __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + if (!td->active_slots) { + td->active_slots = devm_kcalloc(&td->hdev->dev, + BITS_TO_LONGS(td->maxcontacts), + sizeof(long), + GFP_KERNEL); + if (!td->active_slots) + return -ENOMEM; + } + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, BITS_TO_LONGS(td->maxcontacts), sizeof(long), @@ -1907,7 +1915,7 @@ static void mt_release_contacts(struct hid_device *hid) for (i = 0; i < mt->num_slots; i++) { input_mt_slot(input_dev, i); input_mt_report_slot_inactive(input_dev); - clear_bit(i, &td->mt_io_flags); + clear_bit(i, td->active_slots); } input_mt_sync_frame(input_dev); input_sync(input_dev); @@ -1930,7 +1938,7 @@ static void mt_expired_timeout(struct timer_list *t) */ if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mt_release_contacts(hdev); clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } From b363d964ca829c1761c9f04188dfa28f90b0f2d4 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 23:17:52 +0545 Subject: [PATCH 0378/3297] HID: appleir: fix UAF on pending key_up_timer in remove() commit 75fe87e19d8aff81eb2c64d15d244ab8da4de945 upstream. appleir_remove() runs hid_hw_stop() before timer_delete_sync(). hid_hw_stop() synchronously unregisters the HID input device via hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(), which drops the last reference and frees the underlying input_dev when no userspace handle holds it open. key_up_tick() reads appleir->input_dev and calls input_report_key() / input_sync() on it. The timer is armed from appleir_raw_event() with a HZ/8 (~125 ms) timeout on every keydown and key-repeat report. If a key was pressed shortly before the device is disconnected, the timer can fire after hid_hw_stop() has freed input_dev but before the teardown drains it. A simple reorder is not sufficient. Putting the timer drain first still leaves a window where a USB URB completion (raw_event) running during hid_hw_stop() can call mod_timer() and re-arm the timer, which then fires after hidinput_disconnect() has freed input_dev. The same URB-completion window also lets raw_event() reach key_up(), key_down() and battery_flat() directly, all of which dereference appleir->input_dev. Introduce a 'removing' flag on struct appleir, gated by the existing spinlock. appleir_remove() sets the flag under the lock and then shuts down the timer with timer_shutdown_sync(), which both drains any in-flight callback and permanently disables further mod_timer() calls. appleir_raw_event() and key_up_tick() bail out early if the flag is set, so no path can arm or run the timer, or dereference appleir->input_dev, after remove() has started tearing down. The keyrepeat and flatbattery branches of appleir_raw_event() previously called into the input layer without holding the spinlock; take it now so the flag check is well-defined. This incidentally closes a pre-existing read-side race on appleir->current_key in the keyrepeat branch. This bug is structurally a sibling of commit 4db2af929279 ("HID: appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been present since the driver was introduced. Fixes: 9a4a5574ce42 ("HID: appleir: add support for Apple ir devices") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-appleir.c | 45 ++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index 5e8ced7bc05a3..adaa44a858edd 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -109,9 +109,10 @@ struct appleir { struct hid_device *hid; unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; struct timer_list key_up_timer; /* timer for key up */ - spinlock_t lock; /* protects .current_key */ + spinlock_t lock; /* protects .current_key, .removing */ int current_key; /* the currently pressed key */ int prev_key_idx; /* key index in a 2 packets message */ + bool removing; /* set during teardown; gates input_dev access */ }; static int get_key(int data) @@ -172,7 +173,7 @@ static void key_up_tick(struct timer_list *t) unsigned long flags; spin_lock_irqsave(&appleir->lock, flags); - if (appleir->current_key) { + if (!appleir->removing && appleir->current_key) { key_up(hid, appleir, appleir->current_key); appleir->current_key = 0; } @@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, int index; spin_lock_irqsave(&appleir->lock, flags); + if (appleir->removing) { + spin_unlock_irqrestore(&appleir->lock, flags); + goto out; + } /* * If we already have a key down, take it up before marking * this one down @@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, appleir->prev_key_idx = 0; if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { - key_down(hid, appleir, appleir->current_key); - /* - * Remote doesn't do key up, either pull them up, in the test - * above, or here set a timer which pulls them up after 1/8 s - */ - mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) { + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in + * the test above, or here set a timer which pulls them + * up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + } + spin_unlock_irqrestore(&appleir->lock, flags); goto out; } if (!memcmp(data, flatbattery, sizeof(flatbattery))) { - battery_flat(appleir); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) + battery_flat(appleir); + spin_unlock_irqrestore(&appleir->lock, flags); /* Fall through */ } @@ -318,8 +331,20 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) static void appleir_remove(struct hid_device *hid) { struct appleir *appleir = hid_get_drvdata(hid); + unsigned long flags; + + /* + * Mark the driver as tearing down so that any concurrent raw_event + * (e.g. from a USB URB completion that hid_hw_stop() has not yet + * killed) and the key_up_timer softirq stop touching input_dev + * before hid_hw_stop() frees it via hidinput_disconnect(). + */ + spin_lock_irqsave(&appleir->lock, flags); + appleir->removing = true; + spin_unlock_irqrestore(&appleir->lock, flags); + + timer_shutdown_sync(&appleir->key_up_timer); hid_hw_stop(hid); - timer_delete_sync(&appleir->key_up_timer); } static const struct hid_device_id appleir_devices[] = { From 4d0d51bc12d246accbfbb94de05d729c68c9b8fb Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 15:06:35 +0800 Subject: [PATCH 0379/3297] HID: lg-g15: cancel pending work on remove to fix a use-after-free commit 7705b4140d188ce22656f6e541ae7ef834c7e11a upstream. lg_g15_data is allocated with devm and holds a work item. The report handlers schedule that work straight from device input. lg_g15_event() and lg_g15_v2_event() do it on the backlight cycle key, and lg_g510_leds_event() does it too. The worker dereferences the lg_g15_data back through container_of. The driver had no remove callback and never cancelled the work. So if a report scheduled the work and the keyboard was then unplugged, devres freed lg_g15_data while the work was still pending or running, and the worker touched freed memory. This is a use-after-free. It is reachable as a race on device unplug. Add a remove callback that cancels the work before devres frees the state. g15->work is only initialized for the models that schedule it (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so guard the cancel on g15->work.func to avoid cancelling a work that was never set up. The g15 NULL test mirrors the one already in lg_g15_raw_event(). Fixes: 97b741aba918 ("HID: lg-g15: Add keyboard and LCD backlight control") Cc: stable@vger.kernel.org Suggested-by: Hans de Goede Signed-off-by: Maoyi Xie Reviewed-by: Hans de Goede Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-lg-g15.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index f8605656257bb..4a3131cb28f21 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -929,11 +929,27 @@ static const struct hid_device_id lg_g15_devices[] = { }; MODULE_DEVICE_TABLE(hid, lg_g15_devices); +static void lg_g15_remove(struct hid_device *hdev) +{ + struct lg_g15_data *g15 = hid_get_drvdata(hdev); + + /* + * g15->work is only initialized for the models that schedule it + * (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so only + * cancel it when it was set up. + */ + if (g15 && g15->work.func) + cancel_work_sync(&g15->work); + + hid_hw_stop(hdev); +} + static struct hid_driver lg_g15_driver = { .name = "lg-g15", .id_table = lg_g15_devices, .raw_event = lg_g15_raw_event, .probe = lg_g15_probe, + .remove = lg_g15_remove, }; module_hid_driver(lg_g15_driver); From c63bc6308da716098833fe3185481cb9905dde79 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 10 Jun 2026 16:29:09 +0800 Subject: [PATCH 0380/3297] HID: sensor-hub: Add sensor_hub_input_attr_read_values() for multi-byte reads commit f784fcea450617055d2d12eec5b2f6e0e38bf878 upstream. sensor_hub_input_attr_get_raw_value() is limited to returning a single 32-bit value, which is insufficient for sensors that report data larger than 32 bits, such as a quaternion with four s16 elements. Add sensor_hub_input_attr_read_values() that accepts a caller-provided buffer and accumulates incoming data until the buffer is full. The two paths are distinguished in sensor_hub_raw_event() by pending.max_raw_size being non-zero, preserving backward compatibility. Signed-off-by: Srinivas Pandruvada Co-developed-by: Zhang Lixu Signed-off-by: Zhang Lixu Acked-by: Jiri Kosina Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-sensor-hub.c | 77 +++++++++++++++++++++++++++++++--- include/linux/hid-sensor-hub.h | 25 +++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 4c94c03cb5739..1b725c8796506 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -286,6 +286,54 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, } EXPORT_SYMBOL_GPL(sensor_hub_get_feature); +int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev, + u32 usage_id, u32 attr_usage_id, + u32 report_id, + enum sensor_hub_read_flags flag, + u32 buffer_size, u8 *buffer) +{ + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + struct hid_report *report; + unsigned long flags; + long cycles; + int ret; + + report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); + if (!report) + return -EINVAL; + + mutex_lock(hsdev->mutex_ptr); + if (flag == SENSOR_HUB_SYNC) { + memset(&hsdev->pending, 0, sizeof(hsdev->pending)); + init_completion(&hsdev->pending.ready); + hsdev->pending.usage_id = usage_id; + hsdev->pending.attr_usage_id = attr_usage_id; + hsdev->pending.max_raw_size = buffer_size; + hsdev->pending.raw_data = buffer; + + spin_lock_irqsave(&data->lock, flags); + hsdev->pending.status = true; + spin_unlock_irqrestore(&data->lock, flags); + } + mutex_lock(&data->mutex); + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); + mutex_unlock(&data->mutex); + ret = 0; + if (flag == SENSOR_HUB_SYNC) { + cycles = wait_for_completion_interruptible_timeout(&hsdev->pending.ready, + HZ * 5); + if (cycles == 0) + ret = -ETIMEDOUT; + else if (cycles < 0) + ret = cycles; + + hsdev->pending.status = false; + } + mutex_unlock(hsdev->mutex_ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_input_attr_read_values); int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, u32 usage_id, @@ -480,6 +528,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev, struct hid_collection *collection = NULL; void *priv = NULL; struct hid_sensor_hub_device *hsdev = NULL; + u32 copy_size; + u32 avail; hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", report->id, size, report->type); @@ -520,12 +570,27 @@ static int sensor_hub_raw_event(struct hid_device *hdev, hsdev->pending.attr_usage_id == report->field[i]->logical)) { hid_dbg(hdev, "data was pending ...\n"); - hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); - if (hsdev->pending.raw_data) - hsdev->pending.raw_size = sz; - else - hsdev->pending.raw_size = 0; - complete(&hsdev->pending.ready); + if (hsdev->pending.max_raw_size) { + if (hsdev->pending.index < hsdev->pending.max_raw_size) { + avail = hsdev->pending.max_raw_size - hsdev->pending.index; + copy_size = clamp(sz, 0U, avail); + + memcpy(hsdev->pending.raw_data + hsdev->pending.index, + ptr, copy_size); + hsdev->pending.index += copy_size; + if (hsdev->pending.index >= hsdev->pending.max_raw_size) { + hsdev->pending.raw_size = hsdev->pending.index; + complete(&hsdev->pending.ready); + } + } + } else { + hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); + if (hsdev->pending.raw_data) + hsdev->pending.raw_size = sz; + else + hsdev->pending.raw_size = 0; + complete(&hsdev->pending.ready); + } } if (callback->capture_sample) { if (report->field[i]->logical) diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index e710565531085..ab5cc8db3fbb3 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -43,6 +43,8 @@ struct hid_sensor_hub_attribute_info { * @attr_usage_id: Usage Id of a field, e.g. X-axis for a gyro. * @raw_size: Response size for a read request. * @raw_data: Place holder for received response. + * @index: Current write index into raw_data for multi-byte reads. + * @max_raw_size: Total buffer size for multi-byte reads; 0 for single-value reads. */ struct sensor_hub_pending { bool status; @@ -51,6 +53,8 @@ struct sensor_hub_pending { u32 attr_usage_id; int raw_size; u8 *raw_data; + u32 index; + u32 max_raw_size; }; /** @@ -183,6 +187,27 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, bool is_signed ); +/** + * sensor_hub_input_attr_read_values() - Synchronous multi-byte read request + * @hsdev: Hub device instance. + * @usage_id: Attribute usage id of parent physical device as per spec + * @attr_usage_id: Attribute usage id as per spec + * @report_id: Report id to look for + * @flag: Synchronous or asynchronous read + * @buffer_size: Size of the buffer in bytes + * @buffer: Buffer to store the read data + * + * Issues a synchronous or asynchronous read request for an input attribute, + * accumulating data into the provided buffer until it is full. + * Return: 0 on success, -ETIMEDOUT if the device did not respond, or a + * negative error code. + */ +int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev, + u32 usage_id, u32 attr_usage_id, + u32 report_id, + enum sensor_hub_read_flags flag, + u32 buffer_size, u8 *buffer); + /** * sensor_hub_set_feature() - Feature set request * @hsdev: Hub device instance. From f3461b84a4865d9b5e70fbb71da72ae044a3bcd2 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 5 May 2026 11:12:59 +0000 Subject: [PATCH 0381/3297] hfs/hfsplus: zero-initialize buffer in hfs_bnode_read commit d67aadee19ffdf3cc8520c5a4f4d5b2916d30baf upstream. hfs_bnode_read() can return early without writing to the output buffer when is_bnode_offset_valid() fails or when check_and_correct_requested_ length() corrects the length to zero. Callers such as hfs_bnode_read_ u16() and hfs_bnode_read_u8() pass stack-allocated buffers and use the result unconditionally, leading to KMSAN uninit-value reports. Rather than initializing at each individual call site, zero the buffer at the start of hfs_bnode_read() before any validation checks. This ensures all callers in both hfs and hfsplus get a deterministic zero value regardless of which early-return path is taken. Reported-by: syzbot+217eb327242d08197efb@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=217eb327242d08197efb Tested-by: syzbot+217eb327242d08197efb@syzkaller.appspotmail.com Fixes: a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Link: https://lore.kernel.org/r/20260505111300.3592757-3-tristmd@gmail.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Greg Kroah-Hartman --- fs/hfs/bnode.c | 2 ++ fs/hfsplus/bnode.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index fcfffe75d84e7..c5ebff5f68875 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -64,6 +64,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) int bytes_read; int bytes_to_read; + memset(buf, 0, len); + if (!is_bnode_offset_valid(node, off)) return; diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index 8e60e04c427bd..76dea3bc5fffe 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -25,6 +25,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) struct page **pagep; int l; + memset(buf, 0, len); + if (!is_bnode_offset_valid(node, off)) return; From 223463c488b0554212a94de971ea538eb2805fc7 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sun, 3 May 2026 13:33:29 +0900 Subject: [PATCH 0382/3297] nilfs2: reject CLEAN_SEGMENTS ioctl with out-of-range segment numbers commit 0e7a690fe435f8d5ea3feb7c1d8d73ba7e8b8aa9 upstream. Syzbot reported a hung task in nilfs_transaction_begin() where multiple tasks performing chmod() on a nilfs2 mount blocked for over 143 seconds waiting to acquire ns_segctor_sem for read: INFO: task syz.0.17:5918 blocked for more than 143 seconds. Call Trace: schedule+0x164/0x360 rwsem_down_read_slowpath+0x6d9/0x940 down_read+0x99/0x2e0 nilfs_transaction_begin+0x364/0x710 fs/nilfs2/segment.c:221 nilfs_setattr+0x124/0x2c0 fs/nilfs2/inode.c:921 notify_change+0xc1a/0xf40 chmod_common+0x273/0x4a0 do_fchmodat+0x12d/0x230 The writer holding ns_segctor_sem was a concurrent NILFS_IOCTL_CLEAN_SEGMENTS caller, stuck inside printk while emitting per-element warnings from nilfs_sufile_updatev(): __nilfs_msg+0x373/0x450 fs/nilfs2/super.c:78 nilfs_sufile_updatev+0x21c/0x6d0 fs/nilfs2/sufile.c:186 nilfs_sufile_freev fs/nilfs2/sufile.h:93 [inline] nilfs_free_segments fs/nilfs2/segment.c:1140 [inline] nilfs_segctor_collect_blocks fs/nilfs2/segment.c:1261 [inline] nilfs_segctor_do_construct+0x1f55/0x76c0 nilfs_clean_segments+0x3bd/0xa50 nilfs_ioctl_clean_segments fs/nilfs2/ioctl.c:922 [inline] nilfs_ioctl+0x261f/0x2780 The root cause is that user-supplied segment numbers are not validated before nilfs_clean_segments() begins doing work; the range check on each segnum is performed deep inside the call chain by nilfs_sufile_updatev(), which emits a nilfs_warn() per invalid entry while still holding the segctor lock and the sufile mi_sem. Under load (repeated invocations across multiple mounts saturating the global printk path), the cumulative printk latency keeps ns_segctor_sem held long enough to trip the hung_task watchdog, blocking concurrent operations such as chmod() that need ns_segctor_sem for read. Fix by validating the contents of kbufs[4] in nilfs_clean_segments() immediately after acquiring ns_segctor_sem via nilfs_transaction_lock(). Holding ns_segctor_sem serializes the check against nilfs_ioctl_resize(), which can modify ns_nsegments, so the validation uses a consistent value. Out-of-range segment numbers are rejected with -EINVAL before any segment-cleaning work begins, so the bad entries never reach the per-element diagnostic path inside nilfs_sufile_updatev(). Reported-by: syzbot+62f0f99d2f2bb8e3bbd7@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=62f0f99d2f2bb8e3bbd7 Tested-by: syzbot+62f0f99d2f2bb8e3bbd7@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Deepanshu Kartikey Fixes: 071cb4b81987 ("nilfs2: eliminate removal list of segments") Signed-off-by: Ryusuke Konishi Signed-off-by: Viacheslav Dubeyko Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/segment.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index deee16bc9d4eb..0bc1f0f02f31d 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2512,12 +2512,33 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, struct nilfs_sc_info *sci = nilfs->ns_writer; struct nilfs_transaction_info ti; int err; + size_t i, nfreesegs = argv[4].v_nmembs; + __u64 *segnumv = kbufs[4]; if (unlikely(!sci)) return -EROFS; nilfs_transaction_lock(sb, &ti, 1); + /* + * Validate segment numbers under ns_segctor_sem (held for write + * by nilfs_transaction_lock above) so the check is serialized + * against nilfs_ioctl_resize(), which can modify ns_nsegments. + * Rejecting bad input here, before any segment-cleaning work + * begins, avoids the per-element diagnostic path inside + * nilfs_sufile_updatev() that would otherwise run under this + * same lock and stall concurrent readers. + */ + for (i = 0; i < nfreesegs; i++) { + if (segnumv[i] >= nilfs->ns_nsegments) { + nilfs_err(sb, + "Segment number %llu to be freed is out of range", + (unsigned long long)segnumv[i]); + err = -EINVAL; + goto bail_unlock; + } + } + err = nilfs_mdt_save_to_shadow_map(nilfs->ns_dat); if (unlikely(err)) goto out_unlock; @@ -2558,6 +2579,7 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, sci->sc_freesegs = NULL; sci->sc_nfreesegs = 0; nilfs_mdt_clear_shadow_map(nilfs->ns_dat); + bail_unlock: nilfs_transaction_unlock(sb); return err; } From 973408ceab14555a8548b97c8cc7b54208c3f251 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 1 Apr 2026 11:44:15 +0200 Subject: [PATCH 0383/3297] media: mtk-jpeg: cancel workqueue on release for supported platforms only commit b1845a227fda37b2fe5327df3ca0015d7e290235 upstream. Since a recent fix the mtk_jpeg_release function cancels any pending or running work present in the driver workqueue using cancel_work_sync function. Currently, only the multicore based variants use this workqueue and they have the jpeg_worker platform data field initialized with a workqueue callback function. For the others, this field value remain NULL by default. The cancel_work_sync function is unconditionally called in mtk_jpeg_release function, even for the variants that do not use the workqueue. This call generates a WARN_ON print in __flush_work because the workqueue callback function presence check fails in __flush_work function (used by cancel_work_sync). So, to avoid these warnings, call cancel_work_sync only if a workqueue callback is defined in platform data. Fixes: 34c519feef3e ("media: mtk-jpeg: fix use-after-free in release path due to uncancelled work") Cc: stable@vger.kernel.org Signed-off-by: Louis-Alexis Eyraud Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index a2518793c685a..5a9ba04d3d480 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1209,7 +1209,8 @@ static int mtk_jpeg_release(struct file *file) struct mtk_jpeg_dev *jpeg = video_drvdata(file); struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file); - cancel_work_sync(&ctx->jpeg_work); + if (jpeg->variant->jpeg_worker) + cancel_work_sync(&ctx->jpeg_work); mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); From 1cd54e217c6e2cdb794a897b2f855e13ffcee586 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 26 Jun 2026 11:49:37 +0200 Subject: [PATCH 0384/3297] serial: 8250_mid: Disable DMA for selected platforms commit b1b4efea05a56c0995e4702a86d6624b4fdff32f upstream. In accordance with Errata (specification updates) HSUART May Stop Functioning when DMA is Active. - Denverton document #572409, rev 3.4, DNV60 - Ice Lake Xeon D document #714070, ICXD65 - Snowridge document #731931, SNR44 For a quick fix just disable the respective callbacks during the device probe. Depending on the future development we might remove them completely. Reported-by: micas-opensource Closes: https://lore.kernel.org/linux-serial/20250625031409.2404219-1-opensource@ruijie.com.cn/ Fixes: 6ede6dcd87aa ("serial: 8250_mid: add support for DMA engine handling from UART MMIO") Cc: stable Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260626094937.561776-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 8ec03863606e4..f88809ff370b7 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -368,8 +369,16 @@ static const struct mid8250_board dnv_board = { .freq = 133333333, .base_baud = 115200, .bar = 1, - .setup = dnv_setup, - .exit = dnv_exit, + /* + * Errata: + * HSUART May Stop Functioning when DMA is Active. + * + * - Denverton document #572409, rev 3.4, DNV60 + * - Ice Lake Xeon D document #714070, ICXD65 + * - Snowridge document #731931, SNR44 + */ + .setup = PTR_IF(false, dnv_setup), + .exit = PTR_IF(false, dnv_exit), }; static const struct pci_device_id pci_ids[] = { From 200794d0354cdcad9464c611b96e2e5b1fef09d8 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 12:06:07 +0200 Subject: [PATCH 0385/3297] xfs: use null daddr for unset first bad log block commit cc9af5e461ea5f6e37738f3f1e41c45a9b7f45d6 upstream. xlog_do_recovery_pass() may return before setting first_bad. The caller must distinguish that case from an error at a valid log block, including block zero after the log wraps. Initialize first_bad to XFS_BUF_DADDR_NULL and test it explicitly before treating the error as a torn write. Fixes: 7088c4136fa1 ("xfs: detect and trim torn writes during log recovery") Suggested-by: Darrick J. Wong Reported-by: syzbot+b7dfbed0c6c2b5e9fd34@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b7dfbed0c6c2b5e9fd34 Cc: stable@vger.kernel.org # v4.5 Signed-off-by: Yousef Alhouseen Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_log_recover.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 549d60959aee5..cc5ffa8e4bc2a 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1028,7 +1028,7 @@ xlog_verify_head( { struct xlog_rec_header *tmp_rhead; char *tmp_buffer; - xfs_daddr_t first_bad; + xfs_daddr_t first_bad = XFS_BUF_DADDR_NULL; xfs_daddr_t tmp_rhead_blk; int found; int error; @@ -1057,7 +1057,8 @@ xlog_verify_head( */ error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk, XLOG_RECOVER_CRCPASS, &first_bad); - if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) { + if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && + first_bad != XFS_BUF_DADDR_NULL) { /* * We've hit a potential torn write. Reset the error and warn * about it. @@ -3582,4 +3583,3 @@ xlog_recover_cancel( if (xlog_recovery_needed(log)) xlog_recover_cancel_intents(log); } - From 936618643591c6e6f6a3e4f9dbbc9e44ac1d6dea Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 25 Jun 2026 21:16:23 +0800 Subject: [PATCH 0386/3297] xfs: release dquot buffer after dqflush failure commit 0c1b3a823a22af623d55f225fe2ac7e8b9052821 upstream. xfs_qm_dqpurge() gets a locked buffer from xfs_dquot_use_attached_buf(). If xfs_qm_dqflush() fails, the error path skips xfs_buf_relse() and then calls xfs_dquot_detach_buf(), which tries to lock the same buffer again. Release the buffer after xfs_qm_dqflush() returns so the error path drops the caller hold and unlocks the buffer before the dquot is detached, matching the other dqflush callers. Fixes: a40fe30868ba ("xfs: separate dquot buffer reads from xfs_dqflush") Cc: stable@vger.kernel.org # v6.13+ Signed-off-by: Yingjie Gao Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_qm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 18a19947bbdba..48a5a294f0a13 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -163,10 +163,9 @@ xfs_qm_dqpurge( * does it on success. */ error = xfs_qm_dqflush(dqp, bp); - if (!error) { + if (!error) error = xfs_bwrite(bp); - xfs_buf_relse(bp); - } + xfs_buf_relse(bp); xfs_dqflock(dqp); } xfs_dquot_detach_buf(dqp); From dd8d0665cdabfc07543661fe6197c87ea5f1bdb1 Mon Sep 17 00:00:00 2001 From: Alexey Nepomnyashih Date: Wed, 3 Jun 2026 20:41:47 +0000 Subject: [PATCH 0387/3297] xfs: fix unreachable BIGTIME check in dquot flush validation commit 03866d130ed33ab68cc7faaf4bf2c4abef96d42e upstream. The dqp->q_id == 0 check inside the XFS_DQTYPE_BIGTIME block is unreachable because root dquots return successfully earlier. Reject root dquots with XFS_DQTYPE_BIGTIME before that early return, preserving the intended validation and removing the unreachable condition. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 4ea1ff3b4968 ("xfs: widen ondisk quota expiration timestamps to handle y2038+") Cc: stable@vger.kernel.org # v5.10+ Signed-off-by: Alexey Nepomnyashih Reviewed-by: "Darrick J. Wong" Reviewed-by: Allison Henderson Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_dquot.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 92a8863bee36c..896ff8080b75b 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -1241,6 +1241,14 @@ xfs_qm_dqflush_check( type != XFS_DQTYPE_PROJ) return __this_address; + /* bigtime flag should never be set on root dquots */ + if (dqp->q_type & XFS_DQTYPE_BIGTIME) { + if (!xfs_has_bigtime(dqp->q_mount)) + return __this_address; + if (dqp->q_id == 0) + return __this_address; + } + if (dqp->q_id == 0) return NULL; @@ -1256,14 +1264,6 @@ xfs_qm_dqflush_check( !dqp->q_rtb.timer) return __this_address; - /* bigtime flag should never be set on root dquots */ - if (dqp->q_type & XFS_DQTYPE_BIGTIME) { - if (!xfs_has_bigtime(dqp->q_mount)) - return __this_address; - if (dqp->q_id == 0) - return __this_address; - } - return NULL; } From 55e4d8413fb5442c11fbbf484f1bfad9db3499e5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 9 Jun 2026 21:57:24 -0700 Subject: [PATCH 0388/3297] xfs: fix pointer arithmetic error on 32-bit systems commit 84eec3f7fc73144d1a230c9e8ad92721e37dcaab upstream. The translation of the old XFS_BMBT_KEY_ADDR macro into a static function is not correct on 32-bit systems because the sizeof() argument went from being a xfs_bmbt_key_t (i.e. a struct) to a (struct xfs_bmbt_key *) (i.e. a pointer to the same struct). On 64-bit systems this turns out ok because they are the same size, but on 32-bit systems this is catastrophic because they are not the same size. So far there have been no complaints, most likely because the xfs developers urge against running it on 32-bit systems. But this needs fixing asap. Cc: stable@vger.kernel.org # v6.12 Fixes: 79124b37400635 ("xfs: replace shouty XFS_BM{BT,DR} macros") Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_bmap_btree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h index b238d559ab036..e0c870beaf670 100644 --- a/fs/xfs/libxfs/xfs_bmap_btree.h +++ b/fs/xfs/libxfs/xfs_bmap_btree.h @@ -89,7 +89,7 @@ xfs_bmbt_key_addr( { return (struct xfs_bmbt_key *) ((char *)block + xfs_bmbt_block_len(mp) + - (index - 1) * sizeof(struct xfs_bmbt_key *)); + (index - 1) * sizeof(struct xfs_bmbt_key)); } static inline xfs_bmbt_ptr_t * From a62ef2d13d6e7270dd5e88c6082bf2d0edcd5112 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 4 Jun 2026 20:03:17 +0800 Subject: [PATCH 0389/3297] xfs: fix exchmaps reservation limit check commit 0a5213bbff62b51c7d4999ac8c7e11ea57d00d45 upstream. xfs_exchmaps_estimate_overhead() adds the bmbt and rmapbt overhead to a local resblks variable, but the final UINT_MAX check still tests req->resblks. That is the reservation value from before the overhead was added. The computed value is stored back in req->resblks and later passed to xfs_trans_alloc(), whose block reservation argument is unsigned int. Check the computed reservation so the existing limit applies to the value that will be used. Fixes: 966ceafc7a43 ("xfs: create deferred log items for file mapping exchanges") Cc: stable@vger.kernel.org # v6.10 Signed-off-by: Yingjie Gao Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_exchmaps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c index 932ee4619e9ea..f021fe3c9cb64 100644 --- a/fs/xfs/libxfs/xfs_exchmaps.c +++ b/fs/xfs/libxfs/xfs_exchmaps.c @@ -711,7 +711,7 @@ xfs_exchmaps_estimate_overhead( return -ENOSPC; /* Can't actually reserve more than UINT_MAX blocks. */ - if (req->resblks > UINT_MAX) + if (resblks > UINT_MAX) return -ENOSPC; req->resblks = resblks; From c3d3d2212c2966973dd7d603c6c6e6ed6fc7fbe1 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sat, 27 Jun 2026 14:04:02 +0800 Subject: [PATCH 0390/3297] xfs: fix memory leak in xfs_dqinode_metadir_create() commit 45de375b25060edf46e20abb36521ba530336ceb upstream. If xfs_metadir_create() fails in xfs_dqinode_metadir_create(), the current code returns directly, leaking the allocated update and transaction state. If the subsequent commit fails, the caller-owned inode reference is left behind. Fix this memory leak by routing the create failure path through xfs_metadir_cancel(). For both create and commit failures, finish and release any inode returned to the caller, mirroring the unwind pattern in xfs_metadir_mkdir(). 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.1. An x86_64 allyesconfig build showed no new warnings. Runtime validation used kprobe fault injection during `mount -o uquota` on a metadir XFS image. Injecting xfs_metadir_create() reproduced the old active-update path that left mount stuck later in mount setup; after this change, the same injection reported cancel_hits=1 and irele_hits=1. Injecting xfs_metadir_commit() exercised the old inode-reference leak path; after this change, it reported irele_hits=1. Fixes: e80fbe1ad8ef ("xfs: use metadir for quota inodes") Cc: stable@vger.kernel.org # v6.13 Signed-off-by: Dawei Feng Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_dquot_buf.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index dceef2abd4e2a..785f422dfedbb 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -436,17 +436,27 @@ xfs_dqinode_metadir_create( error = xfs_metadir_create(&upd, S_IFREG); if (error) - return error; + goto out_cancel; xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE); error = xfs_metadir_commit(&upd); if (error) - return error; + goto out_irele; xfs_finish_inode_setup(upd.ip); *ipp = upd.ip; return 0; + +out_cancel: + xfs_metadir_cancel(&upd, error); +out_irele: + /* Have to finish setting up the inode to ensure it's deleted. */ + if (upd.ip) { + xfs_finish_inode_setup(upd.ip); + xfs_irele(upd.ip); + } + return error; } #ifndef __KERNEL__ From a9bb2d9c798cb62a4050a991c27b752770c33afe Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Tue, 2 Jun 2026 16:43:33 +0800 Subject: [PATCH 0391/3297] bpf: Reject fragmented frames in devmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit aa496720618f1a6054f1c870bf10b4f6c99bf656 upstream. Devmap broadcast redirects clone the packet for all but the last destination. For native XDP, that clone path copies only the linear xdp_frame data, while fragmented frames keep skb_shared_info in tailroom outside the linear area. Cloning such a frame leaves XDP_FLAGS_HAS_FRAGS set but without valid frag metadata, and the later free path can interpret uninitialized tail data as skb_shared_info, leading to an out-of-bounds access during frame return. Reject fragmented native XDP frames in dev_map_enqueue_clone(). Add the same restriction to the generic XDP clone path in dev_map_redirect_clone(). Generic XDP represents fragmented packets as nonlinear skbs, and rejecting them here keeps clone-based broadcast support aligned between native and generic XDP. Fixes: e624d4ed4aa8 ("xdp: Extend xdp_redirect_map with broadcast support") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Reviewed-by: Emil Tsalapatis Reviewed-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/21c2d153dd25603d359069a02bf06779b51f6423.1780385378.git.zzhan461@ucr.edu Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/devmap.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index cc0a43ebab6b9..5b9eac5342a90 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -581,6 +581,10 @@ static int dev_map_enqueue_clone(struct bpf_dtab_netdev *obj, { struct xdp_frame *nxdpf; + /* Frags live outside the linear frame and cannot be cloned safely. */ + if (unlikely(xdp_frame_has_frags(xdpf))) + return -EOPNOTSUPP; + nxdpf = xdpf_clone(xdpf); if (!nxdpf) return -ENOMEM; @@ -726,6 +730,9 @@ static int dev_map_redirect_clone(struct bpf_dtab_netdev *dst, struct sk_buff *nskb; int err; + if (unlikely(skb_is_nonlinear(skb))) + return -EOPNOTSUPP; + nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) return -ENOMEM; From d94ab0e91d3ff7577cd0306a47270a6dcb82f76c Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 3 Jun 2026 18:53:17 +0800 Subject: [PATCH 0392/3297] bpf: Restore sysctl new-value from 1 to 0 commit 2566c3b24219c5b30e35205cba029ff34ff7c78b upstream. Commit 4e63acdff864 ("bpf: Introduce bpf_sysctl_{get,set}_new_value helpers") changed the success return value to 0, but failed to update the corresponding check in __cgroup_bpf_run_filter_sysctl(). Since bpf_prog_run_array_cg() now returns 0 on success, the legacy ret == 1 condition is never satisfied. As a result, the modified value is ignored, and bpf_sysctl_set_new_value() fails to replace the write buffer. Fix this by checking for a return value of 0 instead, so cgroup/sysctl programs can correctly replace the pending sysctl buffer. This bug was discovered during a manual code review. Tested via a cgroup/sysctl BPF reproducer overriding writes to a target sysctl. Pre-fix, bpf_sysctl_set_new_value("foo") was silently ignored: the write returned 8192 and the value remained "600". Post-fix, the BPF replacement buffer properly propagates: the write returns 3 and the value updates to "foo". Fixes: f10d05966196 ("bpf: Make BPF_PROG_RUN_ARRAY return -err instead of allow boolean") Cc: stable@vger.kernel.org Acked-by: Yonghong Song Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Reviewed-by: Jiayuan Chen Acked-by: Xu Kuohai Link: https://lore.kernel.org/r/20260603105317.944304-4-dawei.feng@seu.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 159e29e809362..d5eed35b376ab 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1939,7 +1939,7 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, kfree(ctx.cur_val); - if (ret == 1 && ctx.new_updated) { + if (!ret && ctx.new_updated) { kvfree(*buf); *buf = ctx.new_val; *pcount = ctx.new_len; From cd407de2ef5dc70f1970b343ffaa16186340fdfd Mon Sep 17 00:00:00 2001 From: Paul Moses Date: Fri, 5 Jun 2026 23:43:09 +0000 Subject: [PATCH 0393/3297] bpf: Validate BTF repeated field counts before expansion commit b9452b594fd3aecbfd4aa0a6a1f741330a37dab7 upstream. btf_parse_struct_metas() walks user-supplied BTF during BPF_BTF_LOAD, and btf_repeat_fields() expands repeatable fields from array elements into the fixed BTF_FIELDS_MAX scratch array used by btf_parse_fields(). The remaining-capacity check performs the expanded field count calculation in u32. A malformed BTF can wrap that calculation, causing the check to pass even when the expanded field count exceeds the scratch array capacity. The following memcpy() can then write past the end of the array. Use checked addition and multiplication before copying repeated fields and reject impossible counts. Fixes: 797d73ee232d ("bpf: Check the remaining info_cnt before repeating btf fields") Cc: stable@vger.kernel.org Signed-off-by: Paul Moses Acked-by: Eduard Zingerman Link: https://lore.kernel.org/bpf/20260605234301.1109063-1-p@1g4.org Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/btf.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 75a5df36f9170..7a4618c054075 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3550,7 +3550,7 @@ static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt, u32 field_cnt, u32 repeat_cnt, u32 elem_size) { - u32 i, j; + u32 i, j, total_cnt, total_repeats; u32 cur; /* Ensure not repeating fields that should not be repeated. */ @@ -3568,10 +3568,9 @@ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt, } } - /* The type of struct size or variable size is u32, - * so the multiplication will not overflow. - */ - if (field_cnt * (repeat_cnt + 1) > info_cnt) + if (check_add_overflow(repeat_cnt, 1, &total_repeats) || + check_mul_overflow(field_cnt, total_repeats, &total_cnt) || + total_cnt > (u32)info_cnt) return -E2BIG; cur = field_cnt; From e24eb271061db384a3c3ef6f107fe515e68ef222 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 22 Jun 2026 16:01:57 +0800 Subject: [PATCH 0394/3297] net: usb: kalmia: bound RX frame length in kalmia_rx_fixup() commit 47b6bcef6e679593d2e86e04ee72c46a4e2f7139 upstream. kalmia_rx_fixup() computes usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH) as a u16, guarded only by a pre-loop check that skb->len is at least KALMIA_HEADER_LENGTH, which is 6. A device can deliver a short bulk-IN frame with skb->len in the 6 to 11 range, or leave a short trailing remainder on a later loop iteration. Either case underflows usb_packet_length to about 65530. That bypasses the usb_packet_length < ether_packet_length truncation path. The device-supplied ether_packet_length, a le16 up to 65535 read from header_start[2], then drives a memcmp() and the following skb_trim() and skb_pull() past the end of the rx buffer. The rx buffer is hard_mtu * 10, which is 14000 bytes. That is an out of bounds read. Require both the start and end framing headers to be present before subtracting them, on every loop iteration. Fixes: d40261236e8e ("net/usb: Add Samsung Kalmia driver for Samsung GT-B3730") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/178211531778.2216480.12637613349790980750@maoyixie.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/kalmia.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c index ee9c48f7f68f9..0dd0a30c3db41 100644 --- a/drivers/net/usb/kalmia.c +++ b/drivers/net/usb/kalmia.c @@ -276,6 +276,14 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) "Received header: %6phC. Package length: %i\n", header_start, skb->len - KALMIA_HEADER_LENGTH); + /* both framing headers must be present before we subtract + * them, otherwise usb_packet_length underflows and the + * device-supplied ether_packet_length drives an out of bounds + * access below + */ + if (skb->len < 2 * KALMIA_HEADER_LENGTH) + return 0; + /* subtract start header and end header */ usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); ether_packet_length = get_unaligned_le16(&header_start[2]); From e22f044b0b20df5304e017912483d065047a12df Mon Sep 17 00:00:00 2001 From: Jared Baldridge Date: Sat, 30 May 2026 18:19:48 -0400 Subject: [PATCH 0395/3297] usb: cdc_acm: Add quirk for Uniden BC125AT scanner commit 6eba58568f6cc3ff8515a00b05e258d8cfb72b72 upstream. Uniden BC125AT radio scanner has a USB interface which fails to work with the cdc_acm driver: usb 1-1: new full-speed USB device number 2 using uhci_hcd cdc_acm 1-1:1.0: Zero length descriptor references cdc_acm 1-1:1.0: probe with driver cdc_acm failed with error -22 usbcore: registered new interface driver cdc_acm Adding the NO_UNION_NORMAL quirk for the device fixes the issue: usb 1-1: new full-speed USB device number 2 using uhci_hcd cdc_acm 1-1:1.0: ttyACM0: USB ACM device usbcore: registered new interface driver cdc_acm `lsusb -v` of the device: Bus 001 Device 002: ID 1965:0017 Uniden Corporation BC125AT Negotiated speed: Full Speed (12Mbps) Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 2 Communications bDeviceSubClass 0 [unknown] bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x1965 Uniden Corporation idProduct 0x0017 BC125AT bcdDevice 0.01 iManufacturer 1 Uniden America Corp. iProduct 2 BC125AT iSerial 3 0001 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0030 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x87 EP 7 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 10 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 [unknown] bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Device Status: 0x0000 (Bus Powered) Signed-off-by: Jared Baldridge Cc: stable Link: https://patch.msgid.link/20260530221959.612526-1-jrb@expunge.us Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 730ea34cb7449..12f9fe62199ba 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1810,6 +1810,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */ .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */ }, + { USB_DEVICE(0x1965, 0x0017), /* Uniden BC125AT */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, From c00826e87bb75e14e0381b05da5f18ffd0241ab6 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 13:26:27 +0800 Subject: [PATCH 0396/3297] usb: cdnsp: fix stream context array leak in cdnsp_alloc_stream_info() commit 3348f444a4ce43dd5c2d1aa41634cb6eff33aa64 upstream. cdnsp_alloc_stream_info() allocates stream_info->stream_ctx_array with cdnsp_alloc_stream_ctx(). If a later stream ring allocation or stream mapping update fails, the error path frees the allocated stream rings and stream_rings array, but leaves stream_ctx_array allocated. Free the stream context array before falling through to the stream_rings cleanup path. Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") Cc: stable Signed-off-by: Haoxiang Li Acked-by: Peter Chen Link: https://patch.msgid.link/20260622052627.696373-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdnsp-mem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index 97866bfb2da9d..3f35c8af271e0 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -632,6 +632,8 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, } } + cdnsp_free_stream_ctx(pdev, pep); + cleanup_stream_rings: kfree(pep->stream_info.stream_rings); From 4b0779207e36d924bcfe5e34e8b817aecdea87a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Lugathe=20da=20Concei=C3=A7=C3=A3o=20Alves?= Date: Wed, 3 Jun 2026 08:36:26 -0300 Subject: [PATCH 0397/3297] USB: core: add USB_QUIRK_NO_LPM for VIA Labs USB 2.0 hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bd728c3d9b1cc0bb0fda6a7055c5c8b55d7477b2 upstream. The VIA Labs, Inc. USB 2.0 hub controller (2109:2817), found in a KVM switch, fails to enumerate high-power devices during cold boot and system restart. Applying the kernel parameter usbcore.quirks=2109:2817:k resolves the issue. Enumeration failure log: usb 1-1.2.3: device descriptor read/64, error -32 usb 1-1.2.3: Device not responding to setup address. usb 1-1.2.3: device not accepting address 11, error -71 usb 1-1.2-port3: unable to enumerate USB device Add USB_QUIRK_NO_LPM for this device. Signed-off-by: Rodrigo Lugathe da Conceição Alves Cc: stable Link: https://patch.msgid.link/20260603113626.395612-1-lugathe2@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 21f9b7fec8cb5..c799f8f7b1fb0 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -577,6 +577,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* VLI disk */ { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM }, + /* VIA Labs, Inc. USB2.0 Hub */ + { USB_DEVICE(0x2109, 0x2817), .driver_info = USB_QUIRK_NO_LPM }, + /* Raydium Touchscreen */ { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM }, From 0bbab8882a319f74dbcd40fd30b8e072656eb360 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 21:11:21 +0800 Subject: [PATCH 0398/3297] usb: dwc3: meson-g12a: fix refcount leak in dwc3_meson_g12a_resume() commit 692c354bef03b77b30e57e61934da502c8a12d45 upstream. If dwc3_meson_g12a_resume() succeeds in calling reset_control_reset(), an internal triggered_count reference is acquired. If any later step fails (usb_init, phy_init, phy_power_on, regulator_enable, or usb_post_init), the function returns the error without rearming the reset control. This leaks the reference and leaves the reset control in a triggered state, causing future reset_control_reset() calls to incorrectly return early as if already reset. Add an error path that calls reset_control_rearm() to balance the reference before returning the error. Cc: stable Fixes: 5b0ba0caaf3a ("usb: dwc3: meson-g12a: refactor usb init") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260611131121.81784-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 55e144ba8cfc6..4d611c08e8a4c 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -907,35 +907,39 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) ret = priv->drvdata->usb_init(priv); if (ret) - return ret; + goto err_rearm; /* Init PHYs */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_init(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } /* Set PHY Power */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_power_on(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { ret = regulator_enable(priv->vbus); if (ret) - return ret; + goto err_rearm; } if (priv->drvdata->usb_post_init) { ret = priv->drvdata->usb_post_init(priv); if (ret) - return ret; + goto err_rearm; } return 0; + +err_rearm: + reset_control_rearm(priv->reset); + return ret; } static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { From 6bc17a78a05671d303820224fb37ca339c1dc2cb Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:14:19 +0800 Subject: [PATCH 0399/3297] usb: free iso schedules on failed submit commit b9399d25fbb34a05bbe76eeedd730f62ff2670e9 upstream. EHCI and FOTG210 isochronous submits build an ehci_iso_sched before linking the URB to the endpoint queue, and keep the staged schedule in urb->hcpriv until iso_stream_schedule() and the link helpers consume it. If the controller is no longer accessible, or usb_hcd_link_urb_to_ep() fails, submit jumps to done_not_linked before that handoff happens and leaks the staged schedule still attached to urb->hcpriv. Free the staged schedule from done_not_linked when submit fails before the URB is linked and clear urb->hcpriv after the 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.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an EHCI host controller with a USB isochronous device to test with, no runtime testing was able to be performed. Fixes: 8de98402652c ("[PATCH] USB: Fix USB suspend/resume crasher (#2)") Fixes: e9df41c5c589 ("USB: make HCDs responsible for managing endpoint queues") Fixes: 7d50195f6c50 ("usb: host: Faraday fotg210-hcd driver") Cc: stable Signed-off-by: Dawei Feng Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260630071419.349161-1-dawei.feng@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/fotg210/fotg210-hcd.c | 6 ++++-- drivers/usb/host/ehci-sched.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 64c4965a160f4..340b45c94d75d 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -4267,8 +4267,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, return 0; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -4562,6 +4560,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, else usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&fotg210->lock, flags); done: return status; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 7e834587e7de1..e6742838f9a68 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1624,6 +1624,7 @@ iso_stream_schedule( status = 1; /* and give it back immediately */ iso_sched_free(stream, sched); sched = NULL; + urb->hcpriv = NULL; } } urb->error_count = skip / period; @@ -1654,8 +1655,6 @@ iso_stream_schedule( return status; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -1967,6 +1966,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; @@ -2344,6 +2347,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; From 01feaf024f29618d5ffa7ab0fd858e0579dcbf7b Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 27 May 2026 23:08:32 +0800 Subject: [PATCH 0400/3297] usb: gadget: composite: fix dead empty check in the USB_DT_OTG handler commit f8f680609c2b3ab795ffcd6f21585b6dfc46d395 upstream. The OTG branch of composite_setup() falls back to the first configuration when none is selected: if (cdev->config) config = cdev->config; else config = list_first_entry(&cdev->configs, struct usb_configuration, list); if (!config) goto done; ... memcpy(req->buf, config->descriptors[0], value); list_first_entry() never returns NULL. On an empty list it returns container_of() of the list head. So the "if (!config)" check is dead. When cdev->configs is empty, config points at the head inside struct usb_composite_dev. config->descriptors[0] reads whatever sits at that offset. The memcpy copies up to w_length bytes of it into the response buffer. cdev->configs can be empty in two cases. One is a teardown race on gadget unbind with a control transfer in flight. The other is a driver that sets is_otg before it adds a config. A reproducer that holds cdev->configs empty triggers a KASAN fault in this branch. Use list_first_entry_or_null() so the existing check does its job. Fixes: 53e6242db8d6 ("usb: gadget: composite: add USB_DT_OTG request handling") Cc: stable Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/20260527150832.2943293-1-maoyixie.tju@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index bb3fa6fa9e8b9..e09256304dc0a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1863,9 +1863,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (cdev->config) config = cdev->config; else - config = list_first_entry( + config = list_first_entry_or_null( &cdev->configs, - struct usb_configuration, list); + struct usb_configuration, + list); if (!config) goto done; From b52476a83d9e12df00765359d728a875b128bef1 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Thu, 25 Jun 2026 15:37:04 +0800 Subject: [PATCH 0401/3297] usb: gadget: udc: Fix use-after-free in gadget_match_driver commit 67e511d2989eb1c8c588b599ce2fcc6bb8e6f7ea upstream. The udc structure acts as the management structure for the gadget, but their lifecycles are decoupled. A race condition exists where usb_del_gadget() frees the udc memory (e.g., via mode-switch work) while gadget_match_driver() concurrently accesses the freed udc memory (e.g., via configfs), causing a Use-After-Free (UAF) that triggers a NULL pointer dereference when the freed memory is zeroed: [39430.908615][ T1171] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 [39430.911397][ T1171] pc : __pi_strcmp+0x20/0x140 [39430.911441][ T1171] lr : gadget_match_driver+0x34/0x60 ... [39430.911890][ T1171] usb_gadget_register_driver_owner+0x50/0xf8 [39430.911910][ T1171] gadget_dev_desc_UDC_store+0xf4/0x140 [39430.931308][ T1171] configfs_write_iter+0xec/0x134 [39430.957058][ T1171] Workqueue: events_freezable __dwc3_set_mode [39430.957287][ T1171] dwc3_gadget_exit+0x34/0x8c [39430.957304][ T1171] __dwc3_set_mode+0xc0/0x664 Fix this by ensuring the udc structure remains allocated until the gadget is released. To achieve this, introduce a new usb_gadget_release() routine to the core. When the gadget is added, usb_add_gadget() stores the gadget's release routine in the udc structure and takes a reference to the udc. When the gadget is released, usb_gadget_release() drops the reference to the udc and then calls the gadget's release routine. Suggested-by: Alan Stern Cc: stable Signed-off-by: Jimmy Hu Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260625073705.803880-1-hhhuuu@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 8dbe79bdc0f9c..ab1bcc5512b2c 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type; /** * struct usb_udc - describes one usb device controller * @driver: the gadget driver pointer. For use by the class code - * @dev: the child device to the actual controller * @gadget: the gadget. For use by the class code + * @gadget_release: the gadget's release routine + * @dev: the child device to the actual controller * @list: for use by the udc class driver * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true @@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type; struct usb_udc { struct usb_gadget_driver *driver; struct usb_gadget *gadget; + void (*gadget_release)(struct device *dev); struct device dev; struct list_head list; bool vbus; @@ -1356,6 +1358,17 @@ static void usb_udc_nop_release(struct device *dev) dev_vdbg(dev, "%s\n", __func__); } +static void usb_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + /* Cache the gadget's release routine to prevent UAF */ + void (*release)(struct device *dev) = udc->gadget_release; + + put_device(&udc->dev); + release(dev); +} + /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's @@ -1412,6 +1425,14 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_init(&udc->connect_lock); udc->started = false; + /* + * Align decoupled lifecycles: take a UDC reference to ensure it + * remains allocated until the gadget is released, requiring an + * override of the gadget's release routine to drop it. + */ + udc->gadget_release = gadget->dev.release; + gadget->dev.release = usb_gadget_release; + get_device(&udc->dev); mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -1456,6 +1477,12 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); + /* + * Revert the override and drop the UDC reference to prevent + * leaking the UDC if the gadget was statically allocated. + */ + gadget->dev.release = udc->gadget_release; + put_device(&udc->dev); err_put_udc: put_device(&udc->dev); From 8a5eba992c862b0c94411eecf9b7121e8636db38 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 14:46:17 +0800 Subject: [PATCH 0402/3297] usb: gadget: f_printer: take kref only for successful open commit 30adce93d5c4a5a1ec29d9249e3fdfcc391d406b upstream. printer_open() returns -EBUSY when the character device is already open, but it increments dev->kref regardless of the return value. VFS does not call ->release() for a failed open, so every rejected second open permanently leaks one reference. Move kref_get() into the successful-open branch. Fixes: e8d5f92b8d30 ("usb: gadget: function: printer: fix use-after-free in __lock_acquire") Cc: stable Signed-off-by: Xu Rao Link: https://patch.msgid.link/80295742B820DA9B+20260626064617.4090626-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_printer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index d295ade8fa679..a3884afa42b4b 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd) ret = 0; /* Change the printer status to show that it's on-line. */ dev->printer_status |= PRINTER_SELECTED; + kref_get(&dev->kref); } spin_unlock_irqrestore(&dev->lock, flags); - kref_get(&dev->kref); - return ret; } From 54c2b7356b4aeea467f9fb13b85e9e036bc428cb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:10 +0200 Subject: [PATCH 0403/3297] USB: idmouse: fix use-after-free on disconnect race commit ff002c153f9722caece3983cc23dc4d9d4652cb4 upstream. mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 54d2bc068fd2 ("USB: fix locking in idmouse") Cc: stable@vger.kernel.org # 2.6.24 Cc: Oliver Neukum Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 45 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index ea39243efee39..0ce12717c14d7 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -63,6 +63,7 @@ MODULE_DEVICE_TABLE(usb, idmouse_table); /* structure to hold all of our device specific stuff */ struct usb_idmouse { + struct kref kref; struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ @@ -209,8 +210,10 @@ static int idmouse_resume(struct usb_interface *intf) return 0; } -static inline void idmouse_delete(struct usb_idmouse *dev) +static inline void idmouse_delete(struct kref *kref) { + struct usb_idmouse *dev = container_of(kref, struct usb_idmouse, kref); + kfree(dev->bulk_in_buffer); kfree(dev); } @@ -254,6 +257,8 @@ static int idmouse_open(struct inode *inode, struct file *file) /* increment our usage count for the driver */ ++dev->open; + kref_get(&dev->kref); + /* save our object in the file's private structure */ file->private_data = dev; @@ -277,16 +282,11 @@ static int idmouse_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->lock); - --dev->open; + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); - if (!dev->present) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - mutex_unlock(&dev->lock); - } return 0; } @@ -334,6 +334,7 @@ static int idmouse_probe(struct usb_interface *interface, if (dev == NULL) return -ENOMEM; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = udev; dev->interface = interface; @@ -342,8 +343,7 @@ static int idmouse_probe(struct usb_interface *interface, result = usb_find_bulk_in_endpoint(iface_desc, &endpoint); if (result) { dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } dev->orig_bi_size = usb_endpoint_maxp(endpoint); @@ -351,8 +351,8 @@ static int idmouse_probe(struct usb_interface *interface, dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { - idmouse_delete(dev); - return -ENOMEM; + result = -ENOMEM; + goto err_put_kref; } /* allow device read, write and ioctl */ @@ -364,14 +364,18 @@ static int idmouse_probe(struct usb_interface *interface, if (result) { /* something prevented us from registering this device */ dev_err(&interface->dev, "Unable to allocate minor number.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } /* be noisy */ dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC); return 0; + +err_put_kref: + kref_put(&dev->kref, idmouse_delete); + + return result; } static void idmouse_disconnect(struct usb_interface *interface) @@ -387,14 +391,9 @@ static void idmouse_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; - /* if the device is opened, idmouse_release will clean this up */ - if (!dev->open) { - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - /* unlock */ - mutex_unlock(&dev->lock); - } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); dev_info(&interface->dev, "disconnected\n"); } From 2107a4fc8ff1cf1d52f416c1e5cc8e97413a5915 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:11 +0200 Subject: [PATCH 0404/3297] USB: ldusb: fix use-after-free on disconnect race commit 19bdfc7b3c179331eafa423d87e1336f43bbfeb8 upstream. mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: ce0d7d3f575f ("usb: ldusb: ld_usb semaphore to mutex") Cc: stable Cc: Daniel Walker Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index f392d6f84df9c..7903ef81b8f95 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -150,6 +150,7 @@ MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in /* Structure to hold all of our device specific stuff */ struct ld_usb { + struct kref kref; struct mutex mutex; /* locks this structure */ struct usb_interface *intf; /* save off the usb interface pointer */ unsigned long disconnected:1; @@ -201,8 +202,10 @@ static void ld_usb_abort_transfers(struct ld_usb *dev) /* * ld_usb_delete */ -static void ld_usb_delete(struct ld_usb *dev) +static void ld_usb_delete(struct kref *kref) { + struct ld_usb *dev = container_of(kref, struct ld_usb, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -355,6 +358,8 @@ static int ld_usb_open(struct inode *inode, struct file *file) goto unlock_exit; } + kref_get(&dev->kref); + /* save device in the file's private structure */ file->private_data = dev; @@ -381,17 +386,8 @@ static int ld_usb_release(struct inode *inode, struct file *file) mutex_lock(&dev->mutex); - if (dev->open_count != 1) { - retval = -ENODEV; + if (dev->disconnected) goto unlock_exit; - } - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->mutex); - /* unlock here as ld_usb_delete frees dev */ - ld_usb_delete(dev); - goto exit; - } /* wait until write transfer is finished */ if (dev->interrupt_out_busy) @@ -401,7 +397,7 @@ static int ld_usb_release(struct inode *inode, struct file *file) unlock_exit: mutex_unlock(&dev->mutex); - + kref_put(&dev->kref, ld_usb_delete); exit: return retval; } @@ -659,6 +655,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) goto exit; + + kref_init(&dev->kref); mutex_init(&dev->mutex); spin_lock_init(&dev->rbsl); dev->intf = intf; @@ -740,7 +738,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * return retval; error: - ld_usb_delete(dev); + kref_put(&dev->kref, ld_usb_delete); return retval; } @@ -768,18 +766,18 @@ static void ld_usb_disconnect(struct usb_interface *intf) mutex_lock(&dev->mutex); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->mutex); - ld_usb_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->mutex); } + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, ld_usb_delete); + dev_info(&intf->dev, "LD USB Device #%d now disconnected\n", (minor - USB_LD_MINOR_BASE)); } From b748f97aff339e7f08dca9cf38a05b980fb66fea Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 23 May 2026 19:05:23 +0200 Subject: [PATCH 0405/3297] USB: iowarrior: fix use-after-free on disconnect commit bc0e4f16c44e50daa0b1ea729934baa3b4815dee upstream. Submitted write URBs are not stopped on close() and therefore need to be stopped unconditionally on disconnect() to avoid use-after-free in the completion handler. Fixes: b5f8d46867ca ("USB: iowarrior: fix use-after-free after driver unbind") Fixes: 946b960d13c1 ("USB: add driver for iowarrior devices.") Reported-by: syzbot+ad2aac2febc3bedf0962@syzkaller.appspotmail.com Link: https://lore.kernel.org/all/6a0ce39b.170a0220.39a13.0007.GAE@google.com/ Cc: stable Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260523170523.1074563-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/iowarrior.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 365c100693458..38f6dcdfd7fb9 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -919,13 +919,15 @@ static void iowarrior_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; + /* write urbs are not stopped on close() so kill unconditionally */ + usb_kill_anchored_urbs(&dev->submitted); + if (dev->opened) { /* There is a process that holds a filedescriptor to the device , so we only shutdown read-/write-ops going on. Deleting the device is postponed until close() was called. */ usb_kill_urb(dev->int_in_urb); - usb_kill_anchored_urbs(&dev->submitted); wake_up_interruptible(&dev->read_wait); wake_up_interruptible(&dev->write_wait); mutex_unlock(&dev->mutex); From 6af28345cbf8b9a5142353353871f73433f8dd9d Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Tue, 2 Jun 2026 14:45:08 -0600 Subject: [PATCH 0406/3297] USB: quirks: add NO_LPM for the Samsung T5 EVO Portable SSD commit fc591787785b9709a0bb65a7df3ba2537d611c47 upstream. The Samsung T5 EVO Portable SSD (04e8:6200) exhibit two forms of link instability when USB Link Power Management is enabled: 1. The units fail to initialize properly on first detection, resulting in a lockup in the drive where it must be power cycled or the kernel will not recognize the presence of the device. 2. If used for sustained operations (small amounts of continuous data are transferred to the unit) then the unit will "hiccup" after roughly 8 hours of use and will disconnect and reconnect. This has a certain probability of triggering the first issue, but also causes mount points to become invalid since the device gets issued a new letter. Signed-off-by: Erich E. Hoover Cc: stable Link: https://patch.msgid.link/20260602204508.48856-1-erich.e.hoover@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index c799f8f7b1fb0..c921732836f7a 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -297,6 +297,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* CarrolTouch 4500U */ { USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Samsung T5 EVO Portable SSD */ + { USB_DEVICE(0x04e8, 0x6200), .driver_info = USB_QUIRK_NO_LPM }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, From 766738ecf2b819e54d38763c8d1c8ae6cff14b39 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:12 +0200 Subject: [PATCH 0407/3297] USB: legousbtower: fix use-after-free on disconnect race commit 62fc8eb1b1481051f7bab4aa93d79809053dd09f upstream. mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1] a51749ab34d9 ("locking/mutex: Document that mutex_unlock() is non-atomic") [2] 2b9d9e0a9ba0 ("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes: 18bcbcfe9ca2 ("USB: misc: legousbtower: semaphore to mutex") Cc: stable Cc: Daniel Walker Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260622152612.116422-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 379cf01a6e96e..22e82648b3c23 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -185,6 +185,7 @@ MODULE_DEVICE_TABLE(usb, tower_table); /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { + struct kref kref; struct mutex lock; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ @@ -220,7 +221,6 @@ struct lego_usb_tower { /* local function prototypes */ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); -static inline void tower_delete(struct lego_usb_tower *dev); static int tower_open(struct inode *inode, struct file *file); static int tower_release(struct inode *inode, struct file *file); static __poll_t tower_poll(struct file *file, poll_table *wait); @@ -286,8 +286,10 @@ static inline void lego_usb_tower_debug_data(struct device *dev, /* * tower_delete */ -static inline void tower_delete(struct lego_usb_tower *dev) +static inline void tower_delete(struct kref *kref) { + struct lego_usb_tower *dev = container_of(kref, struct lego_usb_tower, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -381,6 +383,8 @@ static int tower_open(struct inode *inode, struct file *file) dev->open_count = 1; + kref_get(&dev->kref); + unlock_exit: mutex_unlock(&dev->lock); @@ -404,14 +408,8 @@ static int tower_release(struct inode *inode, struct file *file) mutex_lock(&dev->lock); - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - - /* unlock here as tower_delete frees dev */ - mutex_unlock(&dev->lock); - tower_delete(dev); - goto exit; - } + if (dev->disconnected) + goto out_unlock; /* wait until write transfer is finished */ if (dev->interrupt_out_busy) { @@ -425,7 +423,9 @@ static int tower_release(struct inode *inode, struct file *file) dev->open_count = 0; +out_unlock: mutex_unlock(&dev->lock); + kref_put(&dev->kref, tower_delete); exit: return retval; } @@ -752,6 +752,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ if (!dev) goto exit; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = usb_get_dev(udev); spin_lock_init(&dev->read_buffer_lock); @@ -828,7 +829,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ return retval; error: - tower_delete(dev); + kref_put(&dev->kref, tower_delete); return retval; } @@ -856,18 +857,18 @@ static void tower_disconnect(struct usb_interface *interface) mutex_lock(&dev->lock); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->lock); - tower_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->lock); } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, tower_delete); + dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n", (minor - LEGO_USB_TOWER_MINOR_BASE)); } From e0886775952e3dd542b11ec54cb5476e62acc4f6 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 21:16:25 +0900 Subject: [PATCH 0408/3297] usb: sl811-hcd: disable controller wakeup on remove commit 4e8ba83ac4d311992e6a4c21de5dd705010df06e upstream. sl811h_probe() enables the HCD controller device as a wakeup source after usb_add_hcd() succeeds, but sl811h_remove() removes the HCD and releases the driver resources without disabling that wakeup source. Disable controller wakeup after usb_remove_hcd() and before usb_put_hcd() so the wakeup source object is detached while the controller device pointer is still available. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 3c9740a117d4 ("usb: hcd: move controller wakeup setting initialization to individual driver") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260701121625.96815-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index ea3cab99c5d40..9dce7150480f5 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1591,6 +1591,7 @@ sl811h_remove(struct platform_device *dev) remove_debug_file(sl811); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); /* some platforms may use IORESOURCE_IO */ res = platform_get_resource(dev, IORESOURCE_MEM, 1); From 964d572b6c00902fe216f2e1832cce7136cac31d Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 2 Jun 2026 13:38:42 +0800 Subject: [PATCH 0409/3297] USB: storage: include US_FL_NO_SAME in quirks mask commit 2c00e09e3f9f06f8434f5ea2ee6179ce46692ee6 upstream. usb_stor_adjust_quirks() parses the usb-storage.quirks module parameter into a new flag set and then applies it with the quirk mask to override built-in flags. The mask is meant to cover the flags that can be overridden by the module parameter. The 'k' quirk character sets US_FL_NO_SAME, but US_FL_NO_SAME is not included in the mask. As a result, the module parameter can set US_FL_NO_SAME, but it cannot clear a built-in US_FL_NO_SAME flag by providing an override entry that omits 'k'. Add US_FL_NO_SAME to the mask so that the module parameter can override it in the same way as the other supported flags. Fixes: 8010622c86ca ("USB: UAS: introduce a quirk to set no_write_same") Cc: stable Signed-off-by: Xu Rao Reviewed-by: Alan Stern Link: https://patch.msgid.link/3BCE5880F9A45C2E+20260602053842.2920137-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 152ee33765500..27901b7a000ad 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -570,7 +570,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, u64 *fflags) US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES | US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS | - US_FL_ALWAYS_SYNC); + US_FL_ALWAYS_SYNC | US_FL_NO_SAME); p = quirks; while (*p) { From 48394f94211cf8fe0ea8604fc441633abf90fc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?HE=20WEI=20=28=E3=82=AE=E3=82=AB=E3=82=AF=29?= Date: Wed, 24 Jun 2026 18:09:52 +0900 Subject: [PATCH 0410/3297] usb: misc: usbio: bound bulk IN response length to the received transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8c6314489550fa81d41723a0ff33f655b5b6c7b6 upstream. usbio_bulk_msg() copies bpkt_len = le16_to_cpu(bpkt->len) bytes out of the bulk IN buffer (usbio->rxbuf, allocated with size usbio->rxbuf_len) into the caller's buffer. bpkt_len is fully controlled by the device and is only checked against ibuf_len; ibuf_len in turn is checked against usbio->txbuf_len, not against rxbuf_len: if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) || (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt)))) return -EMSGSIZE; txbuf_len and rxbuf_len are taken independently from the bulk OUT and bulk IN endpoint wMaxPacketSize in usbio_probe(). A malicious or malfunctioning device that advertises a large bulk OUT endpoint and a small bulk IN endpoint (e.g. by claiming one of the quirk-free IDs such as the Lattice NX33U, 0x2ac1:0x20cb) therefore makes ibuf_len, and hence the device-supplied bpkt_len, exceed rxbuf_len. memcpy() then reads up to txbuf_len - rxbuf_len bytes past the end of the rxbuf slab object. The over-read bytes are handed back to the i2c layer and on to user space through i2c-dev, disclosing adjacent slab memory; with KASAN this is reported as a slab-out-of-bounds read. The number of bytes actually received is already known: act equals the URB actual_length and is bounded by rxbuf_len. Reject any response that claims more payload than was received, mirroring the existing "act < sizeof(*bpkt)" check just above. The control path (usbio_ctrl_msg()) is not affected: it uses a single buffer (ctrlbuf) for both directions, so its analogous copy can never leave the allocation. Found by code review. The out-of-bounds read was confirmed under AddressSanitizer with a faithful userspace model of usbio_bulk_msg()'s receive path (an rxbuf_len-sized buffer, the same act/ibuf_len/bpkt_len checks and the memcpy). A USB raw-gadget + dummy_hcd reproducer is also available. Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Signed-off-by: HE WEI (ギカク) Link: https://patch.msgid.link/20260624090952.86439-1-skyexpoc@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 759af25313b0f..7315cd2469234 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -344,6 +344,10 @@ int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last, if (ibuf_len < bpkt_len) return -ENOSPC; + /* The device must not claim more payload than it actually sent. */ + if (bpkt_len > act - sizeof(*bpkt)) + return -EPROTO; + memcpy(ibuf, bpkt->data, bpkt_len); return bpkt_len; From 729b68a5bad71220ae0914c8bdab9488ad5be6c8 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 7 Jul 2026 00:10:49 +0900 Subject: [PATCH 0411/3297] USB: misc: uss720: unregister parport on probe failure commit b4ecbdc4f8830f5586c4a5cfc384c00f20f8f8b3 upstream. uss720_probe() registers a parport before reading the 1284 register used to detect unsupported Belkin F5U002 adapters. If get_1284_register() fails, the error path drops the driver private data and the USB device reference, but leaves the parport device registered. Leaving the port registered is more than a private allocation leak: parport_register_port() has already reserved a parport number and registered the parport bus device, while pp->private_data still points at the private data that the common error path is about to release. Undo the pre-announce registration in the get_1284_register() failure branch before jumping to the common private-data cleanup path. Clear priv->pp first, matching the disconnect path and avoiding a stale pointer in the private data. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 3295f1b866bf ("usb: misc: uss720: check for incompatible versions of the Belkin F5U002") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Alex Henrie Link: https://patch.msgid.link/20260706151049.63470-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 3138f5dca6da4..6ebcfaf525989 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -735,8 +735,11 @@ static int uss720_probe(struct usb_interface *intf, * here. */ ret = get_1284_register(pp, 0, ®, GFP_KERNEL); dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg); - if (ret < 0) + if (ret < 0) { + priv->pp = NULL; + parport_del_port(pp); goto probe_abort; + } ret = usb_find_last_int_in_endpoint(interface, &epd); if (!ret) { From 8c29d9cfab1c3cf0d0b7fcdf9255597be30aa3e1 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 17:33:25 +0800 Subject: [PATCH 0412/3297] usb: mtu3: unmap request DMA on queue failure commit 0bddda5a11665c210339de76d27ebbd1a2e0b43c upstream. mtu3_gadget_queue() maps the request before checking whether the QMU GPD ring can accept another transfer. the request is returned with -EAGAIN before it is linked on the endpoint request list if mtu3_prepare_transfer() fails. Normal completion and dequeue paths unmap requests from mtu3_req_complete(), but this error path never reaches that helper, so the DMA mapping is left active. Unmap the request before returning from the failed queue path. Fixes: df2069acb005 ("usb: Add MediaTek USB3 DRD driver") Cc: stable Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260623093325.2105323-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mtu3/mtu3_gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index bf73fbc29976f..47fd998f09d53 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -305,6 +305,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep, if (mtu3_prepare_transfer(mep)) { ret = -EAGAIN; + usb_gadget_unmap_request(&mtu->g, req, mep->is_in); goto error; } From cf6ca0aefae03958cfb5b189b0adbfb25c06bfac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 29 Jun 2026 14:45:26 +0200 Subject: [PATCH 0413/3297] USB: serial: keyspan_pda: fix information leak commit 6bfc8d01ac4068eced509f8fc74d0cd205e4dcec upstream. The write() callback is supposed to return the number of characters accepted or a negative errno. Since the addition of write fifo support the keyspan_pda implementation will however return the number characters submitted to the device if the write urb is not already in use. If this number is larger than the number of characters passed to write(), the line discipline continues writing data from beyond the tty write buffer. Fix the information leak by making sure that keyspan_pda_write_start() returns zero on success as intended. Fixes: 034e38e8f687 ("USB: serial: keyspan_pda: add write-fifo support") Cc: stable@vger.kernel.org # 5.11 Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan_pda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index e98b479593d37..f800bf5ac1de9 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -518,7 +518,7 @@ static int keyspan_pda_write_start(struct usb_serial_port *port) if (count == room) schedule_work(&priv->unthrottle_work); - return count; + return 0; } static void keyspan_pda_write_bulk_callback(struct urb *urb) From 4b147eb6ae6e0da5a214aa201b1a3c091827e40f Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Fri, 12 Jun 2026 13:39:16 +0200 Subject: [PATCH 0414/3297] USB: serial: option: add Telit Cinterion FE990D50 compositions commit b33ab1dd80f5c1742f49eb6ec7b337c5ffcf3d32 upstream. Add support for Telit Cinterion FE990D50 compositions: 0x990: RNDIS + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0990 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=ef(misc ) Sub=04 Prot=01 Driver=rndis_host E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(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=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(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=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 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=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x991: rmnet + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 9 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0991 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs= 9 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=(none) 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= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms 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=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(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=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x992: MBIM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 12 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0992 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(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=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(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=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 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=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms 0x993: ECM + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) + tty (diag) + ADPL + adb T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 15 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0993 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=90b6a3ed C: #Ifs=10 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=06 Prot=00 Driver=cdc_ether E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(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=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(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=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 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=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 9 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms Cc: stable@vger.kernel.org Signed-off-by: Fabio Porcedda Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 2f6be5e1f01f2..b4b58e48e2253 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1326,6 +1326,22 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x30), /* Telit FE990D50 (RNDIS) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x30), /* Telit FE990D50 (rmnet) */ + .driver_info = NCTRL(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x30), /* Telit FE990D50 (MBIM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x30), /* Telit FE990D50 (ECM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x60) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1031, 0xff), /* Telit LE910C1-EUX */ .driver_info = NCTRL(0) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff), /* Telit LE910C1-EUX (ECM) */ From eab394781e9321c0c7e97a24fd092387cb262f40 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:08:15 +0200 Subject: [PATCH 0415/3297] USB: serial: digi_acceleport: fix broken rx after throttle commit 83a3dfc018943b05b6daf3a6f891833e1aabfa1f upstream. If the port is closed while throttled, the read urb is never resubmitted and the port will not receive any further data until the device is reconnected (or the driver is rebound). Clear the throttle flags and submit the urb if needed when opening the port. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index a876d6629b65d..83bf3abd09c5f 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1071,6 +1071,7 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char buf[32]; struct digi_port *priv = usb_get_serial_port_data(port); struct ktermios not_termios; + int throttled; /* be sure the device is started up */ if (digi_startup_device(port->serial) != 0) @@ -1098,6 +1099,21 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) not_termios.c_iflag = ~tty->termios.c_iflag; digi_set_termios(tty, port, ¬_termios); } + + spin_lock_irq(&priv->dp_port_lock); + throttled = priv->dp_throttle_restart; + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irq(&priv->dp_port_lock); + + if (throttled) { + ret = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (ret) { + dev_err(&port->dev, "failed to submit read urb: %d\n", ret); + return ret; + } + } + return 0; } From 2b7dc482f859f2d027db07ff0efc1c5df5b3451a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:11:10 +0200 Subject: [PATCH 0416/3297] USB: serial: digi_acceleport: fix hard lockup on disconnect commit 5c1ea24b53bf3bfb859f0a05573997487975da23 upstream. If submitting the OOB write urb fails persistently (e.g if the device is being disconnected) the driver would loop indefinitely with interrupts disabled. Check for urb submission errors when sending OOB commands to avoid hanging if, for example, open(), set_termios() or close() races with a physical disconnect. This is issue was flagged by Sashiko when reviewing an unrelated change to the driver. Link: https://sashiko.dev/#/patchset/20260610132232.356139-1-johan%40kernel.org?part=1 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 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 83bf3abd09c5f..3f0bd8a358907 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -394,12 +394,14 @@ static int digi_write_oob_command(struct usb_serial_port *port, len &= ~3; memcpy(oob_port->write_urb->transfer_buffer, buf, len); oob_port->write_urb->transfer_buffer_length = len; + ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); - if (ret == 0) { - oob_priv->dp_write_urb_in_use = 1; - count -= len; - buf += len; - } + if (ret) + break; + + oob_priv->dp_write_urb_in_use = 1; + count -= len; + buf += len; } spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags); if (ret) From 1243f120790042c2ac92e84e797dacc75fff4366 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:12:29 +0200 Subject: [PATCH 0417/3297] USB: serial: digi_acceleport: fix write buffer corruption commit 24ca1fea8f2753bf33e1d458ec1ae5d9b7796a65 upstream. The digi_write_inb_command() is supposed to wait for the write urb to become available or return an error, but instead it updates the transfer buffer and tries to resubmit the urb on timeout. To make things worse, for commands like break control where no timeout is used, the driver would corrupt the urb immediately due to a broken jiffies comparison (on 32-bit machines this takes five minutes of uptime to trigger due to INITIAL_JIFFIES). Fix this by adding the missing return on timeout and waiting indefinitely when no timeout has been specified as intended. This issue was (sort of) flagged by Sashiko when reviewing an unrelated change to the driver. Link: https://sashiko.dev/#/patchset/20260610132232.356139-1-johan%40kernel.org?part=11 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 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 3f0bd8a358907..3fbda586037db 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -431,20 +431,22 @@ static int digi_write_inb_command(struct usb_serial_port *port, int len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; + unsigned long expire; unsigned long flags; dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n", priv->dp_port_num, count); if (timeout) - timeout += jiffies; - else - timeout = ULONG_MAX; + expire = jiffies + timeout; spin_lock_irqsave(&priv->dp_port_lock, flags); while (count > 0 && ret == 0) { - while (priv->dp_write_urb_in_use && - time_before(jiffies, timeout)) { + while (priv->dp_write_urb_in_use) { + if (timeout && time_after(jiffies, expire)) { + ret = -ETIMEDOUT; + break; + } cond_wait_interruptible_timeout_irqrestore( &priv->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -453,6 +455,9 @@ static int digi_write_inb_command(struct usb_serial_port *port, spin_lock_irqsave(&priv->dp_port_lock, flags); } + if (ret) + break; + /* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */ From 1967a7f0cd5c08eb479196daa5aaa4b7b7a7bd04 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 8 Jun 2026 16:58:03 +0200 Subject: [PATCH 0418/3297] USB: ulpi: fix memory leak on registration failure commit 8af6812795869a66e9b26044f455b13deecdb69c upstream. The allocated device name is never freed on early ULPI device registration failures. Fix this by initialising the device structure earlier and releasing the initial reference whenever registration fails. Fixes: 289fcff4bcdb ("usb: add bus type for USB ULPI") Cc: stable Cc: Heikki Krogerus Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260608145803.69360-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/ulpi.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 95caad156de58..968b8f903526e 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -281,28 +281,24 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ulpi->dev.parent = dev; /* needed early for ops */ ulpi->dev.bus = &ulpi_bus; ulpi->dev.type = &ulpi_dev_type; + + device_initialize(&ulpi->dev); + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); ret = ulpi_of_register(ulpi); - if (ret) { - kfree(ulpi); + if (ret) return ret; - } ret = ulpi_read_id(ulpi); - if (ret) { - of_node_put(ulpi->dev.of_node); - kfree(ulpi); + if (ret) return ret; - } - ret = device_register(&ulpi->dev); - if (ret) { - put_device(&ulpi->dev); + ret = device_add(&ulpi->dev); + if (ret) return ret; - } root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root); debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops); @@ -334,9 +330,10 @@ struct ulpi *ulpi_register_interface(struct device *dev, ulpi->ops = ops; ret = ulpi_register(dev, ulpi); - if (ret) + if (ret) { + put_device(&ulpi->dev); return ERR_PTR(ret); - + } return ulpi; } From 2d84c8376f7aa7ca703010e79dec26a2183d0ec3 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 15:06:07 +0800 Subject: [PATCH 0419/3297] USB: usb-storage: ene_ub6250: restore media-ready check commit 5fc3f333c001f1e308bbcdeecdec0d054d24338b upstream. Commit 1892bf90677a ("USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c") converted the media status fields from bitfields to bit masks. The original ene_transport() test called ene_init() only when neither media type was ready: !(sd_ready || ms_ready) The converted test became: !sd_ready || ms_ready This is not equivalent. Restore the original semantics by testing that both ready bits are clear before calling ene_init(). Fixes: 1892bf90677a ("USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c") Cc: stable Signed-off-by: Xu Rao Reviewed-by: Alan Stern Link: https://patch.msgid.link/F42641386E32404F+20260626070607.4119527-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/ene_ub6250.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index ce91fb1059754..5b4cbd15c3c8a 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2306,7 +2306,8 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) /*US_DEBUG(usb_stor_show_command(us, srb)); */ scsi_set_resid(srb, 0); - if (unlikely(!(info->SD_Status & SD_Ready) || (info->MS_Status & MS_Ready))) + if (unlikely(!(info->SD_Status & SD_Ready) && + !(info->MS_Status & MS_Ready))) result = ene_init(us); if (result == USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; From 6c7e8e2514374b0b1ea5897c227de5ebff7b06ec Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 17 Jun 2026 10:06:13 +0800 Subject: [PATCH 0420/3297] usbip: tools: support SuperSpeedPlus devices commit 195e667c8719480c320cd48ac0fbf1cb81d6ffe0 upstream. USB devices running at SuperSpeedPlus report "10000" or "20000" in their sysfs speed attribute. usbip currently maps only "5000" to USB_SPEED_SUPER, so a SuperSpeedPlus device is imported as USB_SPEED_UNKNOWN. The attach request is then rejected by vhci_hcd: vhci_hcd: Failed attach request for unsupported USB speed: UNKNOWN Map the SuperSpeedPlus sysfs speed values to USB_SPEED_SUPER_PLUS, use the SuperSpeed VHCI hub for SuperSpeedPlus devices, and recognize the gadget current_speed string used by the kernel. Fixes: b2316645ca5e ("usb: show speed "10000" in sysfs for USB 3.1 SuperSpeedPlus devices") Cc: stable Signed-off-by: Yichong Chen Reviewed-by: Shuah Khan Link: https://patch.msgid.link/00C828F338E43447+20260617020613.199086-1-chenyichong@uniontech.com Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/libsrc/usbip_common.c | 2 ++ tools/usb/usbip/libsrc/usbip_device_driver.c | 4 ++++ tools/usb/usbip/libsrc/vhci_driver.c | 1 + 3 files changed, 7 insertions(+) diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c index b8d7d480595a8..f4734f552d314 100644 --- a/tools/usb/usbip/libsrc/usbip_common.c +++ b/tools/usb/usbip/libsrc/usbip_common.c @@ -29,6 +29,8 @@ static const struct speed_string speed_strings[] = { { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, + { USB_SPEED_SUPER_PLUS, "10000", "Super Speed Plus(10000Mbps)" }, + { USB_SPEED_SUPER_PLUS, "20000", "Super Speed Plus(20000Mbps)" }, { 0, NULL, NULL } }; diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c index 927a151fa9aa9..f8879a580407e 100644 --- a/tools/usb/usbip/libsrc/usbip_device_driver.c +++ b/tools/usb/usbip/libsrc/usbip_device_driver.c @@ -57,6 +57,10 @@ static struct { .speed = USB_SPEED_SUPER, .name = "super-speed", }, + { + .speed = USB_SPEED_SUPER_PLUS, + .name = "super-speed-plus", + }, }; static diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c index 8159fd98680b4..4ca3783ee5b7c 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.c +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -338,6 +338,7 @@ int usbip_vhci_get_free_port(uint32_t speed) switch (speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER) continue; break; From 347b59e9f96719d89b6ef555d02a18ada1a5846f Mon Sep 17 00:00:00 2001 From: Sam Day Date: Fri, 26 Jun 2026 14:29:10 +1000 Subject: [PATCH 0421/3297] usbip: vudc: fix NULL deref in vep_dequeue() commit c5371e0b91b24159a3ebaa61e70b0980bcf03c0a upstream. vep_alloc_request() wasn't initializing vrequest->udc, so cancellations on the FunctionFS AIO path were arriving in vep_dequeue without a valid UDC reference. Since vrequest->udc is never actually properly used anywhere, we opt to remove it, and update vep_dequeue to obtain a reference to the udc with ep_to_vudc(), consistent with the other vep_ ops. AFAICT this bug has existed for ~10 years. Seems that nobody has really stressed the FunctionFS AIO path on usbip's vudc. I tested this fix in a QEMU aarch64 guest driving FunctionFS endpoints via AIO. Before the fix, running `usbip attach` from the host would cause the guest to oops with the following backtrace: Call trace: vep_dequeue+0x1c/0xe4 (P) usb_ep_dequeue+0x14/0x20 ffs_aio_cancel+0x24/0x34 __arm64_sys_io_cancel+0xb0/0x124 do_el0_svc+0x68/0x100 el0_svc+0x18/0x5c el0t_64_sync_handler+0x98/0xdc el0t_64_sync+0x154/0x158 Assisted-by: opencode:openai/gpt-5.5 Cc: stable Fixes: b6a0ca111867 ("usbip: vudc: Add UDC specific ops") Reviewed-by: Igor Kotrasinski Signed-off-by: Sam Day Link: https://patch.msgid.link/20260626-usbip-vudc-deque-fix-v3-1-98c2dc4d6a48@samcday.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc.h | 1 - drivers/usb/usbip/vudc_dev.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index faf61c9c6a989..5ef0e7d9b23ad 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -38,7 +38,6 @@ struct vep { struct vrequest { struct usb_request req; - struct vudc *udc; struct list_head req_entry; /* Request queue */ }; diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index a5c1001071868..98e13f164ac75 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -333,7 +333,6 @@ static int vep_queue(struct usb_ep *_ep, struct usb_request *_req, static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct vep *ep; - struct vrequest *req; struct vudc *udc; struct vrequest *lst; unsigned long flags; @@ -343,8 +342,7 @@ static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) return ret; ep = to_vep(_ep); - req = to_vrequest(_req); - udc = req->udc; + udc = ep_to_vudc(ep); if (!udc->driver) return -ESHUTDOWN; From 1126f1110b86c1f8146338b8b7df905438ea0747 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 20:40:06 +0900 Subject: [PATCH 0422/3297] usb: typec: anx7411: use devm_pm_runtime_enable() commit 0ef7cc27da8b9e315a4a5a665c68c44206f5e559 upstream. anx7411_i2c_probe() enables runtime PM before returning successfully, but anx7411_i2c_remove() tears down the Type-C partner state, workqueue, dummy I2C device, mux, switch and port without disabling runtime PM. Use devm_pm_runtime_enable() so runtime PM is disabled automatically on driver detach. Since devres action registration can fail, route that failure through the existing probe unwind path. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: fe6d8a9c8e64 ("usb: typec: anx7411: Add Analogix PD ANX7411 support") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260701114006.75738-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/anx7411.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 0ae0a5ee3fae0..6b4cbc3bfa37d 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1538,7 +1538,9 @@ static int anx7411_i2c_probe(struct i2c_client *client) if (anx7411_typec_check_connection(plat)) dev_err(dev, "check status\n"); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto free_wq; return 0; From bf6aa6c0ce0db4a1cf1ac4194c6b3f450b9d710b Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Thu, 2 Jul 2026 15:13:29 -0400 Subject: [PATCH 0423/3297] usb: typec: class: drop PD lookup reference commit 43ae2f90b70cda374c487c1639a01d0f14e5d583 upstream. usb_power_delivery_find() wraps class_find_device_by_name(). That helper returns a device reference that must be released by the caller. select_usb_power_delivery_store() only needs this reference while calling the pd_set callback. Drop it once the callback returns. Otherwise the sysfs write can pin the selected USB Power Delivery object and prevent it from being released on unregister. Fixes: a7cff92f0635 ("usb: typec: USB Power Delivery helpers for ports and partners") Cc: stable Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260702191329.2648043-1-shuangpeng.kernel@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 67a533e351506..851dda428f552 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1525,6 +1525,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev, return -EINVAL; ret = port->ops->pd_set(port, pd); + put_device(&pd->dev); if (ret) return ret; From 0bc177820bd38a3ae77e53b55be770cce2dd0887 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Thu, 4 Jun 2026 18:50:24 +0800 Subject: [PATCH 0424/3297] usb: typec: tcpm: Fix VDM type for Enter Mode commands commit 9cff680e47632b7723cb19f9c5e63669063c3417 upstream. VDO() second parameter is VDM type (bit 15): 1 for SVDM, 0 for UVDM. Using 'vdo ? 2 : 1' corrupts SVID low bit when vdo is non-NULL (2 << 15 = BIT(16)). Enter Mode is always SVDM, hardcode to 1. Fixes: 8face9aa57c8 ("usb: typec: Add parameter for the VDO to typec_altmode_enter()") Cc: stable Signed-off-by: Andy Yan Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260604105059.18750-1-andyshrk@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index e1ca6e7bbe557..97db11a4d6796 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2928,7 +2928,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP); @@ -2976,7 +2976,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME); From 3e1b1ac47e8163627f159f30d80d51b914620dd4 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 22 Jun 2026 22:08:03 +0000 Subject: [PATCH 0425/3297] usb: typec: tcpm: Validate SVID index in svdm_consume_modes() commit 7b681dd5fbf60b24a13c14661e5b7735759fb491 upstream. In svdm_consume_modes(), the SVID value is read from pmdata->svids using pmdata->svid_index as an array index without bounds validation: paltmode->svid = pmdata->svids[pmdata->svid_index]; If pmdata->svid_index is driven beyond SVID_DISCOVERY_MAX (16), it results in an out-of-bounds read of the pmdata->svids array. Because pd_mode_data is embedded inside struct tcpm_port, indexing past svids reads into adjacent fields. In particular: - At index 16, it reads the altmodes count. - At index 18 and beyond, it reads into altmode_desc[], which contains partner-supplied SVDM Discovery Modes VDOs. By injecting a chosen SVID into altmode_desc[0].vdo and driving svid_index to 20, the partner can force paltmode->svid to be loaded with an arbitrary, partner- chosen SVID, which is then registered via typec_partner_register_altmode(). Fix this by validating that pmdata->svid_index is non-negative and strictly less than pmdata->nsvids before accessing the pmdata->svids array inside svdm_consume_modes(). Assisted-by: Antigravity:gemini-3.5-flash Fixes: 4ab8c18d4d67 ("usb: typec: Register a device for every mode") Cc: stable Signed-off-by: Badhri Jagan Sridharan Reviewed-by: RD Babiera Acked-by: Heikki Krogerus Link: https://patch.msgid.link/20260622220803.305750-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 97db11a4d6796..2209fab8f4431 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1840,6 +1840,11 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, return; } + if (pmdata->svid_index < 0 || pmdata->svid_index >= pmdata->nsvids) { + tcpm_log(port, "Invalid SVID index %d", pmdata->svid_index); + return; + } + for (i = 1; i < cnt; i++) { if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { /* Already logged in svdm_consume_svids() */ From 8c00aec752ce1d8b07bbf5db2193e8192cbd1a53 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 1 Jun 2026 14:28:37 +0000 Subject: [PATCH 0426/3297] usb: typec: ucsi: Invert DisplayPort role assignment commit d092d7edf8faefa3e27b9fc7f0e7904b06c833a2 upstream. The existing implementation assigned these flags backwards, configuring the partner's DisplayPort role to match the port's role instead of complementing it. This prevents proper configuration during DP altmode activation, often causing `pin_assignment` to remain 0 in `dp_altmode_configure()` and resulting in VDM negotiation failures: [ 583.328246] typec port1.1: VDM 0xff01a150 failed Additionally, the fix ensures that the `pin_assignment` sysfs attribute displays the correct values. Cc: stable Fixes: af8622f6a585 ("usb: typec: ucsi: Support for DisplayPort alt mode") Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260601142837.3240207-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 67a0991a7b769..c44da2fae81fb 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -166,12 +166,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) * that Multi-function is preferred. */ if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) { - dp->data.status |= DP_STATUS_CON_UFP_D; + dp->data.status |= DP_STATUS_CON_DFP_D; if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; } else { - dp->data.status |= DP_STATUS_CON_DFP_D; + dp->data.status |= DP_STATUS_CON_UFP_D; if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; From b1dfdff51a865674cc43ce84256d2c85843e8c7e Mon Sep 17 00:00:00 2001 From: Madhu M Date: Fri, 19 Jun 2026 21:03:11 +0530 Subject: [PATCH 0427/3297] usb: typec: ucsi: Pass full DP config payload in SET_NEW_CAM for DP alt mode commit 735a461d060d4eeb2f9732aba1295bc32a3982e2 upstream. In the UCSI Specification Revision 3.1 RC1, bits 32-63 of the SET_NEW_CAM command hold the 32-bit Alternate Mode Specific (AMSpecific) field. For DisplayPort Alternate Mode, this field must contain the full 32-bit DisplayPort configuration VDO payload that the OPM wants the connector to operate in, rather than just the pin assignment value. This AMSpecific value follows the DisplayPort Configurations defined in the DisplayPort Alt Mode on USB Type-C Specification v2.1a, Table 5-13: SOP DisplayPort Configurations. Fixes: af8622f6a585 ("usb: typec: ucsi: Support for DisplayPort alt mode") Cc: stable Signed-off-by: Madhu M Reviewed-by: Jameson Thies Reviewed-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260619153311.3526083-1-madhu.m@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index c44da2fae81fb..7067f2561b846 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -185,13 +185,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) static int ucsi_displayport_configure(struct ucsi_dp *dp) { - u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); u64 command; if (!dp->override) return 0; - command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); + command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, dp->data.conf); return ucsi_send_command(dp->con->ucsi, command, NULL, 0); } From f5c772b76bbd95de8be51cf849c6098f6af6fcf9 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Tue, 16 Jun 2026 13:20:11 +0000 Subject: [PATCH 0428/3297] usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove commit 1f0bdc2884b67de337215079bba166df0cdf4ac5 upstream. The threaded IRQ handler ccg_irq_handler() calls ucsi_notify_common(), which on a connector-change event calls ucsi_connector_change() and schedules connector work. In ucsi_ccg_remove(), ucsi_destroy() frees uc->ucsi (kfree) before free_irq() is called, so a handler invocation already in flight may access the freed object after ucsi_destroy(). CPU 0 (remove) | CPU 1 (threaded IRQ) ucsi_destroy(uc->ucsi) | ccg_irq_handler() kfree(ucsi) // FREE | ucsi_notify_common(uc->ucsi) // USE Move free_irq() before ucsi_destroy() in the remove path. It is kept after ucsi_unregister(): ucsi_unregister() cancels connector work whose handler issues GET_CONNECTOR_STATUS through ucsi_send_command_common(), which waits for a completion that is signalled from the IRQ handler, so the IRQ must stay active until that work has been cancelled. The probe error path already orders free_irq() before ucsi_destroy(). This bug was found by static analysis. Fixes: e32fd989ac1c ("usb: typec: ucsi: ccg: Move to the new API") Cc: stable Signed-off-by: Fan Wu Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260616132011.103279-1-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 9d7b834a76fa8..6c1f62c78ac25 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1518,8 +1518,8 @@ static void ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); pm_runtime_disable(uc->dev); ucsi_unregister(uc->ucsi); - ucsi_destroy(uc->ucsi); free_irq(uc->irq, uc); + ucsi_destroy(uc->ucsi); } static const struct of_device_id ucsi_ccg_of_match_table[] = { From b45be66ed47d45d7d4af5c9c5bc8aebaada4e8aa Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Fri, 3 Jul 2026 13:07:37 +0200 Subject: [PATCH 0429/3297] usb: typec: ucsi: cancel pending work on system suspend commit 7c4a234bd31a64a8dbd0140dc812da592c5e0787 upstream. On a Dell XPS 13 9360 (BIOS 2.21.0), entering system suspend (deep/S3) races a pending UCSI connector-change worker against the ACPI EC teardown. The worker evaluates the UCSI _DSM (GET_CONNECTOR_STATUS), whose AML accesses the Embedded Controller. By that point the ACPI EC has already been stopped for suspend, so the EC address space handler rejects the access with AE_BAD_PARAMETER, aborting the AML and failing the connector query: [22314.689495] ACPI: EC: interrupt blocked [22314.711981] ACPI: PM: Preparing to enter system sleep state S3 [22314.743260] ACPI: EC: event blocked [22314.743265] ACPI: EC: EC stopped [22314.743267] ACPI: PM: Saving platform NVS memory [22314.744241] ACPI Error: AE_BAD_PARAMETER, Returned by Handler for [EmbeddedControl] (20260408/evregion-303) [22314.744432] ACPI Error: Aborting method \_SB.PCI0.LPCB.ECDV.ECW1 due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.744673] ACPI Error: Aborting method \ECWB due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.745201] ACPI Error: Aborting method \_SB.UBTC._DSM due to previous error (AE_BAD_PARAMETER) (20260408/psparse-543) [22314.745394] ACPI: \_SB_.UBTC: failed to evaluate _DSM c298836f-a47c-e411-ad36-631042b5008f rev:1 func:1 (0x1001) [22314.745414] ucsi_acpi USBC000:00: ucsi_acpi_dsm: failed to evaluate _DSM 1 [22314.745424] ucsi_acpi USBC000:00: ucsi_handle_connector_change: GET_CONNECTOR_STATUS failed (-5) ucsi_acpi implements a resume callback but no suspend callback, so nothing cancels the connector-change work before the firmware/EC is torn down. Add a `ucsi_suspend()` core helper that cancels the pending init and connector-change work, and wire it into ucsi_acpi's PM ops. The connector state is re-read on resume by `ucsi_resume()`, so cancelling the work loses nothing. Fixes: 4e3a50293c2b ("usb: typec: ucsi: acpi: Implement resume callback") Cc: stable Signed-off-by: Paul Menzel Assisted-by: Claude Opus 4.8 Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260703110738.8457-2-pmenzel@molgen.mpg.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 20 ++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 1 + drivers/usb/typec/ucsi/ucsi_acpi.c | 10 +++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 34e93d6c67690..b4b0c46d2fa44 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1918,6 +1918,26 @@ static void ucsi_resume_work(struct work_struct *work) } } +int ucsi_suspend(struct ucsi *ucsi) +{ + int i; + + /* + * Cancel pending work so it cannot access the firmware after the ACPI + * EC is stopped for suspend; state is re-read on resume. + */ + cancel_delayed_work_sync(&ucsi->work); + + if (!ucsi->connector) + return 0; + + for (i = 0; i < ucsi->cap.num_connectors; i++) + cancel_work_sync(&ucsi->connector[i].work); + + return 0; +} +EXPORT_SYMBOL_GPL(ucsi_suspend); + int ucsi_resume(struct ucsi *ucsi) { if (ucsi->connector) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index e301d9012936f..a9144c2997391 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -551,6 +551,7 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command, void *retval, size_t size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_suspend(struct ucsi *ucsi); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 6b92f296e9850..94b52ccf6f426 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -245,6 +245,13 @@ static void ucsi_acpi_remove(struct platform_device *pdev) ucsi_acpi_notify); } +static int ucsi_acpi_suspend(struct device *dev) +{ + struct ucsi_acpi *ua = dev_get_drvdata(dev); + + return ucsi_suspend(ua->ucsi); +} + static int ucsi_acpi_resume(struct device *dev) { struct ucsi_acpi *ua = dev_get_drvdata(dev); @@ -252,7 +259,8 @@ static int ucsi_acpi_resume(struct device *dev) return ucsi_resume(ua->ucsi); } -static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, ucsi_acpi_suspend, + ucsi_acpi_resume); static const struct acpi_device_id ucsi_acpi_match[] = { { "PNP0CA0", 0 }, From e086c16962a1b0142e2675610e9c06fcfcd4c3a8 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 9 Jun 2026 17:29:05 +0200 Subject: [PATCH 0430/3297] usb: gadget: f_fs: Fix DMA fence leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit baa6b6068a3f2bf2ed525a1cb37975905dadc658 upstream. In ffs_dmabuf_transfer(), a ffs_dma_fence object is kmalloc'd, with the underlying dma_fence later initialized by dma_fence_init(), which sets its kref counter to 1. Then, dma_resv_add_fence() gets a second reference, and a pointer to the ffs_dma_fence is passed as the usb_request's "context" field. The dma-resv mechanism will manage the second reference, but the first reference is never properly released; the ffs_dmabuf_cleanup() function decreases the reference count, but only to balance with the reference grab in ffs_dmabuf_signal_done(). The code will then slowly leak memory as more ffs_dma_fence objects are created without being ever freed. Address this issue by transferring ownership of the fence to the DMA reservation object, by calling dma_fence_put() right after dma_resv_add_fence(). The ffs_dma_fence then gets properly discarded after being signalled. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Cc: stable Signed-off-by: Paul Cercueil Tested-by: Nuno Sá Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20260609152905.729328-1-paul@crapouillou.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 737d60b26e029..7cddf88589335 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1684,6 +1684,7 @@ static int ffs_dmabuf_transfer(struct file *file, resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE; dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir); + dma_fence_put(&fence->base); dma_resv_unlock(dmabuf->resv); /* Now that the dma_fence is in place, queue the transfer. */ From 9818bcae3c0ca1dde4b9a334125c46676e0a9b29 Mon Sep 17 00:00:00 2001 From: Chao Shi Date: Fri, 22 May 2026 18:00:25 -0400 Subject: [PATCH 0431/3297] block: skip sync_blockdev() on surprise removal in bdev_mark_dead() commit 49f06cff50a4ccf3b7a1a662ceb892b3b21a527a upstream. bdev_mark_dead()'s @surprise == true means the device is already gone. The filesystem callback fs_bdev_mark_dead() honours this and skips sync_filesystem(), but the bare block device path (no ->mark_dead op) lost its !surprise guard when the holder ->mark_dead callback was wired up (see Fixes), and now calls sync_blockdev() unconditionally, which can hang forever waiting on writeback that can no longer complete. syzkaller hit this via nvme_reset_work()'s "I/O queues lost" path: nvme_mark_namespaces_dead() -> blk_mark_disk_dead() -> bdev_mark_dead(bdev, true) -> sync_blockdev() blocks in folio_wait_writeback(), wedging the reset worker and every task waiting on it. Skip the sync on surprise removal, matching fs_bdev_mark_dead(); invalidate_bdev() still runs. Orderly removal (surprise == false) is unchanged. Found by FuzzNvme(Syzkaller with FEMU fuzzing framework). Fixes: d8530de5a6e8 ("block: call into the file system for bdev_mark_dead") Acked-by: Sungwoo Kim Acked-by: Dave Tian Acked-by: Weidong Zhu Signed-off-by: Chao Shi Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260522220025.1770388-1-coshi036@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/bdev.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/bdev.c b/block/bdev.c index 57e78692084b1..02d7503e7c2c4 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -1237,7 +1237,13 @@ void bdev_mark_dead(struct block_device *bdev, bool surprise) bdev->bd_holder_ops->mark_dead(bdev, surprise); else { mutex_unlock(&bdev->bd_holder_lock); - sync_blockdev(bdev); + /* + * On surprise removal the device is already gone; syncing is + * futile and can hang forever waiting on I/O that will never + * complete. Match fs_bdev_mark_dead(), which also skips it. + */ + if (!surprise) + sync_blockdev(bdev); } invalidate_bdev(bdev); From b7b2d2ccdbc4dce33a882d9c00484a3a2dc10866 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Fri, 10 Jul 2026 10:09:22 +0800 Subject: [PATCH 0432/3297] mm: shmem: fix potential livelock issue for shmem direct swapin When skipping swapcache for synchronous IO swap devices, swapcache_prepare() is used to prevent parallel swapin from proceeding with the swap cache flag. However, on PREEMPT kernels this can lead to a livelock, as reported by Chao[1]: Thread A starts direct swapin of a shmem folio and calls swapcache_prepare() to set SWAP_HAS_CACHE. It may then be preempted inside workingset_refault(). Meanwhile, a higher priority thread B also attempts direct swapin of the same shmem swap entry. Since swapcache_prepare() already marks the entry, thread B repeatedly gets -EEXIST and busy-loops waiting for thread A to finish. But as thread B runs at higher priority, thread A cannot preempt it, resulting in starvation and a livelock. Fix it by yielding the CPU with schedule_timeout_uninterruptible(1) when swapcache_prepare() fails, following the same approach used in commit 029c4628b2eb ("mm: swap: get rid of livelock in swapin readahead") and commit 13ddaf26be32 ("mm/swap: fix race when skipping swapcache"). However, commit 01626a1823 ("mm: avoid unconditional one-tick sleep when swapcache_prepare fails") found that the unconditional one-tick sleep can cause UI stuttering on latency-sensitive Android devices. So we can follow the same approach by adding a waitqueue to wake up tasks when needed, instead of always sleeping for a full tick. Note that mainline does not have this potential issue, which has already been resolved by Kairui's swap refactoring work[2]. [1] https://lore.kernel.org/all/700a2cbf90a2484f979aac858f08f5d4@xiaomi.com/ [2] https://lore.kernel.org/all/20260517-swap-table-p4-v5-0-88ae43e064c7@tencent.com/ Fixes: 1dd44c0af4fa ("mm: shmem: skip swapcache for swapin of synchronous swap device") Reported-by: Ma Chao Closes: https://lore.kernel.org/all/700a2cbf90a2484f979aac858f08f5d4@xiaomi.com/ Signed-off-by: Baolin Wang Signed-off-by: Sasha Levin --- mm/shmem.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mm/shmem.c b/mm/shmem.c index 94c5b0d78ac31..3c329b794ae4d 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2005,11 +2005,14 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf, return ERR_PTR(error); } +static DECLARE_WAIT_QUEUE_HEAD(shmem_swapcache_wq); + static struct folio *shmem_swap_alloc_folio(struct inode *inode, struct vm_area_struct *vma, pgoff_t index, swp_entry_t entry, int order, gfp_t gfp) { struct shmem_inode_info *info = SHMEM_I(inode); + DECLARE_WAITQUEUE(wait, current); int nr_pages = 1 << order; struct folio *new; gfp_t alloc_gfp; @@ -2066,6 +2069,10 @@ static struct folio *shmem_swap_alloc_folio(struct inode *inode, if (swapcache_prepare(entry, nr_pages)) { folio_put(new); new = ERR_PTR(-EEXIST); + /* Relax a bit to prevent rapid repeated page faults */ + add_wait_queue(&shmem_swapcache_wq, &wait); + schedule_timeout_uninterruptible(1); + remove_wait_queue(&shmem_swapcache_wq, &wait); /* Try smaller folio to avoid cache conflict */ goto fallback; } @@ -2423,6 +2430,8 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index, if (skip_swapcache) { folio->swap.val = 0; swapcache_clear(si, swap, nr_pages); + if (waitqueue_active(&shmem_swapcache_wq)) + wake_up(&shmem_swapcache_wq); } else { swap_cache_del_folio(folio); } @@ -2442,8 +2451,11 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index, if (folio) folio_unlock(folio); failed_nolock: - if (skip_swapcache) + if (skip_swapcache) { swapcache_clear(si, folio->swap, folio_nr_pages(folio)); + if (waitqueue_active(&shmem_swapcache_wq)) + wake_up(&shmem_swapcache_wq); + } if (folio) folio_put(folio); put_swap_device(si); From be1567992417dc92133e74126de7a6066c825ac9 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Mon, 13 Jul 2026 15:17:37 -0700 Subject: [PATCH 0433/3297] x86,fs/resctrl: Prevent out-of-bounds access while offlining CPU when SNC enabled commit fc16126cc11d9f507130bf84ab137ee0938c900e upstream. The architecture updates the cpu_mask in a domain's header to track which online CPUs are associated with the domain. When this mask becomes empty the architecture initiates offline of the domain that includes calling on resctrl fs to offline the domain. If it is a monitoring domain in which LLC occupancy is tracked resctrl fs forces the limbo handler to clear all busy RMID state associated with the domain. The limbo handler always reads the current event value associated with a busy RMID irrespective of it being checked as part of regular "is it still busy" check or whether it will be forced released anyway. When reading an RMID on a system with SNC enabled the "logical RMID" is converted to the "physical RMID" and this conversion requires the NUMA node ID of the resctrl monitoring domain that is in turn determined by querying the NUMA node ID of any CPU belonging to the monitoring domain. When the monitoring domain is going offline its cpu_mask is empty causing the NUMA node ID query via cpu_to_node() to be done with "nr_cpu_ids" as argument resulting in an out-of-bounds access. Refactor the limbo handler to skip reading the RMID when the RMID will just be forced to no longer be dirty in the domain anyway. Add a safety check to the architecture's RMID reader to protect against this scenario. Fixes: e13db55b5a0d ("x86/resctrl: Introduce snc_nodes_per_l3_cache") Closes: https://sashiko.dev/#/patchset/cover.1780456704.git.reinette.chatre%40intel.com?part=9 Reported-by: Sashiko Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/16137433df42f85013b2f7a53626795cbd6637b9.1781029125.git.reinette.chatre@intel.com Signed-off-by: Sasha Levin --- arch/x86/kernel/cpu/resctrl/monitor.c | 8 +++++- fs/resctrl/monitor.c | 39 +++++++++++++++------------ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index fe1a2aa53c16a..4b2b539ddff95 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -243,14 +243,20 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, u64 *val, void *ignored) { struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); - int cpu = cpumask_any(&d->hdr.cpu_mask); struct arch_mbm_state *am; u64 msr_val; u32 prmid; + int cpu; int ret; resctrl_arch_rmid_read_context_check(); + if (cpumask_empty(&d->hdr.cpu_mask)) { + pr_warn_once("Domain %d has no CPUs\n", d->hdr.id); + return -EINVAL; + } + + cpu = cpumask_any(&d->hdr.cpu_mask); prmid = logical_rmid_to_physical_rmid(cpu, rmid); ret = __rmid_read_phys(prmid, eventid, &msr_val); diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 572a9925bd6ca..7012a75814ac3 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -135,9 +135,9 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free) struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); u32 idx_limit = resctrl_arch_system_num_rmid_idx(); struct rmid_entry *entry; + bool rmid_dirty = true; u32 idx, cur_idx = 1; void *arch_mon_ctx; - bool rmid_dirty; u64 val = 0; arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, QOS_L3_OCCUP_EVENT_ID); @@ -159,22 +159,27 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free) break; entry = __rmid_entry(idx); - if (resctrl_arch_rmid_read(r, d, entry->closid, entry->rmid, - QOS_L3_OCCUP_EVENT_ID, &val, - arch_mon_ctx)) { - rmid_dirty = true; - } else { - rmid_dirty = (val >= resctrl_rmid_realloc_threshold); - - /* - * x86's CLOSID and RMID are independent numbers, so the entry's - * CLOSID is an empty CLOSID (X86_RESCTRL_EMPTY_CLOSID). On Arm the - * RMID (PMG) extends the CLOSID (PARTID) space with bits that aren't - * used to select the configuration. It is thus necessary to track both - * CLOSID and RMID because there may be dependencies between them - * on some architectures. - */ - trace_mon_llc_occupancy_limbo(entry->closid, entry->rmid, d->hdr.id, val); + if (!force_free) { + if (resctrl_arch_rmid_read(r, d, entry->closid, + entry->rmid, QOS_L3_OCCUP_EVENT_ID, + &val, arch_mon_ctx)) { + rmid_dirty = true; + } else { + rmid_dirty = (val >= resctrl_rmid_realloc_threshold); + + /* + * x86's CLOSID and RMID are independent numbers, + * so the entry's CLOSID is an empty CLOSID + * (X86_RESCTRL_EMPTY_CLOSID). On Arm the RMID + * (PMG) extends the CLOSID (PARTID) space with + * bits that aren't used to select the configuration. + * It is thus necessary to track both CLOSID and + * RMID because there may be dependencies between + * them on some architectures. + */ + trace_mon_llc_occupancy_limbo(entry->closid, entry->rmid, + d->hdr.id, val); + } } if (force_free || !rmid_dirty) { From e5049526a7aacdb53aaaee2e21b0fd8c865f02b4 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 14 Jul 2026 06:45:53 +0000 Subject: [PATCH 0434/3297] rust_binder: introduce TransactionInfo [ Upstream commit 5326a18e3e640061ca4b65c1b732feaeace61c39 ] Rust Binder exposes information about transactions that are sent in various ways: printing to the kernel log, tracepoints, files in binderfs, and the upcoming netlink support. Currently all these mechanisms use disparate ways of obtaining the same information, so let's introduce a single Info struct that collects all the required information in a single place, so that all of these different mechanisms can operate in a more uniform way. For now, the new info struct is only used to replace a few things: * The BinderTransactionDataSg struct that is passed as an argument to several methods is removed as the information is moved into the new info struct and passed down that way. * The oneway spam detection fields on Transaction and Allocation can be removed, as the information can be returned to the caller via the mutable info struct instead. But several other uses of the info struct are planned in follow-up patches. Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260306-transaction-info-v1-1-fda58fca558b@google.com Signed-off-by: Greg Kroah-Hartman [aliceryhl: explicitly impl Zeroable for UserPtr instead of using derive] Signed-off-by: Alice Ryhl Signed-off-by: Sasha Levin --- drivers/android/binder/allocation.rs | 3 - drivers/android/binder/error.rs | 10 +- drivers/android/binder/process.rs | 15 +- drivers/android/binder/thread.rs | 190 +++++++++++++++----------- drivers/android/binder/transaction.rs | 83 ++++++----- rust/kernel/uaccess.rs | 3 + 6 files changed, 170 insertions(+), 134 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index a9475e136d76f..3aaeefef6bdd9 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -56,7 +56,6 @@ pub(crate) struct Allocation { pub(crate) process: Arc, allocation_info: Option, free_on_drop: bool, - pub(crate) oneway_spam_detected: bool, #[allow(dead_code)] pub(crate) debug_id: usize, } @@ -68,7 +67,6 @@ impl Allocation { offset: usize, size: usize, ptr: usize, - oneway_spam_detected: bool, ) -> Self { Self { process, @@ -76,7 +74,6 @@ impl Allocation { size, ptr, debug_id, - oneway_spam_detected, allocation_info: None, free_on_drop: true, } diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs index 9921827267d0d..c6a834071f8ce 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -12,7 +12,7 @@ pub(crate) type BinderResult = core::result::Result; /// errno. pub(crate) struct BinderError { pub(crate) reply: u32, - source: Option, + pub(crate) source: Option, } impl BinderError { @@ -40,14 +40,6 @@ impl BinderError { pub(crate) fn is_dead(&self) -> bool { self.reply == BR_DEAD_REPLY } - - pub(crate) fn as_errno(&self) -> kernel::ffi::c_int { - self.source.unwrap_or(EINVAL).to_errno() - } - - pub(crate) fn should_pr_warn(&self) -> bool { - self.source.is_some() - } } /// Convert an errno into a `BinderError` and store the errno used to construct it. The errno diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 8ccc9ffbcd90e..d33af8a043676 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -47,6 +47,7 @@ use crate::{ range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs}, stats::BinderStats, thread::{PushWorkRes, Thread}, + transaction::TransactionInfo, BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead, }; @@ -981,16 +982,15 @@ impl Process { self: &Arc, debug_id: usize, size: usize, - is_oneway: bool, - from_pid: i32, + info: &mut TransactionInfo, ) -> BinderResult { use kernel::page::PAGE_SIZE; let mut reserve_new_args = ReserveNewArgs { debug_id, size, - is_oneway, - pid: from_pid, + is_oneway: info.is_oneway(), + pid: info.from_pid, ..ReserveNewArgs::default() }; @@ -1006,13 +1006,13 @@ impl Process { reserve_new_args = alloc_request.make_alloc()?; }; + info.oneway_spam_suspect = new_alloc.oneway_spam_detected; let res = Allocation::new( self.clone(), debug_id, new_alloc.offset, size, addr + new_alloc.offset, - new_alloc.oneway_spam_detected, ); // This allocation will be marked as in use until the `Allocation` is used to free it. @@ -1044,7 +1044,7 @@ impl Process { let mapping = inner.mapping.as_mut()?; let offset = ptr.checked_sub(mapping.address)?; let (size, debug_id, odata) = mapping.alloc.reserve_existing(offset).ok()?; - let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } @@ -1397,8 +1397,7 @@ impl Process { .alloc .take_for_each(|offset, size, debug_id, odata| { let ptr = offset + address; - let mut alloc = - Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 6a2ddf0039c14..93aa98c2a173a 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -19,7 +19,7 @@ use kernel::{ sync::{Arc, SpinLock}, task::Task, types::ARef, - uaccess::UserSlice, + uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, }; @@ -30,7 +30,7 @@ use crate::{ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::Transaction, + transaction::{Transaction, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; @@ -948,13 +948,11 @@ impl Thread { pub(crate) fn copy_transaction_data( &self, to_process: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, debug_id: usize, allow_fds: bool, txn_security_ctx_offset: Option<&mut usize>, ) -> BinderResult { - let trd = &tr.transaction_data; - let is_oneway = trd.flags & TF_ONE_WAY != 0; let mut secctx = if let Some(offset) = txn_security_ctx_offset { let secid = self.process.cred.get_secid(); let ctx = match security::SecurityCtx::from_secid(secid) { @@ -969,10 +967,10 @@ impl Thread { None }; - let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?; + let data_size = info.data_size; let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?; - let offsets_size: usize = trd.offsets_size.try_into().map_err(|_| EINVAL)?; - let buffers_size: usize = tr.buffers_size.try_into().map_err(|_| EINVAL)?; + let offsets_size = info.offsets_size; + let buffers_size = info.buffers_size; let aligned_secctx_size = match secctx.as_ref() { Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?, None => 0, @@ -995,32 +993,25 @@ impl Thread { size_of::(), ); let secctx_off = aligned_data_size + offsets_size + buffers_size; - let mut alloc = - match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!( - "Failed to allocate buffer. len:{}, is_oneway:{}", - len, - is_oneway - ); - return Err(err); - } - }; + let mut alloc = match to_process.buffer_alloc(debug_id, len, info) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!( + "Failed to allocate buffer. len:{}, is_oneway:{}", + len, + info.is_oneway(), + ); + return Err(err); + } + }; - // SAFETY: This accesses a union field, but it's okay because the field's type is valid for - // all bit-patterns. - let trd_data_ptr = unsafe { &trd.data.ptr }; - let mut buffer_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader(); + let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader(); let mut end_of_previous_object = 0; let mut sg_state = None; // Copy offsets if there are any. if offsets_size > 0 { - let mut offsets_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size) - .reader(); + let mut offsets_reader = UserSlice::new(info.offsets_ptr, offsets_size).reader(); let offsets_start = aligned_data_size; let offsets_end = aligned_data_size + offsets_size; @@ -1194,37 +1185,92 @@ impl Thread { } } - fn transaction(self: &Arc, tr: &BinderTransactionDataSg, inner: T) - where - T: FnOnce(&Arc, &BinderTransactionDataSg) -> BinderResult, - { - if let Err(err) = inner(self, tr) { - if err.should_pr_warn() { - let mut ee = self.inner.lock().extended_error; - ee.command = err.reply; - ee.param = err.as_errno(); - pr_warn!( - "Transaction failed: {:?} my_pid:{}", - err, - self.process.pid_in_current_ns() - ); + // No inlining avoids allocating stack space for `BinderTransactionData` for the entire + // duration of `transaction()`. + #[inline(never)] + fn read_transaction_info( + &self, + cmd: u32, + reader: &mut UserSliceReader, + info: &mut TransactionInfo, + ) -> Result<()> { + let td = match cmd { + BC_TRANSACTION | BC_REPLY => { + reader.read::()?.with_buffers_size(0) + } + BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::()?, + _ => return Err(EINVAL), + }; + + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + let trd_data_ptr = unsafe { &td.transaction_data.data.ptr }; + + info.is_reply = matches!(cmd, BC_REPLY | BC_REPLY_SG); + info.from_pid = self.process.task.pid(); + info.from_tid = self.id; + info.code = td.transaction_data.code; + info.flags = td.transaction_data.flags; + info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize); + info.data_size = td.transaction_data.data_size as usize; + info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize); + info.offsets_size = td.transaction_data.offsets_size as usize; + info.buffers_size = td.buffers_size as usize; + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + info.target_handle = unsafe { td.transaction_data.target.handle }; + Ok(()) + } + + #[inline(never)] + fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Result<()> { + let mut info = TransactionInfo::zeroed(); + self.read_transaction_info(cmd, reader, &mut info)?; + + let ret = if info.is_reply { + self.reply_inner(&mut info) + } else if info.is_oneway() { + self.oneway_transaction_inner(&mut info) + } else { + self.transaction_inner(&mut info) + }; + + if let Err(err) = ret { + if err.reply != BR_TRANSACTION_COMPLETE { + info.reply = err.reply; } self.push_return_work(err.reply); + if let Some(source) = &err.source { + info.errno = source.to_errno(); + info.reply = err.reply; + + { + let mut ee = self.inner.lock().extended_error; + ee.command = err.reply; + ee.param = source.to_errno(); + } + + pr_warn!( + "{}:{} transaction to {} failed: {source:?}", + info.from_pid, + info.from_tid, + info.to_pid + ); + } } + + Ok(()) } - fn transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: Handle's type has no invalid bit patterns. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; // TODO: We need to ensure that there isn't a pending transaction in the work queue. How // could this happen? let top = self.top_of_transaction_stack()?; let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let completion = list_completion.clone_arc(); - let transaction = Transaction::new(node_ref, top, self, tr)?; + let transaction = Transaction::new(node_ref, top, self, info)?; // Check that the transaction stack hasn't changed while the lock was released, then update // it with the new transaction. @@ -1240,7 +1286,7 @@ impl Thread { inner.push_work_deferred(list_completion); } - if let Err(e) = transaction.submit() { + if let Err(e) = transaction.submit(info) { completion.skip(); // Define `transaction` first to drop it after `inner`. let transaction; @@ -1253,18 +1299,21 @@ impl Thread { } } - fn reply_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { + fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { let orig = self.inner.lock().pop_transaction_to_reply(self)?; if !orig.from.is_current_transaction(&orig) { return Err(EINVAL.into()); } + info.to_tid = orig.from.id; + info.to_pid = orig.from.process.task.pid(); + // We need to complete the transaction even if we cannot complete building the reply. let out = (|| -> BinderResult<_> { let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let process = orig.from.process.clone(); let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; - let reply = Transaction::new_reply(self, process, tr, allow_fds)?; + let reply = Transaction::new_reply(self, process, info, allow_fds)?; self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig); Ok(()) @@ -1285,16 +1334,12 @@ impl Thread { out } - fn oneway_transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: The `handle` field is valid for all possible byte values, so reading from the - // union is okay. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn oneway_transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; - let transaction = Transaction::new(node_ref, None, self, tr)?; - let code = if self.process.is_oneway_spam_detection_enabled() - && transaction.oneway_spam_detected - { + let transaction = Transaction::new(node_ref, None, self, info)?; + let code = if self.process.is_oneway_spam_detection_enabled() && info.oneway_spam_suspect { BR_ONEWAY_SPAM_SUSPECT } else { BR_TRANSACTION_COMPLETE @@ -1302,7 +1347,7 @@ impl Thread { let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?; let completion = list_completion.clone_arc(); self.inner.lock().push_work(list_completion); - match transaction.submit() { + match transaction.submit(info) { Ok(()) => Ok(()), Err(err) => { completion.skip(); @@ -1323,29 +1368,8 @@ impl Thread { GLOBAL_STATS.inc_bc(cmd); self.process.stats.inc_bc(cmd); match cmd { - BC_TRANSACTION => { - let tr = reader.read::()?.with_buffers_size(0); - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_TRANSACTION_SG => { - let tr = reader.read::()?; - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_REPLY => { - let tr = reader.read::()?.with_buffers_size(0); - self.transaction(&tr, Self::reply_inner) - } - BC_REPLY_SG => { - let tr = reader.read::()?; - self.transaction(&tr, Self::reply_inner) + BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_SG => { + self.transaction(cmd, &mut reader)?; } BC_FREE_BUFFER => { let buffer = self.process.buffer_get(reader.read()?); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 50bd1cee0f1a3..733f452d8f49d 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -8,7 +8,7 @@ use kernel::{ seq_file::SeqFile, seq_print, sync::{Arc, SpinLock}, - task::Kuid, + task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, }; @@ -24,6 +24,33 @@ use crate::{ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; +#[derive(Zeroable)] +pub(crate) struct TransactionInfo { + pub(crate) from_pid: Pid, + pub(crate) from_tid: Pid, + pub(crate) to_pid: Pid, + pub(crate) to_tid: Pid, + pub(crate) code: u32, + pub(crate) flags: u32, + pub(crate) data_ptr: UserPtr, + pub(crate) data_size: usize, + pub(crate) offsets_ptr: UserPtr, + pub(crate) offsets_size: usize, + pub(crate) buffers_size: usize, + pub(crate) target_handle: u32, + pub(crate) errno: i32, + pub(crate) reply: u32, + pub(crate) oneway_spam_suspect: bool, + pub(crate) is_reply: bool, +} + +impl TransactionInfo { + #[inline] + pub(crate) fn is_oneway(&self) -> bool { + self.flags & TF_ONE_WAY != 0 + } +} + #[pin_data(PinnedDrop)] pub(crate) struct Transaction { pub(crate) debug_id: usize, @@ -41,7 +68,6 @@ pub(crate) struct Transaction { data_address: usize, sender_euid: Kuid, txn_security_ctx_off: Option, - pub(crate) oneway_spam_detected: bool, start_time: Instant, } @@ -54,17 +80,16 @@ impl Transaction { node_ref: NodeRef, from_parent: Option>, from: &Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; let to = node_ref.node.owner.clone(); let mut alloc = match from.copy_transaction_data( to.clone(), - tr, + info, debug_id, allow_fds, txn_security_ctx_off.as_mut(), @@ -77,15 +102,14 @@ impl Transaction { return Err(err); } }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_ONE_WAY != 0 { + if info.is_oneway() { if from_parent.is_some() { pr_warn!("Oneway transaction should not be in a transaction stack."); return Err(EINVAL.into()); } alloc.set_info_oneway_node(node_ref.node.clone()); } - if trd.flags & TF_CLEAR_BUF != 0 { + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } let target_node = node_ref.node.clone(); @@ -99,15 +123,14 @@ impl Transaction { sender_euid: from.process.task.euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: AtomicBool::new(false), txn_security_ctx_off, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -115,21 +138,19 @@ impl Transaction { pub(crate) fn new_reply( from: &Arc, to: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; - let mut alloc = match from.copy_transaction_data(to.clone(), tr, debug_id, allow_fds, None) - { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!("Failure in copy_transaction_data: {:?}", err); - return Err(err); - } - }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_CLEAR_BUF != 0 { + let mut alloc = + match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!("Failure in copy_transaction_data: {:?}", err); + return Err(err); + } + }; + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { @@ -139,15 +160,14 @@ impl Transaction { sender_euid: from.process.task.euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address: alloc.ptr, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: AtomicBool::new(false), txn_security_ctx_off: None, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -237,7 +257,7 @@ impl Transaction { /// stack, otherwise uses the destination process. /// /// Not used for replies. - pub(crate) fn submit(self: DLArc) -> BinderResult { + pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderResult { // Defined before `process_inner` so that the destructor runs after releasing the lock. let _t_outdated; let _oneway_node; @@ -295,6 +315,7 @@ impl Transaction { } let res = if let Some(thread) = self.find_target_thread() { + info.to_tid = thread.id; match thread.push_work(self) { PushWorkRes::Ok => Ok(()), PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)), diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index a8fb4764185a1..2035c2877a2b2 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -21,6 +21,9 @@ use core::mem::{size_of, MaybeUninit}; #[derive(Copy, Clone)] pub struct UserPtr(*mut c_void); +// SAFETY: Null pointer is a valid value. +unsafe impl Zeroable for UserPtr {} + impl UserPtr { /// Create a `UserPtr` from an integer representing the userspace address. #[inline] From bd818dcf4783ef5b924732f0ec52d931b94459bf Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 14 Jul 2026 06:45:54 +0000 Subject: [PATCH 0435/3297] rust_binder: fix BINDER_GET_EXTENDED_ERROR [ Upstream commit 77bfebf110773f5a0d6b5ff8110896adb2c9c335 ] This code currently copies the ExtendedError struct to the stack, modifies the copy, and then doesn't modify the original. Thus, fix it. Furthermore, errors when replying must be delivered directly to the remote thread, so update deliver_reply() to take an extended error argument. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Signed-off-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260605-set-extended-error-v3-1-d60b69a75f97@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Alice Ryhl Signed-off-by: Sasha Levin --- drivers/android/binder/error.rs | 13 +++--- drivers/android/binder/thread.rs | 65 +++++++++++++++++++-------- drivers/android/binder/transaction.rs | 15 +++---- 3 files changed, 58 insertions(+), 35 deletions(-) diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs index c6a834071f8ce..fd9e9d9e5658b 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -72,20 +72,17 @@ impl core::fmt::Debug for BinderError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self.reply { BR_FAILED_REPLY => match self.source.as_ref() { - Some(source) => f - .debug_struct("BR_FAILED_REPLY") - .field("source", source) - .finish(), + Some(source) => source.fmt(f), None => f.pad("BR_FAILED_REPLY"), }, BR_DEAD_REPLY => f.pad("BR_DEAD_REPLY"), BR_FROZEN_REPLY => f.pad("BR_FROZEN_REPLY"), BR_TRANSACTION_PENDING_FROZEN => f.pad("BR_TRANSACTION_PENDING_FROZEN"), BR_TRANSACTION_COMPLETE => f.pad("BR_TRANSACTION_COMPLETE"), - _ => f - .debug_struct("BinderError") - .field("reply", &self.reply) - .finish(), + _ => match self.source.as_ref() { + Some(source) => source.fmt(f), + None => self.reply.fmt(f), + }, } } } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 93aa98c2a173a..ad8bdf762ddc6 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -498,9 +498,16 @@ impl Thread { Ok(()) } + pub(crate) fn clear_extended_error(&self, debug_id: usize) { + self.inner.lock().extended_error = ExtendedError::new(debug_id as u32, BR_OK, 0); + } + pub(crate) fn get_extended_error(&self, data: UserSlice) -> Result { let mut writer = data.writer(); - let ee = self.inner.lock().extended_error; + let mut inner = self.inner.lock(); + let ee = inner.extended_error; + inner.extended_error = ExtendedError::new(0, BR_OK, 0); + drop(inner); writer.write(&ee)?; Ok(()) } @@ -1105,7 +1112,10 @@ impl Thread { inner.pop_transaction_to_reply(thread.as_ref()) } { let reply = Err(BR_DEAD_REPLY); - if !transaction.from.deliver_single_reply(reply, &transaction) { + if !transaction + .from + .deliver_single_reply(reply, &transaction, None) + { break; } @@ -1117,8 +1127,9 @@ impl Thread { &self, reply: Result, u32>, transaction: &DArc, + extended_error: Option, ) { - if self.deliver_single_reply(reply, transaction) { + if self.deliver_single_reply(reply, transaction, extended_error) { transaction.from.unwind_transaction_stack(); } } @@ -1132,6 +1143,7 @@ impl Thread { &self, reply: Result, u32>, transaction: &DArc, + extended_error: Option, ) -> bool { if let Ok(transaction) = &reply { transaction.set_outstanding(&mut self.process.inner.lock()); @@ -1147,6 +1159,12 @@ impl Thread { return true; } + if let Some(ee) = extended_error { + if inner.extended_error.command == BR_OK { + inner.extended_error = ee; + } + } + match reply { Ok(work) => { inner.push_work(work); @@ -1217,6 +1235,9 @@ impl Thread { info.buffers_size = td.buffers_size as usize; // SAFETY: Above `read` call initializes all bytes, so this union read is ok. info.target_handle = unsafe { td.transaction_data.target.handle }; + + info.debug_id = super::next_debug_id(); + Ok(()) } @@ -1225,6 +1246,8 @@ impl Thread { let mut info = TransactionInfo::zeroed(); self.read_transaction_info(cmd, reader, &mut info)?; + self.clear_extended_error(info.debug_id); + let ret = if info.is_reply { self.reply_inner(&mut info) } else if info.is_oneway() { @@ -1234,23 +1257,21 @@ impl Thread { }; if let Err(err) = ret { - if err.reply != BR_TRANSACTION_COMPLETE { - info.reply = err.reply; - } - self.push_return_work(err.reply); - if let Some(source) = &err.source { - info.errno = source.to_errno(); + if err.reply != BR_TRANSACTION_COMPLETE { info.reply = err.reply; + if let Some(source) = &err.source { + info.errno = source.to_errno(); - { - let mut ee = self.inner.lock().extended_error; - ee.command = err.reply; - ee.param = source.to_errno(); + { + let mut inner = self.inner.lock(); + inner.extended_error = + ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); + } } pr_warn!( - "{}:{} transaction to {} failed: {source:?}", + "{}:{} transaction to {} failed: {err:?}", info.from_pid, info.from_tid, info.to_pid @@ -1315,18 +1336,24 @@ impl Thread { let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; let reply = Transaction::new_reply(self, process, info, allow_fds)?; self.inner.lock().push_work(completion); - orig.from.deliver_reply(Ok(reply), &orig); + orig.from.deliver_reply(Ok(reply), &orig, None); Ok(()) })() .map_err(|mut err| { // At this point we only return `BR_TRANSACTION_COMPLETE` to the caller, and we must let // the sender know that the transaction has completed (with an error in this case). + pr_warn!( - "Failure {:?} during reply - delivering BR_FAILED_REPLY to sender.", - err + "{}:{} reply to {} failed: {err:?}", + info.from_pid, + info.from_tid, + info.to_pid ); - let reply = Err(BR_FAILED_REPLY); - orig.from.deliver_reply(reply, &orig); + + let param = err.source.as_ref().map_or(0, |e| e.to_errno()); + let ee = ExtendedError::new(info.debug_id as u32, err.reply, param); + orig.from + .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee)); err.reply = BR_TRANSACTION_COMPLETE; err }); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 733f452d8f49d..b5434a2ae8193 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -42,6 +42,7 @@ pub(crate) struct TransactionInfo { pub(crate) reply: u32, pub(crate) oneway_spam_suspect: bool, pub(crate) is_reply: bool, + pub(crate) debug_id: usize, } impl TransactionInfo { @@ -82,7 +83,6 @@ impl Transaction { from: &Arc, info: &mut TransactionInfo, ) -> BinderResult> { - let debug_id = super::next_debug_id(); let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; @@ -90,7 +90,7 @@ impl Transaction { let mut alloc = match from.copy_transaction_data( to.clone(), info, - debug_id, + info.debug_id, allow_fds, txn_security_ctx_off.as_mut(), ) { @@ -117,7 +117,7 @@ impl Transaction { let data_address = alloc.ptr; Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { - debug_id, + debug_id: info.debug_id, target_node: Some(target_node), from_parent, sender_euid: from.process.task.euid(), @@ -141,9 +141,8 @@ impl Transaction { info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { - let debug_id = super::next_debug_id(); let mut alloc = - match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) { + match from.copy_transaction_data(to.clone(), info, info.debug_id, allow_fds, None) { Ok(alloc) => alloc, Err(err) => { pr_warn!("Failure in copy_transaction_data: {:?}", err); @@ -154,7 +153,7 @@ impl Transaction { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { - debug_id, + debug_id: info.debug_id, target_node: None, from_parent: None, sender_euid: from.process.task.euid(), @@ -380,7 +379,7 @@ impl DeliverToRead for Transaction { let send_failed_reply = ScopeGuard::new(|| { if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { let reply = Err(BR_FAILED_REPLY); - self.from.deliver_reply(reply, &self); + self.from.deliver_reply(reply, &self, None); } self.drop_outstanding_txn(); }); @@ -462,7 +461,7 @@ impl DeliverToRead for Transaction { // If this is not a reply or oneway transaction, then send a dead reply. if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 { let reply = Err(BR_DEAD_REPLY); - self.from.deliver_reply(reply, &self); + self.from.deliver_reply(reply, &self, None); } self.drop_outstanding_txn(); From 8ff183ee4d8c452960df58175a094828c0513b2e Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Mon, 13 Jul 2026 18:11:37 -0700 Subject: [PATCH 0436/3297] bpf: Support for hardening against JIT spraying commit 96cce16e26dd02a8678f1e87f88a4b5cdb63b995 upstream. The BPF JIT allocator packs many small programs into larger executable allocations and reuses space within those allocations as programs are loaded and freed. When fresh code is written into space that a previous program occupied, an indirect jump into the new program can reuse a branch prediction left behind by the old one. Flush the indirect branch predictors before reusing JIT memory so that indirect jumps into a newly written program don't reuse predictions from an old program that occupied the same space. Introduce bpf_arch_pred_flush_enabled static key and bpf_arch_pred_flush static call for flushing the branch predictors on JIT memory reuse. Architectures that need a flush, can update it to a predictor flush function. By default, its a NOP and does not emit any CALL. Allocations larger than a pack are not covered by this flush. That is safe because cBPF programs (the unprivileged attack surface) are bounded well below a pack size. Issue a warning if this assumption is ever violated while the flush is active. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- include/linux/filter.h | 10 ++++++++++ kernel/bpf/core.c | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/linux/filter.h b/include/linux/filter.h index cf7a0bce1bb6d..3c4dd718beced 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -1266,6 +1267,15 @@ extern long bpf_jit_limit_max; typedef void (*bpf_jit_fill_hole_t)(void *area, unsigned int size); +/* + * Flush the indirect branch predictors before reusing JIT memory, so that + * indirect jumps into a newly written program don't reuse predictions left + * behind by an old program that occupied the same space. + */ +void bpf_arch_pred_flush(void); +DECLARE_STATIC_CALL(bpf_arch_pred_flush, bpf_arch_pred_flush); +DECLARE_STATIC_KEY_FALSE(bpf_pred_flush_enabled); + void bpf_jit_fill_hole_with_zero(void *area, unsigned int size); struct bpf_binary_header * diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 931a4ddd8530c..a043fccb917e8 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -864,6 +865,15 @@ void bpf_jit_fill_hole_with_zero(void *area, unsigned int size) memset(area, 0, size); } +DEFINE_STATIC_CALL_NULL(bpf_arch_pred_flush, bpf_arch_pred_flush); + +/* + * Enabled once bpf_arch_pred_flush points at a real flush routine. Lets the + * pack allocator test "is a predictor flush wired up at all" with a cheap + * static branch instead of repeatedly querying the static call target. + */ +DEFINE_STATIC_KEY_FALSE(bpf_pred_flush_enabled); + #define BPF_PROG_SIZE_TO_NBITS(size) (round_up(size, BPF_PROG_CHUNK_SIZE) / BPF_PROG_CHUNK_SIZE) static DEFINE_MUTEX(pack_mutex); @@ -923,6 +933,14 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) mutex_lock(&pack_mutex); if (size > BPF_PROG_PACK_SIZE) { + /* + * Allocations larger than a pack get their own pages, and + * predictors are not flushed for such allocation. This is only + * safe because cBPF programs (the unprivileged attack surface) + * are bounded well below a pack size. + */ + if (static_branch_unlikely(&bpf_pred_flush_enabled)) + pr_warn_once("BPF: Predictors not flushed for allocations greater than BPF_PROG_PACK_SIZE\n"); size = round_up(size, PAGE_SIZE); ptr = bpf_jit_alloc_exec(size); if (ptr) { @@ -953,6 +971,7 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) pos = 0; found_free_area: + static_call_cond(bpf_arch_pred_flush)(); bitmap_set(pack->bitmap, pos, nbits); ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT); From 8a4c8af9ae67eb072d90d1b339f14d27a82bd2a1 Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Mon, 13 Jul 2026 18:11:52 -0700 Subject: [PATCH 0437/3297] x86/bugs: Enable IBPB flush on BPF JIT allocation commit a3af84b0fa00ead01fcd0e28b5d773ff25990a0d upstream. Enable hardening against JIT spraying when Spectre-v2 mitigations are in use. Specifically, issue an IBPB flush on BPF JIT memory reuse. Skip enabling the IBPB flush if the BPF dispatcher is already using a retpoline sequence. This hardening applies only when BPF-JIT is in use. Guard the enabling under CONFIG_BPF_JIT so that bugs.c still builds with CONFIG_BPF_JIT=n. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Acked-by: Dave Hansen Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- arch/x86/include/asm/nospec-branch.h | 4 +++ arch/x86/kernel/cpu/bugs.c | 50 +++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index 08ed5a2e46a5f..71e9861a16a89 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -386,6 +386,10 @@ extern void srso_alias_return_thunk(void); extern void entry_untrain_ret(void); extern void write_ibpb(void); +#ifdef CONFIG_BPF_JIT +extern void bpf_arch_ibpb(void); +#endif + #ifdef CONFIG_X86_64 extern void clear_bhb_loop(void); #endif diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index d7fa03bf51b45..fd0b7880cf7e5 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1796,8 +1797,21 @@ static inline const char *spectre_v2_module_string(void) { return spectre_v2_bad_module ? " - vulnerable module loaded" : ""; } + +/* + * The "retpoline sequence" is the "call;mov;ret" sequence that + * replaces normal indirect branch instructions. Differentiate + * *the* retpoline sequence from the LFENCE-prefixed indirect + * branches that simply use the retpoline infrastructure. + */ +static inline bool retpoline_seq_enabled(void) +{ + return boot_cpu_has(X86_FEATURE_RETPOLINE) && !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE); +} + #else static inline const char *spectre_v2_module_string(void) { return ""; } +static inline bool retpoline_seq_enabled(void) { return false; } #endif #define SPECTRE_V2_LFENCE_MSG "WARNING: LFENCE mitigation is not recommended for this CPU, data leaks possible!\n" @@ -2240,8 +2254,7 @@ static void __init bhi_apply_mitigation(void) return; /* Retpoline mitigates against BHI unless the CPU has RRSBA behavior */ - if (boot_cpu_has(X86_FEATURE_RETPOLINE) && - !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE)) { + if (retpoline_seq_enabled()) { spec_ctrl_disable_kernel_rrsba(); if (rrsba_disabled) return; @@ -2383,6 +2396,27 @@ static void __init spectre_v2_update_mitigation(void) pr_info("%s\n", spectre_v2_strings[spectre_v2_enabled]); } +#ifdef CONFIG_BPF_JIT +static void __bpf_arch_ibpb(void *unused) +{ + write_ibpb(); +} + +void bpf_arch_ibpb(void) +{ + on_each_cpu(__bpf_arch_ibpb, NULL, 1); +} + +static bool __init cpu_wants_ibpb_bpf(void) +{ + /* A genuine retpoline already neutralizes ring0 indirect predictions */ + if (retpoline_seq_enabled()) + return false; + + return boot_cpu_has(X86_FEATURE_IBPB); +} +#endif + static void __init spectre_v2_apply_mitigation(void) { if (spectre_v2_enabled == SPECTRE_V2_EIBRS && unprivileged_ebpf_enabled()) @@ -2459,6 +2493,14 @@ static void __init spectre_v2_apply_mitigation(void) setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW); pr_info("Enabling Restricted Speculation for firmware calls\n"); } + +#ifdef CONFIG_BPF_JIT + if (cpu_wants_ibpb_bpf()) { + static_call_update(bpf_arch_pred_flush, bpf_arch_ibpb); + static_branch_enable(&bpf_pred_flush_enabled); + pr_info("Enabling IBPB for BPF\n"); + } +#endif } static void update_stibp_msr(void * __unused) @@ -3544,9 +3586,7 @@ static const char *spectre_bhi_state(void) return "; BHI: BHI_DIS_S"; else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_LOOP)) return "; BHI: SW loop, KVM: SW loop"; - else if (boot_cpu_has(X86_FEATURE_RETPOLINE) && - !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE) && - rrsba_disabled) + else if (retpoline_seq_enabled() && rrsba_disabled) return "; BHI: Retpoline"; else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_VMEXIT)) return "; BHI: Vulnerable, KVM: SW loop"; From 666fc2e6e4d0a37592e5a3029deddc272e10df1e Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Mon, 13 Jul 2026 18:12:07 -0700 Subject: [PATCH 0438/3297] bpf: Restrict JIT predictor flush to cBPF commit 0bb99f2cfaae6822d734d69722de30af823efdf3 upstream. Currently predictor flush on memory reuse is done for all BPF JIT allocations, but only cBPF programs can be loaded by an unprivileged user. eBPF is privileged by default, and flushing predictors for all CPUs on every eBPF reuse penalizes the common case for no security benefit. eBPF allocations can be frequent on busy systems, only flush predictors for cBPF programs. Trampoline and dispatcher allocations also skip the flush as they are eBPF-only. [pawan: backport had various conflicts in arches bpf_int_jit_compile(). loongarch bpf_int_jit_compile() doesn't use pack allocator, dropped was_classic hunk. ] Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- arch/arm64/net/bpf_jit_comp.c | 4 ++-- arch/loongarch/net/bpf_jit.c | 2 +- arch/powerpc/net/bpf_jit_comp.c | 4 ++-- arch/riscv/net/bpf_jit_comp64.c | 2 +- arch/riscv/net/bpf_jit_core.c | 3 ++- arch/x86/net/bpf_jit_comp.c | 5 +++-- include/linux/filter.h | 5 +++-- kernel/bpf/core.c | 13 ++++++++----- kernel/bpf/dispatcher.c | 2 +- 9 files changed, 23 insertions(+), 17 deletions(-) diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 873c1b784a872..c563bae8fce1c 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -2117,7 +2117,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) image_size = extable_offset + extable_size; ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u64), &header, &image_ptr, - jit_fill_hole); + jit_fill_hole, was_classic); if (!ro_header) { prog = orig_prog; goto out_off; @@ -2754,7 +2754,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, jit_fill_hole); + return bpf_prog_pack_alloc(size, jit_fill_hole, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index e9d666508ae28..a000620ca99da 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -1474,7 +1474,7 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, jit_fill_hole); + return bpf_prog_pack_alloc(size, jit_fill_hole, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 189ef7b72081d..cedab25aa3a82 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -246,7 +246,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) alloclen = proglen + FUNCTION_DESCR_SIZE + fixup_len + extable_len; fhdr = bpf_jit_binary_pack_alloc(alloclen, &fimage, 4, &hdr, &image, - bpf_jit_fill_ill_insns); + bpf_jit_fill_ill_insns, bpf_prog_was_classic(fp)); if (!fhdr) { fp = org_fp; goto out_addrs; @@ -468,7 +468,7 @@ bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena) void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, bpf_jit_fill_ill_insns); + return bpf_prog_pack_alloc(size, bpf_jit_fill_ill_insns, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index 45cbc7c6fe490..9e9e6dcfc4825 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -1268,7 +1268,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, bpf_fill_ill_insns); + return bpf_prog_pack_alloc(size, bpf_fill_ill_insns, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c index 191bf0e66c824..e4ab5bb9c9f64 100644 --- a/arch/riscv/net/bpf_jit_core.c +++ b/arch/riscv/net/bpf_jit_core.c @@ -125,7 +125,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) bpf_jit_binary_pack_alloc(prog_size + extable_size, &jit_data->ro_image, sizeof(u32), &jit_data->header, &jit_data->image, - bpf_fill_ill_insns); + bpf_fill_ill_insns, + bpf_prog_was_classic(prog)); if (!jit_data->ro_header) { prog = orig_prog; goto out_offset; diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 788671a32d8ee..c3798cab3b7db 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -3447,7 +3447,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im void *arch_alloc_bpf_trampoline(unsigned int size) { - return bpf_prog_pack_alloc(size, jit_fill_hole); + return bpf_prog_pack_alloc(size, jit_fill_hole, false); } void arch_free_bpf_trampoline(void *image, unsigned int size) @@ -3780,7 +3780,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) /* allocate module memory for x86 insns and extable */ header = bpf_jit_binary_pack_alloc(roundup(proglen, align) + extable_size, &image, align, &rw_header, &rw_image, - jit_fill_hole); + jit_fill_hole, + bpf_prog_was_classic(prog)); if (!header) { prog = orig_prog; goto out_addrs; diff --git a/include/linux/filter.h b/include/linux/filter.h index 3c4dd718beced..2469fd2e40157 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1290,7 +1290,7 @@ void bpf_jit_free(struct bpf_prog *fp); struct bpf_binary_header * bpf_jit_binary_pack_hdr(const struct bpf_prog *fp); -void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns); +void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic); void bpf_prog_pack_free(void *ptr, u32 size); static inline bool bpf_prog_kallsyms_verify_off(const struct bpf_prog *fp) @@ -1304,7 +1304,8 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **ro_image, unsigned int alignment, struct bpf_binary_header **rw_hdr, u8 **rw_image, - bpf_jit_fill_hole_t bpf_fill_ill_insns); + bpf_jit_fill_hole_t bpf_fill_ill_insns, + bool was_classic); int bpf_jit_binary_pack_finalize(struct bpf_binary_header *ro_header, struct bpf_binary_header *rw_header); void bpf_jit_binary_pack_free(struct bpf_binary_header *ro_header, diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index a043fccb917e8..338a806e25c04 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -924,7 +924,7 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins return NULL; } -void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) +void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic) { unsigned int nbits = BPF_PROG_SIZE_TO_NBITS(size); struct bpf_prog_pack *pack; @@ -939,7 +939,7 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) * safe because cBPF programs (the unprivileged attack surface) * are bounded well below a pack size. */ - if (static_branch_unlikely(&bpf_pred_flush_enabled)) + if (was_classic && static_branch_unlikely(&bpf_pred_flush_enabled)) pr_warn_once("BPF: Predictors not flushed for allocations greater than BPF_PROG_PACK_SIZE\n"); size = round_up(size, PAGE_SIZE); ptr = bpf_jit_alloc_exec(size); @@ -971,7 +971,9 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) pos = 0; found_free_area: - static_call_cond(bpf_arch_pred_flush)(); + /* Flush only for cBPF as it may contain a crafted gadget */ + if (static_branch_unlikely(&bpf_pred_flush_enabled) && was_classic) + static_call_cond(bpf_arch_pred_flush)(); bitmap_set(pack->bitmap, pos, nbits); ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT); @@ -1131,7 +1133,8 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr, unsigned int alignment, struct bpf_binary_header **rw_header, u8 **rw_image, - bpf_jit_fill_hole_t bpf_fill_ill_insns) + bpf_jit_fill_hole_t bpf_fill_ill_insns, + bool was_classic) { struct bpf_binary_header *ro_header; u32 size, hole, start; @@ -1144,7 +1147,7 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr, if (bpf_jit_charge_modmem(size)) return NULL; - ro_header = bpf_prog_pack_alloc(size, bpf_fill_ill_insns); + ro_header = bpf_prog_pack_alloc(size, bpf_fill_ill_insns, was_classic); if (!ro_header) { bpf_jit_uncharge_modmem(size); return NULL; diff --git a/kernel/bpf/dispatcher.c b/kernel/bpf/dispatcher.c index b77db7413f8c7..ea2d60dc1feeb 100644 --- a/kernel/bpf/dispatcher.c +++ b/kernel/bpf/dispatcher.c @@ -145,7 +145,7 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, mutex_lock(&d->mutex); if (!d->image) { - d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero); + d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero, false); if (!d->image) goto out; d->rw_image = bpf_jit_alloc_exec(PAGE_SIZE); From f1f36bf9bb1173ef4ddb7fb917bbda75fb1db690 Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Mon, 13 Jul 2026 18:12:23 -0700 Subject: [PATCH 0439/3297] bpf: Skip redundant IBPB in pack allocator commit a23c1c5396a91680703360d1ee28a44657c503c4 upstream. bpf_prog_pack_alloc() issues IBPB on all CPUs on every cBPF allocation, even when reusing chunks from an existing pack where no new memory was touched since the last IBPB. Since IBPB on all CPUs is heavy, Dave Hansen suggested to track allocation since last IBPB, and only issue IBPB at reuse for the chunks that have not seen an IBPB since they were last freed. Track per-pack whether an IBPB is needed via arch_flush_needed. Set it when allocating a chunk, reset on IBPB flush. On reuse, conditionally issue the flush. Since IBPB invalidates all BTB entries, clear the flag on all packs after flushing. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 338a806e25c04..49ca543a41115 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -857,6 +857,7 @@ int bpf_jit_add_poke_descriptor(struct bpf_prog *prog, struct bpf_prog_pack { struct list_head list; void *ptr; + bool arch_flush_needed; unsigned long bitmap[]; }; @@ -910,6 +911,8 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins bpf_fill_ill_insns(pack->ptr, BPF_PROG_PACK_SIZE); bitmap_zero(pack->bitmap, BPF_PROG_PACK_SIZE / BPF_PROG_CHUNK_SIZE); + if (static_branch_unlikely(&bpf_pred_flush_enabled)) + pack->arch_flush_needed = true; set_vm_flush_reset_perms(pack->ptr); err = set_memory_rox((unsigned long)pack->ptr, BPF_PROG_PACK_SIZE / PAGE_SIZE); @@ -972,8 +975,15 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool found_free_area: /* Flush only for cBPF as it may contain a crafted gadget */ - if (static_branch_unlikely(&bpf_pred_flush_enabled) && was_classic) + if (static_branch_unlikely(&bpf_pred_flush_enabled) && + pack->arch_flush_needed && + was_classic) { + struct bpf_prog_pack *p; + static_call_cond(bpf_arch_pred_flush)(); + list_for_each_entry(p, &pack_list, list) + p->arch_flush_needed = false; + } bitmap_set(pack->bitmap, pos, nbits); ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT); @@ -1011,6 +1021,9 @@ void bpf_prog_pack_free(void *ptr, u32 size) "bpf_prog_pack bug: missing bpf_arch_text_invalidate?\n"); bitmap_clear(pack->bitmap, pos, nbits); + + if (static_branch_unlikely(&bpf_pred_flush_enabled)) + pack->arch_flush_needed = true; if (bitmap_find_next_zero_area(pack->bitmap, BPF_PROG_CHUNK_COUNT, 0, BPF_PROG_CHUNK_COUNT, 0) == 0) { list_del(&pack->list); From 0229944ba7923a73f54e94e42139518bfff9124c Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Mon, 13 Jul 2026 18:12:38 -0700 Subject: [PATCH 0440/3297] bpf: Prefer packs that won't trigger an IBPB flush on allocation commit a9b1f19a6a673ba06820898d0f1ad02883ea1639 upstream. Currently BPF pack allocator picks the chunks from the first available pack. While this is okay, it naturally leads to more frequent flushes when there are multiple packs in the system that weren't used since the last flush. As an optimization prefer allocating the new programs from packs that are unused since last flush. When all packs are dirty, allocation forces a flush and marks all packs clean. Below are some future optimizations ideas: 1. Currently, the "dirty" tracking is only done at the pack-level. Flush frequency can further be reduced with chunk-level tracking. This requires a new bitmap per-pack to track the dirty state. 2. IBPB flush is done on all CPUs, even if only a single CPU ran the BPF program. On a system with hundreds of CPUs this could be a major bottleneck forcing hundreds of IPIs to deliver the flush. The solution is to track the CPUs where a BPF program ran, and issue IBPB only on those CPUs. 3. Avoid IBPB when flush is already done at other sources (e.g. context switch). Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 49ca543a41115..e0ffdd985eb5d 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -930,8 +930,8 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic) { unsigned int nbits = BPF_PROG_SIZE_TO_NBITS(size); - struct bpf_prog_pack *pack; - unsigned long pos; + struct bpf_prog_pack *pack, *fallback_pack = NULL; + unsigned long pos, fallback_pos = 0; void *ptr = NULL; mutex_lock(&pack_mutex); @@ -963,8 +963,29 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool list_for_each_entry(pack, &pack_list, list) { pos = bitmap_find_next_zero_area(pack->bitmap, BPF_PROG_CHUNK_COUNT, 0, nbits, 0); - if (pos < BPF_PROG_CHUNK_COUNT) + if (pos >= BPF_PROG_CHUNK_COUNT) + continue; + /* Flush not enabled, use any pack */ + if (!static_branch_unlikely(&bpf_pred_flush_enabled)) goto found_free_area; + /* + * cBPF reuse of a dirty pack triggers a flush, so prefer a + * clean pack for cBPF. eBPF never flushes, so pick the first + * free pack, dirty or clean. + */ + if (!was_classic || !pack->arch_flush_needed) + goto found_free_area; + if (!fallback_pack) { + fallback_pack = pack; + fallback_pos = pos; + } + } + + /* No preferred pack found */ + if (fallback_pack) { + pack = fallback_pack; + pos = fallback_pos; + goto found_free_area; } pack = alloc_new_pack(bpf_fill_ill_insns); From d944b8add331883d8e026e152356e7d9349a3bcf Mon Sep 17 00:00:00 2001 From: Pawan Gupta Date: Mon, 13 Jul 2026 18:12:53 -0700 Subject: [PATCH 0441/3297] bpf: Prefer dirty packs for eBPF allocations commit b72e29e0f7ee329d89f86db8700c8ea99b4a370a upstream. The pack allocator only flushes predictors when reusing a dirty pack for cBPF, eBPF allocations never trigger a flush. Currently, eBPF picks the first free pack, which could be a clean pack. As an optimization, leaving a clean pack for cBPF can avoid flushes. Prefer dirty packs for eBPF and keep clean packs free for cBPF. This mirrors the existing cBPF preference for clean packs: each program kind prefers the pack that avoids an extra flush, and falls back to the other kind only when no preferred pack has room. eBPF reuse of a dirty pack is harmless since eBPF being privileged does not flush. Signed-off-by: Pawan Gupta Acked-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index e0ffdd985eb5d..b102704f89bc7 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -970,10 +970,10 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool goto found_free_area; /* * cBPF reuse of a dirty pack triggers a flush, so prefer a - * clean pack for cBPF. eBPF never flushes, so pick the first - * free pack, dirty or clean. + * clean pack for cBPF. eBPF never flushes, so steer it to a + * dirty pack and keep clean packs free for cBPF. */ - if (!was_classic || !pack->arch_flush_needed) + if (was_classic ^ pack->arch_flush_needed) goto found_free_area; if (!fallback_pack) { fallback_pack = pack; From 335202ab25b01fdd45889ff25eab70864686dea3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 10:23:27 -0400 Subject: [PATCH 0442/3297] udf: validate free block extents against the partition length commit 5f0419457f89dce1a3f1c8e62a3adf2f39ab8168 upstream. udf_free_blocks() checks the logical block number and count against the partition length, but drops the extent offset from that final bound. A crafted extent can pass the guard while logicalBlockNum + offset + count points past the partition, which later indexes past the space bitmap array. A single ftruncate(2) on a file backed by such an extent reliably panics the kernel. This is a local availability issue. On desktop systems where UDisks/polkit allows the active user to mount removable UDF media without CAP_SYS_ADMIN, an unprivileged local user can supply the crafted filesystem and trigger the panic by truncating a writable file on it. Systems that require root or CAP_SYS_ADMIN to mount the image have a higher prerequisite. No confidentiality or integrity impact is claimed: the reproduced primitive is an out-of-bounds read of a bitmap pointer slot followed by a kernel panic. Use the already computed logicalBlockNum + offset + count value for the partition length check. Also make load_block_bitmap() reject an out-of-range block group before indexing s_block_bitmap[], so corrupted callers cannot walk past the flexible array. Fixes: 56e69e59751d ("udf: prevent integer overflow in udf_bitmap_free_blocks()") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260515142327.1120767-1-michael.bommarito@gmail.com Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/balloc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 807c493ed0cd5..cc6dc6e1d84de 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -82,8 +82,9 @@ static int load_block_bitmap(struct super_block *sb, int nr_groups = bitmap->s_nr_groups; if (block_group >= nr_groups) { - udf_debug("block_group (%u) > nr_groups (%d)\n", + udf_debug("block_group (%u) >= nr_groups (%d)\n", block_group, nr_groups); + return -EFSCORRUPTED; } if (bitmap->s_block_bitmap[block_group]) { @@ -662,7 +663,7 @@ void udf_free_blocks(struct super_block *sb, struct inode *inode, if (check_add_overflow(bloc->logicalBlockNum, offset, &blk) || check_add_overflow(blk, count, &blk) || - bloc->logicalBlockNum + count > map->s_partition_len) { + blk > map->s_partition_len) { udf_debug("Invalid request to free blocks: (%d, %u), off %u, " "len %u, partition len %u\n", partition, bloc->logicalBlockNum, offset, count, From e610fb113cdfa8bf4247c9bf4f2337b81ad4ddad Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 02:53:31 -0500 Subject: [PATCH 0443/3297] udf: validate VAT header length against the VAT inode size commit d8202786b3d75125c84ebc4de6d946f92fde0ee8 upstream. udf_load_vat() takes the virtual partition's start offset straight from the on-disk VAT 2.0 header without checking it against the VAT inode size: map->s_type_specific.s_virtual.s_start_offset = le16_to_cpu(vat20->lengthHeader); map->s_type_specific.s_virtual.s_num_entries = (sbi->s_vat_inode->i_size - map->s_type_specific.s_virtual.s_start_offset) >> 2; lengthHeader is a fully attacker-controlled 16-bit value. If it exceeds the VAT inode size, the s_num_entries subtraction underflows to a huge count, which defeats the "block > s_num_entries" bound in udf_get_pblock_virt15(); and on the ICB-inline path that function reads ((__le32 *)(iinfo->i_data + s_start_offset))[block] so a large s_start_offset indexes past the inode's in-ICB data. Mounting a crafted UDF image with a virtual (VAT) partition then triggers an out-of-bounds read. Reject a VAT whose header length does not leave room for at least one entry within the VAT inode. Fixes: fa5e08156335 ("udf: Handle VAT packed inside inode properly") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260612-b4-disp-9a2317ee-v1-1-fefef5736154@proton.me Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/super.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/udf/super.c b/fs/udf/super.c index b2f168b0a0d18..4a325af4ba9c2 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1263,6 +1263,14 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) map->s_type_specific.s_virtual.s_start_offset = le16_to_cpu(vat20->lengthHeader); + if (map->s_type_specific.s_virtual.s_start_offset + > sbi->s_vat_inode->i_size) { + udf_err(sb, "Corrupted VAT header length %u (VAT inode size %lld)\n", + map->s_type_specific.s_virtual.s_start_offset, + sbi->s_vat_inode->i_size); + brelse(bh); + return -EFSCORRUPTED; + } map->s_type_specific.s_virtual.s_num_entries = (sbi->s_vat_inode->i_size - map->s_type_specific.s_virtual. From 04f4599a9efb90992d072a814960edf0cd62805d Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 01:40:01 -0500 Subject: [PATCH 0444/3297] udf: validate sparing table length as an entry count, not a byte count commit 3ec997bd5508e9b25210b5bbec89031629cdb093 upstream. udf_load_sparable_map() accepts a sparing table when sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > sb->s_blocksize is false, i.e. it treats reallocationTableLen as a number of BYTES that must fit in the block. But the table is walked as an array of 8-byte sparingEntry elements: for (i = 0; i < le16_to_cpu(st->reallocationTableLen); i++) { struct sparingEntry *entry = &st->mapEntry[i]; ... entry->origLocation ... } in udf_get_pblock_spar15() and udf_relocate_blocks(). A reallocationTableLen of N therefore passes the check whenever sizeof(*st) + N <= blocksize, yet the consumers index sizeof(*st) + N * sizeof(struct sparingEntry) bytes -- up to ~8x the block. On a crafted UDF image this is an out-of-bounds read in udf_get_pblock_spar15(); udf_relocate_blocks() additionally feeds the same length to udf_update_tag(), whose crc_itu_t() reads far past the block, and its memmove() through st->mapEntry[] is an out-of-bounds write. Validate reallocationTableLen as the entry count it is, with struct_size(). Fixes: 1df2ae31c724 ("udf: Fortify loading of sparing table") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260612-b4-disp-91780c4e-v1-1-f15112ff6882@proton.me Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/udf/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 4a325af4ba9c2..62a311d88f2d6 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1426,7 +1426,8 @@ static int udf_load_sparable_map(struct super_block *sb, if (ident != 0 || strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING)) || - sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > + struct_size(st, mapEntry, + le16_to_cpu(st->reallocationTableLen)) > sb->s_blocksize) { brelse(bh); continue; From c60932d6f8373268d0393053c45c522263feeed8 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 3 Jun 2026 11:03:27 +0000 Subject: [PATCH 0445/3297] hwrng: jh7110 - fix refcount leak in starfive_trng_read() commit 8d13f7a8450206e3f820cdb26e33e91d181071b4 upstream. The starfive_trng_read() function acquires a runtime PM reference via pm_runtime_get_sync() but fails to release it on two error paths. If starfive_trng_wait_idle() or starfive_trng_cmd() returns an error, the function exits without calling pm_runtime_put_sync_autosuspend(), leaving the runtime PM usage counter permanently elevated and preventing the device from entering runtime suspend. Refactor the function to use a unified error path that calls pm_runtime_put_sync_autosuspend() before returning. Cc: stable@vger.kernel.org Fixes: c388f458bc34 ("hwrng: starfive - Add TRNG driver for StarFive SoC") Signed-off-by: Wentao Liang Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/jh7110-trng.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c index 9776f4daa0445..4712c3c530e42 100644 --- a/drivers/char/hw_random/jh7110-trng.c +++ b/drivers/char/hw_random/jh7110-trng.c @@ -256,19 +256,22 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai if (wait) { ret = starfive_trng_wait_idle(trng); - if (ret) - return -ETIMEDOUT; + if (ret) { + ret = -ETIMEDOUT; + goto out_put; + } } ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait); if (ret) - return ret; + goto out_put; memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max); + ret = max; +out_put: pm_runtime_put_sync_autosuspend(trng->dev); - - return max; + return ret; } static int starfive_trng_probe(struct platform_device *pdev) From d161d47aba31dc99bbf6d1788c316a1b06ae89b0 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 28 Apr 2026 12:14:32 +0200 Subject: [PATCH 0446/3297] crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A commit ea5e57cc97185329dcc5ebdcaae7e1500bf0ad0b upstream. Commit 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to lowest possible") reduced the hwrng quality to 1 based on a review by Bill Cox [1]. However, despite its title, the review only tested the ATSHA204, not the ATSHA204A. In the same thread, Atmel engineer Landon Cox wrote "this behavior has been eliminated entirely"[2] in the ATSHA204A and "this problem does not affect the ATECC108 or the ATECC108A (or the ATSHA204A)"[3]. According to the official ATSHA204A datasheet [4], the device contains a high-quality hardware RNG that combines its output with an internal seed value stored in EEPROM or SRAM to generate random numbers. The device also implements all security functions using SHA-256, and the driver uses the chip's Random command in seed-update mode. Keep 'quality = 1' for ATSHA204, but drop the explicit hwrng quality reduction for ATSHA204A and fall back to the hwrng core default. [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html [2] https://www.metzdowd.com/pipermail/cryptography/2014-December/023852.html [3] https://www.metzdowd.com/pipermail/cryptography/2014-December/023886.html [4] https://ww1.microchip.com/downloads/en/DeviceDoc/ATSHA204A-Data-Sheet-40002025A.pdf Fixes: 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to lowest possible") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Ard Biesheuvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/atmel-sha204a.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c index ae04b27a9ad11..49e545ef6493b 100644 --- a/drivers/crypto/atmel-sha204a.c +++ b/drivers/crypto/atmel-sha204a.c @@ -19,6 +19,12 @@ #include #include "atmel-i2c.h" +/* + * According to review by Bill Cox [1], the ATSHA204 has very low entropy. + * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html + */ +static const unsigned short atsha204_quality = 1; + static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data, void *areq, int status) { @@ -158,6 +164,7 @@ static const struct attribute_group atmel_sha204a_groups = { static int atmel_sha204a_probe(struct i2c_client *client) { struct atmel_i2c_client_priv *i2c_priv; + const unsigned short *quality; int ret; ret = atmel_i2c_probe(client); @@ -171,11 +178,9 @@ static int atmel_sha204a_probe(struct i2c_client *client) i2c_priv->hwrng.name = dev_name(&client->dev); i2c_priv->hwrng.read = atmel_sha204a_rng_read; - /* - * According to review by Bill Cox [1], this HWRNG has very low entropy. - * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html - */ - i2c_priv->hwrng.quality = 1; + quality = i2c_get_match_data(client); + if (quality) + i2c_priv->hwrng.quality = *quality; ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng); if (ret) @@ -207,14 +212,14 @@ static void atmel_sha204a_remove(struct i2c_client *client) } static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = { - { .compatible = "atmel,atsha204", }, + { .compatible = "atmel,atsha204", .data = &atsha204_quality }, { .compatible = "atmel,atsha204a", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids); static const struct i2c_device_id atmel_sha204a_id[] = { - { "atsha204" }, + { "atsha204", (kernel_ulong_t)&atsha204_quality }, { "atsha204a" }, { /* sentinel */ } }; From a8803c4f0ac3fa7df5551bbb5a8800c434a94357 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 27 May 2026 08:45:44 +0000 Subject: [PATCH 0447/3297] nvme: target: rdma: fix ndev refcount leak on queue connect commit badc53620fe813b3a9f727ef9526f98567c2c898 upstream. nvmet_rdma_queue_connect() calls nvmet_rdma_find_get_device() which acquires a reference on the returned ndev via kref_get(). On the path where the host queue backlog is exceeded and the function returns NVME_SC_CONNECT_CTRL_BUSY, reference of ndev is not released, leaking the kref. Fix this by adding a goto to the existing put_device label before the early return. Fixes: 31deaeb11ba7 ("nvmet-rdma: avoid circular locking dependency on install_queue()") Cc: stable@vger.kernel.org Reviewed-by: Christoph Hellwig Signed-off-by: Wentao Liang Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/rdma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index 284ab759bbce3..9307dbc41303c 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -1599,8 +1599,10 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, pending++; } mutex_unlock(&nvmet_rdma_queue_mutex); - if (pending > NVMET_RDMA_BACKLOG) - return NVME_SC_CONNECT_CTRL_BUSY; + if (pending > NVMET_RDMA_BACKLOG) { + ret = NVME_SC_CONNECT_CTRL_BUSY; + goto put_device; + } } ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn); From 427c82497e269579c95ce9142f07e88aa58f2b45 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 26 May 2026 10:21:24 +0000 Subject: [PATCH 0448/3297] block: partitions: fix of_node refcount leak in of_partition() commit 148cd4873115feb266c002d4d4618ea7f14342d9 upstream. of_partition() calls of_node_get() on the parent device node at the beginning of the function, storing the reference in 'partitions_np'. This reference is leaked in two paths: 1. The compatibility check at the top of the function returns 0 without releasing partitions_np when the node exists but is not "fixed-partitions" compatible. 2. The function returns 1 at the end after successfully processing all partitions without releasing partitions_np. Fix both leaks by adding of_node_put(partitions_np) on each path. Fixes: 2e3a191e89f9 ("block: add support for partition table defined in OF") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Reviewed-by: Md Haris Iqbal Link: https://patch.msgid.link/20260526102124.2283846-1-vulab@iscas.ac.cn Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/partitions/of.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/partitions/of.c b/block/partitions/of.c index 4e760fdffb3fe..629f0fdbeb737 100644 --- a/block/partitions/of.c +++ b/block/partitions/of.c @@ -76,8 +76,10 @@ int of_partition(struct parsed_partitions *state) struct device_node *partitions_np = of_node_get(ddev->of_node); if (!partitions_np || - !of_device_is_compatible(partitions_np, "fixed-partitions")) + !of_device_is_compatible(partitions_np, "fixed-partitions")) { + of_node_put(partitions_np); return 0; + } slot = 1; /* Validate parition offset and size */ @@ -106,5 +108,6 @@ int of_partition(struct parsed_partitions *state) strlcat(state->pp_buf, "\n", PAGE_SIZE); + of_node_put(partitions_np); return 1; } From 13f2f5defb4d7f8fe9510e4ec1a42f9a862bc441 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Mon, 11 May 2026 13:04:16 +0200 Subject: [PATCH 0449/3297] dm-ioctl: report an error if a device has no table commit 457e32348d606a77f9b20e25e989734189834c07 upstream. When we send a message to a device that has no table, the return code was not set. The code would return "2", which is not considered a valid return value. Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Reviewed-by: Benjamin Marzinski Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-ioctl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index de90feb842ab6..128dd1a939254 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1806,8 +1806,11 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para goto out_argv; table = dm_get_live_table(md, &srcu_idx); - if (!table) + if (!table) { + DMERR("The device has no table."); + r = -EINVAL; goto out_table; + } if (dm_deleting_md(md)) { r = -ENXIO; From 7a69463e9ad23650b07b9d166f0ddfafbfb8c530 Mon Sep 17 00:00:00 2001 From: "Achkinazi, Igor" Date: Thu, 28 May 2026 15:24:27 +0000 Subject: [PATCH 0450/3297] nvme-multipath: set BIO_REMAPPED on bios remapped to per-path namespace disks commit 88bac2c1a72b8f4f71e9845699aa872df04e5850 upstream. When nvme_ns_head_submit_bio() remaps a bio from the multipath head to a per-path namespace, bio_set_dev() clears BIO_REMAPPED. The remapped bio is then resubmitted through submit_bio_noacct() which calls bio_check_eod() because BIO_REMAPPED is not set. This races with nvme_ns_remove() which zeroes the per-path capacity before synchronize_srcu(): CPU 0 (IO submission) --------------------- srcu_read_lock() nvme_find_path() -> ns [NVME_NS_READY is set] CPU 1 (namespace removal) ------------------------- clear_bit(NVME_NS_READY) set_capacity(ns->disk, 0) synchronize_srcu() <- blocks CPU 0 (IO submission) --------------------- bio_set_dev(bio, ns->disk->part0) [clears BIO_REMAPPED] submit_bio_noacct(bio) -> bio_check_eod() sees capacity=0 -> bio fails with IO error The SRCU read lock prevents synchronize_srcu() from completing, but does not prevent set_capacity(0) from executing. The bio fails the EOD check before it reaches the NVMe driver, so nvme_failover_req() never gets a chance to redirect it to another path of multipath. IO errors are reported to the application despite another path being available. On older kernels (before commit 0b64682e78f7 "block: skip unnecessary checks for split bio"), the same race was also reachable through split remainders resubmitted via submit_bio_noacct(). Fix this by setting BIO_REMAPPED after bio_set_dev() in nvme_ns_head_submit_bio(). This skips bio_check_eod() on the per-path device; the EOD check already passed on the multipath head. NVMe per-path namespace devices are always whole disks (bd_partno=0), so the blk_partition_remap() skip also gated by BIO_REMAPPED is a no-op. The flag does not persist across failover and cannot go stale if the namespace geometry changes between attempts: nvme_failover_req() calls bio_set_dev() to redirect the bio back to the multipath head, which clears BIO_REMAPPED. When nvme_requeue_work() resubmits through submit_bio_noacct(), bio_check_eod() runs normally against the current capacity. Same approach as commit 3a905c37c351 ("block: skip bio_check_eod for partition-remapped bios"). Fixes: a7c7f7b2b641 ("nvme: use bio_set_dev to assign ->bi_bdev") Cc: stable@vger.kernel.org Reviewed-by: Christoph Hellwig Signed-off-by: Igor Achkinazi Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/host/multipath.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 81ccdd91f7790..616d1ce6b8e5c 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -528,6 +528,12 @@ static void nvme_ns_head_submit_bio(struct bio *bio) ns = nvme_find_path(head); if (likely(ns)) { bio_set_dev(bio, ns->disk->part0); + /* + * Use BIO_REMAPPED to skip bio_check_eod() when this bio + * enters submit_bio_noacct() for the per-path device. The EOD + * check already passed on the multipath head. + */ + bio_set_flag(bio, BIO_REMAPPED); bio->bi_opf |= REQ_NVME_MPATH; trace_block_bio_remap(bio, disk_devt(ns->head->disk), bio->bi_iter.bi_sector); From 56c021a0869260d04c4b65d1471936aaf9177114 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Wed, 27 May 2026 15:00:00 -0500 Subject: [PATCH 0451/3297] nvmet: fix pre-auth out-of-bounds heap read in Discovery Get Log Page commit 53cd102a7a56079b11b897835bd9b94c14e6322c upstream. nvmet_execute_disc_get_log_page() validates only the dword alignment of the host-supplied Log Page Offset (lpo). The 64-bit offset is then added to a small kzalloc'd buffer that holds the discovery log page and the result is passed straight to nvmet_copy_to_sgl(), which memcpy()s data_len bytes out to the host with no source-side bound check: u64 offset = nvmet_get_log_page_offset(req->cmd); /* 64-bit host */ size_t data_len = nvmet_get_log_page_len(req->cmd); /* 32-bit host */ ... if (offset & 0x3) { ... } /* only check */ ... alloc_len = sizeof(*hdr) + entry_size * discovery_log_entries(req); buffer = kzalloc(alloc_len, GFP_KERNEL); ... status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len); The Discovery controller is unauthenticated -- nvmet_host_allowed() returns true unconditionally for the discovery subsystem -- so the call is reachable pre-authentication by any TCP/RDMA/FC peer that can reach the nvmet target. With a discovery log page of ~1 KiB, an attacker requesting up to 4 KiB starting at offset == alloc_len reads the next slab page out and gets its content returned over the fabric (an empirical run on a default nvmet-tcp loopback target leaked 81 canonical kernel pointers in one Get Log Page response). Pointing the offset at unmapped kernel memory faults the in-kernel memcpy and crashes (or panics, on panic_on_oops=1) the target host instead. The attacker-controlled source-side offset pattern "nvmet_copy_to_sgl(req, 0, buffer + ATTACKER_OFFSET, ...)" is unique to nvmet_execute_disc_get_log_page in the entire nvmet codebase: every other Get Log Page handler in admin-cmd.c either ignores lpo (and silently starts every response at offset 0) or tracks a local destination offset with a fixed source pointer. Validate the host-supplied offset against the log page size, cap the copy length to what is actually available, and zero-fill any remainder of the host transfer buffer. The zero-fill matches the existing short-response pattern in nvmet_execute_get_log_changed_ns() (admin-cmd.c) and prevents leaking transport SGL contents when the host asks for more bytes than the log page contains. Fixes: a07b4970f464 ("nvmet: add a generic NVMe target") Cc: stable@vger.kernel.org Reviewed-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Signed-off-by: Bryam Vargas Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/discovery.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c index c06f3e04296cc..1d22291bd8b07 100644 --- a/drivers/nvme/target/discovery.c +++ b/drivers/nvme/target/discovery.c @@ -166,6 +166,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) u64 offset = nvmet_get_log_page_offset(req->cmd); size_t data_len = nvmet_get_log_page_len(req->cmd); size_t alloc_len; + size_t copy_len; struct nvmet_subsys_link *p; struct nvmet_port *r; u32 numrec = 0; @@ -242,7 +243,27 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) up_read(&nvmet_config_sem); - status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len); + /* + * Validate the host-supplied log page offset before copying out. + * Without this check, the host controls a 64-bit byte offset into + * a small kzalloc'd buffer: a value past the log page lets the + * subsequent memcpy read adjacent kernel heap, and a value aimed + * at unmapped kernel memory faults the in-kernel copy and crashes + * the target host. The Discovery controller is unauthenticated, + * so the bug is reachable from any reachable fabric peer. + */ + if (offset > alloc_len) { + req->error_loc = + offsetof(struct nvme_get_log_page_command, lpo); + status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR; + goto out_free_buffer; + } + + copy_len = min_t(size_t, data_len, alloc_len - offset); + status = nvmet_copy_to_sgl(req, 0, buffer + offset, copy_len); + if (!status && copy_len < data_len) + status = nvmet_zero_sgl(req, copy_len, data_len - copy_len); +out_free_buffer: kfree(buffer); out: nvmet_req_complete(req, status); From 6d7649c1231dac14d906985d2936967e23041c26 Mon Sep 17 00:00:00 2001 From: Tianchu Chen Date: Fri, 29 May 2026 14:18:39 +0000 Subject: [PATCH 0452/3297] nvmet-auth: validate reply message payload bounds against transfer length commit 3a413ece2504c70aa34a20be4dafec04e8c741f9 upstream. nvmet_auth_reply() accesses the variable-length rval[] array using attacker-controlled hl (hash length) and dhvlen (DH value length) fields without verifying they fit within the allocated buffer of tl bytes. A malicious NVMe-oF initiator can craft a DHCHAP_REPLY message with a small transfer length but large hl/dhvlen values, causing out-of-bounds heap reads when the target processes the DH public key (rval + 2*hl) or performs the host response memcmp. With DH authentication configured, the OOB pointer is passed directly to sg_init_one() and read by crypto_kpp_compute_shared_secret(), reaching up to 526 bytes past the buffer. This is exploitable pre-authentication. Add bounds validation ensuring sizeof(*data) + 2*hl + dhvlen <= tl before any access to the variable-length fields. Discovered by Atuin - Automated Vulnerability Discovery Engine. Fixes: db1312dd9548 ("nvmet: implement basic In-Band Authentication") Cc: stable@vger.kernel.org Reviewed-by: Hannes Reinecke Signed-off-by: Tianchu Chen Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/fabrics-cmd-auth.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 5946681cb0e32..4f2f03bdf2b38 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -134,13 +134,22 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d) return 0; } -static u8 nvmet_auth_reply(struct nvmet_req *req, void *d) +static u8 nvmet_auth_reply(struct nvmet_req *req, void *d, u32 tl) { struct nvmet_ctrl *ctrl = req->sq->ctrl; struct nvmf_auth_dhchap_reply_data *data = d; - u16 dhvlen = le16_to_cpu(data->dhvlen); + u16 dhvlen; u8 *response; + if (tl < sizeof(*data)) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + + dhvlen = le16_to_cpu(data->dhvlen); + + /* Validate that hl and dhvlen fit within the transfer length */ + if (sizeof(*data) + 2 * (size_t)data->hl + dhvlen > tl) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n", __func__, ctrl->cntlid, req->sq->qid, data->hl, data->cvalid, dhvlen); @@ -339,7 +348,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req) switch (data->auth_id) { case NVME_AUTH_DHCHAP_MESSAGE_REPLY: - dhchap_status = nvmet_auth_reply(req, d); + dhchap_status = nvmet_auth_reply(req, d, tl); if (dhchap_status == 0) req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1; From 0912b98151eea6acfa2a0e11b5f772b198d29ab7 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Apr 2026 18:32:49 +0930 Subject: [PATCH 0453/3297] btrfs: check and set EXTENT_DELALLOC_NEW before clearing EXTENT_DELALLOC commit 95ee2231896d5f2a31760411429075a99d6045a7 upstream. [WARNING] When running test cases with injected errors or shutdown, e.g. generic/388 or generic/475, there is a chance that the following kernel warning is triggered: BTRFS info (device dm-2): first mount of filesystem d8a19a28-3232-4809-b0df-38df83e71bff BTRFS info (device dm-2): using crc32c checksum algorithm BTRFS info (device dm-2): checking UUID tree BTRFS info (device dm-2): turning on async discard BTRFS info (device dm-2): enabling free space tree BTRFS critical (device dm-2 state E): emergency shutdown ------------[ cut here ]------------ WARNING: extent_io.c:1742 at extent_writepage_io+0x437/0x520 [btrfs], CPU#2: kworker/u43:2/651591 CPU: 2 UID: 0 PID: 651591 Comm: kworker/u43:2 Tainted: G W OE 7.0.0-rc6-custom+ #365 PREEMPT(full) 5804053f02137e627472d94b5128cc9fcb110e88 RIP: 0010:extent_writepage_io+0x437/0x520 [btrfs] Call Trace: extent_write_cache_pages+0x2a5/0x820 [btrfs 70299925d0856939e93b17d480651713b3cbba58] btrfs_writepages+0x74/0x130 [btrfs 70299925d0856939e93b17d480651713b3cbba58] do_writepages+0xd0/0x160 __writeback_single_inode+0x42/0x340 writeback_sb_inodes+0x22d/0x580 wb_writeback+0xc6/0x360 wb_workfn+0xbd/0x470 process_one_work+0x198/0x3b0 worker_thread+0x1c8/0x330 kthread+0xee/0x120 ret_from_fork+0x2a6/0x330 ret_from_fork_asm+0x11/0x20 ---[ end trace 0000000000000000 ]--- BTRFS error (device dm-2 state E): root 5 ino 259 folio 1323008 is marked dirty without notifying the fs BTRFS error (device dm-2 state E): failed to submit blocks, root=5 inode=259 folio=1323008 submit_bitmap=0: -117 BTRFS info (device dm-2 state E): last unmount of filesystem d8a19a28-3232-4809-b0df-38df83e71bff [CAUSE] Inside btrfs we have the following pattern in several locations, for example inside btrfs_dirty_folio(): btrfs_clear_extent_bit(&inode->io_tree, start_pos, end_of_last_block, EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, cached); ret = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block, extra_bits, cached); if (ret) return ret; However btrfs_set_extent_delalloc() can return IO errors other than -ENOMEM through the following callchain: btrfs_set_extent_delalloc() \- btrfs_find_new_delalloc_bytes() \- btrfs_get_extent() \- btrfs_lookup_file_extent() \- btrfs_search_slot() When such IO error happened, the previous btrfs_clear_extent_bit() has cleared the EXTENT_DELALLOC for the range, and we're expecting btrfs_set_extent_delalloc() to re-set EXTENT_DELALLOC. But since btrfs_set_extent_delalloc() failed before btrfs_set_extent_bit(), EXTENT_DELALLOC flag is no longer present. And if the folio range is dirty before entering btrfs_set_extent_delalloc(), we got a dirty folio but no EXTENT_DELALLOC flag now. Then we hit the folio writeback: extent_writepage() |- writepage_delalloc() | No ordered extent is created, as there is no EXTENT_DELALLOC set | for the folio range. | This also means the folio has no ordered flag set. | |- extent_writepage_io() \- if (unlikely(!folio_test_ordered(folio)) Now we hit the warning. [FIX] Introduce a new helper, btrfs_reset_extent_delalloc() to replace the currently open-coded btrfs_clear_extent_bit() + btrfs_set_extent_delalloc() combination. Instead of calling btrfs_clear_extent_bit() first, update EXTENT_DELALLOC_NEW first, as that part can fail due to metadata IO, meanwhile btrfs_clear_extent_bit() and btrfs_set_extent_bit() won't return any error but retry memory allocation until succeeded. This allows us to fail early without clearing EXTENT_DELALLOC bit, so even if that new btrfs_reset_extent_delalloc() failed before touching EXTENT_DELALLOC, the existing dirty range will still have their old EXTENT_DELALLOC flag present, thus avoid the warning. CC: stable@vger.kernel.org # 6.1+ Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/btrfs_inode.h | 2 ++ fs/btrfs/file.c | 25 +++-------------- fs/btrfs/inode.c | 61 +++++++++++++++++++++++++++++++++++++----- fs/btrfs/reflink.c | 4 +-- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index a66ca5531b5c5..b6b46f951f11a 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -575,6 +575,8 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, long nr, int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, unsigned int extra_bits, struct extent_state **cached_state); +int btrfs_reset_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, + unsigned int extra_bits, struct extent_state **cached_state); struct btrfs_new_inode_args { /* Input */ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 0fee45d35a5f8..66c76f425afb5 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -93,16 +93,8 @@ int btrfs_dirty_folio(struct btrfs_inode *inode, struct folio *folio, loff_t pos end_of_last_block = start_pos + num_bytes - 1; - /* - * The pages may have already been dirty, clear out old accounting so - * we can set things up properly - */ - btrfs_clear_extent_bit(&inode->io_tree, start_pos, end_of_last_block, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, - cached); - - ret = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block, - extra_bits, cached); + ret = btrfs_reset_extent_delalloc(inode, start_pos, end_of_last_block, + extra_bits, cached); if (ret) return ret; @@ -1962,18 +1954,7 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf) } } - /* - * page_mkwrite gets called when the page is firstly dirtied after it's - * faulted in, but write(2) could also dirty a page and set delalloc - * bits, thus in this case for space account reason, we still need to - * clear any delalloc bits within this page range since we have to - * reserve data&meta space before lock_page() (see above comments). - */ - btrfs_clear_extent_bit(io_tree, page_start, end, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | - EXTENT_DEFRAG, &cached_state); - - ret = btrfs_set_extent_delalloc(inode, page_start, end, 0, &cached_state); + ret = btrfs_reset_extent_delalloc(inode, page_start, end, 0, &cached_state); if (ret < 0) { btrfs_unlock_extent(io_tree, page_start, page_end, &cached_state); goto out_unlock; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a4f1810db079d..b972d4cba9802 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2720,7 +2720,13 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, unsigned int extra_bits, struct extent_state **cached_state) { - WARN_ON(PAGE_ALIGNED(end)); + const u32 blocksize = inode->root->fs_info->sectorsize; + + /* Basic alignment check. */ + ASSERT(IS_ALIGNED(start, blocksize), "start=%llu blocksize=%u", + start, blocksize); + ASSERT(IS_ALIGNED(end + 1, blocksize), "inclusive end=%llu blocksize=%u", + end, blocksize); if (start >= i_size_read(&inode->vfs_inode) && !(inode->flags & BTRFS_INODE_PREALLOC)) { @@ -2943,6 +2949,52 @@ int btrfs_writepage_cow_fixup(struct folio *folio) return -EAGAIN; } +/* + * Clear the old accounting flags and set EXTENT_DELALLOC for the range. + * + * Return <0 for error, in that case no range has EXTENT_DELALLOC bit cleared or set. + */ +int btrfs_reset_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, + unsigned int extra_bits, struct extent_state **cached_state) +{ + const u32 blocksize = inode->root->fs_info->sectorsize; + + /* The @extra_bits can only be EXTENT_NORESERVE for now. */ + ASSERT(!(extra_bits & ~EXTENT_NORESERVE), "extra_bits=0x%x", extra_bits); + + /* Basic alignment check. */ + ASSERT(IS_ALIGNED(start, blocksize), "start=%llu blocksize=%u", + start, blocksize); + ASSERT(IS_ALIGNED(end + 1, blocksize), "inclusive end=%llu blocksize=%u", + end, blocksize); + + /* + * Check and set DELALLOC_NEW flag, this needs to search tree thus can + * fail early. Thus we want to do this before clearing EXTENT_DELALLOC. + */ + if (start >= i_size_read(&inode->vfs_inode) && + !(inode->flags & BTRFS_INODE_PREALLOC)) { + /* + * There can't be any extents following EOF in this case so just + * set the delalloc new bit for the range directly. + */ + extra_bits |= EXTENT_DELALLOC_NEW; + } else { + int ret; + + ret = btrfs_find_new_delalloc_bytes(inode, start, end + 1 - start, + NULL); + if (unlikely(ret)) + return ret; + } + /* Clear the old accounting as the range may already be dirty. */ + btrfs_clear_extent_bit(&inode->io_tree, start, end, + EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | + EXTENT_DEFRAG, cached_state); + return btrfs_set_extent_bit(&inode->io_tree, start, end, + EXTENT_DELALLOC | extra_bits, cached_state); +} + static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, struct btrfs_inode *inode, u64 file_pos, struct btrfs_file_extent_item *stack_fi, @@ -5053,12 +5105,7 @@ int btrfs_truncate_block(struct btrfs_inode *inode, u64 offset, u64 start, u64 e goto again; } - btrfs_clear_extent_bit(&inode->io_tree, block_start, block_end, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, - &cached_state); - - ret = btrfs_set_extent_delalloc(inode, block_start, block_end, 0, - &cached_state); + ret = btrfs_reset_extent_delalloc(inode, block_start, block_end, 0, &cached_state); if (ret) { btrfs_unlock_extent(io_tree, block_start, block_end, &cached_state); goto out_unlock; diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 614e884c2a9bc..b5f7b20c585a0 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -93,9 +93,7 @@ static int copy_inline_to_page(struct btrfs_inode *inode, if (ret < 0) goto out_unlock; - btrfs_clear_extent_bit(&inode->io_tree, file_offset, range_end, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, NULL); - ret = btrfs_set_extent_delalloc(inode, file_offset, range_end, 0, NULL); + ret = btrfs_reset_extent_delalloc(inode, file_offset, range_end, 0, NULL); if (ret) goto out_unlock; From 7a64521802997257b144e6edfb4e278dbeb972dd Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 2 Jun 2026 13:34:46 +0930 Subject: [PATCH 0454/3297] btrfs: do not trim a device which is not writeable commit 1b1937eb08f51319bf71575484cde2b8c517aedc upstream. [BUG] There is a bug report that btrfs/242 can randomly fail with the following NULL pointer dereference: run fstests btrfs/242 at 2026-06-01 10:25:08 BTRFS: device fsid d4d7f234-487c-4787-88e4-47a8b68c9874 devid 1 transid 9 /dev/sdc (8:32) scanned by mount (122609) BTRFS info (device sdc): first mount of filesystem d4d7f234-487c-4787-88e4-47a8b68c9874 BTRFS info (device sdc): using crc32c checksum algorithm BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing BTRFS info (device sdc): allowing degraded mounts BTRFS info (device sdc): turning on async discard BTRFS info (device sdc): enabling free space tree Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 user pgtable: 4k pages, 48-bit VAs, pgdp=000000013fd6b000 CPU: 4 UID: 0 PID: 122625 Comm: fstrim Not tainted 7.0.10-2-default #1 PREEMPT(full) openSUSE Tumbleweed e9a5f6b24978fba3bf015a992f865837fdfff3dd Hardware name: QEMU KVM Virtual Machine, BIOS edk2-20250812-19.fc42 08/12/2025 pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) pc : btrfs_trim_fs+0x34c/0xa00 [btrfs] lr : btrfs_trim_fs+0x1f0/0xa00 [btrfs] Call trace: btrfs_trim_fs+0x34c/0xa00 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] (P) btrfs_ioctl_fitrim+0xe8/0x178 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] btrfs_ioctl+0xdd4/0x2bd8 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] __arm64_sys_ioctl+0xac/0x108 invoke_syscall.constprop.0+0x5c/0xd0 el0_svc_common.constprop.0+0x40/0xf0 do_el0_svc+0x24/0x40 el0_svc+0x40/0x1d0 el0t_64_sync_handler+0xa0/0xe8 el0t_64_sync+0x1b0/0x1b8 Code: 17ffff83 f94017e0 f9002be0 f9402ea0 (f9400c00) ---[ end trace 0000000000000000 ]--- Also the reporter is very kind to test the following ASSERT() added to btrfs_trim_free_extents_throttle(): ASSERT(device->bdev, "devid=%llu path=%s dev_state=0x%lx\n", device->devid, btrfs_dev_name(device), device->dev_state); And it shows the following output: assertion failed: device->bdev, in extent-tree.c:6630 (devid=2 path=/dev/sdd dev_state=0x82) Which means the device->bdev is NULL, and the dev_state is BTRFS_DEV_STATE_IN_FS_METADATA | BTRFS_DEV_STATE_ITEM_FOUND, without BTRFS_DEV_STATE_WRITEABLE flag set. [CAUSE] The pc points to the following call chain: btrfs_trim_fs() |- btrfs_trim_free_extents() |- btrfs_trim_free_extents_throttle() |- bdev_max_discard_sectors(device->bdev) So the NULL pointer dereference is caused by device->bdev being NULL. This looks impossible by a quick glance, as just before calling btrfs_trim_free_extents_throttle(), we have skipped any device that has BTRFS_DEV_STATE_MISSING flag set. However in this particular case, there is a window where the missing device is later re-scanned, causing btrfs to remove the BTRFS_DEV_STATE_MISSING flag: btrfs_control_ioctl() |- btrfs_scan_one_device() |- device_list_add() |- rcu_assign_pointer(device->name, name); | This updates the missing device's path to the new good path. | |- clear_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state) This removes the BTRFS_DEV_STATE_MISSING flag. This allows the missing device to re-appear and clear the BTRFS_DEV_STATE_MISSING flag. However the device still does not have the BTRFS_DEV_STATE_WRITEABLE flag set, nor is its bdev pointer updated. The bdev pointer remains NULL, triggering the crash later. [FIX] This is a big de-synchronization between BTRFS_DEV_STATE_MISSING and device->bdev pointer, and shows a gap in btrfs's re-appearing-device handling. The proper handling of re-appearing device will need quite some extra work, which is out of the context of this small fix. Thankfully the regular bbio submission path has already handled it well by checking if the device->bdev is NULL before submitting. So here we just fix the crash by checking if the device is writeable and has a bdev pointer before calling bdev_max_discard_sectors(). Reported-by: Su Yue Link: https://lore.kernel.org/linux-btrfs/wlwir19t.fsf@damenly.org/ Fixes: 499f377f49f0 ("btrfs: iterate over unused chunk space in FITRIM") CC: stable@vger.kernel.org # 5.10+ Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/extent-tree.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 663526d909ab1..ffe6c80495666 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -6411,12 +6411,16 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) *trimmed = 0; - /* Discard not supported = nothing to do. */ - if (!bdev_max_discard_sectors(device->bdev)) + /* + * The caller only filters out MISSING devices, but a device that was + * missing at mount and later rescanned has MISSING cleared while bdev + * is still NULL and WRITEABLE is still unset. Skip those here. + */ + if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) || !device->bdev) return 0; - /* Not writable = nothing to do. */ - if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) + /* Discard not supported = nothing to do. */ + if (!bdev_max_discard_sectors(device->bdev)) return 0; /* No free space = nothing to do. */ From ce93228e2193a17d2c58b656e439bb39fe5c3af8 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 06:41:43 +0000 Subject: [PATCH 0455/3297] partitions: aix: bound the pp_count scan to the ppe array commit 2dc0bfd2fe355fb930de63c2f2eb8ced8570c579 upstream. aix_partition() reads the physical volume descriptor into a fixed-size struct pvd and then scans its physical-partition-extent array: int numpps = be16_to_cpu(pvd->pp_count); ... for (i = 0; i < numpps; i += 1) { struct ppe *p = pvd->ppe + i; ... lp_ix = be16_to_cpu(p->lp_ix); pvd points at a single kmalloc()'d struct pvd whose ppe[] member holds a fixed ARRAY_SIZE(pvd->ppe) (1016) entries, but the loop runs up to the on-disk pp_count. pp_count is an unvalidated __be16 read straight from the descriptor, so a crafted AIX image with pp_count larger than 1016 drives the loop to read pvd->ppe[i] past the end of the allocation (up to 65535 entries, ~2 MB out of bounds). The partition scan runs without mounting anything, when a block device with a crafted AIX/IBM partition table appears (an attacker-supplied image attached with losetup -P, or a device auto-scanned by udev), via msdos_partition() -> aix_partition(). Clamp the scan to the number of entries the ppe[] array can hold. Fixes: 6ceea22bbbc8 ("partitions: add aix lvm partition support files") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Acked-by: Philippe De Muyter Link: https://patch.msgid.link/20260607064137.302574-1-hexlabsecurity@proton.me Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/partitions/aix.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/block/partitions/aix.c b/block/partitions/aix.c index 85f4b967565e3..cdcd60686e0ba 100644 --- a/block/partitions/aix.c +++ b/block/partitions/aix.c @@ -228,6 +228,15 @@ int aix_partition(struct parsed_partitions *state) int next_lp_ix = 1; int lp_ix; + /* + * pvd was read into a fixed-size struct pvd whose ppe[] array + * holds ARRAY_SIZE(pvd->ppe) entries. pp_count is an + * unvalidated on-disk __be16, so clamp the scan to the array + * size to avoid walking past the allocation. + */ + if (numpps > ARRAY_SIZE(pvd->ppe)) + numpps = ARRAY_SIZE(pvd->ppe); + for (i = 0; i < numpps; i += 1) { struct ppe *p = pvd->ppe + i; unsigned int lv_ix; From b5699642640d6cff357638738c5293985cd5a53d Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 7 Jun 2026 01:18:27 +0000 Subject: [PATCH 0456/3297] isofs: bound Rock Ridge symlink components to the SL record commit 5fa1d6a5ec2356d2107dead614437c66fa7138b1 upstream. get_symlink_chunk() and the SL handling in parse_rock_ridge_inode_internal() walk the variable-length components of a Rock Ridge "SL" (symbolic link) record. Each component is a two-byte header (flags, len) followed by len bytes of text, so it occupies slp->len + 2 bytes. Both loops read slp->len and advance to the next component, and get_symlink_chunk() additionally does memcpy(rpnt, slp->text, slp->len), but neither checks that the component lies within the SL record before dereferencing it. A crafted SL record whose component declares a len that runs past the record (rr->len) therefore triggers an out-of-bounds read of up to 255 bytes. When the record sits at the tail of its backing buffer - for example a small kmalloc()ed continuation block reached through a CE record - the read crosses the allocation; get_symlink_chunk() then copies the out-of-bounds bytes into the symlink body returned to user space by readlink(), disclosing adjacent kernel memory. ISO 9660 images are routinely mounted from untrusted removable media - desktop environments auto-mount them (e.g. via udisks2) without CAP_SYS_ADMIN - so the record contents are attacker-controlled. Reject any component that does not fit in the remaining record bytes before using it. In get_symlink_chunk() return NULL, like the existing output-buffer (plimit) checks, so a malformed record makes readlink() fail with -EIO rather than silently returning a truncated target; in parse_rock_ridge_inode_internal() stop the inode-size walk. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Suggested-by: Michael Bommarito Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260607011823.217748-1-hexlabsecurity@proton.me Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/isofs/rock.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 6c104fcb84481..869b331eff4f6 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -466,6 +466,9 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de, inode->i_size = symlink_len; while (slen > 1) { rootflag = 0; + /* keep the component within the SL record */ + if (slp->len + 2 > slen) + goto eio; switch (slp->flags & ~1) { case 0: inode->i_size += @@ -621,6 +624,14 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit) slp = &rr->u.SL.link; while (slen > 1) { rootflag = 0; + /* + * A component is slp->len + 2 bytes (a two-byte header plus + * len bytes of text). If it does not fit in the bytes left in + * the SL record the record is malformed: fail like the plimit + * checks below so readlink() returns -EIO, not a truncated path. + */ + if (slp->len + 2 > slen) + return NULL; switch (slp->flags & ~1) { case 0: if (slp->len > plimit - rpnt) From 7465ed1524ace60c4794effbc94fd06c2a6c9fac Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 4 May 2026 15:53:28 -0700 Subject: [PATCH 0457/3297] crypto: af_alg - Remove zero-copy support from skcipher and aead commit ffdd2bc378953b525aca61902534e753f1f8e734 upstream. The zero-copy support is one of the riskiest aspects of AF_ALG. It allows userspace to request cryptographic operations directly on pagecache pages of files like the 'su' binary. It also allows userspace to concurrently modify the memory which is being operated on, a recipe for TOCTOU vulnerabilities. While zero-copy support is more valuable in other areas of the kernel like the frequently used networking and file I/O code, it has far less value in AF_ALG, which is a niche UAPI. AF_ALG primarily just exists for backwards compatibility with a small set of userspace programs such as 'iwd' that haven't yet been fixed to use userspace crypto code. Originally AF_ALG was intended to be used to access hardware crypto accelerators. However, it isn't an efficient interface for that anyway, and it turned out to be rarely used in this way in practice. Thus, the risks of the zero-copy support in AF_ALG vastly outweigh its benefits. Let's just remove it. This commit removes it from the "skcipher" and "aead" algorithm types. "hash" will be handled separately. This is a soft break, not a hard break. Even after this commit, it still works to use splice() or sendfile() to transfer data to an AF_ALG request socket from a pipe or any file, respectively. What changes is just that the kernel now makes an internal, stable copy of the data before doing the crypto operation. So performance is slightly reduced, but the UAPI isn't broken. And, very importantly, it's much safer. Tested with libkcapi/test.sh. All its test cases still pass. I also verified that this would have prevented the copy.fail exploit as well. I also used a custom test program to verify that sendfile() still works. Fixes: 8ff590903d5f ("crypto: algif_skcipher - User-space interface for skcipher operations") Fixes: 400c40cf78da ("crypto: algif - add AEAD support") Reported-by: Taeyang Lee <0wn@theori.io> Link: https://copy.fail/ Reported-by: Feng Ning Closes: https://lore.kernel.org/r/afYcc-tZFwvZZo76@ans-MacBook-Pro.local Reviewed-by: Demi Marie Obenour Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- Documentation/crypto/userspace-if.rst | 31 ++---------- crypto/af_alg.c | 73 +++++++++------------------ crypto/algif_aead.c | 8 +-- 3 files changed, 33 insertions(+), 79 deletions(-) diff --git a/Documentation/crypto/userspace-if.rst b/Documentation/crypto/userspace-if.rst index f80f243e227e6..4630d3d74913b 100644 --- a/Documentation/crypto/userspace-if.rst +++ b/Documentation/crypto/userspace-if.rst @@ -328,33 +328,10 @@ CRYPTO_USER_API_RNG_CAVP option: Zero-Copy Interface ------------------- -In addition to the send/write/read/recv system call family, the AF_ALG -interface can be accessed with the zero-copy interface of -splice/vmsplice. As the name indicates, the kernel tries to avoid a copy -operation into kernel space. - -The zero-copy operation requires data to be aligned at the page -boundary. Non-aligned data can be used as well, but may require more -operations of the kernel which would defeat the speed gains obtained -from the zero-copy interface. - -The system-inherent limit for the size of one zero-copy operation is 16 -pages. If more data is to be sent to AF_ALG, user space must slice the -input into segments with a maximum size of 16 pages. - -Zero-copy can be used with the following code example (a complete -working example is provided with libkcapi): - -:: - - int pipes[2]; - - pipe(pipes); - /* input data in iov */ - vmsplice(pipes[1], iov, iovlen, SPLICE_F_GIFT); - /* opfd is the file descriptor returned from accept() system call */ - splice(pipes[0], NULL, opfd, NULL, ret, 0); - read(opfd, out, outlen); +AF_ALG used to have zero-copy support, but it was removed due to it being a +frequent source of vulnerabilities. For backwards compatibility the splice() +and sendfile() system calls are still supported, but the kernel will make an +internal copy of the data before passing it to the crypto code. Setsockopt Interface diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 0111d07545931..3c3cb965fd74e 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -977,7 +977,7 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, ssize_t plen; /* use the existing memory in an allocated page */ - if (ctx->merge && !(msg->msg_flags & MSG_SPLICE_PAGES)) { + if (ctx->merge) { sgl = list_entry(ctx->tsgl_list.prev, struct af_alg_tsgl, list); sg = sgl->sg + sgl->cur - 1; @@ -1021,60 +1021,37 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size, if (sgl->cur) sg_unmark_end(sg + sgl->cur - 1); - if (msg->msg_flags & MSG_SPLICE_PAGES) { - struct sg_table sgtable = { - .sgl = sg, - .nents = sgl->cur, - .orig_nents = sgl->cur, - }; - - plen = extract_iter_to_sg(&msg->msg_iter, len, &sgtable, - MAX_SGL_ENTS - sgl->cur, 0); - if (plen < 0) { - err = plen; + do { + struct page *pg; + unsigned int i = sgl->cur; + + plen = min_t(size_t, len, PAGE_SIZE); + + pg = alloc_page(GFP_KERNEL); + if (!pg) { + err = -ENOMEM; goto unlock; } - for (; sgl->cur < sgtable.nents; sgl->cur++) - get_page(sg_page(&sg[sgl->cur])); + sg_assign_page(sg + i, pg); + + err = memcpy_from_msg(page_address(sg_page(sg + i)), + msg, plen); + if (err) { + __free_page(sg_page(sg + i)); + sg_assign_page(sg + i, NULL); + goto unlock; + } + + sg[i].length = plen; len -= plen; ctx->used += plen; copied += plen; size -= plen; - } else { - do { - struct page *pg; - unsigned int i = sgl->cur; - - plen = min_t(size_t, len, PAGE_SIZE); - - pg = alloc_page(GFP_KERNEL); - if (!pg) { - err = -ENOMEM; - goto unlock; - } - - sg_assign_page(sg + i, pg); - - err = memcpy_from_msg( - page_address(sg_page(sg + i)), - msg, plen); - if (err) { - __free_page(sg_page(sg + i)); - sg_assign_page(sg + i, NULL); - goto unlock; - } - - sg[i].length = plen; - len -= plen; - ctx->used += plen; - copied += plen; - size -= plen; - sgl->cur++; - } while (len && sgl->cur < MAX_SGL_ENTS); - - ctx->merge = plen & (PAGE_SIZE - 1); - } + sgl->cur++; + } while (len && sgl->cur < MAX_SGL_ENTS); + + ctx->merge = plen & (PAGE_SIZE - 1); if (!size) sg_mark_end(sg + sgl->cur - 1); diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index cb651ab58d629..c6c2ce21895dd 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -9,10 +9,10 @@ * The following concept of the memory management is used: * * The kernel maintains two SGLs, the TX SGL and the RX SGL. The TX SGL is - * filled by user space with the data submitted via sendmsg (maybe with - * MSG_SPLICE_PAGES). Filling up the TX SGL does not cause a crypto operation - * -- the data will only be tracked by the kernel. Upon receipt of one recvmsg - * call, the caller must provide a buffer which is tracked with the RX SGL. + * filled by user space with the data submitted via sendmsg. Filling up the TX + * SGL does not cause a crypto operation -- the data will only be tracked by the + * kernel. Upon receipt of one recvmsg call, the caller must provide a buffer + * which is tracked with the RX SGL. * * During the processing of the recvmsg operation, the cipher request is * allocated and prepared. As part of the recvmsg operation, the processed From 6f7b8e0321f3a8fbbd267d2ac15c671ab59e919e Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 27 Apr 2026 18:39:37 +0200 Subject: [PATCH 0458/3297] crypto: caam - use print_hex_dump_devel to guard key hex dumps commit 3f57657b6ea23f933371f2c2846322f441773cee upstream. Use print_hex_dump_devel() for dumping sensitive key material in *_setkey() and gen_split_key() to avoid leaking secrets at runtime when CONFIG_DYNAMIC_DEBUG is enabled. Fixes: 6e005503199b ("crypto: caam - print debug messages at debug level") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/caam/caamalg.c | 12 ++++++------ drivers/crypto/caam/caamalg_qi.c | 12 ++++++------ drivers/crypto/caam/caamhash.c | 4 ++-- drivers/crypto/caam/key_gen.c | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 2cfb1b8d8c7cf..1bec11f3abbb1 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -597,7 +597,7 @@ static int aead_setkey(struct crypto_aead *aead, dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* @@ -633,7 +633,7 @@ static int aead_setkey(struct crypto_aead *aead, dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -674,7 +674,7 @@ static int gcm_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -695,7 +695,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -721,7 +721,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (err) return err; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -748,7 +748,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, u32 *desc; const bool is_rfc3686 = alg->caam.rfc3686; - print_hex_dump_debug("key in @"__stringify(__LINE__)": ", + print_hex_dump_devel("key in @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->cdata.keylen = keylen; diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c index 65f6adb6c673f..aa779caacfe59 100644 --- a/drivers/crypto/caam/caamalg_qi.c +++ b/drivers/crypto/caam/caamalg_qi.c @@ -212,7 +212,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); /* @@ -248,7 +248,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -371,7 +371,7 @@ static int gcm_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -475,7 +475,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -581,7 +581,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -631,7 +631,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, const bool is_rfc3686 = alg->caam.rfc3686; int ret = 0; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->cdata.keylen = keylen; diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index cc942e5ab2799..4c2a7159398d6 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -505,7 +505,7 @@ static int axcbc_setkey(struct crypto_ahash *ahash, const u8 *key, DMA_TO_DEVICE); ctx->adata.keylen = keylen; - print_hex_dump_debug("axcbc ctx.key@" __stringify(__LINE__)" : ", + print_hex_dump_devel("axcbc ctx.key@" __stringify(__LINE__)" : ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, keylen, 1); return axcbc_set_sh_desc(ahash); @@ -525,7 +525,7 @@ static int acmac_setkey(struct crypto_ahash *ahash, const u8 *key, ctx->adata.key_virt = key; ctx->adata.keylen = keylen; - print_hex_dump_debug("acmac ctx.key@" __stringify(__LINE__)" : ", + print_hex_dump_devel("acmac ctx.key@" __stringify(__LINE__)" : ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); return acmac_set_sh_desc(ahash); diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c index 88cc4fe2a585d..de2fcc387477d 100644 --- a/drivers/crypto/caam/key_gen.c +++ b/drivers/crypto/caam/key_gen.c @@ -58,7 +58,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, dev_dbg(jrdev, "split keylen %d split keylen padded %d\n", adata->keylen, adata->keylen_pad); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key_in, keylen, 1); if (local_max > max_keylen) @@ -113,7 +113,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out, wait_for_completion(&result.completion); ret = result.err; - print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key_out, adata->keylen_pad, 1); } From d0b8cafd529b4ec759190c6081f7a76efb563a8f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 27 Apr 2026 18:39:39 +0200 Subject: [PATCH 0459/3297] crypto: caam - use print_hex_dump_devel to guard key hex dumps again commit 8005dc808bcce7d6cc2ae015a3cde1683bee602d upstream. Use print_hex_dump_devel() for dumping sensitive key material in *_setkey() to avoid leaking secrets at runtime when CONFIG_DYNAMIC_DEBUG is enabled. Fixes: 8d818c105501 ("crypto: caam/qi2 - add DPAA2-CAAM driver") Fixes: 226853ac3ebe ("crypto: caam/qi2 - add skcipher algorithms") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/caam/caamalg_qi2.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c index c97c10cdf207a..b26b5395ffef1 100644 --- a/drivers/crypto/caam/caamalg_qi2.c +++ b/drivers/crypto/caam/caamalg_qi2.c @@ -300,7 +300,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, dev_dbg(dev, "keylen %d enckeylen %d authkeylen %d\n", keys.authkeylen + keys.enckeylen, keys.enckeylen, keys.authkeylen); - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->adata.keylen = keys.authkeylen; @@ -314,7 +314,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key, memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen); dma_sync_single_for_device(dev, ctx->key_dma, ctx->adata.keylen_pad + keys.enckeylen, ctx->dir); - print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ", + print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); @@ -731,7 +731,7 @@ static int gcm_setkey(struct crypto_aead *aead, ret = aes_check_keylen(keylen); if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -827,7 +827,7 @@ static int rfc4106_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -926,7 +926,7 @@ static int rfc4543_setkey(struct crypto_aead *aead, if (ret) return ret; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); memcpy(ctx->key, key, keylen); @@ -954,7 +954,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key, u32 *desc; const bool is_rfc3686 = alg->caam.rfc3686; - print_hex_dump_debug("key in @" __stringify(__LINE__)": ", + print_hex_dump_devel("key in @" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1); ctx->cdata.keylen = keylen; From 0016d3c21c6ab60a20be7f565cefb5999f3adeb6 Mon Sep 17 00:00:00 2001 From: Xiaonan Zhao Date: Tue, 26 May 2026 18:11:43 +0800 Subject: [PATCH 0460/3297] crypto: chacha20poly1305 - validate poly1305 template argument commit 265b861bece38318b8e0fc8fac0643d4ef906d31 upstream. chachapoly_create() still accepts the compatibility poly1305 parameter in the template name, but it assumes the second template argument is always present and immediately passes it to strcmp(). When the argument is missing, crypto_attr_alg_name() returns an error pointer. Check for that before comparing the name so malformed template instantiations fail with an error instead of dereferencing the error pointer in strcmp(). This matches the surrounding Crypto API template pattern where crypto_attr_alg_name() results are validated before string-specific use. Fixes: a298765e28ad ("crypto: chacha20poly1305 - Use lib/crypto poly1305") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Co-developed-by: Luxing Yin Signed-off-by: Luxing Yin Signed-off-by: Xiaonan Zhao Signed-off-by: Ren Wei Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/chacha20poly1305.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crypto/chacha20poly1305.c b/crypto/chacha20poly1305.c index b4b5a7198d841..27df9e1eb0580 100644 --- a/crypto/chacha20poly1305.c +++ b/crypto/chacha20poly1305.c @@ -375,6 +375,7 @@ static int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb, struct aead_instance *inst; struct chachapoly_instance_ctx *ctx; struct skcipher_alg_common *chacha; + const char *poly_name; int err; if (ivsize > CHACHAPOLY_IV_SIZE) @@ -396,9 +397,15 @@ static int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb, goto err_free_inst; chacha = crypto_spawn_skcipher_alg_common(&ctx->chacha); + poly_name = crypto_attr_alg_name(tb[2]); + if (IS_ERR(poly_name)) { + err = PTR_ERR(poly_name); + goto err_free_inst; + } + err = -EINVAL; - if (strcmp(crypto_attr_alg_name(tb[2]), "poly1305") && - strcmp(crypto_attr_alg_name(tb[2]), "poly1305-generic")) + if (strcmp(poly_name, "poly1305") && + strcmp(poly_name, "poly1305-generic")) goto err_free_inst; /* Need 16-byte IV size, including Initial Block Counter value */ if (chacha->ivsize != CHACHA_IV_SIZE) From ac667f9f18c6b36e02eef59ea46d647729b0805f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 29 May 2026 15:04:30 -0700 Subject: [PATCH 0461/3297] crypto: crypto4xx - Remove insecure and unused rng_alg commit 7811ec9e973d2c9e465083699f0c8240b98cb8c4 upstream. Remove crypto4xx_rng, as it is insecure and unused: - It has only a 64-bit security strength, which is highly inadequate. This can be seen by the fact that crypto4xx_hw_init() seeds it with only 64 bits of entropy, and the fact that the original commit mentions that it implements ANSI X9.17 Annex C. Another issue was that this driver didn't implement the crypto_rng API correctly, as crypto4xx_prng_generate() didn't return 0 on success. - No user of this code is known. It's usable only theoretically via the "rng" algorithm type of AF_ALG. But userspace actually just uses the actual Linux RNG (/dev/random etc) instead. And rng_algs don't contribute entropy to the actual Linux RNG either. (This may have been confused with hwrng, which does contribute entropy.) Fixes: d072bfa48853 ("crypto: crypto4xx - add prng crypto support") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Acked-by: Christian Lamparter Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/amcc/crypto4xx_core.c | 88 ------------------------- drivers/crypto/amcc/crypto4xx_core.h | 4 -- drivers/crypto/amcc/crypto4xx_reg_def.h | 11 ---- 3 files changed, 103 deletions(-) diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index 8cdc66d520c99..be55eb6a03164 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -31,11 +31,9 @@ #include #include #include -#include #include #include #include -#include #include #include "crypto4xx_reg_def.h" #include "crypto4xx_core.h" @@ -986,10 +984,6 @@ static int crypto4xx_register_alg(struct crypto4xx_device *sec_dev, rc = crypto_register_aead(&alg->alg.u.aead); break; - case CRYPTO_ALG_TYPE_RNG: - rc = crypto_register_rng(&alg->alg.u.rng); - break; - default: rc = crypto_register_skcipher(&alg->alg.u.cipher); break; @@ -1015,10 +1009,6 @@ static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev) crypto_unregister_aead(&alg->alg.u.aead); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&alg->alg.u.rng); - break; - default: crypto_unregister_skcipher(&alg->alg.u.cipher); } @@ -1077,69 +1067,6 @@ static irqreturn_t crypto4xx_ce_interrupt_handler_revb(int irq, void *data) PPC4XX_TMO_ERR_INT); } -static int ppc4xx_prng_data_read(struct crypto4xx_device *dev, - u8 *data, unsigned int max) -{ - unsigned int i, curr = 0; - u32 val[2]; - - do { - /* trigger PRN generation */ - writel(PPC4XX_PRNG_CTRL_AUTO_EN, - dev->ce_base + CRYPTO4XX_PRNG_CTRL); - - for (i = 0; i < 1024; i++) { - /* usually 19 iterations are enough */ - if ((readl(dev->ce_base + CRYPTO4XX_PRNG_STAT) & - CRYPTO4XX_PRNG_STAT_BUSY)) - continue; - - val[0] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_0); - val[1] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_1); - break; - } - if (i == 1024) - return -ETIMEDOUT; - - if ((max - curr) >= 8) { - memcpy(data, &val, 8); - data += 8; - curr += 8; - } else { - /* copy only remaining bytes */ - memcpy(data, &val, max - curr); - break; - } - } while (curr < max); - - return curr; -} - -static int crypto4xx_prng_generate(struct crypto_rng *tfm, - const u8 *src, unsigned int slen, - u8 *dstn, unsigned int dlen) -{ - struct rng_alg *alg = crypto_rng_alg(tfm); - struct crypto4xx_alg *amcc_alg; - struct crypto4xx_device *dev; - int ret; - - amcc_alg = container_of(alg, struct crypto4xx_alg, alg.u.rng); - dev = amcc_alg->dev; - - mutex_lock(&dev->core_dev->rng_lock); - ret = ppc4xx_prng_data_read(dev, dstn, dlen); - mutex_unlock(&dev->core_dev->rng_lock); - return ret; -} - - -static int crypto4xx_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - return 0; -} - /* * Supported Crypto Algorithms */ @@ -1269,18 +1196,6 @@ static struct crypto4xx_alg_common crypto4xx_alg[] = { .cra_module = THIS_MODULE, }, } }, - { .type = CRYPTO_ALG_TYPE_RNG, .u.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "crypto4xx_rng", - .cra_priority = 300, - .cra_ctxsize = 0, - .cra_module = THIS_MODULE, - }, - .generate = crypto4xx_prng_generate, - .seed = crypto4xx_prng_seed, - .seedsize = 0, - } }, }; /* @@ -1354,9 +1269,6 @@ static int crypto4xx_probe(struct platform_device *ofdev) core_dev->dev->core_dev = core_dev; core_dev->dev->is_revb = is_revb; core_dev->device = dev; - rc = devm_mutex_init(&ofdev->dev, &core_dev->rng_lock); - if (rc) - return rc; spin_lock_init(&core_dev->lock); INIT_LIST_HEAD(&core_dev->dev->alg_list); ratelimit_default_init(&core_dev->dev->aead_ratelimit); diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h index ee36630c670f2..3a028aec3f0c1 100644 --- a/drivers/crypto/amcc/crypto4xx_core.h +++ b/drivers/crypto/amcc/crypto4xx_core.h @@ -14,10 +14,8 @@ #define __CRYPTO4XX_CORE_H__ #include -#include #include #include -#include #include #include "crypto4xx_reg_def.h" #include "crypto4xx_sa.h" @@ -111,7 +109,6 @@ struct crypto4xx_core_device { u32 irq; struct tasklet_struct tasklet; spinlock_t lock; - struct mutex rng_lock; }; struct crypto4xx_ctx { @@ -135,7 +132,6 @@ struct crypto4xx_alg_common { union { struct skcipher_alg cipher; struct aead_alg aead; - struct rng_alg rng; } u; }; diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h index 1038061224da6..73d626308a848 100644 --- a/drivers/crypto/amcc/crypto4xx_reg_def.h +++ b/drivers/crypto/amcc/crypto4xx_reg_def.h @@ -90,20 +90,9 @@ #define CRYPTO4XX_BYTE_ORDER_CFG 0x000600d8 #define CRYPTO4XX_ENDIAN_CFG 0x000600d8 -#define CRYPTO4XX_PRNG_STAT 0x00070000 -#define CRYPTO4XX_PRNG_STAT_BUSY 0x1 #define CRYPTO4XX_PRNG_CTRL 0x00070004 #define CRYPTO4XX_PRNG_SEED_L 0x00070008 #define CRYPTO4XX_PRNG_SEED_H 0x0007000c - -#define CRYPTO4XX_PRNG_RES_0 0x00070020 -#define CRYPTO4XX_PRNG_RES_1 0x00070024 -#define CRYPTO4XX_PRNG_RES_2 0x00070028 -#define CRYPTO4XX_PRNG_RES_3 0x0007002C - -#define CRYPTO4XX_PRNG_LFSR_L 0x00070030 -#define CRYPTO4XX_PRNG_LFSR_H 0x00070034 - /* * Initialize CRYPTO ENGINE registers, and memory bases. */ From 774ddddf5eb26eeca177350413e3e2bc50930ee9 Mon Sep 17 00:00:00 2001 From: Anastasia Tishchenko Date: Wed, 13 May 2026 13:57:40 +0300 Subject: [PATCH 0462/3297] crypto: ecc - Fix carry overflow in vli multiplication commit 27b536a2ec8e2f85a0380c2d13c9ecbc7aaab406 upstream. The carry flag calculation fails when r01.m_high is saturated (0xFFFFFFFFFFFFFFFF) and addition of lower bits overflows. The condition (r01.m_high < product.m_high) doesn't handle the case where r01.m_high == product.m_high and an additional carry exists from lower-bit overflow. When commit 3c4b23901a0c ("crypto: ecdh - Add ECDH software support") introduced crypto/ecc.c, it split the muladd() function in the micro-ecc library into separate mul_64_64() and add_128_128() helpers. It seems the check got lost in translation. Add proper handling for this boundary by accounting for the carry from the lower addition. Fixes: 3c4b23901a0c ("crypto: ecdh - Add ECDH software support") Signed-off-by: Anastasia Tishchenko Cc: stable@vger.kernel.org # v4.8+ Reviewed-by: Lukas Wunner Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/ecc.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/crypto/ecc.c b/crypto/ecc.c index 6cf9a945fc6c2..f97ff234086ff 100644 --- a/crypto/ecc.c +++ b/crypto/ecc.c @@ -402,14 +402,26 @@ static uint128_t mul_64_64(u64 left, u64 right) return result; } -static uint128_t add_128_128(uint128_t a, uint128_t b) +/* Calculate addition with overflow checking. Returns true on wrap-around, + * false otherwise. + */ +static bool check_add_128_128_overflow(uint128_t *result, uint128_t a, + uint128_t b) { - uint128_t result; + bool carry; - result.m_low = a.m_low + b.m_low; - result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low); + result->m_low = a.m_low + b.m_low; + carry = (result->m_low < a.m_low); - return result; + result->m_high = a.m_high + b.m_high + carry; + + /* Using constant-time bitwise arithmetic to prevent timing + * side-channels. + */ + carry = (result->m_high < a.m_high) | + ((result->m_high == a.m_high) & carry); + + return carry; } static void vli_mult(u64 *result, const u64 *left, const u64 *right, @@ -434,9 +446,7 @@ static void vli_mult(u64 *result, const u64 *left, const u64 *right, uint128_t product; product = mul_64_64(left[i], right[k - i]); - - r01 = add_128_128(r01, product); - r2 += (r01.m_high < product.m_high); + r2 += check_add_128_128_overflow(&r01, r01, product); } result[k] = r01.m_low; @@ -459,7 +469,7 @@ static void vli_umult(u64 *result, const u64 *left, u32 right, uint128_t product; product = mul_64_64(left[k], right); - r01 = add_128_128(r01, product); + check_add_128_128_overflow(&r01, r01, product); /* no carry */ result[k] = r01.m_low; r01.m_low = r01.m_high; @@ -496,8 +506,7 @@ static void vli_square(u64 *result, const u64 *left, unsigned int ndigits) product.m_low <<= 1; } - r01 = add_128_128(r01, product); - r2 += (r01.m_high < product.m_high); + r2 += check_add_128_128_overflow(&r01, r01, product); } result[k] = r01.m_low; From ee6a2a25665c6b599091acac22dc28e6c0006e12 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 30 May 2026 13:26:23 -0700 Subject: [PATCH 0463/3297] crypto: hisi-trng - Remove crypto_rng interface commit 216a7795ec210bdabd5dad42323eee70bbfc8d90 upstream. drivers/crypto/hisilicon/trng/trng.c exposes the same hardware through two completely separate interfaces, crypto_rng and hwrng. However, the implementation of this is buggy because it permits generation operations from these interfaces to run concurrently with each other, accessing the same registers. That is, hisi_trng_generate() synchronizes with itself but not with hisi_trng_read(). This results in potential repetition of output from the RNG, output of non-random values, etc. Fortunately, there's actually no point in hardware RNG drivers implementing the crypto_rng interface. It's not actually used by anything besides the "rng" algorithm type of AF_ALG, which in turn is not actually used in practice. Other crypto_rng hardware drivers are likewise being phased out, leaving just the hwrng support. Thus, remove it to simplify the code and avoid conflict (and confusion) with the hwrng interface which is the one that actually matters. Fixes: e4d9d10ef4be ("crypto: hisilicon/trng - add support for PRNG") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/hisilicon/Kconfig | 1 - drivers/crypto/hisilicon/trng/trng.c | 296 +-------------------------- 2 files changed, 2 insertions(+), 295 deletions(-) diff --git a/drivers/crypto/hisilicon/Kconfig b/drivers/crypto/hisilicon/Kconfig index a0cb1a8186ac9..6f2094404079e 100644 --- a/drivers/crypto/hisilicon/Kconfig +++ b/drivers/crypto/hisilicon/Kconfig @@ -79,6 +79,5 @@ config CRYPTO_DEV_HISI_TRNG tristate "Support for HISI TRNG Driver" depends on ARM64 && ACPI select HW_RANDOM - select CRYPTO_RNG help Support for HiSilicon TRNG Driver. diff --git a/drivers/crypto/hisilicon/trng/trng.c b/drivers/crypto/hisilicon/trng/trng.c index 5ca0b90859a81..6584ed051e099 100644 --- a/drivers/crypto/hisilicon/trng/trng.c +++ b/drivers/crypto/hisilicon/trng/trng.c @@ -1,234 +1,27 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 HiSilicon Limited. */ -#include #include -#include #include #include #include #include #include -#include #include -#include #include #include #define HISI_TRNG_REG 0x00F0 #define HISI_TRNG_BYTES 4 #define HISI_TRNG_QUALITY 512 -#define HISI_TRNG_VERSION 0x01B8 -#define HISI_TRNG_VER_V1 GENMASK(31, 0) #define SLEEP_US 10 #define TIMEOUT_US 10000 -#define SW_DRBG_NUM_SHIFT 2 -#define SW_DRBG_KEY_BASE 0x082C -#define SW_DRBG_SEED(n) (SW_DRBG_KEY_BASE - ((n) << SW_DRBG_NUM_SHIFT)) -#define SW_DRBG_SEED_REGS_NUM 12 -#define SW_DRBG_SEED_SIZE 48 -#define SW_DRBG_BLOCKS 0x0830 -#define SW_DRBG_INIT 0x0834 -#define SW_DRBG_GEN 0x083c -#define SW_DRBG_STATUS 0x0840 -#define SW_DRBG_BLOCKS_NUM 4095 -#define SW_DRBG_DATA_BASE 0x0850 -#define SW_DRBG_DATA_NUM 4 -#define SW_DRBG_DATA(n) (SW_DRBG_DATA_BASE - ((n) << SW_DRBG_NUM_SHIFT)) -#define SW_DRBG_BYTES 16 -#define SW_DRBG_ENABLE_SHIFT 12 -#define SEED_SHIFT_24 24 -#define SEED_SHIFT_16 16 -#define SEED_SHIFT_8 8 -#define SW_MAX_RANDOM_BYTES 65520 - -struct hisi_trng_list { - struct mutex lock; - struct list_head list; - bool is_init; -}; struct hisi_trng { void __iomem *base; - struct hisi_trng_list *trng_list; - struct list_head list; struct hwrng rng; - u32 ver; - u32 ctx_num; - /* The bytes of the random number generated since the last seeding. */ - u32 random_bytes; - struct mutex lock; -}; - -struct hisi_trng_ctx { - struct hisi_trng *trng; }; -static atomic_t trng_active_devs; -static struct hisi_trng_list trng_devices; -static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait); - -static int hisi_trng_set_seed(struct hisi_trng *trng, const u8 *seed) -{ - u32 val, seed_reg, i; - int ret; - - writel(0x0, trng->base + SW_DRBG_BLOCKS); - - for (i = 0; i < SW_DRBG_SEED_SIZE; - i += SW_DRBG_SEED_SIZE / SW_DRBG_SEED_REGS_NUM) { - val = seed[i] << SEED_SHIFT_24; - val |= seed[i + 1UL] << SEED_SHIFT_16; - val |= seed[i + 2UL] << SEED_SHIFT_8; - val |= seed[i + 3UL]; - - seed_reg = (i >> SW_DRBG_NUM_SHIFT) % SW_DRBG_SEED_REGS_NUM; - writel(val, trng->base + SW_DRBG_SEED(seed_reg)); - } - - writel(SW_DRBG_BLOCKS_NUM | (0x1 << SW_DRBG_ENABLE_SHIFT), - trng->base + SW_DRBG_BLOCKS); - writel(0x1, trng->base + SW_DRBG_INIT); - ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, - val, val & BIT(0), SLEEP_US, TIMEOUT_US); - if (ret) { - pr_err("failed to init trng(%d)\n", ret); - return -EIO; - } - - trng->random_bytes = 0; - - return 0; -} - -static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); - struct hisi_trng *trng = ctx->trng; - int ret; - - if (slen < SW_DRBG_SEED_SIZE) { - pr_err("slen(%u) is not matched with trng(%d)\n", slen, - SW_DRBG_SEED_SIZE); - return -EINVAL; - } - - mutex_lock(&trng->lock); - ret = hisi_trng_set_seed(trng, seed); - mutex_unlock(&trng->lock); - - return ret; -} - -static int hisi_trng_reseed(struct hisi_trng *trng) -{ - u8 seed[SW_DRBG_SEED_SIZE]; - int size; - - if (!trng->random_bytes) - return 0; - - size = hisi_trng_read(&trng->rng, seed, SW_DRBG_SEED_SIZE, false); - if (size != SW_DRBG_SEED_SIZE) - return -EIO; - - return hisi_trng_set_seed(trng, seed); -} - -static int hisi_trng_get_bytes(struct hisi_trng *trng, u8 *dstn, unsigned int dlen) -{ - u32 data[SW_DRBG_DATA_NUM]; - u32 currsize = 0; - u32 val = 0; - int ret; - u32 i; - - ret = hisi_trng_reseed(trng); - if (ret) - return ret; - - do { - ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, - val, val & BIT(1), SLEEP_US, TIMEOUT_US); - if (ret) { - pr_err("failed to generate random number(%d)!\n", ret); - break; - } - - for (i = 0; i < SW_DRBG_DATA_NUM; i++) - data[i] = readl(trng->base + SW_DRBG_DATA(i)); - - if (dlen - currsize >= SW_DRBG_BYTES) { - memcpy(dstn + currsize, data, SW_DRBG_BYTES); - currsize += SW_DRBG_BYTES; - } else { - memcpy(dstn + currsize, data, dlen - currsize); - currsize = dlen; - } - - trng->random_bytes += SW_DRBG_BYTES; - writel(0x1, trng->base + SW_DRBG_GEN); - } while (currsize < dlen); - - return ret; -} - -static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dstn, unsigned int dlen) -{ - struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); - struct hisi_trng *trng = ctx->trng; - unsigned int currsize = 0; - unsigned int block_size; - int ret; - - if (!dstn || !dlen) { - pr_err("output is error, dlen %u!\n", dlen); - return -EINVAL; - } - - do { - block_size = min_t(unsigned int, dlen - currsize, SW_MAX_RANDOM_BYTES); - mutex_lock(&trng->lock); - ret = hisi_trng_get_bytes(trng, dstn + currsize, block_size); - mutex_unlock(&trng->lock); - if (ret) - return ret; - currsize += block_size; - } while (currsize < dlen); - - return 0; -} - -static int hisi_trng_init(struct crypto_tfm *tfm) -{ - struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); - struct hisi_trng *trng; - u32 ctx_num = ~0; - - mutex_lock(&trng_devices.lock); - list_for_each_entry(trng, &trng_devices.list, list) { - if (trng->ctx_num < ctx_num) { - ctx_num = trng->ctx_num; - ctx->trng = trng; - } - } - ctx->trng->ctx_num++; - mutex_unlock(&trng_devices.lock); - - return 0; -} - -static void hisi_trng_exit(struct crypto_tfm *tfm) -{ - struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); - - mutex_lock(&trng_devices.lock); - ctx->trng->ctx_num--; - mutex_unlock(&trng_devices.lock); -} - static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { struct hisi_trng *trng; @@ -260,42 +53,6 @@ static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) return currsize; } -static struct rng_alg hisi_trng_alg = { - .generate = hisi_trng_generate, - .seed = hisi_trng_seed, - .seedsize = SW_DRBG_SEED_SIZE, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "hisi_stdrng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct hisi_trng_ctx), - .cra_module = THIS_MODULE, - .cra_init = hisi_trng_init, - .cra_exit = hisi_trng_exit, - }, -}; - -static void hisi_trng_add_to_list(struct hisi_trng *trng) -{ - mutex_lock(&trng_devices.lock); - list_add_tail(&trng->list, &trng_devices.list); - mutex_unlock(&trng_devices.lock); -} - -static int hisi_trng_del_from_list(struct hisi_trng *trng) -{ - int ret = -EBUSY; - - mutex_lock(&trng_devices.lock); - if (!trng->ctx_num) { - list_del(&trng->list); - ret = 0; - } - mutex_unlock(&trng_devices.lock); - - return ret; -} - static int hisi_trng_probe(struct platform_device *pdev) { struct hisi_trng *trng; @@ -305,68 +62,20 @@ static int hisi_trng_probe(struct platform_device *pdev) if (!trng) return -ENOMEM; - platform_set_drvdata(pdev, trng); - trng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(trng->base)) return PTR_ERR(trng->base); - trng->ctx_num = 0; - trng->random_bytes = SW_MAX_RANDOM_BYTES; - mutex_init(&trng->lock); - trng->ver = readl(trng->base + HISI_TRNG_VERSION); - if (!trng_devices.is_init) { - INIT_LIST_HEAD(&trng_devices.list); - mutex_init(&trng_devices.lock); - trng_devices.is_init = true; - } - - hisi_trng_add_to_list(trng); - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_inc_return(&trng_active_devs) == 1) { - ret = crypto_register_rng(&hisi_trng_alg); - if (ret) { - dev_err(&pdev->dev, - "failed to register crypto(%d)\n", ret); - atomic_dec_return(&trng_active_devs); - goto err_remove_from_list; - } - } - trng->rng.name = pdev->name; trng->rng.read = hisi_trng_read; trng->rng.quality = HISI_TRNG_QUALITY; + ret = devm_hwrng_register(&pdev->dev, &trng->rng); - if (ret) { + if (ret) dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret); - goto err_crypto_unregister; - } - - return ret; - -err_crypto_unregister: - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_dec_return(&trng_active_devs) == 0) - crypto_unregister_rng(&hisi_trng_alg); - -err_remove_from_list: - hisi_trng_del_from_list(trng); return ret; } -static void hisi_trng_remove(struct platform_device *pdev) -{ - struct hisi_trng *trng = platform_get_drvdata(pdev); - - /* Wait until the task is finished */ - while (hisi_trng_del_from_list(trng)) - ; - - if (trng->ver != HISI_TRNG_VER_V1 && - atomic_dec_return(&trng_active_devs) == 0) - crypto_unregister_rng(&hisi_trng_alg); -} - static const struct acpi_device_id hisi_trng_acpi_match[] = { { "HISI02B3", 0 }, { } @@ -375,7 +84,6 @@ MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); static struct platform_driver hisi_trng_driver = { .probe = hisi_trng_probe, - .remove = hisi_trng_remove, .driver = { .name = "hisi-trng-v2", .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), From c4bd2f4c35b0e15b6040c2f7e7e7986780c066cf Mon Sep 17 00:00:00 2001 From: Ruijie Li Date: Mon, 25 May 2026 19:45:21 +0800 Subject: [PATCH 0464/3297] crypto: pcrypt - restore callback for non-parallel fallback commit ed459fe319376e876de433d12b6c6772e612ca36 upstream. pcrypt installs pcrypt_aead_done() on the child AEAD request before trying to submit it through padata. If padata_do_parallel() returns -EBUSY, pcrypt falls back to calling the child AEAD directly. That fallback must not keep the padata completion callback. Otherwise an asynchronous completion runs pcrypt_aead_done() even though the request was never enrolled in padata. Restore the original request callback and callback data before calling the child AEAD directly. This keeps the fallback path aligned with a direct AEAD request while leaving the parallel path unchanged. Fixes: 662f2f13e66d ("crypto: pcrypt - Call crypto layer directly when padata_do_parallel() return -EBUSY") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Ruijie Li Signed-off-by: Ren Wei Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/pcrypt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index ed0feaba23832..9f372442981e6 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -122,6 +122,8 @@ static int pcrypt_aead_encrypt(struct aead_request *req) return -EINPROGRESS; if (err == -EBUSY) { /* try non-parallel mode */ + aead_request_set_callback(creq, flags, req->base.complete, + req->base.data); return crypto_aead_encrypt(creq); } @@ -173,6 +175,8 @@ static int pcrypt_aead_decrypt(struct aead_request *req) return -EINPROGRESS; if (err == -EBUSY) { /* try non-parallel mode */ + aead_request_set_callback(creq, flags, req->base.complete, + req->base.data); return crypto_aead_decrypt(creq); } From cc4e42b3ee9fc53ba1a63106c2db46f17ffedd00 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 4 Jun 2026 10:27:06 +0000 Subject: [PATCH 0465/3297] crypto: tegra - fix refcount leak in tegra_se_host1x_submit() commit 6ea0ce3a19f9c37a014099e2b0a46b27fa164564 upstream. The timeout error path in tegra_se_host1x_submit() returns without calling host1x_job_put(), while all other paths (success, submit error, pin error) properly release the job reference through the job_put label. Since host1x_job_alloc() initializes the reference count and host1x_job_put() is required to drop it, omitting it on timeout causes a permanent refcount leak. Fix this by redirecting the timeout return to the existing job_put label, ensuring the job reference and any associated syncpt references are consistently released. Cc: stable@vger.kernel.org Fixes: 0880bb3b00c8 ("crypto: tegra - Add Tegra Security Engine driver") Signed-off-by: Wentao Liang Reviewed-by: Akhil R Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/tegra/tegra-se-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c index 7237f14eaf5ae..2755f19ad05e9 100644 --- a/drivers/crypto/tegra/tegra-se-main.c +++ b/drivers/crypto/tegra/tegra-se-main.c @@ -180,7 +180,7 @@ int tegra_se_host1x_submit(struct tegra_se *se, struct tegra_se_cmdbuf *cmdbuf, MAX_SCHEDULE_TIMEOUT, NULL); if (ret) { dev_err(se->dev, "host1x job timed out\n"); - return ret; + goto job_put; } host1x_job_put(job); From 53b8fb85f332b71f9946c1216620e57f1bfde72d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 21 May 2026 21:25:25 -0500 Subject: [PATCH 0466/3297] crypto: loongson - Select CRYPTO_RNG commit 4c600ab0d8cfc9d75b92f3426dbcb2ad85eac91d upstream. This driver registers a rng_alg, so it requires CRYPTO_RNG. Fixes: 766b2d724c8d ("crypto: loongson - add Loongson RNG driver support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605201622.qWOiiZTV-lkp@intel.com/ Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Reviewed-by: Huacai Chen Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/loongson/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/crypto/loongson/Kconfig b/drivers/crypto/loongson/Kconfig index 15475da8fc11d..f4e1544ffbb49 100644 --- a/drivers/crypto/loongson/Kconfig +++ b/drivers/crypto/loongson/Kconfig @@ -1,5 +1,6 @@ config CRYPTO_DEV_LOONGSON_RNG tristate "Support for Loongson RNG Driver" depends on MFD_LOONGSON_SE + select CRYPTO_RNG help Support for Loongson RNG Driver. From 9e983d0a74a6a2348e4ce61647ec8a4dfbe198ac Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:44 -0600 Subject: [PATCH 0467/3297] crypto: ccp - Do not initialize SNP for SEV ioctls commit fb1758e74b8061aacfbce7bbb7a7cc650537e167 upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? sev_move_to_init_state() is called for ioctls requiring only SEV firmware: SEV_PEK_GEN, SEV_PDH_GEN, SEV_PEK_CSR, SEV_PEK_CERT_IMPORT, and SEV_PDH_CERT_EXPORT. After the firmware command, it does SEV_SHUTDOWN on the SEV firmware. Since these commands do not require SNP to be initialized, skip it by calling __sev_platform_init_locked() which only initializes the SEV firmware. This way SNP is not Initialized at all, and HSAVE_PA is not cleared. The previous code saved any SEV initialization firmware error to init_args.error and then threw it away and hardcoded the return value of INVALID_PLATFORM_STATE regardless of the real firmware error. This patch changes it to surface the underlying error, which is hopefully both more useful and doesn't cause any problems. Note that it is still safe to call __sev_firmware_shutdown() directly: it calls __sev_snp_shutdown_locked(), which skips SNP shutdown if SNP was not initialized. Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 5c7a5ff2d9eaa..ac640dcda7030 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1689,14 +1689,11 @@ static int sev_get_platform_state(int *state, int *error) static int sev_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_required) { - struct sev_platform_init_args init_args = {0}; int rc; - rc = _sev_platform_init_locked(&init_args); - if (rc) { - argp->error = SEV_RET_INVALID_PLATFORM_STATE; + rc = __sev_platform_init_locked(&argp->error); + if (rc) return rc; - } *shutdown_required = true; From 7a361c74bb12f3398c388905f1d325be642cd36e Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:45 -0600 Subject: [PATCH 0468/3297] crypto: ccp - Do not initialize SNP for ioctl(SNP_COMMIT) commit 5a1364da2f04217a36e2fdfa2db4ee025b383a20 upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? The SNP_COMMIT command does not require the firmware to be in any particular state. Skip initializing it if it was previously uninitialized. The SEV-SNP firmware specification doc 56860 does not mention SNP_COMMIT in Table 5 as a command that is allowed in the UNINIT state, but it is in fact allowed and a future documentation update will reflect that. Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index ac640dcda7030..42dd9b2717798 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2411,24 +2411,13 @@ static int sev_ioctl_do_snp_platform_status(struct sev_issue_cmd *argp) static int sev_ioctl_do_snp_commit(struct sev_issue_cmd *argp) { - struct sev_device *sev = psp_master->sev_data; struct sev_data_snp_commit buf; - bool shutdown_required = false; - int ret, error; - - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - return ret; - } + int ret; buf.len = sizeof(buf); ret = __sev_do_cmd_locked(SEV_CMD_SNP_COMMIT, &buf, &argp->error); - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - return ret; } From 92567ed9306d5a3d1b007eb4faeff30cc3ffc3e4 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:46 -0600 Subject: [PATCH 0469/3297] crypto: ccp - Do not initialize SNP for ioctl(SNP_VLEK_LOAD) commit f91e9dbb5845d1e5abf1028e6df57dcf61583e1b upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? The SEV firmware docs for SNP_VLEK_LOAD note: > On SNP_SHUTDOWN, the VLEK is deleted. That is, the initialization/shutdown wrapper here is pointless, because the firmware immediately throws away the key anyway. Instead, refuse to do anything if SNP has not been previously initialized. This is an ABI break: before, this was a no-op and almost certainly a mistake by userspace, and now it returns -ENODEV. ABI compatibility could be maintained here by simply returning 0 in the check instead. Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 42dd9b2717798..89276f839298e 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2455,9 +2455,8 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) { struct sev_device *sev = psp_master->sev_data; struct sev_user_data_snp_vlek_load input; - bool shutdown_required = false; - int ret, error; void *blob; + int ret; if (!argp->data) return -EINVAL; @@ -2465,6 +2464,9 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) if (!writable) return -EPERM; + if (!sev->snp_initialized) + return -ENODEV; + if (copy_from_user(&input, u64_to_user_ptr(argp->data), sizeof(input))) return -EFAULT; @@ -2478,18 +2480,7 @@ static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) input.vlek_wrapped_address = __psp_pa(blob); - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - goto cleanup; - } - ret = __sev_do_cmd_locked(SEV_CMD_SNP_VLEK_LOAD, &input, &argp->error); - - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - -cleanup: kfree(blob); return ret; From 441ea32cf2755a0dc593557056b00b7caa0651f5 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 4 May 2026 10:51:47 -0600 Subject: [PATCH 0470/3297] crypto: ccp - Do not initialize SNP for ioctl(SNP_CONFIG) commit 08f0e65e784c4b20e6e620dd4f68d8636073a3d2 upstream. Sashiko notes: > if SEV initialization fails and KVM is actively running normal VMs, could a > userspace process trigger this code path via /dev/sev ioctls (e.g., > SEV_PDH_GEN) and zero out MSR_VM_HSAVE_PA globally? Would the next VMRUN > execution for an active VM trigger a general protection fault and crash the > host? Refuse to re-try initialization if SNP is not already initialized for SNP_CONFIG. This is technically an ABI break: before if SNP initialization failed it could be transparently retriggered by this ioctl, and if no VMs were running, everything worked fine. Hopefully this is enough of a corner case that nobody will notice, but someone does, there are a few options: * do something like symbol_get() for kvm and refuse to initialize if KVM is loaded * check each cpu's HSAVE_PA for non-zero data before re-initializing * once initialization has failed, continue to refuse to initialize until the ccp module is unloaded Fixes: ceac7fb89e8d ("crypto: ccp - Ensure implicit SEV/SNP init and shutdown in ioctls") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org CC: Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/ccp/sev-dev.c | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 89276f839298e..d99726d1d57f0 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1700,21 +1700,6 @@ static int sev_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_req return 0; } -static int snp_move_to_init_state(struct sev_issue_cmd *argp, bool *shutdown_required) -{ - int error, rc; - - rc = __sev_snp_init_locked(&error, 0); - if (rc) { - argp->error = SEV_RET_INVALID_PLATFORM_STATE; - return rc; - } - - *shutdown_required = true; - - return 0; -} - static int sev_ioctl_do_reset(struct sev_issue_cmd *argp, bool writable) { int state, rc; @@ -2425,8 +2410,6 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writable { struct sev_device *sev = psp_master->sev_data; struct sev_user_data_snp_config config; - bool shutdown_required = false; - int ret, error; if (!argp->data) return -EINVAL; @@ -2434,21 +2417,13 @@ static int sev_ioctl_do_snp_set_config(struct sev_issue_cmd *argp, bool writable if (!writable) return -EPERM; + if (!sev->snp_initialized) + return -ENODEV; + if (copy_from_user(&config, (void __user *)argp->data, sizeof(config))) return -EFAULT; - if (!sev->snp_initialized) { - ret = snp_move_to_init_state(argp, &shutdown_required); - if (ret) - return ret; - } - - ret = __sev_do_cmd_locked(SEV_CMD_SNP_CONFIG, &config, &argp->error); - - if (shutdown_required) - __sev_snp_shutdown_locked(&error, false); - - return ret; + return __sev_do_cmd_locked(SEV_CMD_SNP_CONFIG, &config, &argp->error); } static int sev_ioctl_do_snp_vlek_load(struct sev_issue_cmd *argp, bool writable) From 23b8b188cb32e5531d0f8d3af9506f8959cb369e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:45 -0700 Subject: [PATCH 0471/3297] crypto: drbg - Fix returning success on failure in CTR_DRBG commit 39a31ad9e2a5ed7e9c9c6f711dca96c8c8f5f26b upstream. drbg_ctr_generate() sometimes returns success when it fails, leaving the output buffer uninitialized. Fix it. Fixes: cde001e4c3c3 ("crypto: rng - RNGs must return 0 in success case") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/drbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/drbg.c b/crypto/drbg.c index dbe4c8bb5ceb7..a40cb75cecdcd 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -592,7 +592,7 @@ static int drbg_ctr_generate(struct drbg_state *drbg, if (addtl && !list_empty(addtl)) { ret = drbg_ctr_update(drbg, addtl, 2); if (ret) - return 0; + return ret; } /* 10.2.1.5.2 step 4.1 */ From 53d38b93cadc08bcbe6e200bb19d37cb9bec1f1e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:48 -0700 Subject: [PATCH 0472/3297] crypto: drbg - Fix drbg_max_addtl() on 64-bit kernels commit 6f49f00c981bbb9ef602966f19bfdbef46b681d2 upstream. On 64-bit kernels, drbg_max_addtl() returns 2**35 bytes. That's too large, for two reasons: 1. SP800-90A says the maximum limit is 2**35 *bits*, not 2**35 bytes. So the implemented limit has confused bits and bytes. 2. When drbg_kcapi_hash() calls crypto_shash_update() on the additional information string, the length is implicitly cast to 'unsigned int'. That truncates the additional information string to U32_MAX bytes. Fix the maximum additional information string length to always be U32_MAX - 1, causing an error to be returned for any longer lengths. Fixes: 541af946fe13 ("crypto: drbg - SP800-90A Deterministic Random Bit Generator") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- include/crypto/drbg.h | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h index af5ad51d3eef8..1e3dd17266740 100644 --- a/include/crypto/drbg.h +++ b/include/crypto/drbg.h @@ -171,19 +171,15 @@ static inline size_t drbg_max_request_bytes(struct drbg_state *drbg) return (1 << 16); } +/* + * SP800-90A allows implementations to support additional info / personalization + * strings of up to 2**35 bits. Implementations can have a smaller maximum. We + * use 2**35 - 16 bits == U32_MAX - 1 bytes so that the max + 1 always fits in a + * size_t, allowing drbg_healthcheck_sanity() to verify its enforcement. + */ static inline size_t drbg_max_addtl(struct drbg_state *drbg) { - /* SP800-90A requires 2**35 bytes additional info str / pers str */ -#if (__BITS_PER_LONG == 32) - /* - * SP800-90A allows smaller maximum numbers to be returned -- we - * return SIZE_MAX - 1 to allow the verification of the enforcement - * of this value in drbg_healthcheck_sanity. - */ - return (SIZE_MAX - 1); -#else - return (1UL<<35); -#endif + return U32_MAX - 1; } static inline size_t drbg_max_requests(struct drbg_state *drbg) From 5337b5cd3608a7d25cd89ce0225f4e020015edfa Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 19 Apr 2026 23:33:49 -0700 Subject: [PATCH 0473/3297] crypto: drbg - Fix the fips_enabled priority boost commit a8a1f93080efc83a9ff8452954429ae379e9e614 upstream. When fips_enabled=1, it seems to have been intended for one of the algorithms defined in crypto/drbg.c to be the highest priority "stdrng" algorithm, so that it is what is used by "stdrng" users. However, the code only boosts the priority to 400, which is less than the priority 500 used in drivers/crypto/caam/caamprng.c. Thus, the CAAM RNG could be used instead. Fix this by boosting the priority by 2000 instead of 200. Fixes: 541af946fe13 ("crypto: drbg - SP800-90A Deterministic Random Bit Generator") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/drbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/drbg.c b/crypto/drbg.c index a40cb75cecdcd..dc8996789da67 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -2082,7 +2082,7 @@ static inline void __init drbg_fill_array(struct rng_alg *alg, * it is selected. */ if (fips_enabled) - alg->base.cra_priority += 200; + alg->base.cra_priority += 2000; alg->base.cra_ctxsize = sizeof(struct drbg_state); alg->base.cra_module = THIS_MODULE; From 050bded706ee59d86615e8c535232edc55c200e0 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:56 +0200 Subject: [PATCH 0474/3297] crypto: qat - centralize bus master enable commit e5712ff82947dd02d118cee119ed36cec671d814 upstream. QAT driver currently toggles PCI bus mastering in multiple places (probe paths, and reset callbacks). This makes BME state depend on call ordering and on what PCI command bits were captured in saved PCI config state. Make BME control explicit and deterministic: - remove pci_set_master() from device-specific probe paths - add adf_set_bme() and call it from adf_dev_init() so BME is enabled at one point before device bring-up - drop redundant pci_set_master() and pci_clear_master from adf_aer.c and rely on the unified init path for BME enablement This is in preparation for adding reset_prepare() and reset_done() hooks. In the PCI reset callback flow, the PCI core saves and restores device configuration state around reset_prepare() and reset_done(). This change is needed to ensure that we are able to properly shutdown or reinitialize the device post sysfs triggered resets. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_420xx/adf_drv.c | 2 -- drivers/crypto/intel/qat/qat_4xxx/adf_drv.c | 2 -- drivers/crypto/intel/qat/qat_6xxx/adf_drv.c | 2 -- drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_c62x/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_common/adf_aer.c | 10 +++++++--- drivers/crypto/intel/qat/qat_common/adf_common_drv.h | 1 + drivers/crypto/intel/qat/qat_common/adf_init.c | 2 ++ drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c | 1 - drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c | 1 - 12 files changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c index cfa00daeb4fba..3acbbb586dc0c 100644 --- a/drivers/crypto/intel/qat/qat_420xx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_420xx/adf_drv.c @@ -146,8 +146,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state.\n"); ret = -ENOMEM; diff --git a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c index c9be5dcddb277..f33182c101e2e 100644 --- a/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_4xxx/adf_drv.c @@ -148,8 +148,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state.\n"); ret = -ENOMEM; diff --git a/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c index f0d112e4b56c3..a30c65af6b4f7 100644 --- a/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_6xxx/adf_drv.c @@ -162,8 +162,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - pci_set_master(pdev); - /* * The PCI config space is saved at this point and will be restored * after a Function Level Reset (FLR) as the FLR does not completely diff --git a/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c b/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c index bceb5dd8b148c..c745397e63a52 100644 --- a/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c3xxx/adf_drv.c @@ -167,7 +167,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state\n"); diff --git a/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c b/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c index c622793e94a87..b31c58fdfb58e 100644 --- a/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c3xxxvf/adf_drv.c @@ -163,7 +163,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); diff --git a/drivers/crypto/intel/qat/qat_c62x/adf_drv.c b/drivers/crypto/intel/qat/qat_c62x/adf_drv.c index 23ccb72b6ea22..941400c64d4be 100644 --- a/drivers/crypto/intel/qat/qat_c62x/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c62x/adf_drv.c @@ -167,7 +167,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state\n"); diff --git a/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c b/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c index 4840d44bbd5b2..631eb2b411506 100644 --- a/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_c62xvf/adf_drv.c @@ -163,7 +163,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index f63e78724c7a7..1722315762791 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -41,7 +41,6 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, adf_error_notifier(accel_dev); adf_pf2vf_notify_fatal_error(accel_dev); adf_dev_restarting_notify(accel_dev); - pci_clear_master(pdev); adf_dev_down(accel_dev); return PCI_ERS_RESULT_NEED_RESET; @@ -106,6 +105,13 @@ void adf_dev_restore(struct adf_accel_dev *accel_dev) } } +void adf_set_bme(struct adf_accel_dev *accel_dev) +{ + struct pci_dev *pdev = accel_to_pci_dev(accel_dev); + + pci_set_master(pdev); +} + static void adf_device_sriov_worker(struct work_struct *work) { struct adf_sriov_dev_data *sriov_data = @@ -198,8 +204,6 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } - if (!pdev->is_busmaster) - pci_set_master(pdev); pci_restore_state(pdev); res = adf_dev_up(accel_dev, false); if (res && res != -EALREADY) diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index c625324bd47f9..a2b9491c26b13 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -84,6 +84,7 @@ extern const struct pci_error_handlers adf_err_handler; void adf_reset_sbr(struct adf_accel_dev *accel_dev); void adf_reset_flr(struct adf_accel_dev *accel_dev); void adf_dev_restore(struct adf_accel_dev *accel_dev); +void adf_set_bme(struct adf_accel_dev *accel_dev); int adf_init_aer(void); void adf_exit_aer(void); int adf_init_arb(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/intel/qat/qat_common/adf_init.c b/drivers/crypto/intel/qat/qat_common/adf_init.c index 46491048e0bb4..389d5eb66b51f 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_init.c +++ b/drivers/crypto/intel/qat/qat_common/adf_init.c @@ -73,6 +73,8 @@ static int adf_dev_init(struct adf_accel_dev *accel_dev) return -EFAULT; } + adf_set_bme(accel_dev); + if (!test_bit(ADF_STATUS_CONFIGURED, &accel_dev->status) && !accel_dev->is_vf) { dev_err(&GET_DEV(accel_dev), "Device not configured\n"); diff --git a/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c index b59e0cc49e521..99a6bf9e2b005 100644 --- a/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_dh895xcc/adf_drv.c @@ -167,7 +167,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); if (pci_save_state(pdev)) { dev_err(&pdev->dev, "Failed to save pci state\n"); diff --git a/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c b/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c index 7cd528ee31e77..e3eab03ad65bd 100644 --- a/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c +++ b/drivers/crypto/intel/qat/qat_dh895xccvf/adf_drv.c @@ -163,7 +163,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_free_reg; } } - pci_set_master(pdev); /* Completion for VF2PF request/response message exchange */ init_completion(&accel_dev->vf.msg_received); From 33cfc0ce28ac9f8c228a93dc4f1b70fc7c3d22c0 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:59 +0200 Subject: [PATCH 0475/3297] crypto: qat - handle sysfs-triggered reset callbacks commit 4627ef7019bc532f992c0723e881811ce12f0a02 upstream. A reset requested through /sys/bus/pci/devices/.../reset invokes the driver reset_prepare() and reset_done() callbacks. The QAT driver does not implement those callbacks today, so the reset proceeds without quiescing the device or bringing it back up afterward, which leaves the device unusable. Hook reset_prepare() and reset_done() into adf_err_handler so the common shutdown and recovery flow also runs for reset. Skip device quiesce if the device is already in a down state. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index 1722315762791..a9a2dd5222e3d 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -222,10 +222,22 @@ static void adf_resume(struct pci_dev *pdev) dev_info(&pdev->dev, "Device is up and running\n"); } +static void adf_reset_prepare(struct pci_dev *pdev) +{ + reset_prepare(pdev); +} + +static void adf_reset_done(struct pci_dev *pdev) +{ + reset_done(pdev); +} + const struct pci_error_handlers adf_err_handler = { .error_detected = adf_error_detected, .slot_reset = adf_slot_reset, .resume = adf_resume, + .reset_prepare = adf_reset_prepare, + .reset_done = adf_reset_done, }; EXPORT_SYMBOL_GPL(adf_err_handler); From 45b65a21edbe0cf93a1e1451cfeefc5d4965deab Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:54 +0200 Subject: [PATCH 0476/3297] crypto: qat - keep VFs enabled during reset commit 57518500053987672050dc2f7bf8a774d5d52fd9 upstream. When a reset is triggered via sysfs, the PCI core invokes the reset_prepare() callback while holding pci_dev_lock(), which includes the PCI configuration space access semaphore. If reset_prepare() calls adf_dev_down(), the call chain adf_dev_stop() -> adf_disable_sriov() -> pci_disable_sriov() attempts to acquire the same semaphore, resulting in a deadlock. Avoid this by skipping pci_disable_sriov() when ADF_STATUS_RESTARTING is set. During reset the PCI topology is preserved, so VF devices remain valid and enumerated across the reset. VF notification and the quiesce handshake via adf_pf2vf_notify_restarting() are still performed unconditionally so that VFs stop submitting work before the PF shuts down. Correspondingly, skip pci_enable_sriov() in adf_enable_sriov() when VFs are already present, since their PCI devices were preserved from before the restart. This is in preparation for adding reset_prepare() and reset_done() callbacks in adf_aer.c. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_sriov.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c index 31d1ef0cb1f52..e9f825368b7ea 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c @@ -77,6 +77,10 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) /* Enable VF to PF interrupts for all VFs */ adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1); + /* Do not enable SR-IOV if already enabled */ + if (pci_num_vf(pdev)) + return 0; + /* * Due to the hardware design, when SR-IOV and the ring arbiter * are enabled all the VFs supported in hardware must be enabled in @@ -247,7 +251,13 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev) adf_pf2vf_notify_restarting(accel_dev); adf_pf2vf_wait_for_restarting_complete(accel_dev); - pci_disable_sriov(accel_to_pci_dev(accel_dev)); + /* + * When the device is restarting, preserve VF PCI devices across + * the reset by skipping pci_disable_sriov(). VFs are notified to + * quiesce regardless so the PF can safely shut down. + */ + if (!test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) + pci_disable_sriov(accel_to_pci_dev(accel_dev)); /* Disable VF to PF interrupts */ adf_disable_all_vf2pf_interrupts(accel_dev); From e310e8dc8ce72f5b314db631bd5e500c95b8fcd7 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:55 +0200 Subject: [PATCH 0477/3297] crypto: qat - notify fatal error before AER reset preparation commit 6931835f2fdd0cb9b1c7791748d7e67d0749056a upstream. Send fatal error notifications to subsystems and VFs as soon as AER error detection starts, before entering the reset preparation shutdown sequence. This reduces notification latency and ensures peers are informed immediately on fatal detection, rather than after restart-state setup and arbitration teardown. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index a9a2dd5222e3d..1d786f793b8b7 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -33,13 +33,13 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_DISCONNECT; } + adf_error_notifier(accel_dev); + adf_pf2vf_notify_fatal_error(accel_dev); set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); if (accel_dev->hw_device->exit_arb) { dev_dbg(&pdev->dev, "Disabling arbitration\n"); accel_dev->hw_device->exit_arb(accel_dev); } - adf_error_notifier(accel_dev); - adf_pf2vf_notify_fatal_error(accel_dev); adf_dev_restarting_notify(accel_dev); adf_dev_down(accel_dev); From c3c5925791cff3b84d313293fd60f384d877d793 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 20 May 2026 13:41:55 +0100 Subject: [PATCH 0478/3297] crypto: qat - protect service table iterations with service_lock commit 5c6f845e77ec35f9b7b047cc8f9789bf397cdd3e upstream. The service_table list is protected by service_lock when entries are added or removed (in adf_service_add() and adf_service_remove()), but several functions iterate over the list without holding this lock. A concurrent adf_service_register() or adf_service_unregister() call could modify the list during traversal, leading to list corruption or a use-after-free. Fix this by holding service_lock across all list_for_each_entry() iterations of service_table in adf_dev_init(), adf_dev_start(), adf_dev_stop(), adf_dev_shutdown(), adf_dev_restarting_notify(), adf_dev_restarted_notify(), and adf_error_notifier(). The lock ordering is safe: callers of the static helpers (adf_dev_up() and adf_dev_down()) acquire state_lock before service_lock, and no event_hld callback or service_lock holder ever acquires state_lock in the reverse order. Cc: stable@vger.kernel.org Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework") Signed-off-by: Ahsan Atta Co-developed-by: Maksim Lukoshkov Signed-off-by: Maksim Lukoshkov Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_init.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/crypto/intel/qat/qat_common/adf_init.c b/drivers/crypto/intel/qat/qat_common/adf_init.c index 389d5eb66b51f..fe397f39a6c4f 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_init.c +++ b/drivers/crypto/intel/qat/qat_common/adf_init.c @@ -154,15 +154,18 @@ static int adf_dev_init(struct adf_accel_dev *accel_dev) * This is to facilitate any ordering dependencies between services * prior to starting any of the accelerators. */ + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_INIT)) { dev_err(&GET_DEV(accel_dev), "Failed to initialise service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); return 0; } @@ -231,15 +234,18 @@ static int adf_dev_start(struct adf_accel_dev *accel_dev) if (ret && ret != -EOPNOTSUPP) return ret; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_START)) { dev_err(&GET_DEV(accel_dev), "Failed to start service %s\n", service->name); + mutex_unlock(&service_lock); return -EFAULT; } set_bit(accel_dev->accel_id, service->start_status); } + mutex_unlock(&service_lock); clear_bit(ADF_STATUS_STARTING, &accel_dev->status); set_bit(ADF_STATUS_STARTED, &accel_dev->status); @@ -310,6 +316,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) qat_comp_algs_unregister(); clear_bit(ADF_STATUS_COMP_ALGS_REGISTERED, &accel_dev->status); + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (!test_bit(accel_dev->accel_id, service->start_status)) continue; @@ -321,6 +328,7 @@ static void adf_dev_stop(struct adf_accel_dev *accel_dev) clear_bit(accel_dev->accel_id, service->start_status); } } + mutex_unlock(&service_lock); if (hw_data->stop_timer) hw_data->stop_timer(accel_dev); @@ -370,6 +378,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) &accel_dev->status); } + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (!test_bit(accel_dev->accel_id, service->init_status)) continue; @@ -380,6 +389,7 @@ static void adf_dev_shutdown(struct adf_accel_dev *accel_dev) else clear_bit(accel_dev->accel_id, service->init_status); } + mutex_unlock(&service_lock); adf_rl_exit(accel_dev); @@ -414,12 +424,14 @@ int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_RESTARTING)) dev_err(&GET_DEV(accel_dev), "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } @@ -427,12 +439,14 @@ int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_RESTARTED)) dev_err(&GET_DEV(accel_dev), "Failed to restart service %s.\n", service->name); } + mutex_unlock(&service_lock); return 0; } @@ -440,12 +454,14 @@ void adf_error_notifier(struct adf_accel_dev *accel_dev) { struct service_hndl *service; + mutex_lock(&service_lock); list_for_each_entry(service, &service_table, list) { if (service->event_hld(accel_dev, ADF_EVENT_FATAL_ERROR)) dev_err(&GET_DEV(accel_dev), "Failed to send error event to %s.\n", service->name); } + mutex_unlock(&service_lock); } int adf_dev_down(struct adf_accel_dev *accel_dev) From fabf364ef9db5c6315205f2eac1d066b7e044cbe Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:57 +0200 Subject: [PATCH 0479/3297] crypto: qat - skip restart for down devices commit 9676657117b79f88c22875680c36d7da84b75eca upstream. Skip the shutdown and restart flow when adf_slot_reset() is entered for a device that is already down. In that case, leave ADF_STATUS_RESTARTING clear and let adf_slot_reset() restore PCI function state without calling adf_dev_up(), re-enabling SR-IOV, or sending restarted notifications. This is in preparation for adding reset_prepare() and reset_done() callbacks in adf_aer.c. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index 1d786f793b8b7..0fa8300c239b5 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -33,6 +33,9 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_DISCONNECT; } + if (!adf_dev_started(accel_dev)) + return PCI_ERS_RESULT_CAN_RECOVER; + adf_error_notifier(accel_dev); adf_pf2vf_notify_fatal_error(accel_dev); set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); @@ -204,6 +207,9 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } + if (!adf_devmgr_in_reset(accel_dev)) + goto reset_complete; + pci_restore_state(pdev); res = adf_dev_up(accel_dev, false); if (res && res != -EALREADY) @@ -213,6 +219,8 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) adf_pf2vf_notify_restarted(accel_dev); adf_dev_restarted_notify(accel_dev); clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); + +reset_complete: return PCI_ERS_RESULT_RECOVERED; } From 6fb62b767f3e27661e8f8d2f7b85f4e098fcdb1a Mon Sep 17 00:00:00 2001 From: Giovanni Cabiddu Date: Thu, 28 May 2026 16:57:44 +0100 Subject: [PATCH 0480/3297] crypto: qat - validate RSA CRT component lengths commit b3ac78756588059729b9195fcc9f4b37d54057a5 upstream. The generic RSA key parser (rsa_helper.c) bounds each CRT component (p, q, dp, dq, qinv) by the modulus size n_sz, but qat_rsa_setkey_crt() allocates half-size DMA buffers (key_sz / 2) and right-aligns each component with: memcpy(dst + half_key_sz - len, src, len) When a CRT component is larger than half_key_sz the subtraction underflows and memcpy writes past the DMA buffer, causing memory corruption. Add a len > half_key_sz check next to the existing !len check for each of the five CRT components so the driver falls back to the non-CRT path instead of writing out of bounds. Fixes: 879f77e9071f ("crypto: qat - Add RSA CRT mode") Cc: stable@vger.kernel.org Signed-off-by: Giovanni Cabiddu Reviewed-by: Ahsan Atta Reviewed-by: Laurent M Coquerel Tested-by: Laurent M Coquerel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/qat_asym_algs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c index 85c682e248fb9..afb7e3f44a5f8 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c @@ -1085,7 +1085,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->p; len = rsa_key->p_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto err; ctx->p = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_p, GFP_KERNEL); if (!ctx->p) @@ -1096,7 +1096,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->q; len = rsa_key->q_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_p; ctx->q = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_q, GFP_KERNEL); if (!ctx->q) @@ -1107,7 +1107,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dp; len = rsa_key->dp_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_q; ctx->dp = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dp, GFP_KERNEL); @@ -1119,7 +1119,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dq; len = rsa_key->dq_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dp; ctx->dq = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dq, GFP_KERNEL); @@ -1131,7 +1131,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->qinv; len = rsa_key->qinv_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dq; ctx->qinv = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_qinv, GFP_KERNEL); From 7584c92f72447b20347ec8e7b6a17fb16b3daa4a Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Wed, 13 May 2026 17:16:58 +0200 Subject: [PATCH 0481/3297] crypto: qat - factor out AER reset helpers commit 56707afb92fee371c0f2e04332c9aa03cdb89793 upstream. Move the shutdown and recovery sequences out of adf_error_detected() and adf_slot_reset() into reset_prepare() and reset_done() helpers. This makes the AER recovery path easier to follow and prepares the common reset flow for reuse by additional PCI reset callbacks without duplicating the logic. No functional change intended. Cc: stable@vger.kernel.org Signed-off-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Reviewed-by: Damian Muszynski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 86 ++++++++++++------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index 0fa8300c239b5..ad35d1887f162 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -17,27 +17,18 @@ struct adf_fatal_error_data { static struct workqueue_struct *device_reset_wq; static struct workqueue_struct *device_sriov_wq; -static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, - pci_channel_state_t state) +static pci_ers_result_t reset_prepare(struct pci_dev *pdev) { struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); - dev_info(&pdev->dev, "Acceleration driver hardware error detected.\n"); if (!accel_dev) { dev_err(&pdev->dev, "Can't find acceleration device\n"); return PCI_ERS_RESULT_DISCONNECT; } - if (state == pci_channel_io_perm_failure) { - dev_err(&pdev->dev, "Can't recover from device error\n"); - return PCI_ERS_RESULT_DISCONNECT; - } - if (!adf_dev_started(accel_dev)) return PCI_ERS_RESULT_CAN_RECOVER; - adf_error_notifier(accel_dev); - adf_pf2vf_notify_fatal_error(accel_dev); set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); if (accel_dev->hw_device->exit_arb) { dev_dbg(&pdev->dev, "Disabling arbitration\n"); @@ -49,6 +40,57 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_NEED_RESET; } +static pci_ers_result_t reset_done(struct pci_dev *pdev) +{ + struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); + int res; + + if (!accel_dev) { + dev_err(&pdev->dev, "Can't find acceleration device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (!adf_devmgr_in_reset(accel_dev)) + goto reset_complete; + + pci_restore_state(pdev); + res = adf_dev_up(accel_dev, false); + if (res && res != -EALREADY) + return PCI_ERS_RESULT_DISCONNECT; + + adf_reenable_sriov(accel_dev); + adf_pf2vf_notify_restarted(accel_dev); + adf_dev_restarted_notify(accel_dev); + clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); + +reset_complete: + dev_info(&pdev->dev, "Device reset completed successfully\n"); + + return PCI_ERS_RESULT_RECOVERED; +} + +static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); + + dev_info(&pdev->dev, "Acceleration driver hardware error detected.\n"); + if (!accel_dev) { + dev_err(&pdev->dev, "Can't find acceleration device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (state == pci_channel_io_perm_failure) { + dev_err(&pdev->dev, "Can't recover from device error\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + adf_error_notifier(accel_dev); + adf_pf2vf_notify_fatal_error(accel_dev); + + return reset_prepare(pdev); +} + /* reset dev data */ struct adf_reset_dev_data { int mode; @@ -199,29 +241,7 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev, static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) { - struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); - int res = 0; - - if (!accel_dev) { - pr_err("QAT: Can't find acceleration device\n"); - return PCI_ERS_RESULT_DISCONNECT; - } - - if (!adf_devmgr_in_reset(accel_dev)) - goto reset_complete; - - pci_restore_state(pdev); - res = adf_dev_up(accel_dev, false); - if (res && res != -EALREADY) - return PCI_ERS_RESULT_DISCONNECT; - - adf_reenable_sriov(accel_dev); - adf_pf2vf_notify_restarted(accel_dev); - adf_dev_restarted_notify(accel_dev); - clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); - -reset_complete: - return PCI_ERS_RESULT_RECOVERED; + return reset_done(pdev); } static void adf_resume(struct pci_dev *pdev) From f52aa95e3cae15311378a24f116224c797b1fa3d Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:47 +0200 Subject: [PATCH 0482/3297] crypto: talitos - use dma_sync_single_for_cpu() before reading descriptor header commit e17ff3d6ff907dc8406261e8fd3e1fc8a908f0f6 upstream. In order to know if a descriptor has been processed by the device, the driver polls the FIFO to see if DESC_HDR_DONE is set on a descriptor header to confirm completion. The current code does not make sure that the CPU gets up to date data before reading the descriptor. Fix this by calling dma_sync_single_for_cpu() before reading memory written by the device. Cc: stable@vger.kernel.org Fixes: 58cdbc6d2263 ("crypto: talitos - fix hash on SEC1.") Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index bc61d0fe35140..440e19dc8de60 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -322,19 +322,31 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, return -EINPROGRESS; } -static __be32 get_request_hdr(struct talitos_request *request, bool is_sec1) +static __be32 get_request_hdr(struct device *dev, + struct talitos_request *request, bool is_sec1) { struct talitos_edesc *edesc; - if (!is_sec1) + if (!is_sec1) { + dma_sync_single_for_cpu(dev, request->dma_desc, + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + return request->desc->hdr; + } - if (!request->desc->next_desc) + if (!request->desc->next_desc) { + dma_sync_single_for_cpu(dev, request->dma_desc, + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); return request->desc->hdr1; + } else { + dma_sync_single_for_cpu(dev, + be32_to_cpu(request->desc->next_desc), + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = container_of(request->desc, struct talitos_edesc, desc); - edesc = container_of(request->desc, struct talitos_edesc, desc); - - return ((struct talitos_desc *)(edesc->buf + edesc->dma_len))->hdr1; + return ((struct talitos_desc *)(edesc->buf + edesc->dma_len)) + ->hdr1; + } } /* @@ -358,7 +370,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) /* descriptors with their done bits set don't get the error */ rmb(); - hdr = get_request_hdr(request, is_sec1); + hdr = get_request_hdr(dev, request, is_sec1); if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE) status = 0; From 664e7f16e74fcf73b8ae40cbf9a1e288bb5c4c3a Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:48 +0200 Subject: [PATCH 0483/3297] crypto: talitos - add chaining of arbitrary number of descriptor for the SEC1 commit f126384ed55279c3b676f89d5ab547b8de8df782 upstream. The SEC1 hardware can process a chain of descriptors without host intervention. Only the hash implementation currently use this feature, but with a chain of at most 2 descriptors added in commit 37b5e8897eb5 ("crypto: talitos - chain in buffered data for ahash on SEC1"). Add supports for chaining an arbitrary number of descriptors in a chain. Adapt the ahash implementation to make it compatible. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 180 ++++++++++++++++++++++++++------------- drivers/crypto/talitos.h | 2 + 2 files changed, 124 insertions(+), 58 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 440e19dc8de60..b0ebf99b94f5f 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -273,7 +273,10 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, void *context, int error), void *context) { + struct talitos_edesc *edesc = container_of(desc, struct talitos_edesc, desc); struct talitos_private *priv = dev_get_drvdata(dev); + dma_addr_t dma_desc, prev_dma_desc; + struct talitos_edesc *prev_edesc = NULL; struct talitos_request *request; unsigned long flags; int head; @@ -292,10 +295,31 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, /* map descriptor and save caller data */ if (is_sec1) { - desc->hdr1 = desc->hdr; - request->dma_desc = dma_map_single(dev, &desc->hdr1, + while (edesc) { + edesc->desc.hdr1 = edesc->desc.hdr; + + dma_desc = dma_map_single(dev, &edesc->desc.hdr1, + TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + + if (!prev_edesc) { + request->dma_desc = dma_desc; + goto next; + } + + /* Chain in any previous descriptors. */ + + prev_edesc->desc.next_desc = cpu_to_be32(dma_desc); + + dma_sync_single_for_device(dev, prev_dma_desc, TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); + +next: + prev_edesc = edesc; + prev_dma_desc = dma_desc; + edesc = edesc->next_desc; + } } else { request->dma_desc = dma_map_single(dev, desc, TALITOS_DESC_SIZE, @@ -326,6 +350,7 @@ static __be32 get_request_hdr(struct device *dev, struct talitos_request *request, bool is_sec1) { struct talitos_edesc *edesc; + dma_addr_t dma_desc; if (!is_sec1) { dma_sync_single_for_cpu(dev, request->dma_desc, @@ -334,19 +359,17 @@ static __be32 get_request_hdr(struct device *dev, return request->desc->hdr; } - if (!request->desc->next_desc) { - dma_sync_single_for_cpu(dev, request->dma_desc, - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - return request->desc->hdr1; - } else { - dma_sync_single_for_cpu(dev, - be32_to_cpu(request->desc->next_desc), - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - edesc = container_of(request->desc, struct talitos_edesc, desc); - - return ((struct talitos_desc *)(edesc->buf + edesc->dma_len)) - ->hdr1; + edesc = container_of(request->desc, struct talitos_edesc, desc); + dma_desc = request->dma_desc; + while (edesc->next_desc) { + dma_desc = be32_to_cpu(edesc->desc.next_desc); + edesc = edesc->next_desc; } + + dma_sync_single_for_cpu(dev, dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + + return edesc->desc.hdr1; } /* @@ -356,6 +379,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) { struct talitos_private *priv = dev_get_drvdata(dev); struct talitos_request *request, saved_req; + struct talitos_edesc *edesc; unsigned long flags; int tail, status; bool is_sec1 = has_ftr_sec1(priv); @@ -380,9 +404,22 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) else status = error; - dma_unmap_single(dev, request->dma_desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); + if (is_sec1) { + dma_unmap_single(dev, request->dma_desc, + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = container_of(request->desc, + struct talitos_edesc, desc); + while (edesc->next_desc) { + dma_unmap_single( + dev, be32_to_cpu(edesc->desc.next_desc), + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = edesc->next_desc; + } + } else { + dma_unmap_single(dev, request->dma_desc, + TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } /* copy entries so we can call callback outside lock */ saved_req.desc = request->desc; @@ -477,8 +514,12 @@ DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE) static __be32 current_desc_hdr(struct device *dev, int ch) { struct talitos_private *priv = dev_get_drvdata(dev); + bool is_sec1 = has_ftr_sec1(priv); + struct talitos_request *request; + struct talitos_edesc *edesc; int tail, iter; dma_addr_t cur_desc; + __be32 hdr = 0; cur_desc = ((u64)in_be32(priv->chan[ch].reg + TALITOS_CDPR)) << 32; cur_desc |= in_be32(priv->chan[ch].reg + TALITOS_CDPR_LO); @@ -489,27 +530,35 @@ static __be32 current_desc_hdr(struct device *dev, int ch) } tail = priv->chan[ch].tail; - iter = tail; - while (priv->chan[ch].fifo[iter].dma_desc != cur_desc && - priv->chan[ch].fifo[iter].desc->next_desc != cpu_to_be32(cur_desc)) { - iter = (iter + 1) & (priv->fifo_len - 1); - if (iter == tail) { - dev_err(dev, "couldn't locate current descriptor\n"); - return 0; + do { + request = &priv->chan[ch].fifo[iter]; + + if (request->dma_desc == cur_desc) { + hdr = request->desc->hdr; + } else if (is_sec1) { + edesc = container_of(request->desc, + struct talitos_edesc, desc); + while (edesc->next_desc) { + if (edesc->desc.next_desc == + cpu_to_be32(cur_desc)) { + hdr = edesc->next_desc->desc.hdr1; + break; + } + edesc = edesc->next_desc; + } } - } - if (priv->chan[ch].fifo[iter].desc->next_desc == cpu_to_be32(cur_desc)) { - struct talitos_edesc *edesc; + if (hdr) + break; - edesc = container_of(priv->chan[ch].fifo[iter].desc, - struct talitos_edesc, desc); - return ((struct talitos_desc *) - (edesc->buf + edesc->dma_len))->hdr; - } + iter = (iter + 1) & (priv->fifo_len - 1); + } while (iter != tail); + + if (!hdr) + dev_err(dev, "couldn't locate current descriptor\n"); - return priv->chan[ch].fifo[iter].desc->hdr; + return hdr; } /* @@ -1408,10 +1457,6 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev, dma_len = 0; } alloc_len += icv_stashing ? authsize : 0; - - /* if its a ahash, add space for a second desc next to the first one */ - if (is_sec1 && !dst) - alloc_len += sizeof(struct talitos_desc); alloc_len += ivsize; edesc = kmalloc(ALIGN(alloc_len, dma_get_cache_alignment()), flags); @@ -1427,6 +1472,7 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev, edesc->dst_nents = dst_nents; edesc->iv_dma = iv_dma; edesc->dma_len = dma_len; + edesc->next_desc = NULL; if (dma_len) edesc->dma_link_tbl = dma_map_single(dev, &edesc->link_tbl[0], edesc->dma_len, @@ -1727,8 +1773,10 @@ static void common_nonsnoop_hash_unmap(struct device *dev, struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); struct talitos_desc *desc = &edesc->desc; - struct talitos_desc *desc2 = (struct talitos_desc *) - (edesc->buf + edesc->dma_len); + struct talitos_desc *desc2; + + if (desc->next_desc) + desc2 = &edesc->next_desc->desc; unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE); if (desc->next_desc && @@ -1756,10 +1804,17 @@ static void common_nonsnoop_hash_unmap(struct device *dev, if (edesc->dma_len) dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); +} - if (desc->next_desc) - dma_unmap_single(dev, be32_to_cpu(desc->next_desc), - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); +static void free_edesc_list_from(struct talitos_edesc *edesc) +{ + struct talitos_edesc *next; + + while (edesc) { + next = edesc->next_desc; + kfree(edesc); + edesc = next; + } } static void ahash_done(struct device *dev, @@ -1778,7 +1833,7 @@ static void ahash_done(struct device *dev, } common_nonsnoop_hash_unmap(dev, edesc, areq); - kfree(edesc); + free_edesc_list_from(edesc); if (err) { ahash_request_complete(areq, err); @@ -1894,14 +1949,23 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]); if (is_sec1 && req_ctx->nbuf && length) { - struct talitos_desc *desc2 = (struct talitos_desc *) - (edesc->buf + edesc->dma_len); - dma_addr_t next_desc; + struct talitos_edesc *edesc2; + struct talitos_desc *desc2; + + edesc2 = kzalloc(sizeof(*edesc2), + areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? + GFP_KERNEL : + GFP_ATOMIC); + if (!edesc2) { + ret = -ENOMEM; + goto err; + } + edesc->next_desc = edesc2; + + desc2 = &edesc2->desc; - memset(desc2, 0, sizeof(*desc2)); desc2->hdr = desc->hdr; desc2->hdr &= ~DESC_HDR_MODE0_MDEU_INIT; - desc2->hdr1 = desc2->hdr; desc->hdr &= ~DESC_HDR_MODE0_MDEU_PAD; desc->hdr |= DESC_HDR_MODE0_MDEU_CONT; desc->hdr &= ~DESC_HDR_DONE_NOTIFY; @@ -1925,21 +1989,21 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, req_ctx->hw_context_size, req_ctx->hw_context, DMA_FROM_DEVICE); - - next_desc = dma_map_single(dev, &desc2->hdr1, TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - desc->next_desc = cpu_to_be32(next_desc); } if (sync_needed) dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); - ret = talitos_submit(dev, ctx->ch, desc, callback, areq); - if (ret != -EINPROGRESS) { - common_nonsnoop_hash_unmap(dev, edesc, areq); - kfree(edesc); - } + ret = talitos_submit(dev, ctx->ch, desc, callback, + areq); + if (ret != -EINPROGRESS) + goto err; + + return -EINPROGRESS; +err: + common_nonsnoop_hash_unmap(dev, edesc, areq); + kfree(edesc); return ret; } diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h index 1a93ee355929d..596f96bba3efa 100644 --- a/drivers/crypto/talitos.h +++ b/drivers/crypto/talitos.h @@ -49,6 +49,7 @@ struct talitos_desc { * @iv_dma: dma address of iv for checking continuity and link table * @dma_len: length of dma mapped link_tbl space * @dma_link_tbl: bus physical address of link_tbl/buf + * @next_desc: next descriptor * @desc: h/w descriptor * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1) (SEC2) * @buf: input and output buffeur (if {src,dst}_nents > 1) (SEC1) @@ -63,6 +64,7 @@ struct talitos_edesc { dma_addr_t iv_dma; int dma_len; dma_addr_t dma_link_tbl; + struct talitos_edesc *next_desc; struct talitos_desc desc; union { DECLARE_FLEX_ARRAY(struct talitos_ptr, link_tbl); From 3fa1846f75edff437a7a9a001031a193e9d4629b Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:49 +0200 Subject: [PATCH 0484/3297] crypto: talitos - move dma unmapping code in flush_channel() into a standalone dma_unmap_request() function commit 4d9b0b7415b9e79a3d54d18b5ff230974ea78740 upstream. Previously added code to flush_channel() in order to unmap an entire descriptor. Move that code into a standalone function to improve readability. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b0ebf99b94f5f..a98b40f566dd8 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -372,6 +372,27 @@ static __be32 get_request_hdr(struct device *dev, return edesc->desc.hdr1; } +static void dma_unmap_request(struct device *dev, + struct talitos_request *request, bool is_sec1) +{ + struct talitos_edesc *edesc; + + if (is_sec1) { + dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + edesc = container_of(request->desc, struct talitos_edesc, desc); + while (edesc->next_desc) { + dma_unmap_single(dev, + be32_to_cpu(edesc->desc.next_desc), + TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); + edesc = edesc->next_desc; + } + } else { + dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } +} + /* * process what was done, notify callback of error if not */ @@ -379,7 +400,6 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) { struct talitos_private *priv = dev_get_drvdata(dev); struct talitos_request *request, saved_req; - struct talitos_edesc *edesc; unsigned long flags; int tail, status; bool is_sec1 = has_ftr_sec1(priv); @@ -404,22 +424,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) else status = error; - if (is_sec1) { - dma_unmap_single(dev, request->dma_desc, - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - edesc = container_of(request->desc, - struct talitos_edesc, desc); - while (edesc->next_desc) { - dma_unmap_single( - dev, be32_to_cpu(edesc->desc.next_desc), - TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL); - edesc = edesc->next_desc; - } - } else { - dma_unmap_single(dev, request->dma_desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - } + dma_unmap_request(dev, request, is_sec1); /* copy entries so we can call callback outside lock */ saved_req.desc = request->desc; From aea8cfbd60da997a59ccebf93045adbb812f8d5a Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:50 +0200 Subject: [PATCH 0485/3297] crypto: talitos - move dma mapping code in talitos_submit() into a standalone dma_map_request() function commit 5c0aa8cad7745505297103f05dda3fa06e8ac670 upstream. Previously added code to talitos_submit() in order to map an entire descriptor chain. Move that code into a standalone function to improve readability. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 75 ++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index a98b40f566dd8..183d51fa9a4ac 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -255,6 +255,46 @@ static int init_device(struct device *dev) return 0; } +static void dma_map_request(struct device *dev, struct talitos_request *request, + struct talitos_desc *desc, bool is_sec1) +{ + struct talitos_edesc *edesc = + container_of(desc, struct talitos_edesc, desc); + dma_addr_t dma_desc, prev_dma_desc; + struct talitos_edesc *prev_edesc = NULL; + + if (is_sec1) { + while (edesc) { + edesc->desc.hdr1 = edesc->desc.hdr; + + dma_desc = dma_map_single(dev, &edesc->desc.hdr1, + TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + + if (!prev_edesc) { + request->dma_desc = dma_desc; + goto next; + } + + /* Chain in any previous descriptors. */ + + prev_edesc->desc.next_desc = cpu_to_be32(dma_desc); + + dma_sync_single_for_device(dev, prev_dma_desc, + TALITOS_DESC_SIZE, + DMA_TO_DEVICE); + +next: + prev_edesc = edesc; + prev_dma_desc = dma_desc; + edesc = edesc->next_desc; + } + } else { + request->dma_desc = dma_map_single(dev, desc, TALITOS_DESC_SIZE, + DMA_BIDIRECTIONAL); + } +} + /** * talitos_submit - submits a descriptor to the device for processing * @dev: the SEC device to be used @@ -273,10 +313,7 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, void *context, int error), void *context) { - struct talitos_edesc *edesc = container_of(desc, struct talitos_edesc, desc); struct talitos_private *priv = dev_get_drvdata(dev); - dma_addr_t dma_desc, prev_dma_desc; - struct talitos_edesc *prev_edesc = NULL; struct talitos_request *request; unsigned long flags; int head; @@ -294,37 +331,7 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc, request = &priv->chan[ch].fifo[head]; /* map descriptor and save caller data */ - if (is_sec1) { - while (edesc) { - edesc->desc.hdr1 = edesc->desc.hdr; - - dma_desc = dma_map_single(dev, &edesc->desc.hdr1, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - - if (!prev_edesc) { - request->dma_desc = dma_desc; - goto next; - } - - /* Chain in any previous descriptors. */ - - prev_edesc->desc.next_desc = cpu_to_be32(dma_desc); - - dma_sync_single_for_device(dev, prev_dma_desc, - TALITOS_DESC_SIZE, - DMA_TO_DEVICE); - -next: - prev_edesc = edesc; - prev_dma_desc = dma_desc; - edesc = edesc->next_desc; - } - } else { - request->dma_desc = dma_map_single(dev, desc, - TALITOS_DESC_SIZE, - DMA_BIDIRECTIONAL); - } + dma_map_request(dev, request, desc, is_sec1); request->callback = callback; request->context = context; From a8decb89920a1562634a68b8f3e9384b72df569e Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:51 +0200 Subject: [PATCH 0486/3297] crypto: talitos - move code in current_desc_hdr() into a standalone function commit f8713d9e6091755dd30f7f1cfa25f8440cddf81b upstream. Previously added code in current_desc_hdr() in order to add support for searching an offending descriptor inside a descriptor chain. Move that code into a standalone function to improve readability. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 183d51fa9a4ac..b808833d18ef1 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -520,6 +520,24 @@ DEF_TALITOS2_DONE(ch0, TALITOS2_ISR_CH_0_DONE) DEF_TALITOS2_DONE(ch0_2, TALITOS2_ISR_CH_0_2_DONE) DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE) +static __be32 search_desc_hdr_in_request(struct talitos_request *request, + dma_addr_t cur_desc, bool is_sec1) +{ + struct talitos_edesc *edesc; + + if (request->dma_desc == cur_desc) { + return request->desc->hdr; + } else if (is_sec1) { + edesc = container_of(request->desc, struct talitos_edesc, desc); + while (edesc->next_desc) { + if (edesc->desc.next_desc == cpu_to_be32(cur_desc)) + return edesc->next_desc->desc.hdr1; + edesc = edesc->next_desc; + } + } + return 0; +} + /* * locate current (offending) descriptor */ @@ -528,7 +546,6 @@ static __be32 current_desc_hdr(struct device *dev, int ch) struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); struct talitos_request *request; - struct talitos_edesc *edesc; int tail, iter; dma_addr_t cur_desc; __be32 hdr = 0; @@ -546,21 +563,7 @@ static __be32 current_desc_hdr(struct device *dev, int ch) do { request = &priv->chan[ch].fifo[iter]; - if (request->dma_desc == cur_desc) { - hdr = request->desc->hdr; - } else if (is_sec1) { - edesc = container_of(request->desc, - struct talitos_edesc, desc); - while (edesc->next_desc) { - if (edesc->desc.next_desc == - cpu_to_be32(cur_desc)) { - hdr = edesc->next_desc->desc.hdr1; - break; - } - edesc = edesc->next_desc; - } - } - + hdr = search_desc_hdr_in_request(request, cur_desc, is_sec1); if (hdr) break; From 40a2e90acdb1a0963584c4d48c37dafae08f8410 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:52 +0200 Subject: [PATCH 0487/3297] crypto: talitos/hash - prepare SEC1 descriptor chaining, remove additional descriptor commit 59b5d899e33701665f18abe6bebf987427876e3e upstream. Currently, when SEC1 has buffered data (nbuf != 0), the ahash code creates an additional descriptor on the fly inside common_nonsnoop_hash() to handle the remainder of the data. This approach is incompatible with the arbitrary-length descriptor chaining that follows. Remove the "additional descriptor" logic from common_nonsnoop_hash() and common_nonsnoop_hash_unmap(). Also remove the nbytes adjustment for SEC1 in ahash_edesc_alloc() that subtracted nbuf. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 101 +++------------------------------------ 1 file changed, 6 insertions(+), 95 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b808833d18ef1..0184982cb39b8 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1788,15 +1788,9 @@ static void common_nonsnoop_hash_unmap(struct device *dev, struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); struct talitos_desc *desc = &edesc->desc; - struct talitos_desc *desc2; - - if (desc->next_desc) - desc2 = &edesc->next_desc->desc; unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE); - if (desc->next_desc && - desc->ptr[5].ptr != desc2->ptr[5].ptr) - unmap_single_talitos_ptr(dev, &desc2->ptr[5], DMA_FROM_DEVICE); + if (req_ctx->last_desc) memcpy(areq->result, req_ctx->hw_context, crypto_ahash_digestsize(tfm)); @@ -1808,13 +1802,6 @@ static void common_nonsnoop_hash_unmap(struct device *dev, if (from_talitos_ptr_len(&desc->ptr[1], is_sec1)) unmap_single_talitos_ptr(dev, &desc->ptr[1], DMA_TO_DEVICE); - else if (desc->next_desc) - unmap_single_talitos_ptr(dev, &desc2->ptr[1], - DMA_TO_DEVICE); - - if (is_sec1 && req_ctx->nbuf) - unmap_single_talitos_ptr(dev, &desc->ptr[3], - DMA_TO_DEVICE); if (edesc->dma_len) dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len, @@ -1922,9 +1909,6 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, to_talitos_ptr(&desc->ptr[2], ctx->dma_key, ctx->keylen, is_sec1); - if (is_sec1 && req_ctx->nbuf) - length -= req_ctx->nbuf; - sg_count = edesc->src_nents ?: 1; if (is_sec1 && sg_count > 1) sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length); @@ -1934,16 +1918,10 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, /* * data in */ - if (is_sec1 && req_ctx->nbuf) { - map_single_talitos_ptr(dev, &desc->ptr[3], req_ctx->nbuf, - req_ctx->buf[req_ctx->buf_idx], - DMA_TO_DEVICE); - } else { - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc->ptr[3], sg_count, 0, 0); - if (sg_count > 1) - sync_needed = true; - } + sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, + &desc->ptr[3], sg_count, 0, 0); + if (sg_count > 1) + sync_needed = true; /* fifth DWORD empty */ @@ -1963,49 +1941,6 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, if (is_sec1 && from_talitos_ptr_len(&desc->ptr[3], true) == 0) talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]); - if (is_sec1 && req_ctx->nbuf && length) { - struct talitos_edesc *edesc2; - struct talitos_desc *desc2; - - edesc2 = kzalloc(sizeof(*edesc2), - areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? - GFP_KERNEL : - GFP_ATOMIC); - if (!edesc2) { - ret = -ENOMEM; - goto err; - } - edesc->next_desc = edesc2; - - desc2 = &edesc2->desc; - - desc2->hdr = desc->hdr; - desc2->hdr &= ~DESC_HDR_MODE0_MDEU_INIT; - desc->hdr &= ~DESC_HDR_MODE0_MDEU_PAD; - desc->hdr |= DESC_HDR_MODE0_MDEU_CONT; - desc->hdr &= ~DESC_HDR_DONE_NOTIFY; - - if (desc->ptr[1].ptr) - copy_talitos_ptr(&desc2->ptr[1], &desc->ptr[1], - is_sec1); - else - map_single_talitos_ptr_nosync(dev, &desc2->ptr[1], - req_ctx->hw_context_size, - req_ctx->hw_context, - DMA_TO_DEVICE); - copy_talitos_ptr(&desc2->ptr[2], &desc->ptr[2], is_sec1); - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc2->ptr[3], sg_count, 0, 0); - if (sg_count > 1) - sync_needed = true; - copy_talitos_ptr(&desc2->ptr[5], &desc->ptr[5], is_sec1); - if (req_ctx->last_desc) - map_single_talitos_ptr_nosync(dev, &desc->ptr[5], - req_ctx->hw_context_size, - req_ctx->hw_context, - DMA_FROM_DEVICE); - } - if (sync_needed) dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); @@ -2028,11 +1963,6 @@ static struct talitos_edesc *ahash_edesc_alloc(struct ahash_request *areq, struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - struct talitos_private *priv = dev_get_drvdata(ctx->dev); - bool is_sec1 = has_ftr_sec1(priv); - - if (is_sec1) - nbytes -= req_ctx->nbuf; return talitos_edesc_alloc(ctx->dev, req_ctx->psrc, NULL, NULL, 0, nbytes, 0, 0, 0, areq->base.flags, false); @@ -2051,8 +1981,6 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes unsigned int nsg; int nents; struct device *dev = ctx->dev; - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); u8 *ctx_buf = req_ctx->buf[req_ctx->buf_idx]; if (!req_ctx->last_desc && (nbytes + req_ctx->nbuf <= blocksize)) { @@ -2084,30 +2012,13 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes } /* Chain in any previously buffered data */ - if (!is_sec1 && req_ctx->nbuf) { + if (req_ctx->nbuf) { nsg = (req_ctx->nbuf < nbytes_to_hash) ? 2 : 1; sg_init_table(req_ctx->bufsl, nsg); sg_set_buf(req_ctx->bufsl, ctx_buf, req_ctx->nbuf); if (nsg > 1) sg_chain(req_ctx->bufsl, 2, req_ctx->request_sl); req_ctx->psrc = req_ctx->bufsl; - } else if (is_sec1 && req_ctx->nbuf && req_ctx->nbuf < blocksize) { - int offset; - - if (nbytes_to_hash > blocksize) - offset = blocksize - req_ctx->nbuf; - else - offset = nbytes_to_hash - req_ctx->nbuf; - nents = sg_nents_for_len(req_ctx->request_sl, offset); - if (nents < 0) { - dev_err(dev, "Invalid number of src SG.\n"); - return nents; - } - sg_copy_to_buffer(req_ctx->request_sl, nents, - ctx_buf + req_ctx->nbuf, offset); - req_ctx->nbuf += offset; - req_ctx->psrc = scatterwalk_ffwd(req_ctx->bufsl, req_ctx->request_sl, - offset); } else req_ctx->psrc = req_ctx->request_sl; From 042730207a9917949a5ba4704d5bc2c9fb0c4617 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:53 +0200 Subject: [PATCH 0488/3297] crypto: talitos/hash - use descriptor chaining for SEC1 instead of workqueue commit f1ede6d95d8ad3b32c6a552d2baab805bd00fc38 upstream. Rework the SEC1 ahash implementation to build a chain of hardware descriptors, replacing the previous approach of submitting one descriptor at a time via a workqueue, introduced by commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation"). Introduce ahash_process_req_prepare() which iterates over the request data, allocating enough descriptors to cover the entire ahash request. The new fields (bufsl, src, first, last) are added to talitos_edesc for this purpose. common_nonsnoop_hash() no longer calls talitos_submit(); it only maps and sets up the descriptor. Submission is now done by the caller after the chain is built. ahash_free_desc_list_from() takes over calling common_nonsnoop_hash_unmap() for each descriptor during cleanup. Compared to the workqueue based solution, request are slightly faster since there is no more scheduling latency induced by the workqueue, and only one interrupt is generated by the device at the end of a chain. Commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation") : $ /usr/libexec/libkcapi/sha256sum ./test_5M.bin 013c5609d63c... ./test_5M.bin real 0m 0.41s user 0m 0.01s sys 0m 0.07s Now : $ /usr/libexec/libkcapi/sha256sum ./test_5M.bin 013c5609d63c... ./test_5M.bin real 0m 0.33s user 0m 0.01s sys 0m 0.20s Tested on a system with an MPC885 SoC featuring the SEC1 Lite. The increase in sys time is due to the fact that commit 37b5e8897eb5 ("crypto: talitos - chain in buffered data for ahash on SEC1") can no longer be applied. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 168 ++++++++++++++++++++++++--------------- drivers/crypto/talitos.h | 10 +++ 2 files changed, 115 insertions(+), 63 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 0184982cb39b8..883115d66fc44 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1791,12 +1791,12 @@ static void common_nonsnoop_hash_unmap(struct device *dev, unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE); - if (req_ctx->last_desc) + if (edesc->last && req_ctx->last_request) memcpy(areq->result, req_ctx->hw_context, crypto_ahash_digestsize(tfm)); - if (req_ctx->psrc) - talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL, 0, 0); + if (edesc->src) + talitos_sg_unmap(dev, edesc, edesc->src, NULL, 0, 0); /* When using hashctx-in, must unmap it. */ if (from_talitos_ptr_len(&desc->ptr[1], is_sec1)) @@ -1808,12 +1808,14 @@ static void common_nonsnoop_hash_unmap(struct device *dev, DMA_BIDIRECTIONAL); } -static void free_edesc_list_from(struct talitos_edesc *edesc) +static void free_edesc_list_from(struct ahash_request *areq, struct talitos_edesc *edesc) { + struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); struct talitos_edesc *next; while (edesc) { next = edesc->next_desc; + common_nonsnoop_hash_unmap(ctx->dev, edesc, areq); kfree(edesc); edesc = next; } @@ -1828,19 +1830,18 @@ static void ahash_done(struct device *dev, container_of(desc, struct talitos_edesc, desc); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + if (!req_ctx->last_desc && req_ctx->to_hash_later) { /* Position any partial block for next update/final/finup */ req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; req_ctx->nbuf = req_ctx->to_hash_later; } - common_nonsnoop_hash_unmap(dev, edesc, areq); - free_edesc_list_from(edesc); + free_edesc_list_from(areq, edesc); - if (err) { - ahash_request_complete(areq, err); - return; - } + ahash_request_complete(areq, err); + + return; req_ctx->remaining_ahash_request_bytes -= req_ctx->current_ahash_request_bytes; @@ -1874,18 +1875,15 @@ static void talitos_handle_buggy_hash(struct talitos_ctx *ctx, (char *)padded_hash, DMA_TO_DEVICE); } -static int common_nonsnoop_hash(struct talitos_edesc *edesc, - struct ahash_request *areq, unsigned int length, - void (*callback) (struct device *dev, - struct talitos_desc *desc, - void *context, int error)) +static void common_nonsnoop_hash(struct talitos_edesc *edesc, + struct ahash_request *areq, + unsigned int length) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); struct device *dev = ctx->dev; struct talitos_desc *desc = &edesc->desc; - int ret; bool sync_needed = false; struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); @@ -1894,7 +1892,7 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, /* first DWORD empty */ /* hash context in */ - if (!req_ctx->first_desc || req_ctx->swinit) { + if (!edesc->first || !req_ctx->first_desc || req_ctx->swinit) { map_single_talitos_ptr_nosync(dev, &desc->ptr[1], req_ctx->hw_context_size, req_ctx->hw_context, @@ -1911,22 +1909,22 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, sg_count = edesc->src_nents ?: 1; if (is_sec1 && sg_count > 1) - sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length); + sg_copy_to_buffer(edesc->src, sg_count, edesc->buf, length); else if (length) - sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count, - DMA_TO_DEVICE); + sg_count = dma_map_sg(dev, edesc->src, sg_count, DMA_TO_DEVICE); + /* * data in */ - sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, - &desc->ptr[3], sg_count, 0, 0); + sg_count = talitos_sg_map(dev, edesc->src, length, edesc, &desc->ptr[3], + sg_count, 0, 0); if (sg_count > 1) sync_needed = true; /* fifth DWORD empty */ /* hash/HMAC out -or- hash context out */ - if (req_ctx->last_desc) + if (edesc->last && req_ctx->last_request) map_single_talitos_ptr(dev, &desc->ptr[5], crypto_ahash_digestsize(tfm), req_ctx->hw_context, DMA_FROM_DEVICE); @@ -1944,30 +1942,89 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, if (sync_needed) dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); - - ret = talitos_submit(dev, ctx->ch, desc, callback, - areq); - if (ret != -EINPROGRESS) - goto err; - - return -EINPROGRESS; -err: - common_nonsnoop_hash_unmap(dev, edesc, areq); - kfree(edesc); - return ret; } static struct talitos_edesc *ahash_edesc_alloc(struct ahash_request *areq, + struct scatterlist *src, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); - struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - return talitos_edesc_alloc(ctx->dev, req_ctx->psrc, NULL, NULL, 0, + return talitos_edesc_alloc(ctx->dev, src, NULL, NULL, 0, nbytes, 0, 0, 0, areq->base.flags, false); } +static struct talitos_edesc * +ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, + unsigned int blocksize, bool is_sec1) +{ + struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); + struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + struct talitos_edesc *first = NULL, *prev_edesc = NULL, *edesc; + size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN : SIZE_MAX; + struct scatterlist tmp[2]; + size_t to_hash_this_desc; + struct scatterlist *src; + size_t offset = 0; + + do { + src = scatterwalk_ffwd(tmp, req_ctx->psrc, offset); + + to_hash_this_desc = + min(nbytes, ALIGN_DOWN(desc_max, blocksize)); + + /* Allocate extended descriptor */ + edesc = ahash_edesc_alloc(areq, src, to_hash_this_desc); + if (IS_ERR(edesc)) { + if (first) + free_edesc_list_from(areq, first); + return edesc; + } + + edesc->src = + scatterwalk_ffwd(edesc->bufsl, req_ctx->psrc, offset); + edesc->desc.hdr = ctx->desc_hdr_template; + edesc->first = offset == 0; + edesc->last = nbytes - to_hash_this_desc == 0; + + /* On last one, request SEC to pad; otherwise continue */ + if (req_ctx->last_request && edesc->last) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD; + else + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; + + /* request SEC to INIT hash. */ + if (req_ctx->first_desc && edesc->first && !req_ctx->swinit) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; + + /* + * When the tfm context has a keylen, it's an HMAC. + * A first or last (ie. not middle) descriptor must request HMAC. + */ + if (ctx->keylen && ((req_ctx->first_desc && edesc->first) || + (req_ctx->last_request && edesc->last))) + edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; + + /* clear the DN bit */ + if (is_sec1 && !edesc->last) + edesc->desc.hdr &= ~DESC_HDR_DONE_NOTIFY; + + common_nonsnoop_hash(edesc, areq, to_hash_this_desc); + + offset += to_hash_this_desc; + nbytes -= to_hash_this_desc; + + if (!prev_edesc) + first = edesc; + else + prev_edesc->next_desc = edesc; + prev_edesc = edesc; + } while (nbytes); + + return first; +} + static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); @@ -1976,14 +2033,16 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes struct talitos_edesc *edesc; unsigned int blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + bool is_sec1 = has_ftr_sec1(dev_get_drvdata(ctx->dev)); unsigned int nbytes_to_hash; unsigned int to_hash_later; unsigned int nsg; int nents; struct device *dev = ctx->dev; u8 *ctx_buf = req_ctx->buf[req_ctx->buf_idx]; + int ret; - if (!req_ctx->last_desc && (nbytes + req_ctx->nbuf <= blocksize)) { + if (!req_ctx->last_request && (nbytes + req_ctx->nbuf <= blocksize)) { /* Buffer up to one whole block */ nents = sg_nents_for_len(req_ctx->request_sl, nbytes); if (nents < 0) { @@ -2000,7 +2059,7 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes nbytes_to_hash = nbytes + req_ctx->nbuf; to_hash_later = nbytes_to_hash & (blocksize - 1); - if (req_ctx->last_desc) + if (req_ctx->last_request) to_hash_later = 0; else if (to_hash_later) /* There is a partial block. Hash the full block(s) now */ @@ -2035,30 +2094,16 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes } req_ctx->to_hash_later = to_hash_later; - /* Allocate extended descriptor */ - edesc = ahash_edesc_alloc(req_ctx->areq, nbytes_to_hash); + edesc = ahash_process_req_prepare(areq, nbytes_to_hash, blocksize, + is_sec1); if (IS_ERR(edesc)) return PTR_ERR(edesc); - edesc->desc.hdr = ctx->desc_hdr_template; - - /* On last one, request SEC to pad; otherwise continue */ - if (req_ctx->last_desc) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD; - else - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; - - /* request SEC to INIT hash. */ - if (req_ctx->first_desc && !req_ctx->swinit) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; - - /* When the tfm context has a keylen, it's an HMAC. - * A first or last (ie. not middle) descriptor must request HMAC. - */ - if (ctx->keylen && (req_ctx->first_desc || req_ctx->last_desc)) - edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; + ret = talitos_submit(dev, ctx->ch, &edesc->desc, ahash_done, areq); + if (ret != -EINPROGRESS) + free_edesc_list_from(areq, edesc); - return common_nonsnoop_hash(edesc, req_ctx->areq, nbytes_to_hash, ahash_done); + return ret; } static void sec1_ahash_process_remaining(struct work_struct *work) @@ -2102,16 +2147,13 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) req_ctx->remaining_ahash_request_bytes = nbytes; if (is_sec1) { - if (nbytes > TALITOS1_MAX_DATA_LEN) - nbytes = TALITOS1_MAX_DATA_LEN; - else if (req_ctx->last_request) + if (req_ctx->last_request) req_ctx->last_desc = 1; } req_ctx->current_ahash_request_bytes = nbytes; - return ahash_process_req_one(req_ctx->areq, - req_ctx->current_ahash_request_bytes); + return ahash_process_req_one(req_ctx->areq, nbytes); } static int ahash_init(struct ahash_request *areq) diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h index 596f96bba3efa..11f0eb6a41db6 100644 --- a/drivers/crypto/talitos.h +++ b/drivers/crypto/talitos.h @@ -44,6 +44,11 @@ struct talitos_desc { /* * talitos_edesc - s/w-extended descriptor + * @bufsl: scatterlist buffer + * @src: pointer to input scatterlist + * @first: first descriptor of a chain + * @last: last descriptor of a chain + * * @src_nents: number of segments in input scatterlist * @dst_nents: number of segments in output scatterlist * @iv_dma: dma address of iv for checking continuity and link table @@ -59,6 +64,11 @@ struct talitos_desc { * of link_tbl data */ struct talitos_edesc { + struct scatterlist bufsl[2]; + struct scatterlist *src; + int first; + int last; + int src_nents; int dst_nents; dma_addr_t iv_dma; From b960edc92c81be0ad008f3808e1ef2f1b26890a6 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:54 +0200 Subject: [PATCH 0489/3297] crypto: talitos/hash - drop workqueue mechanism for SEC1 commit be4802afb1700534e48cb776d0d1e772c27de130 upstream. Now that SEC1 hash uses hardware descriptor chaining instead of a workqueue to process requests exceeding TALITOS1_MAX_DATA_LEN, the workqueue code is no longer needed. Remove sec1_ahash_process_remaining(), the related fields from talitos_ahash_req_ctx (request_bufsl, areq, request_sl, remaining_ahash_request_bytes, current_ahash_request_bytes, sec1_ahash_process_remaining), the dead code in ahash_done(), and simplify ahash_process_req() to call ahash_process_req_one() directly with the original areq->src and areq->nbytes. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 80 ++++------------------------------------ 1 file changed, 7 insertions(+), 73 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 883115d66fc44..1f497930800b6 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -12,7 +12,6 @@ * All rights reserved. */ -#include #include #include #include @@ -952,13 +951,6 @@ struct talitos_ahash_req_ctx { unsigned int nbuf; struct scatterlist bufsl[2]; struct scatterlist *psrc; - - struct scatterlist request_bufsl[2]; - struct ahash_request *areq; - struct scatterlist *request_sl; - unsigned int remaining_ahash_request_bytes; - unsigned int current_ahash_request_bytes; - struct work_struct sec1_ahash_process_remaining; }; struct talitos_export_state { @@ -1840,18 +1832,6 @@ static void ahash_done(struct device *dev, free_edesc_list_from(areq, edesc); ahash_request_complete(areq, err); - - return; - - req_ctx->remaining_ahash_request_bytes -= - req_ctx->current_ahash_request_bytes; - - if (!req_ctx->remaining_ahash_request_bytes) { - ahash_request_complete(areq, 0); - return; - } - - schedule_work(&req_ctx->sec1_ahash_process_remaining); } /* @@ -2044,12 +2024,12 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes if (!req_ctx->last_request && (nbytes + req_ctx->nbuf <= blocksize)) { /* Buffer up to one whole block */ - nents = sg_nents_for_len(req_ctx->request_sl, nbytes); + nents = sg_nents_for_len(areq->src, nbytes); if (nents < 0) { dev_err(dev, "Invalid number of src SG.\n"); return nents; } - sg_copy_to_buffer(req_ctx->request_sl, nents, + sg_copy_to_buffer(areq->src, nents, ctx_buf + req_ctx->nbuf, nbytes); req_ctx->nbuf += nbytes; return 0; @@ -2076,18 +2056,18 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes sg_init_table(req_ctx->bufsl, nsg); sg_set_buf(req_ctx->bufsl, ctx_buf, req_ctx->nbuf); if (nsg > 1) - sg_chain(req_ctx->bufsl, 2, req_ctx->request_sl); + sg_chain(req_ctx->bufsl, 2, areq->src); req_ctx->psrc = req_ctx->bufsl; } else - req_ctx->psrc = req_ctx->request_sl; + req_ctx->psrc = areq->src; if (to_hash_later) { - nents = sg_nents_for_len(req_ctx->request_sl, nbytes); + nents = sg_nents_for_len(areq->src, nbytes); if (nents < 0) { dev_err(dev, "Invalid number of src SG.\n"); return nents; } - sg_pcopy_to_buffer(req_ctx->request_sl, nents, + sg_pcopy_to_buffer(areq->src, nents, req_ctx->buf[(req_ctx->buf_idx + 1) & 1], to_hash_later, nbytes - to_hash_later); @@ -2106,54 +2086,9 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes return ret; } -static void sec1_ahash_process_remaining(struct work_struct *work) -{ - struct talitos_ahash_req_ctx *req_ctx = - container_of(work, struct talitos_ahash_req_ctx, - sec1_ahash_process_remaining); - int err = 0; - - req_ctx->request_sl = scatterwalk_ffwd(req_ctx->request_bufsl, - req_ctx->request_sl, TALITOS1_MAX_DATA_LEN); - - if (req_ctx->remaining_ahash_request_bytes > TALITOS1_MAX_DATA_LEN) - req_ctx->current_ahash_request_bytes = TALITOS1_MAX_DATA_LEN; - else { - req_ctx->current_ahash_request_bytes = - req_ctx->remaining_ahash_request_bytes; - - if (req_ctx->last_request) - req_ctx->last_desc = 1; - } - - err = ahash_process_req_one(req_ctx->areq, - req_ctx->current_ahash_request_bytes); - - if (err != -EINPROGRESS) - ahash_request_complete(req_ctx->areq, err); -} - static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) { - struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); - struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); - struct device *dev = ctx->dev; - struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); - - req_ctx->areq = areq; - req_ctx->request_sl = areq->src; - req_ctx->remaining_ahash_request_bytes = nbytes; - - if (is_sec1) { - if (req_ctx->last_request) - req_ctx->last_desc = 1; - } - - req_ctx->current_ahash_request_bytes = nbytes; - - return ahash_process_req_one(req_ctx->areq, nbytes); + return ahash_process_req_one(areq, nbytes); } static int ahash_init(struct ahash_request *areq) @@ -2176,7 +2111,6 @@ static int ahash_init(struct ahash_request *areq) req_ctx->hw_context_size = size; req_ctx->last_request = 0; req_ctx->last_desc = 0; - INIT_WORK(&req_ctx->sec1_ahash_process_remaining, sec1_ahash_process_remaining); dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size, DMA_TO_DEVICE); From 99cc3f5511d8ba9d39cc60c46e9f82cadac7dfeb Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:55 +0200 Subject: [PATCH 0490/3297] crypto: talitos/hash - rename first_desc/last_desc to first_request/last_request commit 8bcf00671400ac3b3a4cc3011e6c1496dbd880fd upstream. In talitos_ahash_req_ctx and talitos_export_state, the fields first_desc and last_desc describe request-level (not descriptor-level) state. Rename them to first_request and last_request for clarity. last_desc is also removed from talitos_ahash_req_ctx as it is no longer used. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 1f497930800b6..6be42935068a8 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -944,8 +944,7 @@ struct talitos_ahash_req_ctx { u8 buf[2][HASH_MAX_BLOCK_SIZE]; int buf_idx; unsigned int swinit; - unsigned int first_desc; - unsigned int last_desc; + unsigned int first_request; unsigned int last_request; unsigned int to_hash_later; unsigned int nbuf; @@ -957,8 +956,8 @@ struct talitos_export_state { u32 hw_context[TALITOS_MDEU_MAX_CONTEXT_SIZE / sizeof(u32)]; u8 buf[HASH_MAX_BLOCK_SIZE]; unsigned int swinit; - unsigned int first_desc; - unsigned int last_desc; + unsigned int first_request; + unsigned int last_request; unsigned int to_hash_later; unsigned int nbuf; }; @@ -1822,8 +1821,7 @@ static void ahash_done(struct device *dev, container_of(desc, struct talitos_edesc, desc); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); - - if (!req_ctx->last_desc && req_ctx->to_hash_later) { + if (!req_ctx->last_request && req_ctx->to_hash_later) { /* Position any partial block for next update/final/finup */ req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; req_ctx->nbuf = req_ctx->to_hash_later; @@ -1872,7 +1870,7 @@ static void common_nonsnoop_hash(struct talitos_edesc *edesc, /* first DWORD empty */ /* hash context in */ - if (!edesc->first || !req_ctx->first_desc || req_ctx->swinit) { + if (!edesc->first || !req_ctx->first_request || req_ctx->swinit) { map_single_talitos_ptr_nosync(dev, &desc->ptr[1], req_ctx->hw_context_size, req_ctx->hw_context, @@ -1880,7 +1878,7 @@ static void common_nonsnoop_hash(struct talitos_edesc *edesc, req_ctx->swinit = 0; } /* Indicate next op is not the first. */ - req_ctx->first_desc = 0; + req_ctx->first_request = 0; /* HMAC key */ if (ctx->keylen) @@ -1975,14 +1973,14 @@ ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT; /* request SEC to INIT hash. */ - if (req_ctx->first_desc && edesc->first && !req_ctx->swinit) + if (req_ctx->first_request && edesc->first && !req_ctx->swinit) edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT; /* * When the tfm context has a keylen, it's an HMAC. * A first or last (ie. not middle) descriptor must request HMAC. */ - if (ctx->keylen && ((req_ctx->first_desc && edesc->first) || + if (ctx->keylen && ((req_ctx->first_request && edesc->first) || (req_ctx->last_request && edesc->last))) edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; @@ -2103,14 +2101,13 @@ static int ahash_init(struct ahash_request *areq) /* Initialize the context */ req_ctx->buf_idx = 0; req_ctx->nbuf = 0; - req_ctx->first_desc = 1; /* first_desc indicates h/w must init its context */ + req_ctx->first_request = 1; req_ctx->swinit = 0; /* assume h/w init of context */ size = (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE) ? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256 : TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512; req_ctx->hw_context_size = size; req_ctx->last_request = 0; - req_ctx->last_desc = 0; dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size, DMA_TO_DEVICE); @@ -2202,8 +2199,8 @@ static int ahash_export(struct ahash_request *areq, void *out) req_ctx->hw_context_size); memcpy(export->buf, req_ctx->buf[req_ctx->buf_idx], req_ctx->nbuf); export->swinit = req_ctx->swinit; - export->first_desc = req_ctx->first_desc; - export->last_desc = req_ctx->last_desc; + export->first_request = req_ctx->first_request; + export->last_request = req_ctx->last_request; export->to_hash_later = req_ctx->to_hash_later; export->nbuf = req_ctx->nbuf; @@ -2228,8 +2225,8 @@ static int ahash_import(struct ahash_request *areq, const void *in) memcpy(req_ctx->hw_context, export->hw_context, size); memcpy(req_ctx->buf[0], export->buf, export->nbuf); req_ctx->swinit = export->swinit; - req_ctx->first_desc = export->first_desc; - req_ctx->last_desc = export->last_desc; + req_ctx->first_request = export->first_request; + req_ctx->last_request = export->last_request; req_ctx->to_hash_later = export->to_hash_later; req_ctx->nbuf = export->nbuf; From fda9cb9b7191c981877ff7927aade2e26b841ec3 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:56 +0200 Subject: [PATCH 0491/3297] crypto: talitos/hash - remove useless wrapper commit 907ae6088c82c9abae2d26477fddd60df6ad003b upstream. ahash_process_req() was a wrapper used in commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation"). Rename ahash_process_req_one() to ahash_process_req() and remove the wrapper. Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 6be42935068a8..b4283b6c18ef6 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -2003,7 +2003,7 @@ ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, return first; } -static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes) +static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); @@ -2084,11 +2084,6 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes return ret; } -static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes) -{ - return ahash_process_req_one(areq, nbytes); -} - static int ahash_init(struct ahash_request *areq) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); From 93f000e89976eb5cc55385d80516f247ca4a27a6 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Thu, 7 May 2026 16:41:57 +0200 Subject: [PATCH 0492/3297] crypto: talitos/hash - fix SEC2 64k - 1 ahash request limitation commit 6e12daff6ec125102a6fdcafc5aa7199f7ce8933 upstream. The problem described in commit 655ef638a2bc ("crypto: talitos - fix SEC1 32k ahash request limitation") also apply for the SEC2 hardware, but with a limitation of 64k - 1 bytes. Split ahash_done() into SEC1 and SEC2 paths: SEC1 continues to free the whole descriptor list at once, while SEC2 now iterates through descriptors one by one, submitting the next only after the previous completes, which is required since SEC2 cannot chain descriptors in hardware. Cc: stable@vger.kernel.org Fixes: c662b043cdca ("crypto: af_alg/hash: Support MSG_SPLICE_PAGES") Signed-off-by: Paul Louvel Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 47 +++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b4283b6c18ef6..4b53b13f96d9f 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1820,16 +1820,46 @@ static void ahash_done(struct device *dev, struct talitos_edesc *edesc = container_of(desc, struct talitos_edesc, desc); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + bool is_sec1 = has_ftr_sec1(dev_get_drvdata(dev)); + struct talitos_ctx *ctx = crypto_ahash_ctx(tfm); + struct talitos_edesc *next; - if (!req_ctx->last_request && req_ctx->to_hash_later) { - /* Position any partial block for next update/final/finup */ - req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; - req_ctx->nbuf = req_ctx->to_hash_later; - } + if (is_sec1) { + if (!req_ctx->last_request && req_ctx->to_hash_later) { + /* Position any partial block for next update/final/finup */ + req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; + req_ctx->nbuf = req_ctx->to_hash_later; + } + + free_edesc_list_from(areq, edesc); + ahash_request_complete(areq, err); + } else { + next = edesc->next_desc; - free_edesc_list_from(areq, edesc); + common_nonsnoop_hash_unmap(dev, edesc, areq); + kfree(edesc); - ahash_request_complete(areq, err); + if (err) + goto out; + + if (next) { + err = talitos_submit(dev, ctx->ch, &next->desc, + ahash_done, areq); + if (err != -EINPROGRESS) + goto out; + return; + } +out: + if (!req_ctx->last_request && req_ctx->to_hash_later) { + /* Position any partial block for next update/final/finup */ + req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1; + req_ctx->nbuf = req_ctx->to_hash_later; + } + if (err && next) + free_edesc_list_from(areq, next); + ahash_request_complete(areq, err); + } } /* @@ -1940,7 +1970,8 @@ ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes, struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq)); struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); struct talitos_edesc *first = NULL, *prev_edesc = NULL, *edesc; - size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN : SIZE_MAX; + size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN : + TALITOS2_MAX_DATA_LEN; struct scatterlist tmp[2]; size_t to_hash_this_desc; struct scatterlist *src; From 75422f5e50222fa056cb8d0a837cd869892e7751 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 3 Jun 2026 12:06:12 +0100 Subject: [PATCH 0493/3297] arm64: fpsimd: Fix type mismatch in sme_{save,load}_state() commit 247bd153905085c18ff9006cca1ccb96dfd18e7f upstream. The sme_save_state() and sme_load_state() functions take a 32-bit int argument that describes whether to save/restore ZT0. Their assembly implementations consume the entire 64-bit register containing this 32-bit value, and will attempt to save/restore ZT0 if any bit of that 64-bit register is non-zero. Per the AAPCS64 parameter passing rules, the callee is responsible for any necessary widening, and the upper 32-bits are permitted to contain arbitrary values. If the upper 32 bits are non-zero, this could result in an unexpected attempt to save/restore ZT0, and consequently could lead to unexpected traps/undefs/faults. In practice compilers are very unlikely to generate code where the upper 32-bits would be non-zero, but they are permitted to do so. Fix this by only consuming the low 32 bits of the register, and update comments accordingly. Fixes: 95fcec713259 ("arm64/sme: Implement context switching for ZT0") Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Fuad Tabba Cc: James Morse Cc: Marc Zyngier Cc: Mark Brown Cc: Oliver Upton Cc: Vladimir Murzin Cc: Will Deacon Cc: stable@vger.kernel.org Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry-fpsimd.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S index 6325db1a2179c..fb441702495fa 100644 --- a/arch/arm64/kernel/entry-fpsimd.S +++ b/arch/arm64/kernel/entry-fpsimd.S @@ -103,13 +103,13 @@ SYM_FUNC_END(sme_set_vq) * Save the ZA and ZT state * * x0 - pointer to buffer for state - * x1 - number of ZT registers to save + * w1 - number of ZT registers to save */ SYM_FUNC_START(sme_save_state) _sme_rdsvl 2, 1 // x2 = VL/8 sme_save_za 0, x2, 12 // Leaves x0 pointing to the end of ZA - cbz x1, 1f + cbz w1, 1f _str_zt 0 1: ret @@ -119,13 +119,13 @@ SYM_FUNC_END(sme_save_state) * Load the ZA and ZT state * * x0 - pointer to buffer for state - * x1 - number of ZT registers to save + * w1 - number of ZT registers to save */ SYM_FUNC_START(sme_load_state) _sme_rdsvl 2, 1 // x2 = VL/8 sme_load_za 0, x2, 12 // Leaves x0 pointing to the end of ZA - cbz x1, 1f + cbz w1, 1f _ldr_zt 0 1: ret From 18d6048b1b1b4bc37c8883a39289f4b5c9a6fd5f Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:23:56 +0800 Subject: [PATCH 0494/3297] spi: fsl-lpspi: replace dmaengine_terminate_all() with dmaengine_terminate_sync() commit e703ce47691b967fe9b4057fb1d062273211afa9 upstream. dmaengine_terminate_all() has been deprecated, so replace it with dmaengine_terminate_sync(). Fixes: 09c04466ce7e ("spi: lpspi: add dma mode support") Cc: stable@vger.kernel.org Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062357.3191349-2-carlos.song@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-fsl-lpspi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 47d372557e4f6..3e3d8ea56ad54 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -619,7 +619,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { - dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_tx); return -EINVAL; } @@ -640,8 +640,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -650,8 +650,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -660,8 +660,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA TX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } @@ -670,8 +670,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA RX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } From 808033d80d5c9f8adf7e8de9317389270ce13430 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:23:57 +0800 Subject: [PATCH 0495/3297] spi: fsl-lpspi: terminate the RX channel on TX prepare failure path commit 01980b5da56e573d62798d0ff6c86bcaa2b22cbe upstream. When dmaengine_prep_slave_sg() fails for the TX channel, the error path terminates the TX DMA channel but leaves the RX channel running. Since the RX channel was already submitted and issued prior to preparing the TX descriptor, returning -EINVAL causes the SPI core to unmap the DMA buffers while the RX DMA engine continues writing to them, leading to potential memory corruption or use-after-free. Terminate the RX channel before returning on the TX prepare failure path. Fixes: 09c04466ce7e ("spi: lpspi: add dma mode support") Cc: stable@vger.kernel.org Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062357.3191349-3-carlos.song@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-fsl-lpspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 3e3d8ea56ad54..9fb2c1c18f62a 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -619,7 +619,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { - dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -EINVAL; } From add1e4112e00b619614784bf630aeebfdefa23e1 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Wed, 29 Apr 2026 12:49:14 +0200 Subject: [PATCH 0496/3297] x86/mm: Fix freeing of PMD-sized vmemmap pages commit 39406c05f8f150f1685839acd38ffdd69ff92031 upstream. Commit bf9e4e30f353 ("x86/mm: use pagetable_free()"), switched from freeing non-boot page tables through __free_pages() to pagetable_free(). However, the function is also called to free vmemmap pages. Given that vmemmap pages are not page tables, already the page_ptdesc(page) is wrong. But worse, pagetable_free() calls: __free_pages(page, compound_order(page)); Since vmemmap pages are not compound pages (see vmemmap_alloc_block()) -- except for HVO, which doesn't apply here -- only first page of a PMD-sized vmemmap page is freed, leaking the other ones. Fix it by properly decoupling pagetable and vmemmap freeing. free_pagetable() no longer has to mess with SECTION_INFO, as only the vmemmap is marked like that in register_page_bootmem_memmap(). The indentation in remove_pmd_table() is messed up. Fix that while touching it. Bootmem info handling will soon be fixed up. For now, handle it similar to free_pagetable(), just avoiding the ifdef. [ dhansen: changelog munging. More imperative voice ] Fixes: bf9e4e30f353 ("x86/mm: use pagetable_free()") Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Signed-off-by: Dave Hansen Acked-by: Mike Rapoport (Microsoft) Tested-by: Lance Yang Link: https://lore.kernel.org/20260429-vmemmap-v2-1-8dfcacffd877@kernel.org Link: https://patch.msgid.link/20260429-vmemmap-v2-1-8dfcacffd877@kernel.org Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/x86/mm/init_64.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 3d9a5e4ccaa43..74eda988e0b02 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1021,7 +1021,7 @@ static void __meminit free_pagetable(struct page *page, int order) #ifdef CONFIG_HAVE_BOOTMEM_INFO_NODE enum bootmem_type type = bootmem_type(page); - if (type == SECTION_INFO || type == MIX_SECTION_INFO) { + if (type == MIX_SECTION_INFO) { while (nr_pages--) put_page_bootmem(page++); } else { @@ -1035,13 +1035,24 @@ static void __meminit free_pagetable(struct page *page, int order) } } -static void __meminit free_hugepage_table(struct page *page, +static void __meminit free_vmemmap_pages(struct page *page, unsigned int order, struct vmem_altmap *altmap) { - if (altmap) - vmem_altmap_free(altmap, PMD_SIZE / PAGE_SIZE); - else - free_pagetable(page, get_order(PMD_SIZE)); + unsigned long nr_pages = 1u << order; + + if (altmap) { + vmem_altmap_free(altmap, nr_pages); + } else if (PageReserved(page)) { + if (IS_ENABLED(CONFIG_HAVE_BOOTMEM_INFO_NODE) && + bootmem_type(page) == SECTION_INFO) { + while (nr_pages--) + put_page_bootmem(page++); + } else { + free_reserved_pages(page, nr_pages); + } + } else { + __free_pages(page, order); + } } static void __meminit free_pte_table(pte_t *pte_start, pmd_t *pmd) @@ -1125,7 +1136,8 @@ remove_pte_table(pte_t *pte_start, unsigned long addr, unsigned long end, return; if (!direct) - free_pagetable(pte_page(*pte), 0); + /* We never populate base pages from the altmap. */ + free_vmemmap_pages(pte_page(*pte), 0, NULL); spin_lock(&init_mm.page_table_lock); pte_clear(&init_mm, addr, pte); @@ -1160,19 +1172,19 @@ remove_pmd_table(pmd_t *pmd_start, unsigned long addr, unsigned long end, if (IS_ALIGNED(addr, PMD_SIZE) && IS_ALIGNED(next, PMD_SIZE)) { if (!direct) - free_hugepage_table(pmd_page(*pmd), - altmap); + free_vmemmap_pages(pmd_page(*pmd), + PMD_ORDER, altmap); spin_lock(&init_mm.page_table_lock); pmd_clear(pmd); spin_unlock(&init_mm.page_table_lock); pages++; } else if (vmemmap_pmd_is_unused(addr, next)) { - free_hugepage_table(pmd_page(*pmd), - altmap); - spin_lock(&init_mm.page_table_lock); - pmd_clear(pmd); - spin_unlock(&init_mm.page_table_lock); + free_vmemmap_pages(pmd_page(*pmd), PMD_ORDER, + altmap); + spin_lock(&init_mm.page_table_lock); + pmd_clear(pmd); + spin_unlock(&init_mm.page_table_lock); } continue; } From f4dd5621a6eefd16666352ce90ec46920aa0737e Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 14 Apr 2026 11:17:16 -0700 Subject: [PATCH 0497/3297] EDAC/i10nm: Don't fail probing if ADXL is missing commit e360a6d65bb46c527a5909430a31d640cdd5036e upstream. ADXL is not present in Coreboot- or Slimbootloader-based BIOSes and as result, the driver fails to probe there. Since commit 2738c69a8813 ("EDAC/i10nm: Add driver decoder for Ice Lake and Tremont CPUs"), i10nm_edac supports driver decoder. Switch to driver decoding when ADXL is not present. Signed-off-by: Vasily Khoruzhick Signed-off-by: Tony Luck Reviewed-by: Qiuxu Zhuo Cc: stable@vger.kernel.org # v6.1+ Link: https://patch.msgid.link/20260414181735.87023-1-anarsoul@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/edac/i10nm_base.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c index 2010a47149f44..dfd49412f3199 100644 --- a/drivers/edac/i10nm_base.c +++ b/drivers/edac/i10nm_base.c @@ -79,6 +79,7 @@ static struct res_config *res_cfg; static int retry_rd_err_log; static int decoding_via_mca; static bool mem_cfg_2lm; +static bool no_adxl; static struct reg_rrl icx_reg_rrl_ddr = { .set_num = 2, @@ -1207,8 +1208,14 @@ static int __init i10nm_init(void) } rc = skx_adxl_get(); - if (rc) - goto fail; + if (rc) { + /* Decoding errors via MCA banks for 2LM isn't supported yet */ + if (rc != -ENODEV || mem_cfg_2lm) + goto fail; + i10nm_printk(KERN_INFO, "ADXL not found, falling back to MCA-based decoding.\n"); + no_adxl = true; + decoding_via_mca = true; + } opstate_init(); mce_register_decode_chain(&i10nm_mce_dec); @@ -1242,7 +1249,8 @@ static void __exit i10nm_exit(void) skx_teardown_debug(); mce_unregister_decode_chain(&i10nm_mce_dec); - skx_adxl_put(); + if (!no_adxl) + skx_adxl_put(); skx_remove(); } From 6b01ed165d2982a64ba75f86aa2cabac0b8697ef Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 31 Dec 2025 13:07:21 +0100 Subject: [PATCH 0498/3297] watchdog: apple: Add "apple,t8103-wdt" compatible commit 14ca4868886f2188401fe06cd7bf01a330b3fb99 upstream. After discussion with the devicetree maintainers we agreed to not extend lists with the generic compatible "apple,wdt" anymore [1]. Use "apple,t8103-wdt" as base compatible as it is the SoC the driver and bindings were written for. [1]: https://lore.kernel.org/asahi/12ab93b7-1fc2-4ce0-926e-c8141cfe81bf@kernel.org/ Fixes: 4ed224aeaf66 ("watchdog: Add Apple SoC watchdog driver") Cc: stable@vger.kernel.org Reviewed-by: Neal Gompa Signed-off-by: Janne Grunau Link: https://lore.kernel.org/r/20251231-watchdog-apple-t8103-base-compat-v1-1-1702a02e0c45@jannau.net Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/apple_wdt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index 66a158f67a712..6b9b0f9b05ced 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -218,6 +218,7 @@ static int apple_wdt_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(apple_wdt_pm_ops, apple_wdt_suspend, apple_wdt_resume); static const struct of_device_id apple_wdt_of_match[] = { + { .compatible = "apple,t8103-wdt" }, { .compatible = "apple,wdt" }, {}, }; From 22cb337370e6539b0418832c6040e9b00c1b74ca Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 27 May 2026 10:48:50 +0000 Subject: [PATCH 0499/3297] regulator: scmi: fix of_node refcount leak in scmi_regulator_probe() commit fa11039d6cdff84584a3ef8cc1f5e1b56e045da2 upstream. scmi_regulator_probe() calls of_find_node_by_name() which takes a reference on the returned device node. On the error path where process_scmi_regulator_of_node() fails, the function returns without calling of_node_put() on the child node, leaking the reference. Add of_node_put(np) on the error path to properly release the reference. Cc: stable@vger.kernel.org Fixes: 0fbeae70ee7c ("regulator: add SCMI driver") Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260527104850.872415-1-vulab@iscas.ac.cn Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/scmi-regulator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c index 6d609c42e4793..c005e65ba0ecb 100644 --- a/drivers/regulator/scmi-regulator.c +++ b/drivers/regulator/scmi-regulator.c @@ -345,8 +345,10 @@ static int scmi_regulator_probe(struct scmi_device *sdev) for_each_child_of_node_scoped(np, child) { ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo); /* abort on any mem issue */ - if (ret == -ENOMEM) + if (ret == -ENOMEM) { + of_node_put(np); return ret; + } } of_node_put(np); /* From af6048e913052dd59a6faa59e7c53ce627360ebc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:07 +0200 Subject: [PATCH 0500/3297] i2c: core: fix hang on adapter registration failure commit 3c7e164344e5bcf6f274bbf59a3274f5caad9bc1 upstream. Clients may be registered from bus notifier callbacks when the adapter is registered. On a subsequent error during registration, the adapter references taken by such clients prevent the wait for the references to be released from ever completing. Fix this by refactoring client deregistration and deregistering also on late adapter registration failures. Fixes: f8756c67b3de ("i2c: core: call of_i2c_setup_smbus_alert in i2c_register_adapter") Cc: stable@vger.kernel.org # 4.15 Cc: Phil Reid Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 49 ++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 05b315405639b..e0453f7b5d972 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -63,6 +63,7 @@ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); +static void i2c_deregister_clients(struct i2c_adapter *adap); static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); @@ -1609,6 +1610,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return 0; out_reg: + i2c_deregister_clients(adap); debugfs_remove_recursive(adap->debugfs); init_completion(&adap->dev_released); device_unregister(&adap->dev); @@ -1748,29 +1750,10 @@ static int __process_removed_adapter(struct device_driver *d, void *data) return 0; } -/** - * i2c_del_adapter - unregister I2C adapter - * @adap: the adapter being unregistered - * Context: can sleep - * - * This unregisters an I2C adapter which was previously registered - * by @i2c_add_adapter or @i2c_add_numbered_adapter. - */ -void i2c_del_adapter(struct i2c_adapter *adap) +static void i2c_deregister_clients(struct i2c_adapter *adap) { - struct i2c_adapter *found; struct i2c_client *client, *next; - /* First make sure that this adapter was ever added */ - mutex_lock(&core_lock); - found = idr_find(&i2c_adapter_idr, adap->nr); - mutex_unlock(&core_lock); - if (found != adap) { - pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); - return; - } - - i2c_acpi_remove_space_handler(adap); /* Tell drivers about this removal */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, @@ -1796,6 +1779,32 @@ void i2c_del_adapter(struct i2c_adapter *adap) * them up properly, so we give them a chance to do that first. */ device_for_each_child(&adap->dev, NULL, __unregister_client); device_for_each_child(&adap->dev, NULL, __unregister_dummy); +} + +/** + * i2c_del_adapter - unregister I2C adapter + * @adap: the adapter being unregistered + * Context: can sleep + * + * This unregisters an I2C adapter which was previously registered + * by @i2c_add_adapter or @i2c_add_numbered_adapter. + */ +void i2c_del_adapter(struct i2c_adapter *adap) +{ + struct i2c_adapter *found; + + /* First make sure that this adapter was ever added */ + mutex_lock(&core_lock); + found = idr_find(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + if (found != adap) { + pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); + return; + } + + i2c_acpi_remove_space_handler(adap); + + i2c_deregister_clients(adap); /* device name is gone after device_unregister */ dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); From c8b7e113f7b61eef2f017e6329c27c2331058c5a Mon Sep 17 00:00:00 2001 From: Lee Jia Jie Date: Thu, 9 Jul 2026 21:56:19 +0800 Subject: [PATCH 0501/3297] perf/aux: Fix page UAF in map_range() commit 5948aaf64f81f217a25dcc2bf6c0779bca19566c upstream. map_range() reads rb->aux_pages[], rb->aux_nr_pages and rb->aux_pgoff via perf_mmap_to_page() while holding only event->mmap_mutex. Those fields are serialized by rb->aux_mutex, and mmap_mutex is per event. Thus, two events sharing one rb via PERF_EVENT_IOC_SET_OUTPUT can race rb_alloc_aux() with map_range(), leading to a page-UAF scenario as follows: CPU 0 CPU 1 ===== ===== rb_alloc_aux() map_range() [1]: allocate rb->aux_pages[0] [2]: rb->aux_nr_pages++ [3]: perf_mmap_to_page() returns rb->aux_pages[0] [4]: map it as VM_PFNMAP [5]: rb->aux_pgoff = 1 munmap the page [6]: free rb->aux_pages[0] Pages mapped as VM_PFNMAP have no refcount protection, so CPU 1 holds a mapping to a freed physical frame. Fix this by taking rb->aux_mutex across the page walk in map_range(). Fixes: b709eb872e19 ("perf: map pages in advance") Signed-off-by: Lee Jia Jie Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Namhyung Kim Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index 449550b3bb286..f3bd7c97c3aab 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6879,6 +6879,8 @@ static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma) int err = 0; unsigned long pagenum; + guard(mutex)(&rb->aux_mutex); + /* * We map this as a VM_PFNMAP VMA. * From 2dad64a97e1df47f5d9ccb17fa319aa348617226 Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Wed, 1 Jul 2026 18:28:46 +0800 Subject: [PATCH 0502/3297] tracing: Prevent out-of-bounds read in glob matching commit 0a6070839b1ef276d5b05bedfb787743e140fb17 upstream. String event fields are not necessarily NUL-terminated, so the filter predicate functions (filter_pred_string(), filter_pred_strloc() and filter_pred_strrelloc()) pass the field length to the regex match callbacks, and the length-aware matchers honour it. regex_match_glob() was the exception: it ignored the length and called glob_match(), which scans the string until it hits a NUL byte. Some string fields are not NUL-terminated. One example is the dynamic char array of the xfs_* namespace tracepoints, which is copied without a trailing NUL. For such a field, glob matching reads past the end of the event field, causing a KASAN slab-out-of-bounds read in glob_match(), reached via regex_match_glob() and filter_match_preds() from the xfs_lookup tracepoint. Add a length-bounded glob_match_len() and use it from regex_match_glob() so glob matching always stops at the field boundary. The matching loop is factored into a shared helper so glob_match() keeps its behaviour. Fixes: 60f1d5e3bac4 ("ftrace: Support full glob matching") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/da1aaf125fc3b63320b0c540fd6afa7c3d5b4f1a.1782836943.git.hhhuang@smu.edu.sg Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Huihui Huang Signed-off-by: Ren Wei Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- include/linux/glob.h | 1 + kernel/trace/trace_events_filter.c | 6 ++---- lib/glob.c | 31 ++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/include/linux/glob.h b/include/linux/glob.h index 861327b33e41d..91595e750936e 100644 --- a/include/linux/glob.h +++ b/include/linux/glob.h @@ -6,5 +6,6 @@ #include /* For __pure */ bool __pure glob_match(char const *pat, char const *str); +bool __pure glob_match_len(char const *pat, char const *str, size_t len); #endif /* _LINUX_GLOB_H */ diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 54226b48b2d16..c6679caf2e9b6 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, struct regex *r, int len) return 0; } -static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused) +static int regex_match_glob(char *str, struct regex *r, int len) { - if (glob_match(r->pattern, str)) - return 1; - return 0; + return glob_match_len(r->pattern, str, len) ? 1 : 0; } /** diff --git a/lib/glob.c b/lib/glob.c index aa57900d2062c..d3f36de9ab5a4 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -9,6 +9,9 @@ MODULE_DESCRIPTION("glob(7) matching"); MODULE_LICENSE("Dual MIT/GPL"); +static bool __pure glob_match_str(char const *pat, char const *str, + char const *str_end); + /** * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0) * @pat: Shell-style pattern to match, e.g. "*.[ch]". @@ -38,6 +41,29 @@ MODULE_LICENSE("Dual MIT/GPL"); * An opening bracket without a matching close is matched literally. */ bool __pure glob_match(char const *pat, char const *str) +{ + return glob_match_str(pat, str, NULL); +} +EXPORT_SYMBOL(glob_match); + +/** + * glob_match_len - glob match against a length-bounded string + * @pat: Shell-style pattern to match. + * @str: String to match. Need not be NUL-terminated. + * @len: Number of bytes of @str that may be read. + * + * Like glob_match(), but @str is only read up to @len bytes, so it can be + * used on buffers that are not NUL-terminated (e.g. trace event fields). + * A NUL byte within @len still terminates the string. + */ +bool __pure glob_match_len(char const *pat, char const *str, size_t len) +{ + return glob_match_str(pat, str, str + len); +} +EXPORT_SYMBOL(glob_match_len); + +static bool __pure glob_match_str(char const *pat, char const *str, + char const *str_end) { /* * Backtrack to previous * on mismatch and retry starting one @@ -53,9 +79,11 @@ bool __pure glob_match(char const *pat, char const *str) * on mismatch, or true after matching the trailing nul bytes. */ for (;;) { - unsigned char c = *str++; + unsigned char c = (str_end && str >= str_end) ? '\0' : *str; unsigned char d = *pat++; + str++; + switch (d) { case '?': /* Wildcard: anything but nul */ if (c == '\0') @@ -122,4 +150,3 @@ bool __pure glob_match(char const *pat, char const *str) } } } -EXPORT_SYMBOL(glob_match); From 75ca99875aa4e2241a0a3f5bfd2d03b44d93933d Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Thu, 2 Jul 2026 11:04:11 -0300 Subject: [PATCH 0503/3297] audit: fix potential integer overflow in audit_log_n_hex() commit 65dfde57d1e29ce2b76fc23dd565eccd5c0bc0f0 upstream. The function calculates new_len as len << 1 for hex encoding. This has two overflow risks: the shift itself can overflow when len is large, and the result can be truncated when assigned to new_len (declared as int) from the size_t calculation. Fix by using check_shl_overflow() to catch shift overflow and changing new_len and loop counter i to size_t to prevent truncation. Cc: stable@vger.kernel.org Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") Reviewed-by: Richard Guy Briggs Signed-off-by: Ricardo Robaina [PM: remove vertical whitspace noise] Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 0594897ce1595..db7868945a877 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -2075,7 +2076,8 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len) { - int i, avail, new_len; + int avail; + size_t i, new_len; unsigned char *ptr; struct sk_buff *skb; @@ -2085,7 +2087,12 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); - new_len = len<<1; + + if (check_shl_overflow(len, 1, &new_len)) { + audit_log_format(ab, "?"); + return; + } + if (new_len >= avail) { /* Round the buffer request up to the next multiple */ new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1); From a937e92c1d00534b5c2e3e9f4381b7e988180797 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Thu, 11 Jun 2026 17:02:15 -0400 Subject: [PATCH 0504/3297] NFSv4: include MAY_WRITE in open permission mask for O_TRUNC commit 5140f099ecd8a2f2808b7f7b720ee1bad8468974 upstream. POSIX requires write permission to truncate a file, so an open() that specifies O_TRUNC must be authorized for write access regardless of the O_ACCMODE access mode. nfs_open_permission_mask() builds the access mask passed to nfs_may_open(), which is the local authorization gate for OPENs the client serves itself from a cached write delegation via the can_open_delegated() path in nfs4_try_open_cached(). The mask is derived from O_ACCMODE alone, so an open(O_RDONLY | O_TRUNC) against a file the caller cannot write requests only MAY_READ and passes the local check. The OPEN is then satisfied locally and the truncation is issued to the server as a SETATTR(size=0) over the delegation stateid, which the server accepts under standard write-delegation semantics. POSIX requires that this open fail with EACCES. Include MAY_WRITE in the mask whenever O_TRUNC is set so the local check matches the access the server would have enforced. Suggested-by: Trond Myklebust Fixes: af22f94ae02a ("NFSv4: Simplify _nfs4_do_access()") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Coddington Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/dir.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index a653a401b7970..31ca8bbfb9702 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -3302,6 +3302,8 @@ static int nfs_open_permission_mask(int openflags) mask |= MAY_READ; if ((openflags & O_ACCMODE) != O_RDONLY) mask |= MAY_WRITE; + if (openflags & O_TRUNC) + mask |= MAY_WRITE; } return mask; From b883733302508be1c678ce90b63d6b403f849995 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Wed, 10 Jun 2026 11:04:29 +0200 Subject: [PATCH 0505/3297] rqspinlock: Fix order in raw_res_spin_(un)lock_irq to allow schedule commit b48bd16eb9fc57a463a337ca148516cdf3212d61 upstream. raw_res_spin_unlock_irqrestore() calls raw_res_spin_unlock() and then restores interrupts, this means preemption is enabled when interrupts are still disabled (as part of raw_res_spin_unlock()) so this cannot trigger an actual preemption. This is inconsistent with other spinlock implementations (raw_spin_unlock_irqrestore() and bpf_res_spin_unlock_irqrestore() itself). Adjust the macro to ensure interrupts are enabled before enabling preemption, allowing to schedule at that point. Make the same modification in the error path of raw_res_spin_lock_irqsave(). Fixes: 101acd2e78b1 ("rqspinlock: Add macros for rqspinlock usage") Cc: stable@vger.kernel.org Acked-by: Arnd Bergmann # asm-generic Acked-by: Waiman Long Acked-by: Kumar Kartikeya Dwivedi Signed-off-by: Gabriele Monaco Link: https://lore.kernel.org/r/20260610090431.32427-1-gmonaco@redhat.com Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- include/asm-generic/rqspinlock.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/include/asm-generic/rqspinlock.h b/include/asm-generic/rqspinlock.h index 5c5cf2f7fc395..780fdeb82f669 100644 --- a/include/asm-generic/rqspinlock.h +++ b/include/asm-generic/rqspinlock.h @@ -243,12 +243,20 @@ static __always_inline void res_spin_unlock(rqspinlock_t *lock) ({ \ int __ret; \ local_irq_save(flags); \ - __ret = raw_res_spin_lock(lock); \ - if (__ret) \ + preempt_disable(); \ + __ret = res_spin_lock(lock); \ + if (__ret) { \ local_irq_restore(flags); \ + preempt_enable(); \ + } \ __ret; \ }) -#define raw_res_spin_unlock_irqrestore(lock, flags) ({ raw_res_spin_unlock(lock); local_irq_restore(flags); }) +#define raw_res_spin_unlock_irqrestore(lock, flags) \ + ({ \ + res_spin_unlock(lock); \ + local_irq_restore(flags); \ + preempt_enable(); \ + }) #endif /* __ASM_GENERIC_RQSPINLOCK_H */ From a82e170637e050a803b4f37542371ef216bf66d2 Mon Sep 17 00:00:00 2001 From: Andrii Kuchmenko Date: Mon, 18 May 2026 17:32:33 +0300 Subject: [PATCH 0506/3297] module: decompress: check return value of module_extend_max_pages() commit 786d2d84416a9a1c1a47b71a68d679d886284be2 upstream. module_extend_max_pages() calls kvrealloc() internally and returns -ENOMEM on allocation failure. The return value is never checked. If the initial allocation fails, info->pages remains NULL and info->max_pages remains 0. Subsequent calls to module_get_next_page() will attempt to dynamically grow the array by calling module_extend_max_pages(info, 0) since info->used_pages is 0. This results in kvrealloc(NULL, 0) returning ZERO_SIZE_PTR, which is treated as a success, leading to a dereference of ZERO_SIZE_PTR and a kernel oops. Fix: add the missing error check after module_extend_max_pages() and return immediately on failure. This matches the pattern used by every other kvrealloc() caller in the module loading path. Fixes: b1ae6dc41eaa ("module: add in-kernel support for decompressing") Cc: Dmitry Torokhov Cc: Luis Chamberlain Cc: stable@vger.kernel.org Signed-off-by: Andrii Kuchmenko Reviewed-by: Christophe Leroy (CS GROUP) [Sami: Corrected the analysis in the commit message.] Signed-off-by: Sami Tolvanen Signed-off-by: Greg Kroah-Hartman --- kernel/module/decompress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c index 474e68f0f0634..ce9d90808d952 100644 --- a/kernel/module/decompress.c +++ b/kernel/module/decompress.c @@ -307,6 +307,8 @@ int module_decompress(struct load_info *info, const void *buf, size_t size) */ n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2; error = module_extend_max_pages(info, n_pages); + if (error) + return error; data_size = MODULE_DECOMPRESS_FN(info, buf, size); if (data_size < 0) { From 33c0b96d7e1672be1de0053786637ea46fb81507 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 14:29:06 -0500 Subject: [PATCH 0507/3297] exfat: bound uniname advance in exfat_find_dir_entry() commit 3a1230e7b043c62737b05a3e9275ca83a43ad20a upstream. In exfat_find_dir_entry(), each TYPE_EXTEND (file name) entry advances the output pointer by a fixed amount while the loop guard only tracks the accumulated name length: if (++order == 2) uniname = p_uniname->name; else uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); name_len += len; unichar = *(uniname+len); *(uniname+len) = 0x0; uniname grows by EXFAT_FILE_NAME_LEN (15) per name entry, but name_len grows only by the actual extracted length, which is shorter when a name fragment contains an early NUL. The only guard is `name_len >= MAX_NAME_LENGTH`, so a crafted directory with many short name fragments lets uniname run far past the p_uniname->name[MAX_NAME_LENGTH + 3] buffer while name_len stays small, causing an out-of-bounds read and write at *(uniname+len). The sibling extractor exfat_get_uniname_from_ext_entry() already stops on a short fragment (the lockstep `len != EXFAT_FILE_NAME_LEN` guard added in commit d42334578eba ("exfat: check if filename entries exceeds max filename length")); exfat_find_dir_entry() never got the equivalent. Track the per-entry write offset as a count and reject a fragment once the offset, or the offset plus the extracted length, would exceed MAX_NAME_LENGTH, before forming the output pointer. Fixes: ca06197382bd ("exfat: add directory operations") Cc: stable@vger.kernel.org Suggested-by: Namjae Jeon Signed-off-by: Bryam Vargas Signed-off-by: Namjae Jeon Signed-off-by: Greg Kroah-Hartman --- fs/exfat/dir.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 97f2ff87220fe..e1b28c1a7e857 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -1083,6 +1083,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; + unsigned int offset; if (step != DIRENT_STEP_NAME || name_len >= MAX_NAME_LENGTH) { @@ -1091,13 +1092,15 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, continue; } - if (++order == 2) - uniname = p_uniname->name; - else - uniname += EXFAT_FILE_NAME_LEN; - + offset = (++order - 2) * EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); brelse(bh); + if (offset > MAX_NAME_LENGTH || + len > MAX_NAME_LENGTH - offset) { + step = DIRENT_STEP_FILE; + continue; + } + uniname = p_uniname->name + offset; name_len += len; unichar = *(uniname+len); From 1c8889e0db01fe5393e71c69cffddfe3f0f49d24 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 4 Mar 2026 17:30:27 +0900 Subject: [PATCH 0508/3297] NTB: epf: Fix request_irq() unwind in ntb_epf_init_isr() commit fcba26efe5efc7441f5505f4ccc69791214b40be upstream. ntb_epf_init_isr() requests multiple MSI/MSI-X vectors in a loop. If request_irq() fails part-way through, it jumps straight to pci_free_irq_vectors() without freeing already requested IRQs. Fix the error path by freeing any successfully requested IRQs before releasing the vectors. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Cc: stable@vger.kernel.org # v5.12+ Link: https://patch.msgid.link/20260304083028.1391068-2-den@valinux.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/ntb/hw/epf/ntb_hw_epf.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 8aaf8f9bc69c9..15d99d08f84f9 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -357,7 +357,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) 0, "ntb_epf", ndev); if (ret) { dev_err(dev, "Failed to request irq\n"); - goto err_request_irq; + goto err_free_irq; } } @@ -367,16 +367,14 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument | irq); if (ret) { dev_err(dev, "Failed to configure doorbell\n"); - goto err_configure_db; + goto err_free_irq; } return 0; -err_configure_db: - for (i = 0; i < ndev->db_count + 1; i++) +err_free_irq: + while (i--) free_irq(pci_irq_vector(pdev, i), ndev); - -err_request_irq: pci_free_irq_vectors(pdev); return ret; From 90405c8822c5d45d4d546e94914a8e3f19f36768 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 0509/3297] riscv: mm: Define DIRECT_MAP_PHYSMEM_END commit f3336b48cf9d3f2d1fc78e3289c0ded2f00876ee upstream. On RISC-V, the actual mappable range of physical address space is dependent on the current MMU mode i.e. satp_mode (See Documentation/arch/riscv/vm-layout.rst). Define the DIRECT_MAP_PHYSMEM_END macro based on the existing virtual address space layout macros to expose this information to get_free_mem_region(). Otherwise, it returns a region that couldn't be mapped, which breaks ZONE_DEVICE. Cc: stable@vger.kernel.org # v6.13+ Tested-by: Han Gao # SG2044 Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260309-riscv-sparsemem-vmemmap-limits-v1-2-f40efe18e3cd@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/pgtable.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 30d1ea93dde34..9b2e967c9fe08 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -93,6 +93,16 @@ */ #define vmemmap ((struct page *)VMEMMAP_START - vmemmap_start_pfn) +/* Needed to limit get_free_mem_region() */ +#if defined(CONFIG_FLATMEM) +#define DIRECT_MAP_PHYSMEM_END (phys_ram_base + KERN_VIRT_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM_VMEMMAP) +#define DIRECT_MAP_PHYSMEM_END \ + ((vmemmap_start_pfn + VMEMMAP_SIZE / sizeof(struct page)) * PAGE_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM) +/* DIRECT_MAP_PHYSMEM_END is not limited by VA space assignment in this case */ +#endif + #define PCI_IO_SIZE SZ_16M #define PCI_IO_END VMEMMAP_START #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) From 4d730cab96e6b75e4a07c4baf37294ebc07f795e Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:49 +0800 Subject: [PATCH 0510/3297] riscv: mm: Unconditionally sfence.vma for spurious fault commit 1b2c6b56a9fa0dcbef461039937de22b1cbecc7d upstream. Svvptc does not guarantee that it's safe to just return here. Since we have already cleared our bit, if, theoretically, the bounded timeframe for the accessed page to become valid still hasn't happened after sret, we could fault again and actually crash. Hopefully, these spurious faults should be rare enough that this is an acceptable slowdown. Cc: stable@vger.kernel.org Fixes: 503638e0babf ("riscv: Stop emitting preventive sfence.vma for new vmalloc mappings") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-5-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/entry.S | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index e7bd03a9ced53..d9095b5718d12 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -75,8 +75,11 @@ /* Atomically reset the current cpu bit in new_vmalloc */ amoxor.d a0, a1, (a0) - /* Only emit a sfence.vma if the uarch caches invalid entries */ - ALTERNATIVE("sfence.vma", "nop", 0, RISCV_ISA_EXT_SVVPTC, 1) + /* + * A sfence.vma is required here. Even if we had Svvptc, there's no + * guarantee that after returning we wouldn't just fault again. + */ + sfence.vma REG_L a0, TASK_TI_A0(tp) REG_L a1, TASK_TI_A1(tp) From 279c2fa731122652ce9aace16dba1b575dbf5bc5 Mon Sep 17 00:00:00 2001 From: Anthony Yznaga Date: Wed, 15 Apr 2026 20:39:37 -0700 Subject: [PATCH 0511/3297] mm: fix mmap errno value when MAP_DROPPABLE is not supported commit d86c9e971af2315119a78c564a802fafcebf1b6b upstream. Patch series "fix MAP_DROPPABLE not supported errno", v4. Mark Brown reported seeing a regression in -next on 32 bit arm with the mlock selftests. Before exiting and marking the tests failed, the following message was logged after an attempt to create a MAP_DROPPABLE mapping: Bail out! mmap error: Unknown error 524 It turns out error 524 is ENOTSUPP which is an error that userspace is not supposed to see, but it indicates in this instance that MAP_DROPPABLE is not supported. The first patch changes the errno returned to EOPNOTSUPP. The second patch is a second version of a prior patch to introduce selftests to verify locking behavior with droppable mappings with the additional change to skip the tests when MAP_DROPPABLE is not supported. The third patch fixes the MAP_DROPPABLE selftest so that it is run by the framework and skips if MAP_DROPPABLE is not supported. This patch (of 3): On configs where MAP_DROPPABLE is not supported (currently any 32-bit config except for PPC32), mmap fails with errno set to ENOTSUPP. However, ENOTSUPP is not a standard error value that userspace knows about. The acceptable userspace-visible errno to use is EOPNOTSUPP. checkpatch.pl has a warning to this effect. Link: https://lore.kernel.org/20260416033939.49981-1-anthony.yznaga@oracle.com Link: https://lore.kernel.org/20260416033939.49981-2-anthony.yznaga@oracle.com Fixes: 9651fcedf7b9 ("mm: add MAP_DROPPABLE for designating always lazily freeable mappings") Signed-off-by: Anthony Yznaga Acked-by: David Hildenbrand (Arm) Acked-by: Vlastimil Babka (SUSE) Reported-by: Mark Brown Reviewed-by: Pedro Falcato Reviewed-by: Lorenzo Stoakes (Oracle) Cc: Jann Horn Cc: Jason A. Donenfeld Cc: Liam Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/mmap.c b/mm/mmap.c index 5fd3b80fda1d5..b29b31a29ef66 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -502,7 +502,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, break; case MAP_DROPPABLE: if (VM_DROPPABLE == VM_NONE) - return -ENOTSUPP; + return -EOPNOTSUPP; /* * A locked or stack area makes no sense to be droppable. * From 785ebd42b8b50dc13f3b705cd72352a82027ef38 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Thu, 11 Jun 2026 12:01:55 +0200 Subject: [PATCH 0512/3297] selftests: mm: fix and speedup "droppable" test commit cc13a7a618fe8354f16d74c06aaf9565a68e9ebd upstream. The droppable test currently relies on creating memory pressure in a child process to trigger dropping the droppable pages. That not only takes a long time on some machines (allocating and filling all that memory), on large machines this will not work as we hardcode the area size to 134217728 bytes. ... further, we rely on timeouts to detect that memory was not dropped, which is really suboptimal. Instead, let's just use MADV_PAGEOUT on a 2 MiB region. MADV_PAGEOUT works with droppable memory even without swap. There is the low chance of MADV_PAGEOUT failing to drop a page because of speculative references. We'll wait 1s and retry 10 times to rule that unlikely case out as best as we can. On a machine without swap: $ ./droppable TAP version 13 1..1 ok 1 madvise(MADV_PAGEOUT) behavior # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Link: https://lore.kernel.org/20260611-droppable_test-v1-1-b6a73d99f658@kernel.org Fixes: 9651fcedf7b9 ("mm: add MAP_DROPPABLE for designating always lazily freeable mappings") Signed-off-by: David Hildenbrand (Arm) Reported-by: Aishwarya TCV Tested-by: Sarthak Sharma Tested-by: Lance Yang Reviewed-by: Dev Jain Reviewed-by: SeongJae Park Tested-by: Lorenzo Stoakes Reviewed-by: Lorenzo Stoakes Reviewed-by: Jason A. Donenfeld Cc: Anthony Yznaga Cc: Liam R. Howlett Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport 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/droppable.c | 46 +++++++++++++++----------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/mm/droppable.c b/tools/testing/selftests/mm/droppable.c index f3d9ecf96890a..c1598fd394cad 100644 --- a/tools/testing/selftests/mm/droppable.c +++ b/tools/testing/selftests/mm/droppable.c @@ -17,10 +17,10 @@ int main(int argc, char *argv[]) { - size_t alloc_size = 134217728; - size_t page_size = getpagesize(); + const size_t alloc_size = 2 * 1024 * 1024; + int retry_count = 10; + bool dropped; void *alloc; - pid_t child; ksft_print_header(); ksft_set_plan(1); @@ -28,26 +28,32 @@ int main(int argc, char *argv[]) alloc = mmap(0, alloc_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); assert(alloc != MAP_FAILED); memset(alloc, 'A', alloc_size); - for (size_t i = 0; i < alloc_size; i += page_size) - assert(*(uint8_t *)(alloc + i)); - - child = fork(); - assert(child >= 0); - if (!child) { - for (;;) - *(char *)malloc(page_size) = 'B'; - } - for (bool done = false; !done;) { - for (size_t i = 0; i < alloc_size; i += page_size) { - if (!*(uint8_t *)(alloc + i)) { - done = true; - break; + while (retry_count--) { + if (madvise(alloc, alloc_size, MADV_PAGEOUT)) { + if (errno == EINVAL) { + ksft_test_result_skip("madvise(MADV_PAGEOUT) not supported\n"); + exit(KSFT_SKIP); } + ksft_test_result_fail("madvise(MADV_PAGEOUT) error: %s\n", strerror(errno)); + exit(KSFT_FAIL); } + + dropped = memchr(alloc, 'A', alloc_size) == NULL; + + /* + * Speculative reference can temporarily prevent some + * pages from getting dropped. So sleep and retry. + * + * If a page is not droppable for 10s, something + * is seriously messed up and we want to fail. + */ + if (dropped) + break; + sleep(1); } - kill(child, SIGTERM); - ksft_test_result_pass("MAP_DROPPABLE: PASS\n"); - exit(KSFT_PASS); + ksft_test_result(dropped, "madvise(MADV_PAGEOUT) behavior\n"); + + ksft_finished(); } From 8dcaa0f87a88d720d13106f3a306c6b61d189d86 Mon Sep 17 00:00:00 2001 From: Ketan Date: Tue, 23 Jun 2026 02:48:04 +0530 Subject: [PATCH 0513/3297] mm: page_ext: add count limit to page_ext_iter_next to prevent invalid PFN access commit ffd017237cfe99e6e5602ab14179b0e6878a0840 upstream. The page_ext iteration API does not validate if the PFN still belongs to a valid section while advancing the iterator. When dynamically adding memory in the hotplug path, it can lead to a NULL pointer dereference during page_ext_lookup at the boundary of the last valid section when iterator count equals __pgcount. The for_each_page_ext() macro calls page_ext_iter_next() as its loop increment. for_each_page_ext() does a "__page_ext = page_ext_iter_next(&__iter)" at the end. This causes page_ext_iter_next() to increment iter->index past __pgcount and call page_ext_lookup(start_pfn + __pgcount). During memory hotplug (online), the PFN at start_pfn + __pgcount may belong to a section that has not yet been initialized, causing page_ext_lookup() to trigger a NULL pointer dereference. [ 14.555124][ T846] Call trace: [ 14.555125][ T846] lookup_page_ext+0x6c/0x108 (P) [ 14.555127][ T846] page_ext_lookup+0x30/0x3c [ 14.555129][ T846] __reset_page_owner+0x11c/0x260 [ 14.571201][ T846] __free_pages_ok+0x5e8/0x8e0 [ 14.571204][ T846] __free_pages_core+0x78/0xf0 [ 14.571206][ T846] generic_online_page+0x14/0x24 [ 14.597782][ T846] online_pages+0x178/0x30c [ 14.597784][ T846] memory_block_change_state+0x284/0x32c [ 14.597787][ T846] memory_subsys_online+0x4c/0x64 [ 14.597789][ T846] device_online+0x88/0xb0 [ 14.597791][ T846] online_memory_block+0x30/0x40 [ 14.597793][ T846] walk_memory_blocks+0xac/0xe8 [ 14.597794][ T846] add_memory_resource+0x280/0x298 [ 14.656161][ T846] add_memory+0x60/0x98 Move the iteration boundary enforcement inside the iterator functions, so callers cannot inadvertently access beyond the requested range. Link: https://lore.kernel.org/20260623-page_ext-v3-1-a89799a5367c@oss.qualcomm.com Fixes: 9039b9096ea2 ("mm: page_ext: add an iteration API for page extensions") Signed-off-by: Ketan Kishore Suggested-by: David Hildenbrand Suggested-by: Matthew Wilcox Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Luiz Capitulino Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/page_ext.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/include/linux/page_ext.h b/include/linux/page_ext.h index 76c817162d2fb..af2b6216c8e09 100644 --- a/include/linux/page_ext.h +++ b/include/linux/page_ext.h @@ -119,14 +119,18 @@ struct page_ext_iter { * page_ext_iter_begin() - Prepare for iterating through page extensions. * @iter: page extension iterator. * @pfn: PFN of the page we're interested in. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no page_ext exists for this page. */ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, - unsigned long pfn) + unsigned long pfn, unsigned long count) { + if (!count) + return NULL; + iter->index = 0; iter->start_pfn = pfn; iter->page_ext = page_ext_lookup(pfn); @@ -137,19 +141,22 @@ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, /** * page_ext_iter_next() - Get next page extension * @iter: page extension iterator. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no next page_ext exists. */ -static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter) +static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter, + unsigned long count) { unsigned long pfn; if (WARN_ON_ONCE(!iter->page_ext)) return NULL; - iter->index++; + if (++iter->index >= count) + return NULL; pfn = iter->start_pfn + iter->index; if (page_ext_iter_next_fast_possible(pfn)) @@ -182,9 +189,9 @@ static inline struct page_ext *page_ext_iter_get(const struct page_ext_iter *ite * IMPORTANT: must be called with RCU read lock taken. */ #define for_each_page_ext(__page, __pgcount, __page_ext, __iter) \ - for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page));\ - __page_ext && __iter.index < __pgcount; \ - __page_ext = page_ext_iter_next(&__iter)) + for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page), __pgcount); \ + __page_ext; \ + __page_ext = page_ext_iter_next(&__iter, __pgcount)) #else /* !CONFIG_PAGE_EXTENSION */ struct page_ext; From 5c942ad7df75925ee166e7f0fb36892d8dde376b Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Thu, 25 Jun 2026 16:38:53 +0100 Subject: [PATCH 0514/3297] mm: do file ownership checks with the proper mount idmap commit e187bc02f8fa4226d62814592cf064ee4557c470 upstream. Ever since idmapped mounts were introduced, inode ownership checks (for side-channel protection) in mincore() and madvise(MADV_PAGEOUT) were done against the nop_mnt_idmap, which completely ignores the file's mount's idmap. This results in odd edgecases like: 1) mount/bind-mount with an idmap userA:userB:1 2) userB runs an owner_or_capable() check on file that is owned by userA on-disk/in-memory, but owned by userB after idmap translation 3) owner_or_capable() mysteriously fails as the correct idmap wasn't supplied In the case of mincore/madvise MADV_PAGEOUT, this is usually benign, because file_permission(file, MAY_WRITE) will probably succeed, as it uses the proper idmap internally, but it does not need to be the case on e.g a 0444 file where even the owner itself doesn't have permissions to write to it. Since this is clearly not trivial to get right, introduce a file_owner_or_capable() that can carry the correct semantics, and switch the various users in mm to it. The issue was found by manual code inspection & an off-list discussion with Jan Kara. Link: https://lore.kernel.org/20260625153853.913949-1-pfalcato@suse.de Fixes: 9caccd41541a ("fs: introduce MOUNT_ATTR_IDMAP") Signed-off-by: Pedro Falcato Reviewed-by: Jan Kara Reviewed-by: Christian Brauner (Amutable) Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Jann Horn Cc: Liam R. Howlett Cc: Matthew Wilcox (Oracle) Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/fs.h | 5 +++++ mm/filemap.c | 2 +- mm/madvise.c | 3 +-- mm/mincore.c | 3 +-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index f3e798184a58e..39269c7d167e6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2852,6 +2852,11 @@ static inline struct mnt_idmap *file_mnt_idmap(const struct file *file) return mnt_idmap(file->f_path.mnt); } +static inline bool file_owner_or_capable(const struct file *file) +{ + return inode_owner_or_capable(file_mnt_idmap(file), file_inode(file)); +} + /** * is_idmapped_mnt - check whether a mount is mapped * @mnt: the mount to check diff --git a/mm/filemap.c b/mm/filemap.c index e3339cf37a1f0..7c26e3735f1bb 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -4620,7 +4620,7 @@ static inline bool can_do_cachestat(struct file *f) { if (f->f_mode & FMODE_WRITE) return true; - if (inode_owner_or_capable(file_mnt_idmap(f), file_inode(f))) + if (file_owner_or_capable(f)) return true; return file_permission(f, MAY_WRITE) == 0; } diff --git a/mm/madvise.c b/mm/madvise.c index 5dbe40be7c650..588a809dac11d 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -338,8 +338,7 @@ static inline bool can_do_file_pageout(struct vm_area_struct *vma) * otherwise we'd be including shared non-exclusive mappings, which * opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } diff --git a/mm/mincore.c b/mm/mincore.c index 4a3d7aa911469..2cf2667bca7e5 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -214,8 +214,7 @@ static inline bool can_do_mincore(struct vm_area_struct *vma) * for writing; otherwise we'd be including shared non-exclusive * mappings, which opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } From 6bbe2000d9f9ffb828764eb6356e8c82feec6e1f Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 28 Jun 2026 18:11:18 +0800 Subject: [PATCH 0515/3297] selftests/mm: pagemap_ioctl: use the correct page size for transact_test() commit dccf636bf1e68c3fda92f0c9e1018ab7e0ac8b2c upstream. There are several places in transact_test() where we use the hardcoded 0x1000 (4k) as page size, which is not always correct for architectures supporting multiple page sizes. Switch to use the correct page size. Otherwise ./ksft_pagemap.sh on a 16k-page-size arm64 box fails with $ ./ksft_pagemap.sh [...] # ok 96 mprotect_tests Both pages written after remap and mprotect # ok 97 mprotect_tests Clear and make the pages written # Bail out! ioctl failed # # Planned tests != run tests (117 != 97) # # Totals: pass:97 fail:0 xfail:0 xpass:0 skip:0 error:0 # [FAIL] not ok 1 pagemap_ioctl # exit=1 # SUMMARY: PASS=0 SKIP=0 FAIL=1 1..1 Link: https://lore.kernel.org/20260628101118.35861-1-zenghui.yu@linux.dev Fixes: 46fd75d4a3c9 ("selftests: mm: add pagemap ioctl tests") Signed-off-by: Zenghui Yu Cc: Muhammad Usama Anjum Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zenghui Yu Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/mm/pagemap_ioctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index 4fc8e578ec7c3..8f42b9e5891bb 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -1367,7 +1367,7 @@ void *thread_proc(void *mem) ksft_exit_fail_msg("pthread_barrier_wait\n"); for (i = 0; i < access_per_thread; ++i) - __atomic_add_fetch(m + i * (0x1000 / sizeof(*m)), 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(m + i * (page_size / sizeof(*m)), 1, __ATOMIC_SEQ_CST); ret = pthread_barrier_wait(&end_barrier); if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) @@ -1402,15 +1402,15 @@ static void transact_test(int page_size) if (pthread_barrier_init(&end_barrier, NULL, nthreads + 1)) ksft_exit_fail_msg("pthread_barrier_init\n"); - mem = mmap(NULL, 0x1000 * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, + mem = mmap(NULL, page_size * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (mem == MAP_FAILED) ksft_exit_fail_msg("Error mmap %s.\n", strerror(errno)); - wp_init(mem, 0x1000 * nthreads * pages_per_thread); - wp_addr_range(mem, 0x1000 * nthreads * pages_per_thread); + wp_init(mem, page_size * nthreads * pages_per_thread); + wp_addr_range(mem, page_size * nthreads * pages_per_thread); - memset(mem, 0, 0x1000 * nthreads * pages_per_thread); + memset(mem, 0, page_size * nthreads * pages_per_thread); count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size); ksft_test_result(count > 0, "%s count %u\n", __func__, count); @@ -1419,7 +1419,7 @@ static void transact_test(int page_size) finish = 0; for (i = 0; i < nthreads; ++i) - pthread_create(&th, NULL, thread_proc, mem + 0x1000 * i * pages_per_thread); + pthread_create(&th, NULL, thread_proc, mem + page_size * i * pages_per_thread); extra_pages = 0; for (i = 0; i < iter_count; ++i) { From 037ec8353711c79353b12d5634e0c9ff363a9efa Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 29 May 2026 16:32:08 -0700 Subject: [PATCH 0516/3297] crypto: loongson - Remove broken and unused loongson-rng commit af3d1bb9a09daf928fc3f173689fb7904d6a6d4f upstream. The loongson-rng rng_alg has several vulnerabilities, including not providing forward security, and a use-after-free bug due to the use of wait_for_completion_interruptible(). Meanwhile, the rng_alg framework doesn't really have any purpose in the first place other than to access the software algorithms crypto/drbg.c and crypto/jitterentropy.c. Hardware-specific rng_algs have no in-kernel user, and unlike hwrng there's no feed into the actual Linux RNG. As such, there's really no point to this code. There are of course other rng_alg drivers that are similarly unused, but they're similarly in the process of being phased out, e.g. https://lore.kernel.org/r/20260529193648.18172-1-ebiggers@kernel.org and https://lore.kernel.org/r/20260529220430.34135-1-ebiggers@kernel.org Given that, there's no point in fixing forward these vulnerabilities, and it makes much more sense to simply roll back the addition of this driver. If this platform provides TRNG (not PRNG) functionality, it could make sense to add a hwrng driver, but it would be quite different. Link: https://lore.kernel.org/linux-crypto/20260525145939.GC2018@quark/ Fixes: 766b2d724c8d ("crypto: loongson - add Loongson RNG driver support") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 - drivers/crypto/Kconfig | 1 - drivers/crypto/Makefile | 1 - drivers/crypto/loongson/Kconfig | 6 - drivers/crypto/loongson/Makefile | 1 - drivers/crypto/loongson/loongson-rng.c | 209 ------------------------- 6 files changed, 219 deletions(-) delete mode 100644 drivers/crypto/loongson/Kconfig delete mode 100644 drivers/crypto/loongson/Makefile delete mode 100644 drivers/crypto/loongson/loongson-rng.c diff --git a/MAINTAINERS b/MAINTAINERS index 554e881b05bea..cbfe2a7dd993f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14617,7 +14617,6 @@ M: Qunqin Zhao L: linux-crypto@vger.kernel.org S: Maintained F: drivers/char/tpm/tpm_loongson.c -F: drivers/crypto/loongson/ F: drivers/mfd/loongson-se.c F: include/linux/mfd/loongson-se.h diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index a6688d54984c4..e121d6929cf19 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -852,7 +852,6 @@ config CRYPTO_DEV_CCREE If unsure say Y. source "drivers/crypto/hisilicon/Kconfig" -source "drivers/crypto/loongson/Kconfig" source "drivers/crypto/amlogic/Kconfig" diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 322ae8854e3ea..170e10b18f9b8 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -44,7 +44,6 @@ obj-y += inside-secure/ obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/ obj-y += xilinx/ obj-y += hisilicon/ -obj-y += loongson/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += intel/ obj-y += starfive/ diff --git a/drivers/crypto/loongson/Kconfig b/drivers/crypto/loongson/Kconfig deleted file mode 100644 index f4e1544ffbb49..0000000000000 --- a/drivers/crypto/loongson/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config CRYPTO_DEV_LOONGSON_RNG - tristate "Support for Loongson RNG Driver" - depends on MFD_LOONGSON_SE - select CRYPTO_RNG - help - Support for Loongson RNG Driver. diff --git a/drivers/crypto/loongson/Makefile b/drivers/crypto/loongson/Makefile deleted file mode 100644 index 1ce5ec32b5534..0000000000000 --- a/drivers/crypto/loongson/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_CRYPTO_DEV_LOONGSON_RNG) += loongson-rng.o diff --git a/drivers/crypto/loongson/loongson-rng.c b/drivers/crypto/loongson/loongson-rng.c deleted file mode 100644 index 3a4940260f9e5..0000000000000 --- a/drivers/crypto/loongson/loongson-rng.c +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2019 HiSilicon Limited. */ -/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SE_SEED_SIZE 32 - -struct loongson_rng_list { - struct mutex lock; - struct list_head list; - int registered; -}; - -struct loongson_rng { - u32 used; - struct loongson_se_engine *engine; - struct list_head list; - struct mutex lock; -}; - -struct loongson_rng_ctx { - struct loongson_rng *rng; -}; - -struct loongson_rng_cmd { - u32 cmd_id; - union { - u32 len; - u32 ret; - } u; - u32 seed_off; - u32 out_off; - u32 pad[4]; -}; - -static struct loongson_rng_list rng_devices = { - .lock = __MUTEX_INITIALIZER(rng_devices.lock), - .list = LIST_HEAD_INIT(rng_devices.list), -}; - -static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dstn, unsigned int dlen) -{ - struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct loongson_rng *rng = ctx->rng; - struct loongson_rng_cmd *cmd = rng->engine->command; - int err, len; - - mutex_lock(&rng->lock); - cmd->seed_off = 0; - do { - len = min(dlen, rng->engine->buffer_size); - cmd = rng->engine->command; - cmd->u.len = len; - err = loongson_se_send_engine_cmd(rng->engine); - if (err) - break; - - cmd = rng->engine->command_ret; - if (cmd->u.ret) { - err = -EIO; - break; - } - - memcpy(dstn, rng->engine->data_buffer, len); - dlen -= len; - dstn += len; - } while (dlen > 0); - mutex_unlock(&rng->lock); - - return err; -} - -static int loongson_rng_init(struct crypto_tfm *tfm) -{ - struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); - struct loongson_rng *rng; - u32 min_used = U32_MAX; - - mutex_lock(&rng_devices.lock); - list_for_each_entry(rng, &rng_devices.list, list) { - if (rng->used < min_used) { - ctx->rng = rng; - min_used = rng->used; - } - } - ctx->rng->used++; - mutex_unlock(&rng_devices.lock); - - return 0; -} - -static void loongson_rng_exit(struct crypto_tfm *tfm) -{ - struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); - - mutex_lock(&rng_devices.lock); - ctx->rng->used--; - mutex_unlock(&rng_devices.lock); -} - -static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct loongson_rng *rng = ctx->rng; - struct loongson_rng_cmd *cmd; - int err; - - if (slen < SE_SEED_SIZE) - return -EINVAL; - - slen = min(slen, rng->engine->buffer_size); - - mutex_lock(&rng->lock); - cmd = rng->engine->command; - cmd->u.len = slen; - cmd->seed_off = rng->engine->buffer_off; - memcpy(rng->engine->data_buffer, seed, slen); - err = loongson_se_send_engine_cmd(rng->engine); - if (err) - goto out; - - cmd = rng->engine->command_ret; - if (cmd->u.ret) - err = -EIO; -out: - mutex_unlock(&rng->lock); - - return err; -} - -static struct rng_alg loongson_rng_alg = { - .generate = loongson_rng_generate, - .seed = loongson_rng_seed, - .seedsize = SE_SEED_SIZE, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "loongson_stdrng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct loongson_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = loongson_rng_init, - .cra_exit = loongson_rng_exit, - }, -}; - -static int loongson_rng_probe(struct platform_device *pdev) -{ - struct loongson_rng_cmd *cmd; - struct loongson_rng *rng; - int ret = 0; - - rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); - if (!rng) - return -ENOMEM; - - rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG); - if (!rng->engine) - return -ENODEV; - cmd = rng->engine->command; - cmd->cmd_id = SE_CMD_RNG; - cmd->out_off = rng->engine->buffer_off; - mutex_init(&rng->lock); - - mutex_lock(&rng_devices.lock); - - if (!rng_devices.registered) { - ret = crypto_register_rng(&loongson_rng_alg); - if (ret) { - dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); - goto out; - } - rng_devices.registered = 1; - } - - list_add_tail(&rng->list, &rng_devices.list); -out: - mutex_unlock(&rng_devices.lock); - - return ret; -} - -static struct platform_driver loongson_rng_driver = { - .probe = loongson_rng_probe, - .driver = { - .name = "loongson-rng", - }, -}; -module_platform_driver(loongson_rng_driver); - -MODULE_ALIAS("platform:loongson-rng"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Yinggang Gu "); -MODULE_AUTHOR("Qunqin Zhao "); -MODULE_DESCRIPTION("Loongson Random Number Generator driver"); From bb354384f40bb087e7c44f0f0743a23fc945f91d Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Thu, 4 Jun 2026 14:03:06 +0800 Subject: [PATCH 0517/3297] iommu/vt-d: Avoid WARNING in sva unbind path commit 534b5f98ab7319d8004bbc7dab6481462243e883 upstream. The Intel IOMMU driver allows SVA on devices even if they do not support PCI/PRI. Commit 39c20c4e83b9 ("iommu/vt-d: Only handle IOPF for SVA when PRI is supported") modified the SVA bind path to allow this configuration by skipping IOPF enablement when PRI is missing. However, it failed to update the unbind path. This creates an imbalance: the unbind path attempts to disable IOPF for a device that never had it enabled, triggering a WARNING in intel_iommu_disable_iopf(): WARNING: drivers/iommu/intel/iommu.c:3475 at intel_iommu_disable_iopf+0x4f/0x90d Call Trace: blocking_domain_set_dev_pasid+0x50/0x70 iommu_detach_device_pasid+0x89/0xc0 iommu_sva_unbind_device+0x73/0x150 xe_vm_close_and_put+0x4d2/0x1200 [xe] Fix this by bypassing IOPF operations for SVA domains on non-PRI hardware in both the bind and unbind paths. Fixes: 39c20c4e83b9 ("iommu/vt-d: Only handle IOPF for SVA when PRI is supported") Cc: stable@vger.kernel.org Reported-by: Nareshkumar Gollakoti Signed-off-by: Lu Baolu Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260519052917.3729796-1-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/intel/iommu.h | 11 +++++++++++ drivers/iommu/intel/svm.c | 12 ++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h index 9198ac7f6bbab..6b11081e690fe 100644 --- a/drivers/iommu/intel/iommu.h +++ b/drivers/iommu/intel/iommu.h @@ -1334,18 +1334,29 @@ void intel_iommu_disable_iopf(struct device *dev); static inline int iopf_for_domain_set(struct iommu_domain *domain, struct device *dev) { + struct device_domain_info *info = dev_iommu_priv_get(dev); + if (!domain || !domain->iopf_handler) return 0; + /* SVA with non-IOMMU/PRI IOPF handling is allowed. */ + if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported) + return 0; + return intel_iommu_enable_iopf(dev); } static inline void iopf_for_domain_remove(struct iommu_domain *domain, struct device *dev) { + struct device_domain_info *info = dev_iommu_priv_get(dev); + if (!domain || !domain->iopf_handler) return; + if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported) + return; + intel_iommu_disable_iopf(dev); } diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index 4cbbebc027496..e147f71f91b72 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -164,12 +164,9 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain, if (IS_ERR(dev_pasid)) return PTR_ERR(dev_pasid); - /* SVA with non-IOMMU/PRI IOPF handling is allowed. */ - if (info->pri_supported) { - ret = iopf_for_domain_replace(domain, old, dev); - if (ret) - goto out_remove_dev_pasid; - } + ret = iopf_for_domain_replace(domain, old, dev); + if (ret) + goto out_remove_dev_pasid; /* Setup the pasid table: */ sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0; @@ -183,8 +180,7 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain, return 0; out_unwind_iopf: - if (info->pri_supported) - iopf_for_domain_replace(old, domain, dev); + iopf_for_domain_replace(old, domain, dev); out_remove_dev_pasid: domain_remove_dev_pasid(domain, dev, pasid); return ret; From 50612ce318b1c156e920c69e5571897f9bedb49e Mon Sep 17 00:00:00 2001 From: Weinan Liu Date: Thu, 28 May 2026 22:31:47 +0000 Subject: [PATCH 0518/3297] iommu/amd: Don't split flush for amd_iommu_domain_flush_all() commit 69fe699afe1afcb730164b86c228483c2da05f94 upstream. We have observed multiple full invalidations occurring during device detach when we are done using the vfio-device. blocked_domain_attach_device() -> detach_device() -> amd_iommu_domain_flush_all() -> amd_iommu_domain_flush_pages(..., CMD_INV_IOMMU_ALL_PAGES_ADDRESS) while (size != 0) { -> __domain_flush_pages( flush_size /* power of 2 flush_size */) -> domain_flush_pages_v1() -> build_inv_iommu_pages() -> build_inv_address() } build_inv_address() will trigger a full invalidation if the chunk size > (1 << 51). Consequently, the guest will issue multiple full invalidations for a single call to amd_iommu_domain_flush_all() Without this patch, we will see 10 time instead of 1 time full invalidations for every amd_iommu_domain_flush_all(). Cc: stable@vger.kernel.org Fixes: a270be1b3fdf ("iommu/amd: Use only natural aligned flushes in a VM") Suggested-by: Josef Bacik Suggested-by: Jason Gunthorpe Signed-off-by: Weinan Liu Reviewed-by: Wei Wang Reviewed-by: Jason Gunthorpe Reviewed-by: Samiullah Khawaja Reviewed-by: Suravee Suthikulpanit Reviewed-by: Vasant Hegde Signed-off-by: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/amd/iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index dfbd09e2e0ca5..5fef68797fdab 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -1689,7 +1689,8 @@ void amd_iommu_domain_flush_pages(struct protection_domain *domain, { lockdep_assert_held(&domain->lock); - if (likely(!amd_iommu_np_cache)) { + if (likely(!amd_iommu_np_cache) || + size >= (1ULL<<52)) { __domain_flush_pages(domain, address, size); /* Wait until IOMMU TLB and all device IOTLB flushes are complete */ From 04a177f91160ee18da98f5689482cf0f589ec869 Mon Sep 17 00:00:00 2001 From: Kai Aizen Date: Thu, 30 Apr 2026 20:56:30 +0300 Subject: [PATCH 0519/3297] iommufd: Use sizeof(*hdr) instead of sizeof(hdr) in veventq read commit be93d186ae88a92e7aa77e122d4e661fa57b1e39 upstream. The bound-check in iommufd_veventq_fops_read() for the normal vEVENT path uses sizeof(hdr) where the surrounding code uses sizeof(*hdr): if (!vevent_for_lost_events_header(cur) && sizeof(hdr) + cur->data_len > count - done) { hdr is declared as struct iommufd_vevent_header *, so sizeof(hdr) evaluates to the size of the pointer. Surrounding code uses sizeof(*hdr) consistently: if (done >= count || sizeof(*hdr) > count - done) { ... if (copy_to_user(buf + done, hdr, sizeof(*hdr))) { ... done += sizeof(*hdr); struct iommufd_vevent_header is currently 8 bytes (two __u32 fields, flags and sequence), so on 64-bit (sizeof(void *) == 8) the two expressions happen to be equal and the check works as intended. On 32-bit (sizeof(void *) == 4) the check under-counts the header by 4 bytes: a vEVENT whose data_len causes 8 + cur->data_len to exceed count - done while 4 + cur->data_len does not will pass the check, then the loop will copy_to_user 8 bytes of header followed by data_len bytes of payload, writing past the user-supplied buffer. It is also a latent bug for any future expansion of struct iommufd_vevent_header beyond sizeof(void *) on 64-bit; the check should not depend on the type happening to match the host pointer width. Use sizeof(*hdr) to match the rest of the function and the actual amount that will be copied. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/20260430175630.67078-1-kai.aizen.dev@gmail.com Cc: stable@vger.kernel.org Reported-by: Kai Aizen Signed-off-by: Kai Aizen Reviewed-by: Kevin Tian Reviewed-by: Nicolin Chen Reviewed-by: Lu Baolu Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 8e45752f500e7..d66d5e27bc74e 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -321,7 +321,7 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, /* If being a normal vEVENT, validate against the full size */ if (!vevent_for_lost_events_header(cur) && - sizeof(hdr) + cur->data_len > count - done) { + sizeof(*hdr) + cur->data_len > count - done) { iommufd_veventq_deliver_restore(veventq, cur); break; } From 5c5f1b5184f7d9786bd7d9646bdbbcdcee3c05f1 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 21 May 2026 17:36:32 -0700 Subject: [PATCH 0520/3297] iommufd: Fix data_len byte-count vs element-count mismatch commit 85345becfead3255a5f875d4b4d82ea01d926239 upstream. kzalloc_flex() computes the allocation size. With event_data typed as u64, data_len is interpreted as a u64 element count. Yet, every caller and the read path treat data_len as a byte count. The current code over-allocates by sizeof(u64) and the __counted_by() annotation overstates the length by the same factor. Re-type event_data as u8. No functional change in user-visible behavior. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/f7665f839b9dce917d6bd394375a1cf56568d86b.1779408671.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Jason Gunthorpe Signed-off-by: Nicolin Chen Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/iommufd_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index 85d0843ed07b0..53d37e77c4b98 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -599,7 +599,7 @@ struct iommufd_vevent { struct iommufd_vevent_header header; struct list_head node; /* for iommufd_eventq::deliver */ ssize_t data_len; - u64 event_data[] __counted_by(data_len); + u8 event_data[] __counted_by(data_len); }; #define vevent_for_lost_events_header(vevent) \ From f565297edf316016be4a1a9e2eb9f39359313f43 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 21 May 2026 17:36:34 -0700 Subject: [PATCH 0521/3297] iommufd: Set veventq_depth upper bound commit 6ebf2eb46fbd5b40393ff8fbb847ba96925beaff upstream. iommufd_veventq_alloc() accepts any !0 veventq_depth from userspace, with an upper bound at U32_MAX. This leaves a vulnerability where userspace can allocate excessively large queues to exhaust kernel memory reserves. Cap the veventq_depth (maximum number of entries) to 1 << 19, matching the maximum number of entries in the SMMUv3 EVTQ (the largest use case today). Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/8426cbaa5e8294472ec7f076ef427cc473be5985.1779408671.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Jason Gunthorpe Signed-off-by: Nicolin Chen Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index d66d5e27bc74e..3cd7d6d15bbdb 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -473,6 +473,9 @@ int iommufd_fault_iopf_handler(struct iopf_group *group) static const struct file_operations iommufd_veventq_fops = INIT_EVENTQ_FOPS(iommufd_veventq_fops_read, NULL); +/* An arbitrary upper bound for veventq_depth that fits all existing HWs */ +#define VEVENTQ_MAX_DEPTH (1U << 19) + int iommufd_veventq_alloc(struct iommufd_ucmd *ucmd) { struct iommu_veventq_alloc *cmd = ucmd->cmd; @@ -484,7 +487,7 @@ int iommufd_veventq_alloc(struct iommufd_ucmd *ucmd) if (cmd->flags || cmd->__reserved || cmd->type == IOMMU_VEVENTQ_TYPE_DEFAULT) return -EOPNOTSUPP; - if (!cmd->veventq_depth) + if (!cmd->veventq_depth || cmd->veventq_depth > VEVENTQ_MAX_DEPTH) return -EINVAL; viommu = iommufd_get_viommu(ucmd, cmd->viommu_id); From 64011399d881966926b1e79e69d54d2c81010754 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:32 -0700 Subject: [PATCH 0522/3297] iommufd: Rewind header length in done if iommufd_veventq_fops_read() fails commit 01e41ad76c12ae5c49ab4ef4fc7dd54e9b8784d6 upstream. When the first event copy fails, rc = -EFAULT will not be reported as done is set to the length of the copied header. Rewind it to report rc correctly. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/78f8caeb6a5d667a26b870e3068cec47dd4b5be1.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 3cd7d6d15bbdb..0584fdac6b7ac 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -336,6 +336,7 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, if (cur->data_len && copy_to_user(buf + done, cur->event_data, cur->data_len)) { iommufd_veventq_deliver_restore(veventq, cur); + done -= sizeof(*hdr); rc = -EFAULT; break; } From f549a749b6255e6932d58c26a023adacb8049c9b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:33 -0700 Subject: [PATCH 0523/3297] iommufd: Reject invalid read count in iommufd_veventq_fops_read() commit 00203ca8323f9714630408c19a209b52397975e6 upstream. The read count must be large enough to hold a vEVENT header. For a normal vEVENT, it must also hold the trailing data following the header. iommufd_veventq_fops_read() does not validate the count, but returns 0 as if the read had succeeded while leaving the pending event in the queue. Return -EINVAL in both undersize cases. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/e1111adcc8a8882fbfd84accd6674dc846dc5689.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 0584fdac6b7ac..b6123de99dc09 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -310,6 +310,9 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, if (*ppos) return -ESPIPE; + /* Minimum read count is a vEVENT header */ + if (count < sizeof(*hdr)) + return -EINVAL; while ((cur = iommufd_veventq_deliver_fetch(veventq))) { /* Validate the remaining bytes against the header size */ @@ -323,6 +326,9 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, if (!vevent_for_lost_events_header(cur) && sizeof(*hdr) + cur->data_len > count - done) { iommufd_veventq_deliver_restore(veventq, cur); + /* Read count doesn't fit a single normal vEVENT */ + if (done == 0) + rc = -EINVAL; break; } From f2dbe1dba01e6639731042b23d2e74cb773f8760 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:35 -0700 Subject: [PATCH 0524/3297] iommufd: Reject invalid read count in iommufd_fault_fops_read() commit 47916a54eeb2a9e654512ee609f71bd5b29db702 upstream. The read count must be large enough to hold one fault or a group's faults. iommufd_fault_fops_read() does not validate the count, but returns 0 as if the read had succeeded while leaving the pending fault in the queue. Return -EINVAL in the undersize cases. Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") Link: https://patch.msgid.link/r/85c118a606fbedc5c132a1f5ec223a5ba23b92d2.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index b6123de99dc09..82796236bcf4c 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -142,6 +142,9 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, if (done >= count || group->fault_count * fault_size > count - done) { iommufd_fault_deliver_restore(fault, group); + /* Read count doesn't fit the first fault group */ + if (done == 0) + rc = -EINVAL; break; } From 5539da127d03c1f6c2e2a49fdfbe331a0ccbdea8 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:36 -0700 Subject: [PATCH 0525/3297] iommufd: Break the loop on failure in iommufd_fault_fops_read() commit 172fc8b19825a0f5884c38f2289188284e2d45ee upstream. On a copy_to_user() failure inside the inner list_for_each_entry, only the inner loop breaks; the outer while re-fetches the just-restored fault group and retries the failing copy_to_user() forever, spinning the reader at 100% CPU with fault->mutex held. Check rc after the inner loop and break the outer while as well. Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") Link: https://patch.msgid.link/r/336a9b6e44fe66a24199d3be777c405c85c98622.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 82796236bcf4c..38e04d8064bc3 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -168,6 +168,8 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, } done += fault_size; } + if (rc) + break; } mutex_unlock(&fault->mutex); From 67daea4c09351467d08c408e6cd23f62501df262 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Jun 2026 13:42:37 -0700 Subject: [PATCH 0526/3297] iommufd: Avoid partial fault group delivery in iommufd_fault_fops_read() commit 091ab6d70dc444f56ed14faedbcacfc979f4c613 upstream. The cookie returned by xa_alloc() in iommufd_fault_fops_read() is per fault group, but the inner copy_to_user() runs per fault inside the group. If a copy fails mid-group, xa_erase clears the cookie and the group is restored to the deliver list, yet done is not rolled back. The function returns the partial byte count, with the successfully copied faults sitting at offsets below done carrying the now-erased cookie. The next read() then re-fetches the group, allocates a fresh cookie, and re-delivers every fault including the ones already copied; userspace sees duplicates carrying the new cookie, and a stale cookie that can never be responded to. Use a local group_done variable that tracks the per-group progress inside the inner loop, and only commit done = group_done after the inner loop has finished successfully. On a copy_to_user failure the outer break skips the commit, so done remains at its prior start-of-group baseline; the partial bytes already written past done are undefined to userspace per the read(2) contract, and the next read re-delivers the whole group atomically. Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") Link: https://patch.msgid.link/r/360cab4d4aeccb0bae275a970e2b3c340a71e0e0.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 38e04d8064bc3..617e20b67bf2a 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -139,6 +139,8 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, mutex_lock(&fault->mutex); while ((group = iommufd_fault_deliver_fetch(fault))) { + size_t group_done = done; + if (done >= count || group->fault_count * fault_size > count - done) { iommufd_fault_deliver_restore(fault, group); @@ -160,16 +162,17 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf, iommufd_compose_fault_message(&iopf->fault, &data, idev, group->cookie); - if (copy_to_user(buf + done, &data, fault_size)) { + if (copy_to_user(buf + group_done, &data, fault_size)) { xa_erase(&fault->response, group->cookie); iommufd_fault_deliver_restore(fault, group); rc = -EFAULT; break; } - done += fault_size; + group_done += fault_size; } if (rc) break; + done = group_done; } mutex_unlock(&fault->mutex); From 32ca4aed2a66205b072fcfecabe220289a8149ff Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 3 Jun 2026 14:26:53 -0700 Subject: [PATCH 0527/3297] iommufd: Set upper bounds on cache invalidation entry_num and entry_len commit 4d70986002f2f3eaaed89124fb2522bded38b016 upstream. iommufd_hwpt_invalidate() takes a user-controlled entry_num and entry_len, each bounded only by U32_MAX. An entry_len beyond the kernel's struct size makes the copy helper verify the extra bytes are zero, scanning that excess in one uninterruptible pass; a multi-gigabyte value over zeroed user memory trips the soft-lockup watchdog. A large entry_num is the other half, driving the backend invalidation loop with no reschedule. The VT-d nested handler, for one, copies each entry and flushes caches per iteration, pinning the CPU on a non-preemptible kernel. Cap both in the ioctl. entry_len is held under PAGE_SIZE, above any request struct, and entry_num under 1 << 19, the order of a hardware invalidation queue and well beyond any real batch, bounding the per-call loop length. Fixes: 8c6eabae3807 ("iommufd: Add IOMMU_HWPT_INVALIDATE") Link: https://patch.msgid.link/r/447fa93663f7526eb361719e83fa8b649464483d.1780521606.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Nicolin Chen Reviewed-by: Lu Baolu Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/hw_pagetable.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c index fe789c2dc0c97..623cc608ca0cd 100644 --- a/drivers/iommu/iommufd/hw_pagetable.c +++ b/drivers/iommu/iommufd/hw_pagetable.c @@ -489,6 +489,9 @@ int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd) return rc; } +/* An arbitrary entry_num cap, far above any realistic invalidation batch */ +#define IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX (1U << 19) + int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) { struct iommu_hwpt_invalidate *cmd = ucmd->cmd; @@ -507,7 +510,13 @@ int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) goto out; } - if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) { + /* + * Bound entry_num and entry_len so a single call cannot pin the CPU; + * entry_len also caps the copy_struct_from_user() trailing-zero scan. + */ + if (cmd->entry_num && + (!cmd->data_uptr || !cmd->entry_len || cmd->entry_len > PAGE_SIZE || + cmd->entry_num > IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX)) { rc = -EINVAL; goto out; } From e4427c19554b56bb38408fba3c3e10e11e3d7f92 Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Wed, 13 May 2026 18:47:59 -0300 Subject: [PATCH 0528/3297] audit: fix removal of dangling executable rules commit 888a0396e154524f4027f27da84bdbec9eb68916 upstream. When an audited executable is deleted from the disk, its dentry becomes negative. Any later attempt to delete the associated audit rule will lead to audit_alloc_mark() encountering this negative dentry and immediately aborting, returning -ENOENT. This early abort prevents the subsystem from allocating the temporary fsnotify mark needed to construct the search key, meaning the kernel cannot find the existing rule in its own lists to delete it. This leaves a dangling rule in memory, resulting in the following error while attempting to delete the rule: # ./audit-dupe-exe-deadlock.sh No rules Error deleting rule (No such file or directory) There was an error while processing parameters # auditctl -l -a always,exit -S all -F exe=/tmp/file -F path=/tmp/file -F key=dr # auditctl -D Error deleting rule (No such file or directory) There was an error while processing parameters This patch fixes this issue by removing the d_really_is_negative() check. By doing so, a dummy mark can be successfully generated for the deleted path, which allows the audit subsystem to properly match and flush the dangling rule. Cc: stable@kernel.org Fixes: 76a53de6f7ff ("VFS/audit: introduce kern_path_parent() for audit") Acked-by: Waiman Long Acked-by: Richard Guy Briggs Signed-off-by: Ricardo Robaina Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/audit_fsnotify.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kernel/audit_fsnotify.c b/kernel/audit_fsnotify.c index b92805b317a2d..0046f93ae9144 100644 --- a/kernel/audit_fsnotify.c +++ b/kernel/audit_fsnotify.c @@ -84,10 +84,6 @@ struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pa dentry = kern_path_parent(pathname, &path); if (IS_ERR(dentry)) return ERR_CAST(dentry); /* returning an error */ - if (d_really_is_negative(dentry)) { - audit_mark = ERR_PTR(-ENOENT); - goto out; - } audit_mark = kzalloc(sizeof(*audit_mark), GFP_KERNEL); if (unlikely(!audit_mark)) { From 859fef2c3d40aa3c6297bba9f15bb95716550ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Fri, 12 Jun 2026 19:27:55 +0200 Subject: [PATCH 0529/3297] landlock: Set audit_net.sk for socket access checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d936e1a9170f9cadaa5f37586b1dfe6f20f98799 upstream. Set audit_net.sk in current_check_access_socket() to provide the socket object to audit_log_lsm_data(). This makes Landlock consistent with AppArmor, which always sets .sk for socket operations, and with SELinux's generic socket permission checks. The socket's local and foreign address information (laddr, lport, faddr, fport) is logged by the shared lsm_audit.c infrastructure when the socket has bound or connected state. Fields with zero values are suppressed by print_ipv4_addr()/print_ipv6_addr(), so the audit output is unchanged for the common case of bind denials on unbound sockets. For connect denials after a prior bind, the bound local address (laddr, lport) appears before the existing sockaddr fields (daddr, dest). No existing fields are removed or reordered, and the new field names (laddr, lport, faddr, fport) are standard audit fields already emitted by other LSMs through the same lsm_audit.c code path. Add a connect_tcp_bound audit test that binds to an allowed port and then connects to a denied one, verifying that the denial record reports laddr/lport from the bound socket in addition to the connect destination. Cc: Günther Noack Cc: Tingmao Wang Cc: stable@vger.kernel.org Fixes: 9f74411a40ce ("landlock: Log TCP bind and connect denials") Link: https://patch.msgid.link/20260612172757.1003481-1-mic@digikod.net Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- security/landlock/net.c | 1 + tools/testing/selftests/landlock/net_test.c | 62 +++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/security/landlock/net.c b/security/landlock/net.c index e6367e30e5b0e..f309ee2def32a 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -199,6 +199,7 @@ static int current_check_access_socket(struct socket *const sock, return 0; audit_net.family = address->sa_family; + audit_net.sk = sock->sk; landlock_log_denial(subject, &(struct landlock_request){ .type = LANDLOCK_REQUEST_NET_ACCESS, diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c index 957a06fde5924..6fecd36f74c63 100644 --- a/tools/testing/selftests/landlock/net_test.c +++ b/tools/testing/selftests/landlock/net_test.c @@ -2014,4 +2014,66 @@ TEST_F(audit, connect) EXPECT_EQ(0, close(sock_fd)); } +static int matches_log_tcp_bound(int audit_fd, const char *const addr, + __u16 lport, __u16 dport) +{ + static const char log_template[] = REGEX_LANDLOCK_PREFIX + " blockers=net\\.connect_tcp laddr=%s lport=%u daddr=%s dest=%u$"; + /* Slack for two addresses and two port numbers. */ + char log_match[sizeof(log_template) + 40]; + int log_match_len; + + log_match_len = snprintf(log_match, sizeof(log_match), log_template, + addr, lport, addr, dport); + if (log_match_len > sizeof(log_match)) + return -E2BIG; + + return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, + NULL); +} + +/* + * After a bind() to an allowed port, a denied connect must report laddr/lport + * from the bound socket (made available through audit_net.sk) in addition to + * the connect sockaddr's daddr/dest. + */ +TEST_F(audit, connect_tcp_bound) +{ + const struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + const struct landlock_net_port_attr rule_bind = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, + .port = self->srv0.port, + }; + struct service_fixture srv_remote; + struct audit_records records; + int ruleset_fd, sock_fd; + + /* Uses a second port as the denied connect target. */ + ASSERT_EQ(0, set_service(&srv_remote, variant->prot, 1)); + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, + &rule_bind, 0)); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + sock_fd = socket_variant(&self->srv0); + ASSERT_LE(0, sock_fd); + EXPECT_EQ(0, bind_variant(sock_fd, &self->srv0)); + EXPECT_EQ(-EACCES, connect_variant(sock_fd, &srv_remote)); + EXPECT_EQ(0, matches_log_tcp_bound(self->audit_fd, variant->addr, + self->srv0.port, srv_remote.port)); + + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); + EXPECT_EQ(1, records.domain); + + EXPECT_EQ(0, close(sock_fd)); +} + TEST_HARNESS_MAIN From b51a7439c166aab2061ae6a7dff6f679fe6b11a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 13 May 2026 12:51:08 +0200 Subject: [PATCH 0530/3297] selftests/landlock: Filter dealloc records in audit_count_records() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 26679fad81a471428707d2dd7b0418204c52b7e4 upstream. audit_count_records() counts both AUDIT_LANDLOCK_DOMAIN allocation and deallocation records in records.domain . Domain deallocation is tied to asynchronous credential freeing via kworker threads (landlock_put_ruleset_deferred), so the dealloc record can arrive after the drain in audit_init() and after the preceding audit_match_record() call. This causes flaky failures in tests that assert an exact records.domain count: a stale dealloc record from a previous test's domain inflates the count by one. Observed on x86_64 under build configurations that delay the kworker firing the dealloc callback (e.g. coverage instrumentation): the audit_layout1 tests in fs_test.c intermittently saw records.domain == 2 where 1 was expected. The fix is in the shared helper, so those existing checks become robust without needing a fs_test.c edit. Filter audit_count_records() with a regex to skip records containing deallocation status. The remaining domain records (allocation, emitted synchronously during landlock_log_denial()) are deterministic. Deallocation records are already tested explicitly via matches_log_domain_deallocated() in audit_test.c, which uses its own domain-ID-based filtering and longer timeout. With this filter in place, re-add the records.domain == 0 checks that were removed in commit 3647a4977fb7 ("selftests/landlock: Drain stale audit records on init") as a workaround for this race. Cc: Günther Noack Cc: stable@vger.kernel.org Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()") Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") Tested-by: Günther Noack Link: https://patch.msgid.link/20260513105112.140137-1-mic@digikod.net Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/landlock/audit.h | 39 ++++++++++++------- tools/testing/selftests/landlock/audit_test.c | 2 + .../testing/selftests/landlock/ptrace_test.c | 1 + .../landlock/scoped_abstract_unix_test.c | 1 + 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h index c0c7e8a66edc1..d6d6f5116dc96 100644 --- a/tools/testing/selftests/landlock/audit.h +++ b/tools/testing/selftests/landlock/audit.h @@ -381,18 +381,24 @@ struct audit_records { }; /* - * WARNING: Do not assert records.domain == 0 without a preceding - * audit_match_record() call. Domain deallocation records are emitted - * asynchronously from kworker threads and can arrive after the drain in - * audit_init(), corrupting the domain count. A preceding audit_match_record() - * call consumes stale records while scanning, making the assertion safe in - * practice because stale deallocation records arrive before the expected access - * records. + * Counts remaining audit records by type, skipping domain deallocation records. + * Deallocation records are emitted asynchronously from kworker threads after a + * previous test's child has exited, so they can arrive after the drain in + * audit_init() and after the preceding audit_match_record() call. Allocation + * records are emitted synchronously during landlock_log_denial() in the current + * test's syscall context, so only those are counted in records->domain. */ static int audit_count_records(int audit_fd, struct audit_records *records) { + static const char dealloc_pattern[] = REGEX_LANDLOCK_PREFIX + " status=deallocated "; struct audit_message msg; - int err; + regex_t dealloc_re; + int ret, err = 0; + + ret = regcomp(&dealloc_re, dealloc_pattern, 0); + if (ret) + return -ENOMEM; records->access = 0; records->domain = 0; @@ -402,9 +408,8 @@ static int audit_count_records(int audit_fd, struct audit_records *records) err = audit_recv(audit_fd, &msg); if (err) { if (err == -EAGAIN) - return 0; - else - return err; + err = 0; + break; } switch (msg.header.nlmsg_type) { @@ -412,12 +417,20 @@ static int audit_count_records(int audit_fd, struct audit_records *records) records->access++; break; case AUDIT_LANDLOCK_DOMAIN: - records->domain++; + ret = regexec(&dealloc_re, msg.data, 0, NULL, 0); + if (ret == REG_NOMATCH) { + records->domain++; + } else if (ret != 0) { + err = -EIO; + goto out; + } break; } } while (true); - return 0; +out: + regfree(&dealloc_re); + return err; } static int audit_init(void) diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c index 08d4c70e015bb..67ab175ef5e3f 100644 --- a/tools/testing/selftests/landlock/audit_test.c +++ b/tools/testing/selftests/landlock/audit_test.c @@ -497,6 +497,7 @@ TEST_F(audit_flags, signal) } else { EXPECT_EQ(1, records.access); } + EXPECT_EQ(0, records.domain); /* Updates filter rules to match the drop record. */ set_cap(_metadata, CAP_AUDIT_CONTROL); @@ -684,6 +685,7 @@ TEST_F(audit_exec, signal_and_open) /* Tests that there was no denial until now. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); /* * Wait for the child to do a first denied action by layer1 and diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c index 84ccdb8f594af..4e356334ecb74 100644 --- a/tools/testing/selftests/landlock/ptrace_test.c +++ b/tools/testing/selftests/landlock/ptrace_test.c @@ -486,6 +486,7 @@ TEST_F(audit, trace) /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); yama_ptrace_scope = get_yama_ptrace_scope(); ASSERT_LE(0, yama_ptrace_scope); diff --git a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c index 5a1afc640c770..6825082c079c0 100644 --- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c +++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c @@ -312,6 +312,7 @@ TEST_F(scoped_audit, connect_to_child) /* Makes sure there is no superfluous logged records. */ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); EXPECT_EQ(0, records.access); + EXPECT_EQ(0, records.domain); ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); From 7c73a269a880b1399baacfb9d521415e6ef7ecc2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 7 Jun 2026 18:57:45 +0100 Subject: [PATCH 0531/3297] KVM: arm64: nv: Avoid dereferencing NULL VNCR pseudo-TLB commit 4be6cbeb93d26994bd1827ddbce391e3c4395c8f upstream. VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and either can race against a vcpu not being onlined yet (no pseudo-TLB allocated). Similarly, the TLB might be invalid, and the invalidation should be skipped in this case. Both kvm_invalidate_vncr_ipa() and kvm_invalidate_vncr_va() are expected to perform the same checks, except that the latter doesn't check for the allocation and blindly dereferences the pointer. Solve this by introducing a new iterator built on top of the usual kvm_for_each_vcpu() that checks for both of the above conditions, and convert the two users to it. Reported-by: Hyunwoo Kim Link: https://lore.kernel.org/r/aiUvSbrWndQeUPc8@v4bel Fixes: 4ffa72ad8f37 ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2") Cc: stable@vger.kernel.org Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260607175745.297793-1-maz@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index df956559fd064..4aa86582d8790 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -824,9 +824,21 @@ static void invalidate_vncr(struct vncr_tlb *vt) clear_fixmap(vncr_fixmap(vt->cpu)); } +/* + * VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and + * either can race against a vcpu not being onlined yet (no pseudo-TLB + * allocated). Similarly, the TLB might be invalid. Skip those, as they + * obviously don't participate in the invalidation at this stage. + */ +#define kvm_for_each_vncr_tlb(idx, vcpup, tlbp, kvm) \ + kvm_for_each_vcpu(idx, vcpup, kvm) \ + if (((tlbp) = vcpup->arch.vncr_tlb) && \ + (tlbp)->valid) + static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) { struct kvm_vcpu *vcpu; + struct vncr_tlb *vt; unsigned long i; lockdep_assert_held_write(&kvm->mmu_lock); @@ -834,24 +846,9 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY)) return; - kvm_for_each_vcpu(i, vcpu, kvm) { - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { u64 ipa_start, ipa_end, ipa_size; - /* - * Careful here: We end-up here from an MMU notifier, - * and this can race against a vcpu not being onlined - * yet, without the pseudo-TLB being allocated. - * - * Skip those, as they obviously don't participate in - * the invalidation at this stage. - */ - if (!vt) - continue; - - if (!vt->valid) - continue; - ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level)); ipa_start = vt->wr.pa & ~(ipa_size - 1); @@ -881,17 +878,14 @@ static void invalidate_vncr_va(struct kvm *kvm, struct s1e2_tlbi_scope *scope) { struct kvm_vcpu *vcpu; + struct vncr_tlb *vt; unsigned long i; lockdep_assert_held_write(&kvm->mmu_lock); - kvm_for_each_vcpu(i, vcpu, kvm) { - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { u64 va_start, va_end, va_size; - if (!vt->valid) - continue; - va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level)); va_start = vt->gva & ~(va_size - 1); From 2d710d4fcd2cd24f25df61f6fc68b02c034d079f Mon Sep 17 00:00:00 2001 From: Zeng Chi Date: Thu, 11 Jun 2026 20:46:44 +0800 Subject: [PATCH 0532/3297] LoongArch: KVM: Add missing slots_lock for device register/unregister commit aeded601d6aceb57cdda4b2701d2ee00c43a8b69 upstream. kvm_io_bus_register_dev() and kvm_io_bus_unregister_dev() should be called under kvm->slots_lock. The unregister calls in ipi.c, eiointc.c and pch_pic.c were also missing this protection. Add it to match the register side. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Zeng Chi Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/intc/eiointc.c | 6 ++++++ arch/loongarch/kvm/intc/ipi.c | 2 ++ arch/loongarch/kvm/intc/pch_pic.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c index 78510fce1a47b..1380186fd5aed 100644 --- a/arch/loongarch/kvm/intc/eiointc.c +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -654,10 +654,14 @@ static int kvm_eiointc_create(struct kvm_device *dev, u32 type) device = &s->device_vext; kvm_iodevice_init(device, &kvm_eiointc_virt_ops); + mutex_lock(&kvm->slots_lock); ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device); + mutex_unlock(&kvm->slots_lock); if (ret < 0) { + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); return ret; } @@ -676,8 +680,10 @@ static void kvm_eiointc_destroy(struct kvm_device *dev) kvm = dev->kvm; eiointc = kvm->arch.eiointc; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext); + mutex_unlock(&kvm->slots_lock); kfree(eiointc); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index 1058c13dba7f4..0e2840f75673f 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -457,7 +457,9 @@ static void kvm_ipi_destroy(struct kvm_device *dev) kvm = dev->kvm; ipi = kvm->arch.ipi; + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &ipi->device); + mutex_unlock(&kvm->slots_lock); kfree(ipi); kfree(dev); } diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index 4addb34bf432b..c0adabfd50793 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -473,7 +473,9 @@ static void kvm_pch_pic_destroy(struct kvm_device *dev) kvm = dev->kvm; s = kvm->arch.pch_pic; /* unregister pch pic device and free it's memory */ + mutex_lock(&kvm->slots_lock); kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device); + mutex_unlock(&kvm->slots_lock); kfree(s); kfree(dev); } From 6bea2f8becdb20d34378493c3b77a9b9cf8c6cfa Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 7 Jun 2026 02:56:10 +0900 Subject: [PATCH 0533/3297] KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU commit e8042f6e1d7befb2fb6b10a75918642bcd0acf9a upstream. flush_hyp_vcpu() copies the host vCPU context into the hyp's private vCPU on every run. ctxt_to_vcpu() expects a guest context to have a NULL __hyp_running_vcpu, which is only ever set on the host context, so that it resolves the vCPU via container_of(). While this is generally the case, flush_hyp_vcpu() copies the context verbatim and does not enforce this, so a value provided by the host is dereferenced at EL2 (host -> EL2). Fix by clearing __hyp_running_vcpu after the copy. Cc: stable@vger.kernel.org Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") Signed-off-by: Hyunwoo Kim Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260606175614.83273-2-imv4bel@gmail.com Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 29430c031095a..3dda50391446d 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -126,6 +126,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt; + /* __hyp_running_vcpu must be NULL in a guest context. */ + hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL; + hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE); hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & From 4b200e0c9c339cfb9385e548cabcfdf52845717c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:13 -0700 Subject: [PATCH 0534/3297] KVM: x86: Add dedicated API for getting mask of accelerated x2APIC MSRs commit 8ba621f335a519b47cb7d3e3f4f15b5101b3a56f upstream. Add a dedicated local APIC API, kvm_x2apic_disable_intercept_reg_mask(), to provide the mask of x2APIC registers whose MSRs can and should be passed through to the guest when x2APIC virtualization is enable, and use it in lieu of the open-coded equivalent VMX logic. Providing a common helper will allow sharing the logic with SVM (x2AVIC), and as a bonus eliminates the somewhat confusing code where KVM enables interception for MSR_TYPE_RW, even though only the READ case actually needs to be updated. No functional change intended. Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 21 +++++++++++++++++++-- arch/x86/kvm/lapic.h | 2 +- arch/x86/kvm/vmx/vmx.c | 3 +-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index fe2806348c94a..9448d34af51ae 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1745,7 +1745,7 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev) #define APIC_REGS_MASK(first, count) \ (APIC_REG_MASK(first) * ((1ull << (count)) - 1)) -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) +static u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) { /* Leave bits '0' for reserved and write-only registers. */ u64 valid_reg_mask = @@ -1781,7 +1781,24 @@ u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) return valid_reg_mask; } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_lapic_readable_reg_mask); + +u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu) +{ + if (WARN_ON_ONCE(!lapic_in_kernel(vcpu))) + return 0; + + /* + * TMMCT, a.k.a. the current APIC timer count, reads aren't accelerated + * by hardware (Intel or AMD) as the timer is emulated in software (by + * KVM), i.e. reads from the virtual APIC page would return garbage. + * Intercept RDMSR, as handling the fault-like APIC-access VM-Exit is + * more expensive than handling a RDMSR VM-Exit (the APIC-access exit + * requires slow emulation of the code stream). + */ + return kvm_lapic_readable_reg_mask(vcpu->arch.apic) & + ~APIC_REG_MASK(APIC_TMCCT); +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_x2apic_disable_read_intercept_reg_mask); static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, void *data) diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 959380b4c067f..7bcf6877c0bc5 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -150,7 +150,7 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data); int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len); void kvm_lapic_exit(void); -u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic); +u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu); static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic) { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index d7a2b75b6c915..1a6489e4a8d95 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -4062,7 +4062,7 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) * mode, only the current timer count needs on-demand emulation by KVM. */ if (mode & MSR_BITMAP_MODE_X2APIC_APICV) - msr_bitmap[read_idx] = ~kvm_lapic_readable_reg_mask(vcpu->arch.apic); + msr_bitmap[read_idx] = ~kvm_x2apic_disable_read_intercept_reg_mask(vcpu); else msr_bitmap[read_idx] = ~0ull; msr_bitmap[write_idx] = ~0ull; @@ -4075,7 +4075,6 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) !(mode & MSR_BITMAP_MODE_X2APIC)); if (mode & MSR_BITMAP_MODE_X2APIC_APICV) { - vmx_enable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_RW); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W); if (enable_ipiv) From 7949aa38e1094fc6da05cefcf7932e186086099a Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:14 -0700 Subject: [PATCH 0535/3297] KVM: SVM: Disable x2AVIC RDMSR interception for MSRs KVM actually supports commit 7f4b7092d9a173a4271e28c0ed1fc235994e309b upstream. When toggling x2AVIC on/off, use KVM's curated mask of x2APIC MSRs that can/should be passed through to the guest (or not) when 2AVIC is enabled. Using the effective list provided by the local APIC emulation fixes multiple (classes of) bugs, as the existing hand-coded list of MSRs is wrong on multiple fronts: - ARBPRI isn't supported by KVM, isn't accelerated by AVIC (for read or write), and its #VMEXIT is fault-like, i.e. requires decoding the instruction. Disabling interception is nonsensical and suboptimal. - DFR and ICR2 aren't supported by x2APIC and so don't need their intercepts disabled for performance reasons. While the #GP due to x2APIC being abled has higher priority than the trap-like #VMEXIT, disabling interception of unsupported MSRs is confusing and unnecessary. - RRR is completely unsupported. - AVIC currently fails to pass through the "range of vectors" registers, IRR, ISR, and TMR, as e.g. X2APIC_MSR(APIC_IRR) only affects IRR0, and thus only disables intercept for vectors 31:0 (which are the *least* interesting registers). - TMCCT (the current APIC timer count) isn't accelerated by hardware, and generates a fault-like AVIC_UNACCELERATED_ACCESS #VMEXIT, i.e. requires KVM to decode the instruction to figure out what the guest was trying to access. Note, the only reason this isn't a fatal bug is that the AVIC architecture had the foresight to guard against buggy hypervisors. E.g. if hardware simply read from the virtual APIC page, the guest would get garbage (because the timer is emulated in software). Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-3-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/avic.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index d7531e7aff4d6..d9261d780d2e7 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -111,6 +111,9 @@ static bool x2avic_enabled; static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, bool intercept) { + struct kvm_vcpu *vcpu = &svm->vcpu; + u64 rd_regs; + static const u32 x2avic_passthrough_msrs[] = { X2APIC_MSR(APIC_ID), X2APIC_MSR(APIC_LVR), @@ -151,9 +154,15 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, if (!x2avic_enabled) return; + rd_regs = kvm_x2apic_disable_read_intercept_reg_mask(vcpu); + + for_each_set_bit(i, (unsigned long *)&rd_regs, BITS_PER_TYPE(rd_regs)) + svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, + MSR_TYPE_R, intercept); + for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) - svm_set_intercept_for_msr(&svm->vcpu, x2avic_passthrough_msrs[i], - MSR_TYPE_RW, intercept); + svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], + MSR_TYPE_W, intercept); svm->x2avic_msrs_intercepted = intercept; } From 35f3ea7e49a373ed982ff6fa62f96cd090c3bfa1 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 14 May 2026 14:31:15 -0700 Subject: [PATCH 0536/3297] KVM: SVM: Only disable x2AVIC WRMSR interception for MSRs that are accelerated commit 8c63179d975f2029c948ecce622f72af616dbff7 upstream. When x2AVIC is enabled, disable WRMSR interception only for MSRs that are actually accelerated by hardware. Disabling interception for MSRs that aren't accelerated is functionally "fine", and in some cases a weird "win" for performance, but only for cases that should never be triggered by a well-behaved VM (writes to read-only registers; the #GP will typically occur in the guest without taking a #VMEXIT, even for fault-like exits). But overall, disabling interception for MSRs that aren't accelerated is at best confusing and unintuitive, and at worst introduces avoidable risk, as the APM's documentation is imperfect and contradictory. The table in "15.29.3.1 Virtual APIC Register Accesses" of simply states that such writes generate exits, where as "Section 15.29.10 x2AVIC" says: x2APIC MSR intercept checks and access checks have higher priority than AVIC access permission checks. CPU behavior follows the latter (which makes perfect sense), but all in all there's simply no reason to disable interception just to make a #GP faster. Note, the set of MSRs that are passed through for write is identical to VMX's set when IPI virtualization is enabled. This is not a coincidence, and is another motiviating factor for cleaning up the intercepts, as x2AVIC is functionally equivalent to APICv+IPIv. Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: stable@vger.kernel.org Reviewed-by: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260514213115.1637082-4-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/avic.c | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index d9261d780d2e7..a700f3795c176 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -113,39 +113,6 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, { struct kvm_vcpu *vcpu = &svm->vcpu; u64 rd_regs; - - static const u32 x2avic_passthrough_msrs[] = { - X2APIC_MSR(APIC_ID), - X2APIC_MSR(APIC_LVR), - X2APIC_MSR(APIC_TASKPRI), - X2APIC_MSR(APIC_ARBPRI), - X2APIC_MSR(APIC_PROCPRI), - X2APIC_MSR(APIC_EOI), - X2APIC_MSR(APIC_RRR), - X2APIC_MSR(APIC_LDR), - X2APIC_MSR(APIC_DFR), - X2APIC_MSR(APIC_SPIV), - X2APIC_MSR(APIC_ISR), - X2APIC_MSR(APIC_TMR), - X2APIC_MSR(APIC_IRR), - X2APIC_MSR(APIC_ESR), - X2APIC_MSR(APIC_ICR), - X2APIC_MSR(APIC_ICR2), - - /* - * Note! Always intercept LVTT, as TSC-deadline timer mode - * isn't virtualized by hardware, and the CPU will generate a - * #GP instead of a #VMEXIT. - */ - X2APIC_MSR(APIC_LVTTHMR), - X2APIC_MSR(APIC_LVTPC), - X2APIC_MSR(APIC_LVT0), - X2APIC_MSR(APIC_LVT1), - X2APIC_MSR(APIC_LVTERR), - X2APIC_MSR(APIC_TMICT), - X2APIC_MSR(APIC_TMCCT), - X2APIC_MSR(APIC_TDCR), - }; int i; if (intercept == svm->x2avic_msrs_intercepted) @@ -160,9 +127,10 @@ static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, MSR_TYPE_R, intercept); - for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) - svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], - MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W, intercept); + svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_ICR), MSR_TYPE_W, intercept); svm->x2avic_msrs_intercepted = intercept; } From eeb456eb355650963c3b8b1581bfd0c413742c23 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 15 May 2026 15:26:29 -0700 Subject: [PATCH 0537/3297] KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs commit c5bad4fa2d5dfd8c25140051a9807eba387a19b8 upstream. Move KVM's stuffing of GUEST_PENDING_DBG_EXCEPTIONS.BS when RFLAGS.TF=1 and MOV/POP SS or STI blocking is active into the exception injection code so that KVM fixes up the VMCS for all injected #DBs, not only those that are reflected back into the guest after #DB interception. E.g. if KVM queues a #DB in the emulator, or more importantly if userspace does save/restore exactly on the #DB+shadow boundary, then KVM needs to massage the VMCS to avoid the VM-Entry consistency check. Opportunistically update the wording of the comment to describe the behavior as a workaround of flawed CPU behavior/architecture, to make it clear that the *only* thing KVM is doing is fudging around a consistency check. Per the SDM: There are no pending debug exceptions after VM entry if any of the following are true: * The VM entry is vectoring with one of the following interruption types: external interrupt, non-maskable interrupt (NMI), hardware exception, or privileged software exception. I.e. forcing GUEST_PENDING_DBG_EXCEPTIONS.BS does *not* impact guest- visible behavior. Fixes: b9bed78e2fa9 ("KVM: VMX: Set vmcs.PENDING_DBG.BS on #DB in STI/MOVSS blocking shadow") Cc: stable@vger.kernel.org Reported-by: Hou Wenlong Closes: https://lore.kernel.org/all/b1a294bc9ed4dae532474a5dc6c8cb6e5962de7c.1757416809.git.houwenlong.hwl@antgroup.com Reviewed-by: Hou Wenlong Link: https://patch.msgid.link/20260515222638.1949982-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/vmx.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 1a6489e4a8d95..2a9d165d5c33f 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -1790,6 +1790,24 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) u32 intr_info = ex->vector | INTR_INFO_VALID_MASK; struct vcpu_vmx *vmx = to_vmx(vcpu); + /* + * When injecting a #DB, single-stepping is enabled in RFLAGS, and STI + * or MOV-SS blocking is active, set vmcs.PENDING_DBG_EXCEPTIONS.BS to + * prevent a false positive from VM-Entry consistency check. VM-Entry + * asserts that a single-step #DB _must_ be pending in this scenario, + * as the previous instruction cannot have toggled RFLAGS.TF 0=>1 + * (because STI and POP/MOV don't modify RFLAGS), therefore the one + * instruction delay when activating single-step breakpoints must have + * already expired. However, the CPU isn't smart enough to peek at + * vmcs.VM_ENTRY_INTR_INFO_FIELD and so doesn't realize that yes, there + * is indeed a #DB pending/imminent. + */ + if (ex->vector == DB_VECTOR && + (vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && + vmx_get_interrupt_shadow(vcpu)) + vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, + vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); + kvm_deliver_exception_payload(vcpu, ex); if (ex->has_error_code) { @@ -5286,26 +5304,9 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) * avoid single-step #DB and MTF updates, as ICEBP is * higher priority. Note, skipping ICEBP still clears * STI and MOVSS blocking. - * - * For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS - * if single-step is enabled in RFLAGS and STI or MOVSS - * blocking is active, as the CPU doesn't set the bit - * on VM-Exit due to #DB interception. VM-Entry has a - * consistency check that a single-step #DB is pending - * in this scenario as the previous instruction cannot - * have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV - * don't modify RFLAGS), therefore the one instruction - * delay when activating single-step breakpoints must - * have already expired. Note, the CPU sets/clears BS - * as appropriate for all other VM-Exits types. */ if (is_icebp(intr_info)) WARN_ON(!skip_emulated_instruction(vcpu)); - else if ((vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && - (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & - (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS))) - vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, - vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); kvm_queue_exception_p(vcpu, DB_VECTOR, dr6); return 1; From ab253cf6e11189d1c709494588736b3c194a1de5 Mon Sep 17 00:00:00 2001 From: Xuanqing Shi <1356292400@qq.com> Date: Tue, 26 May 2026 19:26:17 -0700 Subject: [PATCH 0538/3297] KVM: VMX: Handle bad values on proxied writes to LBR MSRs commit ca674df13b195eb6d124ab059799d4e03fa40624 upstream. Use the "safe" WRMSR API when writing LBRs on behalf of the guest (or host userspace), and propagate any errors back to the instigator, as the value being written is untrusted. E.g. if the guest (or host userspace) attempts to set reserved bits in LBR_SELECT, then KVM needs to return an error, and not WARN on the bad value. Continue using the "unsafe" version of RDMSR, as it should be impossible to reach the helper with a completely bogus MSR, i.e. WARNing on RDMSR failure is very desirable, e.g. to make KVM bugs more visible. unchecked MSR access error: WRMSR to 0x1c8 (tried to write 0x0000000000004000) Call Trace: intel_pmu_set_msr+0x4e0/0x7f0 [kvm_intel] kvm_pmu_set_msr+0x17e/0x1c0 [kvm] kvm_set_msr_common+0xc76/0x1440 [kvm] vmx_set_msr+0x5e6/0x1570 [kvm_intel] kvm_emulate_wrmsr+0x54/0x1d0 [kvm] vmx_handle_exit+0x7fc/0x970 [kvm_intel] Fixes: 1b5ac3226a1a ("KVM: vmx/pmu: Pass-through LBR msrs when the guest LBR event is ACTIVE") Cc: stable@vger.kernel.org Signed-off-by: Xuanqing Shi <1356292400@qq.com> [sean: rework changelog, only modify WRMSR path, tag for stable@] Link: https://patch.msgid.link/20260527022617.3973884-1-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/pmu_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index de1d9785c01ff..333d7c573d40a 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -320,13 +320,15 @@ static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu, */ local_irq_disable(); if (lbr_desc->event->state == PERF_EVENT_STATE_ACTIVE) { + int err = 0; + if (read) rdmsrq(index, msr_info->data); else - wrmsrq(index, msr_info->data); + err = wrmsrq_safe(index, msr_info->data); __set_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); local_irq_enable(); - return true; + return !err; } clear_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); local_irq_enable(); From 4ad73ef0e7966ecfe67de0060537b4cb14d9acd4 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 23 Apr 2026 09:26:27 -0700 Subject: [PATCH 0539/3297] KVM: x86: Ensure vendor's exit handler runs before fastpath userspace exits commit 0ffedf43910e44b76c2c1db4e9fbf12b268190c1 upstream. Move the handling of fastpath userspace exits into vendor code to ensure KVM runs vendor specific operations that need to run before userspace gains control of the vCPU. E.g. for VMX (and soon to be for SVM as well), KVM needs to flush the PML buffer prior to exiting to userspace, otherwise any memory written by the final KVM_RUN might never be flagged as dirty. Note, waiting to snapshot CR0 and CR3 until svm_handle_exit() is flawed in general, as that risks consuming stale state in a fastpath handler. That will be addressed in a future change. Fixes: f7f39c50edb9 ("KVM: x86: Exit to userspace if fastpath triggers one on instruction skip") Cc: stable@vger.kernel.org Cc: Nikunj A. Dadhania Reviewed-by: Nikunj A. Dadhania Reviewed-by: Kai Huang Link: https://patch.msgid.link/20260423162628.490962-2-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/vmx/vmx.c | 3 +++ arch/x86/kvm/x86.c | 3 --- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index fc22d106a3141..bb034896bf6df 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3569,6 +3569,9 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) vcpu->arch.cr3 = svm->vmcb->save.cr3; } + if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) + return 0; + if (is_guest_mode(vcpu)) { int vmexit; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 2a9d165d5c33f..f65081330c8a4 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6498,6 +6498,9 @@ static int __vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) if (enable_pml && !is_guest_mode(vcpu)) vmx_flush_pml_buffer(vcpu); + if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) + return 0; + /* * KVM should never reach this point with a pending nested VM-Enter. * More specifically, short-circuiting VM-Entry to emulate L2 due to diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 7be3ed9e2c7dc..f2708419f7870 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -11498,9 +11498,6 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) if (vcpu->arch.apic_attention) kvm_lapic_sync_from_vapic(vcpu); - if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) - return 0; - r = kvm_x86_call(handle_exit)(vcpu, exit_fastpath); return r; From 0c93681aea0a1b8be14730a88abe77c840272e41 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Tue, 2 Jun 2026 16:54:46 -0700 Subject: [PATCH 0540/3297] KVM: arm64: Don't leak PFN when kvm_translate_vncr() races MMU notifier commit 9f76b039a72d7e06374aa96862f0232ed53f7787 upstream. In the case that kvm_translate_vncr() races with an MMU notifier the early return does not release a reference on the faulted in PFN. Add the necessary call to kvm_release_faultin_page() for the unused PFN. Cc: stable@vger.kernel.org Fixes: 069a05e535496 ("KVM: arm64: nv: Handle VNCR_EL2-triggered faults") Reported-by: Sashiko (local):gemini-3.1-pro Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260602235450.103057-2-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 4aa86582d8790..b1f52f82c9ef6 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1238,8 +1238,10 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { - if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) + if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) { + kvm_release_faultin_page(vcpu->kvm, page, true, false); return -EAGAIN; + } vt->gva = va; vt->hpa = pfn << PAGE_SHIFT; From f8e1dc70efe4820c271de49a2ed2c7d5e10f445c Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Sun, 15 Mar 2026 04:27:22 +0500 Subject: [PATCH 0541/3297] udmabuf: fix DMA direction mismatch in release_udmabuf() commit fb7b1a0ab25a6077d26cb3829e31743972d4f31d upstream. begin_cpu_udmabuf() maps the sg_table with the caller-provided direction (e.g., DMA_TO_DEVICE for a write-only sync), and caches it in ubuf->sg for reuse. However, release_udmabuf() always unmaps this sg_table with a hardcoded DMA_BIDIRECTIONAL, regardless of the direction that was originally used for the mapping. With CONFIG_DMA_API_DEBUG=y this produces: DMA-API: misc udmabuf: device driver frees DMA memory with different direction [device address=0x000000044a123000] [size=4096 bytes] [mapped with DMA_TO_DEVICE] [unmapped with DMA_BIDIRECTIONAL] The issue was found during video playback when GStreamer performed a write-only DMA_BUF_IOCTL_SYNC on a udmabuf. It can be reproduced with CONFIG_DMA_API_DEBUG=y by creating a udmabuf from a memfd, performing a write-only sync (DMA_BUF_SYNC_WRITE without DMA_BUF_SYNC_READ), and closing the file descriptor. Fix this by storing the DMA direction used when the sg_table is first created in begin_cpu_udmabuf(), and passing that same direction to put_sg_table() in release_udmabuf(). Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks") Cc: stable@vger.kernel.org Signed-off-by: Mikhail Gavrilov Reviewed-by: Vivek Kasireddy Signed-off-by: Vivek Kasireddy Link: https://patch.msgid.link/20260314232722.15555-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/udmabuf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 40399c26e6be6..7717b8ccf59df 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -40,6 +40,7 @@ struct udmabuf { struct folio **pinned_folios; struct sg_table *sg; + enum dma_data_direction sg_dir; struct miscdevice *device; pgoff_t *offsets; }; @@ -237,7 +238,7 @@ static void release_udmabuf(struct dma_buf *buf) struct device *dev = ubuf->device->this_device; if (ubuf->sg) - put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); + put_sg_table(dev, ubuf->sg, ubuf->sg_dir); deinit_udmabuf(ubuf); kfree(ubuf); @@ -255,6 +256,8 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, if (IS_ERR(ubuf->sg)) { ret = PTR_ERR(ubuf->sg); ubuf->sg = NULL; + } else { + ubuf->sg_dir = direction; } } else { dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction); From 34696563461c9a23177feb6d8aff43f4c0510278 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 31 Mar 2026 11:16:57 +0500 Subject: [PATCH 0542/3297] dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning commit 504e2b4ab97a51d56d966cd36d0997ad30b65b2d upstream. When CONFIG_DMA_API_DEBUG_SG is enabled, importing a udmabuf into a DRM driver (e.g. amdgpu for video playback in GNOME Videos / Showtime) triggers a spurious warning: DMA-API: amdgpu 0000:03:00.0: cacheline tracking EEXIST, \ overlapping mappings aren't supported WARNING: kernel/dma/debug.c:619 at add_dma_entry+0x473/0x5f0 The call chain is: amdgpu_cs_ioctl -> amdgpu_ttm_backend_bind -> dma_buf_map_attachment -> [udmabuf] map_udmabuf -> get_sg_table -> dma_map_sgtable(dev, sg, direction, 0) // attrs=0 -> debug_dma_map_sg -> add_dma_entry -> EEXIST This happens because udmabuf builds a per-page scatter-gather list via sg_set_folio(). When begin_cpu_udmabuf() has already created an sg table mapped for the misc device, and an importer such as amdgpu maps the same pages for its own device via map_udmabuf(), the DMA debug infrastructure sees two active mappings whose physical addresses share cacheline boundaries and warns about the overlap. The DMA_ATTR_SKIP_CPU_SYNC flag suppresses this check in add_dma_entry() because it signals that no CPU cache maintenance is performed at map/unmap time, making the cacheline overlap harmless. All other major dma-buf exporters already pass this flag: - drm_gem_map_dma_buf() passes DMA_ATTR_SKIP_CPU_SYNC - amdgpu_dma_buf_map() passes DMA_ATTR_SKIP_CPU_SYNC The CPU sync at map/unmap time is also redundant for udmabuf: begin_cpu_udmabuf() and end_cpu_udmabuf() already perform explicit cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU access is requested through the dma-buf interface. Pass DMA_ATTR_SKIP_CPU_SYNC to dma_map_sgtable() and dma_unmap_sgtable() in udmabuf to suppress the spurious warning and skip the redundant sync. Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks") Cc: stable@vger.kernel.org Signed-off-by: Mikhail Gavrilov Acked-by: Vivek Kasireddy Signed-off-by: Vivek Kasireddy Link: https://patch.msgid.link/20260331061657.79983-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/udmabuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 7717b8ccf59df..be47d1ff6c508 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -163,7 +163,7 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf, sg_set_folio(sgl, ubuf->folios[i], PAGE_SIZE, ubuf->offsets[i]); - ret = dma_map_sgtable(dev, sg, direction, 0); + ret = dma_map_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC); if (ret < 0) goto err_map; return sg; @@ -178,7 +178,7 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf, static void put_sg_table(struct device *dev, struct sg_table *sg, enum dma_data_direction direction) { - dma_unmap_sgtable(dev, sg, direction, 0); + dma_unmap_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC); sg_free_table(sg); kfree(sg); } From 59070040fd12e0b78d7b4d341d9f9a183237c5ff Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:41 -0600 Subject: [PATCH 0543/3297] fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region() commit fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 upstream. afu_ioctl_dma_map() accepts a 64-bit length from userspace via DFL_FPGA_PORT_DMA_MAP ioctl without an upper bound check. The value is passed to afu_dma_pin_pages() where npages is derived as length >> PAGE_SHIFT and passed to pin_user_pages_fast() which takes int nr_pages, causing implicit truncation if length is very large. Validate map.length at the ioctl entry point before calling afu_dma_map_region(), rejecting values whose page count exceeds INT_MAX. Fixes: fa8dda1edef9 ("fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-3-sebasjosue84@gmail.com Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/dfl-afu-main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 3bf8e7338dbef..097a97eeea66c 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -723,6 +723,9 @@ afu_ioctl_dma_map(struct dfl_feature_dev_data *fdata, void __user *arg) if (map.argsz < minsz || map.flags) return -EINVAL; + if (map.length >> PAGE_SHIFT > (u64)INT_MAX) + return -EINVAL; + ret = afu_dma_map_region(fdata, map.user_addr, map.length, &map.iova); if (ret) return ret; From 9ec02cc9a04e502847a8313ea690001ae2051a27 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:06 +0200 Subject: [PATCH 0544/3297] i2c: core: fix irq domain leak on adapter registration failure commit 8ce19524e4cc2462685f596a6402fbd8fb984ab2 upstream. Make sure to tear down the host notify irq domain on adapter registration failure to avoid leaking it. This issue was flagged by Sashiko when reviewing another adapter registration fix. Fixes: 4d5538f5882a ("i2c: use an IRQ to report Host Notify events, not alert") Cc: stable@vger.kernel.org # 4.10 Cc: Benjamin Tissoires Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index e0453f7b5d972..8638e4afc216b 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1579,7 +1579,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); put_device(&adap->dev); - goto out_list; + goto err_remove_irq_domain; } adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); @@ -1615,6 +1615,8 @@ static int i2c_register_adapter(struct i2c_adapter *adap) init_completion(&adap->dev_released); device_unregister(&adap->dev); wait_for_completion(&adap->dev_released); +err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); From 3351c5e77749a0c8a1e252b95420c143ebcf07ac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:08 +0200 Subject: [PATCH 0545/3297] i2c: core: fix NULL-deref on adapter registration failure commit 2295d2bb101faa663fbc45fadbb3fec45f107441 upstream. If adapter registration ever fails the release callback would trigger a NULL-pointer dereference as the completion struct has not been initialised. Note that before the offending commit this would instead have resulted in a minor memory leak of the adapter name. Fixes: 3f8c4f5e9a57 ("i2c: core: fix reference leak in i2c_register_adapter()") Cc: stable@vger.kernel.org Cc: Joe Hattori Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 8638e4afc216b..62ab4fc793479 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1578,8 +1578,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - put_device(&adap->dev); - goto err_remove_irq_domain; + goto err_put_adap; } adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); @@ -1612,10 +1611,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) out_reg: i2c_deregister_clients(adap); debugfs_remove_recursive(adap->debugfs); + device_del(&adap->dev); +err_put_adap: init_completion(&adap->dev_released); - device_unregister(&adap->dev); + put_device(&adap->dev); wait_for_completion(&adap->dev_released); -err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); out_list: mutex_lock(&core_lock); From 0345994d64761c73e1df23f075c7d6cc2dbc4cfb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:09 +0200 Subject: [PATCH 0546/3297] i2c: core: fix adapter probe deferral loop commit 158efa411c57111d87bf265a3776614f32d70007 upstream. Drivers must not probe defer after having registered devices as that will trigger a probe loop if the devices bind to a driver (cf. commit fbc35b45f9f6 ("Add documentation on meaning of -EPROBE_DEFER")). Move the recovery initialisation, where the GPIO lookup may fail, before registering the adapter to prevent this. Fixes: 75820314de26 ("i2c: core: add generic I2C GPIO recovery") Cc: stable@vger.kernel.org # 5.9 Cc: Codrin Ciubotariu Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 62ab4fc793479..d068f089fe0a1 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1562,6 +1562,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) adap->dev.type = &i2c_adapter_type; device_initialize(&adap->dev); + res = i2c_init_recovery(adap); + if (res == -EPROBE_DEFER) + goto err_put_adap; + /* * This adapter can be used as a parent immediately after device_add(), * setup runtime-pm (especially ignore-children) before hand. @@ -1587,10 +1591,6 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (res) goto out_reg; - res = i2c_init_recovery(adap); - if (res == -EPROBE_DEFER) - goto out_reg; - dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); /* create pre-declared device nodes */ From 71b7da959031f1cb216e6871cfd84ee82cd50c39 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:10 +0200 Subject: [PATCH 0547/3297] i2c: core: fix adapter debugfs creation commit 07d5fb537928aad4369aaff0cbae73ba38a719af upstream. Clients can be registered from bus notifier callbacks so the debugfs directory needs to be created before registering the adapter as clients use that directory as their debugfs parent. Move debugfs creation before adapter registration to avoid having clients create their debugfs directories in the debugfs root (which is also more likely to fail due to name collisions). Note that failure to allocate the adapter name must now be handled explicitly as debugfs_create_dir() cannot handle a NULL name (unlike device_add() which returns an error). Fixes: 73febd775bdb ("i2c: create debugfs entry per adapter") Cc: stable@vger.kernel.org # 6.8 Cc: Wolfram Sang Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index d068f089fe0a1..ea111822b7da5 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1557,7 +1557,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) goto out_list; } - dev_set_name(&adap->dev, "i2c-%d", adap->nr); + res = dev_set_name(&adap->dev, "i2c-%d", adap->nr); + if (res) + goto err_remove_irq_domain; + adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; device_initialize(&adap->dev); @@ -1579,14 +1582,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap) idr_replace(&i2c_adapter_idr, adap, adap->nr); mutex_unlock(&core_lock); + adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); + res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - goto err_put_adap; + goto err_remove_debugfs; } - adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); - res = i2c_setup_smbus_alert(adap); if (res) goto out_reg; @@ -1610,13 +1613,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap) out_reg: i2c_deregister_clients(adap); - debugfs_remove_recursive(adap->debugfs); device_del(&adap->dev); +err_remove_debugfs: + debugfs_remove_recursive(adap->debugfs); err_put_adap: init_completion(&adap->dev_released); put_device(&adap->dev); wait_for_completion(&adap->dev_released); - +err_remove_irq_domain: i2c_host_notify_irq_teardown(adap); out_list: mutex_lock(&core_lock); From b6d2af6fe9c1f5ec0484536753c979cbd40a8ac3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 16:37:13 +0200 Subject: [PATCH 0548/3297] i2c: core: fix adapter deregistration race commit b1a58ed9eab146b36f41a55db8f5d7ce9fdedf3f upstream. Adapters can be looked up by their id using i2c_get_adapter() which takes a reference to the embedded struct device. Remove the adapter from the IDR before tearing it down during deregistration (and on registration failure) to make sure its resources are not accessed after having been freed (e.g. the device name). Fixes: 35fc37f81881 ("i2c: Limit core locking to the necessary sections") Cc: stable@vger.kernel.org # 2.6.31 Cc: Jean Delvare Signed-off-by: Johan Hovold Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core-base.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index ea111822b7da5..e049fa908231e 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1587,7 +1587,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); - goto err_remove_debugfs; + goto err_replace_id; } res = i2c_setup_smbus_alert(adap); @@ -1614,7 +1614,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap) out_reg: i2c_deregister_clients(adap); device_del(&adap->dev); -err_remove_debugfs: +err_replace_id: + mutex_lock(&core_lock); + idr_replace(&i2c_adapter_idr, NULL, adap->nr); + mutex_unlock(&core_lock); debugfs_remove_recursive(adap->debugfs); err_put_adap: init_completion(&adap->dev_released); @@ -1803,6 +1806,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) /* First make sure that this adapter was ever added */ mutex_lock(&core_lock); found = idr_find(&i2c_adapter_idr, adap->nr); + if (found == adap) + idr_replace(&i2c_adapter_idr, NULL, adap->nr); mutex_unlock(&core_lock); if (found != adap) { pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); From 56945871123e2b6de1c5231e2a60da65b36391bf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Jun 2026 16:49:34 +0200 Subject: [PATCH 0549/3297] i2c: mpc: Fix timeout calculations commit 2e9a7f68329be41792c0b123c28e6c53c2fa2249 upstream. At first glance the harmless cleanup of the driver does nothing bad. However, as the operator precedence list states the '*' (multiplication) and '/' division operators have order 5 with left-to-right associativity the *= has order 17 and associativity right-to-left. It wouldn't be a problem to replace foo = foo * HZ / 1000000; with foo *= HZ / 1000000; if HZ constant is in Hertz. The problem is that in the Linux kernel HZ is defined in jiffy units, which is order of magnitude smaller than a million. That's why operator precedence has a crucial role here. Fix the regression by reverting pre-optimized calculations. Fixes: be40a3ae719f ("i2c: mpc: Use of_property_read_u32 instead of of_get_property") Signed-off-by: Andy Shevchenko Cc: # v6.4+ Reviewed-by: Chris Packham Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260618144934.3249950-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-mpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 28c5c5c1fb7a6..a21fa45bd64c4 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -844,7 +844,7 @@ static int fsl_i2c_probe(struct platform_device *op) "fsl,timeout", &mpc_ops.timeout); if (!result) { - mpc_ops.timeout *= HZ / 1000000; + mpc_ops.timeout = mpc_ops.timeout * HZ / 1000000; if (mpc_ops.timeout < 5) mpc_ops.timeout = 5; } else { From b65667ec5e9a90b259f68f1cec559a983098bae1 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 10 Jun 2026 11:05:13 +0800 Subject: [PATCH 0550/3297] i2c: davinci: Unregister cpufreq notifier on probe failure commit e43f32816a1b1fe5a86279411626fe3a9be56d45 upstream. davinci_i2c_probe() registers a cpufreq transition notifier before adding the I2C adapter. If i2c_add_numbered_adapter() fails, the probe error path releases the device resources without unregistering the notifier. Add a dedicated error path to unregister the cpufreq notifier after i2c_add_numbered_adapter() fails. Fixes: 82c0de11b734 ("i2c: davinci: Add cpufreq support") Signed-off-by: Haoxiang Li Cc: # v2.6.36+ Reviewed-by: Bartosz Golaszewski Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260610030513.2651018-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 66c23535656b5..0617f416cb0b5 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -818,12 +818,14 @@ static int davinci_i2c_probe(struct platform_device *pdev) adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); if (r) - goto err_unuse_clocks; + goto err_cpufreq; pm_runtime_put_autosuspend(dev->dev); return 0; +err_cpufreq: + i2c_davinci_cpufreq_deregister(dev); err_unuse_clocks: pm_runtime_dont_use_autosuspend(dev->dev); pm_runtime_put_sync(dev->dev); From 369635fbcf7f3b5cfffde92469d6c8349dbcc810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Rodr=C3=ADguez?= Date: Thu, 11 Jun 2026 12:48:56 +0200 Subject: [PATCH 0551/3297] i2c: stm32f7: truncate clock period instead of rounding it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 111bb7f9f4a90b32e495d70a607c67b137f3074a upstream. stm32f7_i2c_compute_timing() derives the I2C clock source period (i2cclk) with DIV_ROUND_CLOSEST, which may round it up. When the period is overestimated, all timings computed from it (SCLDEL, SDADEL, SCLL, SCLH) come out shorter on the wire than calculated, and the resulting bus rate can exceed the requested speed, violating the I2C specification minimums for tLOW and tHIGH. For example, with a 104.45 MHz clock source (e.g. PCLK1, the reset-default I2C clock source on STM32MP1), i2cclk is rounded from 9.574 ns up to 10 ns. Requesting a 400 kHz fast mode bus with 72/27 ns rise/fall times and no analog/digital filters then produces an actual bus rate of 415.6 kHz with tLOW = 1254 ns, violating both the 400 kHz maximum rate and the 1300 ns tLOW minimum of the specification. Truncate the period instead, so that it can only be underestimated. The error then falls on the safe side: the programmed timings come out slightly longer than computed and the bus runs marginally below the target rate (375.3 kHz in the example above) while meeting the specification. i2cbus is left rounded-to-closest: it is only used as the target of the clk_error comparison and is never multiplied into the programmed timings, so nearest rounding remains accurate there. Fixes: aeb068c57214 ("i2c: i2c-stm32f7: add driver") Signed-off-by: Guillermo Rodríguez Cc: # v4.14+ Acked-by: Alain Volmat Reviewed-by: Pierre-Yves MORDRET Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260611104857.242153-1-guille.rodriguez@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-stm32f7.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 905aa9ab64e12..c57dda9866cf4 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -464,8 +464,13 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, { struct stm32f7_i2c_spec *specs; u32 p_prev = STM32F7_PRESC_MAX; - u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, - setup->clock_src); + /* + * Truncate instead of rounding to closest: if the clock period is + * overestimated, the computed SCL timings will come out shorter on + * the wire, which can push the bus above the target rate and below + * the spec's tLOW/tHIGH minimums. + */ + u32 i2cclk = NSEC_PER_SEC / setup->clock_src; u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->speed_freq); u32 clk_error_prev = i2cbus; From b2523f26979e0b5bd1422772176b2233fcd1f6d0 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 11:14:50 +0800 Subject: [PATCH 0552/3297] i2c: imx-lpi2c: mark I2C adapter when hardware is powered down commit 218cfe364b55b2768221629bd4a69ad190b7fbbc upstream. On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers. During system suspend/resume, there exists a time window between: - suspend_noirq and the system entering suspend - the system starting to resume and resume_noirq In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored. If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang. Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available. Fixes: 1ee867e465c1 ("i2c: imx-lpi2c: add target mode support") Signed-off-by: Carlos Song Cc: # v6.14+ Acked-by: Mukesh Savaliya Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260525031450.3183421-1-carlos.song@oss.nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx-lpi2c.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 3bf138eba04c9..24c70ea371bb9 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -1588,7 +1588,18 @@ static int __maybe_unused lpi2c_runtime_resume(struct device *dev) static int __maybe_unused lpi2c_suspend_noirq(struct device *dev) { - return pm_runtime_force_suspend(dev); + struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&lpi2c_imx->adapter); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + return ret; + } + + return 0; } static int __maybe_unused lpi2c_resume_noirq(struct device *dev) @@ -1608,6 +1619,8 @@ static int __maybe_unused lpi2c_resume_noirq(struct device *dev) if (lpi2c_imx->target) lpi2c_imx_target_init(lpi2c_imx); + i2c_mark_adapter_resumed(&lpi2c_imx->adapter); + return 0; } From bb5133a7d5f3fe5c387770e25f2e00e682ce11ed Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Tue, 12 May 2026 17:35:34 +0800 Subject: [PATCH 0553/3297] i2c: i801: fix hardware state machine corruption in error path commit 10dd1a736d557e310a77117832874729a0175d57 upstream. A severe livelock and subsequent Hung Task panic were observed in the i2c-i801 driver during concurrent Fuzzing. The crash is caused by an unconditional hardware register cleanup in the error handling path of i801_access(). When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus controller is actively used by BIOS/ACPI), the kernel does not actually acquire the hardware ownership. However, the code jumps to the 'out' label and executes: iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); This forcefully clears the INUSE_STS lock and resets the hardware status flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI transactions and totally corrupts the SMBus hardware state machine. Consequently, all subsequent i801_access() calls fail at the pre-check stage, triggering an endless stream of "SMBus is busy, can't use it!" error logs. Over a slow serial console, this printk flood monopolizes the CPU (Console Livelock), starving other processes trying to acquire the mmap_lock down_read semaphore, ultimately triggering the hung task watchdog. Fix this by moving the 'out' label below the hardware register cleanup. If i801_check_pre() fails, we safely bypass the iowrite8() and only release the software locks (pm_runtime and mutex), strictly adhering to the rule of not releasing resources that were never acquired. Fixes: 1f760b87e54c ("i2c: i801: Call i801_check_pre() from i801_access()") Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Cc: # v6.3+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260512093534.348655-1-w15303746062@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-i801.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 506d69b156f7c..217dea2743e8a 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -927,13 +927,13 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, */ if (hwpec) iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv)); -out: /* * Unlock the SMBus device for use by BIOS/ACPI, * and clear status flags if not done already. */ iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); +out: pm_runtime_put_autosuspend(&priv->pci_dev->dev); mutex_unlock(&priv->acpi_lock); return ret; From 11f275f01c46ba8aaf9ce224f00e241d2d655193 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 10 Jun 2026 16:41:16 -0700 Subject: [PATCH 0554/3297] Input: synaptics-rmi4 - unregister function handlers on physical driver registration failure commit 6251f7d3472c0409e30f8d6a24f10d33d12e3f9a upstream. If rmi_register_physical_driver() fails, the current error path unregisters only the RMI bus. The function handlers registered earlier remain registered with the driver core. Add a separate error path to unregister the function handlers before unregistering the bus in this failure case. Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices") Signed-off-by: Haoxiang Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260610064633.2837084-1-haoxiang_li2024@163.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/rmi4/rmi_bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index b85ee9db87b00..1e0532a944fbc 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -455,11 +455,13 @@ static int __init rmi_bus_init(void) if (error) { pr_err("%s: error registering the RMI physical driver: %d\n", __func__, error); - goto err_unregister_bus; + goto err_unregister_function_handlers; } return 0; +err_unregister_function_handlers: + rmi_unregister_function_handlers(); err_unregister_bus: bus_unregister(&rmi_bus_type); return error; From 8db211aed83733073b0814adaeeab61d4521474e Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 14 Jun 2026 00:36:11 -0500 Subject: [PATCH 0555/3297] Input: synaptics-rmi4 - bound the F3A keymap to the GPIO count commit 57c10915f2c16c90e0d46ad00876bf39ece40fc2 upstream. rmi_f3a_initialize() takes the GPIO count from the device query register (f3a->gpio_count = buf & RMI_F3A_GPIO_COUNT, range 0..127). rmi_f3a_map_gpios() then allocates gpio_key_map with min(gpio_count, TRACKSTICK_RANGE_END) == at most 6 entries, but rmi_f3a_attention() iterates the full gpio_count and dereferences gpio_key_map[i], and input->keycodemax is set to the full gpio_count while input->keycode points at the 6-entry allocation. A device that reports gpio_count > 6 therefore causes an out-of-bounds read of gpio_key_map[] on every attention interrupt, and out-of-bounds accesses through the input core's default keymap ioctls: EVIOCGKEYCODE reads past the buffer (leaking adjacent slab memory to user space) and EVIOCSKEYCODE writes a caller-controlled value past it, for any process able to open the evdev node, since input_default_getkeycode() and input_default_setkeycode() only bound the index against keycodemax. Size the keymap for the full gpio_count. The mapping loop is unchanged: it still assigns only the first min(gpio_count, TRACKSTICK_RANGE_END) entries; the remaining slots stay KEY_RESERVED (devm_kcalloc zero-fills) and are skipped when reporting. Fixes: 9e4c596bfd00 ("Input: synaptics-rmi4 - add support for F3A") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260614-b4-disp-818d6bda-v1-1-cf39a3615085@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/rmi4/rmi_f3a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_f3a.c b/drivers/input/rmi4/rmi_f3a.c index 0e8baed84dbb5..a0777644eef07 100644 --- a/drivers/input/rmi4/rmi_f3a.c +++ b/drivers/input/rmi4/rmi_f3a.c @@ -132,7 +132,7 @@ static int rmi_f3a_map_gpios(struct rmi_function *fn, struct f3a_data *f3a, int button_count = min_t(u8, f3a->gpio_count, TRACKSTICK_RANGE_END); f3a->gpio_key_map = devm_kcalloc(&fn->dev, - button_count, + f3a->gpio_count, sizeof(f3a->gpio_key_map[0]), GFP_KERNEL); if (!f3a->gpio_key_map) { From e849c6f51e6877104c765da084e001ec37c8e119 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 14 Jun 2026 00:36:12 -0500 Subject: [PATCH 0556/3297] Input: synaptics-rmi4 - bound the F30 keymap to the GPIO/LED count commit d577e46785d45484b2ab7e7309c49b18764bf56c upstream. rmi_f30_map_gpios() allocates gpioled_key_map with min(gpioled_count, TRACKSTICK_RANGE_END) == at most 6 entries, but rmi_f30_attention() iterates the full f30->gpioled_count (device query register, range 0..31) and dereferences gpioled_key_map[i], and input->keycodemax is set to the full gpioled_count while input->keycode points at the 6-entry allocation. A device that reports gpioled_count > 6 with GPIO support enabled therefore causes an out-of-bounds read on the attention interrupt and out-of-bounds read/write through the EVIOCGKEYCODE/EVIOCSKEYCODE ioctls, which bound the index only against keycodemax. This is the same defect as the F3A handler, which was copied from F30. Size the keymap for the full gpioled_count; the mapping loop still assigns only the first min(gpioled_count, TRACKSTICK_RANGE_END) entries. Fixes: 3e64fcbdbd10 ("Input: synaptics-rmi4 - limit the range of what GPIOs are buttons") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260614-b4-disp-818d6bda-v1-2-cf39a3615085@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/rmi4/rmi_f30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 35045f161dc2f..b2155c8e20e75 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -233,7 +233,7 @@ static int rmi_f30_map_gpios(struct rmi_function *fn, int button_count = min_t(u8, f30->gpioled_count, TRACKSTICK_RANGE_END); f30->gpioled_key_map = devm_kcalloc(&fn->dev, - button_count, + f30->gpioled_count, sizeof(f30->gpioled_key_map[0]), GFP_KERNEL); if (!f30->gpioled_key_map) { From 01e0317c256c560d8dcce2e9825eb6142ee34611 Mon Sep 17 00:00:00 2001 From: Ranjan Kumar Date: Mon, 22 Jun 2026 22:31:05 -0700 Subject: [PATCH 0557/3297] Input: elan_i2c - prevent division by zero and arithmetic underflow commit df2b818fa009c10ff6ba875a1663ff001cda9558 upstream. The Elan I2C touchpad driver queries the device for its physical dimensions and trace counts to calculate the device resolution and width. However, if the device firmware or device tree provides invalid zero values for x_traces or y_traces, it results in a fatal division-by-zero exception leading to a kernel panic during device probe. Add checks to ensure these parameters are non-zero before performing the division. If invalid trace values are detected, fall back to a safe default of 1. Additionally, prevent an arithmetic underflow in the touch reporting logic. Previously, if the calculated or fallback width was smaller than ETP_FWIDTH_REDUCE (90), the subtraction would underflow, resulting in a massive unsigned integer being reported to userspace. Clamp the adjusted width to a minimum of 0 to safely handle small physical dimensions and fallback scenarios. Completing the probe with safe fallback values ensures the sysfs nodes are created, keeping the firmware update path intact so a recovery firmware can be flashed to the device. Fixes: 6696777c6506 ("Input: add driver for Elan I2C/SMbus touchpad") Fixes: e3a9a1290688 ("Input: elan_i2c - do not query the info if they are provided") Signed-off-by: Ranjan Kumar Link: https://patch.msgid.link/20260612060339.3829666-1-kumarranja@chromium.org Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/elan_i2c_core.c | 36 ++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 74f822cd87746..caffcf755f914 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -425,8 +425,17 @@ static int elan_query_device_parameters(struct elan_tp_data *data) if (error) return error; } - data->width_x = data->max_x / x_traces; - data->width_y = data->max_y / y_traces; + + if (!x_traces || !y_traces) { + dev_warn(&client->dev, + "invalid trace numbers: x=%u, y=%u\n", + x_traces, y_traces); + data->width_x = 1; + data->width_y = 1; + } else { + data->width_x = data->max_x / x_traces; + data->width_y = data->max_y / y_traces; + } if (device_property_read_u32(&client->dev, "touchscreen-x-mm", &x_mm) || @@ -440,8 +449,16 @@ static int elan_query_device_parameters(struct elan_tp_data *data) data->x_res = elan_convert_resolution(hw_x_res, data->pattern); data->y_res = elan_convert_resolution(hw_y_res, data->pattern); } else { - data->x_res = (data->max_x + 1) / x_mm; - data->y_res = (data->max_y + 1) / y_mm; + if (unlikely(x_mm == 0 || y_mm == 0)) { + dev_warn(&client->dev, + "invalid physical dimensions: x_mm=%u, y_mm=%u\n", + x_mm, y_mm); + data->x_res = 1; + data->y_res = 1; + } else { + data->x_res = (data->max_x + 1) / x_mm; + data->y_res = (data->max_y + 1) / y_mm; + } } if (device_property_read_bool(&client->dev, "elan,clickpad")) @@ -953,6 +970,7 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, if (data->report_features & ETP_FEATURE_REPORT_MK) { unsigned int mk_x, mk_y, area_x, area_y; + int adj_width_x, adj_width_y; u8 mk_data = high_precision ? packet[ETP_MK_DATA_OFFSET + contact_num] : finger_data[3]; @@ -964,8 +982,14 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, * To avoid treating large finger as palm, let's reduce * the width x and y per trace. */ - area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE); - area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE); + + adj_width_x = data->width_x > ETP_FWIDTH_REDUCE ? + data->width_x - ETP_FWIDTH_REDUCE : 0; + adj_width_y = data->width_y > ETP_FWIDTH_REDUCE ? + data->width_y - ETP_FWIDTH_REDUCE : 0; + + area_x = mk_x * adj_width_x; + area_y = mk_y * adj_width_y; input_report_abs(input, ABS_TOOL_WIDTH, mk_x); input_report_abs(input, ABS_MT_TOUCH_MAJOR, From 3b32303460155603d25444274856013d211d5e1f Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 12 Jun 2026 21:10:33 -0500 Subject: [PATCH 0558/3297] Input: goodix - clamp the device-reported contact count commit 5ed62a96e06be4e94b8296b7932afee550a70e04 upstream. goodix_ts_read_input_report() copies the number of touch points reported by the device into an on-stack buffer u8 point_data[2 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; which is sized for at most GOODIX_MAX_CONTACTS (10) contacts. The only runtime check bounds the per-interrupt count against ts->max_touch_num, but that value is taken verbatim from a 4-bit field of the device configuration block and is never clamped: ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f; The nibble can be 0..15, so a malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can advertise up to 15 contacts. goodix_ts_read_input_report() then accepts a touch_num of up to 15 and the second goodix_i2c_read() writes ts->contact_size * (touch_num - 1) bytes past the one-contact header into point_data - up to 30 bytes (45 with the 9-byte report format) beyond the 92-byte buffer: a stack out-of-bounds write. Clamp max_touch_num to GOODIX_MAX_CONTACTS, the number of contacts point_data[] is sized for, when reading it from the configuration. Fixes: a7ac7c95d468 ("Input: goodix - use max touch number from device config") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260612-b4-disp-6844625d-v1-1-df0aed080c9d@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/goodix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index f8798d11ec030..17fcfe45988c2 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -1057,7 +1057,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) } ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03; - ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f; + ts->max_touch_num = min(ts->config[MAX_CONTACTS_LOC] & 0x0f, + GOODIX_MAX_CONTACTS); x_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC]); y_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC + 2]); From 70019779325f2bb5f5a4098e91e79c655f50fcef Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 22 Jun 2026 20:47:50 -0700 Subject: [PATCH 0559/3297] Input: iforce - bound the device-reported force-feedback effect index commit 0e9943d2e4c63496b6ca84bc66fd3c71d40558e2 upstream. iforce_process_packet() handles a status report (packet id 0x02) by taking a force-feedback effect index straight from the device wire and using it to address the per-effect state array: i = data[1] & 0x7f; if (data[1] & 0x80) { if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) ... } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { ... } The index is masked only with 0x7f, so it ranges 0..127, but core_effects[] holds only IFORCE_EFFECTS_MAX (32) entries. For an index of 32..127 the test_and_set_bit()/test_and_clear_bit() is an out-of-bounds single-bit read-modify-write past the array. core_effects[] is the second-to-last member of struct iforce, so the write lands in the trailing members and beyond the embedding kzalloc()'d iforce_serio / iforce_usb object. data[1] is unvalidated device payload on both transports (the USB interrupt endpoint and serio), and the status path is not gated on force feedback being present, so a malicious or counterfeit device can set or clear a bit at an attacker-chosen offset past the object. Reject an out-of-range index instead of indexing with it. Bound against the array dimension IFORCE_EFFECTS_MAX rather than dev->ff->max_effects so the check guarantees memory safety regardless of how many effects the device registered. A legitimate "effect started/stopped" status always carries an index below IFORCE_EFFECTS_MAX, so well-formed devices are unaffected; the neighbouring mark_core_as_ready() loop is already bounded and is left untouched. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Bryam Vargas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260613-b4-disp-4828d263-v1-1-02320e1a89dd@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/iforce/iforce-packets.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index fd1cd731d781a..effa76bfd8f9a 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -186,14 +186,18 @@ void iforce_process_packet(struct iforce *iforce, /* Check if an effect was just started or stopped */ i = data[1] & 0x7f; - if (data[1] & 0x80) { - if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report play event */ - input_report_ff_status(dev, i, FF_STATUS_PLAYING); + if (i < IFORCE_EFFECTS_MAX) { + if (data[1] & 0x80) { + if (!test_and_set_bit(FF_CORE_IS_PLAYED, + iforce->core_effects[i].flags)) { + /* Report play event */ + input_report_ff_status(dev, i, FF_STATUS_PLAYING); + } + } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, + iforce->core_effects[i].flags)) { + /* Report stop event */ + input_report_ff_status(dev, i, FF_STATUS_STOPPED); } - } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report stop event */ - input_report_ff_status(dev, i, FF_STATUS_STOPPED); } for (j = 3; j < len; j += 2) From 75b12874b4172533b9efc349db328cb1a59c3981 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Jun 2026 22:35:18 -0700 Subject: [PATCH 0560/3297] Input: mms114 - fix touch indexing for MMS134S and MMS136 commit a6ac4e24c1a8a533bb61035184fdcc7eede4cc8d upstream. The MMS134S and MMS136 touch controllers have an event size of 6 bytes rather than 8 bytes. When __mms114_read_reg() reads the touch data packet from the device into the touch buffer, the events are packed tightly at 6-byte intervals. However, the driver iterates through the events using standard C array indexing (touch[index]), where each element is sizeof(struct mms114_touch) (8 bytes) apart. As a result, any touch events beyond the first one are read from incorrect offsets and parsed improperly. Fix this by explicitly calculating the byte offset for each touch event based on the device's specific event size. Fixes: 53fefdd1d3a3 ("Input: mms114 - support MMS136") Fixes: ab108678195f ("Input: mms114 - support MMS134S") Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Reviewed-by: Bryam Vargas Link: https://patch.msgid.link/20260616050912.1531241-1-dmitry.torokhov@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/mms114.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 9f947044c4d9b..7f01a5a5520a6 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -218,7 +218,9 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) struct i2c_client *client = data->client; struct input_dev *input_dev = data->input_dev; struct mms114_touch touch[MMS114_MAX_TOUCH]; + struct mms114_touch *t; int packet_size; + int event_size; int touch_size; int index; int error; @@ -236,9 +238,11 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) /* MMS136 has slightly different event size */ if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) - touch_size = packet_size / MMS136_EVENT_SIZE; + event_size = MMS136_EVENT_SIZE; else - touch_size = packet_size / MMS114_EVENT_SIZE; + event_size = MMS114_EVENT_SIZE; + + touch_size = packet_size / event_size; error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, (u8 *)touch); @@ -246,18 +250,20 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) goto out; for (index = 0; index < touch_size; index++) { - switch (touch[index].type) { + t = (struct mms114_touch *)((u8 *)touch + index * event_size); + + switch (t->type) { case MMS114_TYPE_TOUCHSCREEN: - mms114_process_mt(data, touch + index); + mms114_process_mt(data, t); break; case MMS114_TYPE_TOUCHKEY: - mms114_process_touchkey(data, touch + index); + mms114_process_touchkey(data, t); break; default: dev_err(&client->dev, "Wrong touch type (%d)\n", - touch[index].type); + t->type); break; } } From 05dee4007cf3029163ac6c09f2b313b7cd177cff Mon Sep 17 00:00:00 2001 From: Kris Bahnsen Date: Thu, 7 May 2026 16:49:43 +0000 Subject: [PATCH 0561/3297] Input: ads7846 - don't use scratch for tx_buf when clearing register commit 856668312685d9a8f32d49a135a89c429d309f81 upstream. The workaround for XPT2046 clears the command register, giving the touchscreen controller a NOP. The change incorrectly re-uses the req->scratch variable which is used as rx_buf for xfer[5], so by the time xfer[6] occurs, the contents of req->scratch may not be 0. It was found that the touchscreen controller can end up in a completely unresponsive state due to it being given a command the driver does not expect. Instead, rely on the spi_transfer behavior of tx_buf being NULL to transmit all 0 bits and use the scratch variable for the rx_buf for both the 1 byte command to and 2 byte response from the controller. Also relocates the scratch member of struct ser_req to force it into a different cache line to prevent any potential issues of DMA stepping on unrelated data in other struct members due to sharing the same cache line. This change was tested on real TSC2046 and ADS7843 controllers, but not the XPT2046 the workaround was originally created for. Confirming that the original modification to clear the command register does not impact either real controller. Fixes: 781a07da9bb94 ("Input: ads7846 - add dummy command register clearing cycle") Cc: stable@vger.kernel.org Co-developed-by: Mark Featherston Signed-off-by: Mark Featherston Signed-off-by: Kris Bahnsen Link: https://patch.msgid.link/20260507164943.760009-1-kris@embeddedTS.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/ads7846.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 67264c5b49cb4..4d3df2fe0b6de 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -329,7 +329,6 @@ struct ser_req { u8 ref_on; u8 command; u8 ref_off; - u16 scratch; struct spi_message msg; struct spi_transfer xfer[8]; /* @@ -337,6 +336,7 @@ struct ser_req { * transfer buffers to live in their own cache lines. */ __be16 sample ____cacheline_aligned; + u16 scratch; }; struct ads7845_ser_req { @@ -408,8 +408,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) spi_message_add_tail(&req->xfer[5], &req->msg); /* clear the command register */ - req->scratch = 0; - req->xfer[6].tx_buf = &req->scratch; + req->xfer[6].rx_buf = &req->scratch; req->xfer[6].len = 1; spi_message_add_tail(&req->xfer[6], &req->msg); From 3e6f007b43e2fc6546e21fa74ee62c38984a6672 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 13 Jun 2026 20:07:20 -0500 Subject: [PATCH 0562/3297] Input: touchwin - reset the packet index on every complete packet commit 478cdd736f2ce3114f90e775d7358136d3977b94 upstream. tw_interrupt() accumulates each non-zero serial byte into a fixed three-byte buffer with a running index that is only reset once a full packet has been received *and* the device's two Y bytes agree: tw->data[tw->idx++] = data; if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { ... tw->idx = 0; } The reset is gated on tw->data[1] == tw->data[2], a value the device controls. A malicious, malfunctioning or counterfeit Touchwindow peripheral can stream non-zero bytes whose 2nd and 3rd bytes differ: the index reaches TW_LENGTH without the equality holding, is never reset, and keeps growing, so tw->data[tw->idx++] walks off the end of the three-byte array and the rest of the heap-allocated struct tw, one attacker-chosen byte at a time -- an unbounded, device-driven heap out-of-bounds write. Reset the index on every completed packet and report an event only when the two Y bytes match, like the other serio touchscreen drivers do. Fixes: 11ea3173d5f2 ("Input: add driver for Touchwin serial touchscreens") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260613-b4-disp-69921bfd-v1-1-82c036899959@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/touchwin.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c index fbd72789ea809..aeb26f7a91d44 100644 --- a/drivers/input/touchscreen/touchwin.c +++ b/drivers/input/touchscreen/touchwin.c @@ -63,12 +63,15 @@ static irqreturn_t tw_interrupt(struct serio *serio, if (data) { /* touch */ tw->touched = 1; tw->data[tw->idx++] = data; - /* verify length and that the two Y's are the same */ - if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { - input_report_abs(dev, ABS_X, tw->data[0]); - input_report_abs(dev, ABS_Y, tw->data[1]); - input_report_key(dev, BTN_TOUCH, 1); - input_sync(dev); + /* a full packet ends the accumulation, valid or not */ + if (tw->idx == TW_LENGTH) { + /* report only if the two Y's are the same */ + if (tw->data[1] == tw->data[2]) { + input_report_abs(dev, ABS_X, tw->data[0]); + input_report_abs(dev, ABS_Y, tw->data[1]); + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + } tw->idx = 0; } } else if (tw->touched) { /* untouch */ From 8301c335305344d4da4ab9442b6a399dacfe5b8d Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 14 Jun 2026 14:19:43 -0700 Subject: [PATCH 0563/3297] Input: mms114 - reject an oversized device packet size commit 66725039f7090afe14c31bd259e2059a68f04023 upstream. mms114_interrupt() reads a packet of touch data from the device into a fixed-size on-stack buffer struct mms114_touch touch[MMS114_MAX_TOUCH]; which holds MMS114_MAX_TOUCH (10) events of MMS114_EVENT_SIZE (8) bytes, i.e. 80 bytes. The length of the I2C read into it is taken verbatim from the device: packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); if (packet_size <= 0) goto out; ... error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, (u8 *)touch); packet_size is a single device register byte (0x0F) and the only check is the lower bound packet_size <= 0; it is never bounded against the size of touch[]. A malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can report a packet_size of up to 255, so __mms114_read_reg() writes up to 175 bytes past the end of touch[] on the IRQ-thread stack: a stack out-of-bounds write that can overwrite the stack canary, saved registers and the return address. A well-formed device never reports more than the buffer holds, so reject an oversized packet and drop the report, consistent with the handler's other error paths, rather than reading past the buffer. Fixes: 07b8481d4aff ("Input: add MELFAS mms114 touchscreen driver") Signed-off-by: Bryam Vargas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260612-b4-disp-dc4b8dc4-v1-1-d7cb0a828d92@proton.me Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/mms114.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 7f01a5a5520a6..b467c601e3fcc 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -236,6 +236,12 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) if (packet_size <= 0) goto out; + if (packet_size > sizeof(touch)) { + dev_err(&client->dev, "Invalid packet size %d (max %zu)\n", + packet_size, sizeof(touch)); + goto out; + } + /* MMS136 has slightly different event size */ if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) event_size = MMS136_EVENT_SIZE; From 37fbe63bccf21e3b98636f6ed4f9bd7891baaefd Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Wed, 24 Jun 2026 17:47:39 +0800 Subject: [PATCH 0564/3297] Input: gscps2 - advance receive buffer write index commit d86d4f8cbb5a55a3b9b86f7b5ab8c4cdda600a3f upstream. Commit 44f920069911 ("Input: gscps2 - use guard notation when acquiring spinlock") moved the receive loop into gscps2_read_data() and gscps2_report_data(). While moving the code, it preserved the writes to buffer[ps2port->append], but omitted the following producer index update from the original loop: ps2port->append = (ps2port->append + 1) & BUFFER_SIZE; As a result, append never advances. Since gscps2_report_data() only reports bytes while act != append, the receive buffer always appears empty and no keyboard or mouse data reaches the serio core. Restore the omitted index update. Fixes: 44f920069911 ("Input: gscps2 - use guard notation when acquiring spinlock") Cc: stable@vger.kernel.org # 6.13+ Signed-off-by: Xu Rao Link: https://patch.msgid.link/460B5655BA580C60+20260624094739.850306-1-raoxu@uniontech.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/serio/gscps2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 9c6ff04c46cf9..d05c5b02ac332 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -219,6 +219,7 @@ static void gscps2_read_data(struct gscps2port *ps2port) ps2port->buffer[ps2port->append].str = status; ps2port->buffer[ps2port->append].data = gscps2_readb_input(ps2port->addr); + ps2port->append = (ps2port->append + 1) & BUFFER_SIZE; } while (true); } From 1b4cb75f254fb0224db1b65a1bb1f735a87df01d Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 29 Jun 2026 18:33:42 -0700 Subject: [PATCH 0565/3297] Input: maplemouse - fix NULL pointer dereference in open() commit ee89db004238bd0b034f2a6176e175561658750b upstream. Commit 555c765b0cc2 ("Input: mouse - drop unnecessary calls to input_set_drvdata") dropped the input_set_drvdata() call in probe because the data appeared to be unused. However, dc_mouse_open() and dc_mouse_close() were using maple_get_drvdata(to_maple_dev(&dev->dev)). This appears to be accessing the data attached to an instance of maple_device structure, while in reality this actually retrieves driver data from the input device's embedded struct device (doing invalid conversion of input device structure to maple device). After input_set_drvdata() was removed, that lookup started returning NULL and opening the input device dereferences mse->mdev. Restore input_set_drvdata() and convert open() and close() to use input_get_drvdata() so the dependency is no longer hidden. Fixes: 6b3480855aad ("maple: input: fix up maple mouse driver") Fixes: 555c765b0cc2 ("Input: mouse - drop unnecessary calls to input_set_drvdata") Signed-off-by: Florian Fuchs Link: https://patch.msgid.link/20260628230715.2982552-1-fuchsfl@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/maplemouse.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index baef4be14b54d..bbef6da638f23 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -48,7 +48,7 @@ static void dc_mouse_callback(struct mapleq *mq) static int dc_mouse_open(struct input_dev *dev) { - struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + struct dc_mouse *mse = input_get_drvdata(dev); maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50, MAPLE_FUNC_MOUSE); @@ -58,7 +58,7 @@ static int dc_mouse_open(struct input_dev *dev) static void dc_mouse_close(struct input_dev *dev) { - struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev)); + struct dc_mouse *mse = input_get_drvdata(dev); maple_getcond_callback(mse->mdev, dc_mouse_callback, 0, MAPLE_FUNC_MOUSE); @@ -88,6 +88,7 @@ static int probe_maple_mouse(struct device *dev) mse->dev = input_dev; mse->mdev = mdev; + input_set_drvdata(input_dev, mse); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); From d7f66fbab5d212840d81b410ddc63780a8d54c4a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 3 Jul 2026 23:01:12 -0700 Subject: [PATCH 0566/3297] Input: mms114 - fix multi-touch slot corruption commit adea84ee6cdea611146c4251d3c1616f5a09feca upstream. If the touchscreen controller reports a touch ID of 0, the driver calculates the slot ID as touch->id - 1, which underflows to UINT_MAX. This is passed to input_mt_slot() as -1. Since the input core ignores negative slot values, the active slot remains unchanged. The driver then reports the touch coordinates for the previously active slot, corrupting its state. Fix this by rejecting touch reports with ID 0. Fixes: 07b8481d4aff ("Input: add MELFAS mms114 touchscreen driver") Cc: stable@vger.kernel.org Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260704060115.353049-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/mms114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index b467c601e3fcc..e703d6cc54d2e 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -165,7 +165,7 @@ static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *tou unsigned int x; unsigned int y; - if (touch->id > MMS114_MAX_TOUCH) { + if (touch->id == 0 || touch->id > MMS114_MAX_TOUCH) { dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); return; } From 699e3abac02de62080c9a95c26b38aec694e415c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 18:44:41 -0700 Subject: [PATCH 0567/3297] Input: maple_keyb - set driver data before registering input device commit 536394ec81419b67d9f4f0028812c4372397be1b upstream. Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/keyboard/maple_keyb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 1a8f1fa53fbb4..9246e5a745746 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -166,6 +166,8 @@ static int probe_maple_kbd(struct device *dev) kbd->dev = idev; memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode)); + maple_set_drvdata(mdev, kbd); + idev->name = mdev->product_name; idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); idev->keycode = kbd->keycode; @@ -190,8 +192,6 @@ static int probe_maple_kbd(struct device *dev) mdev->driver = mdrv; - maple_set_drvdata(mdev, kbd); - return error; fail_register: From 9376c744bea2c1fc759443802a3a4dad14cf4498 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:47:34 -0700 Subject: [PATCH 0568/3297] Input: maplemouse - set driver data before registering input device commit 738f24bbbc95dd50cb4229d1ed62a05f29db2bda upstream. Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Tested-by: Florian Fuchs Link: https://patch.msgid.link/akNXw45L_8bxD6QV@google.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/maplemouse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index bbef6da638f23..2c71e0716d672 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -88,6 +88,8 @@ static int probe_maple_mouse(struct device *dev) mse->dev = input_dev; mse->mdev = mdev; + maple_set_drvdata(mdev, mse); + input_set_drvdata(input_dev, mse); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | @@ -103,12 +105,12 @@ static int probe_maple_mouse(struct device *dev) goto fail_register; mdev->driver = mdrv; - maple_set_drvdata(mdev, mse); return error; fail_register: input_free_device(input_dev); + maple_set_drvdata(mdev, NULL); fail_nomem: kfree(mse); fail: From 95de76f6ad4743dc6c350e170522c74ef93e62bb Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:49:15 -0700 Subject: [PATCH 0569/3297] Input: maplecontrol - set driver data before registering input device commit fe938ee497d58c644f6910cfe6ae155f6fb3e523 upstream. Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered. Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Tested-by: Florian Fuchs Link: https://patch.msgid.link/akNYib9hQFNN1fA9@google.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/maplecontrol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c index 8b54f9b18e7c0..1534640268f30 100644 --- a/drivers/input/joystick/maplecontrol.c +++ b/drivers/input/joystick/maplecontrol.c @@ -112,6 +112,8 @@ static int probe_maple_controller(struct device *dev) pad->dev = idev; pad->mdev = mdev; + maple_set_drvdata(mdev, pad); + idev->open = dc_pad_open; idev->close = dc_pad_close; @@ -146,7 +148,6 @@ static int probe_maple_controller(struct device *dev) goto fail; mdev->driver = mdrv; - maple_set_drvdata(mdev, pad); return 0; From 5a45d0aa1fa50a333ce5763ade744e2d89838667 Mon Sep 17 00:00:00 2001 From: Zhenhao Wan Date: Fri, 12 Jun 2026 01:15:54 +0800 Subject: [PATCH 0570/3297] RDMA/rtrs-srv: Bound RDMA-Write length to chunk size in rdma_write_sg commit 963af8d97a8c6a117134a8d0db1415e0489200b1 upstream. When the server answers an RTRS READ, rdma_write_sg() builds the source scatter/gather entry for the IB_WR_RDMA_WRITE that returns data to the peer. Its length is taken directly from the wire descriptor: plist->length = le32_to_cpu(id->rd_msg->desc[0].len); rd_msg points into the chunk buffer that the remote peer filled via RDMA-WRITE-WITH-IMM (rtrs_srv_rdma_done() -> process_io_req() -> process_read()), so desc[0].len is attacker-controlled and, before this change, was only rejected when zero. The source address is the fixed chunk start (dma_addr[msg_id]) and the source lkey is the PD-wide local_dma_lkey, which is not tied to the chunk's MR mapping, so the verbs layer does not constrain the transfer length to max_chunk_size. msg_id and off are bounded against queue_depth and max_chunk_size in rtrs_srv_rdma_done(), but desc[0].len is a separate field that was not checked against the chunk size. A peer that advertises desc[0].len larger than max_chunk_size can make the posted RDMA write read past the chunk's mapped region. The resulting behaviour depends on the IOMMU configuration: with no IOMMU or in passthrough mode the read may extend into memory adjacent to the chunk and be returned to the peer, which can disclose host memory; with a translating IOMMU the out-of-range access is expected to fault and abort the connection. In either case the transfer exceeds what the protocol permits and is driven by a remote peer. Reject a descriptor length above max_chunk_size, mirroring the existing off >= max_chunk_size bound in rtrs_srv_rdma_done(). Legitimate clients do not exceed it: the client sets desc[0].len to its MR length, which is capped at the negotiated max_io_size (max_chunk_size - MAX_HDR_SIZE). Fixes: 9cb837480424 ("RDMA/rtrs: server: main functionality") Link: https://patch.msgid.link/r/20260612-master-v1-1-70cde5c6fdc9@gmail.com Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Zhenhao Wan Reviewed-by: Md Haris Iqbal Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c index adb798e2a54ae..1fc67fdff9fc2 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c @@ -227,8 +227,9 @@ static int rdma_write_sg(struct rtrs_srv_op *id) /* WR will fail with length error * if this is 0 */ - if (plist->length == 0) { - rtrs_err(s, "Invalid RDMA-Write sg list length 0\n"); + if (plist->length == 0 || plist->length > max_chunk_size) { + rtrs_err(s, "Invalid RDMA-Write sg list length %u\n", + plist->length); return -EINVAL; } From ab45808c141a3b1e04e37aeced7c180ef77449ba Mon Sep 17 00:00:00 2001 From: Maher Sanalla Date: Tue, 9 Jun 2026 14:16:38 +0300 Subject: [PATCH 0571/3297] RDMA/core: Fix broadcast address falsely detected as local commit 942cd47faa2047b46dfd85745603eba9006973e6 upstream. When rdma_resolve_addr() is invoked with a broadcast destination on an IPoIB interface, is_dst_local() inspects the resolved route and incorrectly concludes that the address is local. As a result, the resolution fails with -ENODEV. The issue stems from using '&' to compare rt_type with RTN_LOCAL. The RTN_* values form a sequential enum, not a bitmask (RTN_LOCAL=2, RTN_BROADCAST=3). Thus, "rt_type & RTN_LOCAL" yields a non-zero result for a broadcast route as well. Replace '&' with '==' when comparing rt_type against RTN_LOCAL. Link: https://patch.msgid.link/r/20260609-fix-rdma-resolve-addr-v1-1-449b8b4e6c09@nvidia.com Cc: stable@vger.kernel.org Fixes: c31e4038c97f ("RDMA/core: Use route entry flag to decide on loopback traffic") Signed-off-by: Maher Sanalla Reviewed-by: Vlad Dumitrescu Signed-off-by: Edward Srouji Reviewed-by: Parav Pandit Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/addr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index d2a075a781ce6..e73c9d8333f70 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -439,7 +439,7 @@ static int addr6_resolve(struct sockaddr *src_sock, static bool is_dst_local(const struct dst_entry *dst) { if (dst->ops->family == AF_INET) - return !!(dst_rtable(dst)->rt_type & RTN_LOCAL); + return dst_rtable(dst)->rt_type == RTN_LOCAL; else if (dst->ops->family == AF_INET6) return !!(dst_rt6_info(dst)->rt6i_flags & RTF_LOCAL); else From 423a78ff7928c2601013f73ec6d896f5597d0df5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 2 Jun 2026 15:47:00 -0400 Subject: [PATCH 0572/3297] RDMA/siw: bound Read Response placement to the RREAD length commit 7d29f7e9dbd844cae4d3e559cf78324b9642fd6b upstream. In drivers/infiniband/sw/siw/siw_qp_rx.c, siw_proc_rresp() places each inbound Read Response DDP segment at sge->laddr + wqe->processed and then accumulates wqe->processed, but it never checks the running total against the sink buffer length on continuation segments. siw_check_sge() resolves and validates the sink memory only on the first fragment (the if (!*mem) branch), and siw_rresp_check_ntoh() compares the cumulative length against wqe->bytes only on the final segment (the !frx->more_ddp_segs guard). A connected siw peer that answers an outstanding RREAD with Read Response segments that keep the DDP Last flag clear, carrying more total payload than the RREAD requested, drives wqe->processed past the validated sink buffer; the next siw_rx_data() call writes out of bounds at sge->laddr + wqe->processed. siw runs iWARP over ordinary routable TCP, so the peer is the remote end of an established RDMA connection and needs no local privilege. Bound every segment before placement, exactly as siw_proc_send() and siw_proc_write() already do for their tagged and untagged paths, and terminate the connection with a base-or-bounds DDP error when the Read Response would overrun the sink buffer. This is the second receive-path length fix for this file. A separate change rejects an MPA FPDU length that underflows the per-fragment remainder in the header decode; that guard does not cover this case, because here each individual segment length is self-consistent and only the accumulated placement offset overruns the buffer. Fixes: 8b6a361b8c48 ("rdma/siw: receive path") Link: https://patch.msgid.link/r/20260602194700.2273758-1-michael.bommarito@gmail.com Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/sw/siw/siw_qp_rx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/infiniband/sw/siw/siw_qp_rx.c b/drivers/infiniband/sw/siw/siw_qp_rx.c index 34d03584160c2..b566d163c5aab 100644 --- a/drivers/infiniband/sw/siw/siw_qp_rx.c +++ b/drivers/infiniband/sw/siw/siw_qp_rx.c @@ -844,6 +844,15 @@ int siw_proc_rresp(struct siw_qp *qp) } mem_p = *mem; + if (unlikely(wqe->processed + srx->fpdu_part_rem > wqe->bytes)) { + siw_dbg_qp(qp, "rresp len: %d + %d > %d\n", + wqe->processed, srx->fpdu_part_rem, wqe->bytes); + wqe->wc_status = SIW_WC_LOC_LEN_ERR; + siw_init_terminate(qp, TERM_ERROR_LAYER_DDP, + DDP_ETYPE_TAGGED_BUF, + DDP_ECODE_T_BASE_BOUNDS, 0); + return -EINVAL; + } bytes = min(srx->fpdu_part_rem, srx->skb_new); rv = siw_rx_data(mem_p, srx, &frx->pbl_idx, sge->laddr + wqe->processed, bytes); From 69cfae58b9a3282d832dd83377323bab36f69c5c Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Tue, 26 May 2026 08:20:21 -0700 Subject: [PATCH 0573/3297] fuse: back uncached readdir buffers with pages commit 4dd6f6d3085a84e74b0a1efec3a05ed0b5125dce upstream. Commit dabb90391028 ("fuse: increase readdir buffer size") changed fuse_readdir_uncached() to size its temporary buffer from ctx->count. This is useful for overlayfs and other in-kernel callers that use INT_MAX to indicate an unlimited directory read. The larger buffer is currently supplied as a kvec output argument. For virtiofs, kvec arguments are copied through req->argbuf, which is allocated with kmalloc(..., GFP_ATOMIC). A large uncached readdir buffer can therefore require a multi-megabyte contiguous atomic allocation before the request is queued. Avoid the large bounce-buffer allocation by backing uncached readdir output with pages and setting out_pages. Transports such as virtiofs can then pass the pages as scatter-gather entries instead of copying the output through argbuf. Map the pages with vm_map_ram() only while parsing the returned dirents. The existing parser can then continue to use a linear kernel mapping. [SzM: separate allocation of pages into a helper function] Fixes: dabb90391028 ("fuse: increase readdir buffer size") Cc: stable@vger.kernel.org Signed-off-by: Matthew R. Ochs Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/readdir.c | 85 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index aae657fd56c0e..57a4d6254bc98 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -12,6 +12,7 @@ #include #include #include +#include static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx) { @@ -335,6 +336,43 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, return 0; } +static struct page **fuse_readdir_alloc_buf(struct fuse_args_pages *ap, size_t *bufsize) +{ + unsigned int i, nr_alloc, nr_pages = DIV_ROUND_UP(*bufsize, PAGE_SIZE); + struct page **pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); + + if (!pages) + return NULL; + + nr_alloc = alloc_pages_bulk(GFP_KERNEL, nr_pages, pages); + if (!nr_alloc) + goto free_array; + + if (nr_alloc < nr_pages) { + nr_pages = nr_alloc; + *bufsize = (size_t) nr_pages << PAGE_SHIFT; + } + + ap->folios = fuse_folios_alloc(nr_pages, GFP_KERNEL, &ap->descs); + if (!ap->folios) + goto release_pages; + + for (i = 0; i < nr_pages; i++) { + ap->folios[i] = page_folio(pages[i]); + ap->descs[i].length = min_t(size_t, *bufsize - (size_t)i * PAGE_SIZE, PAGE_SIZE); + } + ap->num_folios = nr_pages; + ap->args.out_pages = true; + + return pages; + +release_pages: + release_pages(pages, nr_pages); +free_array: + kfree(pages); + return NULL; +} + static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) { int plus; @@ -343,18 +381,16 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) struct fuse_mount *fm = get_fuse_mount(inode); struct fuse_conn *fc = fm->fc; struct fuse_io_args ia = {}; - struct fuse_args *args = &ia.ap.args; + struct fuse_args_pages *ap = &ia.ap; void *buf; size_t bufsize = clamp((unsigned int) ctx->count, PAGE_SIZE, fc->max_pages << PAGE_SHIFT); u64 attr_version = 0, evict_ctr = 0; bool locked; + struct page **pages = fuse_readdir_alloc_buf(ap, &bufsize); - buf = kvmalloc(bufsize, GFP_KERNEL); - if (!buf) + if (!pages) return -ENOMEM; - args->out_args[0].value = buf; - plus = fuse_use_readdirplus(inode, ctx); if (plus) { attr_version = fuse_get_attr_version(fm->fc); @@ -364,24 +400,37 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) fuse_read_args_fill(&ia, file, ctx->pos, bufsize, FUSE_READDIR); } locked = fuse_lock_inode(inode); - res = fuse_simple_request(fm, args); + res = fuse_simple_request(fm, &ap->args); fuse_unlock_inode(inode, locked); - if (res >= 0) { - if (!res) { - struct fuse_file *ff = file->private_data; - - if (ff->open_flags & FOPEN_CACHE_DIR) - fuse_readdir_cache_end(file, ctx->pos); - } else if (plus) { - res = parse_dirplusfile(buf, res, file, ctx, attr_version, - evict_ctr); - } else { + if (res < 0) + goto out; + + if (!res) { + struct fuse_file *ff = file->private_data; + + if (ff->open_flags & FOPEN_CACHE_DIR) + fuse_readdir_cache_end(file, ctx->pos); + goto out; + } + + buf = vm_map_ram(pages, ap->num_folios, -1); + if (!buf) { + res = -ENOMEM; + } else { + if (plus) + res = parse_dirplusfile(buf, res, file, ctx, attr_version, evict_ctr); + else res = parse_dirfile(buf, res, file, ctx); - } + + vm_unmap_ram(buf, ap->num_folios); } +out: + kfree(ap->folios); + release_pages(pages, ap->num_folios); + kfree(pages); - kvfree(buf); fuse_invalidate_atime(inode); + return res; } From 6e2d84fdeac05bfd858e84a76353fdb84f23a43e Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 10 Jun 2026 00:37:18 +0000 Subject: [PATCH 0574/3297] fuse: avoid 32-bit prune notification count wrap commit 54243797cedf55447b4c5d560e8cd709900061ae upstream. FUSE_NOTIFY_PRUNE validates the nodeid payload length with: size - sizeof(outarg) != outarg.count * sizeof(u64) On 32-bit kernels, size_t is also 32 bits, so the daemon-controlled count multiplication can wrap. A prune notification with count 0x20000000 and no nodeid payload passes the check, enters the copy loop, and asks the device copy path to read nodeids that are not present in the userspace write buffer. In QEMU this reaches the fuse_copy_fill() BUG_ON(!err) path. Validate the payload length with array_size() instead. That accepts exactly the same valid messages, but avoids wrapping arithmetic before the copy loop consumes the count. Assisted-by: Codex:gpt-5.5-cyber-preview Fixes: 3f29d59e92a9 ("fuse: add prune notification") Cc: stable@vger.kernel.org Signed-off-by: Samuel Moelius Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 2 +- fs/fuse/notify.c | 434 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 fs/fuse/notify.c diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 527f60f7ad7be..403fc18ee3627 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2082,7 +2082,7 @@ static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size, if (err) return err; - if (size - sizeof(outarg) != outarg.count * sizeof(u64)) + if (size - sizeof(outarg) != array_size(outarg.count, sizeof(u64))) return -EINVAL; for (; outarg.count; outarg.count -= num) { diff --git a/fs/fuse/notify.c b/fs/fuse/notify.c new file mode 100644 index 0000000000000..7cd63502e124b --- /dev/null +++ b/fs/fuse/notify.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "dev.h" +#include "fuse_i.h" +#include + +static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_poll_wakeup_out outarg; + int err; + + if (size != sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + fuse_copy_finish(cs); + return fuse_notify_poll_wakeup(fc, &outarg); +} + +static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_inval_inode_out outarg; + int err; + + if (size != sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + fuse_copy_finish(cs); + + down_read(&fc->killsb); + err = fuse_reverse_inval_inode(fc, outarg.ino, + outarg.off, outarg.len); + up_read(&fc->killsb); + return err; +} + +static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_inval_entry_out outarg; + int err; + char *buf; + struct qstr name; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (outarg.namelen > fc->name_max) + return -ENAMETOOLONG; + + err = -EINVAL; + if (size != sizeof(outarg) + outarg.namelen + 1) + return -EINVAL; + + buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + name.name = buf; + name.len = outarg.namelen; + err = fuse_copy_one(cs, buf, outarg.namelen + 1); + if (err) + goto err; + fuse_copy_finish(cs); + buf[outarg.namelen] = 0; + + down_read(&fc->killsb); + err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags); + up_read(&fc->killsb); +err: + kfree(buf); + return err; +} + +static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_delete_out outarg; + int err; + char *buf; + struct qstr name; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (outarg.namelen > fc->name_max) + return -ENAMETOOLONG; + + if (size != sizeof(outarg) + outarg.namelen + 1) + return -EINVAL; + + buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + name.name = buf; + name.len = outarg.namelen; + err = fuse_copy_one(cs, buf, outarg.namelen + 1); + if (err) + goto err; + fuse_copy_finish(cs); + buf[outarg.namelen] = 0; + + down_read(&fc->killsb); + err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0); + up_read(&fc->killsb); +err: + kfree(buf); + return err; +} + +static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_store_out outarg; + struct inode *inode; + struct address_space *mapping; + u64 nodeid; + int err; + unsigned int num; + loff_t file_size; + loff_t pos; + loff_t end; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (size - sizeof(outarg) != outarg.size) + return -EINVAL; + + if (outarg.offset >= MAX_LFS_FILESIZE) + return -EINVAL; + + nodeid = outarg.nodeid; + pos = outarg.offset; + num = min(outarg.size, MAX_LFS_FILESIZE - pos); + + down_read(&fc->killsb); + + err = -ENOENT; + inode = fuse_ilookup(fc, nodeid, NULL); + if (!inode) + goto out_up_killsb; + + mapping = inode->i_mapping; + file_size = i_size_read(inode); + end = pos + num; + if (end > file_size) { + file_size = end; + fuse_write_update_attr(inode, file_size, num); + } + + while (num) { + struct folio *folio; + unsigned int folio_offset; + unsigned int nr_bytes; + pgoff_t index = pos >> PAGE_SHIFT; + + folio = filemap_grab_folio(mapping, index); + err = PTR_ERR(folio); + if (IS_ERR(folio)) + goto out_iput; + + folio_offset = offset_in_folio(folio, pos); + nr_bytes = min(num, folio_size(folio) - folio_offset); + + err = fuse_copy_folio(cs, &folio, folio_offset, nr_bytes, 0); + if (!folio_test_uptodate(folio) && !err && folio_offset == 0 && + (nr_bytes == folio_size(folio) || file_size == end)) { + folio_zero_segment(folio, nr_bytes, folio_size(folio)); + folio_mark_uptodate(folio); + } + folio_unlock(folio); + folio_put(folio); + + if (err) + goto out_iput; + + pos += nr_bytes; + num -= nr_bytes; + } + + err = 0; + +out_iput: + iput(inode); +out_up_killsb: + up_read(&fc->killsb); + return err; +} + +struct fuse_retrieve_args { + struct fuse_args_pages ap; + struct fuse_notify_retrieve_in inarg; +}; + +static void fuse_retrieve_end(struct fuse_args *args, int error) +{ + struct fuse_retrieve_args *ra = + container_of(args, typeof(*ra), ap.args); + + release_pages(ra->ap.folios, ra->ap.num_folios); + kfree(ra); +} + +static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode, + struct fuse_notify_retrieve_out *outarg) +{ + int err; + struct address_space *mapping = inode->i_mapping; + loff_t file_size; + unsigned int num; + unsigned int offset; + size_t total_len = 0; + unsigned int num_pages; + struct fuse_conn *fc = fm->fc; + struct fuse_retrieve_args *ra; + size_t args_size = sizeof(*ra); + struct fuse_args_pages *ap; + struct fuse_args *args; + loff_t pos = outarg->offset; + + offset = offset_in_page(pos); + file_size = i_size_read(inode); + + num = min(outarg->size, fc->max_write); + if (pos > file_size) + num = 0; + else if (num > file_size - pos) + num = file_size - pos; + + num_pages = DIV_ROUND_UP(num + offset, PAGE_SIZE); + num_pages = min(num_pages, fc->max_pages); + num = min(num, num_pages << PAGE_SHIFT); + + args_size += num_pages * (sizeof(ap->folios[0]) + sizeof(ap->descs[0])); + + ra = kzalloc(args_size, GFP_KERNEL); + if (!ra) + return -ENOMEM; + + ap = &ra->ap; + ap->folios = (void *) (ra + 1); + ap->descs = (void *) (ap->folios + num_pages); + + args = &ap->args; + args->nodeid = outarg->nodeid; + args->opcode = FUSE_NOTIFY_REPLY; + args->in_numargs = 3; + args->in_pages = true; + args->end = fuse_retrieve_end; + + while (num && ap->num_folios < num_pages) { + struct folio *folio; + unsigned int folio_offset; + unsigned int nr_bytes; + pgoff_t index = pos >> PAGE_SHIFT; + + folio = filemap_get_folio(mapping, index); + if (IS_ERR(folio)) + break; + + folio_offset = offset_in_folio(folio, pos); + nr_bytes = min(folio_size(folio) - folio_offset, num); + + ap->folios[ap->num_folios] = folio; + ap->descs[ap->num_folios].offset = folio_offset; + ap->descs[ap->num_folios].length = nr_bytes; + ap->num_folios++; + + pos += nr_bytes; + num -= nr_bytes; + total_len += nr_bytes; + } + ra->inarg.offset = outarg->offset; + ra->inarg.size = total_len; + fuse_set_zero_arg0(args); + args->in_args[1].size = sizeof(ra->inarg); + args->in_args[1].value = &ra->inarg; + args->in_args[2].size = total_len; + + err = fuse_simple_notify_reply(fm, args, outarg->notify_unique); + if (err) + fuse_retrieve_end(args, err); + + return err; +} + +static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_retrieve_out outarg; + struct fuse_mount *fm; + struct inode *inode; + u64 nodeid; + int err; + + if (size != sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + fuse_copy_finish(cs); + + if (outarg.offset >= MAX_LFS_FILESIZE) + return -EINVAL; + + down_read(&fc->killsb); + err = -ENOENT; + nodeid = outarg.nodeid; + + inode = fuse_ilookup(fc, nodeid, &fm); + if (inode) { + err = fuse_retrieve(fm, inode, &outarg); + iput(inode); + } + up_read(&fc->killsb); + + return err; +} + +static int fuse_notify_resend(struct fuse_conn *fc) +{ + fuse_chan_resend(fc->chan); + return 0; +} + +/* + * Increments the fuse connection epoch. This will result of dentries from + * previous epochs to be invalidated. Additionally, if inval_wq is set, a work + * queue is scheduled to trigger the invalidation. + */ +static int fuse_notify_inc_epoch(struct fuse_conn *fc) +{ + atomic_inc(&fc->epoch); + if (inval_wq) + schedule_work(&fc->epoch_work); + + return 0; +} + +static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_prune_out outarg; + const unsigned int batch = 512; + u64 *nodeids __free(kfree) = kmalloc(sizeof(u64) * batch, GFP_KERNEL); + unsigned int num, i; + int err; + + if (!nodeids) + return -ENOMEM; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (size - sizeof(outarg) != array_size(outarg.count, sizeof(u64))) + return -EINVAL; + + for (; outarg.count; outarg.count -= num) { + num = min(batch, outarg.count); + err = fuse_copy_one(cs, nodeids, num * sizeof(u64)); + if (err) + return err; + + scoped_guard(rwsem_read, &fc->killsb) { + for (i = 0; i < num; i++) + fuse_try_prune_one_inode(fc, nodeids[i]); + } + } + return 0; +} + +int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, + unsigned int size, struct fuse_copy_state *cs) +{ + switch (code) { + case FUSE_NOTIFY_POLL: + return fuse_notify_poll(fc, size, cs); + + case FUSE_NOTIFY_INVAL_INODE: + return fuse_notify_inval_inode(fc, size, cs); + + case FUSE_NOTIFY_INVAL_ENTRY: + return fuse_notify_inval_entry(fc, size, cs); + + case FUSE_NOTIFY_STORE: + return fuse_notify_store(fc, size, cs); + + case FUSE_NOTIFY_RETRIEVE: + return fuse_notify_retrieve(fc, size, cs); + + case FUSE_NOTIFY_DELETE: + return fuse_notify_delete(fc, size, cs); + + case FUSE_NOTIFY_RESEND: + return fuse_notify_resend(fc); + + case FUSE_NOTIFY_INC_EPOCH: + return fuse_notify_inc_epoch(fc); + + case FUSE_NOTIFY_PRUNE: + return fuse_notify_prune(fc, size, cs); + + default: + return -EINVAL; + } +} From e6620208bdd346a01262ef68b76aaf4e8f1667a1 Mon Sep 17 00:00:00 2001 From: Alberto Ruiz Date: Wed, 8 Apr 2026 17:23:40 +0200 Subject: [PATCH 0575/3297] fuse: fix device node leak in cuse_process_init_reply() commit 9fa4f7a53406430ee9982f2f636a15b338185122 upstream. If device_add() succeeds during CUSE initialization but a subsequent step (cdev_alloc() or cdev_add()) fails, the error path calls put_device() without first calling device_del(). This leaks the devtmpfs entry created by device_add(), leaving a stale /dev/ node that persists until reboot. Since the cuse_conn is never linked into cuse_conntbl on the failure path, cuse_channel_release() sees cc->dev == NULL and skips device_unregister(), so no other code path cleans up the node. This has several consequences: - The device name is permanently poisoned: any subsequent attempt to create a CUSE device with the same name hits the stale sysfs entry, device_add() fails, and the new device is aborted. - The collision manifests as ENODEV returned to userspace with no dmesg diagnostic, making it very difficult to debug. - The failure is self-perpetuating: once a name is leaked, all future attempts with that name fail identically. Fix this by introducing an err_dev label that calls device_del() to undo device_add() before falling through to err_unlock. The existing err_unlock path from a device_add() failure correctly skips device_del() since the device was never added. Testing instructions can be found at the lore link below. Link: https://lore.kernel.org/all/20260408-wip-cuse-leak-fix-v1-0-1c028d575e97@redhat.com/ Signed-off-by: Alberto Ruiz Fixes: 151060ac1314 ("CUSE: implement CUSE - Character device in Userspace") Cc: stable@vger.kernel.org Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/cuse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 28c96961e85d1..2bb98bf81b079 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -391,7 +391,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm, rc = -ENOMEM; cdev = cdev_alloc(); if (!cdev) - goto err_unlock; + goto err_dev; cdev->owner = THIS_MODULE; cdev->ops = &cuse_frontend_fops; @@ -417,6 +417,8 @@ static void cuse_process_init_reply(struct fuse_mount *fm, err_cdev: cdev_del(cdev); +err_dev: + device_del(dev); err_unlock: mutex_unlock(&cuse_lock); put_device(dev); From be353caffa8640f5e25fb3714ce8b0cef5e410e5 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Mon, 18 May 2026 22:28:07 -0700 Subject: [PATCH 0576/3297] fuse: re-lock request before returning from fuse_ref_folio() commit b5befa80fdbe287a98480effed9564712924add5 upstream. fuse_ref_folio() unlocks the request but does not re-lock it before returning. fuse_chan_abort() can end the request and the async end callback (eg fuse_writepage_free()) can free the args while the subsequent copy chain logic after fuse_ref_folio() accesses them, leading to use-after-free issues. Fix this by locking the request in fuse_ref_folio() before returning. Fixes: c3021629a0d8 ("fuse: support splice() reading from fuse device") Cc: stable@vger.kernel.org Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 403fc18ee3627..cdaa50ac49ae6 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1106,7 +1106,7 @@ static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio, cs->nr_segs++; cs->len = 0; - return 0; + return lock_request(cs->req); } /* From 096cb2e58a6db23015f25b3b95e8d0e1bf112020 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Wed, 8 Apr 2026 10:25:10 -0700 Subject: [PATCH 0577/3297] fuse: fix io-uring background queue dispatch on request completion commit 31da059891bd3be9c6e59280b8e1777ead90db34 upstream. When a background request completes via the io_uring path, the background queue gets flushed to dispatch pending background requests, but this is done before the connection-level background counters (fc->num_background, fc->active_background) are properly accounted, which may reduce effective queue depth to one. The connection-level counters are decremented in fuse_request_end(), but flush_bg_queue() flushes the /dev/fuse path queue (fc->bg_queue), not the io_uring per-queue bg one, which means pending uring background requests on the queue are never dispatched in this path. Fix this by accounting the connection-level background counters first before flushing the queue's background queue. Since fuse_request_bg_finish() clears FR_BACKGROUND, fuse_request_end() will skip the background cleanup branch entirely, which avoids any double-decrements; it will call the wake_up(&req->waitq) branch but this is effectively a no-op as background requests have no waiters on req->waitq. Reviewed-by: Bernd Schubert Fixes: 857b0263f30e ("fuse: Allow to queue bg requests through io-uring") Cc: stable@vger.kernel.org Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 41 ++++++++++++++++++++++++----------------- fs/fuse/dev_uring.c | 1 + fs/fuse/fuse_dev_i.h | 1 + 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index cdaa50ac49ae6..e6d366f6b95d6 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -447,6 +447,29 @@ static void flush_bg_queue(struct fuse_conn *fc) } } +void fuse_request_bg_finish(struct fuse_conn *fc, struct fuse_req *req) +{ + lockdep_assert_held(&fc->bg_lock); + + clear_bit(FR_BACKGROUND, &req->flags); + if (fc->num_background == fc->max_background) { + fc->blocked = 0; + wake_up(&fc->blocked_waitq); + } else if (!fc->blocked) { + /* + * Wake up next waiter, if any. It's okay to use + * waitqueue_active(), as we've already synced up + * fc->blocked with waiters with the wake_up() call + * above. + */ + if (waitqueue_active(&fc->blocked_waitq)) + wake_up(&fc->blocked_waitq); + } + + fc->num_background--; + fc->active_background--; +} + /* * This function is called when a request is finished. Either a reply * has arrived or it was aborted (and not yet sent) or some error @@ -479,23 +502,7 @@ void fuse_request_end(struct fuse_req *req) WARN_ON(test_bit(FR_SENT, &req->flags)); if (test_bit(FR_BACKGROUND, &req->flags)) { spin_lock(&fc->bg_lock); - clear_bit(FR_BACKGROUND, &req->flags); - if (fc->num_background == fc->max_background) { - fc->blocked = 0; - wake_up(&fc->blocked_waitq); - } else if (!fc->blocked) { - /* - * Wake up next waiter, if any. It's okay to use - * waitqueue_active(), as we've already synced up - * fc->blocked with waiters with the wake_up() call - * above. - */ - if (waitqueue_active(&fc->blocked_waitq)) - wake_up(&fc->blocked_waitq); - } - - fc->num_background--; - fc->active_background--; + fuse_request_bg_finish(fc, req); flush_bg_queue(fc); spin_unlock(&fc->bg_lock); } else { diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index f0846694822dd..b63892a548108 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -90,6 +90,7 @@ static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req, if (test_bit(FR_BACKGROUND, &req->flags)) { queue->active_background--; spin_lock(&fc->bg_lock); + fuse_request_bg_finish(fc, req); fuse_uring_flush_bg(queue); spin_unlock(&fc->bg_lock); } diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h index 134bf44aff0d3..7da505af6d35e 100644 --- a/fs/fuse/fuse_dev_i.h +++ b/fs/fuse/fuse_dev_i.h @@ -59,6 +59,7 @@ unsigned int fuse_req_hash(u64 unique); struct fuse_req *fuse_request_find(struct fuse_pqueue *fpq, u64 unique); void fuse_dev_end_requests(struct list_head *head); +void fuse_request_bg_finish(struct fuse_conn *fc, struct fuse_req *req); void fuse_copy_init(struct fuse_copy_state *cs, bool write, struct iov_iter *iter); From 7366e6f4d2b4c7002b13fb01219e83679dad4127 Mon Sep 17 00:00:00 2001 From: Ji'an Zhou Date: Tue, 9 Jun 2026 09:58:51 +0000 Subject: [PATCH 0578/3297] fuse: clear intr_entry in fuse_resend and fuse_remove_pending_req commit f8fce75fedf73ac72aa09163deb8f4291fdcaad2 upstream. When fuse_resend() moves a request from fpq->processing back to fiq->pending, it sets FR_PENDING and clears FR_SENT but does not remove the requests intr_entry from fiq->interrupts. If the request had FR_INTERRUPTED set from a prior signal, intr_entry remains dangling on fiq->interrupts. When the requesting task then receives a fatal signal, fuse_remove_pending_req() sees FR_PENDING=1, removes the request from fiq->pending and frees it via the refcount path, also without cleaning intr_entry. The stale intr_entry causes use-after-free when fuse_read_interrupt() iterates fiq->interrupts: - list_del_init(&req->intr_entry) -> UAF write on freed slab - req->in.h.unique -> UAF read, data leaked to userspace Remove intr_entry from fiq->interrupts in fuse_resend() for interrupted requests before they are placed back on fiq->pending. Add a WARN_ON if the intr_entry is not empty on request destruction. Fixes: 760eac73f9f6 ("fuse: Introduce a new notification type for resend pending requests") Cc: stable@vger.kernel.org # 6.9 Signed-off-by: Ji'an Zhou Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index e6d366f6b95d6..77c3982bca3d7 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -148,6 +148,7 @@ static struct fuse_req *fuse_request_alloc(struct fuse_mount *fm, gfp_t flags) static void fuse_request_free(struct fuse_req *req) { + WARN_ON(!list_empty(&req->intr_entry)); kmem_cache_free(fuse_req_cachep, req); } @@ -2046,6 +2047,14 @@ static void fuse_resend(struct fuse_conn *fc) fuse_dev_end_requests(&to_queue); return; } + /* + * Remove interrupt entries for resent requests to prevent stale + * intr_entry on fiq->interrupts after the request is re-queued. + */ + list_for_each_entry(req, &to_queue, list) { + if (test_bit(FR_INTERRUPTED, &req->flags)) + list_del_init(&req->intr_entry); + } /* iq and pq requests are both oldest to newest */ list_splice(&to_queue, &fiq->pending); fuse_dev_wake_and_unlock(fiq); From 0483fffdeeb363f320e6bf5fc0f0306007507306 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 5 Jun 2026 12:27:06 -0700 Subject: [PATCH 0579/3297] fuse-uring: fix EFAULT clobber in fuse_uring_commit commit 3a0a8bc51a13951c5141262bf770eeea3e0b6228 upstream. copy_from_user() returns the number of bytes not copied as an unsigned residual on failure (1..sizeof(struct fuse_out_header)). fuse_uring_commit stores that residual in ssize_t err, sets req->out.h.error to -EFAULT, then jumps to out: with err still holding the positive residual. err = copy_from_user(&req->out.h, &ent->headers->in_out, sizeof(req->out.h)); if (err) { req->out.h.error = -EFAULT; goto out; /* err is the positive residual */ } ... out: fuse_uring_req_end(ent, req, err); fuse_uring_req_end() then runs if (error) req->out.h.error = error; which overwrites the just-assigned -EFAULT with the positive residual. FUSE callers such as fuse_simple_request() test err < 0 to detect failure, so the positive value is interpreted as success and the caller proceeds with an uninitialised or partial req->out.args. Fix by assigning err = -EFAULT in the failure branch before jumping to out, so fuse_uring_req_end() receives a negative errno and sets req->out.h.error to -EFAULT. Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support") Cc: stable@vger.kernel.org Reviewed-by: Joanne Koong Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Reviewed-by: Bernd Schubert Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index b63892a548108..7d04fcf40a2ba 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -818,14 +818,11 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, { struct fuse_ring *ring = ent->queue->ring; struct fuse_conn *fc = ring->fc; - ssize_t err = 0; + ssize_t err = -EFAULT; - err = copy_from_user(&req->out.h, &ent->headers->in_out, - sizeof(req->out.h)); - if (err) { - req->out.h.error = -EFAULT; + if (copy_from_user(&req->out.h, &ent->headers->in_out, + sizeof(req->out.h))) goto out; - } err = fuse_uring_out_header_has_err(&req->out.h, req, fc); if (err) { From b156bb9966972122b148acab8bdf415cdb8176a3 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 5 Jun 2026 12:27:07 -0700 Subject: [PATCH 0580/3297] fuse-uring: fix data races on ring->ready commit 46725a0056c884cf58a6897f222892807327d82d upstream. On weakly-ordered architectures, the store to fiq->ops can be reordered past the store to ring->ready, allowing a CPU that sees ring->ready == true via fuse_uring_ready() to dispatch requests through a stale fiq->ops pointer. Upgrade the store to smp_store_release() and the load in fuse_uring_ready() to smp_load_acquire() so that the preceding WRITE_ONCE(fiq->ops, ...) is visible to any CPU that observes ring->ready == true. Additionally, fuse_uring_do_register() publishes ring->ready with WRITE_ONCE() but the fast-path check reads it with a plain load. This is a marked-vs-unmarked access that KCSAN will flag. Wrap it in READ_ONCE() to mark it without adding unnecessary ordering. Also wrap the fc->ring load in fuse_uring_ready() in READ_ONCE() to prevent the compiler from reloading it between the NULL check and the dereference. Fixes: c2c9af9a0b13 ("fuse: Allow to queue fg requests through io-uring") Cc: stable@vger.kernel.org Reviewed-by: Joanne Koong Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Reviewed-by: Bernd Schubert Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 4 ++-- fs/fuse/dev_uring_i.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 7d04fcf40a2ba..3b6dd10732c33 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -992,12 +992,12 @@ static void fuse_uring_do_register(struct fuse_ring_ent *ent, fuse_uring_ent_avail(ent, queue); spin_unlock(&queue->lock); - if (!ring->ready) { + if (!READ_ONCE(ring->ready)) { bool ready = is_ring_ready(ring, queue->qid); if (ready) { WRITE_ONCE(fiq->ops, &fuse_io_uring_ops); - WRITE_ONCE(ring->ready, true); + smp_store_release(&ring->ready, true); wake_up_all(&fc->blocked_waitq); } } diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h index 51a563922ce14..531233e03e546 100644 --- a/fs/fuse/dev_uring_i.h +++ b/fs/fuse/dev_uring_i.h @@ -169,7 +169,9 @@ static inline void fuse_uring_wait_stopped_queues(struct fuse_conn *fc) static inline bool fuse_uring_ready(struct fuse_conn *fc) { - return fc->ring && fc->ring->ready; + struct fuse_ring *ring = READ_ONCE(fc->ring); + + return ring && smp_load_acquire(&ring->ready); } #else /* CONFIG_FUSE_IO_URING */ From 50f3e03db823cabc41fe35c27d77c2bdb112baad Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Mon, 8 Jun 2026 12:21:49 -0700 Subject: [PATCH 0581/3297] fuse-uring: fix moving cancelled entry to ent_in_userspace list commit 198f45eeb9f78b2a2d6d8be95e4e43468eb2c6bc upstream. fuse_uring_cancel() moves entries that are available (these have no reqs attached) to the ent_in_userspace list. ent_list_request_expired() checks the first entry on ent_in_userspace and dereferences ent->fuse_req unconditionally, which will crash on a cancelled entry that was moved to this list. Fix this by freeing the entry and dropping queue_refs directly in fuse_uring_cancel(). This is safe because cancel is the cancel handler itself - after io_uring_cmd_done(), no more cancels will be dispatched for this command, and teardown serializes with cancel via queue->lock. Since cancel now decrements queue_refs, fuse_uring_abort() must no longer gate fuse_uring_abort_end_requests() on queue_refs > 0, as cancelled entries may have already dropped queue_refs while requests are still queued. Remove the gate so abort always flushes requests and stops queues. Reported-by: Heechan Kang Tested-by: Heechan Kang Reviewed-by: Bernd Schubert Fixes: 4fea593e625c ("fuse: optimize over-io-uring request expiration check") Cc: stable@vger.kernel.org Suggested-by: Jian Huang Li Suggested-by: Horst Birthelmer Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 6 ++++-- fs/fuse/dev_uring_i.h | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 3b6dd10732c33..500b6bb5b4dd9 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -512,8 +512,7 @@ static void fuse_uring_cancel(struct io_uring_cmd *cmd, queue = ent->queue; spin_lock(&queue->lock); if (ent->state == FRRS_AVAILABLE) { - ent->state = FRRS_USERSPACE; - list_move_tail(&ent->list, &queue->ent_in_userspace); + list_del_init(&ent->list); need_cmd_done = true; ent->cmd = NULL; } @@ -522,6 +521,9 @@ static void fuse_uring_cancel(struct io_uring_cmd *cmd, if (need_cmd_done) { /* no queue lock to avoid lock order issues */ io_uring_cmd_done(cmd, -ENOTCONN, issue_flags); + kfree(ent); + if (atomic_dec_and_test(&queue->ring->queue_refs)) + wake_up_all(&queue->ring->stop_waitq); } } diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h index 531233e03e546..054ad4fdd7491 100644 --- a/fs/fuse/dev_uring_i.h +++ b/fs/fuse/dev_uring_i.h @@ -152,10 +152,10 @@ static inline void fuse_uring_abort(struct fuse_conn *fc) if (ring == NULL) return; - if (atomic_read(&ring->queue_refs) > 0) { - fuse_uring_abort_end_requests(ring); + fuse_uring_abort_end_requests(ring); + + if (atomic_read(&ring->queue_refs) > 0) fuse_uring_stop_queues(ring); - } } static inline void fuse_uring_wait_stopped_queues(struct fuse_conn *fc) From bb476ef8e1027a9d509fbaaf81f5061a07e9e5a7 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Mon, 8 Jun 2026 17:28:55 -0700 Subject: [PATCH 0582/3297] fuse-uring: end fuse_req on io-uring cancel task work commit bea4fe98204b6ce7eb8e29f7bf867dd7619b3ddd upstream. When io_uring delivers task work with tw.cancel set (PF_EXITING, PF_KTHREAD fallback, or percpu_ref_is_dying on the ring context), fuse_uring_send_in_task() takes the cancel branch, assigns -ECANCELED, and falls through to fuse_uring_send(). That path only flips the entry to FRRS_USERSPACE and completes the io_uring cmd; it never discharges the ring entry's owning reference to the fuse_req that fuse_uring_add_req_to_ring_ent() handed it at dispatch time. fuse_uring_send_in_task() tw.cancel == true err = -ECANCELED fuse_uring_send(ent, cmd, err, issue_flags) ent->state = FRRS_USERSPACE list_move(&ent->list, &queue->ent_in_userspace) ent->cmd = NULL io_uring_cmd_done(-ECANCELED) /* ent->fuse_req still set, req still hashed */ The fuse_req stays linked on fpq->processing[hash] and fuse_request_end() is never invoked. The originating syscall thread blocks in D-state in request_wait_answer() until fuse_abort_conn() runs, which can be the entire connection lifetime. For FR_BACKGROUND requests fc->num_background is never decremented either, so repeated cancels inflate the counter until max_background is hit and all later background ops stall. tw.cancel does not imply a connection abort (e.g. a single io_uring worker thread exits while the fuse connection stays up), so this cannot be left for fuse_abort_conn() to clean up. Ending the req but still routing the entry through fuse_uring_send() is not enough: that leaves a req-less entry on ent_in_userspace, and ent_list_request_expired() dereferences ent->fuse_req unconditionally on the head of that list, which would then NULL-deref. Fix the cancel branch to release the entry directly. Remove it from the queue, complete the io_uring cmd, end the fuse_req, free the entry, and drop its queue_refs (waking the teardown waiter if it was the last). Fixes: c2c9af9a0b13 ("fuse: Allow to queue fg requests through io-uring") Cc: stable@vger.kernel.org Reviewed-by: Joanne Koong Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 500b6bb5b4dd9..21989f9b4d0d6 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -1226,11 +1226,21 @@ static void fuse_uring_send_in_task(struct io_uring_cmd *cmd, fuse_uring_next_fuse_req(ent, queue, issue_flags); return; } + fuse_uring_send(ent, cmd, err, issue_flags); } else { err = -ECANCELED; - } - fuse_uring_send(ent, cmd, err, issue_flags); + spin_lock(&queue->lock); + list_del_init(&ent->list); + spin_unlock(&queue->lock); + + io_uring_cmd_done(cmd, err, issue_flags); + + fuse_uring_req_end(ent, ent->fuse_req, err); + kfree(ent); + if (atomic_dec_and_test(&queue->ring->queue_refs)) + wake_up_all(&queue->ring->stop_waitq); + } } static struct fuse_ring_queue *fuse_uring_task_to_queue(struct fuse_ring *ring) From 23a356e0bd96c8d5fb3ddff069f692bf10cab5c1 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 8 Jun 2026 23:03:43 +0200 Subject: [PATCH 0583/3297] fuse-uring: Avoid use-after-free in fuse_uring_async_stop_queues commit d351da75066955144515cb2f9aa959f24a04287a upstream. fuse_uring_async_stop_queues() might run when the last reference on ring->queue_refs was already dropped. In order to avoid an early destruction a reference on struct fuse_conn is now taken before starting fuse_uring_async_stop_queues() and that reference is only released when that delayed work queue terminates. Fixes: 4a9bfb9b6850 ("fuse: {io-uring} Handle teardown of ring entries") Cc: stable@kernel.org # 6.14 Reported-by: Berkant Koc Signed-off-by: Bernd Schubert Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 21989f9b4d0d6..3b76f9c36cb0e 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -462,6 +462,7 @@ static void fuse_uring_async_stop_queues(struct work_struct *work) FUSE_URING_TEARDOWN_INTERVAL); } else { wake_up_all(&ring->stop_waitq); + fuse_conn_put(ring->chan->conn); } } @@ -482,6 +483,7 @@ void fuse_uring_stop_queues(struct fuse_ring *ring) } if (atomic_read(&ring->queue_refs) > 0) { + fuse_conn_get(ring->chan->conn); ring->teardown_time = jiffies; INIT_DELAYED_WORK(&ring->async_teardown_work, fuse_uring_async_stop_queues); From 39c8e925b207afceffaa5382416ed405e0223a03 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 8 Jun 2026 23:03:44 +0200 Subject: [PATCH 0584/3297] fuse-uring: Avoid queue->stopped races and set/read that value under lock commit b70a3aca16934c196f92abb17b01c1647b9bb63c upstream. There are several readers of queue->stopped that check the value under lock, but fuse_uring_commit_fetch() did not and actually the value was not set under the lock in fuse_uring_abort_end_requests() either. Especially in fuse_uring_commit_fetch it is important to check under a lock, because due to races 'struct fuse_req' might be freed with fuse_request_end, but another thread/cpu might already do teardown work. Cc: stable@kernel.org # 6.14 Fixes: 4a9bfb9b6850fec ("fuse: {io-uring} Handle teardown of ring entries") Reported-by: Berkant Koc Reported-by: xlabai Signed-off-by: Bernd Schubert Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 3b76f9c36cb0e..7118280ccb1fa 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -131,10 +131,9 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring) if (!queue) continue; - queue->stopped = true; - WARN_ON_ONCE(ring->fc->max_background != UINT_MAX); spin_lock(&queue->lock); + queue->stopped = true; spin_lock(&fc->bg_lock); fuse_uring_flush_bg(queue); spin_unlock(&fc->bg_lock); @@ -462,7 +461,7 @@ static void fuse_uring_async_stop_queues(struct work_struct *work) FUSE_URING_TEARDOWN_INTERVAL); } else { wake_up_all(&ring->stop_waitq); - fuse_conn_put(ring->chan->conn); + fuse_conn_put(ring->fc); } } @@ -483,7 +482,7 @@ void fuse_uring_stop_queues(struct fuse_ring *ring) } if (atomic_read(&ring->queue_refs) > 0) { - fuse_conn_get(ring->chan->conn); + fuse_conn_get(ring->fc); ring->teardown_time = jiffies; INIT_DELAYED_WORK(&ring->async_teardown_work, fuse_uring_async_stop_queues); @@ -903,10 +902,15 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, return err; fpq = &queue->fpq; - if (!READ_ONCE(fc->connected) || READ_ONCE(queue->stopped)) + if (!READ_ONCE(fc->connected)) return err; spin_lock(&queue->lock); + if (unlikely(queue->stopped)) { + spin_unlock(&queue->lock); + return err; + } + /* Find a request based on the unique ID of the fuse request * This should get revised, as it needs a hash calculation and list * search. And full struct fuse_pqueue is needed (memory overhead). From e1711479e9068ea31b31353a702a51e639c3d059 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 8 Jun 2026 23:03:45 +0200 Subject: [PATCH 0585/3297] fuse-uring: make a fuse_req on SQE commit only findable after memcpy commit 1efd3d474fc0ba74dfd984249bca78807d739812 upstream. Bad userspace might try to trick us and send commit SQEs request unique / commit-id of requests that are not even send to fuse-server (io_uring_cmd_done() not called) yet. fuse_uring_commit_fetch() ends the fuse request when the ring entry has a wrong state, but that could have caused a use-after-free with the memcpy operations in fuse_uring_send_in_task(). In order to avoid such races the call of fuse_uring_add_to_pq() is moved after the copy operations and just before completing the io-uring request - malicious userspace cannot find the request anymore until all prepration work in fuse-client/kernel is completed. This also moves fuse_uring_add_to_pq() a bit up in the code to avoid a forward declaration. Also not with a preparation commit, to make it easier to back port to older kernels. Reported-by: xlabai Reported-by: Berkant Koc Fixes: c090c8abae4b6b ("fuse: Add io-uring sqe commit and fetch support") Cc: stable@kernel.org # 6.14 Signed-off-by: Bernd Schubert Reviewed-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 7118280ccb1fa..78a840dd7b9f2 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -718,6 +718,19 @@ static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, return err; } +/* Used to find the request on SQE commit */ +static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent) +{ + struct fuse_ring_queue *queue = ent->queue; + struct fuse_pqueue *fpq = &queue->fpq; + unsigned int hash; + struct fuse_req *req = ent->fuse_req; + + req->ring_entry = ent; + hash = fuse_req_hash(req->in.h.unique); + list_move_tail(&req->list, &fpq->processing[hash]); +} + /* * Write data to the ring buffer and send the request to userspace, * userspace will read it @@ -740,6 +753,7 @@ static int fuse_uring_send_next_to_ring(struct fuse_ring_ent *ent, ent->cmd = NULL; ent->state = FRRS_USERSPACE; list_move_tail(&ent->list, &queue->ent_in_userspace); + fuse_uring_add_to_pq(ent); spin_unlock(&queue->lock); io_uring_cmd_done(cmd, 0, issue_flags); @@ -757,19 +771,6 @@ static void fuse_uring_ent_avail(struct fuse_ring_ent *ent, ent->state = FRRS_AVAILABLE; } -/* Used to find the request on SQE commit */ -static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent, - struct fuse_req *req) -{ - struct fuse_ring_queue *queue = ent->queue; - struct fuse_pqueue *fpq = &queue->fpq; - unsigned int hash; - - req->ring_entry = ent; - hash = fuse_req_hash(req->in.h.unique); - list_move_tail(&req->list, &fpq->processing[hash]); -} - /* * Assign a fuse queue entry to the given entry */ @@ -787,10 +788,13 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent, } clear_bit(FR_PENDING, &req->flags); + + /* Until fuse_uring_add_to_pq() the req is not attached to any list */ + list_del_init(&req->list); + ent->fuse_req = req; ent->state = FRRS_FUSE_REQ; list_move_tail(&ent->list, &queue->ent_w_req_queue); - fuse_uring_add_to_pq(ent, req); } /* Fetch the next fuse request if available */ @@ -1209,6 +1213,7 @@ static void fuse_uring_send(struct fuse_ring_ent *ent, struct io_uring_cmd *cmd, ent->state = FRRS_USERSPACE; list_move_tail(&ent->list, &queue->ent_in_userspace); ent->cmd = NULL; + fuse_uring_add_to_pq(ent); spin_unlock(&queue->lock); io_uring_cmd_done(cmd, ret, issue_flags); From 0b466cf1b96e191b06b496c4de79da15315c3a9a Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Tue, 9 Jun 2026 14:36:58 -0700 Subject: [PATCH 0586/3297] fuse-uring: remove request-less entries from ent_w_req_queue to fix NULL deref commit 1c57a69be962d459c5e705f5cb4355b841b3461c upstream. If a copy into the userspace ring buffer fails, a request will be terminated and fuse_uring_req_end() will set ent->fuse_req to NULL but it will leave the entry on ent_w_req_queue in FRRS_FUSE_REQ state. This can lead to a NULL deref if the request expiration logic scans ent_w_req_queue in the window before the entry is moved off it. Fix this by taking the entry off ent_w_req_queue and changing its state from FRRS_FUSE_REQ to FRRS_INVALID before terminating the request. Fixes: 4fea593e625c ("fuse: optimize over-io-uring request expiration check") Cc: stable@kernel.org Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 78a840dd7b9f2..42c82b5476593 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -710,10 +710,20 @@ static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, int err; err = fuse_uring_copy_to_ring(ent, req); - if (!err) + if (!err) { set_bit(FR_SENT, &req->flags); - else + } else { + /* + * Copying the request failed. Remove the entry from the + * ent_w_req_queue list and terminate the request + */ + spin_lock(&ent->queue->lock); + list_del_init(&ent->list); + ent->state = FRRS_INVALID; + spin_unlock(&ent->queue->lock); + fuse_uring_req_end(ent, req, err); + } return err; } From 75e1d2787005d0bb6ea9dd2ed41a41f2bdceb957 Mon Sep 17 00:00:00 2001 From: Adam Li Date: Sat, 11 Oct 2025 06:43:22 +0000 Subject: [PATCH 0587/3297] sched/fair: Only update stats for allowed CPUs when looking for dst group commit 82d6e01a0699800efd8b048eb584c907ccb47b7a upstream. Load imbalance is observed when the workload frequently forks new threads. Due to CPU affinity, the workload can run on CPU 0-7 in the first group, and only on CPU 8-11 in the second group. CPU 12-15 are always idle. { 0 1 2 3 4 5 6 7 } {8 9 10 11 12 13 14 15} * * * * * * * * * * * * When looking for dst group for newly forked threads, in many times update_sg_wakeup_stats() reports the second group has more idle CPUs than the first group. The scheduler thinks the second group is less busy. Then it selects least busy CPUs among CPU 8-11. Therefore CPU 8-11 can be crowded with newly forked threads, at the same time CPU 0-7 can be idle. A task may not use all the CPUs in a schedule group due to CPU affinity. Only update schedule group statistics for allowed CPUs. Signed-off-by: Adam Li Signed-off-by: Peter Zijlstra (Intel) Cc: Erwan Velu Signed-off-by: Greg Kroah-Hartman --- kernel/sched/fair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7e0e2044d840b..b2f569d6c022d 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -10847,7 +10847,7 @@ static inline void update_sg_wakeup_stats(struct sched_domain *sd, if (sd->flags & SD_ASYM_CPUCAPACITY) sgs->group_misfit_task_load = 1; - for_each_cpu(i, sched_group_span(group)) { + for_each_cpu_and(i, sched_group_span(group), p->cpus_ptr) { struct rq *rq = cpu_rq(i); unsigned int local; From 9e04055ab5fc0470a0031ee6934739f9aa8f34a5 Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Fri, 19 Jun 2026 04:06:03 +0000 Subject: [PATCH 0588/3297] usb: gadget: f_fs: Initialize epfile->in early to fix endpoint direction checks commit 82cfd4739011bdc7e87b5d585703427e89ddfaa5 upstream. When parsing endpoint descriptors, ffs_data_got_descs() generates the eps_addrmap which contains the endpoint direction. However, epfile->in was previously only populated in ffs_func_eps_enable() which executes upon USB host connection. As a result, early userspace ioctls like FUNCTIONFS_DMABUF_ATTACH that run before the host connects would see epfile->in as 0, leading to incorrect DMA directions. By moving the initialization to ffs_epfiles_create(), epfile->in is accurate before userspace opens the endpoint files. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Cc: stable Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Neill Kapron Link: https://patch.msgid.link/20260619040609.4010746-2-nkapron@google.com 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 7cddf88589335..5ef9a7a7f3bc3 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2354,6 +2354,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else sprintf(epfile->name, "ep%u", i); + epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0; epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name, epfile, &ffs_epfile_operations); @@ -2440,7 +2441,6 @@ static int ffs_func_eps_enable(struct ffs_function *func) ret = usb_ep_enable(ep->ep); if (!ret) { epfile->ep = ep; - epfile->in = usb_endpoint_dir_in(ep->ep->desc); epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc); } else { break; From e697df336662b5b14e10a3419d5d8967acdc7977 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 16 Jun 2026 12:09:14 +0500 Subject: [PATCH 0589/3297] timekeeping: Register default clocksource before taking tk_core.lock commit 8fa30821180a9a19e78e9f4df1c0ba710252801e upstream. Commit f24df84cbe05 ("time/jiffies: Register jiffies clocksource before usage") moved the jiffies clocksource registration into clocksource_default_clock(), so that it is registered lazily on the first call. __clocksource_register() acquires clocksource_mutex, but the first caller is timekeeping_init(), which invokes clocksource_default_clock() while holding tk_core.lock, a raw spinlock. Acquiring a sleeping mutex while holding a raw spinlock is invalid. The default clocksource only has to be registered before tk_setup_internals() consumes its mult/shift/maxadj. Neither clocksource_default_clock(), the ->enable() callback, nor the registration itself need tk_core.lock, so fetch and enable the clock before acquiring the lock. This preserves the "register before usage" ordering while keeping clocksource_mutex out of the raw spinlock section. clocksource_default_clock() has a second caller, clocksource_done_booting(), which invokes it with clocksource_mutex already held. That path avoids a recursive lock because timekeeping_init() has already run and set cs_jiffies_registered, so the registration is skipped there. This change does not alter that; it only fixes the invalid wait context in timekeeping_init(). Fixes: f24df84cbe05 ("time/jiffies: Register jiffies clocksource before usage") Signed-off-by: Mikhail Gavrilov Signed-off-by: Thomas Gleixner Reported-by: Breno Leitao Reported-by: Oleg Nesterov Reviewed-by: Breno Leitao Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616070914.65818-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Greg Kroah-Hartman --- kernel/time/timekeeping.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index c7dcccc5f3d6b..889a8de3fc204 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1825,13 +1825,14 @@ void __init timekeeping_init(void) */ wall_to_mono = timespec64_sub(boot_offset, wall_time); + clock = clocksource_default_clock(); + if (clock->enable) + clock->enable(clock); + guard(raw_spinlock_irqsave)(&tk_core.lock); ntp_init(); - clock = clocksource_default_clock(); - if (clock->enable) - clock->enable(clock); tk_setup_internals(tks, clock); tk_set_xtime(tks, &wall_time); From 1c56c46519353112005c248982a7074c63da4926 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 12 Jun 2026 10:21:09 -0400 Subject: [PATCH 0590/3297] Bluetooth: 6lowpan: Fix using chan->conn as indication to no remote netdev commit d38eaf611839b85ade3dd3db309dbc8aaaaf0095 upstream. b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") don't reset the chan->conn to NULL anymore making the bt# netdev not be remove once the last l2cap_chan_del is removed. Instead of restoring the original behavior this remove the logic of keeping the interface after the last channel is removed because it never worked as intended and the l2cap_chan_del always detach its l2cap_conn which results in always removing the channel anyway. Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/6lowpan.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 038f01600eeba..5325c1b8dcd8b 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -777,20 +777,10 @@ static void chan_close_cb(struct l2cap_chan *chan) struct lowpan_btle_dev *dev = NULL; struct lowpan_peer *peer; int err = -ENOENT; - bool last = false, remove = true; + bool last = false; BT_DBG("chan %p conn %p", chan, chan->conn); - if (chan->conn && chan->conn->hcon) { - if (!is_bt_6lowpan(chan->conn->hcon)) - return; - - /* If conn is set, then the netdev is also there and we should - * not remove it. - */ - remove = false; - } - spin_lock(&devices_lock); list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { @@ -817,10 +807,8 @@ static void chan_close_cb(struct l2cap_chan *chan) ifdown(dev->netdev); - if (remove) { - INIT_WORK(&entry->delete_netdev, delete_netdev); - schedule_work(&entry->delete_netdev); - } + INIT_WORK(&entry->delete_netdev, delete_netdev); + schedule_work(&entry->delete_netdev); } else { spin_unlock(&devices_lock); } From 1991d49433e90b2202de2fe90be3c24161873d7c Mon Sep 17 00:00:00 2001 From: Zhaoyang Huang Date: Mon, 8 Jun 2026 17:09:39 +0800 Subject: [PATCH 0591/3297] Revert "f2fs: remove non-uptodate folio from the page cache in move_data_block" commit ccaba785821970f422c47770331c7e3271763f17 upstream. This reverts commit 9609dd704725a40cd63d915f2ab6c44248a44598. The kernel panics are keeping to be reported especially when the f2fs partition get almost full. By investigation, we find that the reason is one f2fs page got freed to buddy without being deleted from LRU and the root cause is the race happened in [2] which is enrolled by this commit. There are 3 race processes in this scenario, please find below for their main activities. The changed code in move_data_block() lets the GC path evict the tail-end folio from the page cache through folio_end_dropbehind(). Once folio_unmap_invalidate() removes the folio from mapping->i_pages, the page-cache references for all pages in the folio are dropped. The folio is then kept alive only by temporary external references, which allows a later split to operate on a folio whose subpages are no longer protected by page-cache references. After the page-cache references are gone, split_folio_to_order() can split the big folio into individual pages and put the resulting subpages back on the LRU. For tail pages beyond EOF, split removes them from the page cache and drops their page-cache references. A tail page can then remain on the LRU with PG_lru set while holding only the split caller's temporary reference. When free_folio_and_swap_cache() drops that final reference, the page enters the final folio_put() release path. In parallel, folio_isolate_lru() can observe the same tail page with a non-zero refcount and PG_lru set. It clears PG_lru before taking its own reference. If this races with the final folio_put() from the split path, __folio_put() sees PG_lru already cleared and skips lruvec_del_folio(). The page is then freed back to the allocator while its lru links are still present in the LRU list. A later LRU operation on a neighboring page detects the stale link and reports list corruption. [1] [ 22.486082] list_del corruption. next->prev should be fffffffec10e0ac8, but was dead000000000122. (next=fffffffec10e0a88) [ 22.486130] ------------[ cut here ]------------ [ 22.486134] kernel BUG at lib/list_debug.c:67! [ 22.486141] Internal error: Oops - BUG: 00000000f2000800 [#1] SMP [ 22.488502] Tainted: [W]=WARN, [O]=OOT_MODULE [ 22.488506] Hardware name: Spreadtrum UMS9230 1H10 SoC (DT) [ 22.488511] pstate: 604000c5 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 22.488517] pc : __list_del_entry_valid_or_report+0x14c/0x154 [ 22.488531] lr : __list_del_entry_valid_or_report+0x14c/0x154 [ 22.488539] sp : ffffffc08006b830 [ 22.488542] x29: ffffffc08006b868 x28: 0000000000003020 x27: 0000000000000000 [ 22.488553] x26: 0000000000000000 x25: 0000000000000004 x24: fffffffec10e0ac0 [ 22.488564] x23: 00000000000000e8 x22: 0000000000000024 x21: dead000000000122 [ 22.488574] x20: fffffffec10e0a88 x19: fffffffec10e0ac8 x18: ffffffc080061060 [ 22.488585] x17: 20747562202c3863 x16: 6130653031636566 x15: 0000000000000058 [ 22.488595] x14: 0000000000000004 x13: ffffff80f91e0000 x12: 0000000000000003 [ 22.488605] x11: 0000000000000003 x10: 0000000000000001 x9 : ffe85721f0e25f00 [ 22.488615] x8 : ffe85721f0e25f00 x7 : 0000000000000000 x6 : 6c65645f7473696c [ 22.488625] x5 : ffffffed39b23026 x4 : 0000000000000000 x3 : 0000000000000010 [ 22.488636] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 000000000000006d [ 22.488647] Call trace: [ 22.488651] __list_del_entry_valid_or_report+0x14c/0x154 (P) [ 22.488661] __folio_put+0x2bc/0x434 [ 22.488670] folio_put+0x28/0x58 [ 22.488678] do_garbage_collect+0x1a34/0x2584 [ 22.488689] f2fs_gc+0x230/0x9b4 [ 22.488697] f2fs_fallocate+0xb90/0xdf4 [ 22.488706] vfs_fallocate+0x1b4/0x2bc [ 22.488716] __arm64_sys_fallocate+0x44/0x78 [ 22.488725] invoke_syscall+0x58/0xe4 [ 22.488732] do_el0_svc+0x48/0xdc [ 22.488739] el0_svc+0x3c/0x98 [ 22.488747] el0t_64_sync_handler+0x20/0x130 [ 22.488754] el0t_64_sync+0x1c4/0x1c8 [2] CPU0 (f2fs GC) CPU1 (split_folio_to_order) CPU2 (folio_isolate_lru) F: pagecache refs = n F: extra refs = GC + split F: PG_lru set move_data_block() folio = f2fs_grab_cache_folio(F) ... __folio_set_dropbehind(F) folio_unlock(F) folio_end_dropbehind(F) folio_unmap_invalidate(F) __filemap_remove_folio(F) folio_put_refs(F, n) folio_put(F) split_folio_to_order(F) folio_ref_freeze(F, 1) ... lru_add_split_folio(T) list_add_tail(&T->lru, &F->lru) folio_set_lru(T) __filemap_remove_folio(T) folio_put_refs(T, 1) /* T refcount == 1, PageLRU set */ folio_isolate_lru(T) folio_test_clear_lru(T) free_folio_and_swap_cache(T) folio_put(T) /* refcount: 1 -> 0 */ __folio_put(T) __page_cache_release(T) folio_test_lru(T) == false /* skip lruvec_del_folio(T) */ free_frozen_pages(T) folio_get(T) lruvec_del_folio(T) later: list_del(adjacent->lru) next == &T->lru next->prev == LIST_POISON / PCP freelist BUG Cc: stable@vger.kernel.org Fixes: 9609dd704725 ("f2fs: remove non-uptodate folio from the page cache in move_data_block") Signed-off-by: Zhaoyang Huang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/gc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 7219a87955b20..3d41c6348b3c1 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1487,11 +1487,7 @@ static int move_data_block(struct inode *inode, block_t bidx, put_out: f2fs_put_dnode(&dn); out: - if (!folio_test_uptodate(folio)) - __folio_set_dropbehind(folio); - folio_unlock(folio); - folio_end_dropbehind(folio); - folio_put(folio); + f2fs_folio_put(folio, true); out_iput: if (atomic_inode) iput(atomic_inode); From fdafa1e68dc75045b7b617e6e7d2854950804d83 Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Sat, 11 Jul 2026 22:33:26 +0900 Subject: [PATCH 0592/3297] smb: client: reject overlapping data areas in SMB2 responses commit 8986c932905ea508d66da421eb2eb6e676ace1fe upstream. Commit 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") restricted the implied bcc[0] length exception to responses without a data area. However, the overlap handling in __smb2_calc_size() clears data_length, which can make an invalid response appear to have no data area and so qualify for the exception. Track data area overlap separately and reject such responses before applying the length compatibility exceptions. Fixes: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") Cc: stable@vger.kernel.org Signed-off-by: Shoichiro Miyamoto Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2misc.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 005a820946da1..c0c0ac3342570 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -19,7 +19,8 @@ #include "nterr.h" #include "cached_dir.h" -static unsigned int __smb2_calc_size(void *buf, bool *have_data); +static unsigned int __smb2_calc_size(void *buf, bool *have_data, + bool *data_area_overlap); static int check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) @@ -147,6 +148,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) __u32 calc_len; /* calculated length */ __u64 mid; bool have_data; + bool data_area_overlap; /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; @@ -231,7 +233,12 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) } have_data = false; - calc_len = __smb2_calc_size(buf, &have_data); + data_area_overlap = false; + calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap); + + /* Reject responses whose data area overlaps the fixed area. */ + if (data_area_overlap) + return 1; /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */ @@ -415,14 +422,15 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) } /* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - * If have_data is non-NULL, it is set to true when a non-empty data area was - * found (data_length > 0), allowing callers to distinguish the implied bcc[0] - * case (no data area) from an overreported data length. + * Calculate the size of the SMB message based on the fixed header, fixed + * parameter area, and variable data area. + * + * If have_data is not NULL, it is set when a non-empty data area is found. + * If data_area_overlap is not NULL, it is set when the data area overlaps + * the fixed area. */ static unsigned int -__smb2_calc_size(void *buf, bool *have_data) +__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap) { struct smb2_pdu *pdu = buf; struct smb2_hdr *shdr = &pdu->hdr; @@ -431,6 +439,11 @@ __smb2_calc_size(void *buf, bool *have_data) /* Structure Size has already been checked to make sure it is 64 */ int len = le16_to_cpu(shdr->StructureSize); + if (have_data) + *have_data = false; + if (data_area_overlap) + *data_area_overlap = false; + /* * StructureSize2, ie length of fixed parameter area has already * been checked to make sure it is the correct length. @@ -453,7 +466,10 @@ __smb2_calc_size(void *buf, bool *have_data) if (offset + 1 < len) { cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", offset + 1, len); + if (data_area_overlap) + *data_area_overlap = true; data_length = 0; + goto calc_size_exit; } else { len = offset + data_length; } @@ -468,7 +484,7 @@ __smb2_calc_size(void *buf, bool *have_data) unsigned int smb2_calc_size(void *buf) { - return __smb2_calc_size(buf, NULL); + return __smb2_calc_size(buf, NULL, NULL); } /* Note: caller must free return buffer */ From dca861f2cc9e6fe991dd23567601d112ca2ccc4f Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Thu, 9 Jul 2026 17:24:07 +0200 Subject: [PATCH 0593/3297] xfs: fix null pointer dereference in tracepoint commit 9202ee546b0cd71004eed7598546efe4660097da upstream. If dfp is not NULL we exit early here, when dfp is NULL it's allocated in xfs_defer_alloc() but not assigned. The tracepoint tries to dereference members of dfp struct. Signed-off-by: Andrey Albershteyn Cc: stable@vger.kernel.org # v6.8 Fixes: 3f3cec031099c3 ("xfs: force small EFIs for reaping btree extents") Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_defer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index e8db2f6149e07..43f352d89172f 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -878,7 +878,7 @@ xfs_defer_add_barrier( if (dfp) return; - xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type); + dfp = xfs_defer_alloc(&tp->t_dfops, &xfs_barrier_defer_type); trace_xfs_defer_add_item(tp->t_mountp, dfp, NULL); } From d98f22d2e11e0a36493aeb25b2933571ee90d9a4 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:20:00 -0700 Subject: [PATCH 0594/3297] xfs: fail recovery on a committed log item with no regions commit 2094dab19d45c487285617b7b68913d0cc0c1211 upstream. If the first op of a transaction is a bare transaction header (len == sizeof(struct xfs_trans_header)), xlog_recover_add_to_trans() adds an item but no region, leaving it on r_itemq with ri_cnt == 0 and ri_buf == NULL. The header can be split across op records, so later ops may still add regions; the item is only invalid if the transaction commits with none. The runtime commit path never emits such a transaction, so this only happens on a crafted log. It came from an AI-assisted code audit of the recovery parser. xlog_recover_reorder_trans() calls ITEM_TYPE() on the item, which reads *(unsigned short *)item->ri_buf[0].iov_base and faults on the NULL ri_buf. Reject it there, before the commit handlers that also read ri_buf[0]. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:xlog_recover_reorder_trans (fs/xfs/xfs_log_recover.c:1836) xlog_recover_commit_trans (fs/xfs/xfs_log_recover.c:2043) xlog_recover_process_data (fs/xfs/xfs_log_recover.c:2501) xlog_do_recovery_pass (fs/xfs/xfs_log_recover.c:3244) xlog_recover (fs/xfs/xfs_log_recover.c:3493) xfs_log_mount (fs/xfs/xfs_log.c:618) xfs_mountfs (fs/xfs/xfs_mount.c:1034) xfs_fs_fill_super (fs/xfs/xfs_super.c:1938) vfs_get_tree (fs/super.c:1695) path_mount (fs/namespace.c:4161) __x64_sys_mount (fs/namespace.c:4367) Fixes: 89cebc847729 ("xfs: validate transaction header length on log recovery") Cc: stable@vger.kernel.org # v4.3 Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reviewed-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_log_recover.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index cc5ffa8e4bc2a..33cf2a31780c4 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1907,6 +1907,15 @@ xlog_recover_reorder_trans( list_for_each_entry_safe(item, n, &sort_list, ri_list) { enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST; + /* a committed item with no regions has a NULL ri_buf[0] */ + if (!item->ri_cnt || !item->ri_buf) { + xfs_warn(log->l_mp, + "%s: committed log item has no regions", + __func__); + error = -EFSCORRUPTED; + break; + } + item->ri_ops = xlog_find_item_ops(item); if (!item->ri_ops) { xfs_warn(log->l_mp, From 206c09b04dc5469c7ff14d8aceff2d47c88078d9 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:03:44 -0700 Subject: [PATCH 0595/3297] xfs: resample the data fork mapping after cycling ILOCK commit 2f4acd0fcd862e22eab45690ec2c08c80b6ef2e7 upstream. xfs_reflink_fill_{cow_hole,delalloc} are both presented with an inode, a data fork mapping, and a cow fork mapping. Unfortunately, these two helpers cycle the ILOCK to grab a transaction, which means that the mappings are stale as soon as we reacquire the ILOCK. Currently we refresh the cow fork mapping by re-calling xfs_find_trim_cow_extent, but we don't refresh the data fork mapping beforehand, which means that the xfs_bmap_trim_cow in that function queries the refcount btree about the wrong physical blocks and returns an inaccurate value in *shared. If *shared is now false, the directio write proceeds with a stale data fork mapping. Fix this by querying the data fork mapping if the sequence counter changes across the ILOCK cycle. Cc: hch@lst.de Cc: stable@vger.kernel.org # v4.11 Fixes: 3c68d44a2b49a0 ("xfs: allocate direct I/O COW blocks in iomap_begin") Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_reflink.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 3f177b4ec131d..d6cd7c1f00ff8 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -440,6 +440,7 @@ xfs_reflink_fill_cow_hole( struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; xfs_filblks_t resaligned; + unsigned int seq_before = READ_ONCE(ip->i_df.if_seq); unsigned int dblocks = 0, rblocks = 0; int nimaps; int error; @@ -465,6 +466,22 @@ xfs_reflink_fill_cow_hole( *lockmode = XFS_ILOCK_EXCL; + /* + * The data fork mapping may have changed while we dropped the ILOCK + * (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full + * CoW cycle including xfs_reflink_end_cow(), which remaps this offset + * and drops the refcount of the old shared block). Re-read it so the + * shared-status recheck below and the caller's in-place iomap both + * operate on the current mapping rather than a stale physical block. + */ + if (seq_before != READ_ONCE(ip->i_df.if_seq)) { + nimaps = 1; + error = xfs_bmapi_read(ip, imap->br_startoff, + imap->br_blockcount, imap, &nimaps, 0); + if (error) + goto out_trans_cancel; + } + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); if (error || !*shared) goto out_trans_cancel; @@ -511,6 +528,8 @@ xfs_reflink_fill_delalloc( bool found; do { + unsigned int seq_before = READ_ONCE(ip->i_df.if_seq); + xfs_iunlock(ip, *lockmode); *lockmode = 0; @@ -521,6 +540,23 @@ xfs_reflink_fill_delalloc( *lockmode = XFS_ILOCK_EXCL; + /* + * The data fork mapping may have changed while we dropped the + * ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can + * complete a full CoW cycle including xfs_reflink_end_cow(), + * which remaps this offset and drops the refcount of the old + * shared block). Re-read it so the shared-status recheck + * below and the caller's in-place iomap both operate on the + * current mapping rather than a stale physical block. + */ + if (seq_before != READ_ONCE(ip->i_df.if_seq)) { + nimaps = 1; + error = xfs_bmapi_read(ip, imap->br_startoff, + imap->br_blockcount, imap, &nimaps, 0); + if (error) + goto out_trans_cancel; + } + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); if (error || !*shared) From d1c4c40599c376aeb0c93068a2ae344e79ee4b90 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:55 -0700 Subject: [PATCH 0596/3297] xfs: don't wrap around quota ids in dqiterate commit d766e4e5e85d829629c3ba503802fe1303d7b591 upstream. LOLLM noticed that q_id is an unsigned 32-bit variable. If it happens to be set to XFS_DQ_ID_MAX due to a filesystem that actually has a dquot for ID_MAX, then this addition will truncate to zero and the iteration starts over. Fix this by casting to u64. Cc: stable@vger.kernel.org # v6.8 Fixes: 21d7500929c8a0 ("xfs: improve dquot iteration for scrub") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/dqiterate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/dqiterate.c b/fs/xfs/scrub/dqiterate.c index 20c4daedd48df..998b0d640d613 100644 --- a/fs/xfs/scrub/dqiterate.c +++ b/fs/xfs/scrub/dqiterate.c @@ -205,7 +205,7 @@ xchk_dquot_iter( if (error) return error; - cursor->id = dq->q_id + 1; + cursor->id = (uint64_t)dq->q_id + 1; *dqpp = dq; return 1; } From 104584477883b0fdb220f2c2892907394453da43 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:57 -0700 Subject: [PATCH 0597/3297] xfs: grab rtrmap btree when checking rgsuper commit ea6e2d9de25d2095845e0cdf2274e581fd40d636 upstream. LOLLM noticed that we aren't grabbing the rtrmap btree when we check the realtime group superblock. As a result, none of the cross-referencing checks have ever run. Fix this. Cc: stable@vger.kernel.org # v6.14 Fixes: 428e4884656db9 ("xfs: allow queued realtime intents to drain before scrubbing") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rgsuper.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index d189732d0e24f..fa72b9d44d4dc 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -23,6 +23,8 @@ int xchk_setup_rgsuperblock( struct xfs_scrub *sc) { + if (xchk_need_intent_drain(sc)) + xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN); return xchk_trans_alloc(sc, 0); } @@ -43,6 +45,7 @@ xchk_rgsuperblock( struct xfs_scrub *sc) { xfs_rgnumber_t rgno = sc->sm->sm_agno; + unsigned int flags; int error; /* @@ -63,7 +66,12 @@ xchk_rgsuperblock( if (!xchk_xref_process_error(sc, 0, 0, &error)) return error; - error = xchk_rtgroup_lock(sc, &sc->sr, XFS_RTGLOCK_BITMAP_SHARED); + if (xfs_has_rtrmapbt(sc->mp)) + flags = XFS_RTGLOCK_BITMAP | XFS_RTGLOCK_RMAP; + else + flags = XFS_RTGLOCK_BITMAP_SHARED; + + error = xchk_rtgroup_lock(sc, &sc->sr, flags); if (error) return error; From 08b191ae64659cc3c4006352827b78f0d48d1e33 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:26 -0700 Subject: [PATCH 0598/3297] xfs: use the rt version of the cow staging checker commit 881f2eb0fcdea2f46a50fa6a892fe9e3ccf19e01 upstream. LOLLM also noticed that xchk_rtrmapbt_xref ought to be using the rtdev version of the "is this a cow extent?" helper function, not the datadev one. Cc: stable@vger.kernel.org # v6.14 Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtrmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c index 12989fe80e8bd..a84347828fccb 100644 --- a/fs/xfs/scrub/rtrmap.c +++ b/fs/xfs/scrub/rtrmap.c @@ -209,7 +209,7 @@ xchk_rtrmapbt_xref( xfs_rgbno_to_rtb(sc->sr.rtg, irec->rm_startblock), irec->rm_blockcount); if (irec->rm_owner == XFS_RMAP_OWN_COW) - xchk_xref_is_cow_staging(sc, irec->rm_startblock, + xchk_xref_is_rt_cow_staging(sc, irec->rm_startblock, irec->rm_blockcount); else xchk_rtrmapbt_xref_rtrefc(sc, irec); From d399b026a6b34228bf6d8258acbf7bb6b8e17c56 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:12 -0700 Subject: [PATCH 0599/3297] xfs: set xfarray killable sort correctly commit 540ddc626245f12f56326ee0c1601f71ebb41d64 upstream. LOLLM noticed that we *disable* interruptible sorts when the KILLABLE flag is set. This is backwards. Fix the incorrect logic, and rename the variable to make the connection more obvious. Cc: stable@vger.kernel.org # v6.10 Fixes: 271557de7cbfde ("xfs: reduce the rate of cond_resched calls inside scrub") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/scrub.h | 6 +++--- fs/xfs/scrub/xfarray.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h index a3f1abc913903..6d7d3523b71f2 100644 --- a/fs/xfs/scrub/scrub.h +++ b/fs/xfs/scrub/scrub.h @@ -11,7 +11,7 @@ struct xfs_scrub; struct xchk_relax { unsigned long next_resched; unsigned int resched_nr; - bool interruptible; + bool killable; }; /* Yield to the scheduler at most 10x per second. */ @@ -21,7 +21,7 @@ struct xchk_relax { (struct xchk_relax){ \ .next_resched = XCHK_RELAX_NEXT, \ .resched_nr = 0, \ - .interruptible = true, \ + .killable = true, \ } /* @@ -45,7 +45,7 @@ static inline int xchk_maybe_relax(struct xchk_relax *widget) widget->next_resched = XCHK_RELAX_NEXT; } - if (widget->interruptible && fatal_signal_pending(current)) + if (widget->killable && fatal_signal_pending(current)) return -EINTR; return 0; diff --git a/fs/xfs/scrub/xfarray.c b/fs/xfs/scrub/xfarray.c index cdd13ed9c569a..a3ad0b3235761 100644 --- a/fs/xfs/scrub/xfarray.c +++ b/fs/xfs/scrub/xfarray.c @@ -487,8 +487,7 @@ xfarray_sortinfo_alloc( xfarray_sortinfo_lo(si)[0] = 0; xfarray_sortinfo_hi(si)[0] = array->nr - 1; si->relax = INIT_XCHK_RELAX; - if (flags & XFARRAY_SORT_KILLABLE) - si->relax.interruptible = false; + si->relax.killable = !!(flags & XFARRAY_SORT_KILLABLE); trace_xfarray_sort(si, nr_bytes); *infop = si; From 424be21ed8cd4251f015f01fde8e4ec1bfc2e445 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:43 -0700 Subject: [PATCH 0600/3297] xfs: handle non-inode owners for rtrmap record checking commit 353a5900bc85234e7df05caac37698042dc26348 upstream. LOLLM noticed that two helper functions in the rtrmapbt scrub code don't actually handle non-inode owners correctly -- CoW staging extents and rgsuperblock extents are not shareable, but they are mergeable. Fix these two helpers. Cc: stable@vger.kernel.org # v6.14 Fixes: 2d9a3e98053e8c ("xfs: allow overlapping rtrmapbt records for shared data extents") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtrmap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/xfs/scrub/rtrmap.c b/fs/xfs/scrub/rtrmap.c index a84347828fccb..e75fa9c095e0b 100644 --- a/fs/xfs/scrub/rtrmap.c +++ b/fs/xfs/scrub/rtrmap.c @@ -87,6 +87,9 @@ xchk_rtrmapbt_is_shareable( return false; if (irec->rm_flags & XFS_RMAP_UNWRITTEN) return false; + if (irec->rm_owner == XFS_RMAP_OWN_COW || + irec->rm_owner == XFS_RMAP_OWN_FS) + return false; return true; } @@ -146,6 +149,9 @@ xchk_rtrmap_mergeable( return false; if (r1->rm_flags != r2->rm_flags) return false; + if (r1->rm_owner == XFS_RMAP_OWN_COW || + r1->rm_owner == XFS_RMAP_OWN_FS) + return true; return r1->rm_offset + r1->rm_blockcount == r2->rm_offset; } From c9662ffd62c4e1b701a4076e9181732e3c09da37 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:15 -0700 Subject: [PATCH 0601/3297] xfs: clamp timestamp nanoseconds correctly commit 15e38a9366b31d3d61081ead115f1dff59379e24 upstream. LOLLM noticed an off-by-one error in the nsec clamping; fix that so that we never have tv_nsec == 1e9. Cc: stable@vger.kernel.org # v6.8 Fixes: 2d295fe65776d1 ("xfs: repair inode records") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/inode_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index a90a011c7e5f8..5043fc52c19b7 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -1753,7 +1753,7 @@ xrep_clamp_timestamp( struct xfs_inode *ip, struct timespec64 *ts) { - ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC); + ts->tv_nsec = clamp_t(long, ts->tv_nsec, 0, NSEC_PER_SEC - 1); *ts = timestamp_truncate(*ts, VFS_I(ip)); } From 1ea0868a477b78d1b6a7ca043231ae6c0c5ea5cc Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:59 -0700 Subject: [PATCH 0602/3297] xfs: fully check the parent handle when it points to the rootdir commit ba150ce63453ccd74bae1404c1dfedbd01ecfd55 upstream. LOLLM noticed that the directory tree path checking declares the path to be ok if the inumber in the parent pointer reaches the root directory. Unfortunately, it neglects to check that the generation is correct. Fix that by moving the generation check up. Cc: stable@vger.kernel.org # v6.10 Fixes: 928b721a11789a ("xfs: teach online scrub to find directory tree structure problems") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/dirtree.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c index 3e0bbe75c44cf..7301e9cbdab91 100644 --- a/fs/xfs/scrub/dirtree.c +++ b/fs/xfs/scrub/dirtree.c @@ -383,6 +383,14 @@ xchk_dirpath_step_up( goto out_scanlock; } + /* The handle encoded in the parent pointer must match. */ + if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { + trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, + path->nr_steps, &dl->xname, &dl->pptr_rec); + error = -EFSCORRUPTED; + goto out_scanlock; + } + /* We've reached the root directory; the path is ok. */ if (parent_ino == dl->root_ino) { xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK); @@ -411,14 +419,6 @@ xchk_dirpath_step_up( goto out_scanlock; } - /* The handle encoded in the parent pointer must match. */ - if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { - trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, - path->nr_steps, &dl->xname, &dl->pptr_rec); - error = -EFSCORRUPTED; - goto out_scanlock; - } - /* Parent pointer must point up to a directory. */ if (!S_ISDIR(VFS_I(dp)->i_mode)) { trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr, From 6403ef9a81e6c27e9e77c1a4d8df29331c41089c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:30 -0700 Subject: [PATCH 0603/3297] xfs: don't zap bmbt forks if they are MAXLEVELS tall commit 59c462b0f5cfa107794228051724b34ae9334168 upstream. LOLLM noticed a discrepancy between the bmbt level checks in the libxfs bmbt code vs. the inode repair code. We do actually allow a bmbt root that proclaims to have a height of XFS_BM_MAXLEVELS. Cc: stable@vger.kernel.org # v6.8 Fixes: e744cef2060559 ("xfs: zap broken inode forks") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/inode_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 5043fc52c19b7..5c45a68793d03 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -921,7 +921,7 @@ xrep_dinode_bad_bmbt_fork( if (nrecs == 0 || xfs_bmdr_space_calc(nrecs) > dfork_size) return true; - if (level == 0 || level >= XFS_BM_MAXLEVELS(sc->mp, whichfork)) + if (level == 0 || level > XFS_BM_MAXLEVELS(sc->mp, whichfork)) return true; dmxr = xfs_bmdr_maxrecs(dfork_size, 0); From e696ef088f557d1c82b564f0d3a61bf568025412 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:28 -0700 Subject: [PATCH 0604/3297] xfs: fix off-by-one error when calling xchk_xref_has_rt_owner commit 5d72a68f2007ba2a968d6bf47dba3f4620bd182e upstream. LOLLM noticed an off-by-one error when computing the length of the rtrmap to cross-check. Cc: stable@vger.kernel.org # v6.14 Fixes: 037a44d8277adf ("xfs: cross-reference the realtime rmapbt") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtbitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index d5ff8609dbfb4..8efde8c26e619 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -258,7 +258,7 @@ xchk_rtbitmap( * the last free extent we saw and the last possible extent in the rt * group. */ - last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize - 1; + last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize; if (rtb->next_free_rgbno < last_rgbno) xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno, last_rgbno - rtb->next_free_rgbno); From 457a93a233bd78ade8531d65b5a13d9727af9c85 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:41 -0700 Subject: [PATCH 0605/3297] xfs: write the rg superblock when fixing it commit 9af789fa274bc14b907f811f79fa988a6fc6d4e7 upstream. The rtgroup superblock fixer should write the rtgroup superblock. LOLLM noticed this, oops. :/ Cc: stable@vger.kernel.org # v6.13 Fixes: 1433f8f9cead37 ("xfs: repair realtime group superblock") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rgsuper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index fa72b9d44d4dc..7b962890a8941 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -88,9 +88,13 @@ int xrep_rgsuperblock( struct xfs_scrub *sc) { + struct xfs_buf *sb_bp; + ASSERT(rtg_rgno(sc->sr.rtg) == 0); + sb_bp = xfs_trans_getsb(sc->tp); xfs_log_sb(sc->tp); + xfs_log_rtsb(sc->tp, sb_bp); return 0; } #endif /* CONFIG_XFS_ONLINE_REPAIR */ From 06b1729436efc1d2b3866b50825bece3d18355f8 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:10 -0700 Subject: [PATCH 0606/3297] xfs: use rtrefcount btree cursor in xchk_xref_is_rt_cow_staging commit ee248157da501f0c02688fb64e5359f2832b0b01 upstream. LOLLM points out that we pass the wrong btree cursor here. We want the rtrefcount btree cursor, not the non-rt one. This is fairly benign since it only affects tracing data. Cc: stable@vger.kernel.org # v6.14 Fixes: 91683bb3f264c0 ("xfs: cross-reference checks with the rt refcount btree") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/rtrefcount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c index 4c5dffc73641b..24eb2a664aab8 100644 --- a/fs/xfs/scrub/rtrefcount.c +++ b/fs/xfs/scrub/rtrefcount.c @@ -608,7 +608,7 @@ xchk_xref_is_rt_cow_staging( /* CoW lookup returned a shared extent record? */ if (rc.rc_domain != XFS_REFC_DOMAIN_COW) - xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); + xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0); /* Must be at least as long as what was passed in */ if (rc.rc_blockcount < len) From f89c296854b755a66657065c35b05406fc18264d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 18 Jul 2026 16:53:38 +0200 Subject: [PATCH 0607/3297] Linux 6.18.39 Link: https://lore.kernel.org/r/20260716133044.672218725@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Florian Fainelli Tested-by: Peter Schneider Tested-by: Miguel Ojeda Tested-by: Ron Economos Tested-by: Wentao Guan Tested-by: Shuah Khan Tested-by: Pavel Machek (CIP) Link: https://lore.kernel.org/r/20260717101904.922623678@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Florian Fainelli Tested-by: Pavel Machek (CIP) Tested-by: Peter Schneider Tested-by: Mark Brown Tested-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9c602286d8e19..f4eef2d694db7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 38 +SUBLEVEL = 39 EXTRAVERSION = NAME = Baby Opossum Posse From 110ce8fed0f8e6efe42442d1b916723e69ca10fa Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 02:03:01 -0300 Subject: [PATCH 0608/3297] perf strlist: Don't write to const memory commit 678ed6b707e4b2db250f255d2f959322896dae65 upstream. Do a strdup to the list string and parse from it, free at the end. This is to deal with newer glibcs const-correctness. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/strlist.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 8a868cbeffae2..98883672fcf47 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -139,21 +139,25 @@ static int strlist__parse_list_entry(struct strlist *slist, const char *s, return err; } -static int strlist__parse_list(struct strlist *slist, const char *s, const char *subst_dir) +static int strlist__parse_list(struct strlist *slist, const char *list, const char *subst_dir) { - char *sep; + char *sep, *s = strdup(list), *sdup = s; int err; + if (s == NULL) + return -ENOMEM; + while ((sep = strchr(s, ',')) != NULL) { *sep = '\0'; err = strlist__parse_list_entry(slist, s, subst_dir); - *sep = ','; if (err != 0) return err; s = sep + 1; } - return *s ? strlist__parse_list_entry(slist, s, subst_dir) : 0; + err = *s ? strlist__parse_list_entry(slist, s, subst_dir) : 0; + free(sdup); + return err; } struct strlist *strlist__new(const char *list, const struct strlist_config *config) From 9ae21594ee518158ce6f5c5de3c39614418fa8aa Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 12:43:43 -0300 Subject: [PATCH 0609/3297] perf strlist: Remove dont_dupstr logic, used only once commit 612e4022c616eba66ed15e6b7a9924251e0298e8 upstream. Ian Rogers noticed that 678ed6b707e4b2db ("perf strlist: Don't write to const memory") breaks the 'Remove thread map' 'perf test' entry, because it keeps pointers to the temporary string introduced to avoid touching the const memory. This is because the thread_map__new_by_[pt]id_str() were the only methods using the slist->dont_dupstr knob to keep pointers to the original const string list, as it uses strtol to parse numbers and it stops at the comma. As this is the only case of dont_dupstr use, dupstr being the default, and it gets in the way of getting rid of the last const-correctness, remove this knob, with it: $ perf test 37 37: Remove thread map : Ok $ Fixes: 678ed6b707e4b2db ("perf strlist: Don't write to const memory") Reported-by: Ian Rogers Tested-by: Ian Rogers Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/strlist.c | 25 ++++++++----------------- tools/perf/util/strlist.h | 2 -- tools/perf/util/thread_map.c | 18 ++++++------------ 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 98883672fcf47..50add72575e0c 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -12,20 +12,16 @@ #include static -struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry) +struct rb_node *strlist__node_new(struct rblist *rblist __maybe_unused, const void *entry) { const char *s = entry; struct rb_node *rc = NULL; - struct strlist *strlist = container_of(rblist, struct strlist, rblist); struct str_node *snode = malloc(sizeof(*snode)); if (snode != NULL) { - if (strlist->dupstr) { - s = strdup(s); - if (s == NULL) - goto out_delete; - } - snode->s = s; + snode->s = strdup(s); + if (snode->s == NULL) + goto out_delete; rc = &snode->rb_node; } @@ -36,20 +32,18 @@ struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry) return NULL; } -static void str_node__delete(struct str_node *snode, bool dupstr) +static void str_node__delete(struct str_node *snode) { - if (dupstr) - zfree((char **)&snode->s); + zfree((char **)&snode->s); free(snode); } static -void strlist__node_delete(struct rblist *rblist, struct rb_node *rb_node) +void strlist__node_delete(struct rblist *rblist __maybe_unused, struct rb_node *rb_node) { - struct strlist *slist = container_of(rblist, struct strlist, rblist); struct str_node *snode = container_of(rb_node, struct str_node, rb_node); - str_node__delete(snode, slist->dupstr); + str_node__delete(snode); } static int strlist__node_cmp(struct rb_node *rb_node, const void *entry) @@ -165,12 +159,10 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf struct strlist *slist = malloc(sizeof(*slist)); if (slist != NULL) { - bool dupstr = true; bool file_only = false; const char *dirname = NULL; if (config) { - dupstr = !config->dont_dupstr; dirname = config->dirname; file_only = config->file_only; } @@ -180,7 +172,6 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf slist->rblist.node_new = strlist__node_new; slist->rblist.node_delete = strlist__node_delete; - slist->dupstr = dupstr; slist->file_only = file_only; if (list && strlist__parse_list(slist, list, dirname) != 0) diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 7e82c71dcc422..3e9533e66ca9e 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h @@ -14,7 +14,6 @@ struct str_node { struct strlist { struct rblist rblist; - bool dupstr; bool file_only; }; @@ -24,7 +23,6 @@ struct strlist { * found */ struct strlist_config { - bool dont_dupstr; bool file_only; const char *dirname; }; diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index ca193c1374ed4..48c70f149e920 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -164,19 +164,16 @@ static struct perf_thread_map *thread_map__new_by_pid_str(const char *pid_str) struct dirent **namelist = NULL; int i, j = 0; pid_t pid, prev_pid = INT_MAX; - char *end_ptr; struct str_node *pos; - struct strlist_config slist_config = { .dont_dupstr = true, }; - struct strlist *slist = strlist__new(pid_str, &slist_config); + struct strlist *slist = strlist__new(pid_str, NULL); if (!slist) return NULL; strlist__for_each_entry(pos, slist) { - pid = strtol(pos->s, &end_ptr, 10); + pid = strtol(pos->s, NULL, 10); - if (pid == INT_MIN || pid == INT_MAX || - (*end_ptr != '\0' && *end_ptr != ',')) + if (pid == INT_MIN || pid == INT_MAX) goto out_free_threads; if (pid == prev_pid) @@ -223,24 +220,21 @@ struct perf_thread_map *thread_map__new_by_tid_str(const char *tid_str) struct perf_thread_map *threads = NULL, *nt; int ntasks = 0; pid_t tid, prev_tid = INT_MAX; - char *end_ptr; struct str_node *pos; - struct strlist_config slist_config = { .dont_dupstr = true, }; struct strlist *slist; /* perf-stat expects threads to be generated even if tid not given */ if (!tid_str) return perf_thread_map__new_dummy(); - slist = strlist__new(tid_str, &slist_config); + slist = strlist__new(tid_str, NULL); if (!slist) return NULL; strlist__for_each_entry(pos, slist) { - tid = strtol(pos->s, &end_ptr, 10); + tid = strtol(pos->s, NULL, 10); - if (tid == INT_MIN || tid == INT_MAX || - (*end_ptr != '\0' && *end_ptr != ',')) + if (tid == INT_MIN || tid == INT_MAX) goto out_free_threads; if (tid == prev_tid) From 972c65697792ceca49db4dbd1776e3a486a1de76 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 01:15:47 -0300 Subject: [PATCH 0610/3297] perf time-utils: Constify variables storing the result of strchr() on const tables commit 21c0bc9144834e39762dd6fddbb255ebb80cf079 upstream. As newer glibcs will propagate the const attribute of the searched table to its return. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/time-utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index 1b91ccd4d5234..d43c4577d7ebc 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c @@ -325,7 +325,7 @@ static int percent_comma_split(struct perf_time_interval *ptime_buf, int num, } static int one_percent_convert(struct perf_time_interval *ptime_buf, - const char *ostr, u64 start, u64 end, char *c) + const char *ostr, u64 start, u64 end, const char *c) { char *str; int len = strlen(ostr), ret; @@ -358,7 +358,7 @@ static int one_percent_convert(struct perf_time_interval *ptime_buf, int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, const char *ostr, u64 start, u64 end) { - char *c; + const char *c; /* * ostr example: From 33250aa5cfade9bb7d4bdadc09b28788b85e7569 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 01:15:47 -0300 Subject: [PATCH 0611/3297] perf jitdump: Constify variables storing the result of strchr() on const tables commit 68abacb0686651dd3f0bbce2fa94b438afeb2fc4 upstream. As newer glibcs will propagate the const attribute of the searched table to its return. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/jitdump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index b062b1f234b62..28d32e3cfdba9 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -754,7 +754,7 @@ jit_inject(struct jit_buf_desc *jd, const char *path) static int jit_detect(const char *mmap_name, pid_t pid, struct nsinfo *nsi, bool *in_pidns) { - char *p; + const char *p; char *end = NULL; pid_t pid2; From 659a56ba86a9694f6b5f33fcdcfc70f1579c13e3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Dec 2025 19:17:53 -0300 Subject: [PATCH 0612/3297] perf list: Remove unused 'sep' variable commit d311783bc68b011c77a4ef81321de2c94d7deffc upstream. It is just being set to the return of strchr() but never used, just ditch it and with it get rid of a warning about it not being const on fedora 44. Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20251211221756.96294-2-acme@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/builtin-list.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index caf42276bd0f5..0ad687663d22a 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -619,7 +619,7 @@ int cmd_list(int argc, const char **argv) } for (i = 0; i < argc; ++i) { - char *sep, *s; + char *s; if (strcmp(argv[i], "tracepoint") == 0) { char *old_pmu_glob = default_ps.pmu_glob; @@ -674,7 +674,7 @@ int cmd_list(int argc, const char **argv) else if (strcmp(argv[i], "pfm") == 0) print_libpfm_events(&print_cb, ps); #endif - else if ((sep = strchr(argv[i], ':')) != NULL) { + else if (strchr(argv[i], ':') != NULL) { char *old_pmu_glob = default_ps.pmu_glob; char *old_event_glob = default_ps.event_glob; From 3b540ba9606bf87148c6f5989598dd534147fe40 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 19 Nov 2025 15:36:21 -0800 Subject: [PATCH 0613/3297] perf tools: Switch printf("...%s", strerror(errno)) to printf("...%m") commit bac74dcbd48b5b441e47841fd0fe507c7b0bcbaf upstream. strerror() has thread safety issues, strerror_r() requires stack allocated buffers. Code in perf has already been using the "%m" formatting flag that is a widely support glibc extension to print the current errno's description. Expand the usage of this formatting flag and remove usage of strerror()/strerror_r(). Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Ghiti Cc: Blake Jones Cc: Chun-Tse Shao Cc: Dmitriy Vyukov Cc: Dr. David Alan Gilbert Cc: Haibo Xu Cc: Howard Chu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Leo Yan Cc: Peter Zijlstra Cc: Stephen Brennan Cc: Thomas Falcon Cc: Yunseong Kim Cc: Zhongqiu Han Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/arch/x86/tests/bp-modify.c | 30 +++++++++++--------------- tools/perf/bench/uprobe.c | 2 +- tools/perf/builtin-daemon.c | 8 +++---- tools/perf/builtin-probe.c | 3 +-- tools/perf/builtin-record.c | 24 +++++++++------------ tools/perf/builtin-stat.c | 9 ++++---- tools/perf/builtin-trace.c | 15 ++++++------- tools/perf/perf.c | 18 ++++++---------- tools/perf/util/bpf-event.c | 11 ++++------ tools/perf/util/bpf-utils.c | 4 ++-- tools/perf/util/bpf_lock_contention.c | 2 +- tools/perf/util/cap.c | 3 +-- tools/perf/util/data.c | 29 +++++++++---------------- tools/perf/util/dso.c | 19 ++++++---------- tools/perf/util/evlist.c | 31 +++++++++++++++------------ tools/perf/util/evsel.c | 17 ++++++++------- tools/perf/util/jitdump.c | 3 ++- tools/perf/util/lzma.c | 6 +++--- tools/perf/util/session.c | 5 +++-- tools/perf/util/symbol-elf.c | 4 ++-- 20 files changed, 104 insertions(+), 139 deletions(-) diff --git a/tools/perf/arch/x86/tests/bp-modify.c b/tools/perf/arch/x86/tests/bp-modify.c index 0924ccd9e36d5..589b432739486 100644 --- a/tools/perf/arch/x86/tests/bp-modify.c +++ b/tools/perf/arch/x86/tests/bp-modify.c @@ -80,26 +80,24 @@ static int bp_modify1(void) */ if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), bp_2)) { - pr_debug("failed to set breakpoint, 1st time: %s\n", - strerror(errno)); + pr_debug("failed to set breakpoint, 1st time: %m\n"); goto out; } if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), bp_1)) { - pr_debug("failed to set breakpoint, 2nd time: %s\n", - strerror(errno)); + pr_debug("failed to set breakpoint, 2nd time: %m\n"); goto out; } if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[7]), dr7)) { - pr_debug("failed to set dr7: %s\n", strerror(errno)); + pr_debug("failed to set dr7: %m\n"); goto out; } if (ptrace(PTRACE_CONT, child, NULL, NULL)) { - pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); + pr_debug("failed to PTRACE_CONT: %m\n"); goto out; } @@ -112,19 +110,17 @@ static int bp_modify1(void) rip = ptrace(PTRACE_PEEKUSER, child, offsetof(struct user_regs_struct, rip), NULL); if (rip == (unsigned long) -1) { - pr_debug("failed to PTRACE_PEEKUSER: %s\n", - strerror(errno)); + pr_debug("failed to PTRACE_PEEKUSER: %m\n"); goto out; } pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); - out: if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { - pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); + pr_debug("failed to PTRACE_DETACH: %m\n"); return TEST_FAIL; - } + } return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; } @@ -157,14 +153,13 @@ static int bp_modify2(void) */ if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), bp_1)) { - pr_debug("failed to set breakpoint: %s\n", - strerror(errno)); + pr_debug("failed to set breakpoint: %m\n"); goto out; } if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[7]), dr7)) { - pr_debug("failed to set dr7: %s\n", strerror(errno)); + pr_debug("failed to set dr7: %m\n"); goto out; } @@ -175,7 +170,7 @@ static int bp_modify2(void) } if (ptrace(PTRACE_CONT, child, NULL, NULL)) { - pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); + pr_debug("failed to PTRACE_CONT: %m\n"); goto out; } @@ -188,8 +183,7 @@ static int bp_modify2(void) rip = ptrace(PTRACE_PEEKUSER, child, offsetof(struct user_regs_struct, rip), NULL); if (rip == (unsigned long) -1) { - pr_debug("failed to PTRACE_PEEKUSER: %s\n", - strerror(errno)); + pr_debug("failed to PTRACE_PEEKUSER: %m\n"); goto out; } @@ -197,7 +191,7 @@ static int bp_modify2(void) out: if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { - pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); + pr_debug("failed to PTRACE_DETACH: %m\n"); return TEST_FAIL; } diff --git a/tools/perf/bench/uprobe.c b/tools/perf/bench/uprobe.c index 0b90275862e19..c4dac868f1eea 100644 --- a/tools/perf/bench/uprobe.c +++ b/tools/perf/bench/uprobe.c @@ -54,7 +54,7 @@ static const char * const bench_uprobe_usage[] = { /*opts=*/&uprobe_opts); \ if (!skel->links.prog) { \ err = -errno; \ - fprintf(stderr, "Failed to attach bench uprobe \"%s\": %s\n", #prog, strerror(errno)); \ + fprintf(stderr, "Failed to attach bench uprobe \"%s\": %m\n", #prog); \ goto cleanup; \ } diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c index f0568431fbd51..33473e0713926 100644 --- a/tools/perf/builtin-daemon.c +++ b/tools/perf/builtin-daemon.c @@ -265,8 +265,7 @@ static int check_base(struct daemon *daemon) daemon->base); return -EACCES; default: - pr_err("failed: can't access base '%s': %s\n", - daemon->base, strerror(errno)); + pr_err("failed: can't access base '%s': %m\n", daemon->base); return -errno; } } @@ -544,8 +543,7 @@ static int daemon_session__control(struct daemon_session *session, err = writen(control, msg, len); if (err != len) { - pr_err("failed: write to control pipe: %d (%s)\n", - errno, control_path); + pr_err("failed: write to control pipe: %m (%s)\n", control_path); goto out; } @@ -586,7 +584,7 @@ static int setup_server_socket(struct daemon *daemon) int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { - fprintf(stderr, "socket: %s\n", strerror(errno)); + fprintf(stderr, "socket: %m\n"); return -1; } diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 69800e4d9530b..1b4ba85ee0190 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -211,8 +211,7 @@ static int opt_set_target_ns(const struct option *opt __maybe_unused, ns_pid = (pid_t)strtol(str, NULL, 10); if (errno != 0) { ret = -errno; - pr_warning("Failed to parse %s as a pid: %s\n", str, - strerror(errno)); + pr_warning("Failed to parse %s as a pid: %m\n", str); return ret; } nsip = nsinfo__new(ns_pid); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b1fb87016d5aa..18e620113247d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1323,7 +1323,6 @@ static int record__mmap_evlist(struct record *rec, struct record_opts *opts = &rec->opts; bool auxtrace_overwrite = opts->auxtrace_snapshot_mode || opts->auxtrace_sample_mode; - char msg[512]; if (opts->affinity != PERF_AFFINITY_SYS) cpu__setup_cpunode_map(); @@ -1342,8 +1341,7 @@ static int record__mmap_evlist(struct record *rec, opts->mmap_pages, opts->auxtrace_mmap_pages); return -errno; } else { - pr_err("failed to mmap with %d (%s)\n", errno, - str_error_r(errno, msg, sizeof(msg))); + pr_err("failed to mmap: %m\n"); if (errno) return -errno; else @@ -1361,7 +1359,8 @@ static int record__mmap_evlist(struct record *rec, if (record__threads_enabled(rec)) { ret = perf_data__create_dir(&rec->data, evlist->core.nr_mmaps); if (ret) { - pr_err("Failed to create data directory: %s\n", strerror(-ret)); + errno = -ret; + pr_err("Failed to create data directory: %m\n"); return ret; } for (i = 0; i < evlist->core.nr_mmaps; i++) { @@ -1422,9 +1421,8 @@ static int record__open(struct record *rec) } if (evlist__apply_filters(evlist, &pos, &opts->target)) { - pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n", - pos->filter ?: "BPF", evsel__name(pos), errno, - str_error_r(errno, msg, sizeof(msg))); + pr_err("failed to set filter \"%s\" on event %s: %m\n", + pos->filter ?: "BPF", evsel__name(pos)); rc = -1; goto out; } @@ -1709,8 +1707,7 @@ static void *record__thread(void *arg) err = write(thread->pipes.ack[1], &msg, sizeof(msg)); if (err == -1) - pr_warning("threads[%d]: failed to notify on start: %s\n", - thread->tid, strerror(errno)); + pr_warning("threads[%d]: failed to notify on start: %m\n", thread->tid); pr_debug("threads[%d]: started on cpu%d\n", thread->tid, sched_getcpu()); @@ -1753,8 +1750,7 @@ static void *record__thread(void *arg) err = write(thread->pipes.ack[1], &msg, sizeof(msg)); if (err == -1) - pr_warning("threads[%d]: failed to notify on termination: %s\n", - thread->tid, strerror(errno)); + pr_warning("threads[%d]: failed to notify on termination: %m\n", thread->tid); return NULL; } @@ -2300,7 +2296,7 @@ static int record__start_threads(struct record *rec) sigfillset(&full); if (sigprocmask(SIG_SETMASK, &full, &mask)) { - pr_err("Failed to block signals on threads start: %s\n", strerror(errno)); + pr_err("Failed to block signals on threads start: %m\n"); return -1; } @@ -2318,7 +2314,7 @@ static int record__start_threads(struct record *rec) if (pthread_create(&handle, &attrs, record__thread, &thread_data[t])) { for (tt = 1; tt < t; tt++) record__terminate_thread(&thread_data[t]); - pr_err("Failed to start threads: %s\n", strerror(errno)); + pr_err("Failed to start threads: %m\n"); ret = -1; goto out_err; } @@ -2341,7 +2337,7 @@ static int record__start_threads(struct record *rec) pthread_attr_destroy(&attrs); if (sigprocmask(SIG_SETMASK, &mask, NULL)) { - pr_err("Failed to unblock signals on threads start: %s\n", strerror(errno)); + pr_err("Failed to unblock signals on threads start: %m\n"); ret = -1; } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9eb0876633c05..e8a75cb198b19 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -868,9 +868,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) } if (evlist__apply_filters(evsel_list, &counter, &target)) { - pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n", - counter->filter, evsel__name(counter), errno, - str_error_r(errno, msg, sizeof(msg))); + pr_err("failed to set filter \"%s\" on event %s: %m\n", + counter->filter, evsel__name(counter)); return -1; } @@ -932,8 +931,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) } if (workload_exec_errno) { - const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); - pr_err("Workload failed: %s\n", emsg); + errno = workload_exec_errno; + pr_err("Workload failed: %m\n"); err = -1; goto err_out; } diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 96efe1fba8547..72d10179e7f27 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2595,12 +2595,10 @@ static struct syscall *trace__syscall_info(struct trace *trace, struct evsel *ev err = syscall__read_info(sc, trace); if (err && verbose > 0) { - char sbuf[STRERR_BUFSIZE]; - - fprintf(trace->output, "Problems reading syscall %d: %d (%s)", id, -err, - str_error_r(-err, sbuf, sizeof(sbuf))); + errno = -err; + fprintf(trace->output, "Problems reading syscall %d: %m", id); if (sc && sc->name) - fprintf(trace->output, "(%s)", sc->name); + fprintf(trace->output, " (%s)", sc->name); fputs(" information\n", trace->output); } return err ? NULL : sc; @@ -4652,9 +4650,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) out_error_apply_filters: fprintf(trace->output, - "Failed to set filter \"%s\" on event %s with %d (%s)\n", - evsel->filter, evsel__name(evsel), errno, - str_error_r(errno, errbuf, sizeof(errbuf))); + "Failed to set filter \"%s\" on event %s: %m\n", + evsel->filter, evsel__name(evsel)); goto out_delete_evlist; } out_error_mem: @@ -4662,7 +4659,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) goto out_delete_evlist; out_errno: - fprintf(trace->output, "errno=%d,%s\n", errno, strerror(errno)); + fprintf(trace->output, "%m\n"); goto out_delete_evlist; } diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 88c60ecf3395d..f475a8664ffce 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -169,8 +169,8 @@ static int set_debug_file(const char *path) { debug_fp = fopen(path, "w"); if (!debug_fp) { - fprintf(stderr, "Open debug file '%s' failed: %s\n", - path, strerror(errno)); + fprintf(stderr, "Open debug file '%s' failed: %m\n", + path); return -1; } @@ -335,7 +335,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; - char sbuf[STRERR_BUFSIZE]; if (use_browser == -1) use_browser = check_browser_config(p->cmd); @@ -363,17 +362,15 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) status = 1; /* Check for ENOSPC and EIO errors.. */ if (fflush(stdout)) { - fprintf(stderr, "write failure on standard output: %s", - str_error_r(errno, sbuf, sizeof(sbuf))); + fprintf(stderr, "write failure on standard output: %m\n"); goto out; } if (ferror(stdout)) { - fprintf(stderr, "unknown write failure on standard output"); + fprintf(stderr, "unknown write failure on standard output\n"); goto out; } if (fclose(stdout)) { - fprintf(stderr, "close failed on standard output: %s", - str_error_r(errno, sbuf, sizeof(sbuf))); + fprintf(stderr, "close failed on standard output: %m\n"); goto out; } status = 0; @@ -459,7 +456,6 @@ int main(int argc, const char **argv) { int err, done_help = 0; const char *cmd; - char sbuf[STRERR_BUFSIZE]; perf_debug_setup(); @@ -573,8 +569,8 @@ int main(int argc, const char **argv) } if (cmd) { - fprintf(stderr, "Failed to run command '%s': %s\n", - cmd, str_error_r(errno, sbuf, sizeof(sbuf))); + fprintf(stderr, "Failed to run command '%s': %m\n", + cmd); } out: if (debug_fp) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 2298cd396c423..2e6da3ad0a4f9 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -787,11 +787,10 @@ int perf_event__synthesize_bpf_events(struct perf_session *session, err = 0; break; } - pr_debug("%s: can't get next program: %s%s\n", - __func__, strerror(errno), - errno == EINVAL ? " -- kernel too old?" : ""); /* don't report error on old kernel or EPERM */ err = (errno == EINVAL || errno == EPERM) ? 0 : -1; + pr_debug("%s: can\'t get next program: %m%s\n", + __func__, errno == EINVAL ? " -- kernel too old?" : ""); break; } fd = bpf_prog_get_fd_by_id(id); @@ -824,10 +823,8 @@ int perf_event__synthesize_bpf_events(struct perf_session *session, .tool = session->tool, }; - if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) { - pr_err("%s: failed to synthesize bpf images: %s\n", - __func__, strerror(errno)); - } + if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) + pr_err("%s: failed to synthesize bpf images: %m\n", __func__); free(event); return err; diff --git a/tools/perf/util/bpf-utils.c b/tools/perf/util/bpf-utils.c index 5a66dc8594aa8..d6d2c9c190f7a 100644 --- a/tools/perf/util/bpf-utils.c +++ b/tools/perf/util/bpf-utils.c @@ -123,7 +123,7 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) /* step 1: get array dimensions */ err = bpf_obj_get_info_by_fd(fd, &info, &info_len); if (err) { - pr_debug("can't get prog info: %s", strerror(errno)); + pr_debug("can't get prog info: %m\n"); return ERR_PTR(-EFAULT); } if (info.type >= __MAX_BPF_PROG_TYPE) @@ -186,7 +186,7 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) /* step 5: call syscall again to get required arrays */ err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len); if (err) { - pr_debug("can't get prog info: %s", strerror(errno)); + pr_debug("can't get prog info: %m\n"); free(info_linear); return ERR_PTR(-EFAULT); } diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c index 7b5671f13c535..788d30be20588 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -42,7 +42,7 @@ static void check_slab_cache_iter(struct lock_contention *con) con->btf = btf__load_vmlinux_btf(); if (con->btf == NULL) { - pr_debug("BTF loading failed: %s\n", strerror(errno)); + pr_debug("BTF loading failed: %m\n"); return; } diff --git a/tools/perf/util/cap.c b/tools/perf/util/cap.c index 24a0ea7e6d977..ac6d1d9a523d9 100644 --- a/tools/perf/util/cap.c +++ b/tools/perf/util/cap.c @@ -28,8 +28,7 @@ bool perf_cap__capable(int cap, bool *used_root) header.version == _LINUX_CAPABILITY_VERSION_1) continue; - pr_debug2("capget syscall failed (%s - %d) fall back on root check\n", - strerror(errno), errno); + pr_debug2("capget syscall failed (%m) fall back on root check\n"); *used_root = true; return geteuid() == 0; } diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 164eb45a0b369..90df41da1a32b 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -213,17 +213,15 @@ static int check_backup(struct perf_data *data) ret = rm_rf_perf_data(oldname); if (ret) { - pr_err("Can't remove old data: %s (%s)\n", - ret == -2 ? - "Unknown file found" : strerror(errno), - oldname); + if (ret == -2) + pr_err("Can't remove old data: Unknown file found (%s)\n", oldname); + else + pr_err("Can't remove old data: %m (%s)\n", oldname); return -1; } if (rename(data->path, oldname)) { - pr_err("Can't move data: %s (%s to %s)\n", - strerror(errno), - data->path, oldname); + pr_err("Can't move data: %m (%s to %s)\n", data->path, oldname); return -1; } } @@ -246,14 +244,12 @@ static int open_file_read(struct perf_data *data) int flags = data->in_place_update ? O_RDWR : O_RDONLY; struct stat st; int fd; - char sbuf[STRERR_BUFSIZE]; fd = open(data->file.path, flags); if (fd < 0) { int err = errno; - pr_err("failed to open %s: %s", data->file.path, - str_error_r(err, sbuf, sizeof(sbuf))); + pr_err("failed to open %s: %m", data->file.path); if (err == ENOENT && !strcmp(data->file.path, "perf.data")) pr_err(" (try 'perf record' first)"); pr_err("\n"); @@ -285,15 +281,10 @@ static int open_file_read(struct perf_data *data) static int open_file_write(struct perf_data *data) { - int fd; - char sbuf[STRERR_BUFSIZE]; - - fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, - S_IRUSR|S_IWUSR); + int fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, S_IRUSR|S_IWUSR); if (fd < 0) - pr_err("failed to open %s : %s\n", data->file.path, - str_error_r(errno, sbuf, sizeof(sbuf))); + pr_err("failed to open %s : %m\n", data->file.path); return fd; } @@ -436,8 +427,8 @@ int perf_data__switch(struct perf_data *data, if (lseek(data->file.fd, pos, SEEK_SET) == (off_t)-1) { ret = -errno; - pr_debug("Failed to lseek to %zu: %s", - pos, strerror(errno)); + pr_debug("Failed to lseek to %zu: %m\n", + pos); goto out; } } diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index dc202d4943721..52d3f537a111d 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -539,16 +539,13 @@ static void close_first_dso(void); static int do_open(char *name) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) { - int fd; - char sbuf[STRERR_BUFSIZE]; - do { - fd = open(name, O_RDONLY|O_CLOEXEC); + int fd = open(name, O_RDONLY|O_CLOEXEC); + if (fd >= 0) return fd; - pr_debug("dso open failed: %s\n", - str_error_r(errno, sbuf, sizeof(sbuf))); + pr_debug("dso open failed: %m\n"); if (!dso__data_open_cnt || errno != EMFILE) break; @@ -1110,7 +1107,6 @@ static int file_size(struct dso *dso, struct machine *machine) { int ret = 0; struct stat st; - char sbuf[STRERR_BUFSIZE]; mutex_lock(dso__data_open_lock()); @@ -1128,8 +1124,7 @@ static int file_size(struct dso *dso, struct machine *machine) if (fstat(dso__data(dso)->fd, &st) < 0) { ret = -errno; - pr_err("dso cache fstat failed: %s\n", - str_error_r(errno, sbuf, sizeof(sbuf))); + pr_err("dso cache fstat failed: %m\n"); dso__data(dso)->status = DSO_DATA_STATUS_ERROR; goto out; } @@ -1784,10 +1779,8 @@ int dso__strerror_load(struct dso *dso, char *buf, size_t buflen) BUG_ON(buflen == 0); if (errnum >= 0) { - const char *err = str_error_r(errnum, buf, buflen); - - if (err != buf) - scnprintf(buf, buflen, "%s", err); + errno = errnum; + scnprintf(buf, buflen, "%m"); return 0; } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 80d8387e6b97b..9ad92fae00a29 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1605,14 +1605,14 @@ int evlist__parse_sample_timestamp(struct evlist *evlist, union perf_event *even int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size) { int printed, value; - char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf)); switch (err) { case EACCES: case EPERM: + errno = err; printed = scnprintf(buf, size, - "Error:\t%s.\n" - "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); + "Error:\t%m.\n" + "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting."); value = perf_event_paranoid(); @@ -1639,16 +1639,18 @@ int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size if (first->core.attr.sample_freq < (u64)max_freq) goto out_default; + errno = err; printed = scnprintf(buf, size, - "Error:\t%s.\n" + "Error:\t%m.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_max_sample_rate.\n" "Hint:\tThe current value is %d and %" PRIu64 " is being requested.", - emsg, max_freq, first->core.attr.sample_freq); + max_freq, first->core.attr.sample_freq); break; } default: out_default: - scnprintf(buf, size, "%s", emsg); + errno = err; + scnprintf(buf, size, "%m"); break; } @@ -1657,17 +1659,17 @@ int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size) { - char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf)); int pages_attempted = evlist->core.mmap_len / 1024, pages_max_per_user, printed = 0; switch (err) { case EPERM: sysctl__read_int("kernel/perf_event_mlock_kb", &pages_max_per_user); + errno = err; printed += scnprintf(buf + printed, size - printed, - "Error:\t%s.\n" + "Error:\t%m.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_mlock_kb (%d kB) setting.\n" "Hint:\tTried using %zd kB.\n", - emsg, pages_max_per_user, pages_attempted); + pages_max_per_user, pages_attempted); if (pages_attempted >= pages_max_per_user) { printed += scnprintf(buf + printed, size - printed, @@ -1679,7 +1681,8 @@ int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size "Hint:\tTry using a smaller -m/--mmap-pages value."); break; default: - scnprintf(buf, size, "%s", emsg); + errno = err; + scnprintf(buf, size, "%m"); break; } @@ -1911,8 +1914,8 @@ static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ */ fd = open(s, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - pr_err("Failed to open '%s'\n", s); ret = -errno; + pr_err("Failed to open '%s': %m\n", s); goto out_free; } *ctl_fd = fd; @@ -1922,7 +1925,7 @@ static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ /* O_RDWR | O_NONBLOCK means the other end need not be open */ fd = open(p, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - pr_err("Failed to open '%s'\n", p); + pr_err("Failed to open '%s': %m\n", p); ret = -errno; goto out_free; } @@ -2354,7 +2357,7 @@ int evlist__parse_event_enable_time(struct evlist *evlist, struct record_opts *o eet->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (eet->timerfd == -1) { err = -errno; - pr_err("timerfd_create failed: %s\n", strerror(errno)); + pr_err("timerfd_create failed: %m\n"); goto free_eet_times; } @@ -2389,7 +2392,7 @@ static int event_enable_timer__set_timer(struct event_enable_timer *eet, int ms) if (timerfd_settime(eet->timerfd, 0, &its, NULL) < 0) { err = -errno; - pr_err("timerfd_settime failed: %s\n", strerror(errno)); + pr_err("timerfd_settime failed: %m\n"); } return err; } diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9df9818e37013..22e43a57dc95f 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -648,8 +648,9 @@ struct tep_event *evsel__tp_format(struct evsel *evsel) if (IS_ERR(tp_format)) { int err = -PTR_ERR(evsel->tp_format); - pr_err("Error getting tracepoint format '%s' '%s'(%d)\n", - evsel__name(evsel), strerror(err), err); + errno = err; + pr_err("Error getting tracepoint format '%s': %m\n", + evsel__name(evsel)); return NULL; } evsel->tp_format = tp_format; @@ -2750,8 +2751,8 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, PERF_EVENT_IOC_SET_BPF, bpf_fd); if (err && errno != EEXIST) { - pr_err("failed to attach bpf fd %d: %s\n", - bpf_fd, strerror(errno)); + pr_err("failed to attach bpf fd %d: %m\n", + bpf_fd); err = -EINVAL; goto out_close; } @@ -3815,7 +3816,6 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, int err, char *msg, size_t size) { struct perf_pmu *pmu; - char sbuf[STRERR_BUFSIZE]; int printed = 0, enforced = 0; int ret; @@ -3948,10 +3948,11 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, if (ret) return ret; + errno = err; return scnprintf(msg, size, - "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" - "\"dmesg | grep -i perf\" may provide additional information.\n", - err, str_error_r(err, sbuf, sizeof(sbuf)), evsel__name(evsel)); + "The sys_perf_event_open() syscall failed for event (%s): %m\n" + "\"dmesg | grep -i perf\" may provide additional information.\n", + evsel__name(evsel)); } struct perf_session *evsel__session(struct evsel *evsel) diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 28d32e3cfdba9..2a4c14b003b31 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -90,7 +90,8 @@ jit_emit_elf(struct jit_buf_desc *jd, saved_errno = errno; nsinfo__mountns_exit(&nsc); if (fd == -1) { - pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(saved_errno)); + errno = saved_errno; + pr_warning("cannot create jit ELF %s: %m\n", filename); return -1; } diff --git a/tools/perf/util/lzma.c b/tools/perf/util/lzma.c index c355757ed3911..91b9b5171d1fb 100644 --- a/tools/perf/util/lzma.c +++ b/tools/perf/util/lzma.c @@ -59,7 +59,7 @@ int lzma_decompress_stream_to_file(FILE *infile, int output_fd) strm.avail_in = fread(buf_in, 1, sizeof(buf_in), infile); if (ferror(infile)) { - pr_debug("lzma: read error: %s\n", strerror(errno)); + pr_debug("lzma: read error: %m\n"); goto err_lzma_end; } @@ -73,7 +73,7 @@ int lzma_decompress_stream_to_file(FILE *infile, int output_fd) ssize_t write_size = sizeof(buf_out) - strm.avail_out; if (writen(output_fd, buf_out, write_size) != write_size) { - pr_debug("lzma: write error: %s\n", strerror(errno)); + pr_debug("lzma: write error: %m\n"); goto err_lzma_end; } @@ -103,7 +103,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) infile = fopen(input, "rb"); if (!infile) { - pr_debug("lzma: fopen failed on %s: '%s'\n", input, strerror(errno)); + pr_debug("lzma: fopen failed on %s: '%m'\n", input); return -1; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 09af486c83e4f..e126026dde500 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2186,9 +2186,10 @@ reader__read_event(struct reader *rd, struct perf_session *session, if (size < sizeof(struct perf_event_header) || (skip = rd->process(session, event, rd->file_pos, rd->path)) < 0) { - pr_err("%#" PRIx64 " [%#x]: failed to process type: %d [%s]\n", + errno = -skip; + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d [%m]\n", rd->file_offset + rd->head, event->header.size, - event->header.type, strerror(-skip)); + event->header.type); err = skip; goto out; } diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 9602cc51dcc65..77a8b529b54f4 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1102,14 +1102,14 @@ static Elf *read_gnu_debugdata(struct dso *dso, Elf *elf, const char *name, int wrapped = fmemopen(scn_data->d_buf, scn_data->d_size, "r"); if (!wrapped) { - pr_debug("%s: fmemopen: %s\n", __func__, strerror(errno)); + pr_debug("%s: fmemopen: %m\n", __func__); *dso__load_errno(dso) = -errno; return NULL; } temp_fd = mkstemp(temp_filename); if (temp_fd < 0) { - pr_debug("%s: mkstemp: %s\n", __func__, strerror(errno)); + pr_debug("%s: mkstemp: %m\n", __func__); *dso__load_errno(dso) = -errno; fclose(wrapped); return NULL; From 5f6d997641de9892f54c4e12dd6a3a47462f8df4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 01:15:47 -0300 Subject: [PATCH 0614/3297] perf bpf-event: Constify variables storing the result of strchr() on const tables commit 8bf093acb3f1f07d846c86e32308f9f9954ed579 upstream. As newer glibcs will propagate the const attribute of the searched table to its return. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/bpf-event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 2e6da3ad0a4f9..67e7786bb878b 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -733,7 +733,8 @@ kallsyms_process_symbol(void *data, const char *_name, char type __maybe_unused, u64 start) { char disp[KSYM_NAME_LEN]; - char *module, *name; + const char *module; + char *name; unsigned long id; int err = 0; From f8cb25b1d5ed2b7c8f5c36446c7f7757e4b45b96 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 02:09:37 -0300 Subject: [PATCH 0615/3297] perf session: Don't write to memory pointed to a const pointer commit f1321cce848c558fde4c0c6bcd5e53f3cefd3af2 upstream. Since it is freshly allocated just attribute it to a non-const pointer and then change it via that pointer. That way we avoid const-correctness warnings in recent glibc versions. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/session.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e126026dde500..3a37f6df7e78f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2474,7 +2474,7 @@ bool perf_session__has_switch_events(struct perf_session *session) int map__set_kallsyms_ref_reloc_sym(struct map *map, const char *symbol_name, u64 addr) { - char *bracket; + char *bracket, *name; struct ref_reloc_sym *ref; struct kmap *kmap; @@ -2482,13 +2482,13 @@ int map__set_kallsyms_ref_reloc_sym(struct map *map, const char *symbol_name, u6 if (ref == NULL) return -ENOMEM; - ref->name = strdup(symbol_name); + ref->name = name = strdup(symbol_name); if (ref->name == NULL) { free(ref); return -ENOMEM; } - bracket = strchr(ref->name, ']'); + bracket = strchr(name, ']'); if (bracket) *bracket = '\0'; From a816153dd85754887dea979c1309e5a96a6ee691 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 01:15:47 -0300 Subject: [PATCH 0616/3297] perf demangle-java: Constify variables storing the result of strchr() on const tables commit 79bba3a1834e7ba6c437674582cc9f3ae6fb638c upstream. As newer glibcs will propagate the const attribute of the searched table to its return. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/demangle-java.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c index ddf33d58bcd35..c3cb327ed5623 100644 --- a/tools/perf/util/demangle-java.c +++ b/tools/perf/util/demangle-java.c @@ -158,7 +158,7 @@ char * java_demangle_sym(const char *str, int flags) { char *buf, *ptr; - char *p; + const char *p; size_t len, l1 = 0; if (!str) From 8bac35a8fd98fe284632c1a6049cb4b0a12c80bb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 01:15:47 -0300 Subject: [PATCH 0617/3297] perf trace-event: Constify variables storing the result of strchr() on const tables commit 97b81df7225830c4db3c17ed1235d2f3eb613d3d upstream. As newer glibcs will propagate the const attribute of the searched table to its return. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/trace-event-info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index c8755679281eb..45774722f2492 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -482,7 +482,7 @@ char *tracepoint_id_to_name(u64 config) static struct tracepoint_path *tracepoint_name_to_path(const char *name) { struct tracepoint_path *path = zalloc(sizeof(*path)); - char *str = strchr(name, ':'); + const char *str = strchr(name, ':'); if (path == NULL || str == NULL) { free(path); From ee8ab4e8e7029ddb058ddccc758ab1e85b77f9c4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 01:15:47 -0300 Subject: [PATCH 0618/3297] perf metricgroup: Constify variables storing the result of strchr() on const tables commit b42868624c7d00206f77d19a6fbfea73a44ff6f2 upstream. As newer glibcs will propagate the const attribute of the searched table to its return. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/metricgroup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 595b83142d2cc..238c3e605bfe1 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -364,7 +364,7 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids, static bool match_metric_or_groups(const char *metric_or_groups, const char *sought) { int len; - char *m; + const char *m; if (!sought) return false; @@ -439,11 +439,10 @@ static const char *code_characters = ",-=@"; static int encode_metric_id(struct strbuf *sb, const char *x) { - char *c; int ret = 0; for (; *x; x++) { - c = strchr(code_characters, *x); + const char *c = strchr(code_characters, *x); if (c) { ret = strbuf_addch(sb, '!'); if (ret) From 5bcff7ce5c3b15938f5316c13b1f88e174cf3cab Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Dec 2025 19:17:55 -0300 Subject: [PATCH 0619/3297] perf tools: Use const for variables receiving str{str,r?chr}() returns commit 45718bce7daf39c618188b70a52644bb5a2f968a upstream. Newer glibc versions return const char for str{str,chr}() where the haystack/s is const so to avoid warnings like these on fedora 44 change some variables to const: 36 8.17 fedora:44 : FAIL gcc version 15.2.1 20251111 (Red Hat 15.2.1-4) (GCC) libbpf.c: In function 'kallsyms_cb': libbpf.c:8489:13: error: assignment discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers] 8489 | res = strstr(sym_name, ".llvm."); Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20251211221756.96294-4-acme@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/jvmti/libjvmti.c | 2 +- tools/perf/util/evlist.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c index 82514e6532b8c..87bfd4781003a 100644 --- a/tools/perf/jvmti/libjvmti.c +++ b/tools/perf/jvmti/libjvmti.c @@ -142,7 +142,7 @@ copy_class_filename(const char * class_sign, const char * file_name, char * resu */ if (*class_sign == 'L') { size_t j, i = 0; - char *p = strrchr(class_sign, '/'); + const char *p = strrchr(class_sign, '/'); if (p) { /* drop the 'L' prefix and copy up to the final '/' */ for (i = 0; i < (size_t)(p - class_sign); i++) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 9ad92fae00a29..314393a430a37 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1939,7 +1939,8 @@ static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ int evlist__parse_control(const char *str, int *ctl_fd, int *ctl_fd_ack, bool *ctl_fd_close) { - char *comma = NULL, *endptr = NULL; + const char *comma = NULL; + char *endptr = NULL; *ctl_fd_close = false; From e7d3f92238a3d0535fa7c559914f40ef06ba146f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 02:15:30 -0300 Subject: [PATCH 0620/3297] perf hwmon_pmu: Constify the variables returning bsearch() on const tables commit 0e47251e8cc438d5b59fcd86d27efade01976fe1 upstream. To address const-correctness errors on newer glibcs (-Werror=discarded-qualifiers). Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/hwmon_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index 5c27256a220a5..5f51cfd13ce32 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -161,7 +161,7 @@ bool parse_hwmon_filename(const char *filename, bool *alarm) { char fn_type[24]; - const char **elem; + const char * const *elem; const char *fn_item = NULL; size_t fn_item_len; From 018533cfe83cff6fff8a5bdec39139a02fcd78d3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 01:15:47 -0300 Subject: [PATCH 0621/3297] perf units: Constify variables storing the result of strchr() on const tables commit 0e14cb3b24f8f301cf6490a4493afc98321ed5bb upstream. As newer glibcs will propagate the const attribute of the searched table to its return. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/units.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/units.c b/tools/perf/util/units.c index 4c6a86e1cb54b..0bbacf5a29aaf 100644 --- a/tools/perf/util/units.c +++ b/tools/perf/util/units.c @@ -12,7 +12,7 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) struct parse_tag *i = tags; while (i->tag) { - char *s = strchr(str, i->tag); + const char *s = strchr(str, i->tag); if (s) { unsigned long int value; From 4283a813d7089f8a28162995d779eae26f918067 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Jan 2026 02:22:00 -0300 Subject: [PATCH 0622/3297] perf tp_pmu: Address const-correctness errors in recent glibcs commit 0341eab66ba03a1f439db91f03bccd5b0a360842 upstream. To avoid having more variables, just cast the const variable searched to non-const since the result will not be modified, its only later that that variable will be used to modify something, but then its non-const memory being modified, so using a cast is the cheapest thing here. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/tp_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c index eddb9807131ab..c2be8c9f9084d 100644 --- a/tools/perf/util/tp_pmu.c +++ b/tools/perf/util/tp_pmu.c @@ -192,7 +192,7 @@ bool tp_pmu__have_event(struct perf_pmu *pmu __maybe_unused, const char *name) char *dup_name, *colon; int id; - colon = strchr(name, ':'); + colon = strchr((char *)name, ':'); if (colon == NULL) return false; From d03adc7039c393678a1b7156cbc6d64a9b1ca0ee Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 20 Jan 2026 18:26:44 -0300 Subject: [PATCH 0623/3297] perf list: Signal changing const memory is ok commit 9a0a5b5ac4372da84394dc329f763d6b7d384a86 upstream. In this case its a temp list that is created just for listing events and will be deleted at the end, so just cast it to get rid of the compiler warning. Reviewed-by: Ian Rogers Signed-off-by: Arnaldo Carvalho de Melo [ Also do it for print_symbol_events() - gregkh ] Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/print-events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index 4153124a9948e..d1ea6fa38636c 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -86,7 +86,7 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) strlist__for_each_entry(sdt_name, sdtlist) { bool show_detail = false; - char *bid = strchr(sdt_name->s, '@'); + char *bid = (char *)strchr(sdt_name->s, '@'); char *evt_name = NULL; if (bid) @@ -271,7 +271,7 @@ void print_symbol_events(const struct print_callbacks *print_cb, void *print_sta } strlist__for_each_entry(nd, evt_name_list) { - char *alias = strstr(nd->s, " OR "); + char *alias = (char *)strstr(nd->s, " OR "); if (alias) { *alias = '\0'; From 292f32503eda369ee316e1069251dc85eca0301a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 20 Jan 2026 18:16:09 -0300 Subject: [PATCH 0624/3297] perf list: Don't write to const memory commit 29132d16965e66fed0bf7b38242e7e57df294ba0 upstream. Something now detected on fedora 44, where strchr() returns const if it is passed a const pointer: util/print-events.c: In function 'print_sdt_events': util/print-events.c:89:29: error: initialization discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers] 89 | char *bid = strchr(sdt_name->s, '@'); | ^~~~~~ Fix it by using strchrnul() + strncmp() instead of temporarily scrubbing it with '\0'. Reviewed-by: Ian Rogers Suggested-by: David Laight Link: https://lore.kernel.org/r/20260121112536.27fd5d11@pumpkin Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/print-events.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index d1ea6fa38636c..f5f828d49ef03 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -97,14 +97,9 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) } else { next_sdt_name = strlist__next(sdt_name); if (next_sdt_name) { - char *bid2 = strchr(next_sdt_name->s, '@'); - - if (bid2) - *bid2 = '\0'; - if (strcmp(sdt_name->s, next_sdt_name->s) == 0) - show_detail = true; - if (bid2) - *bid2 = '@'; + const char *bid2 = strchrnul(next_sdt_name->s, '@'); + + show_detail = strncmp(sdt_name->s, next_sdt_name->s, bid2 - next_sdt_name->s) == 0; } } last_sdt_name = sdt_name->s; From bc29e0699b35deca0c3ec20d6ae995095231c2b4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Dec 2025 19:17:54 -0300 Subject: [PATCH 0625/3297] perf diff: Constify strchr() return variables commit f6f41aef53761517391b6192fe5b4bc30b2d717a upstream. Newer glibc versions return const char for strchr() when the 's' arg is const, change the return variable to const to match that. Also we don't need to turn that ',' into a '\0', as strtol() will stop in the first invalid char. No need to touch read only memory. First noticed with fedora 44. Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20251211221756.96294-3-acme@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/builtin-diff.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 53d5ea4a6a4f7..59bf1f72d12e2 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -178,10 +178,9 @@ static struct header_column { } }; -static int setup_compute_opt_wdiff(char *opt) +static int setup_compute_opt_wdiff(const char *opt) { - char *w1_str = opt; - char *w2_str; + const char *w1_str = opt, *w2_str; int ret = -EINVAL; @@ -192,8 +191,7 @@ static int setup_compute_opt_wdiff(char *opt) if (!w2_str) goto out; - *w2_str++ = 0x0; - if (!*w2_str) + if (!*++w2_str) goto out; compute_wdiff_w1 = strtol(w1_str, NULL, 10); @@ -214,7 +212,7 @@ static int setup_compute_opt_wdiff(char *opt) return ret; } -static int setup_compute_opt(char *opt) +static int setup_compute_opt(const char *opt) { if (compute == COMPUTE_WEIGHTED_DIFF) return setup_compute_opt_wdiff(opt); @@ -234,7 +232,7 @@ static int setup_compute(const struct option *opt, const char *str, char *cstr = (char *) str; char buf[50]; unsigned i; - char *option; + const char *option; if (!str) { *cp = COMPUTE_DELTA; From 30bbd7e8999d1275cd457f820aa2717c514ac405 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Dec 2025 19:17:56 -0300 Subject: [PATCH 0626/3297] perf trace: Don't change const char strings commit c85eff00cf296c146a2b189166eaf85188cd1487 upstream. We got away with this so far but now with fedora 44 complaining about the return value of strchr et all, lets use strdup for good measure. Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20251211221756.96294-5-acme@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/builtin-trace.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 72d10179e7f27..19ea05117e65e 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -5149,8 +5149,8 @@ static int trace__parse_events_option(const struct option *opt, const char *str, int unset __maybe_unused) { struct trace *trace = (struct trace *)opt->value; - const char *s = str; - char *sep = NULL, *lists[2] = { NULL, NULL, }; + const char *s; + char *strd, *sep = NULL, *lists[2] = { NULL, NULL, }; int len = strlen(str) + 1, err = -1, list, idx; char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); char group_name[PATH_MAX]; @@ -5159,6 +5159,10 @@ static int trace__parse_events_option(const struct option *opt, const char *str, if (strace_groups_dir == NULL) return -1; + s = strd = strdup(str); + if (strd == NULL) + return -1; + if (*s == '!') { ++s; trace->not_ev_qualifier = true; @@ -5233,8 +5237,7 @@ static int trace__parse_events_option(const struct option *opt, const char *str, free(strace_groups_dir); free(lists[0]); free(lists[1]); - if (sep) - *sep = ','; + free(strd); return err; } From 40e44eff5116d49b9c5ebab9854a0e2aa377b676 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 20 Jan 2026 17:57:43 -0300 Subject: [PATCH 0627/3297] perf trace: Deal with compiler const checks commit 2c850606a46b319d5128bda59f67b1fc642d94ef upstream. The strchr() function these days return const/non-const based on the arg it receives, and sometimes we need to use casts when we're dealing with variables that are used in code that needs to safely change the returned value and sometimes not (as it points to really const areas). Tweak one such case. Reviewed-by: Ian Rogers Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/builtin-trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 19ea05117e65e..2be8c3ed93c96 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -5169,7 +5169,7 @@ static int trace__parse_events_option(const struct option *opt, const char *str, } while (1) { - if ((sep = strchr(s, ',')) != NULL) + if ((sep = strchr((char *)s, ',')) != NULL) *sep = '\0'; list = 0; From bbf04810b2a0672c8bcfede41b5ba0391ec69f0b Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Tue, 14 Jul 2026 22:13:42 +0200 Subject: [PATCH 0628/3297] nvme-pci: DMA unmap the correct regions in nvme_free_sgls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a54afbc8a2138f8c2490510cf26cde188d480c43 upstream. The call to nvme_free_sgls() in nvme_unmap_data() has the sg_list and sge parameters swapped. This wasn't noticed by the compiler because both share the same type. On a Xen PV hardware domain, and possibly any other architectures that takes that path, this leads to corruption of the NVMe contents. Fixes: f0887e2a52d4 ("nvme-pci: create common sgl unmapping helper") Reviewed-by: Christoph Hellwig Signed-off-by: Roger Pau Monné Signed-off-by: Keith Busch [nb: drop the attrs parameter added in 6.19 by commit 61d43b1731e0 ("nvme-pci: migrate to dma_map_phys instead of map_page"), which is not in 6.18.y] Signed-off-by: Nicolai Buchwitz Signed-off-by: Sasha Levin --- drivers/nvme/host/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 5e36a5926fe03..8c66fd23a143c 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -761,8 +761,8 @@ static void nvme_unmap_data(struct request *req) if (!blk_rq_dma_unmap(req, dma_dev, &iod->dma_state, iod->total_len)) { if (nvme_pci_cmd_use_sgl(&iod->cmd)) - nvme_free_sgls(req, iod->descriptors[0], - &iod->cmd.common.dptr.sgl); + nvme_free_sgls(req, &iod->cmd.common.dptr.sgl, + iod->descriptors[0]); else nvme_free_prps(req); } From e0caf7c3d49c72f0f9036359e9d5478ae95c930c Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Sun, 28 Jun 2026 07:42:43 +0000 Subject: [PATCH 0629/3297] smb/server: do not require delete access for non-replacing links [ Upstream commit 851ed9e09639e0daf79a506ce26097b296ed5518 ] Reproducer: 1. server: systemctl start ksmbd 2. client: mount -t cifs //${server_ip}/export /mnt 3. client: touch /mnt/file; ln /mnt/file /mnt/hardlink 4. client err log: ln: failed to create hard link 'hardlink' => 'file': Permission denied 5. server err log: ksmbd: no right to delete : 0x80 Fixes: 13f3942f2bf4 ("ksmbd: add per-handle permission check to FILE_LINK_INFORMATION") Cc: stable@vger.kernel.org Reported-by: Steve French Signed-off-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smb2pdu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 50e36050f9ea3..44b87196c5afc 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6572,16 +6572,18 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } case FILE_LINK_INFORMATION: { - if (!(fp->daccess & FILE_DELETE_LE)) { - pr_err("no right to delete : 0x%x\n", fp->daccess); - return -EACCES; - } + struct smb2_file_link_info *file_info; if (buf_len < sizeof(struct smb2_file_link_info)) return -EINVAL; - return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)buffer, + file_info = (struct smb2_file_link_info *)buffer; + if (file_info->ReplaceIfExists && !(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + return smb2_create_link(work, work->tcon->share_conf, file_info, buf_len, fp->filp, work->conn->local_nls); } From de5a46f3b2c8d3cd20afa158bd3a725e3e3d6fd3 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 15 Jul 2026 14:07:04 +0200 Subject: [PATCH 0630/3297] tcp: Add preempt_{disable,enable}_nested() in reqsk_queue_hash_req(). [ Upstream commit e10902df24488ca722303133acfc82490f7d59ad ] syzbot reported a weird reqsk->rsk_refcnt underflow in __inet_csk_reqsk_queue_drop(). The captured reqsk_put() in __inet_csk_reqsk_queue_drop() is called only when it successfully removes reqsk from ehash. Moreover, reqsk_timer_handler() calls another reqsk_put() after that. This indicates that the reqsk was missing both refcnts for ehash and the timer itself. Since all the syzbot reports had PREEMPT_RT enabled, the only possible scenario is that reqsk_queue_hash_req() is preempted after mod_timer() and before refcount_set(), and then the timer triggered after 1s aborts the reqsk due to its listener's close(). Let's wrap mod_timer() and refcount_set() with preempt_disable_nested() and preempt_enable_nested(). Note that inet_ehash_insert() holds the normal spin_lock() (mutex in PREEMPT_RT), so it must be called outside of preempt_disable_nested(), but this is fine. The lookup path just ignores 0 sk_refcnt entries in ehash and tries to create another reqsk, but this will fail at inet_ehash_insert(). [0]: refcount_t: underflow; use-after-free. WARNING: lib/refcount.c:28 at refcount_warn_saturate+0xb2/0x110 lib/refcount.c:28, CPU#0: ktimers/0/16 Modules linked in: CPU: 0 UID: 0 PID: 16 Comm: ktimers/0 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)} Tainted: [L]=SOFTLOCKUP Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026 RIP: 0010:refcount_warn_saturate+0xb2/0x110 lib/refcount.c:28 Code: e4 7d d1 0a 67 48 0f b9 3a eb 4a e8 38 3d 23 fd 48 8d 3d e1 7d d1 0a 67 48 0f b9 3a eb 37 e8 25 3d 23 fd 48 8d 3d de 7d d1 0a <67> 48 0f b9 3a eb 24 e8 12 3d 23 fd 48 8d 3d db 7d d1 0a 67 48 0f RSP: 0000:ffffc90000157948 EFLAGS: 00010246 RAX: ffffffff84a1301b RBX: 0000000000000003 RCX: ffff88801ca98000 RDX: 0000000000000100 RSI: 0000000000000000 RDI: ffffffff8f72ae00 RBP: ffffffff99ae3b01 R08: ffff88801ca98000 R09: 0000000000000005 R10: 0000000000000100 R11: 0000000000000004 R12: ffff8880425ef568 R13: ffff8880425ef4f8 R14: ffff8880425ef578 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff888126386000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f7b46710e9c CR3: 000000000dbb6000 CR4: 00000000003526f0 Call Trace: __refcount_sub_and_test include/linux/refcount.h:400 [inline] __refcount_dec_and_test include/linux/refcount.h:432 [inline] refcount_dec_and_test include/linux/refcount.h:450 [inline] reqsk_put include/net/request_sock.h:136 [inline] __inet_csk_reqsk_queue_drop+0x3ce/0x440 net/ipv4/inet_connection_sock.c:1007 reqsk_timer_handler+0x651/0xdf0 net/ipv4/inet_connection_sock.c:1137 call_timer_fn+0x192/0x5e0 kernel/time/timer.c:1748 expire_timers kernel/time/timer.c:1799 [inline] __run_timers kernel/time/timer.c:2374 [inline] __run_timer_base+0x6a3/0x9f0 kernel/time/timer.c:2386 run_timer_base kernel/time/timer.c:2395 [inline] run_timer_softirq+0x67/0x170 kernel/time/timer.c:2403 handle_softirqs+0x1de/0x6d0 kernel/softirq.c:622 __do_softirq kernel/softirq.c:656 [inline] run_ktimerd+0x69/0x100 kernel/softirq.c:1151 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fixes: d2d6422f8bd1 ("x86: Allow to enable PREEMPT_RT.") Reported-by: syzbot+e809069bc15f26300526@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a1a7bcf.0a9e871e.332604.000b.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260601182101.3183993-1-kuniyu@google.com Signed-off-by: Jakub Kicinski [updated to not require timeout changes from commit 3ce5dd8161ec ("tcp: Remove timeout arg from reqsk_queue_hash_req()") DCCP was retired by commit 2a63dd0edf38 ("net: Retire DCCP socket.") after the release of 6.12, so the shared inet_csk_reqsk_queue_hash_add still requires the timout argument] Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- net/ipv4/inet_connection_sock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index c777895a720ee..4aa5cdef8e42c 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1163,6 +1163,9 @@ static bool reqsk_queue_hash_req(struct request_sock *req, /* The timer needs to be setup after a successful insertion. */ timer_setup(&req->rsk_timer, reqsk_timer_handler, TIMER_PINNED); + + preempt_disable_nested(); + mod_timer(&req->rsk_timer, jiffies + timeout); /* before letting lookups find us, make sure all req fields @@ -1170,6 +1173,9 @@ static bool reqsk_queue_hash_req(struct request_sock *req, */ smp_wmb(); refcount_set(&req->rsk_refcnt, 2 + 1); + + preempt_enable_nested(); + return true; } From d7860b682da55433b5da0591b0e4c1982ecd2689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20Kaan=20KILIN=C3=87?= Date: Thu, 16 Jul 2026 02:58:36 +0000 Subject: [PATCH 0631/3297] crypto: algif_skcipher - force synchronous processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AIO/async path in skcipher_recvmsg() passes the socket-wide ctx->iv directly into the skcipher request. After io_submit() the socket lock is dropped and the request is processed asynchronously by a worker (e.g. cryptd), which dereferences ctx->iv only later. A concurrent sendmsg(ALG_SET_IV) on the same socket can overwrite ctx->iv inside this window, so the in-flight request runs under an attacker-controlled IV. For CTR and other stream modes this causes IV/keystream reuse and allows an unprivileged user to recover the plaintext of a concurrent operation. Snapshotting ctx->iv into per-request storage for the async path is not sufficient here. For ciphers with statesize == 0 - which includes cbc and ctr - skcipher_prepare_alg() installs skcipher_noimport()/ skcipher_noexport(), so ctx->state carries nothing and the MSG_MORE inter-chunk IV chaining is carried solely by the in-place req->iv writeback. A snapshot redirects that writeback into per-request memory that af_alg_free_resources() releases on completion, so AIO + MSG_MORE with cbc/ctr would silently produce wrong output. Writing the IV back from the completion callback instead is not possible either: that would require lock_sock() there, but the callback can run in softirq/atomic context, so it must not sleep. Make the operation synchronous instead. ctx->iv is then only ever dereferenced under the socket lock held by recvmsg(), which removes the race, and the req->iv writeback lands in ctx->iv as before, which keeps MSG_MORE chaining intact for statesize == 0 ciphers. The ctx->state import/export path is unchanged for ciphers that do have state. This is equivalent to the upstream resolution: commit fcc77d33a34c ("net: Remove support for AIO on sockets") removed the AIO socket path across net/ entirely, producing the same end state for this file - algif_skcipher never processes an AIO request asynchronously. After this patch, _skcipher_recvmsg() matches mainline's crypto/algif_skcipher.c as it stands today, including the same now-dead -EIOCBQUEUED check. This patch deviates from that commit deliberately: rather than removing AIO socket support tree-wide, which would be far too invasive for stable, it removes only the AIO branch in crypto/algif_skcipher.c. io_submit() now completes synchronously, which is valid for the AIO interface; AF_ALG async is rarely used in practice. The -EIOCBQUEUED check in skcipher_recvmsg() is now dead but harmless, and is left alone to keep the fix minimal. Fixes: e870456d8e7c ("crypto: algif_skcipher - overhaul memory management") Cc: Reported-by: Muhammet Kaan KILINÇ Signed-off-by: Muhammet Kaan KILINÇ Signed-off-by: Sasha Levin --- crypto/algif_skcipher.c | 75 +++++++++++++---------------------------- 1 file changed, 24 insertions(+), 51 deletions(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index ba0a17fd95aca..35ebc3e0201b0 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -79,20 +79,6 @@ static int algif_skcipher_export(struct sock *sk, struct skcipher_request *req) return err; } -static void algif_skcipher_done(void *data, int err) -{ - struct af_alg_async_req *areq = data; - struct sock *sk = areq->sk; - - if (err) - goto out; - - err = algif_skcipher_export(sk, &areq->cra_u.skcipher_req); - -out: - af_alg_async_cb(data, err); -} - static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) { @@ -171,43 +157,30 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, cflags |= CRYPTO_SKCIPHER_REQ_CONT; } - if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) { - /* AIO operation */ - sock_hold(sk); - areq->iocb = msg->msg_iocb; - - /* Remember output size that will be generated. */ - areq->outlen = len; - - skcipher_request_set_callback(&areq->cra_u.skcipher_req, - cflags | - CRYPTO_TFM_REQ_MAY_SLEEP, - algif_skcipher_done, areq); - err = ctx->enc ? - crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : - crypto_skcipher_decrypt(&areq->cra_u.skcipher_req); - - /* AIO operation in progress */ - if (err == -EINPROGRESS) - return -EIOCBQUEUED; - - sock_put(sk); - } else { - /* Synchronous operation */ - skcipher_request_set_callback(&areq->cra_u.skcipher_req, - cflags | - CRYPTO_TFM_REQ_MAY_SLEEP | - CRYPTO_TFM_REQ_MAY_BACKLOG, - crypto_req_done, &ctx->wait); - err = crypto_wait_req(ctx->enc ? - crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : - crypto_skcipher_decrypt(&areq->cra_u.skcipher_req), - &ctx->wait); - - if (!err) - err = algif_skcipher_export( - sk, &areq->cra_u.skcipher_req); - } + /* + * Force synchronous processing. The async (AIO) path passed the + * socket-wide ctx->iv into the request, which the worker + * dereferenced after the socket lock had been dropped, letting a + * concurrent sendmsg(ALG_SET_IV) inject an attacker IV. Mainline + * removed the AIO socket path in commit fcc77d33a34c ("net: Remove + * support for AIO on sockets"); the minimal stable fix is to always + * complete synchronously, so ctx->iv is only ever dereferenced under + * the socket lock. This also keeps the IV chaining intact: for + * ciphers with statesize == 0 (e.g. ctr, cbc) the chained IV is + * carried by the req->iv writeback into ctx->iv, which is only + * consistent on the synchronous path. + */ + skcipher_request_set_callback(&areq->cra_u.skcipher_req, + cflags | + CRYPTO_TFM_REQ_MAY_SLEEP | + CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &ctx->wait); + err = crypto_wait_req(ctx->enc ? + crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : + crypto_skcipher_decrypt(&areq->cra_u.skcipher_req), + &ctx->wait); + if (!err) + err = algif_skcipher_export(sk, &areq->cra_u.skcipher_req); free: af_alg_free_resources(areq); From c646431865f4b1a5b14067233fa27b11e05e0d46 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Thu, 16 Jul 2026 14:24:34 +0100 Subject: [PATCH 0632/3297] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU commit 8cc8bbbfab14c22c5551d0dd19b208a44b141c76 upstream. flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU on every run. The vGIC list register save and restore use used_lrs as their loop bound and expect it to stay within the number of implemented list registers. While this is generally the case, flush_hyp_vcpu() copies vgic_v3 verbatim and does not enforce this, so a value provided by the host is used at EL2 to index vgic_lr[] and access ICH_LR_EL2 (host -> EL2). Fix by clamping used_lrs to the number of implemented list registers after the copy, as the trusted path already does in vgic_flush_lr_state(). The number of implemented list registers is constant after init, so it is replicated once from kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr rather than read on every entry. Cc: stable@vger.kernel.org Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") Signed-off-by: Hyunwoo Kim Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260606175614.83273-3-imv4bel@gmail.com Signed-off-by: Marc Zyngier [ tabba: adjust context in flush_hyp_vcpu() and kvm_hyp.h ] Signed-off-by: Fuad Tabba Signed-off-by: Sasha Levin --- arch/arm64/include/asm/kvm_hyp.h | 1 + arch/arm64/kvm/arm.c | 2 ++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index e6be1f5d0967f..8020e91b536d2 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -146,5 +146,6 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val); extern unsigned long kvm_nvhe_sym(__icache_flags); extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); +extern unsigned int kvm_nvhe_sym(hyp_gicv3_nr_lr); #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index e59cb36b5f36f..10fc6783cd608 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -2311,6 +2311,8 @@ static int __init init_subsystems(void) switch (err) { case 0: vgic_present = true; + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + kvm_nvhe_sym(hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr; break; case -ENODEV: case -ENXIO: diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 3dda50391446d..6e7547b7ca432 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -22,6 +22,9 @@ DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); +/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */ +unsigned int hyp_gicv3_nr_lr; + void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt); static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu) @@ -139,6 +142,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2; hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3; + + /* Bound used_lrs by the number of implemented list registers. */ + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs = + min_t(unsigned int, + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs, + hyp_gicv3_nr_lr); } static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) From 588718101e8449605f1c7e858fecb7cfa701cdab Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 4 Jun 2026 14:03:07 +0800 Subject: [PATCH 0633/3297] iommu/vt-d: Clear Present bit before tearing down scalable-mode context entry [ Upstream commit f46452c3df7a8d8a5addc0926e76ef19ea7da0a0 ] device_pasid_table_teardown() zeroes the 128-bit scalable-mode context entry with context_clear_entry() while the Present bit is still set. This creates a window where the hardware can fetch a torn entry, with some fields already zeroed while Present is still set, leading to unpredictable behavior or spurious faults. The context-cache invalidation is issued only after the entry has been zeroed, and intel_pasid_free_table() then frees the PASID directory pages, so the IOMMU can keep walking a stale Present=1 entry that points at freed memory. While x86 provides strong write ordering, the compiler may reorder the two 64-bit writes to the entry, and the hardware fetch is not guaranteed to be atomic with respect to multiple CPU writes. Commit c1e4f1dccbe9d ("iommu/vt-d: Clear Present bit before tearing down context entry") fixed this exact pattern in domain_context_clear_one() and the copied-context path, but device_pasid_table_teardown() was not converted. Align it with the "Guidance to Software for Invalidations" in the VT-d spec, Section 6.5.3.3, using the same ownership handshake as the sibling fix: clear only the Present bit, flush it to the IOMMU, perform the context-cache invalidation, and only then zero the rest of the entry. Fixes: 81e921fd32161 ("iommu/vt-d: Fix NULL domain on device release") Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Link: https://lore.kernel.org/r/20260528025557.3209367-1-michael.bommarito@gmail.com Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/intel/pasid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c index 787897e61efa3..85c3116e351c6 100644 --- a/drivers/iommu/intel/pasid.c +++ b/drivers/iommu/intel/pasid.c @@ -746,10 +746,12 @@ static void device_pasid_table_teardown(struct device *dev, u8 bus, u8 devfn) } did = context_domain_id(context); - context_clear_entry(context); + context_clear_present(context); __iommu_flush_cache(iommu, context, sizeof(*context)); spin_unlock(&iommu->lock); intel_context_flush_no_pasid(info, context, did); + context_clear_entry(context); + __iommu_flush_cache(iommu, context, sizeof(*context)); } static int pci_pasid_table_teardown(struct pci_dev *pdev, u16 alias, void *data) From 2ed3c9d955e8cd6361f130623baa664a75fb345f Mon Sep 17 00:00:00 2001 From: Shivam Kumar Date: Wed, 18 Mar 2026 18:56:58 -0400 Subject: [PATCH 0634/3297] nvmet-tcp: check INIT_FAILED before nvmet_req_uninit in digest error path commit 4606467a75cfc16721937272ed29462a750b60c8 upstream. In nvmet_tcp_try_recv_ddgst(), when a data digest mismatch is detected, nvmet_req_uninit() is called unconditionally. However, if the command arrived via the nvmet_tcp_handle_req_failure() path, nvmet_req_init() had returned false and percpu_ref_tryget_live() was never executed. The unconditional percpu_ref_put() inside nvmet_req_uninit() then causes a refcount underflow, leading to a WARNING in percpu_ref_switch_to_atomic_rcu, a use-after-free diagnostic, and eventually a permanent workqueue deadlock. Check cmd->flags & NVMET_TCP_F_INIT_FAILED before calling nvmet_req_uninit(), matching the existing pattern in nvmet_tcp_execute_request(). Reviewed-by: Christoph Hellwig Signed-off-by: Shivam Kumar Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 97d8c1d3545b0..d90f8b55b5934 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1344,7 +1344,8 @@ static int nvmet_tcp_try_recv_ddgst(struct nvmet_tcp_queue *queue) queue->idx, cmd->req.cmd->common.command_id, queue->pdu.cmd.hdr.type, le32_to_cpu(cmd->recv_ddgst), le32_to_cpu(cmd->exp_ddgst)); - nvmet_req_uninit(&cmd->req); + if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) + nvmet_req_uninit(&cmd->req); nvmet_tcp_free_cmd_buffers(cmd); nvmet_tcp_fatal_error(queue); ret = -EPROTO; From 088ee46c18d99baef453afd74181dd40ade044ad Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Sun, 10 May 2026 23:30:29 +0300 Subject: [PATCH 0635/3297] nvmet-tcp: Fix potential UAF when ddgst mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit dbbd07d0a7020b80f6a7028e561908f7b83b3d5a upstream. Shivam Kumar found via vulnerability testing: When data digest is enabled on an NVMe/TCP connection and a digest mismatch occurs on a non-final H2C_DATA PDU during an R2T-based data transfer, the digest error handler in nvmet_tcp_try_recv_ddgst() calls nvmet_req_uninit() — which performs percpu_ref_put() on the submission queue — but does NOT mark the command as completed. It does not set cqe->status, does not modify rbytes_done, and does not clear any flag. When the subsequent fatal error triggers queue teardown, nvmet_tcp_uninit_data_in_cmds() iterates all commands, checks nvmet_tcp_need_data_in() for each one, and finds that the already-uninited command still appears to need data (because rbytes_done < transfer_len and cqe->status == 0). It therefore calls nvmet_req_uninit() a second time on the same command — a double percpu_ref_put against a single percpu_ref_get. Reported-by: Shivam Kumar Reviewed-by: Christoph Hellwig Signed-off-by: Sagi Grimberg Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/tcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index d90f8b55b5934..0bc66644a3e66 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1344,8 +1344,10 @@ static int nvmet_tcp_try_recv_ddgst(struct nvmet_tcp_queue *queue) queue->idx, cmd->req.cmd->common.command_id, queue->pdu.cmd.hdr.type, le32_to_cpu(cmd->recv_ddgst), le32_to_cpu(cmd->exp_ddgst)); - if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) + if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) { + cmd->req.cqe->status = NVME_SC_CMD_SEQ_ERROR; nvmet_req_uninit(&cmd->req); + } nvmet_tcp_free_cmd_buffers(cmd); nvmet_tcp_fatal_error(queue); ret = -EPROTO; From c401492e01c7bfd38cf14c94d85c7efafe7d1a25 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 16 Jul 2026 20:50:58 -0700 Subject: [PATCH 0636/3297] crypto: sun4i-ss - Remove insecure and unused rng_alg commit b2c41fa9dd8fc740c489e060b199165771f268d1 upstream. Remove sun4i_ss_rng, as it is insecure and unused: - It has multiple vulnerabilities. sun4i_ss_prng_seed() is missing locking and has a buffer overflow. sun4i_ss_prng_generate() fails to fill the entire buffer with cryptographic random bytes, because it rounds the destination length down and also doesn't actually wait for the hardware to be ready before pulling bytes from it. - No user of this code is known. It's usable only theoretically via the "rng" algorithm type of AF_ALG. But userspace actually just uses the actual Linux RNG (/dev/random etc) instead. And rng_algs don't contribute entropy to the actual Linux RNG either. (This may have been confused with hwrng, which does contribute entropy.) The sun4i_ss_prng_seed() buffer overflow was reported by Tianchu Chen and discovered by Atuin - Automated Vulnerability Discovery Engine There's no point in fixing all these vulnerabilities individually when this is unused code, so let's just remove it. Fixes: b8ae5c7387ad ("crypto: sun4i-ss - support the Security System PRNG") Cc: stable@vger.kernel.org Reported-by: Tianchu Chen Closes: https://lore.kernel.org/r/af749a8447bd7f0e9dd26ca6c87e9c6afecb09d9@linux.dev/ Acked-by: Corentin LABBE Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- arch/arm/configs/sunxi_defconfig | 1 - drivers/crypto/allwinner/Kconfig | 8 --- drivers/crypto/allwinner/sun4i-ss/Makefile | 1 - .../crypto/allwinner/sun4i-ss/sun4i-ss-core.c | 36 ---------- .../crypto/allwinner/sun4i-ss/sun4i-ss-prng.c | 69 ------------------- drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h | 20 ------ 6 files changed, 135 deletions(-) delete mode 100644 drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig index a83d29fed1756..f4b8d8f7dbefb 100644 --- a/arch/arm/configs/sunxi_defconfig +++ b/arch/arm/configs/sunxi_defconfig @@ -170,7 +170,6 @@ CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_CRYPTO_DEV_SUN4I_SS=y -CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG=y CONFIG_CRYPTO_DEV_SUN8I_CE=y CONFIG_CRYPTO_DEV_SUN8I_SS=y CONFIG_DMA_CMA=y diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig index b8e75210a0e31..06ea0e9fe6f22 100644 --- a/drivers/crypto/allwinner/Kconfig +++ b/drivers/crypto/allwinner/Kconfig @@ -24,14 +24,6 @@ config CRYPTO_DEV_SUN4I_SS To compile this driver as a module, choose M here: the module will be called sun4i-ss. -config CRYPTO_DEV_SUN4I_SS_PRNG - bool "Support for Allwinner Security System PRNG" - depends on CRYPTO_DEV_SUN4I_SS - select CRYPTO_RNG - help - Select this option if you want to provide kernel-side support for - the Pseudo-Random Number Generator found in the Security System. - config CRYPTO_DEV_SUN4I_SS_DEBUG bool "Enable sun4i-ss stats" depends on CRYPTO_DEV_SUN4I_SS diff --git a/drivers/crypto/allwinner/sun4i-ss/Makefile b/drivers/crypto/allwinner/sun4i-ss/Makefile index c0a2797d31682..06a9ae81f9f80 100644 --- a/drivers/crypto/allwinner/sun4i-ss/Makefile +++ b/drivers/crypto/allwinner/sun4i-ss/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sun4i-ss.o sun4i-ss-y += sun4i-ss-core.o sun4i-ss-hash.o sun4i-ss-cipher.o -sun4i-ss-$(CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG) += sun4i-ss-prng.o diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c index 58a76e2ba64e2..35ef0930e77f1 100644 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c +++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c @@ -213,23 +213,6 @@ static struct sun4i_ss_alg_template ss_algs[] = { } } }, -#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG -{ - .type = CRYPTO_ALG_TYPE_RNG, - .alg.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "sun4i_ss_rng", - .cra_priority = 300, - .cra_ctxsize = 0, - .cra_module = THIS_MODULE, - }, - .generate = sun4i_ss_prng_generate, - .seed = sun4i_ss_prng_seed, - .seedsize = SS_SEED_LEN / BITS_PER_BYTE, - } -}, -#endif }; static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v) @@ -247,12 +230,6 @@ static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v) ss_algs[i].stat_req, ss_algs[i].stat_opti, ss_algs[i].stat_fb, ss_algs[i].stat_bytes); break; - case CRYPTO_ALG_TYPE_RNG: - seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n", - ss_algs[i].alg.rng.base.cra_driver_name, - ss_algs[i].alg.rng.base.cra_name, - ss_algs[i].stat_req, ss_algs[i].stat_bytes); - break; case CRYPTO_ALG_TYPE_AHASH: seq_printf(seq, "%s %s reqs=%lu\n", ss_algs[i].alg.hash.halg.base.cra_driver_name, @@ -471,13 +448,6 @@ static int sun4i_ss_probe(struct platform_device *pdev) goto error_alg; } break; - case CRYPTO_ALG_TYPE_RNG: - err = crypto_register_rng(&ss_algs[i].alg.rng); - if (err) { - dev_err(ss->dev, "Fail to register %s\n", - ss_algs[i].alg.rng.base.cra_name); - } - break; } } @@ -497,9 +467,6 @@ static int sun4i_ss_probe(struct platform_device *pdev) case CRYPTO_ALG_TYPE_AHASH: crypto_unregister_ahash(&ss_algs[i].alg.hash); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&ss_algs[i].alg.rng); - break; } } error_pm: @@ -520,9 +487,6 @@ static void sun4i_ss_remove(struct platform_device *pdev) case CRYPTO_ALG_TYPE_AHASH: crypto_unregister_ahash(&ss_algs[i].alg.hash); break; - case CRYPTO_ALG_TYPE_RNG: - crypto_unregister_rng(&ss_algs[i].alg.rng); - break; } } diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c deleted file mode 100644 index 491fcb7b81b40..0000000000000 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include "sun4i-ss.h" - -int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct sun4i_ss_alg_template *algt; - struct rng_alg *alg = crypto_rng_alg(tfm); - - algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng); - memcpy(algt->ss->seed, seed, slen); - - return 0; -} - -int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen) -{ - struct sun4i_ss_alg_template *algt; - struct rng_alg *alg = crypto_rng_alg(tfm); - int i, err; - u32 v; - u32 *data = (u32 *)dst; - const u32 mode = SS_OP_PRNG | SS_PRNG_CONTINUE | SS_ENABLED; - size_t len; - struct sun4i_ss_ctx *ss; - unsigned int todo = (dlen / 4) * 4; - - algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng); - ss = algt->ss; - - err = pm_runtime_resume_and_get(ss->dev); - if (err < 0) - return err; - - if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN4I_SS_DEBUG)) { - algt->stat_req++; - algt->stat_bytes += todo; - } - - spin_lock_bh(&ss->slock); - - writel(mode, ss->base + SS_CTL); - - while (todo > 0) { - /* write the seed */ - for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++) - writel(ss->seed[i], ss->base + SS_KEY0 + i * 4); - - /* Read the random data */ - len = min_t(size_t, SS_DATA_LEN / BITS_PER_BYTE, todo); - readsl(ss->base + SS_TXFIFO, data, len / 4); - data += len / 4; - todo -= len; - - /* Update the seed */ - for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++) { - v = readl(ss->base + SS_KEY0 + i * 4); - ss->seed[i] = v; - } - } - - writel(0, ss->base + SS_CTL); - spin_unlock_bh(&ss->slock); - - pm_runtime_put(ss->dev); - - return 0; -} diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h index 6c5d4aa6453c7..f7d1c79ac677d 100644 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h +++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h @@ -31,8 +31,6 @@ #include #include #include -#include -#include #define SS_CTL 0x00 #define SS_KEY0 0x04 @@ -62,10 +60,6 @@ /* SS_CTL configuration values */ -/* PRNG generator mode - bit 15 */ -#define SS_PRNG_ONESHOT (0 << 15) -#define SS_PRNG_CONTINUE (1 << 15) - /* IV mode for hash */ #define SS_IV_ARBITRARY (1 << 14) @@ -94,14 +88,10 @@ #define SS_OP_3DES (2 << 4) #define SS_OP_SHA1 (3 << 4) #define SS_OP_MD5 (4 << 4) -#define SS_OP_PRNG (5 << 4) /* Data end bit - bit 2 */ #define SS_DATA_END (1 << 2) -/* PRNG start bit - bit 1 */ -#define SS_PRNG_START (1 << 1) - /* SS Enable bit - bit 0 */ #define SS_DISABLED (0 << 0) #define SS_ENABLED (1 << 0) @@ -128,9 +118,6 @@ #define SS_RXFIFO_EMP_INT_ENABLE (1 << 2) #define SS_TXFIFO_AVA_INT_ENABLE (1 << 0) -#define SS_SEED_LEN 192 -#define SS_DATA_LEN 160 - /* * struct ss_variant - Describe SS hardware variant * @sha1_in_be: The SHA1 digest is given by SS in BE, and so need to be inverted. @@ -151,9 +138,6 @@ struct sun4i_ss_ctx { char buf[4 * SS_RX_MAX];/* buffer for linearize SG src */ char bufo[4 * SS_TX_MAX]; /* buffer for linearize SG dst */ spinlock_t slock; /* control the use of the device */ -#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG - u32 seed[SS_SEED_LEN / BITS_PER_LONG]; -#endif struct dentry *dbgfs_dir; struct dentry *dbgfs_stats; }; @@ -164,7 +148,6 @@ struct sun4i_ss_alg_template { union { struct skcipher_alg crypto; struct ahash_alg hash; - struct rng_alg rng; } alg; struct sun4i_ss_ctx *ss; unsigned long stat_req; @@ -231,6 +214,3 @@ int sun4i_ss_des_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen); int sun4i_ss_des3_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen); -int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen); -int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen); From 3659deaf7bf690510899a73906b772740d94342c Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 16 Mar 2026 11:58:22 -0400 Subject: [PATCH 0637/3297] media: uvcvideo: Fix deadlock if uvc_status_stop is called from async_ctrl.work [ Upstream commit 6d27f92c54ce28cfbd2a8a479a96d6f4a781b7d2 ] If a UVC camera has an asynchronous control, uvc_status_stop may be called from async_ctrl.work: uvc_ctrl_status_event_work() uvc_ctrl_status_event() uvc_ctrl_clear_handle() uvc_pm_put() uvc_status_put() uvc_status_stop() cancel_work_sync() This will cause a deadlock, since cancel_work_sync will wait for uvc_ctrl_status_event_work to complete before returning. Fix this by returning early from uvc_status_stop if we are currently in the work function. flush_status now remains false until uvc_status_start is called again, ensuring that uvc_ctrl_status_event_work won't resubmit the URB. Fixes: a32d9c41bdb8 ("media: uvcvideo: Make power management granular") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/6733bdfb-3e88-479f-8956-ab09c04c433e@linux.dev/ Signed-off-by: Sean Anderson Link: https://patch.msgid.link/20260316155823.1855434-1-sean.anderson@linux.dev Reviewed-by: Ricardo Ribalda Tested-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/usb/uvc/uvc_status.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 231cfee8e7c2c..7de074006c6a9 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -316,6 +316,16 @@ static int uvc_status_start(struct uvc_device *dev, gfp_t flags) if (!dev->int_urb) return 0; + /* + * If the previous uvc_status_stop() call was from the async work, + * the work may still be running. Wait for it to finish before we submit + * the urb. + */ + flush_work(&dev->async_ctrl.work); + + /* Clear the flush status if we were previously stopped. */ + smp_store_release(&dev->flush_status, false); + return usb_submit_urb(dev->int_urb, flags); } @@ -336,6 +346,15 @@ static void uvc_status_stop(struct uvc_device *dev) */ smp_store_release(&dev->flush_status, true); + /* + * If we are called from the event work function, the URB is guaranteed + * to not be in flight as it has completed and has not been resubmitted. + * There's no need to cancel the work (which would deadlock), or to kill + * the URB. + */ + if (current_work() == &w->work) + return; + /* * Cancel any pending asynchronous work. If any status event was queued, * process it synchronously. @@ -354,15 +373,6 @@ static void uvc_status_stop(struct uvc_device *dev) */ if (cancel_work_sync(&w->work)) uvc_ctrl_status_event(w->chain, w->ctrl, w->data); - - /* - * From this point, there are no events on the queue and the status URB - * is dead. No events will be queued until uvc_status_start() is called. - * The barrier is needed to make sure that flush_status is visible to - * uvc_ctrl_status_event_work() when uvc_status_start() will be called - * again. - */ - smp_store_release(&dev->flush_status, false); } int uvc_status_resume(struct uvc_device *dev) From 564e4ce9fb4011531f64f8986951e43c42852456 Mon Sep 17 00:00:00 2001 From: Puranjay Mohan Date: Fri, 17 Jul 2026 12:47:24 +0800 Subject: [PATCH 0638/3297] selftests: bpf: Add test for multiple syncs from linked register commit 086c99fbe45070d02851427eab5ae26fe7d0f3c0 upstream. Before the last commit, sync_linked_regs() corrupted the register whose bounds are being updated by copying known_reg's id to it. The ids are the same in value but known_reg has the BPF_ADD_CONST flag which is wrongly copied to reg. This later causes issues when creating new links to this reg. assign_scalar_id_before_mov() sees this BPF_ADD_CONST and gives a new id to this register and breaks the old links. This is exposed by the added selftest. Signed-off-by: Puranjay Mohan Tested-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260115151143.1344724-3-puranjay@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- .../bpf/progs/verifier_linked_scalars.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c index 8f755d2464cf5..5f41bbb730a71 100644 --- a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c +++ b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c @@ -31,4 +31,37 @@ l1: \ " ::: __clobber_all); } +/* + * Test that sync_linked_regs() preserves register IDs. + * + * The sync_linked_regs() function copies bounds from known_reg to linked + * registers. When doing so, it must preserve each register's original id + * to allow subsequent syncs from the same source to work correctly. + * + */ +SEC("socket") +__success +__naked void sync_linked_regs_preserves_id(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; /* r0 in [0, 255] */ \ + r1 = r0; /* r0, r1 linked with id 1 */ \ + r1 += 4; /* r1 has id=1 and off=4 in [4, 259] */ \ + if r1 < 10 goto l0_%=; \ + /* r1 in [10, 259], r0 synced to [6, 255] */ \ + r2 = r0; /* r2 has id=1 and in [6, 255] */ \ + if r1 < 14 goto l0_%=; \ + /* r1 in [14, 259], r0 synced to [10, 255] */ \ + if r0 >= 10 goto l0_%=; \ + /* Never executed */ \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; From 07259d661c9d43205f7a7f20f4328b2a2a6f0533 Mon Sep 17 00:00:00 2001 From: Puranjay Mohan Date: Fri, 17 Jul 2026 12:47:25 +0800 Subject: [PATCH 0639/3297] selftests/bpf: Add tests for improved linked register tracking commit 47fcf4dc0a346dd0b873a679c547d6848bd85a37 upstream. Add tests for linked register tracking with negative offsets, BPF_SUB, and alu32. These test for all edge cases like overflows, etc. Signed-off-by: Puranjay Mohan Acked-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260204151741.2678118-3-puranjay@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- .../bpf/progs/verifier_linked_scalars.c | 303 +++++++++++++++++- 1 file changed, 301 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c index 5f41bbb730a71..2ef346c827c25 100644 --- a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c +++ b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include "bpf_misc.h" @@ -18,9 +19,9 @@ __naked void scalars(void) r4 = r1; \ w2 += 0x7FFFFFFF; \ w4 += 0; \ - if r2 == 0 goto l1; \ + if r2 == 0 goto l0_%=; \ exit; \ -l1: \ +l0_%=: \ r4 >>= 63; \ r3 = 1; \ r3 -= r4; \ @@ -64,4 +65,302 @@ l0_%=: \ : __clobber_all); } +SEC("socket") +__success +__naked void scalars_neg(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; \ + r1 = r0; \ + r1 += -4; \ + if r1 s< 0 goto l0_%=; \ + if r0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Same test but using BPF_SUB instead of BPF_ADD with negative immediate */ +SEC("socket") +__success +__naked void scalars_neg_sub(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; \ + r1 = r0; \ + r1 -= 4; \ + if r1 s< 0 goto l0_%=; \ + if r0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* alu32 with negative offset */ +SEC("socket") +__success +__naked void scalars_neg_alu32_add(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w0 &= 0xff; \ + w1 = w0; \ + w1 += -4; \ + if w1 s< 0 goto l0_%=; \ + if w0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* alu32 with negative offset using SUB */ +SEC("socket") +__success +__naked void scalars_neg_alu32_sub(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w0 &= 0xff; \ + w1 = w0; \ + w1 -= 4; \ + if w1 s< 0 goto l0_%=; \ + if w0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Positive offset: r1 = r0 + 4, then if r1 >= 6, r0 >= 2, so r0 != 0 */ +SEC("socket") +__success +__naked void scalars_pos(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; \ + r1 = r0; \ + r1 += 4; \ + if r1 < 6 goto l0_%=; \ + if r0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* SUB with negative immediate: r1 -= -4 is equivalent to r1 += 4 */ +SEC("socket") +__success +__naked void scalars_sub_neg_imm(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; \ + r1 = r0; \ + r1 -= -4; \ + if r1 < 6 goto l0_%=; \ + if r0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Double ADD clears the ID (can't accumulate offsets) */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_double_add(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; \ + r1 = r0; \ + r1 += 2; \ + r1 += 2; \ + if r1 < 6 goto l0_%=; \ + if r0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* + * Test that sync_linked_regs() correctly handles large offset differences. + * r1.off = S32_MIN, r2.off = 1, delta = S32_MIN - 1 requires 64-bit math. + */ +SEC("socket") +__success +__naked void scalars_sync_delta_overflow(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; \ + r1 = r0; \ + r2 = r0; \ + r1 += %[s32_min]; \ + r2 += 1; \ + if r2 s< 100 goto l0_%=; \ + if r1 s< 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32), + [s32_min]"i"(INT_MIN) + : __clobber_all); +} + +/* + * Another large delta case: r1.off = S32_MAX, r2.off = -1. + * delta = S32_MAX - (-1) = S32_MAX + 1 requires 64-bit math. + */ +SEC("socket") +__success +__naked void scalars_sync_delta_overflow_large_range(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r0 &= 0xff; \ + r1 = r0; \ + r2 = r0; \ + r1 += %[s32_max]; \ + r2 += -1; \ + if r2 s< 0 goto l0_%=; \ + if r1 s>= 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32), + [s32_max]"i"(INT_MAX) + : __clobber_all); +} + +/* + * Test linked scalar tracking with alu32 and large positive offset (0x7FFFFFFF). + * After w1 += 0x7FFFFFFF, w1 wraps to negative for any r0 >= 1. + * If w1 is signed-negative, then r0 >= 1, so r0 != 0. + */ +SEC("socket") +__success +__naked void scalars_alu32_big_offset(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w0 &= 0xff; \ + w1 = w0; \ + w1 += 0x7FFFFFFF; \ + if w1 s>= 0 goto l0_%=; \ + if w0 != 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_alu32_basic(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r1 = r0; \ + w1 += 1; \ + if r1 > 10 goto 1f; \ + r0 >>= 32; \ + if r0 == 0 goto 1f; \ + r0 /= 0; \ +1: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* + * Test alu32 linked register tracking with wrapping. + * R0 is bounded to [0xffffff00, 0xffffffff] (high 32-bit values) + * w1 += 0x100 causes R1 to wrap to [0, 0xff] + * + * After sync_linked_regs, if bounds are computed correctly: + * R0 should be [0x00000000_ffffff00, 0x00000000_ffffff80] + * R0 >> 32 == 0, so div by zero is unreachable + * + * If bounds are computed incorrectly (64-bit underflow): + * R0 becomes [0xffffffff_ffffff00, 0xffffffff_ffffff80] + * R0 >> 32 == 0xffffffff != 0, so div by zero is reachable + */ +SEC("socket") +__success +__naked void scalars_alu32_wrap(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w0 |= 0xffffff00; \ + r1 = r0; \ + w1 += 0x100; \ + if r1 > 0x80 goto l0_%=; \ + r2 = r0; \ + r2 >>= 32; \ + if r2 == 0 goto l0_%=; \ + r0 /= 0; \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +SEC("socket") +__success +void alu32_negative_offset(void) +{ + volatile char path[5]; + volatile int offset = bpf_get_prandom_u32(); + int off = offset; + + if (off >= 5 && off < 10) + path[off - 5] = '.'; + + /* So compiler doesn't say: error: variable 'path' set but not used */ + __sink(path[0]); +} + char _license[] SEC("license") = "GPL"; From 19836e8145de96c7cc77a2ee69983e1079bcdaa1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 17 Jul 2026 12:47:26 +0800 Subject: [PATCH 0640/3297] selftests/bpf: Add a test cases for sync_linked_regs regarding zext propagation commit 4a04d13576fd69149b91672b5f1dc62eca272fa5 upstream. Add multiple test cases for linked register tracking with alu32 ops: - Add a test that checks sync_linked_regs() regarding reg->id (the linked target register) for BPF_ADD_CONST32 rather than known_reg->id (the branch register). - Add a test case for linked register tracking that exposes the cross-type sync_linked_regs() bug. One register uses alu32 (w7 += 1, BPF_ADD_CONST32) and another uses alu64 (r8 += 2, BPF_ADD_CONST64), both linked to the same base register. - Add a test case that exercises regsafe() path pruning when two execution paths reach the same program point with linked registers carrying different ADD_CONST flags (BPF_ADD_CONST32 from alu32 vs BPF_ADD_CONST64 from alu64). This particular test passes with and without the fix since the pruning will fail due to different ranges, but it would still be useful to carry this one as a regression test for the unreachable div by zero. With the fix applied all the tests pass: # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_linked_scalars [...] ./test_progs -t verifier_linked_scalars #602/1 verifier_linked_scalars/scalars: find linked scalars:OK #602/2 verifier_linked_scalars/sync_linked_regs_preserves_id:OK #602/3 verifier_linked_scalars/scalars_neg:OK #602/4 verifier_linked_scalars/scalars_neg_sub:OK #602/5 verifier_linked_scalars/scalars_neg_alu32_add:OK #602/6 verifier_linked_scalars/scalars_neg_alu32_sub:OK #602/7 verifier_linked_scalars/scalars_pos:OK #602/8 verifier_linked_scalars/scalars_sub_neg_imm:OK #602/9 verifier_linked_scalars/scalars_double_add:OK #602/10 verifier_linked_scalars/scalars_sync_delta_overflow:OK #602/11 verifier_linked_scalars/scalars_sync_delta_overflow_large_range:OK #602/12 verifier_linked_scalars/scalars_alu32_big_offset:OK #602/13 verifier_linked_scalars/scalars_alu32_basic:OK #602/14 verifier_linked_scalars/scalars_alu32_wrap:OK #602/15 verifier_linked_scalars/scalars_alu32_zext_linked_reg:OK #602/16 verifier_linked_scalars/scalars_alu32_alu64_cross_type:OK #602/17 verifier_linked_scalars/scalars_alu32_alu64_regsafe_pruning:OK #602/18 verifier_linked_scalars/alu32_negative_offset:OK #602/19 verifier_linked_scalars/spurious_precision_marks:OK #602 verifier_linked_scalars:OK Summary: 1/19 PASSED, 0 SKIPPED, 0 FAILED Co-developed-by: Puranjay Mohan Signed-off-by: Puranjay Mohan Signed-off-by: Daniel Borkmann Acked-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260319211507.213816-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- .../bpf/progs/verifier_linked_scalars.c | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c index 2ef346c827c25..87930b5e7b784 100644 --- a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c +++ b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c @@ -348,6 +348,114 @@ l0_%=: \ : __clobber_all); } +/* + * Test that sync_linked_regs() checks reg->id (the linked target register) + * for BPF_ADD_CONST32 rather than known_reg->id (the branch register). + */ +SEC("socket") +__success +__naked void scalars_alu32_zext_linked_reg(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \ + r7 = r6; /* linked: same id as r6 */ \ + w7 += 1; /* alu32: r7.id |= BPF_ADD_CONST32 */ \ + r8 = 0xFFFFffff ll; \ + if r6 < r8 goto l0_%=; \ + /* r6 in [0xFFFFFFFF, 0xFFFFFFFF] */ \ + /* sync_linked_regs: known_reg=r6, reg=r7 */ \ + /* CPU: w7 = (u32)(0xFFFFFFFF + 1) = 0, zext -> r7 = 0 */ \ + /* With fix: r7 64-bit = [0, 0] (zext applied) */ \ + /* Without fix: r7 64-bit = [0x100000000] (no zext) */ \ + r7 >>= 32; \ + if r7 == 0 goto l0_%=; \ + r0 /= 0; /* unreachable with fix */ \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* + * Test that sync_linked_regs() skips propagation when one register used + * alu32 (BPF_ADD_CONST32) and the other used alu64 (BPF_ADD_CONST64). + * The delta relationship doesn't hold across different ALU widths. + */ +SEC("socket") +__failure __msg("div by zero") +__naked void scalars_alu32_alu64_cross_type(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \ + r7 = r6; /* linked: same id as r6 */ \ + w7 += 1; /* alu32: BPF_ADD_CONST32, delta = 1 */ \ + r8 = r6; /* linked: same id as r6 */ \ + r8 += 2; /* alu64: BPF_ADD_CONST64, delta = 2 */ \ + r9 = 0xFFFFffff ll; \ + if r7 < r9 goto l0_%=; \ + /* r7 = 0xFFFFFFFF */ \ + /* sync: known_reg=r7 (ADD_CONST32), reg=r8 (ADD_CONST64) */ \ + /* Without fix: r8 = zext(0xFFFFFFFF + 1) = 0 */ \ + /* With fix: r8 stays [2, 0x100000001] (r8 >= 2) */ \ + if r8 > 0 goto l1_%=; \ + goto l0_%=; \ +l1_%=: \ + r0 /= 0; /* div by zero */ \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* + * Test that regsafe() prevents pruning when two paths reach the same program + * point with linked registers carrying different ADD_CONST flags (one + * BPF_ADD_CONST32 from alu32, another BPF_ADD_CONST64 from alu64). + */ +SEC("socket") +__failure __msg("div by zero") +__flag(BPF_F_TEST_STATE_FREQ) +__naked void scalars_alu32_alu64_regsafe_pruning(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \ + r7 = r6; /* linked: same id as r6 */ \ + /* Get another random value for the path branch */ \ + call %[bpf_get_prandom_u32]; \ + if r0 > 0 goto l_pathb_%=; \ + /* Path A: alu32 */ \ + w7 += 1; /* BPF_ADD_CONST32, delta = 1 */\ + goto l_merge_%=; \ +l_pathb_%=: \ + /* Path B: alu64 */ \ + r7 += 1; /* BPF_ADD_CONST64, delta = 1 */\ +l_merge_%=: \ + /* Merge point: regsafe() compares path B against cached path A. */ \ + /* Narrow r6 to trigger sync_linked_regs for r7 */ \ + r9 = 0xFFFFffff ll; \ + if r6 < r9 goto l0_%=; \ + /* r6 = 0xFFFFFFFF */ \ + /* sync: r7 = 0xFFFFFFFF + 1 = 0x100000000 */ \ + /* Path A: zext -> r7 = 0 */ \ + /* Path B: no zext -> r7 = 0x100000000 */ \ + r7 >>= 32; \ + if r7 == 0 goto l0_%=; \ + r0 /= 0; /* div by zero on path B */ \ +l0_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + SEC("socket") __success void alu32_negative_offset(void) From d75376fbc8563f63a584014e73f4e6d8086fc7ab Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 17 Jul 2026 12:47:27 +0800 Subject: [PATCH 0641/3297] bpf: Clear delta when clearing reg id for non-{add,sub} ops commit 1b327732c84640c1e3da487eefe9d00cc9f2dd34 upstream. When a non-{add,sub} alu op such as xor is performed on a scalar register that previously had a BPF_ADD_CONST delta, the else path in adjust_reg_min_max_vals() only clears dst_reg->id but leaves dst_reg->delta unchanged. This stale delta can propagate via assign_scalar_id_before_mov() when the register is later used in a mov. It gets a fresh id but keeps the stale delta from the old (now-cleared) BPF_ADD_CONST. This stale delta can later propagate leading to a verifier-vs- runtime value mismatch. The clear_id label already correctly clears both delta and id. Make the else path consistent by also zeroing the delta when id is cleared. More generally, this introduces a helper clear_scalar_id() which internally takes care of zeroing. There are various other locations in the verifier where only the id is cleared. By using the helper we catch all current and future locations. Fixes: 98d7ca374ba4 ("bpf: Track delta between "linked" registers.") Reported-by: STAR Labs SG Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260407192421.508817-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov [shung-hsi.yu: `delta` field was called `off` before commit 3d91c618aca4 ("bpf: rename bpf_reg_state->off to bpf_reg_state->delta"), and clear_singular_ids() does not exists before commit b2a0aa3a8739 ("bpf: Clear singular ids for scalars in is_state_visited()")] Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 50 ++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3dcf591acd50d..bf8cadd19593b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5013,27 +5013,30 @@ static bool __is_pointer_value(bool allow_ptr_leaks, return reg->type != SCALAR_VALUE; } +static void clear_scalar_id(struct bpf_reg_state *reg) +{ + reg->id = 0; + reg->off = 0; +} + static void assign_scalar_id_before_mov(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg) { if (src_reg->type != SCALAR_VALUE) return; - - if (src_reg->id & BPF_ADD_CONST) { - /* - * The verifier is processing rX = rY insn and - * rY->id has special linked register already. - * Cleared it, since multiple rX += const are not supported. - */ - src_reg->id = 0; - src_reg->off = 0; - } - + /* + * The verifier is processing rX = rY insn and + * rY->id has special linked register already. + * Cleared it, since multiple rX += const are not supported. + */ + if (src_reg->id & BPF_ADD_CONST) + clear_scalar_id(src_reg); + /* + * Ensure that src_reg has a valid ID that will be copied to + * dst_reg and then will be used by sync_linked_regs() to + * propagate min/max range. + */ if (!src_reg->id && !tnum_is_const(src_reg->var_off)) - /* Ensure that src_reg has a valid ID that will be copied to - * dst_reg and then will be used by sync_linked_regs() to - * propagate min/max range. - */ src_reg->id = ++env->id_gen; } @@ -5466,7 +5469,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, * coerce_reg_to_size will adjust the boundaries. */ if (get_reg_width(reg) > size * BITS_PER_BYTE) - state->regs[dst_regno].id = 0; + clear_scalar_id(&state->regs[dst_regno]); } else { int spill_cnt = 0, zero_cnt = 0; @@ -15476,7 +15479,7 @@ static void scalar_byte_swap(struct bpf_reg_state *dst_reg, struct bpf_insn *ins * any existing ties and avoid incorrect bounds propagation. */ if (need_bswap || insn->imm == 16 || insn->imm == 32) - dst_reg->id = 0; + clear_scalar_id(dst_reg); if (need_bswap) { if (insn->imm == 16) @@ -15845,8 +15848,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * we cannot accumulate another val into rx->off. */ clear_id: - dst_reg->off = 0; - dst_reg->id = 0; + clear_scalar_id(dst_reg); } else { if (alu32) dst_reg->id |= BPF_ADD_CONST32; @@ -15859,7 +15861,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * Make sure ID is cleared otherwise dst_reg min/max could be * incorrectly propagated into other registers by sync_linked_regs() */ - dst_reg->id = 0; + clear_scalar_id(dst_reg); } return 0; } @@ -15990,7 +15992,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); if (!no_sext) - dst_reg->id = 0; + clear_scalar_id(dst_reg); coerce_reg_to_size_sx(dst_reg, insn->off >> 3); dst_reg->subreg_def = DEF_NOT_SUBREG; } else { @@ -16016,7 +16018,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) * propagated into src_reg by sync_linked_regs() */ if (!is_src_reg_u32) - dst_reg->id = 0; + clear_scalar_id(dst_reg); dst_reg->subreg_def = env->insn_idx + 1; } else { /* case: W1 = (s8, s16)W2 */ @@ -16026,7 +16028,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); if (!no_sext) - dst_reg->id = 0; + clear_scalar_id(dst_reg); dst_reg->subreg_def = env->insn_idx + 1; coerce_subreg_to_size_sx(dst_reg, insn->off >> 3); } @@ -16856,7 +16858,7 @@ static void __collect_linked_regs(struct linked_regs *reg_set, struct bpf_reg_st e->is_reg = is_reg; e->regno = spi_or_reg; } else { - reg->id = 0; + clear_scalar_id(reg); } } From 985b8a0e52c5828450cc478d2198c1dacc21f40d Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 17 Jul 2026 12:47:28 +0800 Subject: [PATCH 0642/3297] selftests/bpf: Add tests for delta tracking when src_reg == dst_reg commit ed2eecdc0c6613353bc1565e900d2b23237713da upstream. Extend the verifier_linked_scalars BPF selftest with a rX += rX test such that the div-by-zero path is rejected in the fixed case. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_linked_scalars [...] ./test_progs -t verifier_linked_scalars #612/1 verifier_linked_scalars/scalars: find linked scalars:OK #612/2 verifier_linked_scalars/sync_linked_regs_preserves_id:OK #612/3 verifier_linked_scalars/scalars_neg:OK #612/4 verifier_linked_scalars/scalars_neg_sub:OK #612/5 verifier_linked_scalars/scalars_neg_alu32_add:OK #612/6 verifier_linked_scalars/scalars_neg_alu32_sub:OK #612/7 verifier_linked_scalars/scalars_pos:OK #612/8 verifier_linked_scalars/scalars_sub_neg_imm:OK #612/9 verifier_linked_scalars/scalars_double_add:OK #612/10 verifier_linked_scalars/scalars_sync_delta_overflow:OK #612/11 verifier_linked_scalars/scalars_sync_delta_overflow_large_range:OK #612/12 verifier_linked_scalars/scalars_alu32_big_offset:OK #612/13 verifier_linked_scalars/scalars_alu32_basic:OK #612/14 verifier_linked_scalars/scalars_alu32_wrap:OK #612/15 verifier_linked_scalars/scalars_alu32_zext_linked_reg:OK #612/16 verifier_linked_scalars/scalars_alu32_alu64_cross_type:OK #612/17 verifier_linked_scalars/scalars_alu32_alu64_regsafe_pruning:OK #612/18 verifier_linked_scalars/alu32_negative_offset:OK #612/19 verifier_linked_scalars/spurious_precision_marks:OK #612/20 verifier_linked_scalars/scalars_self_add_clears_id:OK #612/21 verifier_linked_scalars/scalars_self_add_alu32_clears_id:OK #612 verifier_linked_scalars:OK Summary: 1/21 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260407192421.508817-3-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov [shung-hsi.yu: missing context from commit 223ffb6a3d05 ("selftests/bpf: add reproducer for spurious precision propagation through calls").] Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- .../bpf/progs/verifier_linked_scalars.c | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c index 87930b5e7b784..8741238b2ebd9 100644 --- a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c +++ b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c @@ -471,4 +471,61 @@ void alu32_negative_offset(void) __sink(path[0]); } +/* + * Test that r += r (self-add, src_reg == dst_reg) clears the scalar ID + * so that sync_linked_regs() does not propagate an incorrect delta. + */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_self_add_clears_id(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r6 = r0; /* r6 unknown, id A */ \ + r7 = r6; /* r7 linked to r6, id A */ \ + call %[bpf_get_prandom_u32]; \ + r8 = r0; /* r8 unknown, id B */ \ + r9 = r8; /* r9 linked to r8, id B */ \ + if r7 != 1 goto l_exit_%=; \ + /* r7 == 1; sync propagates: r6 = 1 (known, id A) */ \ + r6 += r6; /* r6 = 2; should clear id */ \ + if r7 == r9 goto l_exit_%=; \ + /* Bug: r6 synced to r7(1)+delta(2)=3; Fix: r6 = 2 */ \ + if r6 == 3 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Same as above but with alu32 such that w6 += w6 also clears id. */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_self_add_alu32_clears_id(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w6 = w0; \ + w7 = w6; \ + call %[bpf_get_prandom_u32]; \ + w8 = w0; \ + w9 = w8; \ + if w7 != 1 goto l_exit_%=; \ + w6 += w6; \ + if w7 == w9 goto l_exit_%=; \ + if w6 == 3 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; From f3a0dd2d88e837abe3301c6c00472c5876ae82c9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 17 Jul 2026 12:47:29 +0800 Subject: [PATCH 0643/3297] selftests/bpf: Add tests for stale delta leaking through id reassignment commit cac16ce1e3786bd98cec0c108e3bc06ed3d3c6a9 upstream. Extend the verifier_linked_scalars BPF selftest with a stale delta test such that the div-by-zero path is rejected in the fixed case. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_linked_scalars [...] ./test_progs -t verifier_linked_scalars #612/1 verifier_linked_scalars/scalars: find linked scalars:OK #612/2 verifier_linked_scalars/sync_linked_regs_preserves_id:OK #612/3 verifier_linked_scalars/scalars_neg:OK #612/4 verifier_linked_scalars/scalars_neg_sub:OK #612/5 verifier_linked_scalars/scalars_neg_alu32_add:OK #612/6 verifier_linked_scalars/scalars_neg_alu32_sub:OK #612/7 verifier_linked_scalars/scalars_pos:OK #612/8 verifier_linked_scalars/scalars_sub_neg_imm:OK #612/9 verifier_linked_scalars/scalars_double_add:OK #612/10 verifier_linked_scalars/scalars_sync_delta_overflow:OK #612/11 verifier_linked_scalars/scalars_sync_delta_overflow_large_range:OK #612/12 verifier_linked_scalars/scalars_alu32_big_offset:OK #612/13 verifier_linked_scalars/scalars_alu32_basic:OK #612/14 verifier_linked_scalars/scalars_alu32_wrap:OK #612/15 verifier_linked_scalars/scalars_alu32_zext_linked_reg:OK #612/16 verifier_linked_scalars/scalars_alu32_alu64_cross_type:OK #612/17 verifier_linked_scalars/scalars_alu32_alu64_regsafe_pruning:OK #612/18 verifier_linked_scalars/alu32_negative_offset:OK #612/19 verifier_linked_scalars/spurious_precision_marks:OK #612/20 verifier_linked_scalars/scalars_self_add_clears_id:OK #612/21 verifier_linked_scalars/scalars_self_add_alu32_clears_id:OK #612/22 verifier_linked_scalars/scalars_stale_delta_from_cleared_id:OK #612/23 verifier_linked_scalars/scalars_stale_delta_from_cleared_id_alu32:OK #612 verifier_linked_scalars:OK Summary: 1/23 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260407192421.508817-4-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- .../bpf/progs/verifier_linked_scalars.c | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c index 8741238b2ebd9..3b0c300c9cbd8 100644 --- a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c +++ b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c @@ -528,4 +528,59 @@ l_exit_%=: \ : __clobber_all); } +/* + * Test that stale delta from a cleared BPF_ADD_CONST does not leak + * through assign_scalar_id_before_mov() into a new id, causing + * sync_linked_regs() to compute an incorrect offset. + */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_stale_delta_from_cleared_id(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r6 = r0; /* r6 unknown, gets id A */ \ + r6 += 5; /* id A|ADD_CONST, delta 5 */ \ + r6 ^= 0; /* id cleared; delta stays 5 */ \ + r8 = r6; /* new id B, stale delta 5 */ \ + r8 += 3; /* id B|ADD_CONST, delta 3 */ \ + r9 = r6; /* id B, stale delta 5 */ \ + if r9 != 10 goto l_exit_%=; \ + /* Bug: r8 = 10+(3-5) = 8; Fix: r8 = 10+(3-0) = 13 */ \ + if r8 == 8 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Same as above but with alu32. */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_stale_delta_from_cleared_id_alu32(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w6 = w0; \ + w6 += 5; \ + w6 ^= 0; \ + w8 = w6; \ + w8 += 3; \ + w9 = w6; \ + if w9 != 10 goto l_exit_%=; \ + if w8 == 8 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; From 62dde71ca2c8cfe74d7d01699bb14b81b8d4afb4 Mon Sep 17 00:00:00 2001 From: Rochan Avlur Date: Thu, 16 Jul 2026 09:51:27 -0700 Subject: [PATCH 0644/3297] exfat: preserve benign secondary entries during rename and move [ Upstream commit 942296784b2a9439651750c42f540bf2579b330f ] Commit 8258ef28001a ("exfat: handle unreconized benign secondary entries") added cluster freeing for benign secondary entries inside exfat_remove_entries(). However, exfat_remove_entries() is also called from the rename and move paths (exfat_rename_file and exfat_move_file), where the old entry set is being relocated rather than deleted. This causes benign secondary entries such as vendor extension entries to be silently destroyed on rename or cross-directory move, violating the exFAT spec requirement (section 8.2) that implementations preserve unrecognized benign secondary entries. Fix this by adding a free_benign parameter to exfat_remove_entries() so callers can suppress cluster freeing during relocation, and extending exfat_init_ext_entry() to copy trailing benign secondary entries from the old entry set into the new one internally. Also clean up the error paths to delete newly allocated entries on failure. Fixes: 8258ef28001a ("exfat: handle unreconized benign secondary entries") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-fsdevel/CAG7tbBV--waov7XVu2FHQEc6paR92dufS=em9DW5Kzsrpu3iQg@mail.gmail.com/ Signed-off-by: Rochan Avlur Reviewed-by: Yuezhang Mo Signed-off-by: Namjae Jeon Signed-off-by: Rochan Avlur Signed-off-by: Sasha Levin --- fs/exfat/dir.c | 49 ++++++++++++++++++++++--- fs/exfat/exfat_fs.h | 5 ++- fs/exfat/namei.c | 89 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 116 insertions(+), 27 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index e1b28c1a7e857..1f6cb533a48d1 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -481,31 +481,70 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode, exfat_free_cluster(inode, &dir); } +/* + * exfat_init_ext_entry - initialize extension entries in a directory entry set + * @es: target entry set + * @num_entries: number of entries excluding benign secondary entries + * @p_uniname: filename to store + * @old_es: optional source entry set with benign secondary entries, or NULL + * @num_extra: number of benign secondary entries to copy from @old_es + * + * Set up the file, stream extension, and filename entries in @es, optionally + * preserving @num_extra benign secondary entries from @old_es. @es and @old_es + * may refer to the same entry set; excess entries are marked as deleted. + */ void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, - struct exfat_uni_name *p_uniname) + struct exfat_uni_name *p_uniname, + struct exfat_entry_set_cache *old_es, int num_extra) { - int i; + int i, src_start = 0, old_num; unsigned short *uniname = p_uniname->name; struct exfat_dentry *ep; + if (WARN_ON(num_extra < 0 || (num_extra && (!old_es || + old_es->num_entries < ES_IDX_FIRST_FILENAME + num_extra)))) + num_extra = 0; + + /* + * Save old entry count and source position before modifying + * es->num_entries, since old_es and es may point to the same + * entry set. + */ + old_num = es->num_entries; + if (old_es && num_extra > 0) + src_start = old_es->num_entries - num_extra; + + es->num_entries = num_entries + num_extra; ep = exfat_get_dentry_cached(es, ES_IDX_FILE); - ep->dentry.file.num_ext = (unsigned char)(num_entries - 1); + ep->dentry.file.num_ext = (unsigned char)(num_entries - 1 + num_extra); ep = exfat_get_dentry_cached(es, ES_IDX_STREAM); ep->dentry.stream.name_len = p_uniname->name_len; ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash); + if (old_es && num_extra > 0) { + for (i = 0; i < num_extra; i++) + *exfat_get_dentry_cached(es, num_entries + i) = + *exfat_get_dentry_cached(old_es, src_start + i); + } + for (i = ES_IDX_FIRST_FILENAME; i < num_entries; i++) { ep = exfat_get_dentry_cached(es, i); exfat_init_name_entry(ep, uniname); uniname += EXFAT_FILE_NAME_LEN; } + /* Mark excess old entries as deleted (in-place shrink) */ + for (i = num_entries + num_extra; i < old_num; i++) { + ep = exfat_get_dentry_cached(es, i); + exfat_set_entry_type(ep, TYPE_DELETED); + } + exfat_update_dir_chksum(es); } void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, - int order) + int order, bool free_benign) { int i; struct exfat_dentry *ep; @@ -513,7 +552,7 @@ void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, for (i = order; i < es->num_entries; i++) { ep = exfat_get_dentry_cached(es, i); - if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC) + if (free_benign && (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)) exfat_free_benign_secondary_clusters(inode, ep); exfat_set_entry_type(ep, TYPE_DELETED); diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 38210fb6901c0..9a85270f8493f 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -496,9 +496,10 @@ void exfat_init_dir_entry(struct exfat_entry_set_cache *es, unsigned int type, unsigned int start_clu, unsigned long long size, struct timespec64 *ts); void exfat_init_ext_entry(struct exfat_entry_set_cache *es, int num_entries, - struct exfat_uni_name *p_uniname); + struct exfat_uni_name *p_uniname, + struct exfat_entry_set_cache *old_es, int num_extra); void exfat_remove_entries(struct inode *inode, struct exfat_entry_set_cache *es, - int order); + int order, bool free_benign); void exfat_update_dir_chksum(struct exfat_entry_set_cache *es); int exfat_calc_num_entries(struct exfat_uni_name *p_uniname); int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index dfe957493d49e..6b544df0a03a6 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -509,7 +509,7 @@ static int exfat_add_entry(struct inode *inode, const char *path, * the first cluster is not determined yet. (0) */ exfat_init_dir_entry(&es, type, start_clu, clu_size, &ts); - exfat_init_ext_entry(&es, num_entries, &uniname); + exfat_init_ext_entry(&es, num_entries, &uniname, NULL, 0); ret = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (ret) @@ -820,7 +820,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry) exfat_set_volume_dirty(sb); /* update the directory entry */ - exfat_remove_entries(inode, &es, ES_IDX_FILE); + exfat_remove_entries(inode, &es, ES_IDX_FILE, true); err = exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (err) @@ -981,7 +981,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) exfat_set_volume_dirty(sb); - exfat_remove_entries(inode, &es, ES_IDX_FILE); + exfat_remove_entries(inode, &es, ES_IDX_FILE, true); err = exfat_put_dentry_set(&es, IS_DIRSYNC(dir)); if (err) @@ -1008,6 +1008,23 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) return err; } +/* + * Count benign secondary entries beyond the filename entries. + * Returns the count, or -EIO if the entry set is inconsistent. + */ +static int exfat_count_extra_entries(struct exfat_entry_set_cache *es) +{ + struct exfat_dentry *stream; + unsigned int name_entries; + int extra; + + stream = exfat_get_dentry_cached(es, ES_IDX_STREAM); + name_entries = EXFAT_FILENAME_ENTRY_NUM(stream->dentry.stream.name_len); + extra = es->num_entries - (ES_IDX_FIRST_FILENAME + name_entries); + + return extra >= 0 ? extra : -EIO; +} + static int exfat_rename_file(struct inode *parent_inode, struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) { @@ -1016,6 +1033,7 @@ static int exfat_rename_file(struct inode *parent_inode, struct super_block *sb = parent_inode->i_sb; struct exfat_entry_set_cache old_es, new_es; int sync = IS_DIRSYNC(parent_inode); + unsigned int num_extra_entries, num_total_entries; if (unlikely(exfat_forced_shutdown(sb))) return -EIO; @@ -1025,19 +1043,23 @@ static int exfat_rename_file(struct inode *parent_inode, return num_new_entries; ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei); - if (ret) { - ret = -EIO; - return ret; - } + if (ret) + return -EIO; epold = exfat_get_dentry_cached(&old_es, ES_IDX_FILE); - if (old_es.num_entries < num_new_entries) { + ret = exfat_count_extra_entries(&old_es); + if (ret < 0) + goto put_old_es; + num_extra_entries = ret; + num_total_entries = num_new_entries + num_extra_entries; + + if (old_es.num_entries < num_total_entries) { int newentry; struct exfat_chain dir; newentry = exfat_find_empty_entry(parent_inode, &dir, - num_new_entries, &new_es); + num_total_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_old_es; @@ -1054,13 +1076,23 @@ static int exfat_rename_file(struct inode *parent_inode, epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epold; - exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname, + &old_es, num_extra_entries); ret = exfat_put_dentry_set(&new_es, sync); - if (ret) + if (ret) { + /* Best-effort delete to avoid duplicate entries */ + if (!exfat_get_dentry_set(&new_es, sb, + &dir, newentry, + ES_ALL_ENTRIES)) { + exfat_remove_entries(parent_inode, &new_es, + ES_IDX_FILE, false); + exfat_put_dentry_set(&new_es, false); + } goto put_old_es; + } - exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE); + exfat_remove_entries(parent_inode, &old_es, ES_IDX_FILE, false); ei->dir = dir; ei->entry = newentry; } else { @@ -1069,8 +1101,8 @@ static int exfat_rename_file(struct inode *parent_inode, ei->attr |= EXFAT_ATTR_ARCHIVE; } - exfat_remove_entries(parent_inode, &old_es, ES_IDX_FIRST_FILENAME + 1); - exfat_init_ext_entry(&old_es, num_new_entries, p_uniname); + exfat_init_ext_entry(&old_es, num_new_entries, p_uniname, + &old_es, num_extra_entries); } return exfat_put_dentry_set(&old_es, sync); @@ -1086,6 +1118,7 @@ static int exfat_move_file(struct inode *parent_inode, struct exfat_dentry *epmov, *epnew; struct exfat_entry_set_cache mov_es, new_es; struct exfat_chain newdir; + unsigned int num_extra_entries, num_total_entries; num_new_entries = exfat_calc_num_entries(p_uniname); if (num_new_entries < 0) @@ -1095,8 +1128,14 @@ static int exfat_move_file(struct inode *parent_inode, if (ret) return -EIO; + ret = exfat_count_extra_entries(&mov_es); + if (ret < 0) + goto put_mov_es; + num_extra_entries = ret; + num_total_entries = num_new_entries + num_extra_entries; + newentry = exfat_find_empty_entry(parent_inode, &newdir, - num_new_entries, &new_es); + num_total_entries, &new_es); if (newentry < 0) { ret = newentry; /* -EIO or -ENOSPC */ goto put_mov_es; @@ -1114,21 +1153,31 @@ static int exfat_move_file(struct inode *parent_inode, epnew = exfat_get_dentry_cached(&new_es, ES_IDX_STREAM); *epnew = *epmov; - exfat_init_ext_entry(&new_es, num_new_entries, p_uniname); - exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE); + exfat_init_ext_entry(&new_es, num_new_entries, p_uniname, + &mov_es, num_extra_entries); + + exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE, false); ei->dir = newdir; ei->entry = newentry; ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(parent_inode)); - if (ret) + if (ret) { + /* Best-effort delete to avoid duplicate entries */ + if (!exfat_get_dentry_set(&new_es, parent_inode->i_sb, + &newdir, newentry, + ES_ALL_ENTRIES)) { + exfat_remove_entries(parent_inode, &new_es, + ES_IDX_FILE, false); + exfat_put_dentry_set(&new_es, false); + } goto put_mov_es; + } return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(parent_inode)); put_mov_es: exfat_put_dentry_set(&mov_es, false); - return ret; } @@ -1202,7 +1251,7 @@ static int __exfat_rename(struct inode *old_parent_inode, goto del_out; } - exfat_remove_entries(new_inode, &es, ES_IDX_FILE); + exfat_remove_entries(new_inode, &es, ES_IDX_FILE, true); ret = exfat_put_dentry_set(&es, IS_DIRSYNC(new_inode)); if (ret) From a4fb0954f3dc2e893fa87653327f16bc517f3822 Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Mon, 20 Apr 2026 08:42:03 +0000 Subject: [PATCH 0645/3297] iommu/amd: Use maximum Event log buffer size when SNP is enabled on Family 0x19 [ Upstream commit 58c0ac6125d89bf6ec65a521eaeb52a0e8e20a9f ] Due to CVE-2023-20585, the Event log buffer must use the maximum supported size (512K) on Milan/Genoa (Family 0x19) systems when SNP is enabled, to mitigate a potential security vulnerability. All other systems continue to use the default Event log buffer size (8K). Apply the errata fix by making the following changes: * Introduce new global variable (amd_iommu_evtlog_size) to have event log buffer size. Adjust variable size for family 0x19. * Since 'iommu_snp_enable()' must be called after the core IOMMU subsystem is initialized, it cannot be moved to the early init stage. The SNP errata must also be applied after the 'iommu_snp_enable()' check. Therefore, 'alloc_event_buffer()' and 'iommu_enable_event_buffer()' are now called in the IOMMU_ENABLED state, after the errata is applied. * Adjust alloc_event_buffer() and iommu_enable_event_buffer() to handle all IOMMU instances. * Also rename EVT_* macros to make it more readable. Link: https://www.amd.com/en/resources/product-security/bulletin/amd-sb-3016.html Cc: Borislav Petkov Cc: Suravee Suthikulpanit Cc: Joerg Roedel Signed-off-by: Vasant Hegde Tested-by: Dheeraj Kumar Srivastava Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/amd_iommu.h | 2 + drivers/iommu/amd/amd_iommu_types.h | 10 ++- drivers/iommu/amd/init.c | 110 +++++++++++++++++++--------- drivers/iommu/amd/iommu.c | 2 +- 4 files changed, 86 insertions(+), 38 deletions(-) diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index bf77fdf5529f4..57d7f3faa98c6 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -11,6 +11,8 @@ #include "amd_iommu_types.h" +extern int amd_iommu_evtlog_size; + irqreturn_t amd_iommu_int_thread(int irq, void *data); irqreturn_t amd_iommu_int_thread_evtlog(int irq, void *data); irqreturn_t amd_iommu_int_thread_pprlog(int irq, void *data); diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index b0d919cd1a8fb..bdd6c38ddfec9 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -136,7 +137,6 @@ #define MMIO_STATUS_GALOG_INT_MASK BIT(10) /* event logging constants */ -#define EVENT_ENTRY_SIZE 0x10 #define EVENT_TYPE_SHIFT 28 #define EVENT_TYPE_MASK 0xf #define EVENT_TYPE_ILL_DEV 0x1 @@ -249,8 +249,12 @@ #define MMIO_CMD_SIZE_512 (0x9ULL << MMIO_CMD_SIZE_SHIFT) /* constants for event buffer handling */ -#define EVT_BUFFER_SIZE 8192 /* 512 entries */ -#define EVT_LEN_MASK (0x9ULL << 56) +#define EVTLOG_ENTRY_SIZE 0x10 +#define EVTLOG_SIZE_SHIFT 56 +#define EVTLOG_SIZE_DEF SZ_8K /* 512 entries */ +#define EVTLOG_LEN_MASK_DEF (0x9ULL << EVTLOG_SIZE_SHIFT) +#define EVTLOG_SIZE_MAX SZ_512K /* 32K entries */ +#define EVTLOG_LEN_MASK_MAX (0xFULL << EVTLOG_SIZE_SHIFT) /* Constants for PPR Log handling */ #define PPR_LOG_ENTRIES 512 diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 76efd74124b33..64964bb5ce0d3 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -132,6 +132,8 @@ struct ivhd_entry { u8 uid; } __attribute__((packed)); +int amd_iommu_evtlog_size = EVTLOG_SIZE_DEF; + /* * An AMD IOMMU memory definition structure. It defines things like exclusion * ranges for devices and regions that should be unity mapped. @@ -867,35 +869,47 @@ void *__init iommu_alloc_4k_pages(struct amd_iommu *iommu, gfp_t gfp, } /* allocates the memory where the IOMMU will log its events to */ -static int __init alloc_event_buffer(struct amd_iommu *iommu) +static int __init alloc_event_buffer(void) { - iommu->evt_buf = iommu_alloc_4k_pages(iommu, GFP_KERNEL, - EVT_BUFFER_SIZE); + struct amd_iommu *iommu; - return iommu->evt_buf ? 0 : -ENOMEM; + for_each_iommu(iommu) { + iommu->evt_buf = iommu_alloc_4k_pages(iommu, GFP_KERNEL, + amd_iommu_evtlog_size); + if (!iommu->evt_buf) + return -ENOMEM; + } + + return 0; } -static void iommu_enable_event_buffer(struct amd_iommu *iommu) +static void iommu_enable_event_buffer(void) { + struct amd_iommu *iommu; u64 entry; - BUG_ON(iommu->evt_buf == NULL); + for_each_iommu(iommu) { + BUG_ON(iommu->evt_buf == NULL); - if (!is_kdump_kernel()) { - /* - * Event buffer is re-used for kdump kernel and setting - * of MMIO register is not required. - */ - entry = iommu_virt_to_phys(iommu->evt_buf) | EVT_LEN_MASK; - memcpy_toio(iommu->mmio_base + MMIO_EVT_BUF_OFFSET, - &entry, sizeof(entry)); - } + if (!is_kdump_kernel()) { + /* + * Event buffer is re-used for kdump kernel and setting + * of MMIO register is not required. + */ + entry = iommu_virt_to_phys(iommu->evt_buf); + entry |= (amd_iommu_evtlog_size == EVTLOG_SIZE_DEF) ? + EVTLOG_LEN_MASK_DEF : EVTLOG_LEN_MASK_MAX; + + memcpy_toio(iommu->mmio_base + MMIO_EVT_BUF_OFFSET, + &entry, sizeof(entry)); + } - /* set head and tail to zero manually */ - writel(0x00, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); - writel(0x00, iommu->mmio_base + MMIO_EVT_TAIL_OFFSET); + /* set head and tail to zero manually */ + writel(0x00, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); + writel(0x00, iommu->mmio_base + MMIO_EVT_TAIL_OFFSET); - iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); + iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); + } } /* @@ -984,15 +998,20 @@ static int __init alloc_cwwb_sem(struct amd_iommu *iommu) return 0; } -static int __init remap_event_buffer(struct amd_iommu *iommu) +static int __init remap_event_buffer(void) { + struct amd_iommu *iommu; u64 paddr; pr_info_once("Re-using event buffer from the previous kernel\n"); - paddr = readq(iommu->mmio_base + MMIO_EVT_BUF_OFFSET) & PM_ADDR_MASK; - iommu->evt_buf = iommu_memremap(paddr, EVT_BUFFER_SIZE); + for_each_iommu(iommu) { + paddr = readq(iommu->mmio_base + MMIO_EVT_BUF_OFFSET) & PM_ADDR_MASK; + iommu->evt_buf = iommu_memremap(paddr, amd_iommu_evtlog_size); + if (!iommu->evt_buf) + return -ENOMEM; + } - return iommu->evt_buf ? 0 : -ENOMEM; + return 0; } static int __init remap_command_buffer(struct amd_iommu *iommu) @@ -1044,10 +1063,6 @@ static int __init alloc_iommu_buffers(struct amd_iommu *iommu) ret = remap_command_buffer(iommu); if (ret) return ret; - - ret = remap_event_buffer(iommu); - if (ret) - return ret; } else { ret = alloc_cwwb_sem(iommu); if (ret) @@ -1056,10 +1071,6 @@ static int __init alloc_iommu_buffers(struct amd_iommu *iommu) ret = alloc_command_buffer(iommu); if (ret) return ret; - - ret = alloc_event_buffer(iommu); - if (ret) - return ret; } return 0; @@ -2884,7 +2895,6 @@ static void early_enable_iommu(struct amd_iommu *iommu) iommu_init_flags(iommu); iommu_set_device_table(iommu); iommu_enable_command_buffer(iommu); - iommu_enable_event_buffer(iommu); iommu_set_exclusion_range(iommu); iommu_enable_gt(iommu); iommu_enable_ga(iommu); @@ -2948,7 +2958,6 @@ static void early_enable_iommus(void) iommu_disable_event_buffer(iommu); iommu_disable_irtcachedis(iommu); iommu_enable_command_buffer(iommu); - iommu_enable_event_buffer(iommu); iommu_enable_ga(iommu); iommu_enable_xt(iommu); iommu_enable_irtcachedis(iommu); @@ -3061,6 +3070,7 @@ static void amd_iommu_resume(void) for_each_iommu(iommu) early_enable_iommu(iommu); + iommu_enable_event_buffer(); amd_iommu_enable_interrupts(); } @@ -3387,6 +3397,23 @@ static __init void iommu_snp_enable(void) #endif } +static void amd_iommu_apply_erratum_snp(void) +{ +#ifdef CONFIG_KVM_AMD_SEV + if (!amd_iommu_snp_en) + return; + + /* Errata fix for Family 0x19 */ + if (boot_cpu_data.x86 != 0x19) + return; + + /* Set event log buffer size to max */ + amd_iommu_evtlog_size = EVTLOG_SIZE_MAX; + pr_info("Applying erratum: Increase Event log size to 0x%x\n", + amd_iommu_evtlog_size); +#endif +} + /**************************************************************************** * * AMD IOMMU Initialization State Machine @@ -3423,6 +3450,21 @@ static int __init state_next(void) case IOMMU_ENABLED: register_syscore_ops(&amd_iommu_syscore_ops); iommu_snp_enable(); + + amd_iommu_apply_erratum_snp(); + + /* Allocate/enable event log buffer */ + if (is_kdump_kernel()) + ret = remap_event_buffer(); + else + ret = alloc_event_buffer(); + + if (ret) { + init_state = IOMMU_INIT_ERROR; + break; + } + iommu_enable_event_buffer(); + ret = amd_iommu_init_pci(); init_state = ret ? IOMMU_INIT_ERROR : IOMMU_PCI_INIT; break; @@ -4025,7 +4067,7 @@ int amd_iommu_snp_disable(void) return 0; for_each_iommu(iommu) { - ret = iommu_make_shared(iommu->evt_buf, EVT_BUFFER_SIZE); + ret = iommu_make_shared(iommu->evt_buf, amd_iommu_evtlog_size); if (ret) return ret; diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 5fef68797fdab..c458eec8a5bba 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -992,7 +992,7 @@ static void iommu_poll_events(struct amd_iommu *iommu) iommu_print_event(iommu, iommu->evt_buf + head); /* Update head pointer of hardware ring-buffer */ - head = (head + EVENT_ENTRY_SIZE) % EVT_BUFFER_SIZE; + head = (head + EVTLOG_ENTRY_SIZE) % amd_iommu_evtlog_size; writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); } From 9b6c12e1a6be033fa79880d85edde41908b68d12 Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Mon, 20 Apr 2026 08:42:04 +0000 Subject: [PATCH 0646/3297] iommu/amd: Use maximum PPR log buffer size when SNP is enabled on Family 0x19 [ Upstream commit 1f44aab79bac31f459422dfb213e907bb386509c ] Due to CVE-2023-20585, the PPR log buffer must use the maximum supported size (512K) on Genoa (Family 0x19, model >= 0x10) systems when SNP is enabled, to mitigate a potential security vulnerability. Note that Family 0x19 models below 0x10 (Milan) do not support PPR when SNP is enabled. Hence the PPR log size increase is only applied for model >= 0x10. All other systems continue to use the default PPR log buffer size (8K). Apply the errata fix by making the following changes: - Introduce global new variable (amd_iommu_pprlog_size) to have PPR log buffer size. Adjust variable size for Genoa family. - Extend 'amd_iommu_apply_erratum_snp()' to also set the PPR log buffer size to maximum for Family 0x19 model >= 0x10 when SNP is enabled. - Rename PPR_* macros to make it more readable. Link: https://www.amd.com/en/resources/product-security/bulletin/amd-sb-3016.html Cc: Borislav Petkov Cc: Suravee Suthikulpanit Cc: Joerg Roedel Signed-off-by: Vasant Hegde Tested-by: Dheeraj Kumar Srivastava Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/amd_iommu.h | 1 + drivers/iommu/amd/amd_iommu_types.h | 11 ++++++----- drivers/iommu/amd/init.c | 13 ++++++++++++- drivers/iommu/amd/ppr.c | 8 +++++--- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index 57d7f3faa98c6..ef397c5a2c4ab 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -12,6 +12,7 @@ #include "amd_iommu_types.h" extern int amd_iommu_evtlog_size; +extern int amd_iommu_pprlog_size; irqreturn_t amd_iommu_int_thread(int irq, void *data); irqreturn_t amd_iommu_int_thread_evtlog(int irq, void *data); diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index bdd6c38ddfec9..2494b1958117b 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -257,11 +257,12 @@ #define EVTLOG_LEN_MASK_MAX (0xFULL << EVTLOG_SIZE_SHIFT) /* Constants for PPR Log handling */ -#define PPR_LOG_ENTRIES 512 -#define PPR_LOG_SIZE_SHIFT 56 -#define PPR_LOG_SIZE_512 (0x9ULL << PPR_LOG_SIZE_SHIFT) -#define PPR_ENTRY_SIZE 16 -#define PPR_LOG_SIZE (PPR_ENTRY_SIZE * PPR_LOG_ENTRIES) +#define PPRLOG_ENTRY_SIZE 0x10 +#define PPRLOG_SIZE_SHIFT 56 +#define PPRLOG_SIZE_DEF SZ_8K /* 512 entries */ +#define PPRLOG_LEN_MASK_DEF (0x9ULL << PPRLOG_SIZE_SHIFT) +#define PPRLOG_SIZE_MAX SZ_512K /* 32K entries */ +#define PPRLOG_LEN_MASK_MAX (0xFULL << PPRLOG_SIZE_SHIFT) /* PAGE_SERVICE_REQUEST PPR Log Buffer Entry flags */ #define PPR_FLAG_EXEC 0x002 /* Execute permission requested */ diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 64964bb5ce0d3..6e5efc340c83e 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -133,6 +133,7 @@ struct ivhd_entry { } __attribute__((packed)); int amd_iommu_evtlog_size = EVTLOG_SIZE_DEF; +int amd_iommu_pprlog_size = PPRLOG_SIZE_DEF; /* * An AMD IOMMU memory definition structure. It defines things like exclusion @@ -3411,6 +3412,16 @@ static void amd_iommu_apply_erratum_snp(void) amd_iommu_evtlog_size = EVTLOG_SIZE_MAX; pr_info("Applying erratum: Increase Event log size to 0x%x\n", amd_iommu_evtlog_size); + + /* + * Set PPR log buffer size to max. + * (Family 0x19, model < 0x10 doesn't support PPR when SNP is enabled). + */ + if (boot_cpu_data.x86_model >= 0x10) { + amd_iommu_pprlog_size = PPRLOG_SIZE_MAX; + pr_info("Applying erratum: Increase PPR log size to 0x%x\n", + amd_iommu_pprlog_size); + } #endif } @@ -4071,7 +4082,7 @@ int amd_iommu_snp_disable(void) if (ret) return ret; - ret = iommu_make_shared(iommu->ppr_log, PPR_LOG_SIZE); + ret = iommu_make_shared(iommu->ppr_log, amd_iommu_pprlog_size); if (ret) return ret; diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index e6767c057d01f..1f8d2823bea42 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -20,7 +20,7 @@ int __init amd_iommu_alloc_ppr_log(struct amd_iommu *iommu) { iommu->ppr_log = iommu_alloc_4k_pages(iommu, GFP_KERNEL | __GFP_ZERO, - PPR_LOG_SIZE); + amd_iommu_pprlog_size); return iommu->ppr_log ? 0 : -ENOMEM; } @@ -33,7 +33,9 @@ void amd_iommu_enable_ppr_log(struct amd_iommu *iommu) iommu_feature_enable(iommu, CONTROL_PPR_EN); - entry = iommu_virt_to_phys(iommu->ppr_log) | PPR_LOG_SIZE_512; + entry = iommu_virt_to_phys(iommu->ppr_log); + entry |= (amd_iommu_pprlog_size == PPRLOG_SIZE_DEF) ? + PPRLOG_LEN_MASK_DEF : PPRLOG_LEN_MASK_MAX; memcpy_toio(iommu->mmio_base + MMIO_PPR_LOG_OFFSET, &entry, sizeof(entry)); @@ -201,7 +203,7 @@ void amd_iommu_poll_ppr_log(struct amd_iommu *iommu) raw[0] = raw[1] = 0UL; /* Update head pointer of hardware ring-buffer */ - head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE; + head = (head + PPRLOG_ENTRY_SIZE) % amd_iommu_pprlog_size; writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); /* Handle PPR entry */ From e0a71cbf0c1906a2eccbe69dd7d7f36fd1511d66 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 15 Apr 2026 18:48:29 +0000 Subject: [PATCH 0647/3297] af_unix: Drop all SCM attributes for SOCKMAP. [ Upstream commit 965dc93481d1b80d341bdd16c27b16fe197175ee ] SOCKMAP can hide inflight fd from AF_UNIX GC. When a socket in SOCKMAP receives skb with inflight fd, sk_psock_verdict_data_ready() looks up the mapped socket and enqueue skb to its psock->ingress_skb. Since neither the old nor the new GC can inspect the psock queue, the hidden skb leaks the inflight sockets. Note that this cannot be detected via kmemleak because inflight sockets are linked to a global list. In addition, SOCKMAP redirect breaks the Tarjan-based GC's assumption that unix_edge.successor is always alive, which is no longer true once skb is redirected, resulting in use-after-free below. [0] Moreover, SOCKMAP does not call scm_stat_del() properly, so unix_show_fdinfo() could report an incorrect fd count. sk_msg_recvmsg() does not support any SCM attributes in the first place. Let's drop all SCM attributes before passing skb to the SOCKMAP layer. [0]: BUG: KASAN: slab-use-after-free in unix_del_edges (net/unix/garbage.c:118 net/unix/garbage.c:181 net/unix/garbage.c:251) Read of size 8 at addr ffff888125362670 by task kworker/56:1/496 CPU: 56 UID: 0 PID: 496 Comm: kworker/56:1 Not tainted 7.0.0-rc7-00263-gb9d8b856689d #3 PREEMPT(lazy) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014 Workqueue: events sk_psock_backlog Call Trace: dump_stack_lvl (lib/dump_stack.c:122) print_report (mm/kasan/report.c:379) kasan_report (mm/kasan/report.c:597) unix_del_edges (net/unix/garbage.c:118 net/unix/garbage.c:181 net/unix/garbage.c:251) unix_destroy_fpl (net/unix/garbage.c:317) unix_destruct_scm (./include/net/scm.h:80 ./include/net/scm.h:86 net/unix/af_unix.c:1976) sk_psock_backlog (./include/linux/skbuff.h:?) process_scheduled_works (kernel/workqueue.c:?) worker_thread (kernel/workqueue.c:?) kthread (kernel/kthread.c:438) ret_from_fork (arch/x86/kernel/process.c:164) ret_from_fork_asm (arch/x86/entry/entry_64.S:258) Allocated by task 955: kasan_save_track (mm/kasan/common.c:58 mm/kasan/common.c:78) __kasan_slab_alloc (mm/kasan/common.c:369) kmem_cache_alloc_noprof (mm/slub.c:4539) sk_prot_alloc (net/core/sock.c:2240) sk_alloc (net/core/sock.c:2301) unix_create1 (net/unix/af_unix.c:1099) unix_create (net/unix/af_unix.c:1169) __sock_create (net/socket.c:1606) __sys_socketpair (net/socket.c:1811) __x64_sys_socketpair (net/socket.c:1863 net/socket.c:1860 net/socket.c:1860) do_syscall_64 (arch/x86/entry/syscall_64.c:?) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) Freed by task 496: kasan_save_track (mm/kasan/common.c:58 mm/kasan/common.c:78) kasan_save_free_info (mm/kasan/generic.c:587) __kasan_slab_free (mm/kasan/common.c:287) kmem_cache_free (mm/slub.c:6165) __sk_destruct (net/core/sock.c:2282 net/core/sock.c:2384) sk_psock_destroy (./include/net/sock.h:?) process_scheduled_works (kernel/workqueue.c:?) worker_thread (kernel/workqueue.c:?) kthread (kernel/kthread.c:438) ret_from_fork (arch/x86/kernel/process.c:164) ret_from_fork_asm (arch/x86/entry/entry_64.S:258) Fixes: c63829182c37 ("af_unix: Implement ->psock_update_sk_prot()") Fixes: 77462de14a43 ("af_unix: Add read_sock for stream socket types") Reported-by: Xingyu Jin Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260415184830.3988432-1-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/unix/af_unix.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b339f83caf036..12c075a07e76b 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1985,16 +1985,19 @@ static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb) static void unix_destruct_scm(struct sk_buff *skb) { - struct scm_cookie scm; + struct scm_cookie scm = {}; + + swap(scm.pid, UNIXCB(skb).pid); - memset(&scm, 0, sizeof(scm)); - scm.pid = UNIXCB(skb).pid; if (UNIXCB(skb).fp) unix_detach_fds(&scm, skb); - /* Alas, it calls VFS */ - /* So fscking what? fput() had been SMP-safe since the last Summer */ scm_destroy(&scm); +} + +static void unix_wfree(struct sk_buff *skb) +{ + unix_destruct_scm(skb); sock_wfree(skb); } @@ -2010,7 +2013,7 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen if (scm->fp && send_fds) err = unix_attach_fds(scm, skb); - skb->destructor = unix_destruct_scm; + skb->destructor = unix_wfree; return err; } @@ -2087,6 +2090,13 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb) } } +static void unix_orphan_scm(struct sock *sk, struct sk_buff *skb) +{ + scm_stat_del(sk, skb); + unix_destruct_scm(skb); + skb->destructor = sock_wfree; +} + /* * Send AF_UNIX data. */ @@ -2704,10 +2714,16 @@ static int unix_read_skb(struct sock *sk, skb_read_actor_t recv_actor) int err; mutex_lock(&u->iolock); + skb = skb_recv_datagram(sk, MSG_DONTWAIT, &err); - mutex_unlock(&u->iolock); - if (!skb) + if (!skb) { + mutex_unlock(&u->iolock); return err; + } + + unix_orphan_scm(sk, skb); + + mutex_unlock(&u->iolock); return recv_actor(sk, skb); } @@ -2905,6 +2921,9 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) #endif spin_unlock(&queue->lock); + + unix_orphan_scm(sk, skb); + mutex_unlock(&u->iolock); return recv_actor(sk, skb); From 12e43f99242b0dbb213ab9ed074116658f1d2967 Mon Sep 17 00:00:00 2001 From: Eckhart Mohr Date: Wed, 8 Jul 2026 15:21:06 +0200 Subject: [PATCH 0648/3297] ALSA: hda/realtek: Add quirk for TongFang X6xx45xU commit d595255241e5fec0c94adeebf2565524398e37c5 upstream. Fix microphone detection on built in headphone jack for some devices. Signed-off-by: Eckhart Mohr Cc: stable@vger.kernel.org Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260708132135.102680-1-wse@tuxedocomputers.com 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 04c2bbe9560f8..39a57417296cc 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7550,6 +7550,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6xx45xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), From ae7f5b05d225c85d425f8e8a43deafa41ee143b7 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 13 Jul 2026 18:03:29 +0800 Subject: [PATCH 0649/3297] ALSA: hda: conexant: Remove mic bias threshold override commit f52524da7084c1a54683ae9fbc73e93fff19dd64 upstream. Remove the mic bias current comparator threshold override (NID 0x1c, verb 0x320, value 0x010) from Conexant codec driver. This override was originally intended to support volume up/down controls on headsets with inline remote controls, but it causes microphone detection failures on some headsets with impedance less than 1k ohm. After consulting with the vendor's engineers, it was confirmed that this setting is board-specific and should be handled by BIOS/firmware rather than the generic codec driver, especially since inline remote support is not currently implemented. Fixes: 7aeb25908648 ("ALSA: hda/conexant: Fix headset auto detect fail in cx8070 and SN6140") Cc: stable@vger.kernel.org Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260713100329.306892-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/conexant.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index 263773f8bd6ae..c3c8326d0b77a 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -168,9 +168,6 @@ static void cx_fixup_headset_recog(struct hda_codec *codec) { unsigned int mic_present; - /* fix some headset type recognize fail issue, such as EDIFIER headset */ - /* set micbias output current comparator threshold from 66% to 55%. */ - snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010); /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register * value adjustment trim from 2.2K ohms to 2.0K ohms. */ From 2f33044aedd7aa314d026fa39a809cb4381c208c Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:24:19 +0800 Subject: [PATCH 0650/3297] ALSA: hda: Fix cached processing coefficient verbs commit f67be28fdf8b5d31ac1cc1152bb17250f9f8f513 upstream. Intel HD Audio defines Coefficient Index and Processing Coefficient as separate audio widget controls in the Audio Widget Verb Definitions: Coefficient Index selects the coefficient slot, while Processing Coefficient accesses the value at the selected slot. hda_reg_read_coef() selects the slot with AC_VERB_SET_COEF_INDEX, but then uses AC_VERB_GET_COEF_INDEX for the value read. That reads back the selected index instead of the coefficient value. hda_reg_write_coef() has the same issue and builds the value write from AC_VERB_GET_COEF_INDEX instead of AC_VERB_SET_PROC_COEF. This only affects the regmap coefficient cache path used by codecs that set codec->cache_coef. Direct coefficient helpers already use the normal SET_COEF_INDEX followed by GET_PROC_COEF or SET_PROC_COEF sequence, which is likely why this has not been noticed widely. Use AC_VERB_GET_PROC_COEF for cached coefficient reads and AC_VERB_SET_PROC_COEF for cached coefficient writes. Fixes: 40ba66a702b8 ("ALSA: hda - Add cache support for COEF read/write") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/DB9023BF2920BA99+20260707132419.1731342-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/core/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/hda/core/regmap.c b/sound/hda/core/regmap.c index e7b866fc52c15..d6eb17aa9e086 100644 --- a/sound/hda/core/regmap.c +++ b/sound/hda/core/regmap.c @@ -214,7 +214,7 @@ static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg, err = snd_hdac_exec_verb(codec, verb, 0, NULL); if (err < 0) return err; - verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8); + verb = (reg & ~0xfffff) | (AC_VERB_GET_PROC_COEF << 8); return snd_hdac_exec_verb(codec, verb, 0, val); } @@ -232,7 +232,7 @@ static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg, err = snd_hdac_exec_verb(codec, verb, 0, NULL); if (err < 0) return err; - verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) | + verb = (reg & ~0xfffff) | (AC_VERB_SET_PROC_COEF << 8) | (val & 0xffff); return snd_hdac_exec_verb(codec, verb, 0, NULL); } From 6b2c0cd5f9689dff51dc221d25fe28b393353e9a Mon Sep 17 00:00:00 2001 From: Damien Laine Date: Sun, 12 Jul 2026 23:37:08 +0200 Subject: [PATCH 0651/3297] ALSA: hda/realtek: Fix speakers on Legion Pro 7 16ARX8H with codec SSID 17aa:38a7 commit d35dfb6329accfe1cfa0b57e35214b5cbbe0f9ae upstream. Some units of the Lenovo Legion Pro 7 16ARX8H (82WS) report codec subsystem ID 17aa:38a7 instead of 17aa:38a8. Since only 38a8 has a codec SSID quirk, these machines fall through to the PCI SSID match 17aa:386f (Legion Pro 7i 16IAX7) and get ALC287_FIXUP_CS35L41_I2C_2, which probes the Cirrus amplifiers of the Intel variant. The TI TAS2781 amplifier (ACPI TIAS2781:00) present on this AMD variant is never bound and the internal speakers remain silent. Add a codec SSID quirk for 17aa:38a7 pointing to ALC287_FIXUP_TAS2781_I2C, mirroring the existing 38a8 entry. Tested on a Legion Pro 7 16ARX8H (82WS, BIOS LPCN62WW): with the codec SSID overridden to 17aa:38a8 via the HDA patch loader, the TAS2781 amplifier binds and the internal speakers work. Cc: Signed-off-by: Damien Laine Link: https://patch.msgid.link/20260712213708.1835469-1-damien.laine@gmail.com 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 39a57417296cc..01483eb545cda 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7435,6 +7435,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), From caf800e0cab945e02059f29c8829d1493b46991b Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:21 +0000 Subject: [PATCH 0652/3297] media: uvcvideo: Use hw timestaming if the clock buffer is full commit ede7de6e6b3db552d10ac50557d69c50d1b08486 upstream. In some situations, even with a full clock buffer, it does not contain 250msec of data. This results in the driver jumping back from software to hardware timestapsing creating a nasty artifact in the video. If the clock buffer is full, use it to calculate the timestamp instead of defaulting to software stamps, the reduced accuracy is less visible than jumping from one timestamping mechanism to the other. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-2-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index ec76595f3c4be..709d48ef37a36 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -834,15 +834,22 @@ void uvc_video_clock_update(struct uvc_streaming *stream, y2 += 2048 << 16; /* - * Have at least 1/4 of a second of timestamps before we - * try to do any calculation. Otherwise we do not have enough - * precision. This value was determined by running Android CTS - * on different devices. + * If the buffer is not full, we want to gather at least 1/4th of + * timestamps before using HW timestamping. We do this to avoid jitter + * on the initial frames. + * + * If the buffer is full we would use it regardless of how much data + * it represents. This could be solved with an infinite big circular + * buffer, but RAM is expensive these days, specially the infinitely + * big. + * + * The value of 1/4th of a second was determined by running Android's + * CTS on different devices. * * dev_sof runs at 1KHz, and we have a fixed point precision of * 16 bits. */ - if ((y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From 1a9baac645769691ee0bf0fe6d7379257e8ee714 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 17 Apr 2026 05:19:29 +0000 Subject: [PATCH 0653/3297] media: uvcvideo: Avoid partial metadata buffers commit a15b773fe4ffa450b56347cc506b2d1405600f5d upstream. If the metadata queue that is empty receives a new buffer while we are in the middle of processing a frame, the first metadata buffer will contain partial information. Avoid this by tracking the state of the metadata buffer and making sure that it is in sync with the data buffer. Now that we are at it, make sure that we skip buffers of size 1 or 0. They are not allowed by the spec... but it is a simple check to add and better be safe than sorry. Fixes: 088ead255245 ("media: uvcvideo: Add a metadata device node") Cc: stable@vger.kernel.org Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260417-uvc-meta-partial-v2-2-31d274af7d2d@chromium.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 709d48ef37a36..ce2b5d6f88db5 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1157,7 +1157,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) * uvc_video_decode_end will never be called with a NULL buffer. */ static int uvc_video_decode_start(struct uvc_streaming *stream, - struct uvc_buffer *buf, const u8 *data, int len) + struct uvc_buffer *buf, + struct uvc_buffer *meta_buf, + const u8 *data, int len) { u8 header_len; u8 fid; @@ -1230,6 +1232,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; + if (meta_buf) + meta_buf->state = UVC_BUF_STATE_ACTIVE; } /* @@ -1432,7 +1436,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, ktime_t time; const u8 *scr; - if (!meta_buf || length == 2) + if (length <= 2 || !meta_buf || meta_buf->state != UVC_BUF_STATE_ACTIVE) return; has_pts = mem[1] & UVC_STREAM_PTS; @@ -1549,7 +1553,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(stream, buf, mem, + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); @@ -1598,7 +1602,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, */ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(stream, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, + len); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); } while (ret == -EAGAIN); From 0f64f808fb4ee55abc0ac2d983f80c6e49697a6e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:53 +0000 Subject: [PATCH 0654/3297] media: uvcvideo: Fix buffer sequence in frame gaps commit 2f24ac8dd87983a55f0498898f34a5f2b735b802 upstream. In UVC, the FID flips with every frame. For every FID flip, we increase the stream sequence number. Now, if a FID flips multiple times and there is no data transferred between the flips, the buffer sequence number will be set to the value of the stream sequence number after the first flip. Userspace uses the buffer sequence number to determine if there have been missing frames. With the current behaviour, userspace will think that the gap is in the wrong location. This patch modifies uvc_video_decode_start() to provide the correct buffer sequence number and timestamp. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index ce2b5d6f88db5..b9ab297084059 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1186,6 +1186,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, stream->sequence++; if (stream->sequence) uvc_video_stats_update(stream); + + /* + * On a FID flip initialize sequence number and timestamp. + * + * The driver already takes care of injecting FID flips for + * UVC_QUIRK_STREAM_NO_FID and UVC_QUIRK_MJPEG_NO_EOF. + */ + if (buf) { + buf->buf.field = V4L2_FIELD_NONE; + buf->buf.sequence = stream->sequence; + buf->buf.vb2_buf.timestamp = + ktime_to_ns(uvc_video_get_time()); + } } uvc_video_clock_decode(stream, buf, data, len); @@ -1226,10 +1239,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -ENODATA; } - buf->buf.field = V4L2_FIELD_NONE; - buf->buf.sequence = stream->sequence; - buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time()); - /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; if (meta_buf) From aed8111f2392d70c64bb0a55b3daf70e0df598ff Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:20 +0000 Subject: [PATCH 0655/3297] media: uvcvideo: Fix dev_sof filtering in hw timestamp commit edc1917599c5339aedc83135cade66517e0a2972 upstream. To avoid filling the clock circular buffer with duplicated data we only add it if the new value sof is different than the last added sof. The issue is that we compare the unprocess sof with the processed sof. If there is a sof_offset, or UVC_QUIRK_INVALID_DEVICE_SOF is enabled, the comparison will not work as expected. This patch moves the comparison to the right place. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-1-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index b9ab297084059..43d936b5783ce 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -583,16 +583,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - /* - * To limit the amount of data, drop SCRs with an SOF identical to the - * previous one. This filtering is also needed to support UVC 1.5, where - * all the data packets of the same frame contains the same SOF. In that - * case only the first one will match the host_sof. - */ sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (sample.dev_sof == stream->clock.last_sof) - return; - sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); /* @@ -664,6 +655,16 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, } sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; + + /* + * To limit the amount of data, drop SCRs with an SOF identical to the + * previous one. This filtering is also needed to support UVC 1.5, where + * all the data packets of the same frame contains the same SOF. In that + * case only the first one will match the host_sof. + */ + if (sample.dev_sof == stream->clock.last_sof) + return; + uvc_video_clock_add_sample(&stream->clock, &sample); stream->clock.last_sof = sample.dev_sof; } From bf58be93c51ba02fc05023257261f882175f5ead Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:23 +0000 Subject: [PATCH 0656/3297] media: uvcvideo: Do not add clock samples with small sof delta commit ba649fff36c1fe68489a86d00285224907edd436 upstream. Some UVC 1.1 cameras running in fast isochronous mode tend to spam the USB host with a lot of empty packets. These packets contain clock information and are added to the clock buffer but do not add any accuracy to the calculation. In fact, it is quite the opposite, in our calculations, only the first and the last timestamp is used, and we only have 32 slots. Ignore the samples that will produce less than MIN_HW_TIMESTAMP_DIFF data. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Tested-by: Yunke Cao Reviewed-by: Hans de Goede Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-4-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 43d936b5783ce..d443acdb6af15 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -537,6 +537,15 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_unlock_irqrestore(&clock->lock, flags); } +static inline u16 sof_diff(u16 a, u16 b) +{ + /* + * Because the result is modulo 2048 (via & 2047), we do not need a + * special case for a < b. + */ + return (a - b) & 2047; +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) @@ -657,12 +666,13 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; /* - * To limit the amount of data, drop SCRs with an SOF identical to the + * To limit the amount of data, drop SCRs with an SOF similar to the * previous one. This filtering is also needed to support UVC 1.5, where * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - if (sample.dev_sof == stream->clock.last_sof) + if (sof_diff(sample.dev_sof, stream->clock.last_sof) <= + (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; uvc_video_clock_add_sample(&stream->clock, &sample); From 19cb644d0ca5960be3e8c196e0c8a6da5f526ab9 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:22 +0000 Subject: [PATCH 0657/3297] media: uvcvideo: Relax the constrains for interpolating the hw clock commit 1719d78f832dda8dd3f09a867ba74e05d6f11308 upstream. In the initial version we set the min value to 250msec. Looks like 100msec can also provide a good value. Now that we are at it, add a macro to make it cleaner. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-3-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index d443acdb6af15..6e139f510c2b3 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -494,6 +494,13 @@ static int uvc_commit_video(struct uvc_streaming *stream, * Clocks and timestamps */ +/* + * The accuracy of the hardware timestamping depends on having enough data to + * interpolate between the different clock domains. This value is sof cycles, + * this is, milliseconds. + */ +#define UVC_MIN_HW_TIMESTAMP_DIFF 100 + static inline ktime_t uvc_video_get_time(void) { if (uvc_clock_param == CLOCK_MONOTONIC) @@ -854,13 +861,13 @@ void uvc_video_clock_update(struct uvc_streaming *stream, * buffer, but RAM is expensive these days, specially the infinitely * big. * - * The value of 1/4th of a second was determined by running Android's - * CTS on different devices. + * The value of UVC_MIN_HW_TIMESTAMP_DIFF was determined by running + * Android's CTS on different devices. * - * dev_sof runs at 1KHz, and we have a fixed point precision of - * 16 bits. + * y1 and y2 are dev_sof with a fixed point precision of 16 bits. */ - if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && + (y2 - y1) < (UVC_MIN_HW_TIMESTAMP_DIFF << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From 79982331c1738c84679f2b88a69ddb402292196f Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:52 +0000 Subject: [PATCH 0658/3297] media: uvcvideo: Fix sequence number when no EOF commit f078966ca1fb1b3865d8e6bbe2705cfd277fc637 upstream. If the driver could not detect the EOF, the sequence number is increased twice: 1) When we enter uvc_video_decode_start() with the old buffer and FID has flipped => We return -EAGAIN and last_fid is not flipped 2) When we enter uvc_video_decode_start() with the new buffer. Fix this issue by moving the new frame detection logic earlier in uvc_video_decode_start(). This also has some nice side affects: - The error status from the new packet will no longer get propagated to the previous frame-buffer. - uvc_video_clock_decode() will no longer update the previous frame buf->stf with info from the new packet. - uvc_video_clock_decode() and uvc_video_stats_decode() will no longer get called twice for the same packet. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Reported-by: Hans de Goede Closes: https://lore.kernel.org/linux-media/CANiDSCuj4cPuB5_v2xyvAagA5FjoN8V5scXiFFOeD3aKDMqkCg@mail.gmail.com/T/#me39fb134e8c2c085567a31548c3403eb639625e4 Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_video.c | 92 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 6e139f510c2b3..9629e17cc5eb7 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1196,6 +1196,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, header_len = data[0]; fid = data[1] & UVC_STREAM_FID; + /* + * Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * stream->last_fid is initialized to -1, and buf->bytesused to 0, + * so the first isochronous frame will never trigger an end of frame + * detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting end of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != stream->last_fid && buf && buf->bytesused != 0) { + uvc_dbg(stream->dev, FRAME, + "Frame complete (FID bit toggled)\n"); + buf->state = UVC_BUF_STATE_READY; + + return -EAGAIN; + } + + /* + * Some cameras, when running two parallel streams (one MJPEG alongside + * another non-MJPEG stream), are known to lose the EOF packet for a frame. + * We can detect the end of a frame by checking for a new SOI marker, as + * the SOI always lies on the packet boundary between two frames for + * these devices. + */ + if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc == V4L2_PIX_FMT_JPEG) && + buf && buf->bytesused != 0) { + const u8 *packet = data + header_len; + + if (len >= header_len + 2 && + packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI) { + buf->state = UVC_BUF_STATE_READY; + buf->error = 1; + stream->last_fid ^= UVC_STREAM_FID; + return -EAGAIN; + } + } + /* * Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames. @@ -1263,51 +1310,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, meta_buf->state = UVC_BUF_STATE_ACTIVE; } - /* - * Mark the buffer as done if we're at the beginning of a new frame. - * End of frame detection is better implemented by checking the EOF - * bit (FID bit toggling is delayed by one frame compared to the EOF - * bit), but some devices don't set the bit at end of frame (and the - * last payload can be lost anyway). We thus must check if the FID has - * been toggled. - * - * stream->last_fid is initialized to -1, so the first isochronous - * frame will never trigger an end of frame detection. - * - * Empty buffers (bytesused == 0) don't trigger end of frame detection - * as it doesn't make sense to return an empty buffer. This also - * avoids detecting end of frame conditions at FID toggling if the - * previous payload had the EOF bit set. - */ - if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_dbg(stream->dev, FRAME, - "Frame complete (FID bit toggled)\n"); - buf->state = UVC_BUF_STATE_READY; - return -EAGAIN; - } - - /* - * Some cameras, when running two parallel streams (one MJPEG alongside - * another non-MJPEG stream), are known to lose the EOF packet for a frame. - * We can detect the end of a frame by checking for a new SOI marker, as - * the SOI always lies on the packet boundary between two frames for - * these devices. - */ - if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && - (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || - stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) { - const u8 *packet = data + header_len; - - if (len >= header_len + 2 && - packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI && - buf->bytesused != 0) { - buf->state = UVC_BUF_STATE_READY; - buf->error = 1; - stream->last_fid ^= UVC_STREAM_FID; - return -EAGAIN; - } - } - stream->last_fid = fid; return header_len; From d97a6b4a5a4fb30ebdabfe2c40ac9163c9428cd6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 25 Dec 2025 18:36:12 +0800 Subject: [PATCH 0659/3297] dt-bindings: media: sun4i-a10-video-engine: Add interconnect properties commit 018f2dc3a42098d3b04edd1480adc51d268adb14 upstream. The Allwinner video engine sits behind the MBUS that is represented as an interconnect. Make sure that the interconnect properties are valid in the binding. Fixes: d41662e52a03 ("media: dt-bindings: media: allwinner,sun4i-a10-video-engine: Add R40 compatible") Cc: stable@vger.kernel.org Signed-off-by: Chen-Yu Tsai Acked-by: Rob Herring (Arm) Acked-by: Jernej Skrabec Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- .../media/allwinner,sun4i-a10-video-engine.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml index 541325f900a1d..01f2afa023f04 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml @@ -63,6 +63,16 @@ properties: CMA pool to use for buffers allocation instead of the default CMA pool. + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnects: + maxItems: 1 + + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnect-names: + const: dma-mem + required: - compatible - reg From 03fd857c33456ef23761ce6732474550c37de7e2 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 9 Jun 2026 14:26:40 +0800 Subject: [PATCH 0660/3297] dt-bindings: power: imx93: Add MIPI PHY power domain commit 6aa38ef0eab32df5409b72d62509de6ba09cea50 upstream. Add MIPI PHY power domain for shared PHY resources used by both MIPI DSI and CSI blocks. Signed-off-by: Guoniu Zhou Reviewed-by: Frank Li Acked-by: Krzysztof Kozlowski Reviewed-by: Peng Fan Fixes: e9aa77d413c9 ("soc: imx: add i.MX93 media blk ctrl driver") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- include/dt-bindings/power/fsl,imx93-power.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/power/fsl,imx93-power.h b/include/dt-bindings/power/fsl,imx93-power.h index 17f9f015bf7df..071221fe5c57d 100644 --- a/include/dt-bindings/power/fsl,imx93-power.h +++ b/include/dt-bindings/power/fsl,imx93-power.h @@ -11,5 +11,6 @@ #define IMX93_MEDIABLK_PD_PXP 2 #define IMX93_MEDIABLK_PD_LCDIF 3 #define IMX93_MEDIABLK_PD_ISI 4 +#define IMX93_MEDIABLK_PD_MIPI_PHY 5 #endif From d354716245192de2d203f2b35f22ab8dc13595e2 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 6 Jul 2026 20:03:32 +0200 Subject: [PATCH 0661/3297] serial: msm: Disable DMA for kernel console UART commit 22dd2777e6c180e1c945b00f6d18550979436324 upstream. At the moment, concurrent writes from userspace and the kernel to the console can trigger a race condition that results in an infinite loop of the same messages printed over and over again. This is most likely to happen during system startup or shutdown when the init system starts/stops a large number of system services that interact with various kernel code. When userspace writes to the TTY device, the driver initiates an asynchronous DMA transfer and releases the port lock. At the same moment, the kernel printk path might grab the port lock and re-configure the UART controller for PIO, without waiting for the DMA operation to complete. It seems like this collision results in zero progress being reported for the DMA engine, so the same text is printed to the console over and over again. For the kernel console, we want a reliable output path that will be functional even during crashes etc. So rather than implementing complex code to synchronize the kernel console write routines with the userspace DMA write routines, simply disable DMA for the console UART instance. Similar checks exist in many other serial drivers, e.g. 8250_port.c, imx.c, sh-sci.c etc. Cc: stable Fixes: 3a878c430fd6 ("tty: serial: msm: Add TX DMA support") Signed-off-by: Stephan Gerhold Acked-by: Konrad Dybcio Link: https://patch.msgid.link/20260706-serial-msm-console-dma-collision-v1-1-3179b8cb1d89@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 2e999cb9c9744..bfa44b01c3e97 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1228,7 +1228,8 @@ static int msm_startup(struct uart_port *port) data |= MSM_UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, MSM_UART_MR1); - if (msm_port->is_uartdm) { + /* Disable DMA for console to prevent PIO/DMA collisions */ + if (msm_port->is_uartdm && !uart_console(port)) { msm_request_tx_dma(msm_port, msm_port->uart.mapbase); msm_request_rx_dma(msm_port, msm_port->uart.mapbase); } From c37095c431c39000ce818d3ff568a61b0e0ee02a Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Mon, 15 Jun 2026 06:38:40 +0000 Subject: [PATCH 0662/3297] serial: max310x: implement gpio_chip::get_direction() commit a483b1a91b33b7533280e7c3efd2bc1275caef18 upstream. It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio. The MAX310X keeps the GPIO direction in the GPIOCFG register (a set bit selects output), which the existing direction_input/output callbacks already program, so the current direction can be read back directly. Fixes: f65444187a66 ("serial: New serial driver MAX310X") Cc: stable Signed-off-by: Tapio Reijonen Reviewed-by: Linus Walleij Reviewed-by: Bartosz Golaszewski Reviewed-by: Hugo Villeneuve Link: https://patch.msgid.link/20260615-b4-serial-max310x-gpio-get-direction-v2-1-4704ba2b181a@vaisala.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index ac7d3f197c3a5..09b9ab57d2b44 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1212,6 +1212,17 @@ static int max310x_gpio_set(struct gpio_chip *chip, unsigned int offset, return 0; } +static int max310x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct max310x_port *s = gpiochip_get_data(chip); + struct uart_port *port = &s->p[offset / 4].port; + unsigned int val; + + val = max310x_port_read(port, MAX310X_GPIOCFG_REG); + + return val & BIT(offset % 4) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct max310x_port *s = gpiochip_get_data(chip); @@ -1421,6 +1432,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty s->gpio.owner = THIS_MODULE; s->gpio.parent = dev; s->gpio.label = devtype->name; + s->gpio.get_direction = max310x_gpio_get_direction; s->gpio.direction_input = max310x_gpio_direction_input; s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output; From 2ecd118fb6913584a4889a4a778aef5283bae579 Mon Sep 17 00:00:00 2001 From: Matthias Feser Date: Tue, 26 May 2026 07:35:09 +0000 Subject: [PATCH 0663/3297] serial: 8250_omap: clear rx_running on zero-length DMA completes commit 061b627ba534230a18ec4d7251562af12325d06a upstream. On AM33xx RX DMA only triggers when the FIFO reaches the configured threshold (typically 48 bytes). For smaller bursts no DMA request is issued and the FIFO is drained by RX timeout. In this case __dma_rx_do_complete() can legitimately see count == 0. The current code exits early in this case and does not clear dma->rx_running, leaving the DMA state inconsistent. This can prevent RX DMA from restarting and may cause omap_8250_rx_dma_flush() to fail, marking DMA as broken. Fix this by clearing dma->rx_running once the DMA transfer has completed or been terminated, even if no data was transferred. Fixes: a5fd8945a478 ("serial: 8250: 8250_omap.c: Clear DMA RX running status only after DMA termination is done") Cc: stable Signed-off-by: Matthias Feser Reviewed-by: Moteen Shah Link: https://patch.msgid.link/BE3P281MB55155F2F5795E411F5A65282EE0B2@BE3P281MB5515.DEUP281.PROD.OUTLOOK.COM Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 272bc07c9a6b5..454a5e0f40c42 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -960,11 +960,12 @@ static void __dma_rx_do_complete(struct uart_8250_port *p) dev_err(p->port.dev, "teardown incomplete\n"); } } + + dma->rx_running = 0; if (!count) goto out; ret = tty_insert_flip_string(tty_port, dma->rx_buf, count); - dma->rx_running = 0; p->port.icount.rx += ret; p->port.icount.buf_overrun += count - ret; out: From 35a967ff8b24db09ee429c39c5b5e6571639997d Mon Sep 17 00:00:00 2001 From: Li Daming Date: Tue, 9 Jun 2026 15:09:09 +0100 Subject: [PATCH 0664/3297] rxrpc: serialize kernel accept preallocation with socket teardown commit dc175389b18c29a5303ee83169ec653adfae3e17 upstream. rxrpc_kernel_charge_accept() reads rx->backlog without any socket/backlog synchronization and passes that raw pointer into rxrpc_service_prealloc_one(). A concurrent rxrpc_discard_prealloc() sets rx->backlog = NULL and frees the backlog rings, so a kernel preallocation worker can keep using a freed struct rxrpc_backlog while updating *_backlog_head/tail and array slots. Serialize the state check and backlog lookup with the socket lock, and reject kernel preallocation once teardown has disabled listening or discarded the service backlog. Fixes: 00e907127e6f ("rxrpc: Preallocate peers, conns and calls for incoming service requests") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Li Daming Signed-off-by: Ren Wei Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-6-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/call_accept.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 00982a030744b..eccd13f425ea2 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -471,13 +471,26 @@ int rxrpc_kernel_charge_accept(struct socket *sock, rxrpc_notify_rx_t notify_rx, unsigned long user_call_ID, gfp_t gfp, unsigned int debug_id) { - struct rxrpc_sock *rx = rxrpc_sk(sock->sk); - struct rxrpc_backlog *b = rx->backlog; + struct rxrpc_backlog *b; + struct rxrpc_sock *rx; + struct sock *sk; + int ret; - if (sock->sk->sk_state == RXRPC_CLOSE) - return -ESHUTDOWN; + sk = sock->sk; + rx = rxrpc_sk(sk); + + lock_sock(sk); + if (sk->sk_state != RXRPC_SERVER_LISTENING || !rx->backlog) { + ret = -ESHUTDOWN; + goto out; + } + + b = rx->backlog; + ret = rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID, + gfp, debug_id); - return rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID, - gfp, debug_id); +out: + release_sock(sk); + return ret; } EXPORT_SYMBOL(rxrpc_kernel_charge_accept); From d3b642cf95d48234590cc91450d8705a9bf6b540 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Tue, 9 Jun 2026 15:09:05 +0100 Subject: [PATCH 0665/3297] rxrpc: rxrpc_verify_data ensure rx_dec_buffer alloc commit 16c8ae9735c5bd7e54dd7478d6348e0fc860842d upstream. rxrpc_recvmsg_data() calls rxrpc_verify_data() whenever the rxrpc_call.rx_dec_buffer is unallocated and assumes that upon successful return that rx_dec_buffer must be allocated. However, rxrpc_verify_data() does not request an allocation if the rxrpc_skb_priv.len is zero. In addition, failure to allocate rx_dec_buffer will result in a call to skb_copy_bits() with a NULL destination which can trigger a NULL pointer dereference. To prevent these issues rxrpc_verify_data() is modified to always attempt to allocate the rxrpc_call.rx_dec_buffer if it is NULL. This issue was identified with assistance of a private sashiko instance. Fixes: d2bc90cf6c75cb ("rxrpc: Fix DATA decrypt vs splice() by copying data to buffer in recvmsg") Reported-by: Simon Horman Signed-off-by: Jeffrey Altman Signed-off-by: David Howells cc: Jiayuan Chen cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-2-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index c940600117a48..a3cf5358f16ed 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -161,7 +161,7 @@ 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->len > call->rx_dec_bsize) { + if (sp->len > call->rx_dec_bsize || !call->rx_dec_buffer) { /* Make sure we can hold a 1412-byte jumbo subpacket and make * sure that the buffer size is aligned to a crypto blocksize. */ From 9ada3931beb37068fcb725b34b0398457009f343 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Tue, 9 Jun 2026 15:09:06 +0100 Subject: [PATCH 0666/3297] rxrpc: Don't move a peeked OOB message onto the pending queue commit 5801cff7d5d7b4e9d877dfb627b23eb63167f02c upstream. rxrpc_recvmsg_oob() takes a received oob message off recvmsg_oobq and, if a response is needed, moves it onto the pending_oobq tree. However, only the unlink from recvmsg_oobq is guarded by MSG_PEEK; the move onto pending_oobq always runs. As a result, reading a challenge with MSG_PEEK leaves the skb on recvmsg_oobq while also adding it to pending_oobq. Since struct sk_buff's rbnode shares storage with its next and prev pointers, rb_insert_color() overwrites the list linkage, and the skb, which holds a single reference, becomes reachable from both queues at once. When the socket is closed both queues are drained in turn. While draining recvmsg_oobq, __skb_unlink() follows the next and prev pointers that rbnode has overwritten and writes to a bad address. Also, as the skb holds a single reference but is freed from each queue, both the skb and the connection reference it holds are released twice. This leads to memory corruption and to a use-after-free caused by the connection refcount underflow. MSG_PEEK does not consume the message from the queue, so only unlink it from recvmsg_oobq and then move it onto pending_oobq or free it when the message is actually consumed. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Signed-off-by: Hyunwoo Kim Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-3-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index a3cf5358f16ed..82614cbdb60f5 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -262,12 +262,13 @@ static int rxrpc_recvmsg_oob(struct socket *sock, struct msghdr *msg, break; } - if (!(flags & MSG_PEEK)) + if (!(flags & MSG_PEEK)) { skb_unlink(skb, &rx->recvmsg_oobq); - if (need_response) - rxrpc_add_pending_oob(rx, skb); - else - rxrpc_free_skb(skb, rxrpc_skb_put_oob); + if (need_response) + rxrpc_add_pending_oob(rx, skb); + else + rxrpc_free_skb(skb, rxrpc_skb_put_oob); + } return ret; } From 844b8525ce503405c462ad67f750bec648720397 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Jun 2026 15:09:07 +0100 Subject: [PATCH 0667/3297] rxrpc: Fix UAF in rxgk_issue_challenge() commit 107a4cb0d47e735830f852d83970d5c81f8e1e08 upstream. Fix rxgk_issue_challenge() to free the page containing the challenge content after invoking the tracepoint as the whdr passed to the tracepoint points into the page just freed. Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Reported-by: Marc Dionne Signed-off-by: David Howells cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-4-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/rxgk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 1ad5f7ea32800..3c2bfded7fcc3 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -687,16 +687,17 @@ static int rxgk_issue_challenge(struct rxrpc_connection *conn) ret = do_udp_sendmsg(conn->local->socket, &msg, len); if (ret > 0) rxrpc_peer_mark_tx(conn->peer); - __free_page(page); if (ret < 0) { trace_rxrpc_tx_fail(conn->debug_id, serial, ret, rxrpc_tx_point_rxgk_challenge); + __free_page(page); return -EAGAIN; } trace_rxrpc_tx_packet(conn->debug_id, whdr, rxrpc_tx_point_rxgk_challenge); + __free_page(page); _leave(" = 0"); return 0; } From 0d643a46fdea676afd408b6f81514288903c07e4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:16 +0100 Subject: [PATCH 0668/3297] rxrpc: Fix socket notification race commit e66f8f32f50116670dbbee5bc9e692cd2cd0c8f8 upstream. There's a race between rxrpc_recvmsg() and rxrpc_notify_socket(), whereby the latter's attempt to avoid disabling interrupts and taking the socket's recvmsg_lock if the call is already queued may happen simultaneously with the former's discarding of a call that has nothing queued. Fix this by removing the shortcut. Note that this only affects userspace's use of AF_RXRPC; the AFS filesystem driver doesn't use the socket queue. Fixes: 248f219cb8bc ("rxrpc: Rewrite the data and ack handling code") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-10-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 82614cbdb60f5..d17c81aac6aa8 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -27,8 +27,6 @@ void rxrpc_notify_socket(struct rxrpc_call *call) _enter("%d", call->debug_id); - if (!list_empty(&call->recvmsg_link)) - return; if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { rxrpc_see_call(call, rxrpc_call_see_notify_released); return; From 2b69b61057eb0b3db9ad754ef0f1436b7af3a9f5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:17 +0100 Subject: [PATCH 0669/3297] rxrpc: Fix leak of released call in recvmsg(MSG_PEEK) commit 4bdb9e471f5b1ac9cbe4add5de7ff085a0ec303c upstream. Fix rxrpc_recvmsg() to also drop the ref it holds on an already-released call if MSG_PEEK is in force (the function holds a ref on the call irrespective of whether MSG_PEEK is specified or not). Fixes: 962fb1f651c2 ("rxrpc: Fix recv-recv race of completed call") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-11-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index d17c81aac6aa8..d1ce15929bb85 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -528,8 +528,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { rxrpc_see_call(call, rxrpc_call_see_already_released); mutex_unlock(&call->user_mutex); - if (!(flags & MSG_PEEK)) - rxrpc_put_call(call, rxrpc_call_put_recvmsg); + rxrpc_put_call(call, rxrpc_call_put_recvmsg); goto try_again; } From 0fc5b37faec26241d3cbee732e29ac35ad3184f8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:15 +0100 Subject: [PATCH 0670/3297] rxrpc: Fix potential infinite loop in rxrpc_recvmsg() commit 67a0332f442ef07713cd2d9c13d59db0f1c23648 upstream. Fix the wait in rxrpc_recvmsg() also take check the oob queue. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-9-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index d1ce15929bb85..fb49563dc9271 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -436,7 +436,8 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, return -EAGAIN; } - if (list_empty(&rx->recvmsg_q)) { + if (list_empty(&rx->recvmsg_q) && + skb_queue_empty_lockless(&rx->recvmsg_oobq)) { ret = -EWOULDBLOCK; if (timeo == 0) { call = NULL; From 7940e5c535489deaab3d831c192c089a17df98e0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:18 +0100 Subject: [PATCH 0671/3297] rxrpc: Fix rxrpc_rotate_tx_rotate() to check there's something to rotate commit a5462da5a349fc7f17ad5ebd899380260d03e7ed upstream. Fix rxrpc_rotate_tx_rotate() to check that there's something in the transmission buffer to be rotated before it attempts to rotate anything. Fixes: b341a0263b1b ("rxrpc: Implement progressive transmission queue struct") Link: https://sashiko.dev/#/patchset/20260618134802.2477777-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-12-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/input.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index ce761466b02d6..614c2c9cd387b 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -236,6 +236,9 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, call->acks_lowest_nak = to; } + if (after(seq, to)) + return false; + /* We may have a left over fully-consumed buffer at the front that we * couldn't drop before (rotate_and_keep below). */ From 647e8e69c6ea35b8ed3968617aa694f4df51ff33 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:14 +0100 Subject: [PATCH 0672/3297] rxrpc: Fix oob challenge leak in cleanup after notification failure commit 092275882aec4a70ba55c3efb66fff947c81656a upstream. Fix rxrpc_notify_socket_oob() to return an indication of failure in the event that it failed to queue a packet and fix rxrpc_post_challenge() to clean up the connection ref in such an event. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-8-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/ar-internal.h | 4 ++-- net/rxrpc/conn_event.c | 9 +++++++-- net/rxrpc/oob.c | 7 +++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 98f2165159d72..ead3419f08b76 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1355,9 +1355,9 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net) } /* - * out_of_band.c + * oob.c */ -void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb); +bool rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb); void rxrpc_add_pending_oob(struct rxrpc_sock *rx, struct sk_buff *skb); int rxrpc_sendmsg_oob(struct rxrpc_sock *rx, struct msghdr *msg, size_t len); diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index c96ca615b787c..611c790bc6d0c 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -436,7 +436,7 @@ static bool rxrpc_post_challenge(struct rxrpc_connection *conn, struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_call *call = NULL; struct rxrpc_sock *rx; - bool respond = false; + bool respond = false, queued = false; sp->chall.conn = rxrpc_get_connection(conn, rxrpc_conn_get_challenge_input); @@ -472,8 +472,13 @@ static bool rxrpc_post_challenge(struct rxrpc_connection *conn, } if (call) - rxrpc_notify_socket_oob(call, skb); + queued = rxrpc_notify_socket_oob(call, skb); rcu_read_unlock(); + if (call && !queued) { + rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input); + sp->chall.conn = NULL; + return false; + } if (!call) rxrpc_post_packet_to_conn(conn, skb); diff --git a/net/rxrpc/oob.c b/net/rxrpc/oob.c index 05ca9c1faa577..8a7fccb62f1af 100644 --- a/net/rxrpc/oob.c +++ b/net/rxrpc/oob.c @@ -32,11 +32,12 @@ struct rxrpc_oob_params { * Post an out-of-band message for attention by the socket or kernel service * associated with a reference call. */ -void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) +bool rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_sock *rx; struct sock *sk; + bool queued = false; rcu_read_lock(); @@ -49,6 +50,7 @@ void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) skb->skb_mstamp_ns = rx->oob_id_counter++; rxrpc_get_skb(skb, rxrpc_skb_get_post_oob); skb_queue_tail(&rx->recvmsg_oobq, skb); + queued = true; trace_rxrpc_notify_socket(call->debug_id, sp->hdr.serial); if (rx->app_ops) @@ -56,11 +58,12 @@ void rxrpc_notify_socket_oob(struct rxrpc_call *call, struct sk_buff *skb) } spin_unlock_irq(&rx->recvmsg_lock); - if (!rx->app_ops && !sock_flag(sk, SOCK_DEAD)) + if (queued && !rx->app_ops && !sock_flag(sk, SOCK_DEAD)) sk->sk_data_ready(sk); } rcu_read_unlock(); + return queued; } /* From 8db6a2c95e36938076b37466ce36774526a5c8c3 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Wed, 24 Jun 2026 17:38:08 +0100 Subject: [PATCH 0673/3297] rxrpc: Fix ACKALL packet handling commit 9b6ce594808580b2a19e6e1aa459ef56c0153ac1 upstream. rxrpc_input_ackall() accepts ACKALL packets without checking whether the call is in a state that can legitimately have outstanding transmit buffers. A forged ACKALL can therefore reach a new service call in RXRPC_CALL_SERVER_RECV_REQUEST before any reply packets have been queued. In that state call->tx_top is zero and call->tx_queue is NULL, so rxrpc_rotate_tx_window() dereferences a NULL txqueue and triggers a null-pointer dereference. Fix the handling of ACKALL packets by the following means: (1) Add two new call states: RXRPC_CALL_CLIENT_PRE_SEND which indicates that the client call is connected, but nothing has been transmitted as yet; and RXRPC_CALL_CLIENT_AWAIT_ACK, which indicates that everything has been transmitted at least once, but we're now waiting for the stuff remaining in the Tx buffer to be ACK'd (retransmissions may still happen). The RXRPC_CALL_CLIENT_PRE_SEND state is set when the call is assigned a channel and transitions to RXRPC_CALL_CLIENT_SEND_REQUEST when the first packet is transmitted. RXRPC_CALL_CLIENT_AWAIT_REPLY is then narrowed in scope to indicate that all Tx packets have been ACK'd and we're now waiting for the reply to be received. (2) As per Wyatt Feng's original patch[1], the ACKALL handler then checks that the call state is one in which there might be stuff in the Tx buffer to ACK, but now this includes AWAIT_ACK rather than AWAIT_REPLY. ACKALL packets are ignored if received in the wrong state. Note that unlike Wyatt Feng's patch, it's no longer necessary to check to see if the Tx buffer exists as this the state set now covers this. (3) Make the ACKALL handler use call->tx_transmitted rather than call->tx_top as the former is explicitly the highest packet seq number transmitted, whereas the latter has a looser definition. Thanks to Jeffrey Altman for a description of the history of the ACKALL packet[1]. Fixes: b341a0263b1b ("rxrpc: Implement progressive transmission queue struct") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Signed-off-by: Wyatt Feng Co-developed-by: David Howells Signed-off-by: David Howells cc: Ren Wei cc: Marc Dionne cc: linux-afs@lists.infradead.org Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260616155749.2125907-2-dhowells@redhat.com/ [1] Link: https://lore.kernel.org/r/c0fd4fec-1576-4070-b31e-a37d5506f5ed@auristor.com/ [2] Reviewed-by: Jeffrey Altman Link: https://patch.msgid.link/20260624163819.3017002-2-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/ar-internal.h | 2 ++ net/rxrpc/call_event.c | 5 ++++- net/rxrpc/call_object.c | 2 ++ net/rxrpc/conn_client.c | 2 +- net/rxrpc/input.c | 23 +++++++++++++++++++---- net/rxrpc/sendmsg.c | 3 ++- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ead3419f08b76..d2b31d15851b9 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -650,7 +650,9 @@ enum rxrpc_call_event { enum rxrpc_call_state { RXRPC_CALL_UNINITIALISED, RXRPC_CALL_CLIENT_AWAIT_CONN, /* - client waiting for connection to become available */ + RXRPC_CALL_CLIENT_PRE_SEND, /* - client is connected, but hasn't sent anything yet */ RXRPC_CALL_CLIENT_SEND_REQUEST, /* - client sending request phase */ + RXRPC_CALL_CLIENT_AWAIT_ACK, /* - client awaiting ACKs of request */ RXRPC_CALL_CLIENT_AWAIT_REPLY, /* - client awaiting reply */ RXRPC_CALL_CLIENT_RECV_REPLY, /* - client receiving reply phase */ RXRPC_CALL_SERVER_PREALLOC, /* - service preallocation */ diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index fec59d9338b9f..21be9c86d7a74 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -178,7 +178,7 @@ static void rxrpc_close_tx_phase(struct rxrpc_call *call) switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: - rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_REPLY); + rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_ACK); break; case RXRPC_CALL_SERVER_SEND_REPLY: rxrpc_set_call_state(call, RXRPC_CALL_SERVER_AWAIT_ACK); @@ -244,6 +244,8 @@ static void rxrpc_transmit_fresh_data(struct rxrpc_call *call, unsigned int limi break; } while (req.n < limit && before(seq, send_top)); + if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_PRE_SEND) + rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_SEND_REQUEST); if (txb->flags & RXRPC_LAST_PACKET) { rxrpc_close_tx_phase(call); tq = NULL; @@ -267,6 +269,7 @@ void rxrpc_transmit_some_data(struct rxrpc_call *call, unsigned int limit, fallthrough; case RXRPC_CALL_SERVER_SEND_REPLY: + case RXRPC_CALL_CLIENT_PRE_SEND: case RXRPC_CALL_CLIENT_SEND_REQUEST: if (!rxrpc_tx_window_space(call)) return; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index fcb9d38bb5214..817ed9acb91e6 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -18,7 +18,9 @@ const char *const rxrpc_call_states[NR__RXRPC_CALL_STATES] = { [RXRPC_CALL_UNINITIALISED] = "Uninit ", [RXRPC_CALL_CLIENT_AWAIT_CONN] = "ClWtConn", + [RXRPC_CALL_CLIENT_PRE_SEND] = "ClPreSnd", [RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq", + [RXRPC_CALL_CLIENT_AWAIT_ACK] = "ClAwtAck", [RXRPC_CALL_CLIENT_AWAIT_REPLY] = "ClAwtRpl", [RXRPC_CALL_CLIENT_RECV_REPLY] = "ClRcvRpl", [RXRPC_CALL_SERVER_PREALLOC] = "SvPrealc", diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 63bbcc567f596..ef8df142c16da 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -449,7 +449,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, trace_rxrpc_connect_call(call); call->tx_last_sent = ktime_get_real(); rxrpc_start_call_timer(call); - rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_SEND_REQUEST); + rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_PRE_SEND); wake_up(&call->waitq); } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 614c2c9cd387b..5fee309d0e479 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -181,7 +181,8 @@ void rxrpc_congestion_degrade(struct rxrpc_call *call) if (call->cong_ca_state != RXRPC_CA_SLOW_START && call->cong_ca_state != RXRPC_CA_CONGEST_AVOIDANCE) return; - if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_REPLY) + if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_ACK || + __rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_REPLY) return; rtt = ns_to_ktime(call->srtt_us * (NSEC_PER_USEC / 8)); @@ -359,6 +360,7 @@ static void rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun, switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_CLIENT_AWAIT_ACK: case RXRPC_CALL_CLIENT_AWAIT_REPLY: if (reply_begun) { rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_RECV_REPLY); @@ -697,6 +699,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb) switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_CLIENT_AWAIT_ACK: case RXRPC_CALL_CLIENT_AWAIT_REPLY: /* Received data implicitly ACKs all of the request * packets we sent when we're acting as a client. @@ -1157,10 +1160,12 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) if (hard_ack + 1 == 0) return rxrpc_proto_abort(call, 0, rxrpc_eproto_ackr_zero); - /* Ignore ACKs unless we are or have just been transmitting. */ + /* Ignore ACKs unless we are transmitting or are waiting for + * acknowledgement of the packets we've just been transmitting. + */ switch (__rxrpc_call_state(call)) { case RXRPC_CALL_CLIENT_SEND_REQUEST: - case RXRPC_CALL_CLIENT_AWAIT_REPLY: + case RXRPC_CALL_CLIENT_AWAIT_ACK: case RXRPC_CALL_SERVER_SEND_REPLY: case RXRPC_CALL_SERVER_AWAIT_ACK: break; @@ -1218,7 +1223,17 @@ static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_ack_summary summary = { 0 }; - if (rxrpc_rotate_tx_window(call, call->tx_top, &summary)) + switch (__rxrpc_call_state(call)) { + case RXRPC_CALL_CLIENT_SEND_REQUEST: + case RXRPC_CALL_CLIENT_AWAIT_ACK: + case RXRPC_CALL_SERVER_SEND_REPLY: + case RXRPC_CALL_SERVER_AWAIT_ACK: + break; + default: + return; + } + + if (rxrpc_rotate_tx_window(call, call->tx_transmitted, &summary)) rxrpc_end_tx_phase(call, false, rxrpc_eproto_unexpected_ackall); } diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 39a6b21d6b8f7..65e2f5626947e 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -366,7 +366,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, if (state >= RXRPC_CALL_COMPLETE) goto maybe_error; ret = -EPROTO; - if (state != RXRPC_CALL_CLIENT_SEND_REQUEST && + if (state != RXRPC_CALL_CLIENT_PRE_SEND && + state != RXRPC_CALL_CLIENT_SEND_REQUEST && state != RXRPC_CALL_SERVER_ACK_REQUEST && state != RXRPC_CALL_SERVER_SEND_REPLY) { /* Request phase complete for this client call */ From f9be514984471ff0003738b2e1efed12bc3433ff Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:13 +0100 Subject: [PATCH 0674/3297] rxrpc: Fix the reception of a reply packet before data transmission commit a58e33405acd2584e730c1da72635f822ada6b49 upstream. Fix rxrpc_receiving_reply() to handle the reception of an apparent reply DATA packet before rxrpc has had a chance to send any request DATA packets on a client call by checking to see if the call has been exposed yet by sending the first packet. Without this, rxrpc_rotate_tx_window() might oops. Also fix rxrpc_rotate_tx_window() to handle the Tx queue being empty by changing the do...while loop into a while loop, just in case a call is abnormally terminated by an early reply before the last request packet is transmitted. Fixes: b341a0263b1b ("rxrpc: Implement progressive transmission queue struct") Link: https://sashiko.dev/#/patchset/20260616155749.2125907-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-7-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/input.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 5fee309d0e479..73cafe6bfa9f5 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -251,7 +251,7 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, tq = call->tx_queue; } - do { + while (before_eq(seq, to)) { unsigned int ix = seq - call->tx_qbase; _debug("tq=%x seq=%x i=%d f=%x", tq->qbase, seq, ix, tq->bufs[ix]->flags); @@ -321,8 +321,7 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to, break; } } - - } while (before_eq(seq, to)); + } if (trace) trace_rxrpc_rack_update(call, summary); @@ -397,6 +396,14 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call) trace_rxrpc_timer_can(call, rxrpc_timer_trace_delayed_ack); } + /* Deal with an apparent reply coming in before we've got the request + * queued or transmitted. + */ + if (!test_bit(RXRPC_CALL_EXPOSED, &call->flags)) { + rxrpc_proto_abort(call, top, rxrpc_eproto_early_reply); + return false; + } + if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags)) { if (!rxrpc_rotate_tx_window(call, top, &summary)) { rxrpc_proto_abort(call, top, rxrpc_eproto_early_reply); From a89a13aea38ae990f50287f490e494f8052828de Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:09 +0100 Subject: [PATCH 0675/3297] rxrpc: Fix leak of connection from OOB challenge commit 4b28876e78fd60979afa91fd2ec6ad9cc8b7a6d0 upstream. Fix leak of connection object from OOB challenge queue when response is provided by userspace. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-3-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/oob.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/rxrpc/oob.c b/net/rxrpc/oob.c index 8a7fccb62f1af..c80ee2487d094 100644 --- a/net/rxrpc/oob.c +++ b/net/rxrpc/oob.c @@ -213,6 +213,11 @@ static int rxrpc_respond_to_oob(struct rxrpc_sock *rx, break; } + switch (skb->mark) { + case RXRPC_OOB_CHALLENGE: + rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob); + break; + } rxrpc_free_skb(skb, rxrpc_skb_put_oob); return ret; } From 8cd8cf3052fff9a4b86b25734f610e4af03786d3 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:10 +0100 Subject: [PATCH 0676/3297] rxrpc: Fix double unlock in rxrpc_recvmsg() commit a2f299b4d5510147fa8629a6aba2869bbcc88aea upstream. Fix a double unlock in rxrpc_recvmsg() when dealing with OOB messages. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Marc Dionne cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-4-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/recvmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index fb49563dc9271..efcba4b2e74f0 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -470,7 +470,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, release_sock(&rx->sk); if (ret == -EAGAIN) goto try_again; - goto error_no_call; + goto error_trace; } /* Find the next call and dequeue it if we're not just peeking. If we From b83ecf80e28afb7b6595ba79932e77ca68a5a83d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Jun 2026 15:09:08 +0100 Subject: [PATCH 0677/3297] afs: Fix netns teardown to cancel the preallocation charger commit 47694fbc9d24ab6bf210f91e8efe06a10a478064 upstream. Fix the teardown of an afs network namespace to make sure it cancels the work item that keeps the preallocated rxrpc call/conn/peer queue charged before incoming calls are disabled (i.e. listen 0). Also, if net->live is false because the afs netns is being deleted, make afs_charge_preallocation() skip charging and make afs_rx_new_call() avoid requeuing the charger. (This was found by AI review). Fixes: 00e907127e6f ("rxrpc: Preallocate peers, conns and calls for incoming service requests") Reported-by: Simon Horman Signed-off-by: David Howells cc: Li Daming cc: Ren Wei cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260609140911.838677-5-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- fs/afs/rxrpc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index c1cadf8fb346a..a7af91efc0d93 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -127,6 +127,7 @@ void afs_close_socket(struct afs_net *net) { _enter(""); + cancel_work_sync(&net->charge_preallocation_work); kernel_listen(net->socket, 0); flush_workqueue(afs_async_calls); @@ -742,7 +743,7 @@ void afs_charge_preallocation(struct work_struct *work) container_of(work, struct afs_net, charge_preallocation_work); struct afs_call *call = net->spare_incoming_call; - for (;;) { + while (READ_ONCE(net->live)) { if (!call) { call = afs_alloc_call(net, &afs_RXCMxxxx, GFP_KERNEL); if (!call) @@ -792,7 +793,8 @@ static void afs_rx_new_call(struct sock *sk, struct rxrpc_call *rxcall, if (!call->server) trace_afs_cm_no_server(call, rxrpc_kernel_remote_srx(call->peer)); - queue_work(afs_wq, &net->charge_preallocation_work); + if (net->live) + queue_work(afs_wq, &net->charge_preallocation_work); } /* From 23b3d457d8387bcb2a61063a9e520063ada9335f Mon Sep 17 00:00:00 2001 From: Matvey Kovalev Date: Mon, 22 Jun 2026 10:08:37 +0100 Subject: [PATCH 0678/3297] afs: fix NULL pointer dereference in afs_get_tree() commit 0b70716081c6462be9b2928ad736d0d527b09678 upstream. afs_alloc_sbi() uses kzalloc for memory allocation. And, if ctx->dyn_root is not null, as->cell and as->volume are null. In trace_afs_get_tree() they are dereferenced. KASAN error message: KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 2 PID: 18478 Comm: syz-executor.7 Not tainted 5.10.246-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:perf_trace_afs_get_tree+0x1d9/0x550 include/trace/events/afs.h:1365 Call Trace: trace_afs_get_tree include/trace/events/afs.h:1365 [inline] afs_get_tree+0x922/0x1350 fs/afs/super.c:599 vfs_get_tree+0x8e/0x300 fs/super.c:1572 do_new_mount fs/namespace.c:3011 [inline] path_mount+0x14a5/0x2220 fs/namespace.c:3341 do_mount fs/namespace.c:3354 [inline] __do_sys_mount fs/namespace.c:3562 [inline] __se_sys_mount fs/namespace.c:3539 [inline] __x64_sys_mount+0x283/0x300 fs/namespace.c:3539 do_syscall_64+0x33/0x50 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1 Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 80548b03991f5 ("afs: Add more tracepoints") Cc: stable@vger.kernel.org Signed-off-by: Matvey Kovalev Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-4-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/super.c b/fs/afs/super.c index d672b7ab57ae2..6a74d5e7f70aa 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -587,7 +587,8 @@ static int afs_get_tree(struct fs_context *fc) } fc->root = dget(sb->s_root); - trace_afs_get_tree(as->cell, as->volume); + if (!ctx->dyn_root) + trace_afs_get_tree(as->cell, as->volume); _leave(" = 0 [%p]", sb); return 0; From cc848a080f7a6848dfeef441722419fdcbfe9b8d Mon Sep 17 00:00:00 2001 From: Nan Li Date: Mon, 22 Jun 2026 10:08:35 +0100 Subject: [PATCH 0679/3297] afs: handle CB.InitCallBackState3 requests without a server record commit f3cf725cd284b7912d5522babb44721bf38c8887 upstream. The cache manager callback path now attaches the server record to an incoming call through the rxrpc peer's app data. That association is not guaranteed to exist for every callback request, and most callback handlers already tolerate that case. Make CB.InitCallBackState3 follow the same pattern by checking whether a server record was attached before using it. If the peer is not mapped to a server record, trace the request and ignore it, matching the existing behaviour for other unmatched callback requests. This keeps the callback handler consistent with the rest of the cache manager service and avoids depending on peer state that may not be available for a given request. Fixes: 40e8b52fe8c8 ("afs: Use the per-peer app data provided by rxrpc") 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: David Howells Link: https://patch.msgid.link/20260622090856.2746629-2-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/cmservice.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 1a906805a9e34..8db2b4bbb2c0a 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -365,6 +365,11 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING)) return afs_io_error(call, afs_io_error_cm_reply); + if (!call->server) { + trace_afs_cm_no_server_u(call, call->request); + return 0; + } + if (memcmp(call->request, &call->server->_uuid, sizeof(call->server->_uuid)) != 0) { pr_notice("Callback UUID does not match fileserver UUID\n"); trace_afs_cm_no_server_u(call, call->request); From 8b4d1854295b023f4ce2665200927de28ac35d6a Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:11 +0100 Subject: [PATCH 0680/3297] afs: Fix further netns teardown to cancel the preallocation charger commit 2daf8ac812c3d78c642fe7652f62e29df5e3da20 upstream. When an afs network namespace is torn down, it cancels and waits for the work item that keeps the preallocated rxrpc call/conn/peer queue charged before disabling incoming (i.e. listen 0), but there's a small window in which it can be requeued by an incoming call wending through the I/O thread. Fix this by cancelling the charger work item again after reducing the listen backlog to zero. Fixes: 47694fbc9d24 ("afs: Fix netns teardown to cancel the preallocation charger") Reported-by: Jakub Kicinski Signed-off-by: David Howells Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com cc: Li Daming cc: Ren Wei cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-5-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- fs/afs/rxrpc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index a7af91efc0d93..2059a273cdf36 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -128,8 +128,13 @@ void afs_close_socket(struct afs_net *net) _enter(""); cancel_work_sync(&net->charge_preallocation_work); + /* Future work items should now see ->live is false. */ + kernel_listen(net->socket, 0); + + /* Make sure work items are no longer running. */ flush_workqueue(afs_async_calls); + cancel_work_sync(&net->charge_preallocation_work); if (net->spare_incoming_call) { afs_put_call(net->spare_incoming_call); From 231414253b648b3518b56f09710b831945d6a2fc Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 24 Jun 2026 17:38:12 +0100 Subject: [PATCH 0681/3297] afs: Fix uncancelled rxrpc OOB message handler commit a4057e58b07005d0fe0491bdbf1868c1491909ee upstream. Fix AFS to cancel its OOB message processing (typically to respond to security challenges). Also move OOB message processing to afs_wq so that it's also waited for and make the OOB handler just return if the net namespace is no longer live. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Link: https://sashiko.dev/#/patchset/20260609140911.838677-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Li Daming cc: Ren Wei cc: Marc Dionne cc: Jeffrey Altman cc: Simon Horman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Link: https://patch.msgid.link/20260624163819.3017002-6-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- fs/afs/cm_security.c | 3 ++- fs/afs/rxrpc.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/afs/cm_security.c b/fs/afs/cm_security.c index edcbd249d2024..103168c70dd4d 100644 --- a/fs/afs/cm_security.c +++ b/fs/afs/cm_security.c @@ -101,7 +101,8 @@ void afs_process_oob_queue(struct work_struct *work) struct sk_buff *oob; enum rxrpc_oob_type type; - while ((oob = rxrpc_kernel_dequeue_oob(net->socket, &type))) { + while (READ_ONCE(net->live) && + (oob = rxrpc_kernel_dequeue_oob(net->socket, &type))) { switch (type) { case RXRPC_OOB_CHALLENGE: afs_respond_to_challenge(oob); diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 2059a273cdf36..b3207e141343b 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -128,6 +128,7 @@ void afs_close_socket(struct afs_net *net) _enter(""); cancel_work_sync(&net->charge_preallocation_work); + cancel_work_sync(&net->rx_oob_work); /* Future work items should now see ->live is false. */ kernel_listen(net->socket, 0); @@ -148,6 +149,7 @@ void afs_close_socket(struct afs_net *net) kernel_sock_shutdown(net->socket, SHUT_RDWR); flush_workqueue(afs_async_calls); + cancel_work_sync(&net->rx_oob_work); net->socket->sk->sk_user_data = NULL; sock_release(net->socket); key_put(net->fs_cm_token_key); @@ -989,5 +991,6 @@ static void afs_rx_notify_oob(struct sock *sk, struct sk_buff *oob) { struct afs_net *net = sk->sk_user_data; - schedule_work(&net->rx_oob_work); + if (READ_ONCE(net->live)) + queue_work(afs_wq, &net->rx_oob_work); } From 6617df8c246311c82cebf061a4cee55b9df60922 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 24 Jun 2026 16:11:36 -0500 Subject: [PATCH 0682/3297] fbcon: fix NULL pointer dereference for a console without vc_data commit 5fae9a928482d4845bca169a3a098789203a1ca4 upstream. fbcon_new_modelist() runs when a framebuffer's modelist changes. For each console mapped to it with fb_display[i].mode set, it reads vc_cons[i].d and passes the vc_num to fbcon_set_disp(). This assumes a console with a mode set has a vc_data, but it can be NULL. fbcon_set_disp() sets fb_display[i].mode before it checks vc_data, and fbcon_deinit() leaves the mode set after the vc_data is freed. fbcon_new_modelist() then dereferences the NULL vc_data. Keep fb_display[i].mode set only while the console has a vc_data. Check vc_data before setting the mode in fbcon_set_disp(), and clear the mode in fbcon_deinit(). The existing mode check in fbcon_new_modelist() then skips such consoles. Reported-by: syzbot+42525d636f430fd5d983@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=42525d636f430fd5d983 Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ian Bridges Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbcon.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 926905ad46a30..da4857ec810fb 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1205,6 +1205,7 @@ static void fbcon_deinit(struct vc_data *vc) int idx; fbcon_free_font(p); + p->mode = NULL; idx = con2fb_map[vc->vc_num]; if (idx == -1) @@ -1375,14 +1376,14 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, p = &fb_display[unit]; - if (var_to_display(p, var, info)) - return; - vc = vc_cons[unit].d; if (!vc) return; + if (var_to_display(p, var, info)) + return; + default_mode = vc->vc_display_fg; svc = *default_mode; t = &fb_display[svc->vc_num]; From 24186d6f9b07eaf61a8ae1ed984bb353c9952d18 Mon Sep 17 00:00:00 2001 From: Jiacheng Yu Date: Thu, 14 May 2026 17:19:18 +0800 Subject: [PATCH 0683/3297] fbcon: Use correct type for vc_resize() return value commit 84202754fb1727dc3ee87f47104e4162ecc8ba3a upstream. The return value of vc_resize() is int, but fbcon_set_disp() stores it in an unsigned long variable. While the !ret check happens to work correctly by coincidence (negative values become large positive values), the types should match. Use int instead. Eliminates the following W=3 warning: drivers/video/fbdev/core/fbcon.c: In function 'fbcon_set_disp': drivers/video/fbdev/core/fbcon.c:1494:14: warning: implicit conversion from 'int' to 'unsigned long' [-Wconversion] Fixes: af0db3c1f898 ("fbdev: Fix vmalloc out-of-bounds write in fast_imageblit") Cc: stable@vger.kernel.org # v6.17+ Signed-off-by: Jiacheng Yu Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/core/fbcon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index da4857ec810fb..df1ecbf3f5d02 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1371,8 +1371,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, struct vc_data **default_mode, *vc; struct vc_data *svc; struct fbcon_par *par = info->fbcon_par; - int rows, cols; - unsigned long ret = 0; + int rows, cols, ret; p = &fb_display[unit]; From d50d320e88b4c558f0fca5f63e2dd32f647010f5 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Sat, 21 Mar 2026 09:22:05 +0000 Subject: [PATCH 0684/3297] openrisc: mm: Fix section mismatch between map_page and __set_fixmap [ Upstream commit 431400d49cac4bac944fc2d989921003314667ae ] This warning was reported by the kernel test robot: WARNING: modpost: vmlinux: section mismatch in reference: __set_fixmap+0x84 (section: .text.unlikely) -> map_page.isra.0 (section: .init.text) With commit 4735037b5d9b ("openrisc: Add text patching API support") the __set_fixmap function was moved out of the .init.text section. However, the map_page helper that it uses was not moved. This was not noticed on gcc 15.1.0 where map_page gets inlined unlike lkp@intel.com which uses gcc 10.5.0. Fix this by also moving the map_page helper function out of the init section. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603211503.E8mMETO3-lkp@intel.com/ Fixes: 4735037b5d9b ("openrisc: Add text patching API support") Signed-off-by: Stafford Horne Signed-off-by: Sasha Levin --- arch/openrisc/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index 9382d9a0ec788..c47ec1237a521 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -202,7 +202,7 @@ void __init mem_init(void) return; } -static int __init map_page(unsigned long va, phys_addr_t pa, pgprot_t prot) +static int map_page(unsigned long va, phys_addr_t pa, pgprot_t prot) { p4d_t *p4d; pud_t *pud; From 8e644d9a8c8dc96b91ddacb89e3edbcd42d1259e Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 5 Feb 2026 16:40:37 +0800 Subject: [PATCH 0685/3297] clocksource/drivers/sun5i: Handle error returns from devm_reset_control_get_optional_exclusive() [ Upstream commit fed9f727cc3f91dde8278961269419083502b40e ] The devm_reset_control_get_optional_exclusive() function may return an ERR_PTR in case of genuine reset control acquisition errors, not just NULL which indicates the legitimate absence of an optional reset. Add an IS_ERR() check after the call in sun5i_timer_probe(). On error, return the error code to ensure proper failure handling rather than proceeding with invalid pointers. Fixes: 7e5bac610d2f ("clocksource/drivers/sun5i: Convert to platform device driver") Signed-off-by: Chen Ni Signed-off-by: Daniel Lezcano Acked-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260205084037.3661261-1-nichen@iscas.ac.cn Signed-off-by: Sasha Levin --- drivers/clocksource/timer-sun5i.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index f827d3f98f60e..d7e012992170b 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -286,6 +286,9 @@ static int sun5i_timer_probe(struct platform_device *pdev) } rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "failed to get reset\n"); if (rstc) reset_control_deassert(rstc); From 68dc52f308a17c3103a349f722dd5dea32c50a88 Mon Sep 17 00:00:00 2001 From: Max Zhen Date: Wed, 25 Mar 2026 18:06:42 -0700 Subject: [PATCH 0686/3297] accel/amdxdna: Fix leak when pinning ubuf pages [ Upstream commit d946347edc4f0a7b846325323d77e936a4c90d0f ] When pin_user_pages_fast() returns fewer pages than requested, the pages that were successfully pinned are not released, leading to a leak. Fix this by unpinning any partially pinned pages before returning failure. Fixes: bd72d4acda10 ("accel/amdxdna: Support user space allocated buffer") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260326010642.2596525-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_ubuf.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c index 62a478f6b45fb..9c2e715b769d0 100644 --- a/drivers/accel/amdxdna/amdxdna_ubuf.c +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c @@ -205,13 +205,17 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, ret = pin_user_pages_fast(va_ent[i].vaddr, npages, FOLL_WRITE | FOLL_LONGTERM, &ubuf->pages[start]); - if (ret < 0 || ret != npages) { - ret = -ENOMEM; + if (ret >= 0) { + start += ret; + if (ret != npages) { + XDNA_ERR(xdna, "Partially pinned pages %d/%u", ret, npages); + ret = -ENOMEM; + goto destroy_pages; + } + } else { XDNA_ERR(xdna, "Failed to pin pages ret %d", ret); goto destroy_pages; } - - start += ret; } exp_info.ops = &amdxdna_ubuf_dmabuf_ops; From 0d60b835bca42f0f790689dd47819ed881a92ccc Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 10 Mar 2026 00:44:30 +0200 Subject: [PATCH 0687/3297] drm/rockchip: dw_dp: Switch to drmm_kzalloc() [ Upstream commit ed9da8d23020352ad24c528db09b5acdd78b81fd ] Driver makes use of drmm_encoder_init() to initialize the encoder and automatically handle the cleanup by registering drm_encoder_cleanup() with drmm_add_action(). However, the internal structure containing the encoder part gets allocated with devm_kzalloc(), which happens while component_bind_all() is being called from Rockchip DRM driver. The component framework further ensures it is deallocated as part of releasing all the resources claimed during bind, which is triggered from component_unbind_all(). When the reference to the DRM device gets eventually dropped via drm_dev_put() in rockchip_drm_unbind(), drmm_encoder_alloc_release() attempts to access the now released encoder structure, leading to use-after-free. Ensure driver's internal structure is still reachable on encoder cleanup by switching from a device-managed allocation to a drm-managed one. Fixes: d68ba7bac955 ("drm/rockchip: Add RK3588 DPTX output support") Signed-off-by: Cristian Ciocaltea Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260310-drm-rk-fixes-v2-2-645ecfb43f49@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c index 25ab4e46301e8..98d97e0f3cf4f 100644 --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -82,7 +83,7 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void * struct drm_connector *connector; int ret; - dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); + dp = drmm_kzalloc(drm_dev, sizeof(*dp), GFP_KERNEL); if (!dp) return -ENOMEM; From b3ec263a719e0d499bd95d20012d90d7e8641655 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 10 Mar 2026 00:44:31 +0200 Subject: [PATCH 0688/3297] drm/rockchip: dw_dp: Fix null-ptr-deref in dw_dp_remove() [ Upstream commit 9456381d8b60bb7dd42f2f04afe5ee4ce6e0bc12 ] Attempting to access driver data in the platform driver ->remove() callback may lead to a null pointer dereference since there is no guaranty that the component ->bind() callback invoking platform_set_drvdata() was executed. A common scenario is when Rockchip DRM driver didn't manage to run component_bind_all() because of an (unrelated) error causing early return from rockchip_drm_bind(). Drop the unnecessary call to platform_get_drvdata() and, instead, reference the target device structure via platform_device. Fixes: d68ba7bac955 ("drm/rockchip: Add RK3588 DPTX output support") Signed-off-by: Cristian Ciocaltea Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260310-drm-rk-fixes-v2-3-645ecfb43f49@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c index 98d97e0f3cf4f..6d57e1c746273 100644 --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c @@ -130,9 +130,7 @@ static int dw_dp_probe(struct platform_device *pdev) static void dw_dp_remove(struct platform_device *pdev) { - struct rockchip_dw_dp *dp = platform_get_drvdata(pdev); - - component_del(dp->dev, &dw_dp_rockchip_component_ops); + component_del(&pdev->dev, &dw_dp_rockchip_component_ops); } static const struct of_device_id dw_dp_of_match[] = { From ab487bb0402afa9397db27662925caa5f4c326a2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 27 Feb 2026 14:31:10 +0100 Subject: [PATCH 0689/3297] drm/rockchip: Test for imported buffers with drm_gem_is_imported() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9fc0da81916250f343599a4dd259f097196bf0fb ] Instead of testing import_attach for imported GEM buffers, invoke drm_gem_is_imported() to do the test. The test itself does not change. Signed-off-by: Thomas Zimmermann Cc: Sandy Huang Cc: Heiko Stübner Cc: Andy Yan Cc: linux-rockchip@lists.infradead.org Fixes: b57aa47d39e9 ("drm/gem: Test for imported GEM buffers with helper") Closes: https://lore.kernel.org/dri-devel/38d09d34.4354.196379aa560.Coremail.andyshrk@163.com/ Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260227133113.235940-11-tzimmermann@suse.de Signed-off-by: Sasha Levin --- drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 6330b883efc35..e44396d46dc1f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -332,7 +332,7 @@ void rockchip_gem_free_object(struct drm_gem_object *obj) struct rockchip_drm_private *private = drm->dev_private; struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); - if (obj->import_attach) { + if (drm_gem_is_imported(obj)) { if (private->domain) { rockchip_gem_iommu_unmap(rk_obj); } else { From 626fa93d194db63bd1bcd4a8c1573b0848a3e997 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 11 Mar 2026 11:14:43 +0200 Subject: [PATCH 0690/3297] drm/tidss: Drop extra drm_mode_config_reset() call [ Upstream commit f468fef38716f667e805e0fa2c497c6b9c325bb9 ] We are calling drm_mode_config_reset() twice at probe time. There's no reason for this and the second call can be removed, reducing work at probe time slightly. Fixes: 32a1795f57ee ("drm/tidss: New driver for TI Keystone platform Display SubSystem") Acked-by: Maxime Ripard Link: https://patch.msgid.link/20260311-tidss-minor-fixes-v2-1-cb4479784458@ideasonboard.com Signed-off-by: Tomi Valkeinen Signed-off-by: Sasha Levin --- drivers/gpu/drm/tidss/tidss_kms.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index 9f5f98e707f2c..838111e11186a 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -291,8 +291,6 @@ int tidss_modeset_init(struct tidss_device *tidss) if (ret) return ret; - drm_mode_config_reset(ddev); - dev_dbg(tidss->dev, "%s done\n", __func__); return 0; From 353f26c74660bcbd8d82cd76622748e14a5a5db3 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 25 Mar 2026 16:16:08 -0700 Subject: [PATCH 0691/3297] drm/gpusvm: Reject VMAs with VM_IO or VM_PFNMAP when creating SVM ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b82a225e57a334335a21462b75ee2223bc6efe6d ] VMAs marked with VM_IO or VM_PFNMAP are not backed by struct page objects, which GPUSVM requires in order to operate correctly. In particular, get_pages() relies on hmm_range_fault() to resolve struct pages for the target range. Attempting to create an SVM range on such VMAs results in repeated get_pages() failures and can lead to an infinite loop inside a driver’s page‑fault handler. Prevent this by rejecting ranges on VM_IO or VM_PFNMAP VMAs and returning -EIO. Fixes: 99624bdff867 ("drm/gpusvm: Add support for GPU Shared Virtual Memory") Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260325231608.25581-1-matthew.brost@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gpusvm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 853ca57800d3a..4cde5bf4aedd8 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -944,6 +944,11 @@ drm_gpusvm_range_find_or_insert(struct drm_gpusvm *gpusvm, goto err_notifier_remove; } + if (vas->vm_flags & (VM_IO | VM_PFNMAP)) { + err = -EIO; + goto err_notifier_remove; + } + range = drm_gpusvm_range_find(notifier, fault_addr, fault_addr + 1); if (range) goto out_mmunlock; From 41a60487b5b04e036024c2698f001f45ff250185 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Fri, 30 Jan 2026 19:19:54 +0000 Subject: [PATCH 0692/3297] drm/gpuvm: Do not prepare NULL objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 88f059f6f6f589bd78e2ceb05a1339a8c10b8626 ] Statis analysis issue: drm_gpuvm_prepare_range issues an exec_object_prepare call to all drm_gem_objects mapped between addr and addr + range. However, it is possible (albeit very unlikely) that the objects found through drm_gpuvm_for_each_va_range (as connected to va->gem) are NULL, as seen in other functions such as drm_gpuva_link and drm_gpuva_unlink_defer. Do not prepare NULL objects. Fixes: 50c1a36f594b ("drm/gpuvm: track/lock/validate external/evicted objects") Signed-off-by: Jonathan Cavitt Cc: Matthew Brost Cc: Thomas Hellström Reviewed-by: Krzysztof Karas Link: https://patch.msgid.link/20260130191953.61718-2-jonathan.cavitt@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gpuvm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index af63f4d003155..23e43406a1a8b 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -1289,6 +1289,9 @@ drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, struct drm_exec *exec, drm_gpuvm_for_each_va_range(va, gpuvm, addr, end) { struct drm_gem_object *obj = va->gem.obj; + if (unlikely(!obj)) + continue; + ret = exec_prepare_obj(exec, obj, num_fences); if (ret) return ret; From daf5d03ddb8ccbe4a9bf22b320de049d2f17a3d1 Mon Sep 17 00:00:00 2001 From: Werner Kasselman Date: Tue, 14 Apr 2026 05:08:42 +0000 Subject: [PATCH 0693/3297] drm/amdgpu: fix integer overflow in amdgpu_gem_align_pitch() [ Upstream commit fc3659f178d4a65599167d5a648bbeef4b0d4446 ] amdgpu_gem_align_pitch() is passed u32 width and cpp from dumb buffer creation but uses signed int internally. The round-up add and the aligned * cpp multiplication can overflow, returning zero or a negative pitch. A zero pitch propagates to a zero-sized GEM object allocation that reaches userspace via DRM_IOCTL_MODE_CREATE_DUMB. Switch the helper to unsigned int and use check_add_overflow() / check_mul_overflow() so wraparound returns zero. Reject a zero pitch or size in amdgpu_mode_dumb_create() rather than allocating a zero- byte BO. Fixes: 8e911ab770f7 ("drm: amdgpu: Replace drm_fb_get_bpp_depth() with drm_format_plane_cpp()") Signed-off-by: Werner Kasselman Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 1bc1233487a0b..cd52ddd27c58b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -27,6 +27,7 @@ */ #include #include +#include #include #include #include @@ -1209,13 +1210,14 @@ int amdgpu_gem_list_handles_ioctl(struct drm_device *dev, void *data, return ret; } -static int amdgpu_gem_align_pitch(struct amdgpu_device *adev, - int width, - int cpp, - bool tiled) +static unsigned int amdgpu_gem_align_pitch(struct amdgpu_device *adev, + unsigned int width, + unsigned int cpp, + bool tiled) { - int aligned = width; - int pitch_mask = 0; + unsigned int aligned = width; + unsigned int pitch_mask = 0; + unsigned int pitch; switch (cpp) { case 1: @@ -1230,9 +1232,12 @@ static int amdgpu_gem_align_pitch(struct amdgpu_device *adev, break; } - aligned += pitch_mask; + if (check_add_overflow(aligned, pitch_mask, &aligned)) + return 0; aligned &= ~pitch_mask; - return aligned * cpp; + if (check_mul_overflow(aligned, cpp, &pitch)) + return 0; + return pitch; } int amdgpu_mode_dumb_create(struct drm_file *file_priv, @@ -1259,8 +1264,12 @@ int amdgpu_mode_dumb_create(struct drm_file *file_priv, args->pitch = amdgpu_gem_align_pitch(adev, args->width, DIV_ROUND_UP(args->bpp, 8), 0); + if (!args->pitch) + return -EINVAL; args->size = (u64)args->pitch * args->height; args->size = ALIGN(args->size, PAGE_SIZE); + if (!args->size) + return -EINVAL; domain = amdgpu_bo_get_preferred_domain(adev, amdgpu_display_supported_domains(adev, flags)); r = amdgpu_gem_object_create(adev, args->size, 0, domain, flags, From d9dfa176899d488e48bb7342d2c43ddd36e66318 Mon Sep 17 00:00:00 2001 From: Werner Kasselman Date: Wed, 15 Apr 2026 22:13:52 +0000 Subject: [PATCH 0694/3297] drm/radeon: fix integer overflow in radeon_align_pitch() [ Upstream commit ce3b24eb3ee8f82de851535f516bf21f83e82259 ] radeon_align_pitch() has the same kind of overflow issue as the old amdgpu helper: both the alignment round-up add and the final 'aligned * cpp' calculation can overflow signed int. If that wraps, radeon_mode_dumb_create() can end up returning an invalid pitch or creating a zero-sized dumb buffer. Fix this by using check_add_overflow() for the alignment round-up and check_mul_overflow() for the final pitch calculation, returning 0 on overflow. Also reject zero pitch and size in radeon_mode_dumb_create(). Found via AST-based call-graph analysis using sqry. Fixes: ff72145badb8 ("drm: dumb scanout create/mmap for intel/radeon (v3)") Signed-off-by: Werner Kasselman Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/radeon/radeon_gem.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index f86773f3db200..1e84342ac0513 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -812,6 +813,7 @@ int radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tile int aligned = width; int align_large = (ASIC_IS_AVIVO(rdev)) || tiled; int pitch_mask = 0; + int pitch; switch (cpp) { case 1: @@ -826,9 +828,12 @@ int radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tile break; } - aligned += pitch_mask; + if (check_add_overflow(aligned, pitch_mask, &aligned)) + return 0; aligned &= ~pitch_mask; - return aligned * cpp; + if (check_mul_overflow(aligned, cpp, &pitch)) + return 0; + return pitch; } int radeon_mode_dumb_create(struct drm_file *file_priv, @@ -842,8 +847,12 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, args->pitch = radeon_align_pitch(rdev, args->width, DIV_ROUND_UP(args->bpp, 8), 0); + if (!args->pitch) + return -EINVAL; args->size = (u64)args->pitch * args->height; args->size = ALIGN(args->size, PAGE_SIZE); + if (!args->size) + return -EINVAL; r = radeon_gem_object_create(rdev, args->size, 0, RADEON_GEM_DOMAIN_VRAM, 0, From 06dc892561f5a08b2493c34c8ec2cb94dea33159 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 16 Apr 2026 15:55:37 -0400 Subject: [PATCH 0695/3297] drm/radeon: fix memory leak in radeon_ring_restore() on lock failure [ Upstream commit 82f1d6042611d45b8b9de423bbcb4e0ced9ec62b ] radeon_ring_restore() takes ownership of the data buffer allocated by radeon_ring_backup(). The caller (radeon_gpu_reset()) only frees it in the non-restore branch; in the restore branch it relies on radeon_ring_restore() to free it. If radeon_ring_lock() fails, the function returned early without calling kvfree(data), leaking the ring backup buffer on every GPU reset that fails at the lock stage. During repeated GPU resets this causes cumulative kernel memory exhaustion. Free data before returning the error. Fixes: 55d7c22192be ("drm/radeon: implement ring saving on reset v4") Signed-off-by: Yuho Choi Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/radeon/radeon_ring.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 581ae20c46e4b..a5dff072c1ac0 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -356,8 +356,10 @@ int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring, /* restore the saved ring content */ r = radeon_ring_lock(rdev, ring, size); - if (r) + if (r) { + kvfree(data); return r; + } for (i = 0; i < size; ++i) { radeon_ring_write(ring, data[i]); From 392c03362c2782302c6e36f7b0a06745fb3640ca Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Sat, 18 Apr 2026 23:09:44 -0400 Subject: [PATCH 0696/3297] libbpf: Report error when a negative kprobe offset is specified [ Upstream commit ad35d8018669fd2eea76e3f74eb050fd3d2fb690 ] In attach_kprobe(), the parsing logic uses sscanf() to extract the target function name and offset from the section definition. Currently, if a user specifies a negative offset (e.g., SEC("kprobe/func+-100")), the input is not explicitly caught and reported as an error. This commit updates the logic to explicitly notify the user when a negative integer is provided. To facilitate this check, the offset variable is changed from unsigned long to long so that sscanf() can accurately capture a negative input for evaluation. If a negative offset is detected, the loader will now print an informative warning stating that the offset must be non-negative, and return -EINVAL. Additionally, free(func) is called in this new error path to prevent a memory leak, as the function name string is dynamically allocated by sscanf(). Fixes: e3f9bc35ea7e9 ("libbpf: Allow decimal offset for kprobes") Signed-off-by: Aaron Tomlin Acked-by: Mykyta Yatsenko Link: https://lore.kernel.org/bpf/20260419030944.1423642-1-atomlin@atomlin.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- tools/lib/bpf/libbpf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 84b6fb47a2f79..3baa6025ecba0 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -11826,7 +11826,7 @@ bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog, static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link) { DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts); - unsigned long offset = 0; + long offset = 0; const char *func_name; char *func; int n; @@ -11848,6 +11848,13 @@ static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf pr_warn("kprobe name is invalid: %s\n", func_name); return -EINVAL; } + + if (offset < 0) { + free(func); + pr_warn("kprobe offset must be a non-negative integer: %li\n", offset); + return -EINVAL; + } + if (opts.retprobe && offset != 0) { free(func); pr_warn("kretprobes do not support offset specification\n"); From bc4737e55ec4157380bf010f772719e82deba094 Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Mon, 20 Apr 2026 09:37:34 +0000 Subject: [PATCH 0697/3297] selftests/bpf: Fix off-by-one in bpf_cpumask_populate related selftest [ Upstream commit 0aa6378695b8c67146130812f635f07c4898f171 ] The test_populate test uses >= instead of > when checking if the runtime nr_cpus exceeds the bit capacity of a cpumask_t. On a system where the physical CPU core count perfectly matches the CONFIG_NR_CPUS upper bound (e.g. nr_cpus = 512 and CONFIG_NR_CPUS = 512), the condition nr_cpus >= CPUMASK_TEST_MASKLEN * 8 evaluates to true (512 >= 512). This incorrectly causes the test to fail with an error value of 3. A 512-bit cpumask_t provides enough bits (indices 0 through 511) to represent 512 CPUs. The subsequent bpf_for(i, 0, nr_cpus) loop iterates up to nr_cpus - 1 (511), which perfectly aligns with the maximum valid index of the bitmask. Change the condition to nr_cpus > CPUMASK_TEST_MASKLEN * 8 to fix the false positive failure on these systems. Fixes: 918ba2636d4e ("selftests: bpf: add bpf_cpumask_populate selftests") Signed-off-by: Matt Bobrowski Acked-by: Paul Chaignon Link: https://lore.kernel.org/bpf/20260420093734.2400330-1-mattbobrowski@google.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/cpumask_success.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/cpumask_success.c b/tools/testing/selftests/bpf/progs/cpumask_success.c index 0e04c31b91c0c..774706e7b058b 100644 --- a/tools/testing/selftests/bpf/progs/cpumask_success.c +++ b/tools/testing/selftests/bpf/progs/cpumask_success.c @@ -866,7 +866,7 @@ int BPF_PROG(test_populate, struct task_struct *task, u64 clone_flags) * access NR_CPUS, the upper bound for nr_cpus, so we infer * it from the size of cpumask_t. */ - if (nr_cpus < 0 || nr_cpus >= CPUMASK_TEST_MASKLEN * 8) { + if (nr_cpus < 0 || nr_cpus > CPUMASK_TEST_MASKLEN * 8) { err = 3; goto out; } From afea00ffcf41ce678ee00fcd6dde9835bd6ce2ba Mon Sep 17 00:00:00 2001 From: Nick Hu Date: Sun, 19 Apr 2026 23:18:55 -0700 Subject: [PATCH 0698/3297] dt-bindings: timer: Remove sifive,fine-ctr-bits property [ Upstream commit 1ccca10755107bc8c649937d1ba69651d1ef9da2 ] The counter width can be inferred from the compatible string, making the explicit "sifive,fine-ctr-bits" property redundant. Remove the property to simplify the bindings. Fixes: 0f920690a82c ("dt-bindings: timer: Add SiFive CLINT2") Suggested-by: Conor Dooley Signed-off-by: Nick Hu Signed-off-by: Daniel Lezcano Acked-by: Conor Dooley Link: https://lore.kernel.org/linux-riscv/20260330-relative-hardened-5ce35fe1ef57@spud/ Link: https://patch.msgid.link/20260419-clintv2-remove-fine-ctr-v1-1-7527f4d45850@sifive.com Signed-off-by: Sasha Levin --- .../devicetree/bindings/timer/sifive,clint.yaml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml index d85a1a088b35d..0c809819b69d7 100644 --- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml +++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml @@ -69,22 +69,6 @@ properties: minItems: 1 maxItems: 4095 - sifive,fine-ctr-bits: - maximum: 15 - description: The width in bits of the fine counter. - -if: - properties: - compatible: - contains: - const: sifive,clint2 -then: - required: - - sifive,fine-ctr-bits -else: - properties: - sifive,fine-ctr-bits: false - additionalProperties: false required: From 2dfe817167af0d75bcd647fe2404a9a0d26ef6c1 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Tue, 17 Mar 2026 20:58:07 -0400 Subject: [PATCH 0699/3297] drm/amd/pm: remove trailing semicolon from AMDGPU_PM_POLICY_ATTR macro [ Upstream commit 0d892afb52d2b940d891b6b52dc9f04debef2d81 ] macros should not include a trailing semicolon as per kernel coding style (checkpatch.pl warning). move the semicolon from the macro definition to the invocation sites instead. checkpatch.pl logs: WARNING: macros should not use a trailing semicolon +#define AMDGPU_PM_POLICY_ATTR(_name, _id) \ + static struct amdgpu_pm_policy_attr pm_policy_attr_##_name = { \ + .dev_attr = __ATTR(_name, 0644, amdgpu_get_pm_policy_attr, \ + amdgpu_set_pm_policy_attr), \ + .id = PP_PM_POLICY_##_id, \ + }; Fixes: 4d154b1ca580 ("drm/amd/pm: Add support for DPM policies") Signed-off-by: Yang Wang Acked-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index a7e6d7854b7b2..0d72118a12b4c 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -2463,12 +2463,12 @@ static ssize_t amdgpu_set_pm_policy_attr(struct device *dev, .dev_attr = __ATTR(_name, 0644, amdgpu_get_pm_policy_attr, \ amdgpu_set_pm_policy_attr), \ .id = PP_PM_POLICY_##_id, \ - }; + } #define AMDGPU_PM_POLICY_ATTR_VAR(_name) pm_policy_attr_##_name.dev_attr.attr -AMDGPU_PM_POLICY_ATTR(soc_pstate, SOC_PSTATE) -AMDGPU_PM_POLICY_ATTR(xgmi_plpd, XGMI_PLPD) +AMDGPU_PM_POLICY_ATTR(soc_pstate, SOC_PSTATE); +AMDGPU_PM_POLICY_ATTR(xgmi_plpd, XGMI_PLPD); static struct attribute *pm_policy_attrs[] = { &AMDGPU_PM_POLICY_ATTR_VAR(soc_pstate), From 595710e6838c3fc746660bffde9a4bc52a864f40 Mon Sep 17 00:00:00 2001 From: Gregory Bell Date: Fri, 17 Apr 2026 11:41:21 -0400 Subject: [PATCH 0700/3297] selftests/bpf: Use local type for flow_offload_tuple_rhash in xdp_flowtable [ Upstream commit ac985e7bf840e34a8dafe0808cc571fd85896c30 ] Define flow_offload_tuple_rhash___local and use it in place of the forward-declared kernel type for the bpf_xdp_flow_lookup kfunc return type and tuplehash variable. This is consistent with how bpf_flowtable_opts___local is already handled in the same file and avoids relying on a forward declaration of the struct. Fixes: eeb23b54e447 ("selftests/bpf: fix compilation failure when CONFIG_NF_FLOW_TABLE=m") Signed-off-by: Gregory Bell Link: https://lore.kernel.org/r/20260417154122.2558890-2-grbell@redhat.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/xdp_flowtable.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/xdp_flowtable.c b/tools/testing/selftests/bpf/progs/xdp_flowtable.c index 7fdc7b23ee749..e67daa02749d5 100644 --- a/tools/testing/selftests/bpf/progs/xdp_flowtable.c +++ b/tools/testing/selftests/bpf/progs/xdp_flowtable.c @@ -15,7 +15,10 @@ struct bpf_flowtable_opts___local { s32 error; }; -struct flow_offload_tuple_rhash * +struct flow_offload_tuple_rhash___local { +}; + +struct flow_offload_tuple_rhash___local * bpf_xdp_flow_lookup(struct xdp_md *, struct bpf_fib_lookup *, struct bpf_flowtable_opts___local *, u32) __ksym; @@ -67,7 +70,7 @@ int xdp_flowtable_do_lookup(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; struct bpf_flowtable_opts___local opts = {}; - struct flow_offload_tuple_rhash *tuplehash; + struct flow_offload_tuple_rhash___local *tuplehash; struct bpf_fib_lookup tuple = { .ifindex = ctx->ingress_ifindex, }; From d10227f899c902941f1fb536991c9052b0ead238 Mon Sep 17 00:00:00 2001 From: Gregory Bell Date: Fri, 17 Apr 2026 11:41:22 -0400 Subject: [PATCH 0701/3297] selftests/bpf: Use local type for bpf_fou_encap in test_tunnel_kern [ Upstream commit afb0450be061907a0f5d36bd8b010ca30eda3d3b ] Replace the forward-declared struct bpf_fou_encap with the existing bpf_fou_encap___local type in the bpf_skb_set_fou_encap and bpf_skb_get_fou_encap declarations. This removes the need for the forward declaration and the explicit casts at each call. Fixes: d17f9b370df6 ("selftests/bpf: Fix compilation failure when CONFIG_NET_FOU!=y") Signed-off-by: Gregory Bell Link: https://lore.kernel.org/r/20260417154122.2558890-3-grbell@redhat.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- .../testing/selftests/bpf/progs/test_tunnel_kern.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/test_tunnel_kern.c b/tools/testing/selftests/bpf/progs/test_tunnel_kern.c index 32127f1cd6872..30f1de458669d 100644 --- a/tools/testing/selftests/bpf/progs/test_tunnel_kern.c +++ b/tools/testing/selftests/bpf/progs/test_tunnel_kern.c @@ -6,6 +6,7 @@ * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ +#define BPF_NO_KFUNC_PROTOTYPES #include "vmlinux.h" #include #include @@ -36,12 +37,10 @@ enum bpf_fou_encap_type___local { FOU_BPF_ENCAP_GUE___local, }; -struct bpf_fou_encap; - int bpf_skb_set_fou_encap(struct __sk_buff *skb_ctx, - struct bpf_fou_encap *encap, int type) __ksym; + struct bpf_fou_encap___local *encap, int type) __ksym; int bpf_skb_get_fou_encap(struct __sk_buff *skb_ctx, - struct bpf_fou_encap *encap) __ksym; + struct bpf_fou_encap___local *encap) __ksym; struct xfrm_state * bpf_xdp_get_xfrm_state(struct xdp_md *ctx, struct bpf_xfrm_state_opts *opts, u32 opts__sz) __ksym; @@ -781,7 +780,7 @@ int ipip_gue_set_tunnel(struct __sk_buff *skb) encap.sport = 0; encap.dport = bpf_htons(5555); - ret = bpf_skb_set_fou_encap(skb, (struct bpf_fou_encap *)&encap, + ret = bpf_skb_set_fou_encap(skb, &encap, bpf_core_enum_value(enum bpf_fou_encap_type___local, FOU_BPF_ENCAP_GUE___local)); if (ret < 0) { @@ -820,7 +819,7 @@ int ipip_fou_set_tunnel(struct __sk_buff *skb) encap.sport = 0; encap.dport = bpf_htons(5555); - ret = bpf_skb_set_fou_encap(skb, (struct bpf_fou_encap *)&encap, + ret = bpf_skb_set_fou_encap(skb, &encap, FOU_BPF_ENCAP_FOU___local); if (ret < 0) { log_err(ret); @@ -843,7 +842,7 @@ int ipip_encap_get_tunnel(struct __sk_buff *skb) return TC_ACT_SHOT; } - ret = bpf_skb_get_fou_encap(skb, (struct bpf_fou_encap *)&encap); + ret = bpf_skb_get_fou_encap(skb, &encap); if (ret < 0) { log_err(ret); return TC_ACT_SHOT; From 6005cdd9f48fdaba4131ce5e82e67531f3cfbe4e Mon Sep 17 00:00:00 2001 From: Baolin Liu Date: Fri, 24 Apr 2026 17:06:54 +0800 Subject: [PATCH 0702/3297] Documentation: proc: fix section numbering in table of contents [ Upstream commit 97a7bd8c2c58a6d820df563d1d0f68aafec45477 ] Commit e24ccaaf7ec4 ("block: remove last remaining traces of IDE documentation") removed the IDE section but left its table of contents entry behind. Fix the stale entry and renumber the following sections. Fixes: e24ccaaf7ec4 ("block: remove last remaining traces of IDE documentation") Signed-off-by: Baolin Liu Acked-by: Randy Dunlap Signed-off-by: Jonathan Corbet Message-ID: <20260424090654.19229-1-liubaolin12138@163.com> Signed-off-by: Sasha Levin --- Documentation/filesystems/proc.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index 8256e857e2d74..cf7eee2d9b61d 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -23,13 +23,13 @@ fixes/update part 1.1 Stefani Seibold June 9 2009 1 Collecting System Information 1.1 Process-Specific Subdirectories 1.2 Kernel data - 1.3 IDE devices in /proc/ide - 1.4 Networking info in /proc/net - 1.5 SCSI info - 1.6 Parallel port info in /proc/parport - 1.7 TTY info in /proc/tty - 1.8 Miscellaneous kernel statistics in /proc/stat - 1.9 Ext4 file system parameters + 1.3 Networking info in /proc/net + 1.4 SCSI info + 1.5 Parallel port info in /proc/parport + 1.6 TTY info in /proc/tty + 1.7 Miscellaneous kernel statistics in /proc/stat + 1.8 Ext4 file system parameters + 1.9 /proc/consoles - Shows registered system consoles 2 Modifying System Parameters From 58d0b4c55cdfd843696e56e484367ccd2ed71d47 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 21 Apr 2026 11:45:05 +0200 Subject: [PATCH 0703/3297] arm64: dts: rockchip: fix Ethernet PHY not found on PX30 Cobra [ Upstream commit 6598ed3586a4b1cc79423666e66b9861631a6c7e ] When not passing the PHY ID with an ethernet-phy-idX.Y compatible property, the MDIO bus will attempt to auto-detect the PHY by reading its registers and then probing the appropriate driver. For this to work, the PHY needs to be in a working state. Unfortunately, the net subsystem doesn't control the PHY reset GPIO when attempting to auto-detect the PHY. This means the PHY needs to be in a working state when entering the Linux kernel. This historically has been the case for this device, but only because the bootloader was taking care of initializing the Ethernet controller even when not using it. We're attempting to support the removal of the network stack in the bootloader, which means the Linux kernel will be entered with the PHY still in reset and now Ethernet doesn't work anymore. The devices in the field only ever had a TI DP83825, so let's simply bypass the auto-detection mechanism entirely by passing the appropriate PHY IDs via the compatible. Fixes: bb510ddc9d3e ("arm64: dts: rockchip: add px30-cobra base dtsi and board variants") Signed-off-by: Quentin Schulz Link: https://patch.msgid.link/20260421-px30-eth-phy-v2-1-68c375b120fd@cherry.de Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/px30-cobra.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi index b7e669d8ba4d1..add917af5de78 100644 --- a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi @@ -397,7 +397,7 @@ &mdio { dp83825: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id2000.a140"; reg = <0x0>; pinctrl-names = "default"; pinctrl-0 = <&phy_rst>; From 8f8233d544acacf1a370026efa2f836f5fc207bb Mon Sep 17 00:00:00 2001 From: Diederik de Haas Date: Wed, 1 Apr 2026 15:11:38 +0200 Subject: [PATCH 0704/3297] arm64: dts: rockchip: Fix gmac0 reset pin for NanoPi R5S [ Upstream commit c83c4a09d4c01c91d6c52d6d4d77a06892a3e83b ] According to the NanoPi R5S 2204 schematic on page 6, GPIO0_C4 is for GMAC0_INT/PMEB_GPIO0_C4, while GPIO0_C5 is for GMAC0_RSTn_GPIO0_C5. While the 'reset-gpios' property was set correctly, the corresponding pinctrl didn't match that. Next to fixing the pinctrl definition, also change the node name and phandle to match what is used in the schematic. Fixes: c6629b9a6738 ("arm64: dts: rockchip: Add FriendlyElec Nanopi R5S") Signed-off-by: Diederik de Haas Link: https://patch.msgid.link/20260401131551.734456-2-diederik@cknow-tech.com Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts index 718d1a2da8e56..90ce6f0e1dcff 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts +++ b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts @@ -98,7 +98,7 @@ rgmii_phy0: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <1>; - pinctrl-0 = <ð_phy0_reset_pin>; + pinctrl-0 = <&gmac0_rstn_gpio0_c5_pin>; pinctrl-names = "default"; }; }; @@ -132,8 +132,8 @@ &pinctrl { gmac0 { - eth_phy0_reset_pin: eth-phy0-reset-pin { - rockchip,pins = <0 RK_PC4 RK_FUNC_GPIO &pcfg_pull_up>; + gmac0_rstn_gpio0_c5_pin: gmac0-rstn-gpio0-c5-pin { + rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>; }; }; From d72ae2c63b683cb4783546b0968482888946577b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:30 +0200 Subject: [PATCH 0705/3297] arm64: dts: qcom: ipq5424: Fix USB simple_bus_reg warnings [ Upstream commit 864fde494aa1dd26c68254661f2ce973e9f03832 ] Correct the unit address of USB nodes in Qualcomm IPQ5424 SoC DTSI to fix W=1 DTC warnings: ipq5424.dtsi:642.22-693.5: Warning (simple_bus_reg): /soc@0/usb2@1e00000: simple-bus unit address format error, expected "1ef8800" ipq5424.dtsi:733.22-786.5: Warning (simple_bus_reg): /soc@0/usb3@8a00000: simple-bus unit address format error, expected "8af8800" Fixes: 113d52bdc820 ("arm64: dts: qcom: ipq5424: Add USB controller and phy nodes") Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-3-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/ipq5424.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/ipq5424.dtsi b/arch/arm64/boot/dts/qcom/ipq5424.dtsi index 227d5ce297515..d103368fff9ac 100644 --- a/arch/arm64/boot/dts/qcom/ipq5424.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq5424.dtsi @@ -601,7 +601,7 @@ status = "disabled"; }; - usb2: usb2@1e00000 { + usb2: usb2@1ef8800 { compatible = "qcom,ipq5424-dwc3", "qcom,dwc3"; reg = <0 0x01ef8800 0 0x400>; #address-cells = <2>; @@ -692,7 +692,7 @@ status = "disabled"; }; - usb3: usb3@8a00000 { + usb3: usb3@8af8800 { compatible = "qcom,ipq5424-dwc3", "qcom,dwc3"; reg = <0 0x08af8800 0 0x400>; From d8367ee271aba04cd3e85692fd23ec3896b8cfb4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:31 +0200 Subject: [PATCH 0706/3297] arm64: dts: qcom: sc8180x: Fix phy simple_bus_reg warning [ Upstream commit f319a5fc998e29699d325af0e461721b3768eeec ] Correct the unit address of phy node in Qualcomm SC8180x SoC DTSI to fix W=1 DTC warning: sc8180x.dtsi:2650.31-2695.5: Warning (simple_bus_reg): /soc@0/phy@88ee000: simple-bus unit address format error, expected "88ed000" Fixes: 35e3a9c1afce ("arm64: dts: qcom: sc8180x: switch USB+DP QMP PHYs to new bindings") Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-4-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc8180x.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8180x.dtsi b/arch/arm64/boot/dts/qcom/sc8180x.dtsi index 85c2afcb417de..e20a096aac7f9 100644 --- a/arch/arm64/boot/dts/qcom/sc8180x.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8180x.dtsi @@ -2632,7 +2632,7 @@ status = "disabled"; }; - usb_sec_qmpphy: phy@88ee000 { + usb_sec_qmpphy: phy@88ed000 { compatible = "qcom,sc8180x-qmp-usb3-dp-phy"; reg = <0 0x088ed000 0 0x3000>; From 123e1cd95ba540653745c5d70bb9b9d6eabcb5c7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Apr 2026 15:39:32 +0200 Subject: [PATCH 0707/3297] arm64: dts: qcom: sdm845-mezzanine: Fix camss ports unit_address_vs_reg warning [ Upstream commit f20a82aacd7f381084391a8d1f0f58defa91974c ] Add necessary properties for ports node in SDM845 DB845c Navigation mezzanine overlay to fix W=1 DTC warning: sdm845-db845c-navigation-mezzanine.dtso:19.10-24.5: Warning (unit_address_vs_reg): /fragment@0/__overlay__/ports/port@0: node has a unit name, but no reg or ranges property Fixes: 30df676a31b7 ("arm64: dts: qcom: sdm845-db845c-navigation-mezzanine: Convert mezzanine riser to dtso") Reviewed-by: Dmitry Baryshkov Reviewed-by: David Heidelberg Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260405-dts-qcom-w-1-fixes-v2-5-1f2c7b74a93f@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- .../boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso b/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso index dbe1911d8e470..678a17c805f74 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso @@ -16,7 +16,12 @@ status = "okay"; ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + csiphy0_ep: endpoint { data-lanes = <0 1 2 3>; remote-endpoint = <&ov8856_ep>; From 228db770fb0869fd0ddfa2ba7bf8387d6b608820 Mon Sep 17 00:00:00 2001 From: Louis Kotze Date: Tue, 14 Apr 2026 14:27:28 +0200 Subject: [PATCH 0708/3297] wifi: cfg80211: fix grammar in MLO group key error message [ Upstream commit 9dcc1af3bbf5441f71a1e51b8d81bdf38a249607 ] The error message emitted by nl80211_validate_key_link_id() when a group key install on an MLO wdev is missing the link ID reads "link ID must for MLO group key", which is missing the words "be set". This makes the error harder to grep and parse in userspace logs, and is reported verbatim by wpa_supplicant via its nl80211 extack relay, e.g.: wpa_supplicant: nl80211: kernel reports: link ID must for MLO group key The sibling error strings in the same helper already use grammatical phrasing ("link ID not allowed for pairwise key", "invalid link ID for MLO group key", "link ID not allowed for non-MLO group key"). Fix this one to match. No functional change. Fixes: e7a7b84e3317 ("wifi: cfg80211: Add link_id parameter to various key operations for MLO") Signed-off-by: Louis Kotze Link: https://patch.msgid.link/20260414122728.92234-1-loukot@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 54a4585eb3a2d..6e41a4269e1c5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4828,7 +4828,7 @@ static int nl80211_validate_key_link_id(struct genl_info *info, if (wdev->valid_links) { if (link_id == -1) { GENL_SET_ERR_MSG(info, - "link ID must for MLO group key"); + "link ID must be set for MLO group key"); return -EINVAL; } if (!(wdev->valid_links & BIT(link_id))) { From cc6ef5ee7d88a5125df80b5c83c7e2978f316ae8 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 1 Apr 2026 11:29:41 +0100 Subject: [PATCH 0709/3297] arm64: tegra: Fix Tegra234 MGBE PTP clock [ Upstream commit 8f0cc929a4bad534c5a860a53d88912cf16d9c9c ] The Tegra MGBE PTP clock is incorrectly named as 'ptp-ref' and not 'ptp_ref' and this causing the initialisation of the PTP clock to fail. The device-tree binding doc for the device and the Tegra MGBE driver have been updated to use the correct name and so update the device-tree for Tegra234 as well. Fixes: 610cdf3186bc ("arm64: tegra: Add MGBE nodes on Tegra234") Signed-off-by: Jon Hunter Reviewed-by: Krzysztof Kozlowski Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/nvidia/tegra234.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi index 5657045c53d90..5b245977d6a03 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi @@ -3602,7 +3602,7 @@ <&bpmp TEGRA234_CLK_MGBE0_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE0_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE0_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE0_MAC>, @@ -3644,7 +3644,7 @@ <&bpmp TEGRA234_CLK_MGBE1_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE1_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE1_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE1_MAC>, @@ -3686,7 +3686,7 @@ <&bpmp TEGRA234_CLK_MGBE2_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE2_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE2_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE2_MAC>, @@ -3728,7 +3728,7 @@ <&bpmp TEGRA234_CLK_MGBE3_RX_PCS_M>, <&bpmp TEGRA234_CLK_MGBE3_RX_PCS>, <&bpmp TEGRA234_CLK_MGBE3_TX_PCS>; - clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m", + clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m", "rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m", "rx-pcs", "tx-pcs"; resets = <&bpmp TEGRA234_RESET_MGBE3_MAC>, From 1638e178e49154f3c60139e33c819650889e4378 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Apr 2026 13:10:48 +0200 Subject: [PATCH 0710/3297] dt-bindings: pinctrl: nvidia,tegra234: Add missing required block [ Upstream commit 61b5deb5a968f7a82124f9b2591a6a151ed7273a ] Binding should require 'reg' property, because address space cannot be missing in the hardware and is already needed by the Linux drivers. Require also 'compatible' by convention, although it is not strictly necessary. Fixes: 857982138b79 ("dt-bindings: pinctrl: Document Tegra234 pin controllers") Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- .../bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml | 4 ++++ .../devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml index db8224dfba2c1..56fb9cf763ef7 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml @@ -58,6 +58,10 @@ patternProperties: drive_soc_gpio27_pee6, drive_ao_retention_n_pee2, drive_vcomp_alert_pee1, drive_hdmi_cec_pgg0 ] +required: + - compatible + - reg + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml index f5a3a881dec4f..bd305a34eee2b 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml @@ -115,6 +115,10 @@ patternProperties: drive_sdmmc1_dat2_pj4, drive_sdmmc1_dat1_pj3, drive_sdmmc1_dat0_pj2 ] +required: + - compatible + - reg + unevaluatedProperties: false examples: From 543ed0f61d56501cc585162da600bbedd7c08c0f Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Thu, 23 Apr 2026 18:29:46 +0530 Subject: [PATCH 0711/3297] drm/amdkfd: Validate CRIU-restored IDs before idr_alloc [ Upstream commit 85043dd49c2f51a37b22618168e3ae59ab92f0d6 ] The KFD CRIU restore flow restores previously saved object IDs from userspace. For event restore: kfd_criu_restore_event() -> create_signal_event() / create_other_event() -> allocate_event_notification_slot() -> idr_alloc(..., *restore_id, *restore_id + 1, ...) For BO restore: criu_restore_memory_of_gpu() -> idr_alloc(..., bo_priv->idr_handle, ...) In both cases, the restored ID comes from userspace-provided CRIU data. idr_alloc() expects the ID range values to fit within signed int limits. If a restored ID is larger than INT_MAX, it can trigger a WARN in the IDR layer. A kernel WARN is undesirable because it prints a warning trace and may cause a panic or reboot on systems with panic_on_warn enabled. Smatch reported these paths as allowing unchecked userspace values to reach idr_alloc(). Add INT_MAX validation before using restored IDs in: - kfd_criu_restore_event() - criu_restore_memory_of_gpu() If the restored ID is invalid, return -EINVAL. This prevents invalid restore data from reaching the IDR layer and avoids WARN-triggering paths, while keeping valid restore behavior unchanged. Fixes: 40e8a766a761 ("drm/amdkfd: CRIU checkpoint and restore events") Reported-by: Dan Carpenter Cc: Felix Kuehling Cc: David Yat Sin Cc: Rajneesh Bhardwaj Signed-off-by: Srinivasan Shanmugam Reviewed-by: David Yat Sin Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 3 +++ drivers/gpu/drm/amd/amdkfd/kfd_events.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 09ebfe8a30628..c2100fd78b522 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -2315,6 +2315,9 @@ static int criu_restore_memory_of_gpu(struct kfd_process_device *pdd, const bool criu_resume = true; u64 offset; + if (bo_priv->idr_handle > INT_MAX) + return -EINVAL; + if (bo_bucket->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL) { if (bo_bucket->size != kfd_doorbell_process_slice(pdd->dev->kfd)) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 261db87e86fe6..63039035b1941 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -480,6 +480,11 @@ int kfd_criu_restore_event(struct file *devkfd, } *priv_data_offset += sizeof(*ev_priv); + if (ev_priv->event_id > INT_MAX) { + ret = -EINVAL; + goto exit; + } + if (ev_priv->user_handle) { ret = kfd_kmap_event_page(p, ev_priv->user_handle); if (ret) From cc77f0d91e3214e4292208f02a1dc09a31f9aac7 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Sat, 18 Apr 2026 18:22:18 +0200 Subject: [PATCH 0712/3297] driver core: use READ_ONCE() for dev->driver in dev_has_sync_state() [ Upstream commit e9506871a8ea304cde48ff4a57226df2aadddae3 ] dev_has_sync_state() reads dev->driver twice without holding device_lock() -- once for the NULL check and once to dereference ->sync_state. Some callers only hold device_links_write_lock, which doesn't prevent a concurrent unbind from clearing dev->driver via device_unbind_cleanup(). Fix it by reading dev->driver exactly once with READ_ONCE(), pairing with the WRITE_ONCE() in device_set_driver(). Link: https://lore.kernel.org/driver-core/DHW8QPU1VU1F.3P6PH69HLFBYC@kernel.org/ Fixes: ac338acf514e ("driver core: Add dev_has_sync_state()") Reviewed-by: Rafael J. Wysocki (Intel) Acked-by: Greg Kroah-Hartman Reviewed-by: Saravana Kannan Link: https://patch.msgid.link/20260418162221.1121873-1-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- include/linux/device.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/device.h b/include/linux/device.h index 84910bffe7953..dc1252a06480c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1037,9 +1037,12 @@ static inline void device_lock_assert(struct device *dev) static inline bool dev_has_sync_state(struct device *dev) { + struct device_driver *drv; + if (!dev) return false; - if (dev->driver && dev->driver->sync_state) + drv = READ_ONCE(dev->driver); + if (drv && drv->sync_state) return true; if (dev->bus && dev->bus->sync_state) return true; From aefc30e4a829c1683f6ae999df7f9310c27eae6c Mon Sep 17 00:00:00 2001 From: Christos Longros Date: Sun, 29 Mar 2026 09:38:57 +0200 Subject: [PATCH 0713/3297] wifi: rtw89: fix wrong pci_get_drvdata type in AER handlers [ Upstream commit 7068c379cf9aa8afe4dce4d9d82390187aa9c4d0 ] rtw89 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. The adjacent io_slot_reset() already does it correctly. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Tested on RTL8852CE by calling the handlers from a test module before and after the fix. Fixes: 16e3d93c6183 ("wifi: rtw89: pci: add PCI Express error handling") Signed-off-by: Christos Longros Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260329073857.113081-1-chris.longros@gmail.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 6be1849b0c4d2..17a199ee1d4a4 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4605,9 +4605,9 @@ EXPORT_SYMBOL(rtw89_pm_ops); static pci_ers_result_t rtw89_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -4624,12 +4624,12 @@ static pci_ers_result_t rtw89_pci_io_slot_reset(struct pci_dev *pdev) static void rtw89_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw89_pci_err_handler = { From 1ef3d1338d94ec41f58fbfb6ba7742a27ea61d89 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Mon, 13 Apr 2026 14:59:26 +0800 Subject: [PATCH 0714/3297] wifi: rtw88: fix wrong pci_get_drvdata type in AER handlers [ Upstream commit 706183dbef4a79d120d4e928f693bea50df496f8 ] rtw88 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Fixes: cdb82c80b934 ("wifi: rtw88: pci: add PCI Express error handling") Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260413065926.17027-1-pkshih@realtek.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw88/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index ec0a45bfb670e..d6d8386f930a9 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1710,9 +1710,9 @@ static void rtw_pci_napi_deinit(struct rtw_dev *rtwdev) static pci_ers_result_t rtw_pci_io_err_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -1729,12 +1729,12 @@ static pci_ers_result_t rtw_pci_io_slot_reset(struct pci_dev *pdev) static void rtw_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw_pci_err_handler = { From 7a1ab5fdcae896e20ca6d68fa0fbd2816cb7471f Mon Sep 17 00:00:00 2001 From: Shin-Yi Lin Date: Mon, 20 Apr 2026 11:40:41 +0800 Subject: [PATCH 0715/3297] wifi: rtw89: Correct data type for scan index to avoid infinite loop [ Upstream commit 08fdcb529df6df3562dd2b0035f88dd5be8b3c68 ] A kernel soft lockup was observed during Wi-Fi scanning on the 6GHz band. The CPU becomes stuck in rtw89_hw_scan_add_chan_ax for over 20 seconds, leading to a system panic. RIP points to 0f b6 c3 (movzbl %bl, %eax), which zero-extends the low 8 bits of RBX into RAX. RBX (the counter i) has reached a huge value: 0x137466a1. watchdog: BUG: soft lockup - CPU#2 stuck for 26s! [kworker/u16:4:6124] Workqueue: events_unbound cfg80211_wiphy_work [cfg80211] RIP: 0010:rtw89_hw_scan_add_chan_ax+0xb3/0x6e0 [rtw89_core] Code: a0 48 89 45 a8 44 89 6d 9c 44 89 75 98 eb 29 66 66 2e 0f 1f 84 00 00 00 00 00 66 66 2e 0f 1f 84 00 00 00 00 00 66 90 83 c3 01 <0f> b6 c3 41 3b 44 24 74 0f 83 0b 02 00 00 0f b6 c3 48 8d 14 80 49 RSP: 0018:ffffcb48cbaa39f8 EFLAGS: 00000202 RAX: 0000000000000005 RBX: 00000000137466a1 RCX: 0000000000000000 RDX: ffff89ffc9d851a8 RSI: 0000000000004f0d RDI: 0000000096af0130 RBP: ffffcb48cbaa3a60 R08: 0000000000000000 R09: ffff8a00b7502080 R10: ffff8a00b75ff600 R11: 0000000000000000 R12: ffff89ffc7553870 R13: ffff8a00b7ac8f19 R14: ffff8a00b75020d8 R15: ffff89ffc3d54d80 FS: 0000000000000000(0000) GS:ffff8a014f962000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007558d7f9f4c4 CR3: 0000000178040001 CR4: 00000000001706f0 Call Trace: rtw89_hw_scan_prep_chan_list_ax+0x8a/0x400 [rtw89_core] rtw89_hw_scan_start+0x546/0x8a0 [rtw89_core] ? rtw89_fw_h2c_default_cmac_tbl+0x13c/0x1f0 [rtw89_core] rtw89_ops_hw_scan+0xae/0x120 [rtw89_core] drv_hw_scan+0xbb/0x180 [mac80211] __ieee80211_start_scan+0x2fc/0x750 [mac80211] ieee80211_request_scan+0xe/0x20 [mac80211] ieee80211_scan+0x123/0x190 [mac80211] rdev_scan+0x40/0x110 [cfg80211] cfg80211_scan_6ghz+0x5a1/0xa30 [cfg80211] By objdump with source: for (i = 0; i < req->n_6ghz_params; i++) { 5fbc0: 83 c3 01 add $0x1,%ebx --> i++ 5fbc3: 0f b6 c3 movzbl %bl,%eax --> get counter fbc6: 41 3b 44 24 74 cmp 0x74(%r12),%eax * RBX: 00000000137466a1 -> %bl = a1 -> EAX = 000000a1 (161) Fixes: c6aa9a9c4725 ("wifi: rtw89: add RNR support for 6 GHz scan") Signed-off-by: Shin-Yi Lin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-7-pkshih@realtek.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index c9851aafe649e..15ac357bbdbac 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -7198,7 +7198,7 @@ static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev, struct sk_buff *skb; bool found; int ret = 0; - u8 i; + u32 i; if (!req->n_6ghz_params) return 0; From 01155ded5d4dad61840a9a3c33ab56778ef1f100 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:34 +0000 Subject: [PATCH 0716/3297] wifi: rtw88: fix OOB read from firmware RX descriptor exceeding DMA buffer [ Upstream commit 6e76e9ed273dfb4b3333a5ebbb94958cc5752ab6 ] In rtw_pci_rx_napi(), new_len is computed as the sum of pkt_len (14-bit descriptor field, max 16383) and pkt_offset (drv_info_sz + shift, both firmware-controlled). The result can exceed RTK_PCI_RX_BUF_SIZE (11478), causing an out-of-bounds read from the pre-allocated DMA buffer when skb_put_data copies new_len bytes. The USB transport already validates this (rtw_usb_rx_data_put checks against RTW_USB_MAX_RECVBUF_SZ); the PCIe path does not. Add a check that new_len does not exceed the DMA buffer size. Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111434.3389674-1-tristmd@gmail.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw88/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index d6d8386f930a9..eae54f50485c2 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1076,6 +1076,11 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, * discard the frame if none available */ new_len = pkt_stat.pkt_len + pkt_offset; + if (unlikely(new_len > RTK_PCI_RX_BUF_SIZE)) { + rtw_dbg(rtwdev, RTW_DBG_RX, + "oversized RX packet: %u\n", new_len); + goto next_rp; + } new = dev_alloc_skb(new_len); if (WARN_ONCE(!new, "rx routine starvation\n")) goto next_rp; From ad445de67359f24088d4305c05fbd7e34c4e35e3 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:42 +0000 Subject: [PATCH 0717/3297] wifi: rtw89: add bounds check on firmware mac_id in link lookup [ Upstream commit 6d88244bb129755acca696f9227200f4a2d106a6 ] The mac_id field in RX descriptors is 8 bits wide (0-255), but assoc_link_on_macid[] has only RTW89_MAX_MAC_ID_NUM (128) entries. While the driver currently assigns mac_id values below 128, the descriptor value comes from firmware and is not validated before use as an array index. Add a defensive bounds check in rtw89_assoc_link_rcu_dereference() to guard against out-of-range firmware values. Fixes: 144c6cd24b35 ("wifi: rtw89: 8922a: configure AP_LINK_PS if FW supports") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111442.3395411-1-tristmd@gmail.com Signed-off-by: Sasha Levin --- drivers/net/wireless/realtek/rtw89/core.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c3839d49f4420..e146fd082f21a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6254,6 +6254,9 @@ static inline void rtw89_assoc_link_clr(struct rtw89_sta_link *rtwsta_link) static inline struct rtw89_sta_link * rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid) { + if (unlikely(macid >= RTW89_MAX_MAC_ID_NUM)) + return NULL; + return rcu_dereference(rtwdev->assoc_link_on_macid[macid]); } From fbeea02c3564d1e289d9c5dd676409be314fdf49 Mon Sep 17 00:00:00 2001 From: Xingjing Deng Date: Fri, 6 Mar 2026 02:17:09 +0000 Subject: [PATCH 0718/3297] kconfig: fix potential NULL pointer dereference in conf_askvalue [ Upstream commit b9d21c32dca2167a614e66c9e27999b9e1c33d55 ] In conf_askvalue(), the 'def' argument (retrieved via sym_get_string_value) can be NULL. While current call sites ensure that 'def' is valid, calling printf("%s\n", def) is technically undefined behavior and could lead to a segmentation fault on certain libc implementations if the function were called with a NULL pointer in the future. Improve the robustness of conf_askvalue() by providing an empty string as a fallback. Additionally, remove the redundant re-initialization of the 'line' buffer inside the !sym_is_changeable(sym) block, as it is already properly initialized at the function entry. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Xingjing Deng Reviewed-by: Nathan Chancellor Link: https://patch.msgid.link/20260306021709.27068-1-micro6947@gmail.com Signed-off-by: Nathan Chancellor Signed-off-by: Sasha Levin --- scripts/kconfig/conf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index a7b44cd8ae140..c368bec5ab601 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -297,9 +297,7 @@ static int conf_askvalue(struct symbol *sym, const char *def) line[1] = 0; if (!sym_is_changeable(sym)) { - printf("%s\n", def); - line[0] = '\n'; - line[1] = 0; + printf("%s\n", def ?: ""); return 0; } @@ -307,7 +305,7 @@ static int conf_askvalue(struct symbol *sym, const char *def) case oldconfig: case syncconfig: if (sym_has_value(sym)) { - printf("%s\n", def); + printf("%s\n", def ?: ""); return 0; } /* fall through */ From a83e77d1e52c01c50096e6e97c57d34e0590d628 Mon Sep 17 00:00:00 2001 From: Prasanna Kumar T S M Date: Thu, 19 Mar 2026 23:04:45 -0700 Subject: [PATCH 0719/3297] soc: xilinx: Shutdown and free rx mailbox channel [ Upstream commit fdee7c66c0d7b6869c36b9f9a915abf29ab5b550 ] A mbox rx channel is requested using mbox_request_channel_byname() in probe. In remove callback, the rx mailbox channel is cleaned up when the rx_chan is NULL due to incorrect condition check. The mailbox channel is not shutdown and it can receive messages even after the device removal. This leads to use after free. Also the channel resources are not freed. Fix this by checking the rx_chan correctly. Fixes: ffdbae28d9d1a ("drivers: soc: xilinx: Use mailbox IPI callback") Signed-off-by: Prasanna Kumar T S M Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/20260320060445.1541017-1-ptsm@linux.microsoft.com Signed-off-by: Sasha Levin --- drivers/soc/xilinx/zynqmp_power.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index ae59bf16659a6..84ef7c87dfaa3 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -396,8 +396,10 @@ static void zynqmp_pm_remove(struct platform_device *pdev) { sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); - if (!rx_chan) + if (rx_chan) { mbox_free_channel(rx_chan); + rx_chan = NULL; + } } static const struct of_device_id pm_of_match[] = { From 7c79be7e634474a0914a17474ceb112013c2a619 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 27 Apr 2026 10:11:46 +0800 Subject: [PATCH 0720/3297] pinctrl: mediatek: eint: Drop base from mtk_eint_chip_write_mask() [ Upstream commit 3ca99eed042620d12315e9272ed3ef260ca29877 ] When support for multiple EINT base addresses was added in commit 3ef9f710efcb ("pinctrl: mediatek: Add EINT support for multiple addresses"), mtk_eint_chip_write_mask() was changed to write interrupt masks for all base addresses in one call. However the "base" parameter was left around and now causes sparse warnings: mtk-eint.c:428:44: warning: incorrect type in argument 2 (different address spaces) mtk-eint.c:428:44: expected void [noderef] __iomem *base mtk-eint.c:428:44: got void [noderef] __iomem **base mtk-eint.c:436:44: warning: incorrect type in argument 2 (different address spaces) mtk-eint.c:436:44: expected void [noderef] __iomem *base mtk-eint.c:436:44: got void [noderef] __iomem **base Since the "base" parameter is no longer needed, just drop it. Fixes: 3ef9f710efcb ("pinctrl: mediatek: Add EINT support for multiple addresses") Cc: Hao Chang Cc: Qingliang Li Signed-off-by: Chen-Yu Tsai Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/mtk-eint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/mediatek/mtk-eint.c b/drivers/pinctrl/mediatek/mtk-eint.c index 2ea0902b4f660..5f12af59a91b5 100644 --- a/drivers/pinctrl/mediatek/mtk-eint.c +++ b/drivers/pinctrl/mediatek/mtk-eint.c @@ -241,7 +241,7 @@ static int mtk_eint_irq_set_wake(struct irq_data *d, unsigned int on) } static void mtk_eint_chip_write_mask(const struct mtk_eint *eint, - void __iomem *base, unsigned int **buf) + unsigned int **buf) { int inst, port, port_num; void __iomem *reg; @@ -420,7 +420,7 @@ static void mtk_eint_irq_handler(struct irq_desc *desc) int mtk_eint_do_suspend(struct mtk_eint *eint) { - mtk_eint_chip_write_mask(eint, eint->base, eint->wake_mask); + mtk_eint_chip_write_mask(eint, eint->wake_mask); return 0; } @@ -428,7 +428,7 @@ EXPORT_SYMBOL_GPL(mtk_eint_do_suspend); int mtk_eint_do_resume(struct mtk_eint *eint) { - mtk_eint_chip_write_mask(eint, eint->base, eint->cur_mask); + mtk_eint_chip_write_mask(eint, eint->cur_mask); return 0; } From 5435fd3edcb11c7cc4002847c83e9a50b49284dc Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Wed, 15 Apr 2026 22:23:43 +0000 Subject: [PATCH 0721/3297] wifi: ath9k: fix OOB access from firmware tx status queue ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7ce2f118a2389e8f0a64068c6fe7cc7d40639be0 ] ath_tx_edma_tasklet() accesses sc->tx.txq[ts.qid] where ts.qid is a 4-bit hardware field (0-15), but the txq array only has ATH9K_NUM_TX_QUEUES (10) entries. A qid >= 10 causes an OOB array access. Add a bounds check on ts.qid before using it as an array index. Fixes: fce041beb03f ("ath9k: unify edma and non-edma tx code, improve tx fifo handling") Signed-off-by: Tristan Madani Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260415222343.1540564-1-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/xmit.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 0ac9212e42f75..957646b2df4de 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2746,6 +2746,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) continue; } + if (ts.qid >= ATH9K_NUM_TX_QUEUES) { + ath_dbg(common, XMIT, "invalid qid %d\n", ts.qid); + continue; + } + txq = &sc->tx.txq[ts.qid]; ath_txq_lock(sc, txq); From 19eb0ab0bdffb347f7670ebc1244b56815dba726 Mon Sep 17 00:00:00 2001 From: Jihed Chaibi Date: Wed, 25 Mar 2026 23:34:11 +0100 Subject: [PATCH 0722/3297] ARM: dts: am335x-sl50: Fix audio bitclock and frame master endpoint [ Upstream commit 2bc564f46b00dc4f4331fc337277ff3f5fac8a4e ] The cpu_endpoint in mcasp0 specifies the TLV320AIC3106 codec as the bitclock and frame master, but the phandles point to the codec's port node (codec_port) rather than its endpoint node (codec_endpoint). audio-graph-card calls simple_util_parse_daifmt() with ep_codec set to the endpoint node (codec_endpoint). The function resolves the bitclock-master phandle and checks whether it equals ep_codec. Since codec_port is the parent of codec_endpoint, not the endpoint itself, the comparison always evaluates to false. This causes the mcasp0 CPU side to be silently configured as bitclock and frame master instead of the codec, which is the opposite of the intended configuration. Fix by pointing bitclock-master and frame-master to codec_endpoint. Fixes: e5f89dbdebc5 ("ARM: dts: am335x-sl50: use audio-graph-card for sound") Signed-off-by: Jihed Chaibi Link: https://patch.msgid.link/20260325223411.123666-1-jihed.chaibi.dev@gmail.com Signed-off-by: Kevin Hilman (TI) Signed-off-by: Sasha Levin --- arch/arm/boot/dts/ti/omap/am335x-sl50.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/ti/omap/am335x-sl50.dts b/arch/arm/boot/dts/ti/omap/am335x-sl50.dts index 1dc4e344efd63..c5259eb7d21c7 100644 --- a/arch/arm/boot/dts/ti/omap/am335x-sl50.dts +++ b/arch/arm/boot/dts/ti/omap/am335x-sl50.dts @@ -558,8 +558,8 @@ remote-endpoint = <&codec_endpoint>; dai-format = "dsp_b"; - bitclock-master = <&codec_port>; - frame-master = <&codec_port>; + bitclock-master = <&codec_endpoint>; + frame-master = <&codec_endpoint>; bitclock-inversion; clocks = <&audio_mclk>; }; From 29fd4f4c73e2bf387a28a0e6ff1f948713814a23 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Mon, 27 Apr 2026 15:17:09 +0200 Subject: [PATCH 0723/3297] Documentation/rv: Replace stale website link [ Upstream commit 49cbd359e4a7501e9d6694c072031d9ae6b2d1a5 ] The sched monitor page was linking to Daniel's website which is now down. The main purpose of the link was to point to a source for the models from the original author and that can be found also in his published paper. Replace the link with a reference to Daniel's "A thread synchronization model for the PREEMPT_RT Linux kernel" which can be found online and includes the models definitions as well as the work behind them (not the original patches but since they're based on a 5.0 kernel and are mostly included upstream, there's little value in keeping them in the docs). Fixes: 03abeaa63c08 ("Documentation/rv: Add docs for the sched monitors") Signed-off-by: Gabriele Monaco Acked-by: Matteo Martelli Tested-by: Matteo Martelli Tested-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Jonathan Corbet Message-ID: <20260427131709.170505-2-gmonaco@redhat.com> Signed-off-by: Sasha Levin --- Documentation/trace/rv/monitor_sched.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/trace/rv/monitor_sched.rst b/Documentation/trace/rv/monitor_sched.rst index 3f8381ad9ec7b..608173574ba76 100644 --- a/Documentation/trace/rv/monitor_sched.rst +++ b/Documentation/trace/rv/monitor_sched.rst @@ -36,7 +36,7 @@ Specifications -------------- The specifications included in sched are currently a work in progress, adapting the ones -defined in by Daniel Bristot in [1]. +defined by Daniel Bristot in [1]_. Currently we included the following: @@ -399,4 +399,7 @@ another special case that is currently not supported by the monitor. References ---------- -[1] - https://bristot.me/linux-task-model +.. [1] Daniel Bristot de Oliveira et al.: + `A thread synchronization model for the PREEMPT_RT Linux kernel + `_, + J. Syst. Archit., 2020. From d6e8d6b2f8f043124ae8f16c616ecbf333557d45 Mon Sep 17 00:00:00 2001 From: Yingjie Gao Date: Thu, 2 Apr 2026 15:16:17 +0800 Subject: [PATCH 0724/3297] watchdog: sp5100_tco: Use EFCH MMIO for newer Hygon FCH [ Upstream commit 4f675f036cd5e9cfbe6df5f24f8338158af96a4b ] Commit 009637de1f65 ("watchdog: sp5100_tco: support Hygon FCH/SCH (Server Controller Hub)") added Hygon vendor matching to the efch layout selection, but newer Hygon 0x790b SMBus devices still need the efch_mmio path. The efch_mmio path enables EFCH_PM_DECODEEN_WDT_TMREN before probing the watchdog MMIO block. If firmware leaves that bit clear and the driver picks the legacy efch path instead, probe falls back to the alternate window and fails with "Watchdog hardware is disabled". Select efch_mmio for Hygon 0x790b devices with revision 0x51 or later, matching the equivalent AMD behavior and allowing the watchdog to initialize on those systems. Fixes: 009637de1f65 ("watchdog: sp5100_tco: support Hygon FCH/SCH (Server Controller Hub)") Signed-off-by: Yingjie Gao Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260402071617.634563-2-gaoyingjie@uniontech.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/sp5100_tco.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 2bd3dc25cb030..7e99c3b1f3676 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -92,7 +92,8 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && dev->revision < 0x40) { return sp5100; - } else if (dev->vendor == PCI_VENDOR_ID_AMD && + } else if ((dev->vendor == PCI_VENDOR_ID_AMD || + dev->vendor == PCI_VENDOR_ID_HYGON) && sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) { return efch_mmio; From 1917015aa0a1d5e418370fe2c3f085356801e73c Mon Sep 17 00:00:00 2001 From: Balakrishnan Sambath Date: Mon, 2 Mar 2026 17:03:09 +0530 Subject: [PATCH 0725/3297] watchdog: sama5d4_wdt: Fix WDDIS detection on SAM9X60 and SAMA7G5 [ Upstream commit e8bc610b14a99d24b0916635102cc52ba41def9b ] The driver hardcoded AT91_WDT_WDDIS (bit 15) in wdt_enabled and the probe initial state readout. SAM9X60 and SAMA7G5 use bit 12 (AT91_SAM9X60_WDDIS), causing incorrect WDDIS detection. Introduce a per-device wddis_mask field to select the correct WDDIS bit based on the compatible string. Fixes: 266da53c35fc ("watchdog: sama5d4: readout initial state") Co-developed-by: Andrei Simion Signed-off-by: Andrei Simion Signed-off-by: Balakrishnan Sambath Reviewed-by: Alexandre Belloni Link: https://lore.kernel.org/r/20260302113310.133989-2-balakrishnan.s@microchip.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/sama5d4_wdt.c | 48 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 13e72918338a2..704b786cc2ec6 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -30,6 +30,7 @@ struct sama5d4_wdt { void __iomem *reg_base; u32 mr; u32 ir; + u32 wddis_mask; unsigned long last_ping; bool need_irq; bool sam9x60_support; @@ -48,7 +49,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS)) +static inline bool wdt_enabled(struct sama5d4_wdt *wdt) +{ + return !(wdt->mr & wdt->wddis_mask); +} #define wdt_read(wdt, field) \ readl_relaxed((wdt)->reg_base + (field)) @@ -81,12 +85,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER); - wdt->mr &= ~AT91_SAM9X60_WDDIS; - } else { - wdt->mr &= ~AT91_WDT_WDDIS; - } + wdt->mr &= ~wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -96,12 +97,9 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - if (wdt->sam9x60_support) { + if (wdt->sam9x60_support) writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR); - wdt->mr |= AT91_SAM9X60_WDDIS; - } else { - wdt->mr |= AT91_WDT_WDDIS; - } + wdt->mr |= wdt->wddis_mask; wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -117,7 +115,7 @@ static int sama5d4_wdt_ping(struct watchdog_device *wdd) } static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, - unsigned int timeout) + unsigned int timeout) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); u32 value = WDT_SEC2TICKS(timeout); @@ -140,8 +138,8 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, * If the watchdog is enabled, then the timeout can be updated. Else, * wait that the user enables it. */ - if (wdt_enabled) - wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); + if (wdt_enabled(wdt)) + wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~wdt->wddis_mask); wdd->timeout = timeout; @@ -184,10 +182,7 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - if (wdt->sam9x60_support) - wdt->mr = AT91_SAM9X60_WDDIS; - else - wdt->mr = AT91_WDT_WDDIS; + wdt->mr = wdt->wddis_mask; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) @@ -213,15 +208,11 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) * If the watchdog is already running, we can safely update it. * Else, we have to disable it properly. */ - if (!wdt_enabled) { + if (!wdt_enabled(wdt)) { reg = wdt_read(wdt, AT91_WDT_MR); - if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS))) - wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_SAM9X60_WDDIS); - else if (!wdt->sam9x60_support && - (!(reg & AT91_WDT_WDDIS))) + if (!(reg & wdt->wddis_mask)) wdt_write_nosleep(wdt, AT91_WDT_MR, - reg | AT91_WDT_WDDIS); + reg | wdt->wddis_mask); } if (wdt->sam9x60_support) { @@ -273,6 +264,9 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt")) wdt->sam9x60_support = true; + wdt->wddis_mask = wdt->sam9x60_support ? AT91_SAM9X60_WDDIS + : AT91_WDT_WDDIS; + watchdog_set_drvdata(wdd, wdt); regs = devm_platform_ioremap_resource(pdev, 0); @@ -306,8 +300,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, wdt_timeout, dev); reg = wdt_read(wdt, AT91_WDT_MR); - if (!(reg & AT91_WDT_WDDIS)) { - wdt->mr &= ~AT91_WDT_WDDIS; + if (!(reg & wdt->wddis_mask)) { + wdt->mr &= ~wdt->wddis_mask; set_bit(WDOG_HW_RUNNING, &wdd->status); } From facbb592e22dd674cbf5707e44288de38f48d08c Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 23 Feb 2026 19:59:20 +0800 Subject: [PATCH 0726/3297] watchdog: sprd_wdt: Remove redundant sprd_wdt_disable() on register failure [ Upstream commit 96b3cfc3b8ad0524d12fed1e08bc5df3ff345f64 ] The driver uses devm_add_action_or_reset() to register sprd_wdt_disable() as a managed cleanup action. When devm_watchdog_register_device() fails, the devm core will invoke the cleanup action automatically. The explicit sprd_wdt_disable() call in the error path is therefore redundant and results in adouble cleanup. Fixes: 78d9bfad2e89 ("watchdog: sprd_wdt: Convert to use device managed functions and other improvements") Signed-off-by: Felix Gu Reviewed-by: Guenter Roeck Reviewed-by: Baolin Wang Link: https://lore.kernel.org/r/20260223-sprd_wdt-v1-1-2e71f9a76ecb@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/sprd_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index 4e689b6ff1418..aacf04616fefe 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -320,10 +320,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&wdt->wdd, 0, dev); ret = devm_watchdog_register_device(dev, &wdt->wdd); - if (ret) { - sprd_wdt_disable(wdt); + if (ret) return ret; - } + platform_set_drvdata(pdev, wdt); return 0; From 4534f70aa70421dfd32ea00398dcee17c694bac5 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 7 Apr 2026 01:14:40 +0300 Subject: [PATCH 0727/3297] media: cedrus: Fix failure to clean up hardware on probe failure [ Upstream commit f0a22f1d602ed499a192284de5e811a73421f0c7 ] If V4L2 device fails to register, then SRAM still be claimed and as a result driver will not be able to probe again. cedrus 1c0e000.video-codec: Failed to claim SRAM cedrus 1c0e000.video-codec: Failed to probe hardware cedrus 1c0e000.video-codec: probe with driver cedrus failed with error -16 cedrus_hw_remove undoes everything that was previously done by cedrus_hw_probe, such as disabling runtime power management and releasing the claimed SRAM and reserved memory region. Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Acked-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/staging/media/sunxi/cedrus/cedrus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index bff42ea1871f7..1c7191bb0d8fc 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -476,7 +476,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register V4L2 device\n"); - return ret; + goto err_hw; } vfd = &dev->vfd; @@ -537,6 +537,8 @@ static int cedrus_probe(struct platform_device *pdev) v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); +err_hw: + cedrus_hw_remove(dev); return ret; } From 2b2a17af8d8c7a5aafa6095f65a7c4041c085656 Mon Sep 17 00:00:00 2001 From: Nas Chung Date: Wed, 15 Apr 2026 18:25:21 +0900 Subject: [PATCH 0728/3297] media: v4l2-common: Add YUV24 format info [ Upstream commit 968b741872914a15363af0daf24ed2e82ec355f3 ] The YUV24 format is missing an entry in the v4l2_format_info(). The YUV24 format is the packed YUV 4:4:4 formats with 8 bits per component. Fixes: 0376a51fbe5e ("media: v4l: Add packed YUV444 24bpp pixel format") Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/v4l2-core/v4l2-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index b367d479d6b3b..c1e3bcb4c1c40 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -281,6 +281,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, From 928f758f8f2143eabd0c7aadab67182641045ed6 Mon Sep 17 00:00:00 2001 From: Ashish Mhetre Date: Thu, 30 Apr 2026 09:52:01 +0000 Subject: [PATCH 0729/3297] memory: tegra: Wire up system sleep PM ops [ Upstream commit 2411c8d1e3e09910e94bab0d0a2c071fbc8a9e7b ] The tegra-mc platform driver does not register any dev_pm_ops, so the SoC-specific ->resume() is never invoked (e.g. tegra186_mc_resume) on system wake. On Tegra186 and later this means MC client Stream-ID override registers are not reprogrammed, and clients behind the ARM SMMU fault on the first DMA after resume. Register a dev_pm_ops on the tegra-mc driver and route the system resume callback into mc->soc->ops->resume() so the existing SID restore path runs again on wake. No suspend callback is needed as the resume path reprograms all MC state from the static SoC tables, so there is nothing to save. Fixes: fe3b082a6eb8 ("memory: tegra: Add SID override programming for MC clients") Signed-off-by: Ashish Mhetre Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260430095202.1167651-3-amhetre@nvidia.com Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/memory/tegra/mc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 6edb210287dca..c29df29920f46 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -989,10 +990,23 @@ static void tegra_mc_sync_state(struct device *dev) icc_sync_state(dev); } +static int tegra_mc_resume(struct device *dev) +{ + struct tegra_mc *mc = dev_get_drvdata(dev); + + if (mc->soc->ops && mc->soc->ops->resume) + mc->soc->ops->resume(mc); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(tegra_mc_pm_ops, NULL, tegra_mc_resume); + static struct platform_driver tegra_mc_driver = { .driver = { .name = "tegra-mc", .of_match_table = tegra_mc_of_match, + .pm = pm_sleep_ptr(&tegra_mc_pm_ops), .suppress_bind_attrs = true, .sync_state = tegra_mc_sync_state, }, From 53baa6b90f499a7b1fb7a3787cf3e03d35dcb6c4 Mon Sep 17 00:00:00 2001 From: Damian Muszynski Date: Tue, 7 Apr 2026 12:04:26 +0200 Subject: [PATCH 0730/3297] crypto: qat - fix heartbeat error injection [ Upstream commit 2e96024632b386c86860aa78639940fc96d6fcc9 ] The current implementation of the heartbeat error injection uses adf_disable_arb_thd() to stop a specific accelerator engine thread from processing requests. This does not reliably prevent the device from generating responses. Fix the error injection by disabling the device arbiter through exit_arb() instead. This properly simulates a device failure by stopping all arbitration, which results in missing responses for sent requests. Remove the now unused adf_disable_arb_thd() function and its declaration. Fixes: e2b67859ab6e ("crypto: qat - add heartbeat error simulator") Signed-off-by: Damian Muszynski Reviewed-by: Ahsan Atta Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- .../intel/qat/qat_common/adf_common_drv.h | 1 - .../qat/qat_common/adf_heartbeat_inject.c | 6 ++--- .../intel/qat/qat_common/adf_hw_arbiter.c | 25 ------------------- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index a2b9491c26b13..63e1f1c8358e2 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -90,7 +90,6 @@ void adf_exit_aer(void); int adf_init_arb(struct adf_accel_dev *accel_dev); void adf_exit_arb(struct adf_accel_dev *accel_dev); void adf_update_ring_arb(struct adf_etr_ring_data *ring); -int adf_disable_arb_thd(struct adf_accel_dev *accel_dev, u32 ae, u32 thr); int adf_dev_get(struct adf_accel_dev *accel_dev); void adf_dev_put(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c b/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c index a3b474bdef6c8..023c5f1e78b07 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c +++ b/drivers/crypto/intel/qat/qat_common/adf_heartbeat_inject.c @@ -64,10 +64,8 @@ int adf_heartbeat_inject_error(struct adf_accel_dev *accel_dev) if (ret) return ret; - /* Configure worker threads to stop processing any packet */ - ret = adf_disable_arb_thd(accel_dev, rand_ae, rand_thr); - if (ret) - return ret; + /* Disable arbiter to stop processing any packet */ + hw_device->exit_arb(accel_dev); /* Change HB counters memory to simulate a hang */ adf_set_hb_counters_fail(accel_dev, rand_ae, rand_thr); diff --git a/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c b/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c index f93d9cca70cee..dd9a31c20bc9c 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c +++ b/drivers/crypto/intel/qat/qat_common/adf_hw_arbiter.c @@ -99,28 +99,3 @@ void adf_exit_arb(struct adf_accel_dev *accel_dev) csr_ops->write_csr_ring_srv_arb_en(csr, i, 0); } EXPORT_SYMBOL_GPL(adf_exit_arb); - -int adf_disable_arb_thd(struct adf_accel_dev *accel_dev, u32 ae, u32 thr) -{ - void __iomem *csr = accel_dev->transport->banks[0].csr_addr; - struct adf_hw_device_data *hw_data = accel_dev->hw_device; - const u32 *thd_2_arb_cfg; - struct arb_info info; - u32 ae_thr_map; - - if (ADF_AE_STRAND0_THREAD == thr || ADF_AE_STRAND1_THREAD == thr) - thr = ADF_AE_ADMIN_THREAD; - - hw_data->get_arb_info(&info); - thd_2_arb_cfg = hw_data->get_arb_mapping(accel_dev); - if (!thd_2_arb_cfg) - return -EFAULT; - - /* Disable scheduling for this particular AE and thread */ - ae_thr_map = *(thd_2_arb_cfg + ae); - ae_thr_map &= ~(GENMASK(3, 0) << (thr * BIT(2))); - - WRITE_CSR_ARB_WT2SAM(csr, info.arb_offset, info.wt2sam_offset, ae, - ae_thr_map); - return 0; -} From 796ff973077c70f73a3e9cc2d8399b22a4978fc4 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 26 Mar 2026 21:12:00 +0900 Subject: [PATCH 0731/3297] lib/vsprintf: Fix to check field_width and precision [ Upstream commit 71876dffab295b6e25d4209f0424da8fc5020e12 ] Check the field_width and presition correctly. Previously it depends on the bitfield conversion from int to check out-of-range error. However, commit 938df695e98d ("vsprintf: associate the format state with the format pointer") changed those fields to int. We need to check the out-of-range correctly without bitfield conversion. Fixes: 938df695e98d ("vsprintf: associate the format state with the format pointer") Reported-by: David Laight Closes: https://lore.kernel.org/all/20260318151250.40fef0ab@pumpkin/ Signed-off-by: Masami Hiramatsu (Google) Reviewed-by: Petr Mladek Link: https://patch.msgid.link/177452712047.197965.16376597502504928495.stgit@devnote2 Signed-off-by: Petr Mladek Signed-off-by: Sasha Levin --- lib/vsprintf.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index a356965c1d734..a8f12833fbb32 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -2619,6 +2619,18 @@ static unsigned char spec_flag(unsigned char c) return (c < sizeof(spec_flag_array)) ? spec_flag_array[c] : 0; } +static void set_field_width(struct printf_spec *spec, int width) +{ + spec->field_width = clamp(width, -FIELD_WIDTH_MAX, FIELD_WIDTH_MAX); + WARN_ONCE(spec->field_width != width, "field width %d out of range", width); +} + +static void set_precision(struct printf_spec *spec, int prec) +{ + spec->precision = clamp(prec, 0, PRECISION_MAX); + WARN_ONCE(spec->precision < prec, "precision %d too large", prec); +} + /* * Helper function to decode printf style format. * Each call decode a token from the format and return the @@ -2689,7 +2701,7 @@ struct fmt format_decode(struct fmt fmt, struct printf_spec *spec) spec->field_width = -1; if (isdigit(*fmt.str)) - spec->field_width = skip_atoi(&fmt.str); + set_field_width(spec, skip_atoi(&fmt.str)); else if (unlikely(*fmt.str == '*')) { /* it's the next argument */ fmt.state = FORMAT_STATE_WIDTH; @@ -2703,9 +2715,7 @@ struct fmt format_decode(struct fmt fmt, struct printf_spec *spec) if (unlikely(*fmt.str == '.')) { fmt.str++; if (isdigit(*fmt.str)) { - spec->precision = skip_atoi(&fmt.str); - if (spec->precision < 0) - spec->precision = 0; + set_precision(spec, skip_atoi(&fmt.str)); } else if (*fmt.str == '*') { /* it's the next argument */ fmt.state = FORMAT_STATE_PRECISION; @@ -2778,24 +2788,6 @@ struct fmt format_decode(struct fmt fmt, struct printf_spec *spec) return fmt; } -static void -set_field_width(struct printf_spec *spec, int width) -{ - spec->field_width = width; - if (WARN_ONCE(spec->field_width != width, "field width %d too large", width)) { - spec->field_width = clamp(width, -FIELD_WIDTH_MAX, FIELD_WIDTH_MAX); - } -} - -static void -set_precision(struct printf_spec *spec, int prec) -{ - spec->precision = prec; - if (WARN_ONCE(spec->precision != prec, "precision %d too large", prec)) { - spec->precision = clamp(prec, 0, PRECISION_MAX); - } -} - /* * Turn a 1/2/4-byte value into a 64-bit one for printing: truncate * as necessary and deal with signedness. From c599af2eacd66a6558ea241e743cc2f27673ddc8 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 30 Apr 2026 19:52:12 +0100 Subject: [PATCH 0732/3297] dts: spacemit: set console baud rate on bpif3 [ Upstream commit 24c12ca43b12c104389d9a159207d0b25779d0af ] Because the default console's baud rate is not set, defconfig kernels do not have any serial output on this platform. Set the baud rate to 115200, matching what is used by U-Boot etc on this platform. Suggested-by: Vivian Wang Fixes: d60d57ab6b2a8 ("riscv: dts: spacemit: add Banana Pi BPI-F3 board device tree") Signed-off-by: Conor Dooley Reviewed-by: Yixun Lan Link: https://lore.kernel.org/r/20260430-reword-overstep-3be08b7eab25@spud Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 2aaaff77831e1..a58192ba67b2c 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -17,7 +17,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; leds { From a402b3d09381525dd4fbe888f7464e71692fdbcb Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 4 May 2026 22:53:26 +0800 Subject: [PATCH 0733/3297] pinctrl: sunxi: fix regulator leak in sunxi_pmx_request() error path [ Upstream commit 334f7bcbdc79dc8e5b938ac3372453a5368221cf ] In the error path of sunxi_pmx_request(), the code calls regulator_put(s_reg->regulator) to release the regulator. However, s_reg->regulator is only assigned after a successful regulator_enable(). This causes a memory leak: the regulator obtained via regulator_get() is never properly released when regulator_enable() fails. Fixes: dc1445584177 ("pinctrl: sunxi: Fix and simplify pin bank regulator handling") Signed-off-by: Felix Gu Reviewed-by: Andre Przywara Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 0fb057a07dccb..93e396041456a 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -896,7 +896,7 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset) return 0; out: - regulator_put(s_reg->regulator); + regulator_put(reg); return ret; } From 02b001d89157af83e1421d896836a6946568d3a3 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 22 Apr 2026 23:07:23 +0200 Subject: [PATCH 0734/3297] dt-bindings: vendor-prefixes: Add Displaytech Ltd. [ Upstream commit 42112cff8cb78ff6120983ba71bd14d52ce9dccd ] Add "displaytech" vendor prefix for Displaytech Ltd. . Signed-off-by: Marek Vasut Acked-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260422210806.80948-1-marex@nabladev.com Signed-off-by: Sasha Levin --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index f1d1882009ba9..a9cf85fd6fbec 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -419,6 +419,8 @@ patternProperties: description: Diodes, Inc. "^dioo,.*": description: Dioo Microcircuit Co., Ltd + "^displaytech,.*": + description: Displaytech Ltd. "^djn,.*": description: Shenzhen DJN Optronics Technology Co., Ltd "^dlc,.*": From 76a4c3af3253dc7b27390710c7f402dbb862fa81 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 16 Apr 2026 13:10:54 +0000 Subject: [PATCH 0735/3297] drm/gpuvm: take refcount on DRM device [ Upstream commit c2d72717e0a9dd01c6a1bac61a1462a8e04bd179 ] Currently GPUVM relies on the owner implicitly holding a refcount to the drm device, and it does not implicitly take a refcount on the drm device. This design is error-prone, so take a refcount on the device. Suggested-by: Danilo Krummrich Signed-off-by: Alice Ryhl Fixes: 546ca4d35dcc ("drm/gpuvm: convert WARN() to drm_WARN() variants") Link: https://patch.msgid.link/20260416-gpuvm-drm-dev-get-v1-1-f3bc06571e73@google.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gpuvm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index 23e43406a1a8b..5c8bf010cd965 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -25,6 +25,7 @@ * */ +#include #include #include @@ -1089,6 +1090,7 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, gpuvm->drm = drm; gpuvm->r_obj = r_obj; + drm_dev_get(drm); drm_gem_object_get(r_obj); drm_gpuvm_warn_check_overflow(gpuvm, start_offset, range); @@ -1130,13 +1132,15 @@ static void drm_gpuvm_free(struct kref *kref) { struct drm_gpuvm *gpuvm = container_of(kref, struct drm_gpuvm, kref); + struct drm_device *drm = gpuvm->drm; drm_gpuvm_fini(gpuvm); - if (drm_WARN_ON(gpuvm->drm, !gpuvm->ops->vm_free)) + if (drm_WARN_ON(drm, !gpuvm->ops->vm_free)) return; gpuvm->ops->vm_free(gpuvm); + drm_dev_put(drm); } /** From d003d9bb44da1b48af56689bd2be855962f5cae6 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 5 May 2026 15:53:42 +0200 Subject: [PATCH 0736/3297] drm/panel: Clean up S6E3HA2 config dependencies and fill help text [ Upstream commit 632a8aa96d0717aa92cb63e3939d09b896223b4c ] As per the config name this Display IC features a DSI command-mode interface (or the command to switch to video mode is not known/documented) and does not use any of the video-mode helper utilities, hence should not select VIDEOMODE_HELPERS. In addition it uses devm_gpiod_get() and related functions from GPIOLIB. Fixes: 779679d3c164 ("drm/panel: Add support for S6E3HA8 panel driver") Signed-off-by: David Heidelberg Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260505-panel-clean-up-kconfig-dep-v2-3-9cc31d6e6919@ixit.cz Signed-off-by: Sasha Levin --- drivers/gpu/drm/panel/Kconfig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 407c5f6a268b2..1516c6fa265db 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -810,11 +810,18 @@ config DRM_PANEL_SAMSUNG_S6E3HA2 config DRM_PANEL_SAMSUNG_S6E3HA8 tristate "Samsung S6E3HA8 DSI video mode panel" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE select DRM_DISPLAY_DSC_HELPER - select VIDEOMODE_HELPERS + help + Say Y or M here if you want to enable support for the + Samsung S6E3HA8 DDIC and connected MIPI DSI panel. + Currently supported panels: + + Samsung AMB577PX01 (found in the Samsung S9 smartphone) + config DRM_PANEL_SAMSUNG_S6E63J0X03 tristate "Samsung S6E63J0X03 DSI command mode panel" From 2b553fcec1cd81921c7f7c2cd4ac4acbfe4e2352 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:54 -0700 Subject: [PATCH 0737/3297] ARM: dts: rockchip: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit 98461edf564a35ee00a97a64f5463eaece586546 ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: / { firmware { ranges; coreboot { compatible = "coreboot"; reg = <...>; ...; }; }; }; Notably, the /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson [On RK288-based Chromebooks there is no real other way than to load the DTB together with its kernel when running a mainline kernel and as the whole line is EOL, there also won't be any updates to the bootloader that could fix that issue there.] Link: https://patch.msgid.link/20260428200712.2660635-3-briannorris@chromium.org Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi b/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi index 260d6c92cfd11..ce846b9bd23fb 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron.dtsi @@ -18,6 +18,11 @@ stdout-path = "serial2:115200n8"; }; + firmware { + #address-cells = <1>; + #size-cells = <1>; + }; + /* * The default coreboot on veyron devices ignores memory@0 nodes * and would instead create another memory node. From 6173c83d4d7910b351e39426cfa87ebbffce4a7b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:53 -0700 Subject: [PATCH 0738/3297] arm64: dts: rockchip: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit 0b74f1a037672980c477bbe6b3848fb5341eb4f1 ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: ## From a RK3399 Gru/Kevin Chromebook: # find /sys/firmware/devicetree/base/firmware /sys/firmware/devicetree/base/firmware /sys/firmware/devicetree/base/firmware/coreboot /sys/firmware/devicetree/base/firmware/coreboot/ram-code /sys/firmware/devicetree/base/firmware/coreboot/compatible /sys/firmware/devicetree/base/firmware/coreboot/board-id /sys/firmware/devicetree/base/firmware/coreboot/reg /sys/firmware/devicetree/base/firmware/coreboot/name /sys/firmware/devicetree/base/firmware/chromeos /sys/firmware/devicetree/base/firmware/chromeos/readonly-firmware-version /sys/firmware/devicetree/base/firmware/chromeos/active-ec-firmware /sys/firmware/devicetree/base/firmware/chromeos/firmware-version /sys/firmware/devicetree/base/firmware/chromeos/nonvolatile-context-storage /sys/firmware/devicetree/base/firmware/chromeos/vboot-shared-data /sys/firmware/devicetree/base/firmware/chromeos/nonvolatile-context-size /sys/firmware/devicetree/base/firmware/chromeos/nonvolatile-context-offset /sys/firmware/devicetree/base/firmware/chromeos/hardware-id /sys/firmware/devicetree/base/firmware/chromeos/compatible /sys/firmware/devicetree/base/firmware/chromeos/firmware-type /sys/firmware/devicetree/base/firmware/chromeos/fmap-offset /sys/firmware/devicetree/base/firmware/chromeos/name /sys/firmware/devicetree/base/firmware/ranges /sys/firmware/devicetree/base/firmware/name The /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Reviewed-by: Chen-Yu Tsai [On RK3399-based Chromebooks there is no real other way than to load the DTB together with its kernel when running a mainline kernel and as the whole line is EOL, there also won't be any updates to the bootloader that could fix that issue there.] Link: https://patch.msgid.link/20260428200712.2660635-2-briannorris@chromium.org Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi index 7eca1da78cffa..2f9e39671efc0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi @@ -18,6 +18,11 @@ stdout-path = "serial2:115200n8"; }; + firmware { + #address-cells = <2>; + #size-cells = <2>; + }; + /* * Power Tree * From a5e026d5b94874bbc16a28942535fe19e6ad4e83 Mon Sep 17 00:00:00 2001 From: Weixin Guo <2298701336@qq.com> Date: Mon, 27 Apr 2026 21:58:04 +0800 Subject: [PATCH 0739/3297] arm64: dts: rockchip: fix rk809 interrupt pin on rk3566-roc-pc [ Upstream commit 460bac478c5fe69054ffc60d607bba03bcaf909b ] The RK809 PMIC interrupt pin on the Firefly ROC-RK3566-PC (Station M2) is physically connected to GPIO0_A3 (RK_PA3) according to the board's schematic. Currently, the PMIC node incorrectly specifies RK_PA7 for the interrupt, which prevents the PMIC from correctly signaling interrupts. (Note that the pinctrl node 'pmic_int' correctly configures RK_PA3). Fix this by updating the interrupts property to use RK_PA3. Fixes: 30ac9b4e25d8 ("arm64: dts: rockchip: add dts for Firefly Station M2 rk3566") Signed-off-by: Weixin Guo <2298701336@qq.com> Link: https://patch.msgid.link/tencent_5035EEE630C845B1B51DEA4284DE23DCCE06@qq.com Signed-off-by: Heiko Stuebner Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts index 7e499064e0357..985770e3a5e2c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts @@ -245,7 +245,7 @@ compatible = "rockchip,rk809"; reg = <0x20>; interrupt-parent = <&gpio0>; - interrupts = ; + interrupts = ; clock-output-names = "rk808-clkout1", "rk808-clkout2"; assigned-clocks = <&cru I2S1_MCLKOUT_TX>; assigned-clock-parents = <&cru CLK_I2S1_8CH_TX>; From b9c6ad49a942dd9363710d38a21244b20cb98ee8 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 1 Apr 2026 14:40:56 +0800 Subject: [PATCH 0740/3297] arm64: dts: imx8x-colibri: Correct SODIMM PAD settings [ Upstream commit 0a03ee38a262cab0d9754e9947d565e089a9f7a5 ] SION is BIT(30), not BIT(26). Correct it. Fixes: 7ece3cbc8b1ef ("arm64: dts: colibri-imx8x: Add atmel pinctrl groups") Signed-off-by: Peng Fan Reviewed-by: Daniel Baluta Reviewed-by: Alexander Stein Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi b/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi index 8e9e841cc8281..f0f04599106bb 100644 --- a/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi @@ -604,12 +604,12 @@ */ pinctrl_atmel_adap: atmeladaptergrp { fsl,pins = , /* SODIMM 30 */ - ; /* SODIMM 28 */ + ; /* SODIMM 28 */ }; /* Atmel MXT touchsceen + boards with built-in Capacitive Touch Connector */ pinctrl_atmel_conn: atmelconnectorgrp { - fsl,pins = , /* SODIMM 107 */ + fsl,pins = , /* SODIMM 107 */ ; /* SODIMM 106 */ }; From 5f7777f0da03002efe350889e9923f59554fb4ad Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 3 Apr 2026 17:57:01 +0800 Subject: [PATCH 0741/3297] Revert "arm64: dts: imx8mm-kontron: Add support for reading SD_VSEL signal" [ Upstream commit aa907ee010e396c0c0d31d60495ace6e531ceec9 ] This reverts commit 8472751c4d96b558d60d0f6aede6b24b64bcb3c9. The board uses SDHC VSELECT to automatically switch between 1.8v and 3.3v. It does not use GPIO to control the PMIC SD_VSEL signal. The original commit intends to read back SD_VSEL value from GPIO, but it is wrong. When MUX is configured as SDHC VSELECT, it is impossible to read back the value from GPIO controller. Setting SION could only enable the input path for the mux function. It could not redirect the input to GPIO. And value "0x40000d0" is wrong, SION is BIT30, not BIT26. Fixes: 8472751c4d96b ("arm64: dts: imx8mm-kontron: Add support for reading SD_VSEL signal") Signed-off-by: Peng Fan Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts | 10 +++------- .../arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi | 7 +++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts b/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts index e756fe5db56b6..dd59af0ebaae5 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-kontron-bl.dts @@ -254,10 +254,6 @@ status = "okay"; }; -®_nvcc_sd { - sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; -}; - &uart1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart1>; @@ -466,7 +462,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0 >; }; @@ -479,7 +475,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0 >; }; @@ -492,7 +488,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x19 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0xd0 >; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi index 96987910609f1..4fb13d8ecfd45 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-kontron-osm-s.dtsi @@ -342,7 +342,6 @@ regulator-name = "NVCC_SD (LDO5)"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; - sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; }; }; }; @@ -795,7 +794,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 /* SDIO_A_D2 */ MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 /* SDIO_A_D3 */ MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */ - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90 >; }; @@ -808,7 +807,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 /* SDIO_A_D2 */ MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 /* SDIO_A_D3 */ MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */ - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90 >; }; @@ -821,7 +820,7 @@ MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 /* SDIO_A_D2 */ MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 /* SDIO_A_D3 */ MX8MM_IOMUXC_SD2_WP_USDHC2_WP 0x400000d6 /* SDIO_A_WP */ - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x40000090 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x90 >; }; From 470984f1c2ed8caaabd41c57028b78cf737b24e9 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 3 Apr 2026 17:57:02 +0800 Subject: [PATCH 0742/3297] Revert "arm64: dts: imx8mp-kontron: Add support for reading SD_VSEL signal" [ Upstream commit 22465a195af370ed6311be3adee479fb7c683685 ] This reverts commit 39e4189d9d63a0b6fc15458ce0136e99ecdfb1b8. The board uses SDHC VSELECT to automatically switch between 1.8v and 3.3v. It does not use GPIO to control the PMIC SD_VSEL signal. The original commit intends to read back SD_VSEL value from GPIO, but it is wrong. When MUX is configured as SDHC VSELECT, it is impossible to read back the value from GPIO controller. Setting SION could only enable the input path for the mux function. It could not redirect the input to GPIO. Fixes: 39e4189d9d63a ("arm64: dts: imx8mp-kontron: Add support for reading SD_VSEL signal") Signed-off-by: Peng Fan Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi index bc1a261bb000e..ea69c639b30b8 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi @@ -311,7 +311,6 @@ regulator-name = "NVCC_SD (LDO5)"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; - sd-vsel-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; }; }; }; @@ -815,7 +814,7 @@ MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d0 /* SDIO_A_D1 */ MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d0 /* SDIO_A_D2 */ MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d0 /* SDIO_A_D3 */ - MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0 >; }; @@ -827,7 +826,7 @@ MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d4 /* SDIO_A_D1 */ MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d4 /* SDIO_A_D2 */ MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d4 /* SDIO_A_D3 */ - MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0 >; }; @@ -839,7 +838,7 @@ MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d6 /* SDIO_A_D1 */ MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d6 /* SDIO_A_D2 */ MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d6 /* SDIO_A_D3 */ - MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x400001d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x1d0 >; }; From 4a8cde6f7281ea2c4c290f9ad9923b3631defceb Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Sat, 2 May 2026 03:12:59 +0000 Subject: [PATCH 0743/3297] vxlan: Fix potential null-ptr-deref in vxlan_gro_prepare_receive(). [ Upstream commit 30a45c0bffdd62350261e2f2689fdba426a33578 ] udp_tunnel_sock_release() could set sk->sk_user_data to NULL while vxlan_gro_prepare_receive() is running. Let's check if rcu_dereference_sk_user_data() is NULL after skb_gro_remcsum_init(). Fixes: 5602c48cf875 ("vxlan: change vxlan to use UDP socket GRO") Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260502031401.3557229-7-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/vxlan/vxlan_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index a4ff66e354f53..b706e1777354c 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -658,14 +658,18 @@ static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk, struct sk_buff *skb, struct gro_remcsum *grc) { - struct sk_buff *p; struct vxlanhdr *vh, *vh2; unsigned int hlen, off_vx; - struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk); + struct vxlan_sock *vs; + struct sk_buff *p; __be32 flags; skb_gro_remcsum_init(grc); + vs = rcu_dereference_sk_user_data(sk); + if (!vs) + return NULL; + off_vx = skb_gro_offset(skb); hlen = off_vx + sizeof(*vh); vh = skb_gro_header(skb, hlen, off_vx); From 224d7300b5691efe453c5c39d2c6bcc0124440a3 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Fri, 1 May 2026 09:21:35 -0400 Subject: [PATCH 0744/3297] drm: renesas: rz-du: mipi_dsi: Fix return path on error [ Upstream commit 79e1afecfe1afbfd06f63bf7bbe854a88155b7bd ] In case of error, we should unwind correctly. Switching to using dmam_ instead of dma_ and moving the code earlier fixes the issue. Fixes: 6f392f371650 ("drm: renesas: rz-du: Implement MIPI DSI host transfers") Suggested-by: Pavel Machek Signed-off-by: Chris Brandt Reviewed-by: Biju Das Link: https://patch.msgid.link/20260501132135.196701-1-chris.brandt@renesas.com Signed-off-by: Biju Das Signed-off-by: Sasha Levin --- drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index b164e3a62cc2f..cbd898a182b56 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -1002,6 +1002,11 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) return dev_err_probe(dsi->dev, PTR_ERR(dsi->prstc), "failed to get prst\n"); + dsi->dcs_buf_virt = dmam_alloc_coherent(dsi->dev, RZG2L_DCS_BUF_SIZE, + &dsi->dcs_buf_phys, GFP_KERNEL); + if (!dsi->dcs_buf_virt) + return -ENOMEM; + platform_set_drvdata(pdev, dsi); pm_runtime_enable(dsi->dev); @@ -1034,11 +1039,6 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) if (ret < 0) goto err_pm_disable; - dsi->dcs_buf_virt = dma_alloc_coherent(dsi->host.dev, RZG2L_DCS_BUF_SIZE, - &dsi->dcs_buf_phys, GFP_KERNEL); - if (!dsi->dcs_buf_virt) - return -ENOMEM; - return 0; err_phy: @@ -1053,8 +1053,6 @@ static void rzg2l_mipi_dsi_remove(struct platform_device *pdev) { struct rzg2l_mipi_dsi *dsi = platform_get_drvdata(pdev); - dma_free_coherent(dsi->host.dev, RZG2L_DCS_BUF_SIZE, dsi->dcs_buf_virt, - dsi->dcs_buf_phys); mipi_dsi_host_unregister(&dsi->host); pm_runtime_disable(&pdev->dev); } From 8fe4736532639f585355b458d7b76c4153225b52 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Wed, 29 Apr 2026 16:06:35 +0800 Subject: [PATCH 0745/3297] alarmtimer: Remove stale return description from alarm_handle_timer() [ Upstream commit ed3b3c4976686b63b28e44f9805a88abc20ff18a ] alarm_handle_timer() was converted from returning enum alarmtimer_restart to void, but the kernel-doc "Return:" line was not removed. Remove the stale description. Fixes: 2634303f8773 ("alarmtimers: Remove return value from alarm functions") Signed-off-by: Zhan Xusheng Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260429080635.166790-1-zhanxusheng@xiaomi.com Signed-off-by: Sasha Levin --- kernel/time/alarmtimer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index b64db405ba5c7..189ee8bbc3e65 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -508,8 +508,6 @@ static enum alarmtimer_type clock2alarm(clockid_t clockid) * @now: time at the timer expiration * * Posix timer callback for expired alarm timers. - * - * Return: whether the timer is to be restarted */ static void alarm_handle_timer(struct alarm *alarm, ktime_t now) { From 46696b0b2123475d7f95909435c09808fc5ffd23 Mon Sep 17 00:00:00 2001 From: Di Shen Date: Mon, 27 Apr 2026 20:00:47 +0800 Subject: [PATCH 0746/3297] OPP: Fix race between OPP addition and lookup [ Upstream commit f5e1cc9a284bff2510981643a5bca4bc4c21b81a ] A race exists between dev_pm_opp_add_dynamic() and dev_pm_opp_find_freq_exact(): CPU0 (add) CPU1 (lookup) ------------------------------- ------------------------------ _opp_add() mutex_lock() list_add(&new_opp->node, head) mutex_unlock() _opp_table_find_key() mutex_lock() dev_pm_opp_get(opp) kref_get() mutex_unlock() kref_init(&new_opp->kref) dev_pm_opp_put() kref_put_mutex() The newly added OPP is inserted into the list before its kref is initialized. A concurrent lookup can find this OPP and increment its reference count while it is still uninitialized, leading to refcount corruption and a potential premature free. Fix this by initializing ->kref and ->opp_table before making the OPP visible via list_add(). This ensures any concurrent lookup observes a fully initialized object. Fixes: 7034764a1e4a (PM / OPP: Add 'struct kref' to struct dev_pm_opp) Co-developed-by: Ling Xu Signed-off-by: Ling Xu Signed-off-by: Di Shen [ Viresh: Updated commit log ] Signed-off-by: Viresh Kumar Signed-off-by: Sasha Levin --- drivers/opp/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 157c84b689feb..a179d57a5a150 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2084,11 +2084,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, return ret; list_add(&new_opp->node, head); + new_opp->opp_table = opp_table; + kref_init(&new_opp->kref); } - new_opp->opp_table = opp_table; - kref_init(&new_opp->kref); - opp_debug_create_one(new_opp, opp_table); if (!_opp_supported_by_regulators(new_opp, opp_table)) { From 25b0061adc1c1dd53d4957daaac617bd191dbcfa Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Wed, 8 Apr 2026 08:32:56 -0600 Subject: [PATCH 0747/3297] crypto: ccp - Reverse the cleanup order in psp_dev_destroy() [ Upstream commit 4a76a164ba1617f60d1c8a2fd754466c9d9e48e9 ] Before SNP x86 shutdown [1], all HV_FIXED pages were always leaked on module unload. Now pages can be reclaimed if they are freed before SNP shutdown. The SFS driver does sfs_dev_destroy() -> snp_free_hv_fixed_pages(), marking the command buffer as free. But this happens after sev_dev_destroy() in psp_dev_destroy(), so the pages are always leaked. Rearrange psp_dev_destroy() to destroy things in the reverse order from psp_init(), so that any dependencies can be unwound accordingly. This lets SFS free the page and the subsequent SNP shutdown release it. This was identified with use of Chris Mason's review-prompts: https://github.com/masoncl/review-prompts [1]: https://lore.kernel.org/all/20260324161301.1353976-1-tycho@kernel.org/ Fixes: 648dbccc03a0 ("crypto: ccp - Add AMD Seamless Firmware Servicing (SFS) driver") Reported-by: review-prompts Assisted-by: Claude:claude-4.6-opus Suggested-by: Tom Lendacky Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Ashish Kalra Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/psp-dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c index 5c7f7e02a7d8a..b14ce51065d5d 100644 --- a/drivers/crypto/ccp/psp-dev.c +++ b/drivers/crypto/ccp/psp-dev.c @@ -316,15 +316,15 @@ void psp_dev_destroy(struct sp_device *sp) if (!psp) return; - sev_dev_destroy(psp); + dbc_dev_destroy(psp); - tee_dev_destroy(psp); + platform_access_dev_destroy(psp); sfs_dev_destroy(psp); - dbc_dev_destroy(psp); + tee_dev_destroy(psp); - platform_access_dev_destroy(psp); + sev_dev_destroy(psp); sp_free_psp_irq(sp, psp); From c5c79d92da0f9a09f48be5e2aabed2d6d1a96294 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Wed, 8 Apr 2026 08:32:57 -0600 Subject: [PATCH 0748/3297] crypto: ccp - Fix snp_filter_reserved_mem_regions() off-by-one [ Upstream commit 1b864b6cb213bbd7b406e9b2e98c962077f300df ] Sashiko notes: > regarding the bounds check in snp_filter_reserved_mem_regions() > called via walk_iomem_res_desc(): does the check > if ((range_list->num_elements * 16 + 8) > PAGE_SIZE) > allow an off-by-one heap buffer overflow? > > If range_list->num_elements is 255, 255 * 16 + 8 = 4088, which is <= 4096. > Writing range->base (8 bytes) fills 4088-4095, but writing range->page_count > (4 bytes) would write to 4096-4099, overflowing the kzalloc-allocated > PAGE_SIZE buffer. Fix this by accounting for the entry about to be written to, in addition to the entries that are already allocated. Fixes: 1ca5614b84ee ("crypto: ccp: Add support to initialize the AMD-SP for SEV-SNP") Reported-by: Sashiko Assisted-by: Gemini:gemini-3.1-pro-preview Link: https://sashiko.dev/#/patchset/20260324161301.1353976-1-tycho%40kernel.org Signed-off-by: Tycho Andersen (AMD) Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index d99726d1d57f0..563418b9e8891 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1331,10 +1331,11 @@ static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg) size_t size; /* - * Ensure the list of HV_FIXED pages that will be passed to firmware - * do not exceed the page-sized argument buffer. + * Ensure the list of HV_FIXED pages passed to the firmware including + * the one about to be written to do not exceed the page-sized argument + * buffer. */ - if ((range_list->num_elements * sizeof(struct sev_data_range) + + if (((range_list->num_elements + 1) * sizeof(struct sev_data_range) + sizeof(struct sev_data_range_list)) > PAGE_SIZE) return -E2BIG; From aac63bbea8fd5a34f1a4305cb73369068f952d32 Mon Sep 17 00:00:00 2001 From: Lothar Rubusch Date: Sun, 26 Apr 2026 21:29:47 +0000 Subject: [PATCH 0749/3297] crypto: atmel-sha204a - fix blocking and non-blocking rng logic [ Upstream commit 319400fc5ee15db5793aa45f854968141326effc ] The blocking and non-blocking paths were failing to provide valid entropy due to improper buffer management. Reading the buffer starting from byte 1, only fetch the 32 bytes of random data from the return message. Tested on an Atmel SHA204A device. Before (here for blocking), tests showed repeatedly reading reduced bytes. $ head -c 32 /dev/hwrng | hexdump -C 00000000 02 28 85 b3 47 40 f2 ee 00 00 00 00 00 00 00 00 |.(..G@..........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020 After, the result will be similar to the following: $ head -c 32 /dev/hwrng | hexdump -C 00000000 5a fc 3f 13 14 68 fe 06 68 0a bd 04 83 6e 09 69 |Z.?..h..h....n.i| 00000010 75 ff cf 87 10 84 3b c9 c1 df ae eb 45 53 4c c3 |u.....;.....ESL.| 00000020 Fixes: da001fb651b0 ("crypto: atmel-i2c - add support for SHA204A random number generator") Suggested-by: Ard Biesheuvel Signed-off-by: Lothar Rubusch Tested-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/atmel-sha204a.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c index 49e545ef6493b..e189dfd471c07 100644 --- a/drivers/crypto/atmel-sha204a.c +++ b/drivers/crypto/atmel-sha204a.c @@ -54,8 +54,8 @@ static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data, if (rng->priv) { work_data = (struct atmel_i2c_work_data *)rng->priv; - max = min(sizeof(work_data->cmd.data), max); - memcpy(data, &work_data->cmd.data, max); + max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max); + memcpy(data, &work_data->cmd.data[RSP_DATA_IDX], max); rng->priv = 0; } else { work_data = kmalloc(sizeof(*work_data), GFP_ATOMIC); @@ -93,8 +93,8 @@ static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max, if (ret) return ret; - max = min(sizeof(cmd.data), max); - memcpy(data, cmd.data, max); + max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max); + memcpy(data, &cmd.data[RSP_DATA_IDX], max); return max; } From 9b21d5bd33a7f04a2f4dbc440150cc1d55a2c32b Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 2 May 2026 21:09:04 +0200 Subject: [PATCH 0750/3297] crypto: ecrdsa - fix unknown OID check in ecrdsa_param_curve [ Upstream commit 2d7b2cfc59998baf5e8622a24dc28f69a5212e06 ] The ->curve_oid check in ecrdsa_param_curve() rejects the valid enum value 0 (OID_id_dsa_with_sha1), but look_up_OID() returns OID__NR on lookup failure. Compare ->curve_oid with OID__NR instead to ensure that only unknown OIDs return -EINVAL. Fixes: 0d7a78643f69 ("crypto: ecrdsa - add EC-RDSA (GOST 34.10) algorithm") Signed-off-by: Thorsten Blum Reviewed-by: Lukas Wunner Reviewed-by: Vitaly Chikunov Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/ecrdsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c index 2c0602f0cd406..0cd7eb3676041 100644 --- a/crypto/ecrdsa.c +++ b/crypto/ecrdsa.c @@ -145,7 +145,7 @@ int ecrdsa_param_curve(void *context, size_t hdrlen, unsigned char tag, struct ecrdsa_ctx *ctx = context; ctx->curve_oid = look_up_OID(value, vlen); - if (!ctx->curve_oid) + if (ctx->curve_oid == OID__NR) return -EINVAL; ctx->curve = get_curve_by_oid(ctx->curve_oid); return 0; From 6acd2fbd00f9c72aebefce63fc2e73e8f3d79061 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 30 Apr 2026 10:36:34 -0700 Subject: [PATCH 0751/3297] crypto: asymmetric_keys - fix OOB read in pefile_digest_pe_contents [ Upstream commit f7dd32c5179d7755de18e21d5674b08f9e5cb180 ] pefile_digest_pe_contents() computes the trailing-data hash length as pelen - (hashed_bytes + certs_size). A crafted PE can make the addition exceed pelen, causing the unsigned subtraction to underflow to ~4 GiB. This is passed to crypto_shash_update() which reads out of bounds and panics on unmapped vmalloc guard pages. BUG: unable to handle page fault for address: ffffc900038d8000 Oops: Oops: 0000 [#1] SMP KASAN NOPTI RIP: 0010:sha256_blocks_generic (lib/crypto/sha256.c:152) Call Trace: __sha256_update (lib/crypto/sha256.c:208) crypto_sha256_update (crypto/sha256.c:142) verify_pefile_signature (crypto/asymmetric_keys/verify_pefile.c:436) kexec_kernel_verify_pe_sig (kernel/kexec_file.c:151) __do_sys_kexec_file_load (kernel/kexec_file.c:406) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Kernel panic - not syncing: Fatal exception Validate that the addition does not overflow and the result does not exceed pelen before the subtraction. Return -ELIBBAD on failure. Fixes: af316fc442ef ("pefile: Digest the PE binary and compare to the PKCS#7 data") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/asymmetric_keys/verify_pefile.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 1f3b227ba7f22..cec99db14129a 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -305,6 +305,8 @@ static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen, if (pelen > hashed_bytes) { tmp = hashed_bytes + ctx->certs_size; + if (tmp <= hashed_bytes || pelen < tmp) + return -ELIBBAD; ret = crypto_shash_update(desc, pebuf + hashed_bytes, pelen - tmp); From 4695cfab48f909cb760f6f64117d9c82236ab939 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Apr 2026 19:12:48 +0200 Subject: [PATCH 0752/3297] ARM: multi_v7_defconfig: Correct QCOM_RPMH and QCOM_RPMHPD [ Upstream commit 02802b40c31d2f2cfc94716189cfaf610930c589 ] QCOM_RPMH and QCOM_RPMHPD can be build only as modules when QCOM_COMMAND_DB is module itself. Fixes: 1c25ca9bb5c5 ("ARM: multi_v7_defconfig: enable more Qualcomm drivers") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/configs/multi_v7_defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 12f706e2ded54..2707c4c502264 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -1117,7 +1117,7 @@ CONFIG_QCOM_COMMAND_DB=m CONFIG_QCOM_GSBI=y CONFIG_QCOM_OCMEM=m CONFIG_QCOM_RMTFS_MEM=m -CONFIG_QCOM_RPMH=y +CONFIG_QCOM_RPMH=m CONFIG_QCOM_SMEM=y CONFIG_QCOM_SMD_RPM=y CONFIG_QCOM_SMP2P=y @@ -1135,7 +1135,7 @@ CONFIG_KEYSTONE_NAVIGATOR_QMSS=y CONFIG_KEYSTONE_NAVIGATOR_DMA=y CONFIG_RASPBERRYPI_POWER=y CONFIG_QCOM_CPR=y -CONFIG_QCOM_RPMHPD=y +CONFIG_QCOM_RPMHPD=m CONFIG_QCOM_RPMPD=y CONFIG_ROCKCHIP_PM_DOMAINS=y CONFIG_TI_SCI_PM_DOMAINS=y From 712714f818d83373847874ab0f8e426be79296cf Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 27 Apr 2026 11:59:34 -0400 Subject: [PATCH 0753/3297] dlm: fix add msg handle in send_queue ordered [ Upstream commit d2248cb70c070f8f04762872772e155b59016f17 ] In a benchmark scenario triggering a lot of requests that triggers a lot of DLM messages on the network it can be that the mh->seq is not ordered according the oldest seq number. This ordering is required by dlm_receive_ack as "before(mh->seq, seq)" will stop to check for older sequence numbers that are ordered in the tail of "node->send_queue". The side effects of not having it correct ordered regarding "before(mh->seq, seq)" are refcounting issues and use-after free. I only was able to reproduce this issue in a experimental DLM branch and a user space DLM benchmark that uses io_uring. After changing this I don't experienced any refcounting with the sending buffer issues anymore. Fixes: 489d8e559c659 ("fs: dlm: add reliable connection if reconnect") Signed-off-by: Alexander Aring Signed-off-by: David Teigland Signed-off-by: Sasha Levin --- fs/dlm/midcomms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/dlm/midcomms.c b/fs/dlm/midcomms.c index 2c101bbe261a4..2015a5f40a142 100644 --- a/fs/dlm/midcomms.c +++ b/fs/dlm/midcomms.c @@ -968,10 +968,10 @@ static void midcomms_new_msg_cb(void *data) atomic_inc(&mh->node->send_queue_cnt); spin_lock_bh(&mh->node->send_queue_lock); + /* need to be locked with list_add_tail_rcu() because list is ordered */ + mh->seq = atomic_fetch_inc(&mh->node->seq_send); list_add_tail_rcu(&mh->list, &mh->node->send_queue); spin_unlock_bh(&mh->node->send_queue_lock); - - mh->seq = atomic_fetch_inc(&mh->node->seq_send); } static struct dlm_msg *dlm_midcomms_get_msg_3_2(struct dlm_mhandle *mh, int nodeid, From f9f1a2cd912da08adab8ad25efe43c78934b3bb6 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Thu, 7 May 2026 11:50:21 -0400 Subject: [PATCH 0754/3297] nilfs2: fix backing_dev_info reference leak [ Upstream commit 665f192a2a11384cb7dc1be5f87d16438522a4ed ] setup_bdev_super() already initializes sb->s_bdev and takes a reference on the block device backing_dev_info when assigning sb->s_bdi. nilfs_fill_super() takes another reference to the same backing_dev_info and stores it in sb->s_bdi again. The extra reference is not paired with a matching bdi_put(), since generic_shutdown_super() releases sb->s_bdi only once. Drop the redundant bdi_get() in nilfs_fill_super(). The single reference taken by setup_bdev_super() is enough and is released during superblock shutdown. Fixes: c1e012ea9e83 ("nilfs2: use setup_bdev_super to de-duplicate the mount code") Signed-off-by: Shuangpeng Bai Acked-by: Ryusuke Konishi Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin --- fs/nilfs2/super.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index badc2cbc895e1..8ca3cf2ecdd27 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -1070,8 +1070,6 @@ nilfs_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_time_gran = 1; sb->s_max_links = NILFS_LINK_MAX; - sb->s_bdi = bdi_get(sb->s_bdev->bd_disk->bdi); - err = load_nilfs(nilfs, sb); if (err) goto failed_nilfs; From e725aedd26e96e35d682073e7090cd097fffbc67 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Thu, 19 Mar 2026 17:09:02 +0800 Subject: [PATCH 0755/3297] media: qcom: camss: vfe: fix PIX subdev naming on VFE lite [ Upstream commit c97e797a64cdfe4acecff831b4285418d2815893 ] VFE lite hardware does not provide a functional PIX path, but after the per sub-device type resource changes the PIX subdev name is still assigned unconditionally. Only assign the PIX subdev name on non-lite VFE variants to avoid exposing a misleading device name. Fixes: ae44829a4a97 ("media: qcom: camss: Add per sub-device type resources") Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/camss/camss-vfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index dff8d0a1e8c22..20e0d6604fa93 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -2038,7 +2038,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, v4l2_subdev_init(sd, &vfe_v4l2_ops); sd->internal_ops = &vfe_v4l2_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - if (i == VFE_LINE_PIX) + if (i == VFE_LINE_PIX && vfe->res->is_lite == false) snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", MSM_VFE_NAME, vfe->id, "pix"); else From 9e642727b97ddd4defe1315060b77335a2b2496a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:17 +0200 Subject: [PATCH 0756/3297] media: iris: scale MMCX power domain on SM8250 [ Upstream commit 4170e2f25ce40b69f3827fcdabf395380095e136 ] On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 79865252acb6 ("media: iris: enable video driver probe of SM8250 SoC") Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/iris/iris_platform_sm8250.c | 2 +- drivers/media/platform/qcom/iris/iris_probe.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c index 16486284f8acc..af841f6cd9d2a 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c @@ -265,7 +265,7 @@ static const struct bw_info sm8250_bw_table_dec[] = { static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" }; -static const char * const sm8250_opp_pd_table[] = { "mx" }; +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; static const struct platform_clk_data sm8250_clk_table[] = { {IRIS_AXI_CLK, "iface" }, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 00e99be16e087..a197833bac618 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -61,6 +61,13 @@ static int iris_init_power_domains(struct iris_core *core) return ret; ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) { + iris_opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, + &core->opp_pmdomain_tbl); + } if (ret < 0) return ret; From feb10ab7abc2445e05995f62c1d60e88c1fb30f5 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:18 +0200 Subject: [PATCH 0757/3297] media: venus: scale MMCX power domain on SM8250 [ Upstream commit f45fd3dbb5f6d60a8b31d5cc4affaf1ef40163b3 ] On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 0aeabfa29a9c ("media: venus: core: add sm8250 DT compatible and resource data") Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/venus/core.c | 7 ++++++- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/pm_helpers.c | 8 +++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index abf959b8f3a67..66a750ed16096 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -882,6 +882,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -933,6 +934,7 @@ static const struct venus_resources sc7180_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50, @@ -991,7 +993,8 @@ static const struct venus_resources sm8250_res = { .vcodec_clks_num = 1, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "mx" }, + .opp_pmdomain = (const char *[]) { "mx", "mmcx" }, + .opp_pmdomain_num = 2, .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, @@ -1053,6 +1056,7 @@ static const struct venus_resources sc7280_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, .vpu_version = VPU_VERSION_IRIS2_1, @@ -1100,6 +1104,7 @@ static const struct venus_resources qcm2290_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50_LITE, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7506f5d0f609a..70e7b40affa93 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -83,6 +83,7 @@ struct venus_resources { const char **vcodec_pmdomains; unsigned int vcodec_pmdomains_num; const char **opp_pmdomain; + unsigned int opp_pmdomain_num; unsigned int vcodec_num; const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index f0269524ac70e..14a4e8311a643 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -887,7 +887,7 @@ static int vcodec_domains_get(struct venus_core *core) }; struct dev_pm_domain_attach_data opp_pd_data = { .pd_names = res->opp_pmdomain, - .num_pd_names = 1, + .num_pd_names = res->opp_pmdomain_num, .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, }; @@ -904,6 +904,12 @@ static int vcodec_domains_get(struct venus_core *core) /* Attach the power domain for setting performance state */ ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(dev->of_node, "qcom,sm8250-venus")) { + opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + } if (ret < 0) return ret; From 0dd1cf48a4217625ecf94fccb7d7060883a1f4e9 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 14 Mar 2025 20:21:48 -0700 Subject: [PATCH 0758/3297] iommu/amd: Fix a stale comment about which legacy mode is user visible [ Upstream commit 4bf53c2d0c08bbdaa32f2114281f1ddab61902bf ] Update a stale comment about which of the legacy modes is visible to the user, i.e. can be forced via amd_iommu_intr=legacy. Fixes: b74aa02d7a30 ("iommu/amd: Fix legacy interrupt remapping for x2APIC-enabled system") Signed-off-by: Sean Christopherson Reviewed-by: Vasant Hegde Reviewed-by: Wei Wang Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/amd_iommu_types.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index 2494b1958117b..dee077bd149b7 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -987,12 +987,13 @@ static inline int get_hpet_devid(int id) } enum amd_iommu_intr_mode_type { - AMD_IOMMU_GUEST_IR_LEGACY, - - /* This mode is not visible to users. It is used when - * we cannot fully enable vAPIC and fallback to only support - * legacy interrupt remapping via 128-bit IRTE. + /* + * The legacy format mode is not visible to users to prevent the user + * from crashing x2APIC systems, which for all intents and purposes + * require 128-bit IRTEs. The legacy format will be forced as needed + * when hardware doesn't support 128-bit IRTEs. */ + AMD_IOMMU_GUEST_IR_LEGACY, AMD_IOMMU_GUEST_IR_LEGACY_GA, AMD_IOMMU_GUEST_IR_VAPIC, }; From 8960088511ca168fc7d18977d93cc368357ad390 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Tue, 5 May 2026 22:13:16 +0100 Subject: [PATCH 0759/3297] soc: mediatek: mtk-mmsys: Restore MT8167 routing masks lost during merge [ Upstream commit 7d462de9f65b002b439b1b168bf3b5579b0de48b ] The original patch that was sent to the mailing lists included the values for the route masks, but they got lost during merge: add back the full register masks where missing. Fixes: 060f7875bd23 ("soc: mediatek: mmsys: Add support for MT8167 SoC") Signed-off-by: Luca Leonardo Scorcia Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Sasha Levin --- drivers/soc/mediatek/mt8167-mmsys.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/soc/mediatek/mt8167-mmsys.h b/drivers/soc/mediatek/mt8167-mmsys.h index c468926561b47..eef14083c47b5 100644 --- a/drivers/soc/mediatek/mt8167-mmsys.h +++ b/drivers/soc/mediatek/mt8167-mmsys.h @@ -10,24 +10,29 @@ #define MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN 0x06c #define MT8167_DITHER_MOUT_EN_RDMA0 0x1 +#define MT8167_DITHER_MOUT_EN_MASK 0x7 + #define MT8167_RDMA0_SOUT_DSI0 0x2 +#define MT8167_RDMA0_SOUT_MASK 0x3 + #define MT8167_DSI0_SEL_IN_RDMA0 0x1 +#define MT8167_DSI0_SEL_IN_MASK 0x3 static const struct mtk_mmsys_routes mt8167_mmsys_routing_table[] = { MMSYS_ROUTE(OVL0, COLOR0, MT8167_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0, OVL0_MOUT_EN_COLOR0), MMSYS_ROUTE(DITHER0, RDMA0, - MT8167_DISP_REG_CONFIG_DISP_DITHER_MOUT_EN, MT8167_DITHER_MOUT_EN_RDMA0, + MT8167_DISP_REG_CONFIG_DISP_DITHER_MOUT_EN, MT8167_DITHER_MOUT_EN_MASK, MT8167_DITHER_MOUT_EN_RDMA0), MMSYS_ROUTE(OVL0, COLOR0, MT8167_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0, COLOR0_SEL_IN_OVL0), MMSYS_ROUTE(RDMA0, DSI0, - MT8167_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8167_DSI0_SEL_IN_RDMA0, + MT8167_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8167_DSI0_SEL_IN_MASK, MT8167_DSI0_SEL_IN_RDMA0), MMSYS_ROUTE(RDMA0, DSI0, - MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8167_RDMA0_SOUT_DSI0, + MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8167_RDMA0_SOUT_MASK, MT8167_RDMA0_SOUT_DSI0), }; From e003f3a4be738f650e283e92d07017c3fef8da52 Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Thu, 30 Apr 2026 07:38:36 +0000 Subject: [PATCH 0760/3297] bpf: fix crash in bpf_[set|remove]_dentry_xattr for negative dentries [ Upstream commit 07410646f6ff1d23222f105ccab778957d401bbe ] bpf_set_dentry_xattr and bpf_remove_dentry_xattr BPF kfuncs attempt to lock the inode of the supplied dentry without checking if it is NULL. If a negative dentry is passed (e.g. from security_inode_create), d_inode(dentry) returns NULL, and inode_lock(inode) will cause a NULL pointer dereference. Trivially fix this by adding a NULL check for inode before attempting to lock it, returning -EINVAL if it is NULL. Additionally, drop WARN_ON(!inode) in bpf_xattr_read_permission() and bpf_xattr_write_permission(). These warnings could be triggered by passing a negative dentry to bpf_get_dentry_xattr() or the _locked variants of the xattr kfuncs, potentially causing a Denial of Service on systems with panic_on_warn enabled. Instead, simply return -EINVAL. Reported-by: Quan Sun <2022090917019@std.uestc.edu.cn> Closes: https://lore.kernel.org/bpf/1587cbf4-1293-4e25-ad24-c970836a1686@std.uestc.edu.cn/ Fixes: 56467292794b ("bpf: fs/xattr: Add BPF kfuncs to set and remove xattrs") Signed-off-by: Matt Bobrowski Link: https://patch.msgid.link/20260430073836.2894001-1-mattbobrowski@google.com Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/bpf_fs_kfuncs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 5ace2511fec51..b5a0736b120fa 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -103,7 +103,7 @@ static bool match_security_bpf_prefix(const char *name__str) static int bpf_xattr_read_permission(const char *name, struct inode *inode) { - if (WARN_ON(!inode)) + if (!inode) return -EINVAL; /* Allow reading xattr with user. and security.bpf. prefix */ @@ -173,7 +173,7 @@ __bpf_kfunc_end_defs(); static int bpf_xattr_write_permission(const char *name, struct inode *inode) { - if (WARN_ON(!inode)) + if (!inode) return -EINVAL; /* Only allow setting and removing security.bpf. xattrs */ @@ -292,6 +292,9 @@ __bpf_kfunc int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__st struct inode *inode = d_inode(dentry); int ret; + if (!inode) + return -EINVAL; + inode_lock(inode); ret = bpf_set_dentry_xattr_locked(dentry, name__str, value_p, flags); inode_unlock(inode); @@ -317,6 +320,9 @@ __bpf_kfunc int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name_ struct inode *inode = d_inode(dentry); int ret; + if (!inode) + return -EINVAL; + inode_lock(inode); ret = bpf_remove_dentry_xattr_locked(dentry, name__str); inode_unlock(inode); From 02367b12b303a2883d05a300ff3dd27c23dec5ad Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 7 Apr 2026 18:03:13 +0200 Subject: [PATCH 0761/3297] uaccess: fix ignored_trailing logic in copy_struct_to_user() [ Upstream commit 4911de3145a797389577abfdf9a5185d36cc18d7 ] Currently all callers pass ignored_trailing=NULL, but I have code that will make use of. Now it actually behaves like documented: * If @usize < @ksize, then the kernel is trying to pass userspace a newer struct than it supports. Thus we only copy the interoperable portions (@usize) and ignore the rest (but @ignored_trailing is set to %true if any of the trailing (@ksize - @usize) bytes are non-zero). Fixes: 424a55a4a908 ("uaccess: add copy_struct_to_user helper") Cc: Dmitry Safonov <0x7f454c46@gmail.com> Cc: Dmitry Safonov Cc: Francesco Ruggeri Cc: Salam Noureddine Cc: David Ahern Cc: David S. Miller Cc: Michal Luczaj Cc: David Wei Cc: Luiz Augusto von Dentz Cc: Luiz Augusto von Dentz Cc: Marcel Holtmann Cc: Xin Long Cc: Eric Dumazet Cc: Kuniyuki Iwashima Cc: Paolo Abeni Cc: Willem de Bruijn Cc: Neal Cardwell Cc: Jakub Kicinski Cc: Simon Horman Cc: Aleksa Sarai Cc: Christian Brauner CC: Kees Cook Cc: netdev@vger.kernel.org Cc: linux-bluetooth@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Stefan Metzmacher Link: https://patch.msgid.link/71f69442410c1186ed8ce6d5b4b9d4a5a70edbad.1775576651.git.metze@samba.org Reviewed-by: Aleksa Sarai Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- include/linux/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 7657904c8db9c..6973fee49f091 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -499,7 +499,7 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src, return -EFAULT; } if (ignored_trailing) - *ignored_trailing = ksize < usize && + *ignored_trailing = usize < ksize && memchr_inv(src + size, 0, rest) != NULL; /* Copy the interoperable parts of the struct. */ if (copy_to_user(dst, src, size)) From 9fe7605c1c14394e87e8761fe7f0581e6a333491 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 30 Apr 2026 20:07:24 +0800 Subject: [PATCH 0762/3297] arm64: dts: mediatek: mt8192-asurada: Move PCIe DMA bounce buffer to host [ Upstream commit 533275a4b5240a2bf2c592bd4536560388306d26 ] The DMA bounce buffer is attached to the PCIe host controller, i.e. all PCIe DMA transfers should use it. Move it from the PCIe (WiFi) device node down to the PCIe host controller node. Fixes: 0dca9f0b3e63 ("arm64: dts: mediatek: asurada: Enable PCIe and add WiFi") Signed-off-by: Chen-Yu Tsai Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi index 0b4664f044a15..ff2eb9e10f364 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi @@ -526,6 +526,7 @@ &pcie { pinctrl-names = "default"; pinctrl-0 = <&pcie_pins>; + memory-region = <&wifi_restricted_dma_region>; pcie0: pcie@0,0 { device_type = "pci"; @@ -540,7 +541,6 @@ wifi: wifi@0,0 { reg = <0x10000 0 0 0 0x100000>, <0x10000 0 0x100000 0 0x100000>; - memory-region = <&wifi_restricted_dma_region>; }; }; }; From 8200ffd8259f0944cacd37e64b42f327932edaf2 Mon Sep 17 00:00:00 2001 From: Hsiu Che Yu Date: Sat, 25 Apr 2026 18:16:19 +0800 Subject: [PATCH 0763/3297] rust: alloc: fix `Vec::extend_with` SAFETY comment [ Upstream commit f497aae6ded43f91b1dbf29a35d7062823635640 ] Fix an incorrect operator in the SAFETY comment, changing `<` to `<=`, since `Vec::reserve` guarantees capacity for exactly n additional elements, so the equal case should be included. Signed-off-by: Hsiu Che Yu Reviewed-by: Alexandre Courbot Fixes: 2aac4cd7dae3d ("rust: alloc: implement kernel `Vec` type") Link: https://patch.msgid.link/18fc8eee2f057a6bfbcadae156d1d0b7c40d0077.1777111268.git.yu.whisper.personal@gmail.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- rust/kernel/alloc/kvec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index ac8d6f763ae81..b6a97b8fb5121 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -754,7 +754,7 @@ impl Vec { spare[n - 1].write(value); // SAFETY: - // - `self.len() + n < self.capacity()` due to the call to reserve above, + // - `self.len() + n <= self.capacity()` due to the call to reserve above, // - the loop and the line above initialized the next `n` elements. unsafe { self.inc_len(n) }; From 2e2e5888764bab89e040fbb43bef508622d5f4ca Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Fri, 8 May 2026 16:32:46 +0100 Subject: [PATCH 0764/3297] clk: scmi: Fix clock rate rounding [ Upstream commit d0c81a38d06d446d341532700b3a4a43d3b00eb1 ] While the do_div() helper used for rounding expects its divisor argument to be a 32bits quantity, the currently provided divisor parameter is a 64bit value that, as a consequence, is silently truncated and a possible source of bugs. Fix by using the proper div64_ul helper. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Fixes: 7a8655e19bdb ("clk: scmi: Fix the rounding of clock rate") Signed-off-by: Cristian Marussi Tested-by: Florian Fainelli Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260508153300.2224715-2-cristian.marussi@arm.com Reviewed-by: Brian Masney Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/clk/clk-scmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 6b286ea6f1218..b6a12f3bc123c 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -10,9 +10,9 @@ #include #include #include +#include #include #include -#include #define NOT_ATOMIC false #define ATOMIC true @@ -83,7 +83,7 @@ static int scmi_clk_determine_rate(struct clk_hw *hw, ftmp = req->rate - fmin; ftmp += clk->info->range.step_size - 1; /* to round up */ - do_div(ftmp, clk->info->range.step_size); + ftmp = div64_ul(ftmp, clk->info->range.step_size); req->rate = ftmp * clk->info->range.step_size + fmin; From 886fb63981a921d9aa9678ed292dee084795316c Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Thu, 2 Apr 2026 00:05:09 +0530 Subject: [PATCH 0765/3297] arm64: dts: qcom: kodiak: Fix ICE reg size [ Upstream commit fa9c7403af64d3e56a6c20dedc60e0a55b527509 ] The ICE register region on Kodiak is currently defined as 0x8000 bytes. According to the hardware specification, the correct register size is 0x18000. Update the ICE node reg property to match the hardware. Fixes: dfd5ee7b34bb ("arm64: dts: qcom: sc7280: Add inline crypto engine") Signed-off-by: Kuldeep Singh Reviewed-by: Harshal Dev Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260402-ice_dt_reg_fix-v1-1-74e4c2129238@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc7280.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index 4b04dea57ec8c..07cabbb2058f8 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -2573,7 +2573,7 @@ ice: crypto@1d88000 { compatible = "qcom,sc7280-inline-crypto-engine", "qcom,inline-crypto-engine"; - reg = <0 0x01d88000 0 0x8000>; + reg = <0 0x01d88000 0 0x18000>; clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; }; From 27af16a44f59075dd908d15fa3d05afa366c1f4f Mon Sep 17 00:00:00 2001 From: Kuldeep Singh Date: Thu, 2 Apr 2026 00:05:10 +0530 Subject: [PATCH 0766/3297] arm64: dts: qcom: sm8450: Fix ICE reg size [ Upstream commit 7fb3d0512dacc0d09217eb45057357f00298203d ] The ICE register region size was originally described incorrectly when the ICE hardware was first introduced. The same value was later carried over unchanged when the ICE node was split out from the UFS node into its own DT entry. Correct the register size to match the hardware specification. Fixes: 276ee34a40c1 ("arm64: dts: qcom: sm8450: add Inline Crypto Engine registers and clock") Signed-off-by: Kuldeep Singh Reviewed-by: Harshal Dev Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260402-ice_dt_reg_fix-v1-2-74e4c2129238@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8450.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index 6c00390010f1f..3a352e5f3f191 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -5348,7 +5348,7 @@ ice: crypto@1d88000 { compatible = "qcom,sm8450-inline-crypto-engine", "qcom,inline-crypto-engine"; - reg = <0 0x01d88000 0 0x8000>; + reg = <0 0x01d88000 0 0x18000>; clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; }; From 689bb48c828caa1ed1fe67009679e46c13818aed Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:22:59 +0800 Subject: [PATCH 0767/3297] drm/hisilicon/hibmc: add updating link cap in DP detect() [ Upstream commit 7d380ef98dd469747b6df43fb0243301b0caaacf ] In the past, the link cap is updated in link training at encoder enable stage, but the hibmc_dp_mode_valid() is called before it, which will use DP link's rate and lanes. So add the hibmc_dp_update_caps() in hibmc_dp_update_caps() to avoid some potential risks. Fixes: 607805abfb74 ("drm/hisilicon/hibmc: add dp mode valid check") Signed-off-by: Lin He Signed-off-by: Yongbang Shi Acked-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-2-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 1 + drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c | 2 +- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h index f9ee7ebfec55c..f53dac256ee0b 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -69,5 +69,6 @@ int hibmc_dp_link_training(struct hibmc_dp_dev *dp); int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp); int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp); int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]); +void hibmc_dp_update_caps(struct hibmc_dp_dev *dp); #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c index 0726cb5b736e6..8c53f16db5160 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c @@ -325,7 +325,7 @@ static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp) return hibmc_dp_link_reduce_rate(dp); } -static void hibmc_dp_update_caps(struct hibmc_dp_dev *dp) +void hibmc_dp_update_caps(struct hibmc_dp_dev *dp) { dp->link.cap.link_rate = dp->dpcd[DP_MAX_LINK_RATE]; if (dp->link.cap.link_rate > DP_LINK_BW_8_1 || !dp->link.cap.link_rate) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index 616821e3c933b..35dff7bfbf76f 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -41,6 +41,8 @@ static bool hibmc_dp_get_dpcd(struct hibmc_dp_dev *dp_dev) if (ret) return false; + hibmc_dp_update_caps(dp_dev); + dp_dev->is_branch = drm_dp_is_branch(dp_dev->dpcd); ret = drm_dp_read_desc(dp_dev->aux, &dp_dev->desc, dp_dev->is_branch); From 94ab8a6e02bae0eb094053bd54e73653133e0ec3 Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:23:00 +0800 Subject: [PATCH 0768/3297] drm/hisilicon/hibmc: fix no showing when no connectors connected [ Upstream commit 4110e29a7c384f1ff21d6b187b6a1772f6e17b23 ] Our chip support KVM over IP feature, so hibmc driver need to support displaying without any connectors plugged in. If no connectors are connected, the vdac connector status should be set to 'connected' to ensure proper KVM display functionality. Additionally, for previous-generation products that may lack hardware link support and thus cannot detect the monitor, the same approach should be applied to ensure VGA display functionality. * Add phys_state in the struct of dp and vdac to check physical outputs. * The 'epoch_counter' of the vdac connector is incremented when the physical status changes. For get_modes: using BMC modes for connector if no display is attached to phys VGA cable, otherwise use EDID modes by drm_connector_helper_get_modes, because KVM doesn't provide EDID reads. The polling mechanism for the KMS helper is enabled. Fixes: 4c962bc929f1 ("drm/hisilicon/hibmc: Add vga connector detect functions") Reported-by: Thomas Zimmermann Closes: https://lore.kernel.org/all/0eb5c509-2724-4c57-87ad-74e4270d5a5a@suse.de/ Signed-off-by: Lin He Signed-off-by: Yongbang Shi Reviewed-by: Thomas Zimmermann Tested-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-3-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 1 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 33 ++++++++---- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 3 ++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 1 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 53 +++++++++++++------ 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index 31316fe1ea8db..0f3662d8737e9 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -55,6 +55,7 @@ struct hibmc_dp { struct drm_dp_aux aux; struct hibmc_dp_cbar_cfg cfg; u32 irq_status; + int phys_status; }; int hibmc_dp_hw_init(struct hibmc_dp *dp); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index 35dff7bfbf76f..596c5bfe32d8e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -61,27 +61,38 @@ static int hibmc_dp_detect(struct drm_connector *connector, { struct hibmc_dp *dp = to_hibmc_dp(connector); struct hibmc_dp_dev *dp_dev = dp->dp_dev; - int ret; + int ret = connector_status_disconnected; if (dp->irq_status) { - if (dp_dev->hpd_status != HIBMC_HPD_IN) - return connector_status_disconnected; + if (dp_dev->hpd_status != HIBMC_HPD_IN) { + ret = connector_status_disconnected; + goto exit; + } } - if (!hibmc_dp_get_dpcd(dp_dev)) - return connector_status_disconnected; + if (!hibmc_dp_get_dpcd(dp_dev)) { + ret = connector_status_disconnected; + goto exit; + } - if (!dp_dev->is_branch) - return connector_status_connected; + if (!dp_dev->is_branch) { + ret = connector_status_connected; + goto exit; + } if (drm_dp_read_sink_count_cap(connector, dp_dev->dpcd, &dp_dev->desc) && dp_dev->downstream_ports[0] & DP_DS_PORT_HPD) { ret = drm_dp_read_sink_count(dp_dev->aux); - if (ret > 0) - return connector_status_connected; + if (ret > 0) { + ret = connector_status_connected; + goto exit; + } } - return connector_status_disconnected; +exit: + dp->phys_status = ret; + + return ret; } static int hibmc_dp_mode_valid(struct drm_connector *connector, @@ -243,5 +254,7 @@ int hibmc_dp_init(struct hibmc_drm_private *priv) connector->polled = DRM_CONNECTOR_POLL_HPD; + dp->phys_status = connector_status_disconnected; + return 0; } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 289304500ab09..481e5e62c30bf 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "hibmc_drm_drv.h" #include "hibmc_drm_regs.h" @@ -355,6 +356,8 @@ static int hibmc_load(struct drm_device *dev) /* reset all the states of crtc/plane/encoder/connector */ drm_mode_config_reset(dev); + drmm_kms_helper_poll_init(dev); + return 0; err: diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index ca8502e2760c1..cd3a3fca1fe60 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -31,6 +31,7 @@ struct hibmc_vdac { struct drm_connector connector; struct i2c_adapter adapter; struct i2c_algo_bit_data bit_data; + int phys_status; }; struct hibmc_drm_private { diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 841e81f47b686..2fcfa3246fd12 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -24,28 +24,21 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) { + struct drm_mode_config *mode_config = &connector->dev->mode_config; struct hibmc_vdac *vdac = to_hibmc_vdac(connector); - const struct drm_edid *drm_edid; int count; - drm_edid = drm_edid_read_ddc(connector, &vdac->adapter); - - drm_edid_connector_update(connector, drm_edid); - - if (drm_edid) { - count = drm_edid_connector_add_modes(connector); + if (vdac->phys_status == connector_status_connected) { + count = drm_connector_helper_get_modes(connector); + } else { + drm_edid_connector_update(connector, NULL); + count = drm_add_modes_noedid(connector, + mode_config->max_width, + mode_config->max_height); if (count) - goto out; + drm_set_preferred_mode(connector, 1024, 768); } - count = drm_add_modes_noedid(connector, - connector->dev->mode_config.max_width, - connector->dev->mode_config.max_height); - drm_set_preferred_mode(connector, 1024, 768); - -out: - drm_edid_free(drm_edid); - return count; } @@ -57,10 +50,34 @@ static void hibmc_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } +static int hibmc_vdac_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(connector->dev); + int status = drm_connector_helper_detect_from_ddc(connector, ctx, + force); + struct hibmc_vdac *vdac = to_hibmc_vdac(connector); + + if (priv->dp.phys_status == connector_status_connected) { + vdac->phys_status = status; + return status; + } + + if (status != vdac->phys_status) + ++connector->epoch_counter; + vdac->phys_status = status; + + /* When both the DP and VDAC physical status are disconnected, + * the "connected" status is returned to support KVM display. + */ + return connector_status_connected; +} + static const struct drm_connector_helper_funcs hibmc_connector_helper_funcs = { .get_modes = hibmc_connector_get_modes, - .detect_ctx = drm_connector_helper_detect_from_ddc, + .detect_ctx = hibmc_vdac_detect, }; static const struct drm_connector_funcs hibmc_connector_funcs = { @@ -130,6 +147,8 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + vdac->phys_status = connector_status_disconnected; + return 0; err: From 28b91fd2adc4f57485537a85a9348ad27bdc4dd9 Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:23:01 +0800 Subject: [PATCH 0769/3297] drm/hisilicon/hibmc: move display contrl config to hibmc_probe() [ Upstream commit de2a56c71f9b223922ea8fe0192d3c1fa2954608 ] If there's no VGA output, this encoder modeset won't be called, which will cause displaying data from GPU being cut off. It's actually a common display config for DP and VGA, so move the vdac encoder modeset to driver load stage. Removed invalid bit configurations from `hibmc_display_ctrl` Fixes: 5294967f4ae4 ("drm/hisilicon/hibmc: Add support for VDAC") Signed-off-by: Lin He Signed-off-by: Yongbang Shi Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-4-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 11 ++++++++++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 22 ------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 481e5e62c30bf..99b36de1fe137 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -215,6 +215,15 @@ void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate) writel(gate, mmio + gate_reg); } +static void hibmc_display_ctrl(struct hibmc_drm_private *priv) +{ + u32 reg; + + reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); + reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1); + writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); +} + static void hibmc_hw_config(struct hibmc_drm_private *priv) { u32 reg; @@ -246,6 +255,8 @@ static void hibmc_hw_config(struct hibmc_drm_private *priv) reg |= HIBMC_MSCCTL_LOCALMEM_RESET(1); writel(reg, priv->mmio + HIBMC_MISC_CTRL); + + hibmc_display_ctrl(priv); } static int hibmc_hw_map(struct hibmc_drm_private *priv) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 2fcfa3246fd12..b9bd6d33fb0f7 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -88,26 +88,6 @@ static const struct drm_connector_funcs hibmc_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static void hibmc_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) -{ - u32 reg; - struct drm_device *dev = encoder->dev; - struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); - - reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); - reg |= HIBMC_DISPLAY_CONTROL_FPVDDEN(1); - reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1); - reg |= HIBMC_DISPLAY_CONTROL_FPEN(1); - reg |= HIBMC_DISPLAY_CONTROL_VBIASEN(1); - writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); -} - -static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = { - .mode_set = hibmc_encoder_mode_set, -}; - int hibmc_vdac_init(struct hibmc_drm_private *priv) { struct drm_device *dev = &priv->dev; @@ -130,8 +110,6 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) goto err; } - drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs); - ret = drm_connector_init_with_ddc(dev, connector, &hibmc_connector_funcs, DRM_MODE_CONNECTOR_VGA, From 4c57df82af833368bed790e6e9f3641f265c97d0 Mon Sep 17 00:00:00 2001 From: Lin He Date: Sat, 9 May 2026 11:23:02 +0800 Subject: [PATCH 0770/3297] drm/hisilicon/hibmc: use clock to look up the PLL value [ Upstream commit 99ef3a4f1c1813cabd50bb3e8522e85e00838bb2 ] In the past, we use width and height to look up our PLL value. But actually the actual clock check is also necessnary. There are some resolutions that width and height same, but its clock different. Add the clock check when using pll_table to determine the PLL value. Fixes: da52605eea8f ("drm/hisilicon/hibmc: Add support for display engine") Signed-off-by: Lin He Signed-off-by: Yongbang Shi Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patch.msgid.link/20260509032302.2057227-5-shiyongbang@huawei.com Signed-off-by: Sasha Levin --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 80 +++++++++++-------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 89bed78f14666..db7fce4e8cc34 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -32,26 +32,43 @@ struct hibmc_display_panel_pll { struct hibmc_dislay_pll_config { u64 hdisplay; u64 vdisplay; + int clock; u32 pll1_config_value; u32 pll2_config_value; }; static const struct hibmc_dislay_pll_config hibmc_pll_table[] = { - {640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, - {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, - {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, - {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, - {1280, 768, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ}, - {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, - {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, - {1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, - {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, - {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, - {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, + {640, 480, 25000, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, + {800, 600, 40000, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, + {1024, 768, 65000, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, + {1152, 864, 78750, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, + {1280, 768, 80000, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ}, + {1280, 720, 74375, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, + {1280, 960, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1280, 1024, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1440, 900, 105952, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, + {1600, 900, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1600, 1200, 162500, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, + {1920, 1080, 148750, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, + {1920, 1200, 193750, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, }; +static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode) +{ + int i, diff; + + for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { + if (hibmc_pll_table[i].hdisplay == mode->hdisplay && + hibmc_pll_table[i].vdisplay == mode->vdisplay) { + diff = abs(mode->clock - hibmc_pll_table[i].clock); + if (diff < mode->clock / 100) /* tolerance 1/100 */ + return i; + } + } + + return -MODE_CLOCK_RANGE; +} + static int hibmc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -214,19 +231,15 @@ static enum drm_mode_status hibmc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) { - size_t i = 0; int vrefresh = drm_mode_vrefresh(mode); if (vrefresh < 59 || vrefresh > 61) return MODE_NOCLOCK; - for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { - if (hibmc_pll_table[i].hdisplay == mode->hdisplay && - hibmc_pll_table[i].vdisplay == mode->vdisplay) - return MODE_OK; - } + if (hibmc_get_best_clock_idx(mode) >= 0) + return MODE_OK; - return MODE_BAD; + return MODE_CLOCK_RANGE; } static u32 format_pll_reg(void) @@ -281,23 +294,20 @@ static void set_vclock_hisilicon(struct drm_device *dev, u64 pll) writel(val, priv->mmio + CRT_PLL1_HS); } -static void get_pll_config(u64 x, u64 y, u32 *pll1, u32 *pll2) +static void get_pll_config(struct drm_display_mode *mode, u32 *pll1, u32 *pll2) { - size_t i; - size_t count = ARRAY_SIZE(hibmc_pll_table); - - for (i = 0; i < count; i++) { - if (hibmc_pll_table[i].hdisplay == x && - hibmc_pll_table[i].vdisplay == y) { - *pll1 = hibmc_pll_table[i].pll1_config_value; - *pll2 = hibmc_pll_table[i].pll2_config_value; - return; - } + int idx; + + idx = hibmc_get_best_clock_idx(mode); + if (idx < 0) { + /* if found none, we use default value */ + *pll1 = CRT_PLL1_HS_25MHZ; + *pll2 = CRT_PLL2_HS_25MHZ; + return; } - /* if found none, we use default value */ - *pll1 = CRT_PLL1_HS_25MHZ; - *pll2 = CRT_PLL2_HS_25MHZ; + *pll1 = hibmc_pll_table[idx].pll1_config_value; + *pll2 = hibmc_pll_table[idx].pll2_config_value; } /* @@ -319,7 +329,7 @@ static u32 display_ctrl_adjust(struct drm_device *dev, x = mode->hdisplay; y = mode->vdisplay; - get_pll_config(x, y, &pll1, &pll2); + get_pll_config(mode, &pll1, &pll2); writel(pll2, priv->mmio + CRT_PLL2_HS); set_vclock_hisilicon(dev, pll1); From a0f64cf8bfcb39e533d9e27aeb763a8d81b7f39d Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 23 Apr 2026 23:30:00 +0800 Subject: [PATCH 0771/3297] evm: terminate and bound the evm_xattrs read buffer [ Upstream commit 11143a19f5b8dc8f414deab87571134f9f447313 ] evm_read_xattrs() allocates size + 1 bytes, fills them from the list of enabled xattrs, and then passes strlen(temp) to simple_read_from_buffer(). When no configured xattrs are enabled, the fill loop stores nothing and temp[0] remains uninitialized, so strlen() reads beyond initialized memory. Explicitly terminate the buffer after allocation, use snprintf() for each formatted line, and pass the accumulated length, without risk of truncation, to simple_read_from_buffer(). Fixes: fa516b66a1bf ("EVM: Allow runtime modification of the set of verified xattrs") Signed-off-by: Pengpeng Hou Reviewed-by: Roberto Sassu Signed-off-by: Mimi Zohar Signed-off-by: Sasha Levin --- security/integrity/evm/evm_secfs.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index b0d2aad278507..0611a75310682 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -127,8 +127,8 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { char *temp; - int offset = 0; - ssize_t rc, size = 0; + size_t offset = 0, size = 0; + ssize_t rc; struct xattr_list *xattr; if (*ppos != 0) @@ -151,16 +151,22 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, return -ENOMEM; } + temp[size] = '\0'; + + /* + * No truncation possible: size is computed over the same enabled + * xattrs under xattr_list_mutex, so offset never exceeds size. + */ list_for_each_entry(xattr, &evm_config_xattrnames, list) { if (!xattr->enabled) continue; - sprintf(temp + offset, "%s\n", xattr->name); - offset += strlen(xattr->name) + 1; + offset += snprintf(temp + offset, size + 1 - offset, "%s\n", + xattr->name); } mutex_unlock(&xattr_list_mutex); - rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + rc = simple_read_from_buffer(buf, count, ppos, temp, offset); kfree(temp); From 69bc4a3998742479d0097ae7d15fd61f5da4fbd2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 5 May 2026 13:36:56 +0200 Subject: [PATCH 0772/3297] thermal: hwmon: Fix critical temperature attribute removal [ Upstream commit c2114dbda05354dbcf4dfbb30a2c623e8611c43a ] Since the return value of thermal_zone_crit_temp_valid() depends on the behavior of the thermal zone .get_crit_temp() callback which may change over time in theory, thermal_remove_hwmon_sysfs() may attempt to remove a critical temperature attribute that has not been created, passing a pointer to an uninitialized attribute structure to device_remove_file(). To avoid that, set a flag in struct thermal_hwmon_temp after creating a critical temperature attribute and use the value of that flag to decide whether or not the attribute needs to be removed. Fixes: e8db5d6736a7 ("thermal: hwmon: Make the check for critical temp valid consistent") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2437056.ElGaqSPkdT@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/thermal_hwmon.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 64cc3ab949fed..c68b27b2fb027 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -40,6 +40,7 @@ struct thermal_hwmon_temp { struct thermal_zone_device *tz; struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ + bool temp_crit_present; }; static LIST_HEAD(thermal_hwmon_list); @@ -191,6 +192,8 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) &temp->temp_crit.attr); if (result) goto unregister_input; + + temp->temp_crit_present = true; } mutex_lock(&thermal_hwmon_list_lock); @@ -235,7 +238,7 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) } device_remove_file(hwmon->device, &temp->temp_input.attr); - if (thermal_zone_crit_temp_valid(tz)) + if (temp->temp_crit_present) device_remove_file(hwmon->device, &temp->temp_crit.attr); mutex_lock(&thermal_hwmon_list_lock); From d29b9c38f6eb4fdf984ec74f7782d7abd7f89483 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Wed, 13 May 2026 14:09:00 +0500 Subject: [PATCH 0773/3297] clk: scpi: Unregister child clock providers on remove [ Upstream commit b79d9b5747d961516c35ef4d5e91efa579fd3e9a ] SCPI clock providers are registered for each child node in scpi_clk_add(), but scpi_clocks_remove() unregisters the parent node on each iteration. of_clk_del_provider() matches providers by the node used at registration time, so passing the parent node leaves the child providers registered. This leaks the provider allocations and the node references held by the clock provider core. Pass the child node to of_clk_del_provider() so the remove path matches the probe path. Fixes: cd52c2a4b5c4 ("clk: add support for clocks provided by SCP(System Control Processor)") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260513090900.5323-1-sozdayvek@gmail.com (sudeep.holla: Updated commit title and message a bit) Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/clk/clk-scpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c index 0b592de7bdb2a..e082474250fe3 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -258,7 +258,7 @@ static void scpi_clocks_remove(struct platform_device *pdev) } for_each_available_child_of_node(np, child) - of_clk_del_provider(np); + of_clk_del_provider(child); } static int scpi_clocks_probe(struct platform_device *pdev) From 8b42290df1765670ee8a31975d9798bb9fed078d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 13 May 2026 08:08:53 +0000 Subject: [PATCH 0774/3297] net/sched: sch_hfsc: annotate data-races in hfsc_dump_class_stats() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e300c7d470ad2726d6abf7d11b31b5d9912d9cf0 ] hfsc_dump_class_stats() runs without qdisc spinlock being held. Add READ_ONCE()/WRITE_ONCE() annotations around: - cl->level - cl->cl_vtperiod - cl->cl_total - cl->cl_cumul Fixes: edb09eb17ed8 ("net: sched: do not acquire qdisc spinlock in qdisc/class stats dump") Signed-off-by: Eric Dumazet Reviewed-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260513080853.1383975-4-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_hfsc.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 57221522fe56d..9c6e6d2b47f8e 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -715,7 +715,7 @@ init_vf(struct hfsc_class *cl, unsigned int len) rtsc_min(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total); cl->cl_vtadj = 0; - cl->cl_vtperiod++; /* increment vt period */ + WRITE_ONCE(cl->cl_vtperiod, cl->cl_vtperiod + 1); /* increment vt period */ cl->cl_parentperiod = cl->cl_parent->cl_vtperiod; if (cl->cl_parent->cl_nactive == 0) cl->cl_parentperiod++; @@ -757,7 +757,7 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time) go_passive = 1; for (; cl->cl_parent != NULL; cl = cl->cl_parent) { - cl->cl_total += len; + WRITE_ONCE(cl->cl_total, cl->cl_total + len); if (!(cl->cl_flags & HFSC_FSC) || cl->cl_nactive == 0) continue; @@ -847,7 +847,7 @@ hfsc_adjust_levels(struct hfsc_class *cl) if (p->level >= level) level = p->level + 1; } - cl->level = level; + WRITE_ONCE(cl->level, level); } while ((cl = cl->cl_parent) != NULL); } @@ -1338,10 +1338,10 @@ hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg, __u32 qlen; qdisc_qstats_qlen_backlog(cl->qdisc, &qlen, &cl->qstats.backlog); - xstats.level = cl->level; - xstats.period = cl->cl_vtperiod; - xstats.work = cl->cl_total; - xstats.rtwork = cl->cl_cumul; + xstats.level = READ_ONCE(cl->level); + xstats.period = READ_ONCE(cl->cl_vtperiod); + xstats.work = READ_ONCE(cl->cl_total); + xstats.rtwork = READ_ONCE(cl->cl_cumul); if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || @@ -1452,15 +1452,15 @@ hfsc_change_qdisc(struct Qdisc *sch, struct nlattr *opt, static void hfsc_reset_class(struct hfsc_class *cl) { - cl->cl_total = 0; - cl->cl_cumul = 0; + WRITE_ONCE(cl->cl_total, 0); + WRITE_ONCE(cl->cl_cumul, 0); cl->cl_d = 0; cl->cl_e = 0; cl->cl_vt = 0; cl->cl_vtadj = 0; cl->cl_cvtmin = 0; cl->cl_cvtoff = 0; - cl->cl_vtperiod = 0; + WRITE_ONCE(cl->cl_vtperiod, 0); cl->cl_parentperiod = 0; cl->cl_f = 0; cl->cl_myf = 0; @@ -1626,7 +1626,7 @@ hfsc_dequeue(struct Qdisc *sch) bstats_update(&cl->bstats, skb); update_vf(cl, qdisc_pkt_len(skb), cur_time); if (realtime) - cl->cl_cumul += qdisc_pkt_len(skb); + WRITE_ONCE(cl->cl_cumul, cl->cl_cumul + qdisc_pkt_len(skb)); if (cl->cl_flags & HFSC_RSC) { if (cl->qdisc->q.qlen != 0) { From cb414aff28e18e6f5cff9f87ea31376ed8af317b Mon Sep 17 00:00:00 2001 From: Yihang Li Date: Sat, 25 Apr 2026 16:20:56 +0800 Subject: [PATCH 0775/3297] scsi: hisi_sas: Add slave_destroy interface for v3 hw [ Upstream commit 67b85a88265df19f049241d8c00571a5408f4eeb ] WARNING is triggered when executing link reset of remote PHY and rmmod SAS driver simultaneously. Following is the WARNING log: WARNING: CPU: 61 PID: 21818 at drivers/base/core.c:1347 __device_links_no_driver+0xb4/0xc0 Call trace: __device_links_no_driver+0xb4/0xc0 device_links_driver_cleanup+0xb0/0xfc __device_release_driver+0x198/0x23c device_release_driver+0x38/0x50 bus_remove_device+0x130/0x140 device_del+0x184/0x434 __scsi_remove_device+0x118/0x150 scsi_remove_target+0x1bc/0x240 sas_rphy_remove+0x90/0x94 sas_rphy_delete+0x24/0x3c sas_destruct_devices+0x64/0xa0 [libsas] sas_revalidate_domain+0xe4/0x150 [libsas] process_one_work+0x1e0/0x46c worker_thread+0x15c/0x464 kthread+0x160/0x170 ret_from_fork+0x10/0x20 ---[ end trace 71e059eb58f85d4a ]--- During SAS phy up, link->status is set to DL_STATE_AVAILABLE in device_links_driver_bound, then this setting influences __device_links_no_driver() before driver rmmod and caused WARNING. Add the slave_destroy interface to make sure link is removed after flush workque. Fixes: 16fd4a7c5917 ("scsi: hisi_sas: Add device link between SCSI devices and hisi_hba") Signed-off-by: Yihang Li Link: https://patch.msgid.link/20260425082056.2749910-1-liyihang9@huawei.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index f69efc6494b8e..f30d4a58d7ab5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2977,7 +2977,7 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev, return 0; if (!device_link_add(&sdev->sdev_gendev, dev, - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) { + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) { if (pm_runtime_enabled(dev)) { dev_info(dev, "add device link failed, disable runtime PM for the host\n"); pm_runtime_disable(dev); @@ -2987,6 +2987,15 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev, return 0; } +static void hisi_sas_sdev_destroy(struct scsi_device *sdev) +{ + struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev); + struct hisi_hba *hisi_hba = shost_priv(shost); + struct device *dev = hisi_hba->dev; + + device_link_remove(&sdev->sdev_gendev, dev); +} + static struct attribute *host_v3_hw_attrs[] = { &dev_attr_phy_event_threshold.attr, &dev_attr_intr_conv_v3_hw.attr, @@ -3401,6 +3410,7 @@ static const struct scsi_host_template sht_v3_hw = { .sg_tablesize = HISI_SAS_SGE_PAGE_CNT, .sg_prot_tablesize = HISI_SAS_SGE_PAGE_CNT, .sdev_init = hisi_sas_sdev_init, + .sdev_destroy = hisi_sas_sdev_destroy, .shost_groups = host_v3_hw_groups, .sdev_groups = sdev_groups_v3_hw, .tag_alloc_policy_rr = true, From 21650fe221ea9b72809140b691589dde8bff3eab Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 4 May 2026 15:28:12 -0700 Subject: [PATCH 0776/3297] crypto: ccp - Treat zero-length cert chain as query for blob lengths [ Upstream commit ef8c9dacda2871accd64e3eda951fef6b788b1ea ] When handling a PDH export, treat a zero-length userspace cert chain buffer as a request to query the length of the relevant blobs. Failure to account for the zero-length buffer trips a BUG_ON() when running with CONFIG_DEBUG_VIRTUAL=y due to trying to get the physical address of the ZERO_SIZE_PTR (returned by kzalloc() on the bogus allocation). kernel BUG at arch/x86/mm/physaddr.c:28 ! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI CPU: 30 UID: 0 PID: 28580 Comm: syz.2.18 Kdump: loaded Tainted: G W 6.18.16-smp-DEV #1 NONE Tainted: [W]=WARN Hardware name: Google, Inc. Arcadia_IT_80/Arcadia_IT_80, BIOS 12.62.0-0 11/19/2025 RIP: 0010:__phys_addr+0x16a/0x180 arch/x86/mm/physaddr.c:28 RSP: 0018:ffffc9008329fc80 EFLAGS: 00010293 RAX: ffffffff8179110a RBX: 0000778000000010 RCX: ffff8884e6992600 RDX: 0000000000000000 RSI: 0000000080000010 RDI: 0000778000000010 RBP: ffffc9008329fdf0 R08: 0000000000000dc0 R09: 00000000ffffffff R10: dffffc0000000000 R11: fffffbfff126d297 R12: dffffc0000000000 R13: 1ffff92010653fc8 R14: 0000000080000010 R15: dffffc0000000000 FS: 0000555556bec9c0(0000) GS:ffff88aa4ce1c000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fd3159e7000 CR3: 00000004fbc44000 CR4: 0000000000350ef0 Call Trace: [] sev_ioctl_do_pdh_export+0x559/0x7a0 drivers/crypto/ccp/sev-dev.c:2308 [] sev_ioctl+0x2cd/0x480 drivers/crypto/ccp/sev-dev.c:2556 [] vfs_ioctl fs/ioctl.c:52 [inline] [] __do_sys_ioctl fs/ioctl.c:598 [inline] [] __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:584 [] do_syscall_x64 arch/x86/entry/syscall_64.c:64 [inline] [] do_syscall_64+0x9f/0xf40 arch/x86/entry/syscall_64.c:98 [] entry_SYSCALL_64_after_hwframe+0x76/0x7e RIP: 0033:0x7fd3158eac39 Thankfully, the bug is benign outside of CONFIG_DEBUG_VIRTUAL=y as getting the physical address is just arithmetic, and the PSP errors out before trying to write to the garbage address (which it must, otherwise querying the blob lengths would clobber memory at pfn=0). Fixes: 76a2b524a4b1 ("crypto: ccp: Implement SEV_PDH_CERT_EXPORT ioctl command") Signed-off-by: Sean Christopherson Reviewed-by: Tom Lendacky Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/ccp/sev-dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 563418b9e8891..4eaad21fd8481 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -2258,7 +2258,8 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) /* Userspace wants to query the certificate length. */ if (!input.pdh_cert_address || !input.pdh_cert_len || - !input.cert_chain_address) + !input.cert_chain_address || + !input.cert_chain_len) goto cmd; /* Allocate a physically contiguous buffer to store the PDH blob. */ From 68eae3592438d5421e23f2156ad7c407b526c44f Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Fri, 15 May 2026 18:26:20 +0800 Subject: [PATCH 0777/3297] spi: hisi-kunpeng: Use dev_err_probe() for host registration failure [ Upstream commit 59b991990a04b1d1ce95373983b7c8b65bdf7acc ] When the SPI core registers the Kunpeng controller it may need to acquire chip-select GPIO descriptors. If the GPIO provider has not probed yet, spi_register_controller() returns -EPROBE_DEFER. On a Kunpeng system this currently prints an alarming error even though the next deferred-probe retry succeeds: hisi-kunpeng-spi HISI03E1:00: failed to register spi host, ret=-517 hisi-kunpeng-spi HISI03E1:00: hw version:0x30 max-freq:12500 kHz Use dev_err_probe() so that -EPROBE_DEFER is reported through the deferred probe mechanism instead of as a hard error, while preserving normal error reporting for real registration failures. Fixes: c770d8631e18 ("spi: Add HiSilicon SPI Controller Driver for Kunpeng SoCs") Signed-off-by: Qiang Ma Link: https://patch.msgid.link/20260515102620.1926930-1-maqianga@uniontech.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-hisi-kunpeng.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index a38dcae6271ff..f004920600ae7 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -518,10 +518,8 @@ static int hisi_spi_probe(struct platform_device *pdev) } ret = spi_register_controller(host); - if (ret) { - dev_err(dev, "failed to register spi host, ret=%d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to register spi host\n"); if (hisi_spi_debugfs_init(hs)) dev_info(dev, "failed to create debugfs dir\n"); From d8cae30582f068352d7fd9356402472e9713f0ce Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 May 2026 09:59:32 +0000 Subject: [PATCH 0778/3297] net/sched: sch_htb: do not change sch->flags in htb_dump() [ Upstream commit 9b949fa69129e4b694ed11ee3be6d6edd4a9b8f4 ] htb_dump() runs without holding qdisc spinlock. It is illegal to touch sch->flags with non locked RMW, as concurrent readers might see intermediate wrong values. Set TCQ_F_OFFLOADED in control path (htb_init()) instead. Fixes: d03b195b5aa0 ("sch_htb: Hierarchical QoS hardware offload") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260514095935.3926276-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_htb.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index b5e40c51655a7..1dc8570aa7776 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1148,6 +1148,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt, * parts (especially calling ndo_setup_tc) on errors. */ q->offload = true; + sch->flags |= TCQ_F_OFFLOADED; return 0; } @@ -1208,11 +1209,6 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; struct tc_htb_glob gopt; - if (q->offload) - sch->flags |= TCQ_F_OFFLOADED; - else - sch->flags &= ~TCQ_F_OFFLOADED; - sch->qstats.overlimits = q->overlimits; /* Its safe to not acquire qdisc lock. As we hold RTNL, * no change can happen on the qdisc parameters. From 5c1196b5a3bf368b169d435d6b22cc7d237be3d1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 May 2026 09:59:33 +0000 Subject: [PATCH 0779/3297] net/sched: sch_htb: annotate data-races (I) [ Upstream commit e54c33503bf7cebb1c1790251ce90f1252678081 ] htb_dump() runs without holding qdisc spinlock. Add missing READ_ONCE()/WRITE_ONCE() annotations around q->overlimits and q->direct_pkts. Fixes: edb09eb17ed8 ("net: sched: do not acquire qdisc spinlock in qdisc/class stats dump") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260514095935.3926276-3-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_htb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 1dc8570aa7776..db97fdb403515 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -568,7 +568,7 @@ htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, s64 *diff) if (new_mode == HTB_CANT_SEND) { cl->overlimits++; - q->overlimits++; + WRITE_ONCE(q->overlimits, q->overlimits + 1); } if (cl->prio_activity) { /* not necessary: speed optimization */ @@ -628,7 +628,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* enqueue to helper queue */ if (q->direct_queue.qlen < q->direct_qlen) { __qdisc_enqueue_tail(skb, &q->direct_queue); - q->direct_pkts++; + WRITE_ONCE(q->direct_pkts, q->direct_pkts + 1); } else { return qdisc_drop(skb, sch, to_free); } @@ -1209,12 +1209,12 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; struct tc_htb_glob gopt; - sch->qstats.overlimits = q->overlimits; + sch->qstats.overlimits = READ_ONCE(q->overlimits); /* Its safe to not acquire qdisc lock. As we hold RTNL, * no change can happen on the qdisc parameters. */ - gopt.direct_pkts = q->direct_pkts; + gopt.direct_pkts = READ_ONCE(q->direct_pkts); gopt.version = HTB_VER; gopt.rate2quantum = q->rate2quantum; gopt.defcls = q->defcls; From b61af0268e3d1308c466bf0be5dced844eafc1ef Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Wed, 13 May 2026 10:55:09 +0800 Subject: [PATCH 0780/3297] ipv6: addrconf: bail out of dad_failure when state is no longer POSTDAD [ Upstream commit 627ac78f2741e2ebd2225e2e953b6964a8a9182f ] addrconf_dad_failure() transitions ifp->state from DAD to POSTDAD via addrconf_dad_end(), which drops ifp->lock on return. The lock is re-acquired after net_info_ratelimited(). A concurrent ipv6_del_addr() can take the lock in that window, set ifp->state to DEAD and run list_del_rcu(&ifp->if_list). addrconf_dad_failure() then overwrites DEAD with ERRDAD at errdad: and schedules a new dad_work. The work calls ipv6_del_addr() again, hitting the already-poisoned list entry: general protection fault: 0000 [#1] SMP NOPTI CPU: 4 PID: 217 Comm: kworker/4:1 Workqueue: ipv6_addrconf addrconf_dad_work RIP: 0010:ipv6_del_addr+0xe9/0x280 RAX: dead000000000122 Call Trace: addrconf_dad_stop+0x113/0x140 addrconf_dad_work+0x28c/0x430 process_one_work+0x1eb/0x3b0 worker_thread+0x4d/0x400 kthread+0x104/0x140 ret_from_fork+0x35/0x40 Fold the addrconf_dad_end() logic into addrconf_dad_failure() under a single ifp->lock critical section. The STABLE_PRIVACY branch temporarily drops ifp->lock around address regeneration, so at lock_errdad: verify the state is still POSTDAD before transitioning to ERRDAD; bail out otherwise to avoid overwriting a state set by another path while the lock was released. Fixes: c15b1ccadb32 ("ipv6: move DAD and addrconf_verify processing to workqueue") Signed-off-by: Linmao Li Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260513025509.3776405-1-lilinmao@kylinos.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b2e1328371d3f..c8c82891fe14c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2168,16 +2168,18 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) struct net *net = dev_net(idev->dev); int max_addresses; - if (addrconf_dad_end(ifp)) { + spin_lock_bh(&ifp->lock); + + if (ifp->state != INET6_IFADDR_STATE_DAD) { + spin_unlock_bh(&ifp->lock); in6_ifa_put(ifp); return; } + ifp->state = INET6_IFADDR_STATE_POSTDAD; net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n", ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source); - spin_lock_bh(&ifp->lock); - if (ifp->flags & IFA_F_STABLE_PRIVACY) { struct in6_addr new_addr; struct inet6_ifaddr *ifp2; @@ -2225,6 +2227,11 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) in6_ifa_put(ifp2); lock_errdad: spin_lock_bh(&ifp->lock); + if (ifp->state != INET6_IFADDR_STATE_POSTDAD) { + spin_unlock_bh(&ifp->lock); + in6_ifa_put(ifp); + return; + } } errdad: From 65e344925fa30abf50c8de8c150b397715fa2066 Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Sun, 12 Apr 2026 02:18:49 +0100 Subject: [PATCH 0781/3297] IB/mlx5: Fix transport-domain rollback and initialize lb mutex earlier [ Upstream commit e79389115b9d27287ff6230a9750675106ed7668 ] mlx5_ib_alloc_transport_domain() allocates a transport domain and then may fail in mlx5_ib_enable_lb(). In that case, the allocated TD is leaked. Fix this by deallocating the TD when mlx5_ib_enable_lb() returns an error. Also return 0 explicitly in the no-loopback-capability success branch, and move dev->lb.mutex initialization to mlx5_ib_stage_init_init(). Fixes: 146d2f1af324 ("IB/mlx5: Allocate a Transport Domain for each ucontext") Signed-off-by: Prathamesh Deshpande Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/main.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 984c258ab0a89..012386e8cdd4a 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1941,9 +1941,13 @@ static int mlx5_ib_alloc_transport_domain(struct mlx5_ib_dev *dev, u32 *tdn, if ((MLX5_CAP_GEN(dev->mdev, port_type) != MLX5_CAP_PORT_TYPE_ETH) || (!MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) && !MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc))) - return err; + return 0; + + err = mlx5_ib_enable_lb(dev, true, false); + if (err) + mlx5_cmd_dealloc_transport_domain(dev->mdev, *tdn, uid); - return mlx5_ib_enable_lb(dev, true, false); + return err; } static void mlx5_ib_dealloc_transport_domain(struct mlx5_ib_dev *dev, u32 tdn, @@ -4246,6 +4250,8 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev) dev->port[i].roce.last_port_state = IB_PORT_DOWN; } + mutex_init(&dev->lb.mutex); + err = mlx5r_cmd_query_special_mkeys(dev); if (err) return err; @@ -4506,11 +4512,6 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev) if (err) return err; - if ((MLX5_CAP_GEN(dev->mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && - (MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) || - MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc))) - mutex_init(&dev->lb.mutex); - if (MLX5_CAP_GEN_64(dev->mdev, general_obj_types) & MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q) { err = mlx5_ib_init_var_table(dev); From 2cd221fca036b5a6f5516075a63af2c178ad7d52 Mon Sep 17 00:00:00 2001 From: Alexander Chesnokov Date: Mon, 13 Apr 2026 12:14:43 +0300 Subject: [PATCH 0782/3297] RDMA/hns: Fix arithmetic overflow in calc_hem_config() [ Upstream commit a38e4410af9ad8b7ad2217254da06cd4dc21f0ed ] If bt_num is 3 or 2, then the expressions like l0_idx * chunk_ba_num + l1_idx are computed in 32-bit arithmetic before being assigned to a u64 index field, which can lead to overflow. Cast the first operand to u64 to ensure the arithmetic is performed in 64-bit. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 2f49de21f3e9 ("RDMA/hns: Optimize mhop get flow for multi-hop addressing") Signed-off-by: Alexander Chesnokov Link: https://patch.msgid.link/20260413091527.39990-1-Alexander.Chesnokov@kaspersky.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_hem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index 3d479c63b117a..1680d0ac071ed 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -314,14 +314,14 @@ static int calc_hem_config(struct hns_roce_dev *hr_dev, bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num); switch (bt_num) { case 3: - index->l1 = l0_idx * chunk_ba_num + l1_idx; + index->l1 = (u64)l0_idx * chunk_ba_num + l1_idx; index->l0 = l0_idx; - index->buf = l0_idx * chunk_ba_num * chunk_ba_num + - l1_idx * chunk_ba_num + l2_idx; + index->buf = (u64)l0_idx * chunk_ba_num * chunk_ba_num + + (u64)l1_idx * chunk_ba_num + l2_idx; break; case 2: index->l0 = l0_idx; - index->buf = l0_idx * chunk_ba_num + l1_idx; + index->buf = (u64)l0_idx * chunk_ba_num + l1_idx; break; case 1: index->buf = l0_idx; From ffa85a2c197935ace6f1634ad9eb0a44bc615670 Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Sun, 26 Apr 2026 14:23:41 +0100 Subject: [PATCH 0783/3297] RDMA/mlx5: Fix UMR XLT cleanup on ODP populate failure [ Upstream commit 1eae35b37923cb71b0cb5136d00671440d488b9f ] mlx5r_umr_update_xlt() allocates and DMA maps an XLT buffer with mlx5r_umr_create_xlt(). The buffer is released by the common cleanup path through mlx5r_umr_unmap_free_xlt(). After mlx5_odp_populate_xlt() became fallible, its error path returned directly and skipped that cleanup. This leaks the XLT DMA mapping and buffer. If the emergency XLT page was used, it also leaves xlt_emergency_page_mutex locked. Break out of the loop so execution falls through the existing cleanup path. Fixes: 1efe8c0670d6 ("RDMA/core: Convert UMEM ODP DMA mapping to caching IOVA and page linkage") Signed-off-by: Prathamesh Deshpande Link: https://patch.msgid.link/20260426132356.22264-1-prathameshdeshpande7@gmail.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/umr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 29488fba21a03..71f331ce6d894 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -915,7 +915,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, */ err = mlx5_odp_populate_xlt(xlt, idx, npages, mr, flags); if (err) - return err; + break; dma_sync_single_for_device(ddev, sg.addr, sg.length, DMA_TO_DEVICE); sg.length = ALIGN(size_to_map, MLX5_UMR_FLEX_ALIGNMENT); From 5100febf8e9d6c8c5ba8dc6534c6a5e3376e5989 Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Tue, 28 Apr 2026 23:42:49 +0100 Subject: [PATCH 0784/3297] RDMA/mlx5: Fix devx subscribe-event unwind NULL dereference [ Upstream commit 43f8f7946814c8e5f464518246fdbc69b6e32326 ] MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT() links event_sub into sub_list before initializing the fields used by the shared error path. If eventfd_ctx_fdget() then fails, the unwind path dereferences event_sub->ev_file in uverbs_uobject_put() and calls subscribe_event_xa_dealloc() with an unset xa_key_level1. subscribe_event_xa_alloc() creates the XA entry exactly once for a given key_level1, on the first occurrence of that key. The unwind path must therefore call subscribe_event_xa_dealloc() exactly once for it as well. Enforce that by adding devx_key_in_sub_list() and calling subscribe_event_xa_dealloc() only when the last matching pending entry is being cleaned up. Fixes: 759738537142 ("IB/mlx5: Enable subscription for device events over DEVX") Signed-off-by: Prathamesh Deshpande Link: https://patch.msgid.link/20260428224319.37682-1-prathameshdeshpande7@gmail.com Reviewed-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/devx.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 8b506417ad2fb..dfeffbacea224 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1899,6 +1899,17 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY)( return err; } +static bool devx_key_in_sub_list(struct list_head *list, u32 key_level1) +{ + struct devx_event_subscription *s; + + list_for_each_entry(s, list, event_list) + if (s->xa_key_level1 == key_level1) + return true; + + return false; +} + static void subscribe_event_xa_dealloc(struct mlx5_devx_event_table *devx_event_table, u32 key_level1, @@ -2146,10 +2157,17 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( event_sub = kzalloc(sizeof(*event_sub), GFP_KERNEL); if (!event_sub) { + if (!devx_key_in_sub_list(&sub_list, key_level1)) + subscribe_event_xa_dealloc(devx_event_table, + key_level1, + obj, + obj_id); err = -ENOMEM; goto err; } + event_sub->ev_file = ev_file; + event_sub->xa_key_level1 = key_level1; list_add_tail(&event_sub->event_list, &sub_list); uverbs_uobject_get(&ev_file->uobj); if (use_eventfd) { @@ -2164,9 +2182,6 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( } event_sub->cookie = cookie; - event_sub->ev_file = ev_file; - /* May be needed upon cleanup the devx object/subscription */ - event_sub->xa_key_level1 = key_level1; event_sub->xa_key_level2 = obj_id; INIT_LIST_HEAD(&event_sub->obj_list); } @@ -2211,10 +2226,11 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( list_for_each_entry_safe(event_sub, tmp_sub, &sub_list, event_list) { list_del(&event_sub->event_list); - subscribe_event_xa_dealloc(devx_event_table, - event_sub->xa_key_level1, - obj, - obj_id); + if (!devx_key_in_sub_list(&sub_list, event_sub->xa_key_level1)) + subscribe_event_xa_dealloc(devx_event_table, + event_sub->xa_key_level1, + obj, + obj_id); if (event_sub->eventfd) eventfd_ctx_put(event_sub->eventfd); From 65572fbd86033ae2370125593d59b8be34253aaf Mon Sep 17 00:00:00 2001 From: Sara Venkatesh Date: Mon, 4 May 2026 01:00:36 -0700 Subject: [PATCH 0785/3297] RDMA/srpt: fix integer overflow in immediate data length check [ Upstream commit eb4ecdf631fe00e8020bf461503cb9b7017ed796 ] imm_buf->len is a user-controlled uint32_t received from the network. Adding it to imm_data_offset without overflow checking allows a malicious initiator to send len=0xFFFFFFFF, causing req_size to wrap around to a small value, bypassing the bounds check, and subsequently passing a ~4GB length to sg_init_one(). Use check_add_overflow() to detect wrapping before the comparison. Fixes: 5dabcd0456d7 ("RDMA/srpt: Add support for immediate data") Reported-by: Carlos Bilbao (Lambda) Signed-off-by: Sara Venkatesh Link: https://patch.msgid.link/20260504080036.3482415-1-sarajvenkatesh@gmail.com Reviewed-by: Carlos Bilbao (Lambda) Reviewed-by: Bart Van Assche Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/srpt/ib_srpt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 71269446353db..ba70c64a0aa9e 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1129,9 +1129,10 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx, struct srp_imm_buf *imm_buf = srpt_get_desc_buf(srp_cmd); void *data = (void *)srp_cmd + imm_data_offset; uint32_t len = be32_to_cpu(imm_buf->len); - uint32_t req_size = imm_data_offset + len; + uint32_t req_size; - if (req_size > srp_max_req_size) { + if (check_add_overflow((uint32_t)imm_data_offset, len, &req_size) || + req_size > srp_max_req_size) { pr_err("Immediate data (length %d + %d) exceeds request size %d\n", imm_data_offset, len, srp_max_req_size); return -EINVAL; From be2c137f23d15f03f25052edd00a76b49fbce9f9 Mon Sep 17 00:00:00 2001 From: Junxian Huang Date: Thu, 7 May 2026 09:21:46 +0800 Subject: [PATCH 0786/3297] RDMA/hns: Initialize seqfile before creating file [ Upstream commit b4070770506ff516e21b4923afdaf7eb5da0e150 ] The debugfs file was created before seq->read and seq->data were set, leaving a small window where userspace could access an uninitialized seqfile. Move debugfs_create_file() after the assignments to avoid this issue. Also, inline the original init_debugfs_seqfile() since it is not a really necessary helper. Fixes: ca7ad04cd5d2 ("RDMA/hns: Add debugfs to hns RoCE") Signed-off-by: Junxian Huang Link: https://patch.msgid.link/20260507012148.1079712-2-huangjunxian6@hisilicon.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_debugfs.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_debugfs.c b/drivers/infiniband/hw/hns/hns_roce_debugfs.c index b869cdc541189..db32c5897640f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_debugfs.c +++ b/drivers/infiniband/hw/hns/hns_roce_debugfs.c @@ -26,17 +26,6 @@ static const struct file_operations hns_debugfs_seqfile_fops = { .llseek = seq_lseek }; -static void init_debugfs_seqfile(struct hns_debugfs_seqfile *seq, - const char *name, struct dentry *parent, - int (*read_fn)(struct seq_file *, void *), - void *data) -{ - debugfs_create_file(name, 0400, parent, seq, &hns_debugfs_seqfile_fops); - - seq->read = read_fn; - seq->data = data; -} - static const char * const sw_stat_info[] = { [HNS_ROCE_DFX_AEQE_CNT] = "aeqe", [HNS_ROCE_DFX_CEQE_CNT] = "ceqe", @@ -76,10 +65,12 @@ static void create_sw_stat_debugfs(struct hns_roce_dev *hr_dev, { struct hns_sw_stat_debugfs *dbgfs = &hr_dev->dbgfs.sw_stat_root; - dbgfs->root = debugfs_create_dir("sw_stat", parent); + dbgfs->sw_stat.read = sw_stat_debugfs_show; + dbgfs->sw_stat.data = hr_dev; - init_debugfs_seqfile(&dbgfs->sw_stat, "sw_stat", dbgfs->root, - sw_stat_debugfs_show, hr_dev); + dbgfs->root = debugfs_create_dir("sw_stat", parent); + debugfs_create_file("sw_stat", 0400, dbgfs->root, &dbgfs->sw_stat, + &hns_debugfs_seqfile_fops); } /* debugfs for device */ From 7471006cd854d159723344be809e8287a2d75502 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Thu, 7 May 2026 11:50:46 +0100 Subject: [PATCH 0787/3297] drm/syncobj: Fix memory leak in drm_syncobj_find_fence() [ Upstream commit e5b93bd6fdb92aa5e4689715d7e8487d9ce66a38 ] Commit 18226ba52159 ("drm/syncobj: reject invalid flags in drm_syncobj_find_fence") forgot to take into account the fact that drm_syncobj_find() takes a reference to syncobj and returns early without dropping the reference, leading to memory leaks. Fixes: 18226ba52159 ("drm/syncobj: reject invalid flags in drm_syncobj_find_fence") Reported by: Sam Spencer Signed-off-by: Liviu Dudau Acked-by: Erik Kurzinger Signed-off-by: Liviu Dudau Link: https://lore.kernel.org/all/20260507144425.2488057-1-liviu.dudau@arm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_syncobj.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 7eb2cdbc574a0..c0cb7d79672d8 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -442,13 +442,15 @@ int drm_syncobj_find_fence(struct drm_file *file_private, u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT); int ret; - if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) - return -EINVAL; - if (!syncobj) return -ENOENT; - /* Waiting for userspace with locks help is illegal cause that can + if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { + ret = -EINVAL; + goto out; + } + + /* Waiting for userspace with locks held is illegal cause that can * trivial deadlock with page faults for example. Make lockdep complain * about it early on. */ From e4d599a286b5ab5bfde2f83b93687cfdbf214549 Mon Sep 17 00:00:00 2001 From: Roman Kvasnytskyi Date: Sat, 16 May 2026 14:06:25 +0200 Subject: [PATCH 0788/3297] selftests/bpf: Reject unsupported -k option in vmtest.sh [ Upstream commit 6df582112aa9ac9d190169abdb0e42e496659ec9 ] vmtest.sh does not document a -k option and does not handle it in the getopts case statement. However, the getopts optstring includes k, which causes the script to accept -k silently instead of reporting it as an invalid option. Remove k from the optstring so unsupported options are rejected through the existing invalid-option path. Fixes: c9709f52386d ("bpf: Helper script for running BPF presubmit tests") Signed-off-by: Roman Kvasnytskyi Acked-by: Paul Chaignon Link: https://lore.kernel.org/r/20260516120625.80839-1-roman@kvasnytskyi.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/vmtest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh index 2f869daf8a06b..9ca8022853933 100755 --- a/tools/testing/selftests/bpf/vmtest.sh +++ b/tools/testing/selftests/bpf/vmtest.sh @@ -382,7 +382,7 @@ main() local exit_command="poweroff -f" local debug_shell="no" - while getopts ':hskl:id:j:' opt; do + while getopts ':hsl:id:j:' opt; do case ${opt} in l) LOCAL_ROOTFS_IMAGE="$OPTARG" From bcfc49f1bcf1b2bb5e77b6f7d7fd18adf443e0d7 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Mon, 18 May 2026 18:26:35 +0200 Subject: [PATCH 0789/3297] selftests/bpf: Fix test for refinement of single-value tnum [ Upstream commit 523d2f42b406f5be2989f436b03eacebf3679835 ] This patch fixes the "bounds refinement with single-value tnum on umin" verifier selftest. This selftest was introduced in commit e6ad477d1bf8 ("selftests/bpf: Test refinement of single-value tnum") to cover the logic from __update_reg64_bounds(), introduced in commit efc11a667878 ("bpf: Improve bounds when tnum has a single possible value"). However, the test still passes if that last commit is reverted. The test is supposed to cover the case when the tnum and u64 range (or cnum64 now) overlap in a single value. __update_reg64_bounds() detects that case and refines the bounds to a known constant. However, the constants for the test were poorly chosen and the bounds get refined to a known constant even without __update_reg64_bounds(). The code is as follows: 0: call bpf_get_prandom_u32#7 ; R0=scalar() 1: r0 |= 224 ; R0=scalar(umin=umin32=224,var_off=(0xe0; 0xffffffffffffff1f)) 2: r0 &= 240 ; R0=scalar(smin=umin=smin32=umin32=224,smax=umax=smax32=umax32=240,var_off=(0xe0; 0x10)) 3: if r0 == 0xf0 goto pc+2 ; R0=224 After instruction 3, we have u64=[0xe0; 0xef] and tnum=(0xe0; 0x10). __reg_bound_offset() is able to deduce a new tnum from the u64, tnum=(0xe0; 0x0f), which combined with the existing tnum gives us a constant: 0xe0 or 224. We can easily fix this by choosing different starting bounds. If we make it u64=[0xe1; 0xf0], then __reg_bound_offset() doesn't have any impact. Fixes: e6ad477d1bf8 ("selftests/bpf: Test refinement of single-value tnum") Signed-off-by: Paul Chaignon Link: https://lore.kernel.org/r/be2dc2c3d85120286e60b3029b3338fff339f942.1779121582.git.paul.chaignon@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- .../testing/selftests/bpf/progs/verifier_bounds.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds.c b/tools/testing/selftests/bpf/progs/verifier_bounds.c index ea5db79da40ef..f61733b736f9e 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bounds.c +++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c @@ -1742,26 +1742,26 @@ __naked void bounds_refinement_tnum_umax(void *ctx) /* This test covers the bounds deduction when the u64 range and the tnum * overlap only at umin. After instruction 3, the ranges look as follows: * - * 0 umin=0xe00 umax=0xeff U64_MAX + * 0 umin=0xe1 umax=0xf0 U64_MAX * | [xxxxxxxxxxxxxx] | * |----------------------------|------------------------------| * | x x | tnum values * - * The verifier can therefore deduce that the R0=0xe0=224. + * The verifier can therefore deduce that the R0=0xe1=225. */ SEC("socket") __description("bounds refinement with single-value tnum on umin") -__msg("3: (15) if r0 == 0xf0 {{.*}} R0=224") +__msg("3: (15) if r0 == 0xf1 {{.*}} R0=225") __success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) __naked void bounds_refinement_tnum_umin(void *ctx) { asm volatile(" \ call %[bpf_get_prandom_u32]; \ - r0 |= 0xe0; \ - r0 &= 0xf0; \ - if r0 == 0xf0 goto +2; \ - if r0 == 0xe0 goto +1; \ + r0 |= 0xe1; \ + r0 &= 0xf1; \ + if r0 == 0xf1 goto +2; \ + if r0 == 0xe1 goto +1; \ r10 = 0; \ exit; \ " : From 6a357ceb2110052c6909b246daa63f66ea6639cc Mon Sep 17 00:00:00 2001 From: Bibek Kumar Patro Date: Thu, 23 Apr 2026 15:32:30 +0530 Subject: [PATCH 0790/3297] iommu/arm-smmu-qcom: Fix fastrpc compatible string in ACTLR client match table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a6e1618a65d453d4e739e5898c662ccb1e3de6c0 ] The qcom_smmu_actlr_client_of_match table contained "qcom,fastrpc" as the compatible string for applying ACTLR prefetch settings to FastRPC devices. However, "qcom,fastrpc" is the compatible string for the parent rpmsg channel node, which is not an IOMMU client — it carries no "iommus" property in the device tree and is never attached to an SMMU context bank. The actual IOMMU clients are the compute context bank (CB) child nodes, which use the compatible string "qcom,fastrpc-compute-cb". These nodes carry the "iommus" property and are probed by fastrpc_cb_driver via fastrpc_cb_probe(), which sets up the DMA mask and IOMMU mappings for each FastRPC session. The device tree structure is: fastrpc { compatible = "qcom,fastrpc"; /* rpmsg channel, no iommus */ ... compute-cb@3 { compatible = "qcom,fastrpc-compute-cb"; iommus = <&apps_smmu 0x1823 0x0>; /* actual IOMMU client */ }; }; Since qcom_smmu_set_actlr_dev() calls of_match_device() against the device being attached to the SMMU context bank, the "qcom,fastrpc" entry was never matching any device. As a result, the ACTLR prefetch settings (PREFETCH_DEEP | CPRE | CMTLB) were silently never applied for FastRPC compute context banks. Fix this by replacing "qcom,fastrpc" with "qcom,fastrpc-compute-cb" in the match table so that the ACTLR settings are correctly applied to the compute CB devices that are the true IOMMU clients. Assisted-by: Claude:claude-sonnet-4-6 Fixes: 3e35c3e725de ("iommu/arm-smmu: Add ACTLR data and support for qcom_smmu_500") Reviewed-by: Dmitry Baryshkov Reviewed-by: Shawn Guo Signed-off-by: Bibek Kumar Patro Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index 2c442aa218157..3b9fb1c71e5f9 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -39,7 +39,7 @@ static const struct of_device_id qcom_smmu_actlr_client_of_match[] = { .data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) }, { .compatible = "qcom,adreno-smmu", .data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) }, - { .compatible = "qcom,fastrpc", + { .compatible = "qcom,fastrpc-compute-cb", .data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) }, { .compatible = "qcom,sc7280-mdss", .data = (const void *) (PREFETCH_SHALLOW | CPRE | CMTLB) }, From 5e353d9497f95ebead4fc3fc8cd7eb3ee878bef5 Mon Sep 17 00:00:00 2001 From: Kevin Brodsky Date: Mon, 27 Apr 2026 13:03:34 +0100 Subject: [PATCH 0791/3297] selftests/mm: Fix resv_sz when parsing arm64 signal frame [ Upstream commit c364aa56d6738c8759e88941d7a45a1d6b4c52d0 ] get_header() wants the size of the reserved area in struct sigcontext, but instead we pass it the size of the entire struct. This could in theory result in an out-of-bounds read (if the signal frame is malformed). Fix this using one of the existing macros from tools/testing/selftests/arm64/signal/testcases/testcases.h. This issue was reported by Sashiko on a patch that copied this portion of the code. Link: https://sashiko.dev/#/patchset/20260421144252.1440365-1-kevin.brodsky%40arm.com Fixes: f5b5ea51f78f ("selftests: mm: make protection_keys test work on arm64") Signed-off-by: Kevin Brodsky Reviewed-by: Mark Brown Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/pkey-arm64.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/pkey-arm64.h b/tools/testing/selftests/mm/pkey-arm64.h index 8e9685e03c441..c5a78a2f211d5 100644 --- a/tools/testing/selftests/mm/pkey-arm64.h +++ b/tools/testing/selftests/mm/pkey-arm64.h @@ -130,9 +130,10 @@ static inline u64 get_pkey_bits(u64 reg, int pkey) static inline void aarch64_write_signal_pkey(ucontext_t *uctxt, u64 pkey) { struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uctxt); + size_t resv_size = GET_UCP_RESV_SIZE(uctxt); struct poe_context *poe_ctx = (struct poe_context *) get_header(ctx, POE_MAGIC, - sizeof(uctxt->uc_mcontext), NULL); + resv_size, NULL); if (poe_ctx) poe_ctx->por_el0 = pkey; } From 0da72a88dbbabd846a0c322f9a35973cfd32053c Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Mon, 18 May 2026 13:31:16 -0700 Subject: [PATCH 0792/3297] firmware: arm_ffa: Honor partition info descriptor size [ Upstream commit 01b9cae706161a39452a2cce0f281d4369344c51 ] FFA_PARTITION_INFO_GET_REGS reports the size of each partition information descriptor in x2[63:48]. However, __ffa_partition_info_get_regs() walks the returned register payload with a hardcoded 24-byte stride (regs += 3), even though the size is already read into buf_sz. That works for the FF-A v1.1/v1.2 24-byte descriptor layout, where each descriptor consumes three registers. Newer FF-A revisions can extend the descriptor while keeping the existing fields at the front. For example, a 48-byte descriptor consumes six registers, so advancing by only three registers desynchronises the parser and can make it read subsequent entries from the middle of a descriptor. Use the advertised descriptor size to derive the register stride. Validate that the size is register-aligned, large enough for the fields parsed by the driver, and that the requested number of descriptors fits in the returned x3..x17 register window. The driver still copies only the fields it understands, but now skips over any trailing descriptor fields correctly. Fixes: ba85c644ac8d ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS") Suggested-by: Sudeep Holla Signed-off-by: Jamie Nguyen Link: https://patch.msgid.link/20260518203116.42624-1-jamien@nvidia.com (sudeep.holla: Minor rewordng of the commit message and subject) Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 827aac08a8e9e..3fbc75caf0c50 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -320,11 +320,9 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, #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 FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC 3 +#define FFA_PART_INFO_GET_REGS_NUM_REGS \ + (sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) #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)))) @@ -338,7 +336,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, do { __le64 *regs; - int idx, nr_desc, buf_idx; + int idx, nr_desc, buf_idx, regs_per_desc, max_desc; invoke_ffa_fn((ffa_value_t){ .a0 = FFA_PARTITION_INFO_GET_REGS, @@ -361,8 +359,18 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, if (cur_idx < start_idx || cur_idx >= count) return -EINVAL; + buf_sz = PARTITION_INFO_SZ(partition_info.a2); + if (buf_sz % sizeof(*regs)) + return -EINVAL; + + regs_per_desc = buf_sz / sizeof(*regs); + if (regs_per_desc < FFA_PART_INFO_GET_REGS_MIN_REGS_PER_DESC) + return -EINVAL; + nr_desc = cur_idx - start_idx + 1; - if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC) + max_desc = (FFA_PART_INFO_GET_REGS_NUM_REGS - + FFA_PART_INFO_GET_REGS_FIRST_REG) / regs_per_desc; + if (nr_desc > max_desc) return -EINVAL; buf_idx = buf - buffer; @@ -370,9 +378,6 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, 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 < nr_desc; idx++, buf++) { @@ -391,7 +396,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, buf->exec_ctxt = PART_INFO_EXEC_CXT(val); buf->properties = PART_INFO_PROPERTIES(val); uuid_copy(&buf->uuid, &uuid_regs.uuid); - regs += 3; + regs += regs_per_desc; } start_idx = cur_idx + 1; From 6a576739ce4c974157fb5d3d366361bf80bc9146 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Sat, 9 May 2026 09:54:09 +0800 Subject: [PATCH 0793/3297] arm64: dts: imx8dxl-evk: Remove unnecessary PCIe EP properties [ Upstream commit 538525f34158deb21b782164bde23ec07a353e4a ] For PCIe endpoint mode, only M.2 power supply needs to be ensured. On imx8dxl-evk, the M.2 power is always on and cannot be controlled, while reg_pcieb only controls the M.2 W_DISABLE1# signal. Remove the unnecessary vpcie-supply property from pcie0_ep node. Also remove reset-gpio as PCIe endpoint mode doesn't require reset control. Fixes: c1c4820b60d7 ("arm64: dts: imx8dxl-evk: Add pcie0-ep node and use unified pcie0 label") Signed-off-by: Sherry Sun Reviewed-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8dxl-evk.dts | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts index b75ab1e010f0c..605a423a1b668 100644 --- a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts @@ -681,8 +681,6 @@ phy-names = "pcie-phy"; pinctrl-0 = <&pinctrl_pcieb>; pinctrl-names = "default"; - reset-gpio = <&lsio_gpio4 0 GPIO_ACTIVE_LOW>; - vpcie-supply = <®_pcieb>; status = "disabled"; }; From 084d7d5668f30720629aaa0b9c6c0eb358f4eb4b Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Sat, 9 May 2026 09:54:10 +0800 Subject: [PATCH 0794/3297] arm64: dts: imx8qxp-mek: Remove unnecessary PCIe EP vpcie-supply [ Upstream commit b5ba136d535f1060322af31c0cf74d85ea19892d ] For PCIe endpoint mode, only M.2 power supply needs to be ensured. On imx8qxp-mek, the M.2 power is always on and cannot be controlled, while reg_pcieb only controls the M.2 W_DISABLE1# signal. Remove the unnecessary vpcie-supply property from pcie0_ep node. Fixes: 1c9b0c6044c2 ("arm64: dts: imx8: use common imx-pcie0-ep.dtso to enable PCI ep function") Signed-off-by: Sherry Sun Reviewed-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8qxp-mek.dts | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts index ea78e84341523..eb86977040879 100644 --- a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts +++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts @@ -647,7 +647,6 @@ phy-names = "pcie-phy"; pinctrl-0 = <&pinctrl_pcieb>; pinctrl-names = "default"; - vpcie-supply = <®_pcieb>; status = "disabled"; }; From 5d6f34dc4f056ddb70da73b1b96d7663b4e24ec3 Mon Sep 17 00:00:00 2001 From: Sherry Sun Date: Sat, 9 May 2026 09:54:11 +0800 Subject: [PATCH 0795/3297] arm64: dts: imx95-19x19-evk: Fix PCIe EP vpcie-supply [ Upstream commit 596d0f9f4fefffbf783ab26cfa90cf50f5dd6bb0 ] The vpcie-supply property should reference the regulator that controls the actual M.2 power supply, not the W_DISABLE1# signal. On imx95-19x19-evk: - reg_pcie0 controls M.2 W_DISABLE1# signal - reg_m2_pwr controls the actual M.2 power supply Fix the vpcie-supply to use reg_m2_pwr for proper power control in PCIe endpoint mode. Fixes: 58bea81052d0 ("arm64: dts: imx95: add pcie1 ep overlay file and create pcie-ep dtb files") Signed-off-by: Sherry Sun Reviewed-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts index 9f968feccef67..513c52901e6cf 100644 --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts @@ -548,7 +548,7 @@ &pcie0_ep { pinctrl-0 = <&pinctrl_pcie0>; pinctrl-names = "default"; - vpcie-supply = <®_pcie0>; + vpcie-supply = <®_m2_pwr>; status = "disabled"; }; From 2e3e4cc0dd349f54574526868065d3139ba65f58 Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Tue, 3 Feb 2026 16:31:34 +0000 Subject: [PATCH 0796/3297] media: atomisp: Fix memory leak in atomisp_fixed_pattern_table() [ Upstream commit 4e8156bd9517fa18c3613ea39c222c7035f0221e ] atomisp_v4l2_framebuffer_to_css_frame() allocates memory for temporary variable raw_black_frame, which must be released via ia_css_frame_free() before the function returns. However, if sh_css_set_black_frame() fails, the function returns immediately without performing this cleanup, leading to a memory leak. Fix this by assigning the return value of sh_css_set_black_frame() to ret. This ensures that the error code is propagated while allowing the execution to fall through to the ia_css_frame_free() cleanup call. The bug was originally detected on v6.13-rc1 using an experimental static analysis tool we are developing, and we have verified that the issue persists in the latest mainline kernel. The tool is based on the LLVM framework and is specifically designed to detect memory management issues. It is currently under active development and not yet publicly available. We performed build testing on x86_64 with allyesconfig. Since triggering this error path in atomisp requires specific Intel Atom ISP hardware and firmware, we were unable to perform runtime testing and instead verified the fix according to the code logic. Fixes: 85b606e02ad7 ("media: atomisp: get rid of a bunch of other wrappers") Signed-off-by: Zilin Guan Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Sasha Levin --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 3a4eb4f6d3be5..ae9c4dc8a44b4 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -3367,10 +3367,8 @@ int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd, if (ret) return ret; - if (sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, - raw_black_frame) != 0) - return -ENOMEM; - + ret = sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, + raw_black_frame); ia_css_frame_free(raw_black_frame); return ret; } From d57e67ea48e4d095052f2b14d8dd7593621f862f Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 2 Apr 2026 20:23:19 -0400 Subject: [PATCH 0797/3297] media: atomisp: gc2235: fix UAF and memory leak [ Upstream commit 628f763aee0047ff44974388d6f70f75a763026b ] gc2235_probe() handles its error paths incorrectly. If media_entity_pads_init() fails, gc2235_remove() is called, which tears down the subdev and frees dev, but then still falls through to atomisp_register_i2c_module(). This results in use-after-free. If atomisp_register_i2c_module() fails, the media entity and control handler are left initialized and dev is leaked. gc2235_remove() unconditionally calls media_entity_cleanup() and v4l2_ctrl_handler_free(), but these are not initialized at every error path in gc2235_probe(). Replace gc2235_remove() calls in the probe error paths with explicit unwind labels that free only the resources initialized at each point of failure, in reverse order of initialization. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Signed-off-by: Yuho Choi Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Sasha Levin --- .../media/atomisp/i2c/atomisp-gc2235.c | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 6fc39ab95e46f..4b11a20c2ba19 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -809,7 +809,7 @@ static int gc2235_probe(struct i2c_client *client) ret = gc2235_s_config(&dev->sd, client->irq, gcpdev); if (ret) - goto out_free; + goto err_unregister_subdev; dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; dev->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -818,18 +818,16 @@ static int gc2235_probe(struct i2c_client *client) ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(gc2235_controls)); - if (ret) { - gc2235_remove(client); - return ret; - } + if (ret) + goto err_csi_cfg; for (i = 0; i < ARRAY_SIZE(gc2235_controls); i++) v4l2_ctrl_new_custom(&dev->ctrl_handler, &gc2235_controls[i], NULL); if (dev->ctrl_handler.error) { - gc2235_remove(client); - return dev->ctrl_handler.error; + ret = dev->ctrl_handler.error; + goto err_ctrl_handler; } /* Use same lock for controls as for everything else. */ @@ -838,14 +836,23 @@ static int gc2235_probe(struct i2c_client *client) ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); if (ret) - gc2235_remove(client); + goto err_ctrl_handler; + + ret = atomisp_register_i2c_module(&dev->sd, gcpdev); + if (ret) + goto err_media_cleanup; - return atomisp_register_i2c_module(&dev->sd, gcpdev); + return 0; -out_free: +err_media_cleanup: + media_entity_cleanup(&dev->sd.entity); +err_ctrl_handler: + v4l2_ctrl_handler_free(&dev->ctrl_handler); +err_csi_cfg: + dev->platform_data->csi_cfg(&dev->sd, 0); +err_unregister_subdev: v4l2_device_unregister_subdev(&dev->sd); kfree(dev); - return ret; } From 63aa7dda9a1ede4b479924765e8c514e2b1fb9b1 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:07 +0100 Subject: [PATCH 0798/3297] staging: media: atomisp: fix loop shadowing in ia_css_stream_destroy() [ Upstream commit 9087395a383212ab1beaefcbe3b57ed131c7823d ] The nested loop inside the IS_ISP2401 block incorrectly uses the same variable 'i' as the outer loop. This shadows the outer loop variable and causes premature termination or skipped array elements. Change the inner loop to use a new variable 'j' to prevent this. Fixes: 113401c67386 ("media: atomisp: sh_css: Removed #ifdef ISP2401 to make code generic") Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Sasha Levin --- drivers/staging/media/atomisp/pci/sh_css.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index cd5719dc16467..cc9ca691b1ad7 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8198,7 +8198,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, int ia_css_stream_destroy(struct ia_css_stream *stream) { - int i; + int i, j; int err = 0; IA_CSS_ENTER_PRIVATE("stream = %p", stream); @@ -8229,10 +8229,10 @@ ia_css_stream_destroy(struct ia_css_stream *stream) sp_pipeline_input_terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; - for (i = 0; i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++) { + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { ia_css_isys_stream_h isys_stream = - &sp_pipeline_input_terminal->context.virtual_input_system_stream[i]; - if (stream->config.isys_config[i].valid && isys_stream->valid) + &sp_pipeline_input_terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) ia_css_isys_stream_destroy(isys_stream); } } From 3024229e4a5af93d6731634dd1b825e62844ea0f Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 19 May 2026 06:12:35 +0200 Subject: [PATCH 0799/3297] riscv: dts: spacemit: set console baud rate on Milk-V Jupiter [ Upstream commit 4f97acb4f744f516bbe976da8282c0fe213216ff ] Because the default console's baud rate is not set, defconfig kernels do not have any serial output on this platform. Set the baud rate to 115200, matching what is used by U-Boot etc on this platform. See-also: 24c12ca43b12c ("dts: spacemit: set console baud rate on bpif3") Fixes: 5b90a3d6092d9 ("riscv: dts: spacemit: Add Milk-V Jupiter board device tree") Signed-off-by: Aurelien Jarno Reviewed-by: Yixun Lan Link: https://patch.msgid.link/20260519041458.3287843-2-aurelien@aurel32.net Signed-off-by: Yixun Lan Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index 28afd39b28da3..a01f69f202e37 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -18,7 +18,7 @@ }; chosen { - stdout-path = "serial0"; + stdout-path = "serial0:115200n8"; }; }; From 1f60c3cc302d2df7330ef84de09e2c9d02fc00ef Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 2 Sep 2025 18:20:53 +0100 Subject: [PATCH 0800/3297] firmware: smccc: Fix Arm SMCCC SOC_ID name call [ Upstream commit 70492cfce2a4d41e87bf46989028a90f4bc6b38f ] Commit 5f9c23abc477 ("firmware: smccc: Support optional Arm SMCCC SOC_ID name") introduced the SOC_ID name string call, which reports a human readable string describing the SoC, as returned by firmware. The SMCCC spec v1.6 describes this feature as AArch64 only, since we rely on 8 characters to be transmitted per register. Consequently the SMCCC call must use the AArch64 calling convention, which requires bit 30 of the FID to be set. The spec is a bit confusing here, since it mentions that in the parameter description ("2: SoC name (optionally implemented for SMC64 calls, ..."), but still prints the FID explicitly as 0x80000002. But as this FID is using the SMC32 calling convention (correct for the other two calls), it will not match what any SMCCC conformant firmware is expecting, so any call would return NOT_SUPPORTED. Add a 64-bit version of the ARCH_SOC_ID FID macro, and use that for the SoC name version of the call to fix the issue. Fixes: 5f9c23abc477 ("firmware: smccc: Support optional Arm SMCCC SOC_ID name") Signed-off-by: Andre Przywara Link: https://patch.msgid.link/20250902172053.304911-1-andre.przywara@arm.com Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/smccc/soc_id.c | 2 +- include/linux/arm-smccc.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/smccc/soc_id.c b/drivers/firmware/smccc/soc_id.c index c24b3fca1cfe3..8f556df34fc42 100644 --- a/drivers/firmware/smccc/soc_id.c +++ b/drivers/firmware/smccc/soc_id.c @@ -60,7 +60,7 @@ static char __init *smccc_soc_name_init(void) * to the ARM_SMCCC_ARCH_SOC_ID function. Fetch it if * available. */ - args.a0 = ARM_SMCCC_ARCH_SOC_ID; + args.a0 = ARM_SMCCC_ARCH_SOC_ID64; args.a1 = 2; /* SOC_ID name */ arm_smccc_1_2_invoke(&args, &res); diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 50b47eba7d015..976c5f8001ff2 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -90,6 +90,11 @@ ARM_SMCCC_SMC_32, \ 0, 2) +#define ARM_SMCCC_ARCH_SOC_ID64 \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + 0, 2) + #define ARM_SMCCC_ARCH_WORKAROUND_1 \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ ARM_SMCCC_SMC_32, \ From bac3e70c2fb10d3ecfb7bff95cd520c2f2ca14d1 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Sun, 17 May 2026 20:02:40 +0100 Subject: [PATCH 0801/3297] firmware: arm_scmi: Read sensor config as 32-bit value [ Upstream commit f6fe7c3c007df18afd289ff2d98c692fae9ab085 ] The SENSOR_CONFIG_GET response contains a 32-bit sensor_config field, and the xfer is initialized with a 4-byte RX buffer. Reading it with get_unaligned_le64() can consume bytes past the returned payload. Use get_unaligned_le32() to match the protocol layout and the allocated response size. Fixes: 7b83c5f41088 ("firmware: arm_scmi: Add SCMI v3.0 sensor configuration support") Link: https://patch.msgid.link/20260517-scmi_fixes-v1-1-d86daec4defd@kernel.org Reviewed-by: Cristian Marussi Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_scmi/sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 791efd0f82d73..1be0f89fc2c4b 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -795,7 +795,7 @@ static int scmi_sensor_config_get(const struct scmi_protocol_handle *ph, if (!ret) { struct scmi_sensor_info *s = si->sensors + sensor_id; - *sensor_config = get_unaligned_le64(t->rx.buf); + *sensor_config = get_unaligned_le32(t->rx.buf); s->sensor_config = *sensor_config; } From 407aeb8c1aee8499ac303e29640327613fd556c4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 May 2026 15:07:01 +0200 Subject: [PATCH 0802/3297] sysfs: clamp show() return value in sysfs_kf_read() [ Upstream commit 454257f6d124a92342dcbb7710c03dd6ef96c731 ] sysfs_kf_seq_show() defends against buggy show() callbacks that return larger than PAGE_SIZE by clamping the value and printing a warning. sysfs_kf_read(), the prealloc variant, has no such defense. The only current in-tree user of __ATTR_PREALLOC is drivers/md/md.c, whose show() callbacks are well-behaved, so this is hardening against future drivers doing foolish things and out-of-tree code doing even more foolish things. Cc: NeilBrown Cc: Tejun Heo Fixes: 2b75869bba67 ("sysfs/kernfs: allow attributes to request write buffer be pre-allocated.") Assisted-by: gregkh_clanker_t1000 Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/2026052000-drove-unicycle-d61b@gregkh Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- fs/sysfs/file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 3825e780cc580..b25f6a951c703 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -120,6 +120,10 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf, len = ops->show(kobj, of->kn->priv, buf); if (len < 0) return len; + if (len >= (ssize_t)PAGE_SIZE) { + printk("fill_read_buffer: %pS returned bad count\n", ops->show); + len = PAGE_SIZE - 1; + } if (pos) { if (len <= pos) return 0; From 75c0bc011abddc311b07a2a393620221e3913743 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 2 May 2026 22:25:08 -0700 Subject: [PATCH 0803/3297] bitops: use common function parameter names [ Upstream commit 8a51b2e874f47a6094353b59ecae421f0968fe3a ] Fix the function prototypes to use the common parameter name 'addr' instead of 'p' (common to arch-specific implementations of these functions). This avoids the kernel-doc warnings: Warning: include/asm-generic/bitops/lock.h:19 function parameter 'p' not described in 'arch_test_and_set_bit_lock' Warning: include/asm-generic/bitops/lock.h:41 function parameter 'p' not described in 'arch_clear_bit_unlock' Warning: include/asm-generic/bitops/lock.h:59 function parameter 'p' not described in 'arch___clear_bit_unlock' Fixes: 84c6591103db ("locking/atomics, asm-generic/bitops/lock.h: Rewrite using atomic_fetch_*()") Signed-off-by: Randy Dunlap Signed-off-by: Yury Norov Signed-off-by: Sasha Levin --- include/asm-generic/bitops/lock.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/asm-generic/bitops/lock.h b/include/asm-generic/bitops/lock.h index 14d4ec8c5152d..ffb73b6129e73 100644 --- a/include/asm-generic/bitops/lock.h +++ b/include/asm-generic/bitops/lock.h @@ -16,16 +16,16 @@ * It can be used to implement bit locks. */ static __always_inline int -arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *p) +arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *addr) { long old; unsigned long mask = BIT_MASK(nr); - p += BIT_WORD(nr); - if (READ_ONCE(*p) & mask) + addr += BIT_WORD(nr); + if (READ_ONCE(*addr) & mask) return 1; - old = raw_atomic_long_fetch_or_acquire(mask, (atomic_long_t *)p); + old = raw_atomic_long_fetch_or_acquire(mask, (atomic_long_t *)addr); return !!(old & mask); } @@ -38,10 +38,10 @@ arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *p) * This operation is atomic and provides release barrier semantics. */ static __always_inline void -arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *p) +arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *addr) { - p += BIT_WORD(nr); - raw_atomic_long_fetch_andnot_release(BIT_MASK(nr), (atomic_long_t *)p); + addr += BIT_WORD(nr); + raw_atomic_long_fetch_andnot_release(BIT_MASK(nr), (atomic_long_t *)addr); } /** @@ -56,14 +56,14 @@ arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *p) * See for example x86's implementation. */ static inline void -arch___clear_bit_unlock(unsigned int nr, volatile unsigned long *p) +arch___clear_bit_unlock(unsigned int nr, volatile unsigned long *addr) { unsigned long old; - p += BIT_WORD(nr); - old = READ_ONCE(*p); + addr += BIT_WORD(nr); + old = READ_ONCE(*addr); old &= ~BIT_MASK(nr); - raw_atomic_long_set_release((atomic_long_t *)p, old); + raw_atomic_long_set_release((atomic_long_t *)addr, old); } #ifndef arch_xor_unlock_is_negative_byte From c67c672047667e5b1ff62ffea07fb7993faeae3e Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 14 May 2026 17:15:15 +0800 Subject: [PATCH 0804/3297] regulator: dt-bindings: mt6359: Drop regulator-name pattern restrictions [ Upstream commit cdc517688ffa2c30a64a20b558a9ecbf046c70f1 ] The name of the regulator should match what the board design specifies for the power rail. There should be no limitations on what the name can be, and they definitely don't always follow the PMIC's own names. Drop the restrictions on regulator-name. Fixes: 8771456635d5 ("dt-bindings: regulator: Add document for MT6359 regulator") Reviewed-by: Krzysztof Kozlowski Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260514091520.2718987-3-wenst@chromium.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- .../bindings/regulator/mt6359-regulator.yaml | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml b/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml index d6b3b5a5c0b34..5e4b1e7417950 100644 --- a/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml @@ -18,84 +18,41 @@ patternProperties: "^buck_v(s1|gpu11|modem|pu|core|s2|pa|proc2|proc1|core_sshub)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(s1|gpu11|modem|pu|core|s2|pa|proc2|proc1|core_sshub)$" - unevaluatedProperties: false "^ldo_v(ibr|rf12|usb|camio|efuse|xo22)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(ibr|rf12|usb|camio|efuse|xo22)$" - unevaluatedProperties: false "^ldo_v(rfck|emc|a12|a09|ufs|bbck)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(rfck|emc|a12|a09|ufs|bbck)$" - unevaluatedProperties: false "^ldo_vcn(18|13|33_1_bt|13_1_wifi|33_2_bt|33_2_wifi)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^vcn(18|13|33_1_bt|13_1_wifi|33_2_bt|33_2_wifi)$" - unevaluatedProperties: false "^ldo_vsram_(proc2|others|md|proc1|others_sshub)$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^vsram_(proc2|others|md|proc1|others_sshub)$" - unevaluatedProperties: false "^ldo_v(fe|bif|io)28$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(fe|bif|io)28$" - unevaluatedProperties: false "^ldo_v(aud|io|aux|rf|m)18$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^v(aud|io|aux|rf|m)18$" - unevaluatedProperties: false "^ldo_vsim[12]$": type: object $ref: regulator.yaml# - - properties: - regulator-name: - pattern: "^vsim[12]$" - - required: - - regulator-name - unevaluatedProperties: false additionalProperties: false From 16bf3154f8a8029c08cb6e9dab1892a97307cbf5 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Wed, 20 May 2026 20:19:31 +0900 Subject: [PATCH 0805/3297] tools/nolibc: getopt: Fix potential out of bounds access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 136ca91411b0b637e862eb7b1cce2a56853edd17 ] Running clang-tidy on a program that uses getopt() from nolibc this warning appears: getopt.h:80:6: warning: Out of bound access to memory after the end of the string literal [clang-analyzer-security.ArrayBound] 80 | if (optstring[i] == ':') { This looks like a very unlikely case that an argument inside of argv is being changed between getopt() calls. Adding a check for d becoming 0 in the guard after the loop stops getopt() getting far enough to access beyond the end of the array and seems to correct the issue. Fixes: bae3cd708e8a ("tools/nolibc: add getopt()") Assisted-by: Claude:claude-4.6-sonnet # reproducer Signed-off-by: Daniel Palmer Link: https://patch.msgid.link/20260520111931.1027758-1-daniel@thingy.jp [Thomas: clean up commit message a bit] Signed-off-by: Thomas Weißschuh Signed-off-by: Sasha Levin --- tools/include/nolibc/getopt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/include/nolibc/getopt.h b/tools/include/nolibc/getopt.h index 217abb95264b2..0329690941876 100644 --- a/tools/include/nolibc/getopt.h +++ b/tools/include/nolibc/getopt.h @@ -71,7 +71,7 @@ int getopt(int argc, char * const argv[], const char *optstring) d = optstring[i++]; } while (d && d != c); - if (d != c || c == ':') { + if (!d || d != c || c == ':') { optopt = c; if (optstring[0] != ':' && opterr) fprintf(stderr, "%s: unrecognized option: %c\n", argv[0], *optchar); From 276e731b1b5778c7f4180e6e7c99272ed03c62f5 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 20 May 2026 19:15:47 +0900 Subject: [PATCH 0806/3297] nilfs2: Fix return in nilfs_mkdir [ Upstream commit e5925f33e4fa9ee313d481557607adce8e30ed2e ] Return NULL instead of passing zero to ERR_PTR. Fixes smatch warning: - fs/nilfs2/namei.c:261 nilfs_mkdir() warn: passing zero to 'ERR_PTR' Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Signed-off-by: Hongling Zeng Signed-off-by: Ryusuke Konishi Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin --- fs/nilfs2/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 40f4b1a28705b..4132be9810a05 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -258,7 +258,7 @@ static struct dentry *nilfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, else nilfs_transaction_abort(dir->i_sb); - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; out_fail: drop_nlink(inode); From a88f6da618e8b441b28eb753d2d5f6d9af9909d1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 May 2026 09:46:17 +0000 Subject: [PATCH 0807/3297] net/sched: sch_drr: annotate data-races around cl->deficit [ Upstream commit c67b104fd7982162885c5e43057ca761006748e2 ] drr_dump_class_stats() runs without qdisc spinlock held. Add missing READ_ONCE()/WRITE_ONCE() annotations around cl->deficit. Fixes: edb09eb17ed8 ("net: sched: do not acquire qdisc spinlock in qdisc/class stats dump") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260519094618.2632073-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_drr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 9b6d79bd87371..44686fd1c2749 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -270,7 +270,7 @@ static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, memset(&xstats, 0, sizeof(xstats)); if (qlen) - xstats.deficit = cl->deficit; + xstats.deficit = READ_ONCE(cl->deficit); if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || @@ -362,7 +362,7 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (!cl_is_active(cl)) { list_add_tail(&cl->alist, &q->active); - cl->deficit = cl->quantum; + WRITE_ONCE(cl->deficit, cl->quantum); } sch->qstats.backlog += len; @@ -389,7 +389,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) len = qdisc_pkt_len(skb); if (len <= cl->deficit) { - cl->deficit -= len; + WRITE_ONCE(cl->deficit, cl->deficit - len); skb = qdisc_dequeue_peeked(cl->qdisc); if (unlikely(skb == NULL)) goto out; @@ -403,7 +403,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) return skb; } - cl->deficit += cl->quantum; + WRITE_ONCE(cl->deficit, cl->deficit + cl->quantum); list_move_tail(&cl->alist, &q->active); } out: From 15e7368de5842ffb248b0643cd40f3a57b2c1b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:11 +0200 Subject: [PATCH 0808/3297] media: rockchip: rga: fix too small buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 65017e26c065d1240b0512c15867ef1128034fd8 ] Fix the command buffer size being only a quarter of the actual size. The RGA_CMDBUF_SIZE macro was potentially intended to specify the length of the cmdbuf u32 array pointer. But as it's used to specify the size of the allocation, which is counted in bytes. Therefore adjust the macro size to bytes as it better matches the variable name and adjust it's users accordingly. As the command buffer is relatively small, it probably didn't caused an issue due to being smaller than a single page. Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support") Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/platform/rockchip/rga/rga-hw.c | 2 +- drivers/media/platform/rockchip/rga/rga-hw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 43ed742a16492..d1618bb247501 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -414,7 +414,7 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4); + memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index cc6bd7f5b0300..2b8537a5fd0d7 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -6,7 +6,7 @@ #ifndef __RGA_HW_H__ #define __RGA_HW_H__ -#define RGA_CMDBUF_SIZE 0x20 +#define RGA_CMDBUF_SIZE 0x80 /* Hardware limits */ #define MAX_WIDTH 8192 From 11daac2817dca75e3eabb2050bc620c29e686fcd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 15 May 2026 11:59:15 +0200 Subject: [PATCH 0809/3297] firmware: arm_scmi: Fix OOB in scmi_power_name_get() [ Upstream commit f9ef3f66f4b18078e464b7606f9497e4dbeb9905 ] scmi_power_name_get() does not validate the domain number passed by the external caller, which may lead to an out-of-bounds access. Fix this by returning "unknown" for invalid domains, like scmi_reset_name_get() does. Fixes: 76a6550990e296a7 ("firmware: arm_scmi: add initial support for power protocol") Signed-off-by: Geert Uytterhoeven Reviewed-by: Cristian Marussi Link: https://patch.msgid.link/75caae28bdffb55199a0bc6cac5df112a966c608.1778838987.git.geert+renesas@glider.be Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_scmi/power.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 59aa16444c643..7d3e5f3fc379b 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -205,8 +205,12 @@ scmi_power_name_get(const struct scmi_protocol_handle *ph, u32 domain) { struct scmi_power_info *pi = ph->get_priv(ph); - struct power_dom_info *dom = pi->dom_info + domain; + struct power_dom_info *dom; + + if (domain >= pi->num_domains) + return "unknown"; + dom = pi->dom_info + domain; return dom->name; } From b3e05859cb516331f27f312651f5934f9f15408c Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:21 +0530 Subject: [PATCH 0810/3297] arm64: dts: qcom: lemans: Add power-domain and iface clk for ice node [ Upstream commit 04566e287b35fde9fd129db5fdf6a96e336af55c ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for lemans. Fixes: 96272ba7103d4 ("arm64: dts: qcom: sa8775p: enable the inline crypto engine") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-4-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/lemans.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index ef20ea941aa91..68c0fe6706cb8 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -2753,7 +2753,11 @@ compatible = "qcom,sa8775p-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From a2303478e7301ff33b8309096715d8ecd92b0fee Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:22 +0530 Subject: [PATCH 0811/3297] arm64: dts: qcom: monaco: Add power-domain and iface clk for ice node [ Upstream commit 68d5d9701a7ab1b1f9c76feaa3a24ca716f03f0b ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for monaco. Fixes: cc9d29aad876d ("arm64: dts: qcom: qcs8300: enable the inline crypto engine") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-5-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/qcs8300.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/qcs8300.dtsi b/arch/arm64/boot/dts/qcom/qcs8300.dtsi index 8580884a16e98..501814babed15 100644 --- a/arch/arm64/boot/dts/qcom/qcs8300.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs8300.dtsi @@ -2351,7 +2351,11 @@ compatible = "qcom,qcs8300-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From dc36463b283d9b9a236bbbf6b57ee74fcd3a6bb8 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:23 +0530 Subject: [PATCH 0812/3297] arm64: dts: qcom: sc7180: Add power-domain and iface clk for ice node [ Upstream commit 7cd7271ac525e4eadd22734f418219f247638f43 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sc7180. Fixes: 858536d9dc946 ("arm64: dts: qcom: sc7180: Add UFS nodes") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-6-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc7180.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi index a0df10a97c7f8..892b3d2f1bf99 100644 --- a/arch/arm64/boot/dts/qcom/sc7180.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi @@ -1599,7 +1599,11 @@ compatible = "qcom,sc7180-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0 0x01d90000 0 0x8000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; ipa: ipa@1e40000 { From a8be1bc8d124fb8af0e0c8cd572280edd0def8a8 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:24 +0530 Subject: [PATCH 0813/3297] arm64: dts: qcom: kodiak: Add power-domain and iface clk for ice node [ Upstream commit cca53c338ad87edc4b46d2d82730fd8ca01a164f ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for kodiak. Fixes: dfd5ee7b34bb7 ("arm64: dts: qcom: sc7280: Add inline crypto engine") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Tested-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-7-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sc7280.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index 07cabbb2058f8..ed87bcb0d4b69 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -2574,7 +2574,11 @@ compatible = "qcom,sc7280-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From 0d0ce8c7025ac9d0db9f32c8d00ea4e57cca0f5c Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:25 +0530 Subject: [PATCH 0814/3297] arm64: dts: qcom: sm8450: Add power-domain and iface clk for ice node [ Upstream commit 3a5cb1ccbfb3141862b28f24cd5050083233aae7 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8450. Fixes: 86b0aef435851 ("arm64: dts: qcom: sm8450: Use standalone ICE node for UFS") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-8-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8450.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index 3a352e5f3f191..2d49df7b69e4b 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -5349,7 +5349,11 @@ compatible = "qcom,sm8450-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From 3c808cac676c5058b492e759272f380520f1465f Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:26 +0530 Subject: [PATCH 0815/3297] arm64: dts: qcom: sm8550: Add power-domain and iface clk for ice node [ Upstream commit 52696dbbe7bbe0c8fc8c17133ffb5133b8cf37a6 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8550. Fixes: b8630c48b43fc ("arm64: dts: qcom: sm8550: Add the Inline Crypto Engine node") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-9-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8550.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi index 118a626dfcff5..479c94f5066f3 100644 --- a/arch/arm64/boot/dts/qcom/sm8550.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi @@ -2414,7 +2414,11 @@ "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From 4ba44339fd3ac10849caac1cb478e4e8254077e1 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:27 +0530 Subject: [PATCH 0816/3297] arm64: dts: qcom: sm8650: Add power-domain and iface clk for ice node [ Upstream commit c62b084d5d1564f808408a2f7d4c514e57cd4106 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8650. Fixes: 10e0246712951 ("arm64: dts: qcom: sm8650: add interconnect dependent device nodes") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-10-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8650.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi index 069b7f1267a0e..6c775ef20cb0a 100644 --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi @@ -4046,7 +4046,11 @@ "qcom,inline-crypto-engine"; reg = <0 0x01d88000 0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From 09a2a7d041a78d9113cc7fb8bf53f8a3a4f0315e Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:28 +0530 Subject: [PATCH 0817/3297] arm64: dts: qcom: sm8750: Add power-domain and iface clk for ice node [ Upstream commit 081ac792f0ea6d27a4b130c70cfd7544efee8137 ] Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for sm8750. Fixes: b1dac789c650a ("arm64: dts: qcom: sm8750: Add ICE nodes") Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-11-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/sm8750.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sm8750.dtsi b/arch/arm64/boot/dts/qcom/sm8750.dtsi index 3e1d1133792bb..2760c4f7e6f35 100644 --- a/arch/arm64/boot/dts/qcom/sm8750.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8750.dtsi @@ -2057,7 +2057,11 @@ "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; cryptobam: dma-controller@1dc4000 { From baa333b2700a7fa3529d504529c8c10060fd687c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 30 Apr 2026 12:33:50 +0800 Subject: [PATCH 0818/3297] tracing: Bound synthetic-field strings with seq_buf [ Upstream commit f07883450eb14d1cf020b55d9f3a7ec5683bcd26 ] The synthetic field helpers build a prefixed synthetic variable name and a generated hist command in fixed MAX_FILTER_STR_VAL buffers. The current code appends those strings with raw strcat(), so long key lists, field names, or saved filters can run past the end of the staging buffers. Build both strings with seq_buf and propagate -E2BIG if either the synthetic variable name or the generated command exceeds MAX_FILTER_STR_VAL. This keeps the existing tracing-side limit while using the helper intended for bounded command construction. Cc: Mathieu Desnoyers Cc: Tom Zanussi Link: https://patch.msgid.link/20260430043350.57928-1-pengpeng@iscas.ac.cn Fixes: 02205a6752f2 ("tracing: Add support for 'field variables'") Acked-by: Masami Hiramatsu (Google) Reviewed-by: Tom Zanussi Signed-off-by: Pengpeng Hou [ sdr: Moved struct seq_buf *s for upside-down x-mas tree formatting ] Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace_events_hist.c | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 0089c257b465f..cf2a4388845d6 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -2957,13 +2958,22 @@ find_synthetic_field_var(struct hist_trigger_data *target_hist_data, { struct hist_field *event_var; char *synthetic_name; + struct seq_buf s; synthetic_name = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); if (!synthetic_name) return ERR_PTR(-ENOMEM); - strcpy(synthetic_name, "synthetic_"); - strcat(synthetic_name, field_name); + seq_buf_init(&s, synthetic_name, MAX_FILTER_STR_VAL); + seq_buf_printf(&s, "synthetic_%s", field_name); + + /* Terminate synthetic_name with a NUL. */ + seq_buf_str(&s); + + if (seq_buf_has_overflowed(&s)) { + kfree(synthetic_name); + return ERR_PTR(-E2BIG); + } event_var = find_event_var(target_hist_data, system, event_name, synthetic_name); @@ -3009,6 +3019,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data, struct hist_field *key_field; struct hist_field *event_var; char *saved_filter; + struct seq_buf s; char *cmd; int ret; @@ -3053,28 +3064,34 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data, return ERR_PTR(-ENOMEM); } + seq_buf_init(&s, cmd, MAX_FILTER_STR_VAL); + /* Use the same keys as the compatible histogram */ - strcat(cmd, "keys="); + seq_buf_puts(&s, "keys="); for_each_hist_key_field(i, hist_data) { key_field = hist_data->fields[i]; if (!first) - strcat(cmd, ","); - strcat(cmd, key_field->field->name); + seq_buf_putc(&s, ','); + seq_buf_puts(&s, key_field->field->name); first = false; } /* Create the synthetic field variable specification */ - strcat(cmd, ":synthetic_"); - strcat(cmd, field_name); - strcat(cmd, "="); - strcat(cmd, field_name); + seq_buf_printf(&s, ":synthetic_%s=%s", field_name, field_name); /* Use the same filter as the compatible histogram */ saved_filter = find_trigger_filter(hist_data, file); - if (saved_filter) { - strcat(cmd, " if "); - strcat(cmd, saved_filter); + if (saved_filter) + seq_buf_printf(&s, " if %s", saved_filter); + + /* Terminate cmd with a NUL. */ + seq_buf_str(&s); + + if (seq_buf_has_overflowed(&s)) { + kfree(cmd); + kfree(var_hist); + return ERR_PTR(-E2BIG); } var_hist->cmd = kstrdup(cmd, GFP_KERNEL); From 2469039f0fd687ed351591984622e763196bff32 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Wed, 28 Jan 2026 17:18:50 +0530 Subject: [PATCH 0819/3297] arm64: dts: qcom: lemans: Add eDP ref clock for eDP PHYs [ Upstream commit 4bd073e00fd79c0aead74ad64ade48c904221245 ] The eDP PHY nodes on lemans were missing the reference clock voting. This initially went unnoticed because the clock was implicitly enabled by the UFS PHY driver, and the eDP PHY happened to rely on that. After commit 77d2fa54a945 ("scsi: ufs: qcom : Refactor phy_power_on/off calls"), the UFS driver no longer keeps the reference clock enabled. As a result, the eDP PHY fails to power on. To fix this, add eDP reference clock for eDP PHYs on lemans chipset ensuring reference clock is enabled. Fixes: e1e3e5673f8d7 ("arm64: dts: qcom: sa8775p: add DisplayPort device nodes") Signed-off-by: Ritesh Kumar Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260128114853.2543416-3-quic_riteshk@quicinc.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/qcom/lemans.dtsi | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index 68c0fe6706cb8..5c00d2e89fb8c 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -5039,9 +5039,11 @@ <0x0 0x0aec2000 0x0 0x1c8>; clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>, - <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>; + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_EDP_REF_CLKREF_EN>; clock-names = "aux", - "cfg_ahb"; + "cfg_ahb", + "ref"; #clock-cells = <1>; #phy-cells = <0>; @@ -5058,9 +5060,11 @@ <0x0 0x0aec5000 0x0 0x1c8>; clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX1_AUX_CLK>, - <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>; + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_EDP_REF_CLKREF_EN>; clock-names = "aux", - "cfg_ahb"; + "cfg_ahb", + "ref"; #clock-cells = <1>; #phy-cells = <0>; From 29de8448174cfd93431316c1560e9bc2966b95f3 Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Thu, 21 May 2026 17:50:15 +0800 Subject: [PATCH 0820/3297] writeback: drop now-unnecessary rcu_barrier() in cgroup_writeback_umount() [ Upstream commit e90a6d668e26e00a72df2d09c173b563468f09c9 ] Commit e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes") replaced the queue_rcu_work() based scheduling of inode wb switches with a plain queue_work(). Since then no switcher goes through call_rcu(), so rcu_barrier() in cgroup_writeback_umount() has no callbacks of its own to wait for. It still drains unrelated call_rcu() callbacks from other subsystems on busy systems, which incidentally slows umount down; drop it. Fixes: e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes") Reviewed-by: Jan Kara Signed-off-by: Baokun Li Link: https://patch.msgid.link/20260521095016.2791354-3-libaokun@linux.alibaba.com Acked-by: Tejun Heo Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/fs-writeback.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 99d0658b49a22..d2510c818da9a 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1237,11 +1237,6 @@ void cgroup_writeback_umount(struct super_block *sb) * will then drain it. */ synchronize_rcu(); - /* - * Use rcu_barrier() to wait for all pending callbacks to - * ensure that all in-flight wb switches are in the workqueue. - */ - rcu_barrier(); flush_workqueue(isw_wq); } } From b0444205ba39e44c27f3ebfb7994f0e9a546ed34 Mon Sep 17 00:00:00 2001 From: Conor Kotwasinski Date: Thu, 16 Apr 2026 09:43:15 -0400 Subject: [PATCH 0821/3297] kernfs: fix suspicious RCU usage in kernfs_put() [ Upstream commit 0fdde3f2aeadcf8d090ee3edee0aad73fb91f690 ] Commit 741c10b096bc ("kernfs: Use RCU to access kernfs_node::name.") converted the WARN_ONCE() in kernfs_put() to read kn->name and parent->name via rcu_dereference(), but kernfs_put() has callers that hold neither kernfs_rwsem nor the RCU read lock. The inode eviction path driven by memory reclaim is one such case: kernfs_put+0x53/0x60 fs/kernfs/dir.c:602 evict+0x3c2/0xad0 fs/inode.c:846 iput_final fs/inode.c:1966 [inline] iput.part.0+0x605/0xf50 fs/inode.c:2015 iput+0x35/0x40 fs/inode.c:1981 dentry_unlink_inode+0x2a1/0x490 fs/dcache.c:467 __dentry_kill+0x1d0/0x600 fs/dcache.c:670 shrink_dentry_list+0x180/0x5e0 fs/dcache.c:1174 prune_dcache_sb+0xea/0x150 fs/dcache.c:1256 super_cache_scan+0x328/0x550 fs/super.c:223 ... kswapd+0x556/0xba0 mm/vmscan.c:7343 lockdep complains with "suspicious RCU usage" whenever the WARN fires from such a context. Wrap the rcu_dereference() calls in an RCU read-side critical section. Gate on the active-ref check so the lock is only taken when the WARN is about to fire. Note that this does not address the underlying imbalance in kn->active that triggers the WARN. Fixes: 741c10b096bc ("kernfs: Use RCU to access kernfs_node::name.") Reported-by: syzbot+0dfe499ea713e0a15bec@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0dfe499ea713e0a15bec Signed-off-by: Conor Kotwasinski Acked-by: Tejun Heo Link: https://patch.msgid.link/20260416134315.1474726-1-conorkotwasinski2024@u.northwestern.edu Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- fs/kernfs/dir.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index a670ba3e565e0..360cfbd8dd36e 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -576,10 +576,13 @@ void kernfs_put(struct kernfs_node *kn) */ parent = kernfs_parent(kn); - WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, - "kernfs_put: %s/%s: released with incorrect active_ref %d\n", - parent ? rcu_dereference(parent->name) : "", - rcu_dereference(kn->name), atomic_read(&kn->active)); + if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) { + guard(rcu)(); + WARN_ONCE(1, + "kernfs_put: %s/%s: released with incorrect active_ref %d\n", + parent ? rcu_dereference(parent->name) : "", + rcu_dereference(kn->name), atomic_read(&kn->active)); + } if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); From 62c8cc825f49e20120d893c9171b8d7b055f9493 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 22:14:55 +0500 Subject: [PATCH 0822/3297] device property: fix fwnode reference leak in fwnode_graph_get_endpoint_by_id() [ Upstream commit 9582485a65eacfd7245ec7f0a9d7e2c34749d669 ] When called with FWNODE_GRAPH_ENDPOINT_NEXT, the function walks every endpoint under the requested port and, for any endpoint whose ID is greater than or equal to the requested one, may store a fwnode reference in best_ep via fwnode_handle_get(). If a later iteration finds an exact-ID match, the function returns that endpoint directly without dropping the reference held by best_ep, leaking it. Drop the saved candidate before returning the exact-match endpoint. This affects callers that use FWNODE_GRAPH_ENDPOINT_NEXT to ask for the next endpoint with ID >= the requested one (used by a number of media drivers, e.g. imx7/8, sun6i CSI, omap3isp, xilinx-csi2, stm32-csi). Each leak retains a fwnode reference until reboot/unbind. Fixes: 0fcc2bdc8aff ("device property: Add fwnode_graph_get_endpoint_by_id()") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260514171455.27271-1-sozdayvek@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/property.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 8d9a34be57fbf..b2dc2bd7e5aca 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1267,8 +1267,10 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, if (fwnode_ep.port != port) continue; - if (fwnode_ep.id == endpoint) + if (fwnode_ep.id == endpoint) { + fwnode_handle_put(best_ep); return ep; + } if (!endpoint_next) continue; From 2df37ead6d84b8df5340edad444ca5f7edeec0a3 Mon Sep 17 00:00:00 2001 From: Zhang Yuwei Date: Fri, 10 Apr 2026 10:44:48 +0800 Subject: [PATCH 0823/3297] driver core: Use mod_delayed_work to prevent lost deferred probe work [ Upstream commit 1137838865bfc9a7cd5869c1dc5c22aa45ec12c8 ] The deferred_probe_timeout_work may be permanently and unexpectedly canceled when deferred_probe_extend_timeout() executes concurrently. Starting with deferred_probe_timeout_work pending, the problem can occur after the following sequence: CPU0 CPU1 deferred_probe_extend_timeout -> cancel_delayed_work() => true deferred_probe_extend_timeout -> cancel_delayed_work() -> __cancel_work() -> try_grab_pending() -> schedule_delayed_work() -> queue_delayed_work_on() (Since the pending bit is grabbed, it just returns without queuing) -> set_work_pool_and_clear_pending() (This __cancel_work() returns false and the work will never be queued again) The root cause is that the WORK_STRUCT_PENDING_BIT of the work_struct is set temporarily in __cancel_work() (via try_grab_pending()). This transient state prevents the work_struct from being successfully queued by another CPU. To fix this, replace the original non-atomic cancel and schedule mechanism with mod_delayed_work(). This ensures the modification is handled atomically and guarantees that the work is not lost. Fixes: 2b28a1a84a0e ("driver core: Extend deferred probe timeout on driver registration") Signed-off-by: Zhang Yuwei Link: https://patch.msgid.link/20260410024448.387231-1-zhangyuwei20@huawei.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/dd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 2c3a610f52a74..c42643e26e28a 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -327,12 +327,10 @@ void deferred_probe_extend_timeout(void) * If the work hasn't been queued yet or if the work expired, don't * start a new one. */ - if (cancel_delayed_work(&deferred_probe_timeout_work)) { - schedule_delayed_work(&deferred_probe_timeout_work, - driver_deferred_probe_timeout * HZ); + if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, + driver_deferred_probe_timeout)) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); - } } /** From ae62edb00c0ebe99f7dadd0c6c24789aa52979a4 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 11 May 2026 17:57:48 +0200 Subject: [PATCH 0824/3297] Revert "treewide: Fix probing of devices in DT overlays" [ Upstream commit aaf08c52df9a19148731d4a3cfd85d98455db901 ] This reverts commit 1a50d9403fb90cbe4dea0ec9fd0351d2ecbd8924. While the commit fixed fw_devlink overlay handling for one case, it broke it for another case. So revert it and redo the fix in a separate patch. Fixes: 1a50d9403fb9 ("treewide: Fix probing of devices in DT overlays") Reported-by: Herve Codina Closes: https://lore.kernel.org/lkml/CAMuHMdXEnSD4rRJ-o90x4OprUacN_rJgyo8x6=9F9rZ+-KzjOg@mail.gmail.com/ Closes: https://lore.kernel.org/all/20240221095137.616d2aaa@bootlin.com/ Closes: https://lore.kernel.org/lkml/20240312151835.29ef62a0@bootlin.com/ Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/lkml/20240411235623.1260061-2-saravanak@google.com/ [Herve: Fix conflicts due to f72e77c33e4b ("device property: Make modifications of fwnode "flags" thread safe")] Signed-off-by: Herve Codina Acked-by: Mark Brown Acked-by: Rob Herring (Arm) Acked-by: Wolfram Sang # for I2C Link: https://patch.msgid.link/20260511155755.34428-2-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/bus/imx-weim.c | 6 ------ drivers/i2c/i2c-core-of.c | 5 ----- drivers/of/dynamic.c | 1 - drivers/of/platform.c | 5 ----- drivers/spi/spi.c | 5 ----- 5 files changed, 22 deletions(-) diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index f735e0462c55e..87070155b0572 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -327,12 +327,6 @@ static int of_weim_notify(struct notifier_block *nb, unsigned long action, "Failed to setup timing for '%pOF'\n", rd->dn); if (!of_node_check_flag(rd->dn, OF_POPULATED)) { - /* - * Clear the flag before adding the device so that - * fw_devlink doesn't skip adding consumers to this - * device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { dev_err(&pdev->dev, "Failed to create child device '%pOF'\n", diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index 354a88d0599e3..30b48a428c0be 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -176,11 +176,6 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); client = of_i2c_register_device(adap, rd->dn); if (IS_ERR(client)) { dev_err(&adap->dev, "failed to create client for '%pOF'\n", diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index ab10c180c4887..b5be7484fb36d 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -225,7 +225,6 @@ static void __of_attach_node(struct device_node *np) np->sibling = np->parent->child; np->parent->child = np; of_node_clear_flag(np, OF_DETACHED); - fwnode_set_flag(&np->fwnode, FWNODE_FLAG_NOT_DEVICE); raw_spin_unlock_irqrestore(&devtree_lock, flags); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index da023bc0b5faf..1c63f15fc7b62 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -739,11 +739,6 @@ static int of_platform_notify(struct notifier_block *nb, if (of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); /* pdev_parent may be NULL when no bus platform device */ pdev_parent = of_find_device_by_node(parent); pdev = of_platform_device_create(rd->dn, NULL, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ac4de4703a6f0..6ea9ac2931cde 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -4810,11 +4810,6 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_OK; } - /* - * Clear the flag before adding the device so that fw_devlink - * doesn't skip adding consumers to this device. - */ - fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); spi = of_register_spi_device(ctlr, rd->dn); put_device(&ctlr->dev); From 4a6f5cc42c7bf7cd6f982aaf517289abcf1a5dc1 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 11 May 2026 17:57:49 +0200 Subject: [PATCH 0825/3297] of: dynamic: Fix overlayed devices not probing because of fw_devlink [ Upstream commit 81e7c6befa36cecdcbf7244393bd67e8f8c59bf5 ] When an overlay is applied, if the target device has already probed successfully and bound to a device, then some of the fw_devlink logic that ran when the device was probed needs to be rerun. This allows newly created dangling consumers of the overlayed device tree nodes to be moved to become consumers of the target device. [Herve: Add the call to driver_deferred_probe_trigger()] [Herve: Use fwnode_test_flag() to test fwnode flags value] Fixes: 1a50d9403fb9 ("treewide: Fix probing of devices in DT overlays") Reported-by: Herve Codina Closes: https://lore.kernel.org/lkml/CAMuHMdXEnSD4rRJ-o90x4OprUacN_rJgyo8x6=9F9rZ+-KzjOg@mail.gmail.com/ Closes: https://lore.kernel.org/all/20240221095137.616d2aaa@bootlin.com/ Closes: https://lore.kernel.org/lkml/20240312151835.29ef62a0@bootlin.com/ Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/lkml/20240411235623.1260061-3-saravanak@google.com/ [Herve: Rebase on top of recent kernel] Signed-off-by: Herve Codina Tested-by: Kalle Niemi Tested-by: Geert Uytterhoeven Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260511155755.34428-3-herve.codina@bootlin.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/base/core.c | 83 +++++++++++++++++++++++++++++++++++++----- drivers/of/overlay.c | 15 ++++++++ include/linux/fwnode.h | 1 + 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 01e14f787916a..5034d9b103642 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -235,6 +235,79 @@ static void __fw_devlink_pickup_dangling_consumers(struct fwnode_handle *fwnode, __fw_devlink_pickup_dangling_consumers(child, new_sup); } +static void fw_devlink_pickup_dangling_consumers(struct device *dev) +{ + struct fwnode_handle *child; + + guard(mutex)(&fwnode_link_lock); + + fwnode_for_each_available_child_node(dev->fwnode, child) + __fw_devlink_pickup_dangling_consumers(child, dev->fwnode); + __fw_devlink_link_to_consumers(dev); +} + +/** + * fw_devlink_refresh_fwnode - Recheck the tree under this firmware node + * @fwnode: The fwnode under which the fwnode tree has changed + * + * This function is mainly meant to adjust the supplier/consumer dependencies + * after a fwnode tree overlay has occurred. + */ +void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode) +{ + struct device *dev; + + /* + * Find the closest ancestor fwnode that has been converted to a device + * that can bind to a driver (bus device). + */ + fwnode_handle_get(fwnode); + do { + if (fwnode_test_flag(fwnode, FWNODE_FLAG_NOT_DEVICE)) + continue; + + dev = get_dev_from_fwnode(fwnode); + if (!dev) + continue; + + if (dev->bus) + break; + + put_device(dev); + } while ((fwnode = fwnode_get_next_parent(fwnode))); + + /* + * If none of the ancestor fwnodes have (yet) been converted to a device + * that can bind to a driver, there's nothing to fix up. + */ + if (!fwnode) + return; + + WARN(device_is_bound(dev) && dev->links.status != DL_DEV_DRIVER_BOUND, + "Don't multithread overlaying and probing the same device!\n"); + + /* + * If the device has already bound to a driver, then we need to redo + * some of the work that was done after the device was bound to a + * driver. If the device hasn't bound to a driver, running things too + * soon would incorrectly pick up consumers that it shouldn't. + */ + if (dev->links.status == DL_DEV_DRIVER_BOUND) { + fw_devlink_pickup_dangling_consumers(dev); + /* + * Some of dangling consumers could have been put previously in + * the deferred probe list due to the unavailability of their + * suppliers. Those consumers have been picked up and some of + * their suppliers links have been updated. Time to re-try their + * probe sequence. + */ + driver_deferred_probe_trigger(); + } + + put_device(dev); + fwnode_handle_put(fwnode); +} + static DEFINE_MUTEX(device_links_lock); DEFINE_STATIC_SRCU(device_links_srcu); @@ -1312,16 +1385,8 @@ void device_links_driver_bound(struct device *dev) * child firmware node. */ if (dev->fwnode && dev->fwnode->dev == dev) { - struct fwnode_handle *child; - fwnode_links_purge_suppliers(dev->fwnode); - - guard(mutex)(&fwnode_link_lock); - - fwnode_for_each_available_child_node(dev->fwnode, child) - __fw_devlink_pickup_dangling_consumers(child, - dev->fwnode); - __fw_devlink_link_to_consumers(dev); + fw_devlink_pickup_dangling_consumers(dev); } device_remove_file(dev, &dev_attr_waiting_for_supplier); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 255e8362f600a..1528d8ad9f268 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -185,6 +185,15 @@ static int overlay_notify(struct overlay_changeset *ovcs, return 0; } +static void overlay_fw_devlink_refresh(struct overlay_changeset *ovcs) +{ + for (int i = 0; i < ovcs->count; i++) { + struct device_node *np = ovcs->fragments[i].target; + + fw_devlink_refresh_fwnode(of_fwnode_handle(np)); + } +} + /* * The values of properties in the "/__symbols__" node are paths in * the ovcs->overlay_root. When duplicating the properties, the paths @@ -951,6 +960,12 @@ static int of_overlay_apply(struct overlay_changeset *ovcs, pr_err("overlay apply changeset entry notify error %d\n", ret); /* notify failure is not fatal, continue */ + /* + * Needs to happen after changeset notify to give the listeners a chance + * to finish creating all the devices they need to create. + */ + overlay_fw_devlink_refresh(ovcs); + ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY); if (ret_tmp) if (!ret) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index c30a9baafc0d9..4e86e6990d284 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -253,6 +253,7 @@ int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup, u8 flags); void fwnode_links_purge(struct fwnode_handle *fwnode); void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode); +void fw_devlink_refresh_fwnode(struct fwnode_handle *fwnode); bool fw_devlink_is_strict(void); #endif From 376951c51cdd02334eab7e1c65e2da0c67522e71 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Sat, 16 May 2026 14:26:51 +0200 Subject: [PATCH 0826/3297] crypto: eip93 - fix reset ring register definition [ Upstream commit 09e6b79b8ce388993aec9ac91b1cb2c181c27bd9 ] This patch fixes a descriptor ring reset. This causes a hang in the driver's unload/load sequence. Fixes: 9739f5f93b78 ("crypto: eip93 - Add Inside Secure SafeXcel EIP-93 crypto engine support") Suggested-by: Benjamin Larsson Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/inside-secure/eip93/eip93-regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/inside-secure/eip93/eip93-regs.h b/drivers/crypto/inside-secure/eip93/eip93-regs.h index 116b3fbb6ad79..194436837147c 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-regs.h +++ b/drivers/crypto/inside-secure/eip93/eip93-regs.h @@ -103,7 +103,7 @@ #define EIP93_PE_TARGET_COMMAND_NO_RDR_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x2) #define EIP93_PE_TARGET_COMMAND_WITH_RDR_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x1) #define EIP93_PE_DIRECT_HOST_MODE FIELD_PREP(EIP93_PE_CONFIG_PE_MODE, 0x0) -#define EIP93_PE_CONFIG_RST_RING BIT(2) +#define EIP93_PE_CONFIG_RST_RING BIT(1) #define EIP93_PE_CONFIG_RST_PE BIT(0) #define EIP93_REG_PE_STATUS 0x104 #define EIP93_REG_PE_BUF_THRESH 0x10c From 3fcbfc50dd52f12d9f964dad57c23d9a213f9662 Mon Sep 17 00:00:00 2001 From: Pengjie Zhang Date: Mon, 18 May 2026 21:34:57 +0800 Subject: [PATCH 0827/3297] cpufreq: Documentation: fix sampling_down_factor range [ Upstream commit 85524e651d20944399322d46fb97960337831d43 ] The ondemand governor implementation accepts sampling_down_factor values from 1 to 100000 via MAX_SAMPLING_DOWN_FACTOR, but the documentation in admin-guide/pm/cpufreq.rst still says the valid range is 1 to 100. Update the documentation to match the actual code. Fixes: 2a0e49279850 ("cpufreq: User/admin documentation update and consolidation") Reviewed-by: Zhongqiu Han Signed-off-by: Pengjie Zhang Link: https://patch.msgid.link/20260518133457.2408463-1-zhangpengjie2@huawei.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- Documentation/admin-guide/pm/cpufreq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index 738d7b4dc33af..33122fbe9f6a0 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -516,7 +516,7 @@ This governor exposes the following tunables: of those tasks above 0 and set this attribute to 1. ``sampling_down_factor`` - Temporary multiplier, between 1 (default) and 100 inclusive, to apply to + Temporary multiplier, between 1 (default) and 100000 inclusive, to apply to the ``sampling_rate`` value if the CPU load goes above ``up_threshold``. This causes the next execution of the governor's worker routine (after From 13db458099f28963a088f4f2b7c9be2025f8ac7a Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 22 May 2026 09:49:10 +0530 Subject: [PATCH 0828/3297] cpufreq: conservative: Simplify frequency limit handling [ Upstream commit 3494dff89779b73a6c70481c982e0e96d336454a ] cs_dbs_update() performs explicit checks against policy->min/max before updating the target frequency. These checks are redundant as __cpufreq_driver_target() already clamps the requested frequency to the valid policy limits. Remove the unnecessary boundary checks and simplify the update logic. This also fixes an issue introduced by commit 00bfe05889e9 ("cpufreq: conservative: Decrease frequency faster for deferred updates"), where stale target comparisons could cause frequency updates to be skipped entirely after deferred adjustments. Closes: https://lore.kernel.org/all/20260421123545.1745998-1-zhenglifeng1@huawei.com/ Fixes: 00bfe05889e9 ("cpufreq: conservative: Decrease frequency faster for deferred updates") Signed-off-by: Lifeng Zheng Co-developed-by: Viresh Kumar Signed-off-by: Viresh Kumar Reviewed-by: Zhongqiu Han Link: https://patch.msgid.link/292e6d937890f135e30ec0d2107eaad47cb9a976.1779423281.git.viresh.kumar@linaro.org Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/cpufreq/cpufreq_conservative.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 305a32b6b302e..60bab2b71b1b9 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -103,10 +103,6 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) if (load > dbs_data->up_threshold) { dbs_info->down_skip = 0; - /* if we are already at full speed then break out early */ - if (requested_freq == policy->max) - goto out; - requested_freq += freq_step; if (requested_freq > policy->max) requested_freq = policy->max; @@ -124,13 +120,7 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) /* Check for frequency decrease */ if (load < cs_tuners->down_threshold) { - /* - * if we cannot reduce the frequency anymore, break out early - */ - if (requested_freq == policy->min) - goto out; - - if (requested_freq > freq_step) + if (requested_freq > policy->min + freq_step) requested_freq -= freq_step; else requested_freq = policy->min; From c11039512df495b6e9cfdb2ab25341aa09b2473f Mon Sep 17 00:00:00 2001 From: Ronaldo Nunez Date: Fri, 22 May 2026 16:13:48 -0300 Subject: [PATCH 0829/3297] pwm: imx27: Fix variable truncation in .apply() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit dc9e08fdbcc3eb08a1d2b868b535081c44425e27 ] Fix a variable truncation when calculating period in microseconds as part of the solution for the ERR051198 in .apply() callback. Example scenario: - Period of 3us (PWMPR = 196 and prescaler = 1) - Expected value in tmp: 198000000000 (NSEC_PER_SEC * (196 + 2) * 1) - Actual value is 431504384 (truncation to u32) Signed-off-by: Ronaldo Nunez Reviewed-by: Frank Li Link: https://patch.msgid.link/20260522191348.6227-1-rnunez@baylibre.com Fixes: a25351e4c774 ("pwm: imx27: Workaround of the pwm output bug when decrease the duty cycle") Signed-off-by: Uwe Kleine-König Signed-off-by: Sasha Levin --- drivers/pwm/pwm-imx27.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 3d34cdc4a3a51..c8b801fcb5251 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -200,7 +200,7 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - unsigned long period_cycles, duty_cycles, prescale, period_us, tmp; + unsigned long period_cycles, duty_cycles, prescale, period_us; struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); unsigned long long c; unsigned long long clkrate; @@ -208,6 +208,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, int val; int ret; u32 cr; + u64 tmp; clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk); c = clkrate * state->period; @@ -249,6 +250,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, val = readl(imx->mmio_base + MX3_PWMPR); val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val; cr = readl(imx->mmio_base + MX3_PWMCR); + + /* + * tmp stores period in nanoseconds. Result fits in u64 since + * val <= 0xfffe and prescaler in [1, 0x1000]. + */ tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr); tmp = DIV_ROUND_UP_ULL(tmp, clkrate); period_us = DIV_ROUND_UP_ULL(tmp, 1000); From 090f5fb1e3e23df9dc6380a9fa0b1309c084f20b Mon Sep 17 00:00:00 2001 From: Shiraz Saleem Date: Tue, 12 May 2026 02:40:56 -0700 Subject: [PATCH 0830/3297] RDMA/mana_ib: Use ib_get_eth_speed for reporting port speed [ Upstream commit d28654518c8db5d06d27bd3211c0e9a70c18f7c2 ] Replace hardcoded IB_WIDTH_4X/IB_SPEED_EDR with ib_get_eth_speed() to report the actual link speed in mana_ib_query_port(). Fixes: 4bda1d5332ec ("RDMA/mana_ib: Implement port parameters") Link: https://patch.msgid.link/r/20260512094056.264827-1-kotaranov@linux.microsoft.com Signed-off-by: Shiraz Saleem Signed-off-by: Konstantin Taranov Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mana/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index 4143be70eea20..f5775900190ed 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -633,8 +633,7 @@ int mana_ib_query_port(struct ib_device *ibdev, u32 port, props->phys_state = IB_PORT_PHYS_STATE_DISABLED; } - props->active_width = IB_WIDTH_4X; - props->active_speed = IB_SPEED_EDR; + ib_get_eth_speed(ibdev, port, &props->active_speed, &props->active_width); props->pkey_tbl_len = 1; if (mana_ib_is_rnic(dev)) { props->gid_tbl_len = 16; From 394ed8ad05889bfa442cdbf93f5b50068a6a20ce Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 1 Mar 2026 17:49:39 +0300 Subject: [PATCH 0831/3297] bus: sunxi-rsb: Always check register address validity [ Upstream commit 61192938a5870ac36edae81e4775b680dcf02c61 ] The register address was already validated for read operations in regmap_sunxi_rsb_reg_read before being truncated to a u8. Write operations have the same set of possible addresses, and the address is being truncated from u32 to u8 here as well, so the same check is needed. Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: d787dcdb9c8f ("bus: sunxi-rsb: Add driver for Allwinner Reduced Serial Bus") Reviewed-by: Chen-Yu Tsai Reviewed-by: Jernej Skrabec Link: https://patch.msgid.link/20260301144939.1832806-1-andrej.skvortzov@gmail.com Signed-off-by: Chen-Yu Tsai Signed-off-by: Sasha Levin --- drivers/bus/sunxi-rsb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 7a33c3b31d1e9..9e007fa4c75de 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -447,6 +447,9 @@ static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg, struct sunxi_rsb_ctx *ctx = context; struct sunxi_rsb_device *rdev = ctx->rdev; + if (reg > 0xff) + return -EINVAL; + return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size); } From 7a551951ebeb5b3f05bdb04a73d4593869c984c4 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 20 May 2026 00:40:07 +0800 Subject: [PATCH 0832/3297] pinctrl: spacemit: fix NULL check in spacemit_pin_set_config [ Upstream commit 09c816e5c4d3a8d6d6e4b7537433e5e98505d934 ] spacemit_pin_set_config() looks up the per-pin descriptor with spacemit_get_pin() then checks the wrong variable for failure: const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); ... if (!pin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); pin is an unsigned int pin id, where 0 (GPIO_0 / gmac0_rxdv on K3) is a valid pin, so rejecting it here drops the PAD config write for the first pin of every group. On K3 Pico-ITX the GMAC RGMII group lists pin 0 as its first entry, so its drive-strength / bias configuration was silently ignored. The intended guard is against spacemit_get_pin() returning NULL when the pin id isn't in the SoC's pin table. Check spin instead, which both restores PAD setup for pin 0 and prevents a NULL deref on spin->pin. Fixes: a83c29e1d145 ("pinctrl: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Han Gao Reviewed-by: Troy Mitchell Reviewed-by: Yixun Lan Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/spacemit/pinctrl-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index 33af9b5791c11..8d797bf24b778 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -609,7 +609,7 @@ static int spacemit_pin_set_config(struct spacemit_pinctrl *pctrl, void __iomem *reg; unsigned int mux; - if (!pin) + if (!spin) return -EINVAL; reg = spacemit_pin_to_reg(pctrl, spin->pin); From 424d51d33c7541a86934067c2c0538124687fc90 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 12 May 2026 18:38:52 +0000 Subject: [PATCH 0833/3297] RDMA/irdma: Fix out-of-bounds write in irdma_copy_user_pgaddrs [ Upstream commit 5ebb3ed757be3e04cf803026004aa0beaeb13e9b ] The irdma_copy_user_pgaddrs function loops through all of the umem DMA blocks to populate the PBLEs and will stop when either the last DMA block is reached or palloc->total_cnt is reached. The issue is that the logic for checking palloc->total_cnt would only work for non-zero values. When irdma_setup_pbles is called with lvl==0, it calls irdma_copy_user_pgaddrs with palloc->total_cnt==0, which means the only way to break out of the loop is to reach the last umem DMA block, which means it could end up going beyond the fixed size of 4 iwmr->pgaddrmem array that is used in the lvl==0 case. In the case of QP/CQ/SRQ rings, the value of lvl is determined by a separate input (for example, req.cq_pages in the case of a CQ). So, we must perform explicit checking to ensure we don't overflow the pgaddrmem array if the user provides a umem that consists of more blocks than their provided req.cq_pages. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://patch.msgid.link/r/20260512183852.614045-1-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index ebf328c9ba92d..6c9925f175711 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -2766,10 +2766,11 @@ static inline u64 *irdma_next_pbl_addr(u64 *pbl, struct irdma_pble_info **pinfo, * irdma_copy_user_pgaddrs - copy user page address to pble's os locally * @iwmr: iwmr for IB's user page addresses * @pbl: ple pointer to save 1 level or 0 level pble + * @pbl_len: Max number of PBL entries to populate * @level: indicated level 0, 1 or 2 */ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl, - enum irdma_pble_level level) + u32 pbl_len, enum irdma_pble_level level) { struct ib_umem *region = iwmr->region; struct irdma_pbl *iwpbl = &iwmr->iwpbl; @@ -2777,7 +2778,9 @@ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl, struct irdma_pble_info *pinfo; struct ib_block_iter biter; u32 idx = 0; - u32 pbl_cnt = 0; + + if (!pbl_len) + return; pinfo = (level == PBLE_LEVEL_1) ? NULL : palloc->level2.leaf; @@ -2786,7 +2789,7 @@ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl, rdma_umem_for_each_dma_block(region, &biter, iwmr->page_size) { *pbl = rdma_block_iter_dma_address(&biter); - if (++pbl_cnt == palloc->total_cnt) + if (!--pbl_len) break; pbl = irdma_next_pbl_addr(pbl, &pinfo, &idx); } @@ -2862,6 +2865,7 @@ static int irdma_setup_pbles(struct irdma_pci_f *rf, struct irdma_mr *iwmr, u64 *pbl; int status; enum irdma_pble_level level = PBLE_LEVEL_1; + u32 pbl_len; if (lvl) { status = irdma_get_pble(rf->pble_rsrc, palloc, iwmr->page_cnt, @@ -2869,16 +2873,18 @@ static int irdma_setup_pbles(struct irdma_pci_f *rf, struct irdma_mr *iwmr, if (status) return status; + pbl_len = palloc->total_cnt; iwpbl->pbl_allocated = true; level = palloc->level; pinfo = (level == PBLE_LEVEL_1) ? &palloc->level1 : palloc->level2.leaf; pbl = pinfo->addr; } else { + pbl_len = IRDMA_MAX_SAVED_PHY_PGADDR; pbl = iwmr->pgaddrmem; } - irdma_copy_user_pgaddrs(iwmr, pbl, level); + irdma_copy_user_pgaddrs(iwmr, pbl, pbl_len, level); if (lvl) iwmr->pgaddrmem[0] = *pbl; From e59a6aa89e0fcd1d0707832eb4654fd9ae7d31e6 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Fri, 15 May 2026 02:25:37 +0200 Subject: [PATCH 0834/3297] RDMA/rxe: Fix a use-after-free problem in rxe_mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 35744ab3d03c5fca8c1752f53fc8fc674e14c561 ] rxe_mmap() removes a rxe_mmap_info struct from the pending_mmaps list and releases pending_lock while the struct's kref is still at 1: list_del_init(&ip->pending_mmaps); spin_unlock_bh(&rxe->pending_lock); /* ref == 1, no lock held */ ret = remap_vmalloc_range(vma, ip->obj, 0); /* walks PTEs */ [...] rxe_vma_open(vma); /* kref_get, ref → 2 */ remap_vmalloc_range_partial() walks PTEs without any lock. A concurrent DESTROY_CQ ioctl on another CPU calls: kref_put(&q->ip->ref, rxe_mmap_release) /* ref 1→0 */ vfree(ip->obj) /* clears vmalloc PTEs mid-walk */ kfree(ip) /* frees rxe_mmap_info */ This yields: 1. Kernel crash, vmalloc_to_page() returns NULL when vfree wins the per-PTE race -> vm_insert_page(NULL) → GPF in validate_page_before_insert 2. Page UAF, vmalloc_to_page() reads a stale PTE before vfree clears it. User VMA holds a PTE to a free'd page which might eventually get reallocated later by vmalloc which allows the attacker to get a clean page-level UAF. It is worth noting that even though a page-level UAF is possible given the strong primitive, it is statistically very difficult to achieve given the very short time window (after the last insert_page and before the kref_get). The call trace are as below: 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: 1000 PID: 413 Comm: poc Not tainted 7.0.0-rc5-dirty #28 PREEMPT(lazy) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:validate_page_before_insert+0x32/0x300 Code: e5 41 57 41 56 49 89 fe 41 55 41 54 53 48 89 f3 e8 93 b5 a3 ff 48 8d 7b 08 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 7b 02 00 00 4c 8b 63 08 31 ff 4d 89 e5 41 83 e5 RSP: 0018:ffff88811b15f2f0 EFLAGS: 00000202 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000001 RSI: 0000000000000000 RDI: 0000000000000008 RBP: ffff88811b15f318 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8881181eee00 R13: 0000000000000000 R14: ffff8881181eee00 R15: ffff8881181eee20 FS: 00007b1e000f76c0(0000) GS:ffff8884268e0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007b1e00a24ac0 CR3: 0000000116eb3000 CR4: 00000000000006f0 Call Trace: insert_page+0x8f/0x190 ? __pfx_insert_page+0x10/0x10 ? kasan_save_alloc_info+0x38/0x60 vm_insert_page+0x2e7/0x400 remap_vmalloc_range_partial+0x212/0x3e0 remap_vmalloc_range+0x6e/0xb0 ? __kasan_check_write+0x14/0x30 rxe_mmap+0x2e9/0x5d0 ib_uverbs_mmap+0x1ad/0x2c0 __mmap_region+0x12c2/0x2ad0 ? __pfx___mmap_region+0x10/0x10 ? __sanitizer_cov_trace_switch+0x58/0xb0 ? mas_prev_slot+0x360/0x39c0 ? __sanitizer_cov_trace_switch+0x58/0xb0 ? mas_next_slot+0x1e5b/0x2f40 ? __sanitizer_cov_trace_cmp8+0x18/0x30 ? unmapped_area_topdown+0x4dd/0x610 ? kfree+0x1b1/0x440 ? free_cpumask_var+0x16/0x30 ? __kasan_slab_free+0x7d/0xa0 ? __sanitizer_cov_trace_cmp8+0x18/0x30 mmap_region+0x2e6/0x3c0 do_mmap+0xa3e/0x12a0 ? __pfx_do_mmap+0x10/0x10 ? __kasan_check_write+0x14/0x30 ? down_write_killable+0xba/0x160 ? __pfx_down_write_killable+0x10/0x10 ? __sanitizer_cov_trace_cmp4+0x16/0x30 vm_mmap_pgoff+0x2d4/0x4a0 ? __pfx_vm_mmap_pgoff+0x10/0x10 ? fget+0x1bf/0x270 ksys_mmap_pgoff+0x40c/0x690 ? __sanitizer_cov_trace_const_cmp4+0x16/0x30 ? __pfx_ksys_mmap_pgoff+0x10/0x10 ? __kasan_check_write+0x14/0x30 ? _raw_spin_trylock+0xbb/0x130 ? __pfx__raw_spin_trylock+0x10/0x10 __x64_sys_mmap+0x135/0x1e0 x64_sys_call+0x1c14/0x2790 do_syscall_64+0xd2/0x1050 ? rcu_core+0x352/0x7d0 ? rcu_core_si+0xe/0x20 ? handle_softirqs+0x1aa/0x650 ? __sanitizer_cov_trace_cmp4+0x16/0x30 ? fpregs_assert_state_consistent+0xe1/0x160 ? irqentry_exit+0xb1/0x670 entry_SYSCALL_64_after_hwframe+0x76/0x7e Link: https://patch.msgid.link/r/20260515002537.6209-1-yanjun.zhu@linux.dev Reported-and-tested-by: nasm Suggested-by: nasm Fixes: 8700e3e7c485 ("Soft RoCE driver") Signed-off-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_mmap.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_mmap.c b/drivers/infiniband/sw/rxe/rxe_mmap.c index 6b7f2bd698799..466ab6dbe9af6 100644 --- a/drivers/infiniband/sw/rxe/rxe_mmap.c +++ b/drivers/infiniband/sw/rxe/rxe_mmap.c @@ -93,18 +93,31 @@ int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) goto done; found_it: + /* + * Increment refcount and check whether it is being freed atm while + * holding lock to prevent UAF + */ + if (!kref_get_unless_zero(&ip->ref)) { + spin_unlock_bh(&rxe->pending_lock); + ret = -ENXIO; + goto done; + } + list_del_init(&ip->pending_mmaps); spin_unlock_bh(&rxe->pending_lock); + vma->vm_ops = &rxe_vm_ops; + vma->vm_private_data = ip; + ret = remap_vmalloc_range(vma, ip->obj, 0); if (ret) { + vma->vm_private_data = NULL; + vma->vm_ops = NULL; + kref_put(&ip->ref, rxe_mmap_release); rxe_dbg_dev(rxe, "err %d from remap_vmalloc_range\n", ret); goto done; } - vma->vm_ops = &rxe_vm_ops; - vma->vm_private_data = ip; - rxe_vma_open(vma); done: return ret; } From f67fbbfc3beb850b1ee20a621a51a72a33f90360 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 18 May 2026 10:19:10 +0800 Subject: [PATCH 0835/3297] IB/mlx4: Fix refcount leak in add_port() error path [ Upstream commit 9a8826fdfbcd7ed2ccf745f5d54208358d939def ] After kobject_init_and_add(), the lifetime of the embedded struct kobject is expected to be managed through the kobject core reference counting. In add_port(), failure paths after kobject_init_and_add() must not free struct mlx4_port directly, because the embedded kobject is then managed by the kobject core. Freeing it directly leaves the kobject reference counting unbalanced and can lead to incorrect lifetime handling. Allocate the pkey and gid attribute arrays before kobject_init_and_add(), so failures before kobject initialization can be handled by directly freeing the allocated memory. Once kobject_init_and_add() has been called, unwind later failures by removing any successfully created sysfs groups, calling kobject_del(), and then releasing the embedded kobject with kobject_put(). Fixes: c1e7e466120b ("IB/mlx4: Add iov directory in sysfs under the ib device") Link: https://patch.msgid.link/r/20260518021910.972900-1-lgs201920130244@gmail.com Signed-off-by: Guangshuo Li Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx4/sysfs.c | 45 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index 88f534cf690e9..1676f819c2f76 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -638,12 +638,6 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) p->port_num = port_num; p->slave = slave; - ret = kobject_init_and_add(&p->kobj, &port_type, - kobject_get(dev->dev_ports_parent[slave]), - "%d", port_num); - if (ret) - goto err_alloc; - p->pkey_group.name = "pkey_idx"; p->pkey_group.attrs = alloc_group_attrs(show_port_pkey, @@ -651,13 +645,9 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) dev->dev->caps.pkey_table_len[port_num]); if (!p->pkey_group.attrs) { ret = -ENOMEM; - goto err_alloc; + goto err_free_port; } - ret = sysfs_create_group(&p->kobj, &p->pkey_group); - if (ret) - goto err_free_pkey; - p->gid_group.name = "gid_idx"; p->gid_group.attrs = alloc_group_attrs(show_port_gid_idx, NULL, 1); if (!p->gid_group.attrs) { @@ -665,28 +655,47 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) goto err_free_pkey; } + ret = kobject_init_and_add(&p->kobj, &port_type, + kobject_get(dev->dev_ports_parent[slave]), + "%d", port_num); + if (ret) + goto err_put; + + ret = sysfs_create_group(&p->kobj, &p->pkey_group); + if (ret) + goto err_del; + ret = sysfs_create_group(&p->kobj, &p->gid_group); if (ret) - goto err_free_gid; + goto err_remove_pkey; ret = add_vf_smi_entries(p); if (ret) - goto err_free_gid; + goto err_remove_gid; list_add_tail(&p->kobj.entry, &dev->pkeys.pkey_port_list[slave]); return 0; -err_free_gid: - kfree(p->gid_group.attrs[0]); - kfree(p->gid_group.attrs); +err_remove_gid: + sysfs_remove_group(&p->kobj, &p->gid_group); + +err_remove_pkey: + sysfs_remove_group(&p->kobj, &p->pkey_group); + +err_del: + kobject_del(&p->kobj); + +err_put: + kobject_put(dev->dev_ports_parent[slave]); + kobject_put(&p->kobj); + return ret; err_free_pkey: for (i = 0; i < dev->dev->caps.pkey_table_len[port_num]; ++i) kfree(p->pkey_group.attrs[i]); kfree(p->pkey_group.attrs); -err_alloc: - kobject_put(dev->dev_ports_parent[slave]); +err_free_port: kfree(p); return ret; } From 16138ea9833d6bb1bed37a3321f282242704bf02 Mon Sep 17 00:00:00 2001 From: Lianfa Weng Date: Wed, 20 May 2026 13:57:58 +0800 Subject: [PATCH 0836/3297] RDMA/hns: Fix warning in poll cq direct mode [ Upstream commit 3f19c2a3852e6ba75f3e92dd5edc4e07f3d07f4a ] CQs allocated by ib_alloc_cq() always have a comp_handler. Though in direct mode this handler is never expected to be called, it is still called when the driver is reset, triggering the following WARN_ONCE(): Call trace: ib_cq_completion_direct+0x38/0x60 hns_roce_cq_completion+0x54/0x90 (hns_roce_hw_v2] hns_roce_handle_device_err+Ox1c8/0x340 [hns_roce_hw_v2] hns_roce_hw_v2_uninit_instance.constprop.0+0x34/0x70 [hns_roce_hw_v2] hns_roce_hw_v2_reset_notify+0xc4/0xe0 [hns_roce_hw_v2] hclge_notify_roce_client+0x60/0xbc [hclge] hclge_reset_rebuild+0x48/0x34c [hclge] hclge_reset_subtask+0xcc/0xec [hclge] hclge_reset_service_task+0x80/0x160 [hclge] hclge_service_task+0x50/0x80 (hclge] process_one_work+0x1cc/0x4d0 worker_thread+0x154/0x414 kthread+0x104/0x144 ret_from_fork+0x10/0x18 Fixes: f295e4cece5c ("RDMA/hns: Delete unnecessary callback functions for cq") Link: https://patch.msgid.link/r/20260520055759.2354037-3-huangjunxian6@hisilicon.com Signed-off-by: Lianfa Weng Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index f3607fe107a7f..377b3b983caec 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -1003,7 +1003,7 @@ static void check_and_get_armed_cq(struct list_head *cq_list, struct ib_cq *cq) unsigned long flags; spin_lock_irqsave(&hr_cq->lock, flags); - if (cq->comp_handler) { + if (cq->comp_handler && hr_cq->ib_cq.poll_ctx != IB_POLL_DIRECT) { if (!hr_cq->is_armed) { hr_cq->is_armed = 1; list_add_tail(&hr_cq->node, cq_list); From 66f44ec974ab372e4e1ee9eac4245237c94e8f68 Mon Sep 17 00:00:00 2001 From: Lianfa Weng Date: Wed, 20 May 2026 13:57:59 +0800 Subject: [PATCH 0837/3297] RDMA/hns: Fix log flood after cmd_mbox failure [ Upstream commit bbd97d71e53e551890e4115ad9de46b5f2ac0858 ] hns_roce_cmd_mbox() is the command interface between driver and hardware. When hardware is abnormal, the unlimited error printings after hns_roce_cmd_mbox() failure will cause log flood and even system crash. Replace ibdev_err() and ibdev_warn() with their ratelimited versions in the error handling path after hns_roce_cmd_mbox() (and its wrappers hns_roce_create_hw_ctx/hns_roce_destroy_hw_ctx) fails. Fixes: 9a4435375cd1 ("IB/hns: Add driver files for hns RoCE driver") Link: https://patch.msgid.link/r/20260520055759.2354037-4-huangjunxian6@hisilicon.com Signed-off-by: Lianfa Weng Signed-off-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_cq.c | 6 +++--- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 18 +++++++++--------- drivers/infiniband/hw/hns/hns_roce_mr.c | 6 +++--- drivers/infiniband/hw/hns/hns_roce_srq.c | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c index 6aa82fe9dd3df..150f5052d0b01 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cq.c +++ b/drivers/infiniband/hw/hns/hns_roce_cq.c @@ -171,9 +171,9 @@ static int hns_roce_create_cqc(struct hns_roce_dev *hr_dev, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_CQC, hr_cq->cqn); if (ret) - ibdev_err(ibdev, - "failed to send create cmd for CQ(0x%lx), ret = %d.\n", - hr_cq->cqn, ret); + ibdev_err_ratelimited(ibdev, + "failed to send create cmd for CQ(0x%lx), ret = %d.\n", + hr_cq->cqn, ret); hns_roce_free_cmd_mailbox(hr_dev, mailbox); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index f895731ad74a0..5512a8084b7c2 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -5950,9 +5950,9 @@ static int hns_roce_v2_modify_srq(struct ib_srq *ibsrq, HNS_ROCE_CMD_MODIFY_SRQC, srq->srqn); hns_roce_free_cmd_mailbox(hr_dev, mailbox); if (ret) - ibdev_err(&hr_dev->ib_dev, - "failed to handle cmd of modifying SRQ, ret = %d.\n", - ret); + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to handle cmd of modifying SRQ, ret = %d.\n", + ret); } out: @@ -5978,9 +5978,9 @@ static int hns_roce_v2_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr) ret = hns_roce_cmd_mbox(hr_dev, 0, mailbox->dma, HNS_ROCE_CMD_QUERY_SRQC, srq->srqn); if (ret) { - ibdev_err(&hr_dev->ib_dev, - "failed to process cmd of querying SRQ, ret = %d.\n", - ret); + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to process cmd of querying SRQ, ret = %d.\n", + ret); goto out; } @@ -6086,9 +6086,9 @@ static int hns_roce_v2_query_mpt(struct hns_roce_dev *hr_dev, u32 key, ret = hns_roce_cmd_mbox(hr_dev, 0, mailbox->dma, HNS_ROCE_CMD_QUERY_MPT, key_to_hw_index(key)); if (ret) { - ibdev_err(&hr_dev->ib_dev, - "failed to process cmd when querying MPT, ret = %d.\n", - ret); + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to process cmd when querying MPT, ret = %d.\n", + ret); goto err_mailbox; } diff --git a/drivers/infiniband/hw/hns/hns_roce_mr.c b/drivers/infiniband/hw/hns/hns_roce_mr.c index a14e402892332..2041ee7a30970 100644 --- a/drivers/infiniband/hw/hns/hns_roce_mr.c +++ b/drivers/infiniband/hw/hns/hns_roce_mr.c @@ -173,7 +173,7 @@ static int hns_roce_mr_enable(struct hns_roce_dev *hr_dev, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_MPT, mtpt_idx & (hr_dev->caps.num_mtpts - 1)); if (ret) { - dev_err(dev, "failed to create mpt, ret = %d.\n", ret); + dev_err_ratelimited(dev, "failed to create mpt, ret = %d.\n", ret); goto err_page; } @@ -319,7 +319,7 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, ret = hns_roce_destroy_hw_ctx(hr_dev, HNS_ROCE_CMD_DESTROY_MPT, mtpt_idx); if (ret) - ibdev_warn(ib_dev, "failed to destroy MPT, ret = %d.\n", ret); + ibdev_warn_ratelimited(ib_dev, "failed to destroy MPT, ret = %d.\n", ret); mr->enabled = 0; mr->iova = virt_addr; @@ -350,7 +350,7 @@ struct ib_mr *hns_roce_rereg_user_mr(struct ib_mr *ibmr, int flags, u64 start, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_MPT, mtpt_idx); if (ret) { - ibdev_err(ib_dev, "failed to create MPT, ret = %d.\n", ret); + ibdev_err_ratelimited(ib_dev, "failed to create MPT, ret = %d.\n", ret); goto free_cmd_mbox; } diff --git a/drivers/infiniband/hw/hns/hns_roce_srq.c b/drivers/infiniband/hw/hns/hns_roce_srq.c index 1090051f493b5..fb619988b9c88 100644 --- a/drivers/infiniband/hw/hns/hns_roce_srq.c +++ b/drivers/infiniband/hw/hns/hns_roce_srq.c @@ -104,7 +104,7 @@ static int hns_roce_create_srqc(struct hns_roce_dev *hr_dev, ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_SRQ, srq->srqn); if (ret) - ibdev_err(ibdev, "failed to config SRQC, ret = %d.\n", ret); + ibdev_err_ratelimited(ibdev, "failed to config SRQC, ret = %d.\n", ret); err_mbox: hns_roce_free_cmd_mailbox(hr_dev, mailbox); From 9420871183eabb3b826ebfdf5b5691af85abc7e0 Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Wed, 20 May 2026 18:45:46 +0800 Subject: [PATCH 0838/3297] RDMA/counter: Fix incorrect port index in rdma_counter_init() error cleanup [ Upstream commit b86fd95805a7bd4c5b9465c9e7f75e45bbe7eb6f ] The error cleanup loop in rdma_counter_init() iterates with variable 'i' but accesses dev->port_data[port] instead of dev->port_data[i]. This causes the failed port's hstats to be freed multiple times while leaking hstats of previously initialized ports. Fixes: 56594ae1d250 ("RDMA/core: Annotate destroy of mutex to ensure that it is released as unlocked") Link: https://patch.msgid.link/r/20260520104546.1776253-3-cuitao@kylinos.cn Signed-off-by: Tao Cui Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/counters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c index c3aa6d7fc66b6..5dad5d77ce274 100644 --- a/drivers/infiniband/core/counters.c +++ b/drivers/infiniband/core/counters.c @@ -661,7 +661,7 @@ void rdma_counter_init(struct ib_device *dev) fail: for (i = port; i >= rdma_start_port(dev); i--) { - port_counter = &dev->port_data[port].port_counter; + port_counter = &dev->port_data[i].port_counter; rdma_free_hw_stats_struct(port_counter->hstats); port_counter->hstats = NULL; mutex_destroy(&port_counter->lock); From 80f8e2302e639d456f4e1930c362b3c2e2c47e2d Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Mon, 18 May 2026 08:26:20 +0000 Subject: [PATCH 0839/3297] pinctrl: meson: amlogic-a4: fix gpio output glitch [ Upstream commit 45ad4de324cb1bba88e568b5bef633a79d926aed ] When the system transitions from bootloader to kernel, the GPIO is expected to keep driving high. However, the Linux kernel first configures the pin direction and then sets the output value. This may cause a brief low-level glitch on the GPIO line, which can be problematic for regulator control. By configuring the output value before switching the pin direction to output, the glitch can be avoided. This commit fixes the issue by swapping the configuration order. 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 | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c index 35d27626a336b..1bd58fbbd26ac 100644 --- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c @@ -548,11 +548,11 @@ static int aml_pinconf_set_output_drive(struct aml_pinctrl *info, { int ret; - ret = aml_pinconf_set_output(info, pin, true); + ret = aml_pinconf_set_drive(info, pin, high); if (ret) return ret; - return aml_pinconf_set_drive(info, pin, high); + return aml_pinconf_set_output(info, pin, true); } static int aml_pinconf_set(struct pinctrl_dev *pcdev, unsigned int pin, @@ -921,15 +921,14 @@ static int aml_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio, unsigned int bit, reg; int ret; - aml_gpio_calc_reg_and_bit(bank, AML_REG_DIR, gpio, ®, &bit); - ret = regmap_update_bits(bank->reg_gpio, reg, BIT(bit), 0); + aml_gpio_calc_reg_and_bit(bank, AML_REG_OUT, gpio, ®, &bit); + ret = regmap_update_bits(bank->reg_gpio, reg, BIT(bit), + value ? BIT(bit) : 0); if (ret < 0) return ret; - aml_gpio_calc_reg_and_bit(bank, AML_REG_OUT, gpio, ®, &bit); - - return regmap_update_bits(bank->reg_gpio, reg, BIT(bit), - value ? BIT(bit) : 0); + aml_gpio_calc_reg_and_bit(bank, AML_REG_DIR, gpio, ®, &bit); + return regmap_update_bits(bank->reg_gpio, reg, BIT(bit), 0); } static int aml_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) From c9670fd84df12bceb0ec4a9d1ef35a7ec25ffd09 Mon Sep 17 00:00:00 2001 From: Jiakai Xu Date: Sat, 23 May 2026 02:23:14 +0000 Subject: [PATCH 0840/3297] PM: sleep: Use complete() in device_pm_sleep_init() [ Upstream commit 3855941f1e4069182c895d5093c5fa589f5b38bd ] Replace complete_all() with complete() in device_pm_sleep_init() to allow it to be called in atomic contexts without triggering a false-positive WARNING from lockdep_assert_RT_in_threaded_ctx() when CONFIG_PROVE_RAW_LOCK_NESTING is enabled. device_pm_sleep_init() may be called during device initialization while holding a raw_spinlock (e.g., from within device_initialize()), and complete_all() is unsafe in atomic contexts on PREEMPT_RT kernels. complete(), which is safe to call from any context, is sufficient here. complete_all() sets the completion count to UINT_MAX/2 (permanently signaled), while complete() increments it by 1. Since no threads can be waiting during device initialization, both are functionally equivalent. The completion is always reinitialized via reinit_completion() in dpm_clear_async_state() before each suspend/resume cycle. However, changing to complete() introduces a potential deadlock for devices with no PM support (dev->power.no_pm = true). Such devices are never added to the dpm_list and never go through dpm_clear_async_state(), so their completion is never reinitialized. A parent device waiting on a no_pm child across multiple suspend phases would consume the single-use token in the first phase and block forever in the second. Fix this by adding an early return in dpm_wait() when dev->power.no_pm is set, since no_pm devices do not participate in system suspend/resume. Fixes: 152e1d592071 ("PM: Prevent waiting forever on asynchronous resume after failing suspend") Signed-off-by: Jiakai Xu [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20260523022314.2657232-1-xujiakai24@mails.ucas.ac.cn Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/base/power/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 1de1cd72b616d..13c2c48e587f8 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -111,7 +111,7 @@ void device_pm_sleep_init(struct device *dev) dev->power.is_noirq_suspended = false; dev->power.is_late_suspended = false; init_completion(&dev->power.completion); - complete_all(&dev->power.completion); + complete(&dev->power.completion); dev->power.wakeup = NULL; INIT_LIST_HEAD(&dev->power.entry); } @@ -248,6 +248,10 @@ static void dpm_wait(struct device *dev, bool async) if (!dev) return; + /* Devices with no PM support don't use the completion. */ + if (dev->power.no_pm) + return; + if (async || (pm_async_enabled && dev->power.async_suspend)) wait_for_completion(&dev->power.completion); } From c9a3ceeddc6ad16a3592881fbe2fe94913798284 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sat, 2 May 2026 00:14:20 +0100 Subject: [PATCH 0841/3297] MIPS: Fix big-endian stack argument fetching in o32 wrapper [ Upstream commit 8e0780d30b1b51248e42895b7acc7a20f147b40b ] Fix an issue in call_o32() where the upper 32-bit half of incoming n64 stack arguments is fetched and used for outgoing o32 stack arguments on big-endian platforms. This code was adapted from arch/mips/dec/prom/call_o32.S which was meant for a little-endian platform only and therefore using 32-bit loads from 64-bit stack slot locations holding incoming stack arguments resulted in correct values being retrieved for data that is expected to be 32-bit. This works on little-endian platforms where the lower 32-bit half of the 64-bit value is located at every 64-bit stack slot location. However on big-endian platforms the lower 32-bit half is instead located at offset 4 from every 64-bit stack slot location. So to fix the issue the offset of 4 would have to be used on big-endian platforms only, or alternatively a 64-bit load from the 64-bit stack slot location can be used across the board, as the subsequent 32-bit store to the corresponding outgoing stack argument slot will correctly truncate the value and cause no unpredictable result. We already take advantage of this architectural feature for the incoming arguments held in $a6 and $a7 registers, since the o32 wrapper does not know how many incoming arguments there are and consequently propagates incoming data which may not be 32-bit. Since this code is generally supposed to be used with the stack located in cached memory there is no extra overhead expected for 64-bit loads as opposed to 32-bit ones, so pick this variant for code simplicity. Fixes: 231a35d37293 ("[MIPS] RM: Collected changes") Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/fw/lib/call_o32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S index ee856709e0b60..77533cfbdfc10 100644 --- a/arch/mips/fw/lib/call_o32.S +++ b/arch/mips/fw/lib/call_o32.S @@ -74,7 +74,7 @@ NESTED(call_o32, O32_FRAMESZ, ra) PTR_LA t1,6*O32_SZREG(fp) li t2,O32_ARGC-6 1: - lw t3,(t0) + ld t3,(t0) REG_ADDU t0,SZREG sw t3,(t1) REG_SUBU t2,1 From 0c83d13f9b3f1ff066b9ec0f181e8e19f9493213 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 4 May 2026 21:14:00 +0100 Subject: [PATCH 0842/3297] MIPS: DEC: Remove do_IRQ() call indirection [ Upstream commit 35554eadc1b127bb5294504a4ac8f3a5dd9e299d ] As from commit 8f99a1626535 ("MIPS: Tracing: Add IRQENTRY_EXIT section for MIPS") do_IRQ() is not a macro anymore and can be invoked directly from assembly code, as a tail call. Remove the dec_irq_dispatch() stub then and the indirection previously introduced with commit 187933f23679 ("[MIPS] do_IRQ cleanup"), improving performance by reducing the number of control flow changes and the overall instruction count, while fixing a compiler's complaint about a missing prototype for said stub: arch/mips/dec/setup.c:780:25: warning: no previous prototype for 'dec_irq_dispatch' [-Wmissing-prototypes] 780 | asmlinkage unsigned int dec_irq_dispatch(unsigned int irq) | ^~~~~~~~~~~~~~~~ (which gets promoted to a compilation error with CONFIG_WERROR). Fixes: 8f99a1626535 ("MIPS: Tracing: Add IRQENTRY_EXIT section for MIPS") Signed-off-by: Maciej W. Rozycki Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/dec/int-handler.S | 2 +- arch/mips/dec/setup.c | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S index 011d1d678840a..a0b439c90488f 100644 --- a/arch/mips/dec/int-handler.S +++ b/arch/mips/dec/int-handler.S @@ -277,7 +277,7 @@ srlv t3,t1,t2 handle_it: - j dec_irq_dispatch + j do_IRQ nop #if defined(CONFIG_32BIT) && defined(CONFIG_MIPS_FP_SUPPORT) diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c index 87f0a1436bf9c..abe42616498db 100644 --- a/arch/mips/dec/setup.c +++ b/arch/mips/dec/setup.c @@ -776,9 +776,3 @@ void __init arch_init_irq(void) pr_err("Failed to register halt interrupt\n"); } } - -asmlinkage unsigned int dec_irq_dispatch(unsigned int irq) -{ - do_IRQ(irq); - return 0; -} From 8db227dd895a84bf09c0172e5054a62c8ee9740b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 7 May 2026 21:27:24 -0700 Subject: [PATCH 0843/3297] mips: ralink: mt7621: add missing __iomem [ Upstream commit 4ddaf88aadd3bd09c2eb3734c53d2864af6b144e ] raw_readl and writel calls expect pointers annotated with __iomem. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild/202211060456.cnV6IK6G-lkp@intel.com/ Fixes: cc19db8b312a ("MIPS: ralink: mt7621: do memory detection on KSEG1") Signed-off-by: Rosen Penev Reviewed-by: Sergio Paracuellos Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/ralink/mt7621.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/ralink/mt7621.c b/arch/mips/ralink/mt7621.c index 5a9fd3fe41d7c..e25444b149f77 100644 --- a/arch/mips/ralink/mt7621.c +++ b/arch/mips/ralink/mt7621.c @@ -63,7 +63,7 @@ phys_addr_t mips_cpc_default_phys_base(void) static bool __init mt7621_addr_wraparound_test(phys_addr_t size) { - void *dm = (void *)KSEG1ADDR(&detect_magic); + void __iomem *dm = (void __iomem *)KSEG1ADDR(&detect_magic); if (CPHYSADDR(dm + size) >= MT7621_LOWMEM_MAX_SIZE) return true; From 2c12b0dfcedad501cb7bf4389462f42225731ced Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 8 May 2026 20:57:27 -0700 Subject: [PATCH 0844/3297] mips: n64: add __iomem for writel call [ Upstream commit 579f5329d5df2dbcf4bb5ef398701c2501d24892 ] sparse: incorrect type in argument 2 (different address spaces) @@ expected void volatile [noderef] __iomem *mem @@ got unsigned int [usertype] * Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202105261445.AcvPd2EE-lkp@intel.com/ Fixes: baec970aa5ba ("mips: Add N64 machine type") Signed-off-by: Rosen Penev Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/n64/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/n64/init.c b/arch/mips/n64/init.c index dfbd864f46670..66ec28ab41f32 100644 --- a/arch/mips/n64/init.c +++ b/arch/mips/n64/init.c @@ -50,7 +50,7 @@ void __init prom_init(void) #define W 320 #define H 240 -#define REG_BASE ((u32 *) CKSEG1ADDR(0x4400000)) +#define REG_BASE ((u32 __iomem *) CKSEG1ADDR(0x4400000)) static void __init n64rdp_write_reg(const u8 reg, const u32 value) { From 0a294feec21b68834c12a3b10900acddb305bce9 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 03:23:21 +0200 Subject: [PATCH 0845/3297] driver core: Fix missing jiffies conversion in deferred_probe_extend_timeout() [ Upstream commit f9e6da99fe49277979798a1c3b9790ae10aaa18a ] mod_delayed_work() takes jiffies, not seconds. Thus, restore the dropped conversion. While at it, fix incorrect indentation. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Tested-by: Biju Das Tested-by: Geert Uytterhoeven Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525012340.3860581-1-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index c42643e26e28a..175d9be6455dd 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -328,7 +328,7 @@ void deferred_probe_extend_timeout(void) * start a new one. */ if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, - driver_deferred_probe_timeout)) + secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); } From 3880ee7c88d78c3ece6f9af3bfff94f6e698aeb7 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 25 May 2026 03:23:22 +0200 Subject: [PATCH 0846/3297] driver core: Guard deferred probe timeout extension with delayed_work_pending() [ Upstream commit 557495bc879013c3d5e21d667e987e7ce3a514de ] mod_delayed_work() unconditionally queues the work even when it wasn't previously pending, which can fire the timeout prematurely or restart it after it already fired. Add a delayed_work_pending() guard to restore the originally intended semantics. Premature firing calls fw_devlink_drivers_done() before all built-in drivers have registered, causing fw_devlink to prematurely relax device links for suppliers whose drivers haven't loaded yet. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Tested-by: Geert Uytterhoeven Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260525012340.3860581-2-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/dd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 175d9be6455dd..1ba53078703e4 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -327,7 +327,8 @@ void deferred_probe_extend_timeout(void) * If the work hasn't been queued yet or if the work expired, don't * start a new one. */ - if (mod_delayed_work(system_wq, &deferred_probe_timeout_work, + if (delayed_work_pending(&deferred_probe_timeout_work) && + mod_delayed_work(system_wq, &deferred_probe_timeout_work, secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); From c4bb92b3ef54c8b41b81909eceee83dada94789c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:25 +0200 Subject: [PATCH 0847/3297] mtd: spi-nor: debugfs: Fix the flags list [ Upstream commit 829dff83597615208aedf0f5abb3878b47f2314d ] As mentioned above the spi_nor_option_flags enumeration in core.h, this list should be kept in sync with the one in the core. Add the missing flag. Fixes: 6a42bc97ccda ("mtd: spi-nor: core: Allow specifying the byte order in Octal DTR mode") Reviewed-by: Michael Walle Signed-off-by: Miquel Raynal Signed-off-by: Pratyush Yadav Signed-off-by: Sasha Levin --- drivers/mtd/spi-nor/debugfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index 14ba1680c3154..c0bd8f1149a51 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -29,6 +29,7 @@ static const char *const snor_f_names[] = { SNOR_F_NAME(RWW), SNOR_F_NAME(ECC), SNOR_F_NAME(NO_WP), + SNOR_F_NAME(SWAP16), }; #undef SNOR_F_NAME From 02f156309de0d65641cea88c27d011c2e1b1ea5a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:27 +0200 Subject: [PATCH 0848/3297] mtd: spi-nor: Drop duplicate Kconfig dependency [ Upstream commit a6470e2162e9c3779a4bd6ff3bed1b81d796e46e ] I do not think the MTD dependency is needed twice. This is likely a duplicate coming from a former rebase when the spi-nor core got cleaned up a while ago. Remove the extra line. Fixes: b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") Signed-off-by: Miquel Raynal Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Signed-off-by: Sasha Levin --- drivers/mtd/spi-nor/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 24cd25de2b8b7..fd05a24d64a96 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig MTD_SPI_NOR tristate "SPI NOR device support" - depends on MTD depends on MTD && SPI_MASTER select SPI_MEM help From 56694f00fbe1042a344c19e9cef7dc62428b06f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 26 May 2026 09:48:26 -0300 Subject: [PATCH 0849/3297] ALSA: xen-front: Reset event channel state on stream clear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9cd81152373c560b8aa8299b0705c4db82b103b7 ] xen_snd_front_evtchnl_pair_clear() resets evt_next_id for both channels. That is correct for the request channel, where evt_next_id is used to allocate the next request id. It is wrong for the event channel: incoming events are validated against evt_id, and evt_id is incremented by evtchnl_interrupt_evt(). This leaves the expected event id from the previous stream instance. A backend that restarts event ids for a reopened stream can then have valid current-position events dropped until the stale frontend id catches up. Reset evt_id for the event channel. Also advance the event-page consumer to the current producer while clearing the stream, so obsolete events queued for the previous stream instance are not delivered to the next ALSA runtime. Fixes: 1cee559351a7 ("ALSA: xen-front: Implement ALSA virtual sound driver") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260526-alsa-xen-event-channel-fixes-v1-1-91d3a6a50778@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/xen/xen_snd_front_evtchnl.c | 8 ++++++-- sound/xen/xen_snd_front_evtchnl.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c index 2fbed8e4a490e..ff7f3cc5896fe 100644 --- a/sound/xen/xen_snd_front_evtchnl.c +++ b/sound/xen/xen_snd_front_evtchnl.c @@ -457,7 +457,11 @@ void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pai } scoped_guard(mutex, &evt_pair->evt.ring_io_lock) { - evt_pair->evt.evt_next_id = 0; + evt_pair->evt.evt_id = 0; + /* Drop obsolete events queued for the previous stream instance. */ + evt_pair->evt.u.evt.page->in_cons = + evt_pair->evt.u.evt.page->in_prod; + /* Ensure the consumer index is visible before stream reuse. */ + virt_wmb(); } } - diff --git a/sound/xen/xen_snd_front_evtchnl.h b/sound/xen/xen_snd_front_evtchnl.h index 3675fba705647..8400261ac4660 100644 --- a/sound/xen/xen_snd_front_evtchnl.h +++ b/sound/xen/xen_snd_front_evtchnl.h @@ -37,9 +37,9 @@ struct xen_snd_front_evtchnl { /* State of the event channel. */ enum xen_snd_front_evtchnl_state state; enum xen_snd_front_evtchnl_type type; - /* Either response id or incoming event id. */ + /* Current response id or next expected incoming event id. */ u16 evt_id; - /* Next request id or next expected event id. */ + /* Next request id. */ u16 evt_next_id; /* Shared ring access lock. */ struct mutex ring_io_lock; From de236b813d8b4df0beb1511a2fa4cfb062e59896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 26 May 2026 09:48:27 -0300 Subject: [PATCH 0850/3297] ALSA: xen-front: Connect event channel after stream prepare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3624f0bd4af15a820b1bd88b489980fa9fd61b7a ] The request channel must be connected from ALSA .open(), because hw-rule queries and the stream open request use it. The event channel is different: XENSND_EVT_CUR_POS handling uses ALSA runtime buffer and period geometry, and the corresponding Xen stream parameters are not submitted to the backend until .prepare() sends XENSND_OP_OPEN. Currently .open() connects both channels. A backend current-position event, or a stale event queued for an earlier stream instance, can therefore reach xen_snd_front_alsa_handle_cur_pos() before runtime->buffer_size and runtime->period_size are valid. Add a per-channel connection helper, connect only the request channel in .open(), connect the event channel after a successful stream prepare, and disconnect it before stream close/free. Re-check the event-channel state after taking ring_io_lock so disconnecting the event channel synchronizes against a threaded IRQ that passed the initial lockless state test. Keep defensive runtime geometry checks in the position handler. Fixes: 1cee559351a7 ("ALSA: xen-front: Implement ALSA virtual sound driver") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260526-alsa-xen-event-channel-fixes-v1-2-91d3a6a50778@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/xen/xen_snd_front_alsa.c | 17 ++++++++++++----- sound/xen/xen_snd_front_evtchnl.c | 20 +++++++++++++------- sound/xen/xen_snd_front_evtchnl.h | 2 ++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c index b229eb6f70571..293cc376e3ce6 100644 --- a/sound/xen/xen_snd_front_alsa.c +++ b/sound/xen/xen_snd_front_alsa.c @@ -378,7 +378,7 @@ static int alsa_open(struct snd_pcm_substream *substream) stream_clear(stream); - xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true); + xen_snd_front_evtchnl_set_connected(&stream->evt_pair->req, true); ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, alsa_hw_rule, stream, @@ -499,6 +499,8 @@ static int alsa_hw_free(struct snd_pcm_substream *substream) struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; + xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, false); + ret = xen_snd_front_stream_close(&stream->evt_pair->req); stream_free(stream); return ret; @@ -533,6 +535,7 @@ static int alsa_prepare(struct snd_pcm_substream *substream) return ret; stream->is_open = true; + xen_snd_front_evtchnl_set_connected(&stream->evt_pair->evt, true); } return 0; @@ -572,20 +575,24 @@ void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl, { struct snd_pcm_substream *substream = evtchnl->u.evt.substream; struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t delta, new_hw_ptr, cur_frame; - cur_frame = bytes_to_frames(substream->runtime, pos_bytes); + if (!runtime->buffer_size || !runtime->period_size) + return; + + cur_frame = bytes_to_frames(runtime, pos_bytes); delta = cur_frame - stream->be_cur_frame; stream->be_cur_frame = cur_frame; new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr); - new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size; + new_hw_ptr = (new_hw_ptr + delta) % runtime->buffer_size; atomic_set(&stream->hw_ptr, (int)new_hw_ptr); stream->out_frames += delta; - if (stream->out_frames > substream->runtime->period_size) { - stream->out_frames %= substream->runtime->period_size; + if (stream->out_frames > runtime->period_size) { + stream->out_frames %= runtime->period_size; snd_pcm_period_elapsed(substream); } } diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c index ff7f3cc5896fe..8e95273093a99 100644 --- a/sound/xen/xen_snd_front_evtchnl.c +++ b/sound/xen/xen_snd_front_evtchnl.c @@ -94,6 +94,9 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id) guard(mutex)(&channel->ring_io_lock); + if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) + return IRQ_HANDLED; + prod = page->in_prod; /* Ensure we see ring contents up to prod. */ virt_rmb(); @@ -431,8 +434,8 @@ int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info) return ret; } -void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, - bool is_connected) +void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel, + bool is_connected) { enum xen_snd_front_evtchnl_state state; @@ -441,13 +444,16 @@ void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair else state = EVTCHNL_STATE_DISCONNECTED; - scoped_guard(mutex, &evt_pair->req.ring_io_lock) { - evt_pair->req.state = state; + scoped_guard(mutex, &channel->ring_io_lock) { + channel->state = state; } +} - scoped_guard(mutex, &evt_pair->evt.ring_io_lock) { - evt_pair->evt.state = state; - } +void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, + bool is_connected) +{ + xen_snd_front_evtchnl_set_connected(&evt_pair->req, is_connected); + xen_snd_front_evtchnl_set_connected(&evt_pair->evt, is_connected); } void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair) diff --git a/sound/xen/xen_snd_front_evtchnl.h b/sound/xen/xen_snd_front_evtchnl.h index 8400261ac4660..f6ebdb09c0298 100644 --- a/sound/xen/xen_snd_front_evtchnl.h +++ b/sound/xen/xen_snd_front_evtchnl.h @@ -77,6 +77,8 @@ void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info); int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info); void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *evtchnl); +void xen_snd_front_evtchnl_set_connected(struct xen_snd_front_evtchnl *channel, + bool is_connected); void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, bool is_connected); From 6dc781778b595be94c31395b2cb167f65145d91f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 26 May 2026 17:28:41 +0200 Subject: [PATCH 0851/3297] ALSA: seq: oss: Fix UAF at handling events with embedded SysEx data [ Upstream commit 7c349b4f2a603202fb8c363bd2774a22ac2fddf3 ] The OSS sequencer processes the input MIDI bytes into a sequencer event to be dispatched later (in snd_seq_oss_midi_putc() called from snd_seq_oss_process_event()). When it's a SysEx data, the event record contains data.ext.ptr pointer to the original SysEx bytes, and the referred data is copied into the pool afterwards at dispatching. The problem is that, if the sequencer port gets closed concurrently before the dispatch, the OSS sequencer core also releases the resources (in snd_seq_oss_midi_check_exit_port()), while the pending event may hold a stale pointer, eventually leading to a UAF at a later dispatch. Fortunately, there is already a refcounting mechanism (snd_use_lock_t) for the OSS MIDI device access, and for addressing the issue above, we just need to extend the refcount until the event gets dispatched. This patch extends snd_seq_oss_process_event() to give back the refcount object, which is in turn released after calling the sequencer dispatcher with the given event in the caller side. According to the original report, KASAN report as below: KASAN slab-use-after-free in snd_seq_event_dup+0x40c/0x470 RIP: 0033:0x7f2cb66a6340 Read of size 6 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_seq_event_dup+0x40c/0x470 (?:?) kasan_check_range+0x10c/0x1c0 (?:?) __asan_memcpy+0x27/0x70 (?:?) snd_seq_event_dup+0x9/0x470 (?:?) snd_seq_client_enqueue_event+0x139/0x240 (?:?) _raw_spin_unlock_irqrestore+0x4b/0x60 (?:?) snd_seq_kernel_client_enqueue+0x102/0x120 (?:?) snd_seq_oss_write+0x416/0x4e0 (?:?) apparmor_file_permission+0x20/0x30 (?:?) odev_write+0x3b/0x60 (?:?) vfs_write+0x1ce/0x850 (?:?) lock_release+0xc8/0x2a0 (?:?) __kasan_check_write+0x18/0x20 (?:?) __mutex_unlock_slowpath+0x129/0x510 (?:?) ksys_write+0xe1/0x180 (?:?) mutex_unlock+0x16/0x20 (?:?) odev_ioctl+0x65/0xc0 (?:?) __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: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-and-tested-by: Zhang Cen Closes: https://lore.kernel.org/20260521233900.478153-1-rollkingzzc@gmail.com Link: https://patch.msgid.link/20260526152843.617503-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/oss/seq_oss_event.c | 6 ++++-- sound/core/seq/oss/seq_oss_event.h | 3 ++- sound/core/seq/oss/seq_oss_ioctl.c | 5 ++++- sound/core/seq/oss/seq_oss_midi.c | 6 +++++- sound/core/seq/oss/seq_oss_midi.h | 2 +- sound/core/seq/oss/seq_oss_rw.c | 5 ++++- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c index 76fb81077eef7..122735862044d 100644 --- a/sound/core/seq/oss/seq_oss_event.c +++ b/sound/core/seq/oss/seq_oss_event.c @@ -39,8 +39,10 @@ static int set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct s */ int -snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev) +snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, + struct snd_seq_event *ev, snd_use_lock_t **lockp) { + *lockp = NULL; switch (q->s.code) { case SEQ_EXTENDED: return extended_event(dp, q, ev); @@ -69,7 +71,7 @@ snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE)) break; if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE) - return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev); + return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev, lockp); break; case SEQ_ECHO: diff --git a/sound/core/seq/oss/seq_oss_event.h b/sound/core/seq/oss/seq_oss_event.h index b4f723949a170..a4524e51d0e9d 100644 --- a/sound/core/seq/oss/seq_oss_event.h +++ b/sound/core/seq/oss/seq_oss_event.h @@ -91,7 +91,8 @@ union evrec { #define ev_is_long(ev) ((ev)->s.code >= 128) #define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE) -int snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev); +int snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, + struct snd_seq_event *ev, snd_use_lock_t **lockp); int snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *q); int snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop); diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c index ccf682689ec95..ce7a69d52b308 100644 --- a/sound/core/seq/oss/seq_oss_ioctl.c +++ b/sound/core/seq/oss/seq_oss_ioctl.c @@ -45,14 +45,17 @@ static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg) { unsigned char ev[8]; struct snd_seq_event tmpev; + snd_use_lock_t *lock = NULL; if (copy_from_user(ev, arg, 8)) return -EFAULT; memset(&tmpev, 0, sizeof(tmpev)); snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.client, dp->addr.port); tmpev.time.tick = 0; - if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) { + if (!snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev, &lock)) { snd_seq_oss_dispatch(dp, &tmpev, 0, 0); + if (lock) + snd_use_lock_free(lock); } return 0; } diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index 023e5d0a4351d..553c405ddd71a 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -593,7 +593,8 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq * non-zero : invalid - ignored */ int -snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev) +snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, + struct snd_seq_event *ev, snd_use_lock_t **lockp) { struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL; @@ -602,6 +603,9 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru return -ENODEV; if (snd_midi_event_encode_byte(mdev->coder, c, ev)) { snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port); + /* the caller must release this later */ + *lockp = &mdev->use_lock; + snd_use_lock_use(*lockp); return 0; } return -EINVAL; diff --git a/sound/core/seq/oss/seq_oss_midi.h b/sound/core/seq/oss/seq_oss_midi.h index bcc1683773dfb..4819d4170bf6d 100644 --- a/sound/core/seq/oss/seq_oss_midi.h +++ b/sound/core/seq/oss/seq_oss_midi.h @@ -26,7 +26,7 @@ void snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode); int snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev); void snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev); int snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, - struct snd_seq_event *ev); + struct snd_seq_event *ev, snd_use_lock_t **lockp); int snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private); int snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev); int snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf); diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c index 307ef98c44c7b..111c792bc72ca 100644 --- a/sound/core/seq/oss/seq_oss_rw.c +++ b/sound/core/seq/oss/seq_oss_rw.c @@ -153,6 +153,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) { int rc = 0; struct snd_seq_event event; + snd_use_lock_t *lock = NULL; /* if this is a timing event, process the current time */ if (snd_seq_oss_process_timer_event(dp->timer, rec)) @@ -164,7 +165,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) event.type = SNDRV_SEQ_EVENT_NOTEOFF; snd_seq_oss_fill_addr(dp, &event, dp->addr.client, dp->addr.port); - if (snd_seq_oss_process_event(dp, rec, &event)) + if (snd_seq_oss_process_event(dp, rec, &event, &lock)) return 0; /* invalid event - no need to insert queue */ event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer); @@ -173,6 +174,8 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) else rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt, !is_nonblock_mode(dp->file_mode)); + if (lock) + snd_use_lock_free(lock); return rc; } From 11165fe2c5ea0516debe486d91df67abbe36905e Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Wed, 27 May 2026 14:29:48 +0800 Subject: [PATCH 0852/3297] ALSA: seq: midi: Serialize output teardown with event_input [ Upstream commit ef7607ab1c8adc6258fb1b27d08e26aecdc18a58 ] event_process_midi() borrows msynth->output_rfile.output and then passes the substream to dump_midi() and snd_rawmidi_kernel_write() without synchronizing with the output open/close transition. midisynth_use() also publishes output_rfile before snd_rawmidi_output_params() has finished. The last midisynth_unuse() can therefore release the same rawmidi file and free substream->runtime before snd_rawmidi_kernel_write1() takes its runtime buffer reference. That leaves the event_input path using a stale substream or runtime and can end in a NULL-deref or use-after-free. Fix this with two pieces of synchronization. Keep a short IRQ-safe spinlock only for publishing or clearing output_rfile and for pairing the output snapshot with an snd_use_lock_t reference. Once event_process_midi() has taken that in-flight reference, it drops the spinlock before calling snd_seq_dump_var_event(), dump_midi(), or snd_rawmidi_kernel_write(). midisynth_unuse() now detaches the visible rawmidi file under the same spinlock, waits for the in-flight writers to drain, and only then drains and releases the saved file. midisynth_use() likewise opens into a local snd_rawmidi_file and publishes it only after snd_rawmidi_output_params() succeeds. The buggy scenario involves two paths, with each column showing the order within that path: event_input path: last unuse path: 1. event_process_midi() snapshots 1. midisynth_unuse() starts output_rfile.output. tearing down output_rfile. 2. dump_midi() reaches 2. snd_rawmidi_kernel_release() snd_rawmidi_kernel_write() closes the output file. before runtime is pinned. 3. close_substream() frees 3. The callback keeps using substream->runtime. the borrowed substream. Validation reproduced this kernel report: KASAN null-ptr-deref in snd_rawmidi_kernel_write1+0x56/0x360 RIP: 0033:0x7fde7dd0837f RIP: 0010:snd_rawmidi_kernel_write1+0x56/0x360 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Link: https://patch.msgid.link/20260527062948.3614025-1-rollkingzzc@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_midi.c | 55 +++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 581e138a31159..24d485caeb0a2 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -24,6 +24,7 @@ Possible options for midisynth module: #include #include #include +#include "seq_lock.h" MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); @@ -42,6 +43,8 @@ struct seq_midisynth { int device; int subdevice; struct snd_rawmidi_file input_rfile; + spinlock_t output_lock; /* protects output_rfile publication */ + snd_use_lock_t output_use_lock; /* in-flight event_input users */ struct snd_rawmidi_file output_rfile; int seq_client; int seq_port; @@ -125,31 +128,42 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, struct seq_midisynth *msynth = private_data; unsigned char msg[10]; /* buffer for constructing midi messages */ struct snd_rawmidi_substream *substream; + int err = 0; int len; if (snd_BUG_ON(!msynth)) return -EINVAL; - substream = msynth->output_rfile.output; - if (substream == NULL) - return -ENODEV; + + scoped_guard(spinlock_irqsave, &msynth->output_lock) { + substream = msynth->output_rfile.output; + if (!substream) + return -ENODEV; + snd_use_lock_use(&msynth->output_use_lock); + } + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */ if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { /* invalid event */ pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); - return 0; + goto out; } snd_seq_dump_var_event(ev, __dump_midi, substream); snd_midi_event_reset_decode(msynth->parser); } else { - if (msynth->parser == NULL) - return -EIO; + if (!msynth->parser) { + err = -EIO; + goto out; + } len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); if (len < 0) - return 0; + goto out; if (dump_midi(substream, msg, len) < 0) snd_midi_event_reset_decode(msynth->parser); } - return 0; + +out: + snd_use_lock_free(&msynth->output_use_lock); + return err; } @@ -163,6 +177,8 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, msynth->card = card; msynth->device = device; msynth->subdevice = subdevice; + spin_lock_init(&msynth->output_lock); + snd_use_lock_init(&msynth->output_use_lock); return 0; } @@ -215,12 +231,13 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info { int err; struct seq_midisynth *msynth = private_data; + struct snd_rawmidi_file rfile = {}; struct snd_rawmidi_params params; /* open midi port */ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, - &msynth->output_rfile); + &rfile); if (err < 0) { pr_debug("ALSA: seq_midi: midi output open failed!!!\n"); return err; @@ -229,12 +246,14 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info params.avail_min = 1; params.buffer_size = output_buffer_size; params.no_active_sensing = 1; - err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms); + err = snd_rawmidi_output_params(rfile.output, ¶ms); if (err < 0) { - snd_rawmidi_kernel_release(&msynth->output_rfile); + snd_rawmidi_kernel_release(&rfile); return err; } snd_midi_event_reset_decode(msynth->parser); + scoped_guard(spinlock_irqsave, &msynth->output_lock) + msynth->output_rfile = rfile; return 0; } @@ -242,11 +261,19 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) { struct seq_midisynth *msynth = private_data; + struct snd_rawmidi_file rfile = {}; - if (snd_BUG_ON(!msynth->output_rfile.output)) + scoped_guard(spinlock_irqsave, &msynth->output_lock) { + rfile = msynth->output_rfile; + msynth->output_rfile = (struct snd_rawmidi_file){}; + } + + if (snd_BUG_ON(!rfile.output)) return -EINVAL; - snd_rawmidi_drain_output(msynth->output_rfile.output); - return snd_rawmidi_kernel_release(&msynth->output_rfile); + + snd_use_lock_sync(&msynth->output_use_lock); + snd_rawmidi_drain_output(rfile.output); + return snd_rawmidi_kernel_release(&rfile); } /* delete given midi synth port */ From c418c956c21c97ee66270c1260daff9adfb39390 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 26 May 2026 10:01:08 +0200 Subject: [PATCH 0853/3297] selftests: Fix Makefile target for nsfs [ Upstream commit 77d1a2d2318fa96f8a662c9ad6647abedcd22734 ] The kselftests for nsfs where moved under filesystem/ with commit cae73d3bdce5 ("seltests: move nsfs into filesystems subfolder"). However, the kselftest TARGETS declaration was not adjusted. Since the kselftest Makefile ignores errors unless no target builds, the invalid target declaration can easily be missed. Fix this by adjusting the TARGETS accordingly. Fixes: cae73d3bdce5 ("seltests: move nsfs into filesystems subfolder") Signed-off-by: Florian Schmaus Link: https://patch.msgid.link/20260526-kselftest-nsfs-v1-1-7b042ebe42d6@geekplace.eu Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- tools/testing/selftests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index c46ebdb9b8ef7..dbf81e9a7785a 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -36,6 +36,7 @@ TARGETS += filesystems/fat TARGETS += filesystems/overlayfs TARGETS += filesystems/statmount TARGETS += filesystems/mount-notify +TARGETS += filesystems/nsfs TARGETS += filesystems/fuse TARGETS += firmware TARGETS += fpu @@ -79,7 +80,6 @@ TARGETS += net/packetdrill TARGETS += net/rds TARGETS += net/tcp_ao TARGETS += nolibc -TARGETS += nsfs TARGETS += pci_endpoint TARGETS += pcie_bwctrl TARGETS += perf_events From 95e0b4bc29ab9a4f850bae80d8457a778ee62aae Mon Sep 17 00:00:00 2001 From: Joey Lu Date: Mon, 11 May 2026 11:17:49 +0800 Subject: [PATCH 0854/3297] pinctrl: nuvoton: ma35d1: fix MFP register offset and pin table [ Upstream commit ce4e27247ca643645c6d3043aad1c1c67bf7fdac ] Each GPIO bank has two 32-bit MFP registers: MFPL covering pins 0-7 at the bank base offset, and MFPH covering pins 8-15 at base offset+4. ma35_pinctrl_parse_groups() computed the register address without accounting for this split, so any pin with an index >= 8 within its bank was written to the wrong register. Also fix the pin descriptor table in pinctrl-ma35d1.c: switch from sequential to 16-per-bank pin numbering, add missing PC8-PC11 pins and their mux options, and remove the duplicate PN10-PN15 entries. Fixes: f805e356313b ("pinctrl: nuvoton: Add ma35d1 pinctrl and GPIO driver") Signed-off-by: Joey Lu Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/nuvoton/pinctrl-ma35.c | 3 +- drivers/pinctrl/nuvoton/pinctrl-ma35d1.c | 470 +++++++++++++---------- 2 files changed, 263 insertions(+), 210 deletions(-) diff --git a/drivers/pinctrl/nuvoton/pinctrl-ma35.c b/drivers/pinctrl/nuvoton/pinctrl-ma35.c index cdad01d68a37e..f5efa6725c405 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-ma35.c +++ b/drivers/pinctrl/nuvoton/pinctrl-ma35.c @@ -1018,7 +1018,8 @@ static int ma35_pinctrl_parse_groups(struct fwnode_handle *fwnode, struct group_ grp->data = pin; for (i = 0, j = 0; i < count; i += 3, j++) { - pin->offset = elems[i] * MA35_MFP_REG_SZ_PER_BANK + MA35_MFP_REG_BASE; + pin->offset = elems[i] * MA35_MFP_REG_SZ_PER_BANK + MA35_MFP_REG_BASE + + (elems[i + 1] >= 8 ? 4 : 0); pin->shift = (elems[i + 1] * MA35_MFP_BITS_PER_PORT) % 32; pin->muxval = elems[i + 2]; pin->configs = configs; diff --git a/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c b/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c index eafa06ca08791..9d4627c80a52c 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c +++ b/drivers/pinctrl/nuvoton/pinctrl-ma35d1.c @@ -113,6 +113,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x0, "GPA14"), MA35_MUX(0x2, "UART7_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "NAND_nWP"), MA35_MUX(0x7, "EBI_AD14"), MA35_MUX(0x9, "EBI_ADR14")), @@ -123,6 +124,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x3, "UART6_RXD"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x5, "CAN2_RXD"), + MA35_MUX(0x6, "USBHL0_DM"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0x9, "QEI0_A"), MA35_MUX(0xb, "TM1"), @@ -187,6 +189,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH5"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI0_MOSI"), MA35_MUX(0x6, "EBI_MCLK"), MA35_MUX(0x7, "CCAP1_VSYNC"), @@ -202,6 +205,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_BRAKE1"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI0_MISO"), MA35_MUX(0x6, "I2S1_MCLK"), MA35_MUX(0x7, "CCAP1_SFIELD"), @@ -220,6 +224,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x4, "I2C3_SDA"), MA35_MUX(0x5, "CAN2_RXD"), MA35_MUX(0x6, "I2S1_LRCK"), + MA35_MUX(0x7, "USBHL1_DM"), MA35_MUX(0x8, "ADC0_CH4"), MA35_MUX(0x9, "EBI_ADR16"), MA35_MUX(0xe, "ECAP2_IC0")), @@ -231,6 +236,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x5, "CAN2_TXD"), MA35_MUX(0x6, "I2S1_BCLK"), + MA35_MUX(0x7, "USBHL1_DP"), MA35_MUX(0x8, "ADC0_CH5"), MA35_MUX(0x9, "EBI_ADR17"), MA35_MUX(0xe, "ECAP2_IC1")), @@ -239,6 +245,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH2"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x5, "I2C4_SDA"), MA35_MUX(0x6, "I2S1_DI"), MA35_MUX(0x8, "ADC0_CH6"), @@ -249,6 +256,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x1, "EPWM2_CH3"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x5, "I2C4_SCL"), MA35_MUX(0x6, "I2S1_DO"), MA35_MUX(0x8, "ADC0_CH7"), @@ -264,10 +272,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_PIN(34, PC2, 0x90, 0x8, MA35_MUX(0x0, "GPC2"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x6, "SD0_DAT0/eMMC0_DAT0")), MA35_PIN(35, PC3, 0x90, 0xc, MA35_MUX(0x0, "GPC3"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x6, "SD0_DAT1/eMMC0_DAT1")), MA35_PIN(36, PC4, 0x90, 0x10, MA35_MUX(0x0, "GPC4"), @@ -280,65 +290,100 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_PIN(38, PC6, 0x90, 0x18, MA35_MUX(0x0, "GPC6"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "SD0_nCD")), MA35_PIN(39, PC7, 0x90, 0x1c, MA35_MUX(0x0, "GPC7"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "SD0_WP")), - MA35_PIN(40, PC12, 0x94, 0x10, + MA35_PIN(40, PC8, 0x94, 0x0, + MA35_MUX(0x0, "GPC8"), + MA35_MUX(0x1, "EPWM2_CH0"), + MA35_MUX(0x2, "UART10_nCTS"), + MA35_MUX(0x3, "UART9_RXD"), + MA35_MUX(0x4, "I2C0_SDA"), + MA35_MUX(0x5, "SPI1_SS0"), + MA35_MUX(0x6, "SD0_DAT4/eMMC0_DAT4")), + MA35_PIN(41, PC9, 0x94, 0x4, + MA35_MUX(0x0, "GPC9"), + MA35_MUX(0x1, "EPWM2_CH1"), + MA35_MUX(0x2, "UART10_nRTS"), + MA35_MUX(0x3, "UART9_TXD"), + MA35_MUX(0x4, "I2C0_SCL"), + MA35_MUX(0x5, "SPI1_CLK"), + MA35_MUX(0x6, "SD0_DAT5/eMMC0_DAT5")), + MA35_PIN(42, PC10, 0x94, 0x8, + MA35_MUX(0x0, "GPC10"), + MA35_MUX(0x1, "EPWM2_CH2"), + MA35_MUX(0x2, "UART10_RXD"), + MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), + MA35_MUX(0x5, "SPI1_MOSI"), + MA35_MUX(0x6, "SD0_DAT6/eMMC0_DAT6")), + MA35_PIN(43, PC11, 0x94, 0xc, + MA35_MUX(0x0, "GPC11"), + MA35_MUX(0x1, "EPWM2_CH3"), + MA35_MUX(0x2, "UART10_TXD"), + MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), + MA35_MUX(0x5, "SPI1_MISO"), + MA35_MUX(0x6, "SD0_DAT7/eMMC0_DAT7")), + MA35_PIN(44, PC12, 0x94, 0x10, MA35_MUX(0x0, "GPC12"), MA35_MUX(0x2, "UART12_nCTS"), MA35_MUX(0x3, "UART11_RXD"), MA35_MUX(0x6, "LCM_DATA16")), - MA35_PIN(41, PC13, 0x94, 0x14, + MA35_PIN(45, PC13, 0x94, 0x14, MA35_MUX(0x0, "GPC13"), MA35_MUX(0x2, "UART12_nRTS"), MA35_MUX(0x3, "UART11_TXD"), MA35_MUX(0x6, "LCM_DATA17")), - MA35_PIN(42, PC14, 0x94, 0x18, + MA35_PIN(46, PC14, 0x94, 0x18, MA35_MUX(0x0, "GPC14"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x6, "LCM_DATA18")), - MA35_PIN(43, PC15, 0x94, 0x1c, + MA35_PIN(47, PC15, 0x94, 0x1c, MA35_MUX(0x0, "GPC15"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x6, "LCM_DATA19"), MA35_MUX(0x7, "LCM_MPU_TE"), MA35_MUX(0x8, "LCM_MPU_VSYNC")), - MA35_PIN(44, PD0, 0x98, 0x0, + MA35_PIN(48, PD0, 0x98, 0x0, MA35_MUX(0x0, "GPD0"), MA35_MUX(0x2, "UART3_nCTS"), MA35_MUX(0x3, "UART4_RXD"), MA35_MUX(0x5, "QSPI0_SS0")), - MA35_PIN(45, PD1, 0x98, 0x4, + MA35_PIN(49, PD1, 0x98, 0x4, MA35_MUX(0x0, "GPD1"), MA35_MUX(0x2, "UART3_nRTS"), MA35_MUX(0x3, "UART4_TXD"), MA35_MUX(0x5, "QSPI0_CLK")), - MA35_PIN(46, PD2, 0x98, 0x8, + MA35_PIN(50, PD2, 0x98, 0x8, MA35_MUX(0x0, "GPD2"), MA35_MUX(0x2, "UART3_RXD"), MA35_MUX(0x5, "QSPI0_MOSI0")), - MA35_PIN(47, PD3, 0x98, 0xc, + MA35_PIN(51, PD3, 0x98, 0xc, MA35_MUX(0x0, "GPD3"), MA35_MUX(0x2, "UART3_TXD"), MA35_MUX(0x5, "QSPI0_MISO0")), - MA35_PIN(48, PD4, 0x98, 0x10, + MA35_PIN(52, PD4, 0x98, 0x10, MA35_MUX(0x0, "GPD4"), MA35_MUX(0x2, "UART1_nCTS"), MA35_MUX(0x3, "UART2_RXD"), MA35_MUX(0x4, "I2C2_SDA"), MA35_MUX(0x5, "QSPI0_MOSI1")), - MA35_PIN(49, PD5, 0x98, 0x14, + MA35_PIN(53, PD5, 0x98, 0x14, MA35_MUX(0x0, "GPD5"), MA35_MUX(0x2, "UART1_nRTS"), MA35_MUX(0x3, "UART2_TXD"), MA35_MUX(0x4, "I2C2_SCL"), MA35_MUX(0x5, "QSPI0_MISO1")), - MA35_PIN(50, PD6, 0x98, 0x18, + MA35_PIN(54, PD6, 0x98, 0x18, MA35_MUX(0x0, "GPD6"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x5, "QSPI1_MOSI1"), MA35_MUX(0x6, "I2C0_SDA"), MA35_MUX(0x7, "I2S0_MCLK"), @@ -346,10 +391,11 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD5"), MA35_MUX(0xa, "SPI3_SS1"), MA35_MUX(0xb, "TRACE_CLK")), - MA35_PIN(51, PD7, 0x98, 0x1c, + MA35_PIN(55, PD7, 0x98, 0x1c, MA35_MUX(0x0, "GPD7"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x5, "QSPI1_MISO1"), MA35_MUX(0x6, "I2C0_SCL"), MA35_MUX(0x7, "I2S1_MCLK"), @@ -357,7 +403,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD6"), MA35_MUX(0xa, "SC1_nCD"), MA35_MUX(0xb, "EADC0_ST")), - MA35_PIN(52, PD8, 0x9c, 0x0, + MA35_PIN(56, PD8, 0x9c, 0x0, MA35_MUX(0x0, "GPD8"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART16_nCTS"), @@ -368,7 +414,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD7"), MA35_MUX(0xa, "SC1_CLK"), MA35_MUX(0xb, "TM0")), - MA35_PIN(53, PD9, 0x9c, 0x4, + MA35_PIN(57, PD9, 0x9c, 0x4, MA35_MUX(0x0, "GPD9"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART16_nRTS"), @@ -379,7 +425,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD8"), MA35_MUX(0xa, "SC1_DAT"), MA35_MUX(0xb, "TM0_EXT")), - MA35_PIN(54, PD10, 0x9c, 0x8, + MA35_PIN(58, PD10, 0x9c, 0x8, MA35_MUX(0x0, "GPD10"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART16_RXD"), @@ -389,7 +435,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD9"), MA35_MUX(0xa, "SC1_RST"), MA35_MUX(0xb, "TM2")), - MA35_PIN(55, PD11, 0x9c, 0xc, + MA35_PIN(59, PD11, 0x9c, 0xc, MA35_MUX(0x0, "GPD11"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART16_TXD"), @@ -399,7 +445,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EBI_AD10"), MA35_MUX(0xa, "SC1_PWR"), MA35_MUX(0xb, "TM2_EXT")), - MA35_PIN(56, PD12, 0x9c, 0x10, + MA35_PIN(60, PD12, 0x9c, 0x10, MA35_MUX(0x0, "GPD12"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART11_TXD"), @@ -412,7 +458,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM5"), MA35_MUX(0xc, "I2S1_LRCK"), MA35_MUX(0xd, "INT1")), - MA35_PIN(57, PD13, 0x9c, 0x14, + MA35_PIN(61, PD13, 0x9c, 0x14, MA35_MUX(0x0, "GPD13"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART11_RXD"), @@ -424,11 +470,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "ECAP0_IC0"), MA35_MUX(0xb, "TM5_EXT"), MA35_MUX(0xc, "I2S1_BCLK")), - MA35_PIN(58, PD14, 0x9c, 0x18, + MA35_PIN(62, PD14, 0x9c, 0x18, MA35_MUX(0x0, "GPD14"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART11_nCTS"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "TRACE_DATA2"), MA35_MUX(0x7, "EBI_MCLK"), MA35_MUX(0x8, "EBI_AD6"), @@ -436,116 +483,117 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM6"), MA35_MUX(0xc, "I2S1_DI"), MA35_MUX(0xd, "INT3")), - MA35_PIN(59, PD15, 0x9c, 0x1c, + MA35_PIN(63, PD15, 0x9c, 0x1c, MA35_MUX(0x0, "GPD15"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART11_nRTS"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "TRACE_DATA3"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0x8, "EBI_AD7"), MA35_MUX(0x9, "ECAP0_IC2"), MA35_MUX(0xb, "TM6_EXT"), MA35_MUX(0xc, "I2S1_DO")), - MA35_PIN(60, PE0, 0xa0, 0x0, + MA35_PIN(64, PE0, 0xa0, 0x0, MA35_MUX(0x0, "GPE0"), MA35_MUX(0x2, "UART9_nCTS"), MA35_MUX(0x3, "UART8_RXD"), MA35_MUX(0x7, "CCAP1_DATA0"), MA35_MUX(0x8, "RGMII0_MDC"), MA35_MUX(0x9, "RMII0_MDC")), - MA35_PIN(61, PE1, 0xa0, 0x4, + MA35_PIN(65, PE1, 0xa0, 0x4, MA35_MUX(0x0, "GPE1"), MA35_MUX(0x2, "UART9_nRTS"), MA35_MUX(0x3, "UART8_TXD"), MA35_MUX(0x7, "CCAP1_DATA1"), MA35_MUX(0x8, "RGMII0_MDIO"), MA35_MUX(0x9, "RMII0_MDIO")), - MA35_PIN(62, PE2, 0xa0, 0x8, + MA35_PIN(66, PE2, 0xa0, 0x8, MA35_MUX(0x0, "GPE2"), MA35_MUX(0x2, "UART9_RXD"), MA35_MUX(0x7, "CCAP1_DATA2"), MA35_MUX(0x8, "RGMII0_TXCTL"), MA35_MUX(0x9, "RMII0_TXEN")), - MA35_PIN(63, PE3, 0xa0, 0xc, + MA35_PIN(67, PE3, 0xa0, 0xc, MA35_MUX(0x0, "GPE3"), MA35_MUX(0x2, "UART9_TXD"), MA35_MUX(0x7, "CCAP1_DATA3"), MA35_MUX(0x8, "RGMII0_TXD0"), MA35_MUX(0x9, "RMII0_TXD0")), - MA35_PIN(64, PE4, 0xa0, 0x10, + MA35_PIN(68, PE4, 0xa0, 0x10, MA35_MUX(0x0, "GPE4"), MA35_MUX(0x2, "UART4_nCTS"), MA35_MUX(0x3, "UART3_RXD"), MA35_MUX(0x7, "CCAP1_DATA4"), MA35_MUX(0x8, "RGMII0_TXD1"), MA35_MUX(0x9, "RMII0_TXD1")), - MA35_PIN(65, PE5, 0xa0, 0x14, + MA35_PIN(69, PE5, 0xa0, 0x14, MA35_MUX(0x0, "GPE5"), MA35_MUX(0x2, "UART4_nRTS"), MA35_MUX(0x3, "UART3_TXD"), MA35_MUX(0x7, "CCAP1_DATA5"), MA35_MUX(0x8, "RGMII0_RXCLK"), MA35_MUX(0x9, "RMII0_REFCLK")), - MA35_PIN(66, PE6, 0xa0, 0x18, + MA35_PIN(70, PE6, 0xa0, 0x18, MA35_MUX(0x0, "GPE6"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x7, "CCAP1_DATA6"), MA35_MUX(0x8, "RGMII0_RXCTL"), MA35_MUX(0x9, "RMII0_CRSDV")), - MA35_PIN(67, PE7, 0xa0, 0x1c, + MA35_PIN(71, PE7, 0xa0, 0x1c, MA35_MUX(0x0, "GPE7"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x7, "CCAP1_DATA7"), MA35_MUX(0x8, "RGMII0_RXD0"), MA35_MUX(0x9, "RMII0_RXD0")), - MA35_PIN(68, PE8, 0xa4, 0x0, + MA35_PIN(72, PE8, 0xa4, 0x0, MA35_MUX(0x0, "GPE8"), MA35_MUX(0x2, "UART13_nCTS"), MA35_MUX(0x3, "UART12_RXD"), MA35_MUX(0x7, "CCAP1_SCLK"), MA35_MUX(0x8, "RGMII0_RXD1"), MA35_MUX(0x9, "RMII0_RXD1")), - MA35_PIN(69, PE9, 0xa4, 0x4, + MA35_PIN(73, PE9, 0xa4, 0x4, MA35_MUX(0x0, "GPE9"), MA35_MUX(0x2, "UART13_nRTS"), MA35_MUX(0x3, "UART12_TXD"), MA35_MUX(0x7, "CCAP1_PIXCLK"), MA35_MUX(0x8, "RGMII0_RXD2"), MA35_MUX(0x9, "RMII0_RXERR")), - MA35_PIN(70, PE10, 0xa4, 0x8, + MA35_PIN(74, PE10, 0xa4, 0x8, MA35_MUX(0x0, "GPE10"), MA35_MUX(0x2, "UART15_nCTS"), MA35_MUX(0x3, "UART14_RXD"), MA35_MUX(0x5, "SPI1_SS0"), MA35_MUX(0x7, "CCAP1_HSYNC"), MA35_MUX(0x8, "RGMII0_RXD3")), - MA35_PIN(71, PE11, 0xa4, 0xc, + MA35_PIN(75, PE11, 0xa4, 0xc, MA35_MUX(0x0, "GPE11"), MA35_MUX(0x2, "UART15_nRTS"), MA35_MUX(0x3, "UART14_TXD"), MA35_MUX(0x5, "SPI1_CLK"), MA35_MUX(0x7, "CCAP1_VSYNC"), MA35_MUX(0x8, "RGMII0_TXCLK")), - MA35_PIN(72, PE12, 0xa4, 0x10, + MA35_PIN(76, PE12, 0xa4, 0x10, MA35_MUX(0x0, "GPE12"), MA35_MUX(0x2, "UART15_RXD"), MA35_MUX(0x5, "SPI1_MOSI"), MA35_MUX(0x7, "CCAP1_DATA8"), MA35_MUX(0x8, "RGMII0_TXD2")), - MA35_PIN(73, PE13, 0xa4, 0x14, + MA35_PIN(77, PE13, 0xa4, 0x14, MA35_MUX(0x0, "GPE13"), MA35_MUX(0x2, "UART15_TXD"), MA35_MUX(0x5, "SPI1_MISO"), MA35_MUX(0x7, "CCAP1_DATA9"), MA35_MUX(0x8, "RGMII0_TXD3")), - MA35_PIN(74, PE14, 0xa4, 0x18, + MA35_PIN(78, PE14, 0xa4, 0x18, MA35_MUX(0x0, "GPE14"), MA35_MUX(0x1, "UART0_TXD")), - MA35_PIN(75, PE15, 0xa4, 0x1c, + MA35_PIN(79, PE15, 0xa4, 0x1c, MA35_MUX(0x0, "GPE15"), MA35_MUX(0x1, "UART0_RXD")), - MA35_PIN(76, PF0, 0xa8, 0x0, + MA35_PIN(80, PF0, 0xa8, 0x0, MA35_MUX(0x0, "GPF0"), MA35_MUX(0x2, "UART2_nCTS"), MA35_MUX(0x3, "UART1_RXD"), @@ -553,7 +601,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_MDC"), MA35_MUX(0x9, "RMII1_MDC"), MA35_MUX(0xe, "KPI_COL0")), - MA35_PIN(77, PF1, 0xa8, 0x4, + MA35_PIN(81, PF1, 0xa8, 0x4, MA35_MUX(0x0, "GPF1"), MA35_MUX(0x2, "UART2_nRTS"), MA35_MUX(0x3, "UART1_TXD"), @@ -561,21 +609,21 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_MDIO"), MA35_MUX(0x9, "RMII1_MDIO"), MA35_MUX(0xe, "KPI_COL1")), - MA35_PIN(78, PF2, 0xa8, 0x8, + MA35_PIN(82, PF2, 0xa8, 0x8, MA35_MUX(0x0, "GPF2"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x6, "RGMII0_TXD2"), MA35_MUX(0x8, "RGMII1_TXCTL"), MA35_MUX(0x9, "RMII1_TXEN"), MA35_MUX(0xe, "KPI_COL2")), - MA35_PIN(79, PF3, 0xa8, 0xc, + MA35_PIN(83, PF3, 0xa8, 0xc, MA35_MUX(0x0, "GPF3"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x6, "RGMII0_TXD3"), MA35_MUX(0x8, "RGMII1_TXD0"), MA35_MUX(0x9, "RMII1_TXD0"), MA35_MUX(0xe, "KPI_COL3")), - MA35_PIN(80, PF4, 0xa8, 0x10, + MA35_PIN(84, PF4, 0xa8, 0x10, MA35_MUX(0x0, "GPF4"), MA35_MUX(0x2, "UART11_nCTS"), MA35_MUX(0x3, "UART10_RXD"), @@ -583,9 +631,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x5, "SPI1_SS0"), MA35_MUX(0x8, "RGMII1_TXD1"), MA35_MUX(0x9, "RMII1_TXD1"), + MA35_MUX(0xc, "USBHL0_DM"), MA35_MUX(0xd, "CAN2_RXD"), MA35_MUX(0xe, "KPI_ROW0")), - MA35_PIN(81, PF5, 0xa8, 0x14, + MA35_PIN(85, PF5, 0xa8, 0x14, MA35_MUX(0x0, "GPF5"), MA35_MUX(0x2, "UART11_nRTS"), MA35_MUX(0x3, "UART10_TXD"), @@ -593,9 +642,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x5, "SPI1_CLK"), MA35_MUX(0x8, "RGMII1_RXCLK"), MA35_MUX(0x9, "RMII1_REFCLK"), + MA35_MUX(0xc, "USBHL0_DP"), MA35_MUX(0xd, "CAN2_TXD"), MA35_MUX(0xe, "KPI_ROW1")), - MA35_PIN(82, PF6, 0xa8, 0x18, + MA35_PIN(86, PF6, 0xa8, 0x18, MA35_MUX(0x0, "GPF6"), MA35_MUX(0x2, "UART11_RXD"), MA35_MUX(0x4, "I2S0_DI"), @@ -605,7 +655,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "I2C4_SDA"), MA35_MUX(0xd, "SC0_CLK"), MA35_MUX(0xe, "KPI_ROW2")), - MA35_PIN(83, PF7, 0xa8, 0x1c, + MA35_PIN(87, PF7, 0xa8, 0x1c, MA35_MUX(0x0, "GPF7"), MA35_MUX(0x2, "UART11_TXD"), MA35_MUX(0x4, "I2S0_DO"), @@ -615,7 +665,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "I2C4_SCL"), MA35_MUX(0xd, "SC0_DAT"), MA35_MUX(0xe, "KPI_ROW3")), - MA35_PIN(84, PF8, 0xac, 0x0, + MA35_PIN(88, PF8, 0xac, 0x0, MA35_MUX(0x0, "GPF8"), MA35_MUX(0x2, "UART13_RXD"), MA35_MUX(0x4, "I2C5_SDA"), @@ -624,7 +674,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "RMII1_RXD1"), MA35_MUX(0xd, "SC0_RST"), MA35_MUX(0xe, "KPI_COL4")), - MA35_PIN(85, PF9, 0xac, 0x4, + MA35_PIN(89, PF9, 0xac, 0x4, MA35_MUX(0x0, "GPF9"), MA35_MUX(0x2, "UART13_TXD"), MA35_MUX(0x4, "I2C5_SCL"), @@ -633,7 +683,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "RMII1_RXERR"), MA35_MUX(0xd, "SC0_PWR"), MA35_MUX(0xe, "KPI_COL5")), - MA35_PIN(86, PF10, 0xac, 0x8, + MA35_PIN(90, PF10, 0xac, 0x8, MA35_MUX(0x0, "GPF10"), MA35_MUX(0x2, "UART13_nCTS"), MA35_MUX(0x5, "I2S0_LRCK"), @@ -641,7 +691,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_RXD3"), MA35_MUX(0x9, "SC0_CLK"), MA35_MUX(0xe, "KPI_COL6")), - MA35_PIN(87, PF11, 0xac, 0xc, + MA35_PIN(91, PF11, 0xac, 0xc, MA35_MUX(0x0, "GPF11"), MA35_MUX(0x2, "UART13_nRTS"), MA35_MUX(0x5, "I2S0_BCLK"), @@ -649,21 +699,21 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "RGMII1_TXCLK"), MA35_MUX(0x9, "SC0_DAT"), MA35_MUX(0xe, "KPI_COL7")), - MA35_PIN(88, PF12, 0xac, 0x10, + MA35_PIN(92, PF12, 0xac, 0x10, MA35_MUX(0x0, "GPF12"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "SPI1_MOSI"), MA35_MUX(0x8, "RGMII1_TXD2"), MA35_MUX(0x9, "SC0_RST"), MA35_MUX(0xe, "KPI_ROW4")), - MA35_PIN(89, PF13, 0xac, 0x14, + MA35_PIN(93, PF13, 0xac, 0x14, MA35_MUX(0x0, "GPF13"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x6, "SPI1_MISO"), MA35_MUX(0x8, "RGMII1_TXD3"), MA35_MUX(0x9, "SC0_PWR"), MA35_MUX(0xe, "KPI_ROW5")), - MA35_PIN(90, PF14, 0xac, 0x18, + MA35_PIN(94, PF14, 0xac, 0x18, MA35_MUX(0x0, "GPF14"), MA35_MUX(0x1, "EPWM2_BRAKE0"), MA35_MUX(0x2, "EADC0_ST"), @@ -679,10 +729,10 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "SPI1_SS1"), MA35_MUX(0xe, "QEI2_INDEX"), MA35_MUX(0xf, "I2S0_MCLK")), - MA35_PIN(91, PF15, 0xac, 0x1c, + MA35_PIN(95, PF15, 0xac, 0x1c, MA35_MUX(0x0, "GPF15"), MA35_MUX(0x1, "HSUSB0_VBUSVLD")), - MA35_PIN(92, PG0, 0xb0, 0x0, + MA35_PIN(96, PG0, 0xb0, 0x0, MA35_MUX(0x0, "GPG0"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART7_TXD"), @@ -696,19 +746,20 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xc, "CLKO"), MA35_MUX(0xd, "INT0"), MA35_MUX(0xf, "EBI_ADR15")), - MA35_PIN(93, PG1, 0xb0, 0x4, + MA35_PIN(97, PG1, 0xb0, 0x4, MA35_MUX(0x0, "GPG1"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART9_nRTS"), MA35_MUX(0x3, "UART6_TXD"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x5, "CAN2_TXD"), + MA35_MUX(0x6, "USBHL0_DP"), MA35_MUX(0x7, "EBI_nCS0"), MA35_MUX(0x9, "QEI0_B"), MA35_MUX(0xb, "TM1_EXT"), MA35_MUX(0xe, "RGMII1_PPS"), MA35_MUX(0xf, "RMII1_PPS")), - MA35_PIN(94, PG2, 0xb0, 0x8, + MA35_PIN(98, PG2, 0xb0, 0x8, MA35_MUX(0x0, "GPG2"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART9_RXD"), @@ -719,7 +770,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "QEI0_A"), MA35_MUX(0xb, "TM3"), MA35_MUX(0xd, "INT1")), - MA35_PIN(95, PG3, 0xb0, 0xc, + MA35_PIN(99, PG3, 0xb0, 0xc, MA35_MUX(0x0, "GPG3"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART9_TXD"), @@ -731,7 +782,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "QEI0_B"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xc, "I2S1_MCLK")), - MA35_PIN(96, PG4, 0xb0, 0x10, + MA35_PIN(100, PG4, 0xb0, 0x10, MA35_MUX(0x0, "GPG4"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART5_nCTS"), @@ -745,7 +796,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4"), MA35_MUX(0xd, "INT2"), MA35_MUX(0xe, "ECAP1_IC2")), - MA35_PIN(97, PG5, 0xb0, 0x14, + MA35_PIN(101, PG5, 0xb0, 0x14, MA35_MUX(0x0, "GPG5"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART5_nRTS"), @@ -757,7 +808,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "I2S1_DI"), MA35_MUX(0xa, "SC1_DAT"), MA35_MUX(0xb, "TM4_EXT")), - MA35_PIN(98, PG6, 0xb0, 0x18, + MA35_PIN(102, PG6, 0xb0, 0x18, MA35_MUX(0x0, "GPG6"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART5_RXD"), @@ -769,7 +820,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "SC1_RST"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xd, "INT3")), - MA35_PIN(99, PG7, 0xb0, 0x1c, + MA35_PIN(103, PG7, 0xb0, 0x1c, MA35_MUX(0x0, "GPG7"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART5_TXD"), @@ -780,27 +831,29 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "I2S1_LRCK"), MA35_MUX(0xa, "SC1_PWR"), MA35_MUX(0xb, "TM7_EXT")), - MA35_PIN(100, PG8, 0xb4, 0x0, + MA35_PIN(104, PG8, 0xb4, 0x0, MA35_MUX(0x0, "GPG8"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_SS0"), MA35_MUX(0x6, "LCM_VSYNC"), MA35_MUX(0x7, "I2C3_SDA"), MA35_MUX(0xc, "EBI_AD7"), MA35_MUX(0xd, "EBI_nCS0")), - MA35_PIN(101, PG9, 0xb4, 0x4, + MA35_PIN(105, PG9, 0xb4, 0x4, MA35_MUX(0x0, "GPG9"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_CLK"), MA35_MUX(0x6, "LCM_HSYNC"), MA35_MUX(0x7, "I2C3_SCL"), MA35_MUX(0xc, "EBI_AD8"), MA35_MUX(0xd, "EBI_nCS1")), - MA35_PIN(102, PG10, 0xb4, 0x8, + MA35_PIN(106, PG10, 0xb4, 0x8, MA35_MUX(0x0, "GPG10"), MA35_MUX(0x2, "UART12_nRTS"), MA35_MUX(0x3, "UART13_TXD"), @@ -808,7 +861,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "LCM_CLK"), MA35_MUX(0xc, "EBI_AD9"), MA35_MUX(0xd, "EBI_nWRH")), - MA35_PIN(103, PG11, 0xb4, 0xc, + MA35_PIN(107, PG11, 0xb4, 0xc, MA35_MUX(0x0, "GPG11"), MA35_MUX(0x3, "JTAG_TDO"), MA35_MUX(0x5, "I2S0_MCLK"), @@ -816,93 +869,93 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x7, "EBI_nWRH"), MA35_MUX(0x8, "EBI_nCS1"), MA35_MUX(0xa, "EBI_AD0")), - MA35_PIN(104, PG12, 0xb4, 0x10, + MA35_PIN(108, PG12, 0xb4, 0x10, MA35_MUX(0x0, "GPG12"), MA35_MUX(0x3, "JTAG_TCK/SW_CLK"), MA35_MUX(0x5, "I2S0_LRCK"), MA35_MUX(0x7, "EBI_nWRL"), MA35_MUX(0xa, "EBI_AD1")), - MA35_PIN(105, PG13, 0xb4, 0x14, + MA35_PIN(109, PG13, 0xb4, 0x14, MA35_MUX(0x0, "GPG13"), MA35_MUX(0x3, "JTAG_TMS/SW_DIO"), MA35_MUX(0x5, "I2S0_BCLK"), MA35_MUX(0x7, "EBI_MCLK"), MA35_MUX(0xa, "EBI_AD2")), - MA35_PIN(106, PG14, 0xb4, 0x18, + MA35_PIN(110, PG14, 0xb4, 0x18, MA35_MUX(0x0, "GPG14"), MA35_MUX(0x3, "JTAG_TDI"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "NAND_nCS1"), MA35_MUX(0x7, "EBI_ALE"), MA35_MUX(0xa, "EBI_AD3")), - MA35_PIN(107, PG15, 0xb4, 0x1c, + MA35_PIN(111, PG15, 0xb4, 0x1c, MA35_MUX(0x0, "GPG15"), MA35_MUX(0x3, "JTAG_nTRST"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x7, "EBI_nCS0"), MA35_MUX(0xa, "EBI_AD4")), - MA35_PIN(108, PH0, 0xb8, 0x0, + MA35_PIN(112, PH0, 0xb8, 0x0, MA35_MUX(0x0, "GPH0"), MA35_MUX(0x2, "UART8_nCTS"), MA35_MUX(0x3, "UART7_RXD"), MA35_MUX(0x6, "LCM_DATA8")), - MA35_PIN(109, PH1, 0xb8, 0x4, + MA35_PIN(113, PH1, 0xb8, 0x4, MA35_MUX(0x0, "GPH1"), MA35_MUX(0x2, "UART8_nRTS"), MA35_MUX(0x3, "UART7_TXD"), MA35_MUX(0x6, "LCM_DATA9")), - MA35_PIN(110, PH2, 0xb8, 0x8, + MA35_PIN(114, PH2, 0xb8, 0x8, MA35_MUX(0x0, "GPH2"), MA35_MUX(0x2, "UART8_RXD"), MA35_MUX(0x6, "LCM_DATA10")), - MA35_PIN(111, PH3, 0xb8, 0xc, + MA35_PIN(115, PH3, 0xb8, 0xc, MA35_MUX(0x0, "GPH3"), MA35_MUX(0x2, "UART8_TXD"), MA35_MUX(0x6, "LCM_DATA11")), - MA35_PIN(112, PH4, 0xb8, 0x10, + MA35_PIN(116, PH4, 0xb8, 0x10, MA35_MUX(0x0, "GPH4"), MA35_MUX(0x2, "UART10_nCTS"), MA35_MUX(0x3, "UART9_RXD"), MA35_MUX(0x6, "LCM_DATA12")), - MA35_PIN(113, PH5, 0xb8, 0x14, + MA35_PIN(117, PH5, 0xb8, 0x14, MA35_MUX(0x0, "GPH5"), MA35_MUX(0x2, "UART10_nRTS"), MA35_MUX(0x3, "UART9_TXD"), MA35_MUX(0x6, "LCM_DATA13")), - MA35_PIN(114, PH6, 0xb8, 0x18, + MA35_PIN(118, PH6, 0xb8, 0x18, MA35_MUX(0x0, "GPH6"), MA35_MUX(0x2, "UART10_RXD"), MA35_MUX(0x6, "LCM_DATA14")), - MA35_PIN(115, PH7, 0xb8, 0x1c, + MA35_PIN(119, PH7, 0xb8, 0x1c, MA35_MUX(0x0, "GPH7"), MA35_MUX(0x2, "UART10_TXD"), MA35_MUX(0x6, "LCM_DATA15")), - MA35_PIN(116, PH8, 0xbc, 0x0, + MA35_PIN(120, PH8, 0xbc, 0x0, MA35_MUX(0x0, "GPH8"), MA35_MUX(0x6, "TAMPER0")), - MA35_PIN(117, PH9, 0xbc, 0x4, + MA35_PIN(121, PH9, 0xbc, 0x4, MA35_MUX(0x0, "GPH9"), MA35_MUX(0x4, "CLK_32KOUT"), MA35_MUX(0x6, "TAMPER1")), - MA35_PIN(118, PH12, 0xbc, 0x10, + MA35_PIN(124, PH12, 0xbc, 0x10, MA35_MUX(0x0, "GPH12"), MA35_MUX(0x2, "UART14_nCTS"), MA35_MUX(0x3, "UART13_RXD"), MA35_MUX(0x6, "LCM_DATA20")), - MA35_PIN(119, PH13, 0xbc, 0x14, + MA35_PIN(125, PH13, 0xbc, 0x14, MA35_MUX(0x0, "GPH13"), MA35_MUX(0x2, "UART14_nRTS"), MA35_MUX(0x3, "UART13_TXD"), MA35_MUX(0x6, "LCM_DATA21")), - MA35_PIN(120, PH14, 0xbc, 0x18, + MA35_PIN(126, PH14, 0xbc, 0x18, MA35_MUX(0x0, "GPH14"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x6, "LCM_DATA22")), - MA35_PIN(121, PH15, 0xbc, 0x1c, + MA35_PIN(127, PH15, 0xbc, 0x1c, MA35_MUX(0x0, "GPH15"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x6, "LCM_DATA23")), - MA35_PIN(122, PI0, 0xc0, 0x0, + MA35_PIN(128, PI0, 0xc0, 0x0, MA35_MUX(0x0, "GPI0"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART12_nCTS"), @@ -913,7 +966,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR0"), MA35_MUX(0xb, "TM0"), MA35_MUX(0xc, "ECAP1_IC0")), - MA35_PIN(123, PI1, 0xc0, 0x4, + MA35_PIN(129, PI1, 0xc0, 0x4, MA35_MUX(0x0, "GPI1"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART12_nRTS"), @@ -924,26 +977,28 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR1"), MA35_MUX(0xb, "TM0_EXT"), MA35_MUX(0xc, "ECAP1_IC1")), - MA35_PIN(124, PI2, 0xc0, 0x8, + MA35_PIN(130, PI2, 0xc0, 0x8, MA35_MUX(0x0, "GPI2"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI3_MOSI"), MA35_MUX(0x7, "SC0_DAT"), MA35_MUX(0x8, "EBI_ADR2"), MA35_MUX(0xb, "TM1"), MA35_MUX(0xc, "ECAP1_IC2")), - MA35_PIN(125, PI3, 0xc0, 0xc, + MA35_PIN(131, PI3, 0xc0, 0xc, MA35_MUX(0x0, "GPI3"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x7, "SC0_RST"), MA35_MUX(0x8, "EBI_ADR3"), MA35_MUX(0xb, "TM1_EXT")), - MA35_PIN(126, PI4, 0xc0, 0x10, + MA35_PIN(132, PI4, 0xc0, 0x10, MA35_MUX(0x0, "GPI4"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART14_nCTS"), @@ -953,7 +1008,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "I2S1_LRCK"), MA35_MUX(0x8, "EBI_ADR4"), MA35_MUX(0xd, "INT0")), - MA35_PIN(127, PI5, 0xc0, 0x14, + MA35_PIN(133, PI5, 0xc0, 0x14, MA35_MUX(0x0, "GPI5"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART14_nRTS"), @@ -962,65 +1017,67 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "I2S1_BCLK"), MA35_MUX(0x8, "EBI_ADR5"), MA35_MUX(0xd, "INT1")), - MA35_PIN(128, PI6, 0xc0, 0x18, + MA35_PIN(134, PI6, 0xc0, 0x18, MA35_MUX(0x0, "GPI6"), MA35_MUX(0x1, "EPWM0_BRAKE0"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "I2S1_DI"), MA35_MUX(0x8, "EBI_ADR6"), MA35_MUX(0xc, "QEI1_INDEX"), MA35_MUX(0xd, "INT2")), - MA35_PIN(129, PI7, 0xc0, 0x1c, + MA35_PIN(135, PI7, 0xc0, 0x1c, MA35_MUX(0x0, "GPI7"), MA35_MUX(0x1, "EPWM0_BRAKE1"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x6, "I2S1_DO"), MA35_MUX(0x8, "EBI_ADR7"), MA35_MUX(0xc, "ECAP0_IC0"), MA35_MUX(0xd, "INT3")), - MA35_PIN(130, PI8, 0xc4, 0x0, + MA35_PIN(136, PI8, 0xc4, 0x0, MA35_MUX(0x0, "GPI8"), MA35_MUX(0x2, "UART4_nCTS"), MA35_MUX(0x3, "UART3_RXD"), MA35_MUX(0x6, "LCM_DATA0"), MA35_MUX(0xc, "EBI_AD11")), - MA35_PIN(131, PI9, 0xc4, 0x4, + MA35_PIN(137, PI9, 0xc4, 0x4, MA35_MUX(0x0, "GPI9"), MA35_MUX(0x2, "UART4_nRTS"), MA35_MUX(0x3, "UART3_TXD"), MA35_MUX(0x6, "LCM_DATA1"), MA35_MUX(0xc, "EBI_AD12")), - MA35_PIN(132, PI10, 0xc4, 0x8, + MA35_PIN(138, PI10, 0xc4, 0x8, MA35_MUX(0x0, "GPI10"), MA35_MUX(0x2, "UART4_RXD"), MA35_MUX(0x6, "LCM_DATA2"), MA35_MUX(0xc, "EBI_AD13")), - MA35_PIN(133, PI11, 0xC4, 0xc, + MA35_PIN(139, PI11, 0xC4, 0xc, MA35_MUX(0x0, "GPI11"), MA35_MUX(0x2, "UART4_TXD"), MA35_MUX(0x6, "LCM_DATA3"), MA35_MUX(0xc, "EBI_AD14")), - MA35_PIN(134, PI12, 0xc4, 0x10, + MA35_PIN(140, PI12, 0xc4, 0x10, MA35_MUX(0x0, "GPI12"), MA35_MUX(0x2, "UART6_nCTS"), MA35_MUX(0x3, "UART5_RXD"), MA35_MUX(0x6, "LCM_DATA4")), - MA35_PIN(135, PI13, 0xc4, 0x14, + MA35_PIN(141, PI13, 0xc4, 0x14, MA35_MUX(0x0, "GPI13"), MA35_MUX(0x2, "UART6_nRTS"), MA35_MUX(0x3, "UART5_TXD"), MA35_MUX(0x6, "LCM_DATA5")), - MA35_PIN(136, PI14, 0xc4, 0x18, + MA35_PIN(142, PI14, 0xc4, 0x18, MA35_MUX(0x0, "GPI14"), MA35_MUX(0x2, "UART6_RXD"), MA35_MUX(0x6, "LCM_DATA6")), - MA35_PIN(137, PI15, 0xc4, 0x1c, + MA35_PIN(143, PI15, 0xc4, 0x1c, MA35_MUX(0x0, "GPI15"), MA35_MUX(0x2, "UART6_TXD"), MA35_MUX(0x6, "LCM_DATA7")), - MA35_PIN(138, PJ0, 0xc8, 0x0, + MA35_PIN(144, PJ0, 0xc8, 0x0, MA35_MUX(0x0, "GPJ0"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART8_nCTS"), @@ -1034,7 +1091,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR16"), MA35_MUX(0xb, "EBI_nCS0"), MA35_MUX(0xc, "EBI_AD7")), - MA35_PIN(139, PJ1, 0xc8, 0x4, + MA35_PIN(145, PJ1, 0xc8, 0x4, MA35_MUX(0x0, "GPJ1"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART8_nRTS"), @@ -1048,11 +1105,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR17"), MA35_MUX(0xb, "EBI_nCS1"), MA35_MUX(0xc, "EBI_AD8")), - MA35_PIN(140, PJ2, 0xc8, 0x8, + MA35_PIN(146, PJ2, 0xc8, 0x8, MA35_MUX(0x0, "GPJ2"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART8_RXD"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x5, "SPI2_MOSI"), MA35_MUX(0x6, "eMMC1_DAT6"), MA35_MUX(0x7, "I2S0_DI"), @@ -1061,11 +1119,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR18"), MA35_MUX(0xb, "EBI_nWRH"), MA35_MUX(0xc, "EBI_AD9")), - MA35_PIN(141, PJ3, 0xc8, 0xc, + MA35_PIN(147, PJ3, 0xc8, 0xc, MA35_MUX(0x0, "GPJ3"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART8_TXD"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x5, "SPI2_MISO"), MA35_MUX(0x6, "eMMC1_DAT7"), MA35_MUX(0x7, "I2S0_DO"), @@ -1074,39 +1133,43 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR19"), MA35_MUX(0xb, "EBI_nWRL"), MA35_MUX(0xc, "EBI_AD10")), - MA35_PIN(142, PJ4, 0xc8, 0x10, + MA35_PIN(148, PJ4, 0xc8, 0x10, MA35_MUX(0x0, "GPJ4"), MA35_MUX(0x4, "I2C3_SDA"), MA35_MUX(0x6, "SD1_WP")), - MA35_PIN(143, PJ5, 0xc8, 0x14, + MA35_PIN(149, PJ5, 0xc8, 0x14, MA35_MUX(0x0, "GPJ5"), MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x6, "SD1_nCD")), - MA35_PIN(144, PJ6, 0xc8, 0x18, + MA35_PIN(150, PJ6, 0xc8, 0x18, MA35_MUX(0x0, "GPJ6"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "SD1_CMD/eMMC1_CMD")), - MA35_PIN(145, PJ7, 0xc8, 0x1c, + MA35_PIN(151, PJ7, 0xc8, 0x1c, MA35_MUX(0x0, "GPJ7"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "SD1_CLK/eMMC1_CLK")), - MA35_PIN(146, PJ8, 0xcc, 0x0, + MA35_PIN(152, PJ8, 0xcc, 0x0, MA35_MUX(0x0, "GPJ8"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x6, "SD1_DAT0/eMMC1_DAT0")), - MA35_PIN(147, PJ9, 0xcc, 0x4, + MA35_PIN(153, PJ9, 0xcc, 0x4, MA35_MUX(0x0, "GPJ9"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x6, "SD1_DAT1/eMMC1_DAT1")), - MA35_PIN(148, PJ10, 0xcc, 0x8, + MA35_PIN(154, PJ10, 0xcc, 0x8, MA35_MUX(0x0, "GPJ10"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "SD1_DAT2/eMMC1_DAT2")), - MA35_PIN(149, PJ11, 0xcc, 0xc, + MA35_PIN(155, PJ11, 0xcc, 0xc, MA35_MUX(0x0, "GPJ11"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "SD1_DAT3/eMMC1_DAT3")), - MA35_PIN(150, PJ12, 0xcc, 0x10, + MA35_PIN(156, PJ12, 0xcc, 0x10, MA35_MUX(0x0, "GPJ12"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART2_nCTS"), @@ -1117,7 +1180,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR12"), MA35_MUX(0xb, "TM2"), MA35_MUX(0xc, "QEI0_INDEX")), - MA35_PIN(151, PJ13, 0xcc, 0x14, + MA35_PIN(157, PJ13, 0xcc, 0x14, MA35_MUX(0x0, "GPJ13"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART2_nRTS"), @@ -1127,27 +1190,29 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x7, "SC1_DAT"), MA35_MUX(0x8, "EBI_ADR13"), MA35_MUX(0xb, "TM2_EXT")), - MA35_PIN(152, PJ14, 0xcc, 0x18, + MA35_PIN(158, PJ14, 0xcc, 0x18, MA35_MUX(0x0, "GPJ14"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x7, "SC1_RST"), MA35_MUX(0x8, "EBI_ADR14"), MA35_MUX(0xb, "TM3")), - MA35_PIN(153, PJ15, 0xcc, 0x1c, + MA35_PIN(159, PJ15, 0xcc, 0x1c, MA35_MUX(0x0, "GPJ15"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x5, "SPI3_CLK"), MA35_MUX(0x6, "EADC0_ST"), MA35_MUX(0x7, "SC1_PWR"), MA35_MUX(0x8, "EBI_ADR15"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xd, "INT1")), - MA35_PIN(154, PK0, 0xd0, 0x0, + MA35_PIN(160, PK0, 0xd0, 0x0, MA35_MUX(0x0, "GPK0"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART16_nCTS"), @@ -1157,7 +1222,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR8"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xc, "ECAP0_IC1")), - MA35_PIN(155, PK1, 0xd0, 0x4, + MA35_PIN(161, PK1, 0xd0, 0x4, MA35_MUX(0x0, "GPK1"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART16_nRTS"), @@ -1167,25 +1232,27 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_ADR9"), MA35_MUX(0xb, "TM7_EXT"), MA35_MUX(0xc, "ECAP0_IC2")), - MA35_PIN(156, PK2, 0xd0, 0x8, + MA35_PIN(162, PK2, 0xd0, 0x8, MA35_MUX(0x0, "GPK2"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART16_RXD"), MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI3_I2SMCLK"), MA35_MUX(0x7, "SC0_PWR"), MA35_MUX(0x8, "EBI_ADR10"), MA35_MUX(0xc, "QEI0_A")), - MA35_PIN(157, PK3, 0xd0, 0xc, + MA35_PIN(163, PK3, 0xd0, 0xc, MA35_MUX(0x0, "GPK3"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART16_TXD"), MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI3_SS1"), MA35_MUX(0x7, "SC1_nCD"), MA35_MUX(0x8, "EBI_ADR11"), MA35_MUX(0xc, "QEI0_B")), - MA35_PIN(158, PK4, 0xd0, 0x10, + MA35_PIN(164, PK4, 0xd0, 0x10, MA35_MUX(0x0, "GPK4"), MA35_MUX(0x2, "UART12_nCTS"), MA35_MUX(0x3, "UART13_RXD"), @@ -1193,7 +1260,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "LCM_DEN"), MA35_MUX(0xc, "EBI_AD10"), MA35_MUX(0xd, "EBI_nWRL")), - MA35_PIN(159, PK5, 0xd0, 0x14, + MA35_PIN(165, PK5, 0xd0, 0x14, MA35_MUX(0x0, "GPK5"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART12_nRTS"), @@ -1205,28 +1272,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "EADC0_ST"), MA35_MUX(0xb, "TM8_EXT"), MA35_MUX(0xd, "INT1")), - MA35_PIN(160, PK6, 0xd0, 0x18, + MA35_PIN(166, PK6, 0xd0, 0x18, MA35_MUX(0x0, "GPK6"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART12_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_MOSI"), MA35_MUX(0x7, "I2S1_BCLK"), MA35_MUX(0x8, "SC0_RST"), MA35_MUX(0xb, "TM6"), MA35_MUX(0xd, "INT2")), - MA35_PIN(161, PK7, 0xd0, 0x1c, + MA35_PIN(167, PK7, 0xd0, 0x1c, MA35_MUX(0x0, "GPK7"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART12_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_MISO"), MA35_MUX(0x7, "I2S1_LRCK"), MA35_MUX(0x8, "SC0_PWR"), MA35_MUX(0x9, "CLKO"), MA35_MUX(0xb, "TM6_EXT"), MA35_MUX(0xd, "INT3")), - MA35_PIN(162, PK8, 0xd4, 0x0, + MA35_PIN(168, PK8, 0xd4, 0x0, MA35_MUX(0x0, "GPK8"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x4, "I2C3_SDA"), @@ -1237,25 +1306,27 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xa, "EBI_ADR15"), MA35_MUX(0xb, "TM8"), MA35_MUX(0xc, "QEI1_INDEX")), - MA35_PIN(163, PK9, 0xd4, 0x4, + MA35_PIN(169, PK9, 0xd4, 0x4, MA35_MUX(0x0, "GPK9"), MA35_MUX(0x4, "I2C3_SCL"), MA35_MUX(0x6, "CCAP0_SCLK"), MA35_MUX(0x8, "EBI_AD0"), MA35_MUX(0xa, "EBI_ADR0")), - MA35_PIN(164, PK10, 0xd4, 0x8, + MA35_PIN(170, PK10, 0xd4, 0x8, MA35_MUX(0x0, "GPK10"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL3_DM"), MA35_MUX(0x6, "CCAP0_PIXCLK"), MA35_MUX(0x8, "EBI_AD1"), MA35_MUX(0xa, "EBI_ADR1")), - MA35_PIN(165, PK11, 0xd4, 0xc, + MA35_PIN(171, PK11, 0xd4, 0xc, MA35_MUX(0x0, "GPK11"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL3_DP"), MA35_MUX(0x6, "CCAP0_HSYNC"), MA35_MUX(0x8, "EBI_AD2"), MA35_MUX(0xa, "EBI_ADR2")), - MA35_PIN(166, PK12, 0xd4, 0x10, + MA35_PIN(172, PK12, 0xd4, 0x10, MA35_MUX(0x0, "GPK12"), MA35_MUX(0x1, "EPWM2_CH0"), MA35_MUX(0x2, "UART1_nCTS"), @@ -1266,7 +1337,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "SC0_CLK"), MA35_MUX(0xb, "TM10"), MA35_MUX(0xd, "INT2")), - MA35_PIN(167, PK13, 0xd4, 0x14, + MA35_PIN(173, PK13, 0xd4, 0x14, MA35_MUX(0x0, "GPK13"), MA35_MUX(0x1, "EPWM2_CH1"), MA35_MUX(0x2, "UART1_nRTS"), @@ -1276,28 +1347,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x6, "SPI1_CLK"), MA35_MUX(0x8, "SC0_DAT"), MA35_MUX(0xb, "TM10_EXT")), - MA35_PIN(168, PK14, 0xd4, 0x18, + MA35_PIN(174, PK14, 0xd4, 0x18, MA35_MUX(0x0, "GPK14"), MA35_MUX(0x1, "EPWM2_CH2"), MA35_MUX(0x2, "UART1_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "I2S0_DI"), MA35_MUX(0x6, "SPI1_MOSI"), MA35_MUX(0x8, "SC0_RST"), MA35_MUX(0xa, "I2C5_SDA"), MA35_MUX(0xb, "TM11"), MA35_MUX(0xd, "INT3")), - MA35_PIN(169, PK15, 0xd4, 0x1c, + MA35_PIN(175, PK15, 0xd4, 0x1c, MA35_MUX(0x0, "GPK15"), MA35_MUX(0x1, "EPWM2_CH3"), MA35_MUX(0x2, "UART1_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "I2S0_DO"), MA35_MUX(0x6, "SPI1_MISO"), MA35_MUX(0x8, "SC0_PWR"), MA35_MUX(0xa, "I2C5_SCL"), MA35_MUX(0xb, "TM11_EXT")), - MA35_PIN(170, PL0, 0xd8, 0x0, + MA35_PIN(176, PL0, 0xd8, 0x0, MA35_MUX(0x0, "GPL0"), MA35_MUX(0x1, "EPWM1_CH0"), MA35_MUX(0x2, "UART11_nCTS"), @@ -1310,7 +1383,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_CLK"), MA35_MUX(0xb, "TM5"), MA35_MUX(0xc, "QEI1_A")), - MA35_PIN(171, PL1, 0xd8, 0x4, + MA35_PIN(177, PL1, 0xd8, 0x4, MA35_MUX(0x0, "GPL1"), MA35_MUX(0x1, "EPWM1_CH1"), MA35_MUX(0x2, "UART11_nRTS"), @@ -1323,11 +1396,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_DAT"), MA35_MUX(0xb, "TM5_EXT"), MA35_MUX(0xc, "QEI1_B")), - MA35_PIN(172, PL2, 0xd8, 0x8, + MA35_PIN(178, PL2, 0xd8, 0x8, MA35_MUX(0x0, "GPL2"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x2, "UART11_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI2_SS0"), MA35_MUX(0x6, "QSPI1_SS1"), MA35_MUX(0x7, "I2S0_DI"), @@ -1335,11 +1409,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_RST"), MA35_MUX(0xb, "TM7"), MA35_MUX(0xc, "QEI1_INDEX")), - MA35_PIN(173, PL3, 0xd8, 0xc, + MA35_PIN(179, PL3, 0xd8, 0xc, MA35_MUX(0x0, "GPL3"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x2, "UART11_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI2_CLK"), MA35_MUX(0x6, "QSPI1_CLK"), MA35_MUX(0x7, "I2S0_DO"), @@ -1347,7 +1422,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_PWR"), MA35_MUX(0xb, "TM7_EXT"), MA35_MUX(0xc, "ECAP0_IC0")), - MA35_PIN(174, PL4, 0xd8, 0x10, + MA35_PIN(180, PL4, 0xd8, 0x10, MA35_MUX(0x0, "GPL4"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART2_nCTS"), @@ -1360,7 +1435,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC1_nCD"), MA35_MUX(0xb, "TM9"), MA35_MUX(0xc, "ECAP0_IC1")), - MA35_PIN(175, PL5, 0xd8, 0x14, + MA35_PIN(181, PL5, 0xd8, 0x14, MA35_MUX(0x0, "GPL5"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART2_nRTS"), @@ -1373,28 +1448,30 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_nCD"), MA35_MUX(0xb, "TM9_EXT"), MA35_MUX(0xc, "ECAP0_IC2")), - MA35_PIN(176, PL6, 0xd8, 0x18, + MA35_PIN(182, PL6, 0xd8, 0x18, MA35_MUX(0x0, "GPL6"), MA35_MUX(0x1, "EPWM0_CH0"), MA35_MUX(0x2, "UART2_RXD"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL5_DM"), MA35_MUX(0x6, "QSPI1_MOSI1"), MA35_MUX(0x7, "TRACE_CLK"), MA35_MUX(0x8, "EBI_AD5"), MA35_MUX(0xb, "TM3"), MA35_MUX(0xc, "ECAP1_IC0"), MA35_MUX(0xd, "INT0")), - MA35_PIN(177, PL7, 0xd8, 0x1c, + MA35_PIN(183, PL7, 0xd8, 0x1c, MA35_MUX(0x0, "GPL7"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART2_TXD"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL5_DP"), MA35_MUX(0x6, "QSPI1_MISO1"), MA35_MUX(0x8, "EBI_AD6"), MA35_MUX(0xb, "TM3_EXT"), MA35_MUX(0xc, "ECAP1_IC1"), MA35_MUX(0xd, "INT1")), - MA35_PIN(178, PL8, 0xdc, 0x0, + MA35_PIN(184, PL8, 0xdc, 0x0, MA35_MUX(0x0, "GPL8"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART14_nCTS"), @@ -1408,7 +1485,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4"), MA35_MUX(0xc, "ECAP1_IC2"), MA35_MUX(0xd, "INT2")), - MA35_PIN(179, PL9, 0xdc, 0x4, + MA35_PIN(185, PL9, 0xdc, 0x4, MA35_MUX(0x0, "GPL9"), MA35_MUX(0x1, "EPWM0_CH3"), MA35_MUX(0x2, "UART14_nRTS"), @@ -1422,11 +1499,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM4_EXT"), MA35_MUX(0xc, "QEI0_A"), MA35_MUX(0xd, "INT3")), - MA35_PIN(180, PL10, 0xdc, 0x8, + MA35_PIN(186, PL10, 0xdc, 0x8, MA35_MUX(0x0, "GPL10"), MA35_MUX(0x1, "EPWM0_CH4"), MA35_MUX(0x2, "UART14_RXD"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL2_DM"), MA35_MUX(0x5, "SPI3_MOSI"), MA35_MUX(0x6, "EPWM0_CH5"), MA35_MUX(0x7, "I2S1_DI"), @@ -1434,11 +1512,12 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_RST"), MA35_MUX(0xb, "EBI_nWRH"), MA35_MUX(0xc, "QEI0_B")), - MA35_PIN(181, PL11, 0xdc, 0xc, + MA35_PIN(187, PL11, 0xdc, 0xc, MA35_MUX(0x0, "GPL11"), MA35_MUX(0x1, "EPWM0_CH5"), MA35_MUX(0x2, "UART14_TXD"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL2_DP"), MA35_MUX(0x5, "SPI3_MISO"), MA35_MUX(0x6, "EPWM1_CH5"), MA35_MUX(0x7, "I2S1_DO"), @@ -1446,7 +1525,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x9, "SC0_PWR"), MA35_MUX(0xb, "EBI_nWRL"), MA35_MUX(0xc, "QEI0_INDEX")), - MA35_PIN(182, PL12, 0xdc, 0x10, + MA35_PIN(188, PL12, 0xdc, 0x10, MA35_MUX(0x0, "GPL12"), MA35_MUX(0x1, "EPWM0_SYNC_IN"), MA35_MUX(0x2, "UART7_nCTS"), @@ -1463,7 +1542,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "EBI_AD11"), MA35_MUX(0xe, "RGMII0_PPS"), MA35_MUX(0xf, "RMII0_PPS")), - MA35_PIN(183, PL13, 0xdc, 0x14, + MA35_PIN(189, PL13, 0xdc, 0x14, MA35_MUX(0x0, "GPL13"), MA35_MUX(0x1, "EPWM0_SYNC_OUT"), MA35_MUX(0x2, "UART7_nRTS"), @@ -1480,7 +1559,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xd, "EBI_AD12"), MA35_MUX(0xe, "RGMII1_PPS"), MA35_MUX(0xf, "RMII1_PPS")), - MA35_PIN(184, PL14, 0xdc, 0x18, + MA35_PIN(190, PL14, 0xdc, 0x18, MA35_MUX(0x0, "GPL14"), MA35_MUX(0x1, "EPWM0_CH2"), MA35_MUX(0x2, "UART7_RXD"), @@ -1492,7 +1571,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM2"), MA35_MUX(0xc, "INT0"), MA35_MUX(0xd, "EBI_AD13")), - MA35_PIN(185, PL15, 0xdc, 0x1c, + MA35_PIN(191, PL15, 0xdc, 0x1c, MA35_MUX(0x0, "GPL15"), MA35_MUX(0x1, "EPWM0_CH1"), MA35_MUX(0x2, "UART7_TXD"), @@ -1505,86 +1584,92 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0xb, "TM2_EXT"), MA35_MUX(0xc, "INT2"), MA35_MUX(0xd, "EBI_AD14")), - MA35_PIN(186, PM0, 0xe0, 0x0, + MA35_PIN(192, PM0, 0xe0, 0x0, MA35_MUX(0x0, "GPM0"), MA35_MUX(0x4, "I2C4_SDA"), MA35_MUX(0x6, "CCAP0_VSYNC"), MA35_MUX(0x8, "EBI_AD3"), MA35_MUX(0xa, "EBI_ADR3")), - MA35_PIN(187, PM1, 0xe0, 0x4, + MA35_PIN(193, PM1, 0xe0, 0x4, MA35_MUX(0x0, "GPM1"), MA35_MUX(0x4, "I2C4_SCL"), MA35_MUX(0x5, "SPI3_I2SMCLK"), MA35_MUX(0x6, "CCAP0_SFIELD"), MA35_MUX(0x8, "EBI_AD4"), MA35_MUX(0xa, "EBI_ADR4")), - MA35_PIN(188, PM2, 0xe0, 0x8, + MA35_PIN(194, PM2, 0xe0, 0x8, MA35_MUX(0x0, "GPM2"), MA35_MUX(0x3, "CAN3_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "CCAP0_DATA0"), MA35_MUX(0x8, "EBI_AD5"), MA35_MUX(0xa, "EBI_ADR5")), - MA35_PIN(189, PM3, 0xe0, 0xc, + MA35_PIN(195, PM3, 0xe0, 0xc, MA35_MUX(0x0, "GPM3"), MA35_MUX(0x3, "CAN3_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "CCAP0_DATA1"), MA35_MUX(0x8, "EBI_AD6"), MA35_MUX(0xa, "EBI_ADR6")), - MA35_PIN(190, PM4, 0xe0, 0x10, + MA35_PIN(196, PM4, 0xe0, 0x10, MA35_MUX(0x0, "GPM4"), MA35_MUX(0x4, "I2C5_SDA"), MA35_MUX(0x6, "CCAP0_DATA2"), MA35_MUX(0x8, "EBI_AD7"), MA35_MUX(0xa, "EBI_ADR7")), - MA35_PIN(191, PM5, 0xe0, 0x14, + MA35_PIN(197, PM5, 0xe0, 0x14, MA35_MUX(0x0, "GPM5"), MA35_MUX(0x4, "I2C5_SCL"), MA35_MUX(0x6, "CCAP0_DATA3"), MA35_MUX(0x8, "EBI_AD8"), MA35_MUX(0xa, "EBI_ADR8")), - MA35_PIN(192, PM6, 0xe0, 0x18, + MA35_PIN(198, PM6, 0xe0, 0x18, MA35_MUX(0x0, "GPM6"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "CCAP0_DATA4"), MA35_MUX(0x8, "EBI_AD9"), MA35_MUX(0xa, "EBI_ADR9")), - MA35_PIN(193, PM7, 0xe0, 0x1c, + MA35_PIN(199, PM7, 0xe0, 0x1c, MA35_MUX(0x0, "GPM7"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "CCAP0_DATA5"), MA35_MUX(0x8, "EBI_AD10"), MA35_MUX(0xa, "EBI_ADR10")), - MA35_PIN(194, PM8, 0xe4, 0x0, + MA35_PIN(200, PM8, 0xe4, 0x0, MA35_MUX(0x0, "GPM8"), MA35_MUX(0x4, "I2C0_SDA"), MA35_MUX(0x6, "CCAP0_DATA6"), MA35_MUX(0x8, "EBI_AD11"), MA35_MUX(0xa, "EBI_ADR11")), - MA35_PIN(195, PM9, 0xe4, 0x4, + MA35_PIN(201, PM9, 0xe4, 0x4, MA35_MUX(0x0, "GPM9"), MA35_MUX(0x4, "I2C0_SCL"), MA35_MUX(0x6, "CCAP0_DATA7"), MA35_MUX(0x8, "EBI_AD12"), MA35_MUX(0xa, "EBI_ADR12")), - MA35_PIN(196, PM10, 0xe4, 0x8, + MA35_PIN(202, PM10, 0xe4, 0x8, MA35_MUX(0x0, "GPM10"), MA35_MUX(0x1, "EPWM1_CH2"), MA35_MUX(0x3, "CAN2_RXD"), + MA35_MUX(0x4, "USBHL4_DM"), MA35_MUX(0x5, "SPI3_SS0"), MA35_MUX(0x6, "CCAP0_DATA8"), MA35_MUX(0x7, "SPI2_I2SMCLK"), MA35_MUX(0x8, "EBI_AD13"), MA35_MUX(0xa, "EBI_ADR13")), - MA35_PIN(197, PM11, 0xe4, 0xc, + MA35_PIN(203, PM11, 0xe4, 0xc, MA35_MUX(0x0, "GPM11"), MA35_MUX(0x1, "EPWM1_CH3"), MA35_MUX(0x3, "CAN2_TXD"), + MA35_MUX(0x4, "USBHL4_DP"), MA35_MUX(0x5, "SPI3_SS1"), MA35_MUX(0x6, "CCAP0_DATA9"), MA35_MUX(0x7, "SPI2_SS1"), MA35_MUX(0x8, "EBI_AD14"), MA35_MUX(0xa, "EBI_ADR14")), - MA35_PIN(198, PM12, 0xe4, 0x10, + MA35_PIN(204, PM12, 0xe4, 0x10, MA35_MUX(0x0, "GPM12"), MA35_MUX(0x1, "EPWM1_CH4"), MA35_MUX(0x2, "UART10_nCTS"), @@ -1595,7 +1680,7 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_AD8"), MA35_MUX(0x9, "I2S1_MCLK"), MA35_MUX(0xb, "TM8")), - MA35_PIN(199, PM13, 0xe4, 0x14, + MA35_PIN(205, PM13, 0xe4, 0x14, MA35_MUX(0x0, "GPM13"), MA35_MUX(0x1, "EPWM1_CH5"), MA35_MUX(0x2, "UART10_nRTS"), @@ -1605,99 +1690,66 @@ static const struct pinctrl_pin_desc ma35d1_pins[] = { MA35_MUX(0x8, "EBI_AD9"), MA35_MUX(0x9, "ECAP1_IC0"), MA35_MUX(0xb, "TM8_EXT")), - MA35_PIN(200, PM14, 0xe4, 0x18, + MA35_PIN(206, PM14, 0xe4, 0x18, MA35_MUX(0x0, "GPM14"), MA35_MUX(0x1, "EPWM1_BRAKE0"), MA35_MUX(0x2, "UART10_RXD"), MA35_MUX(0x3, "TRACE_DATA2"), MA35_MUX(0x4, "CAN2_RXD"), + MA35_MUX(0x5, "USBHL3_DM"), MA35_MUX(0x6, "I2C3_SDA"), MA35_MUX(0x8, "EBI_AD10"), MA35_MUX(0x9, "ECAP1_IC1"), MA35_MUX(0xb, "TM10"), MA35_MUX(0xd, "INT1")), - MA35_PIN(201, PM15, 0xe4, 0x1c, + MA35_PIN(207, PM15, 0xe4, 0x1c, MA35_MUX(0x0, "GPM15"), MA35_MUX(0x1, "EPWM1_BRAKE1"), MA35_MUX(0x2, "UART10_TXD"), MA35_MUX(0x3, "TRACE_DATA3"), MA35_MUX(0x4, "CAN2_TXD"), + MA35_MUX(0x5, "USBHL3_DP"), MA35_MUX(0x6, "I2C3_SCL"), MA35_MUX(0x8, "EBI_AD11"), MA35_MUX(0x9, "ECAP1_IC2"), MA35_MUX(0xb, "TM10_EXT"), MA35_MUX(0xd, "INT2")), - MA35_PIN(202, PN0, 0xe8, 0x0, + MA35_PIN(208, PN0, 0xe8, 0x0, MA35_MUX(0x0, "GPN0"), MA35_MUX(0x4, "I2C2_SDA"), MA35_MUX(0x6, "CCAP1_DATA0")), - MA35_PIN(203, PN1, 0xe8, 0x4, + MA35_PIN(209, PN1, 0xe8, 0x4, MA35_MUX(0x0, "GPN1"), MA35_MUX(0x4, "I2C2_SCL"), MA35_MUX(0x6, "CCAP1_DATA1")), - MA35_PIN(204, PN2, 0xe8, 0x8, + MA35_PIN(210, PN2, 0xe8, 0x8, MA35_MUX(0x0, "GPN2"), MA35_MUX(0x3, "CAN0_RXD"), + MA35_MUX(0x4, "USBHL0_DM"), MA35_MUX(0x6, "CCAP1_DATA2")), - MA35_PIN(205, PN3, 0xe8, 0xc, + MA35_PIN(211, PN3, 0xe8, 0xc, MA35_MUX(0x0, "GPN3"), MA35_MUX(0x3, "CAN0_TXD"), + MA35_MUX(0x4, "USBHL0_DP"), MA35_MUX(0x6, "CCAP1_DATA3")), - MA35_PIN(206, PN4, 0xe8, 0x10, + MA35_PIN(212, PN4, 0xe8, 0x10, MA35_MUX(0x0, "GPN4"), MA35_MUX(0x4, "I2C1_SDA"), MA35_MUX(0x6, "CCAP1_DATA4")), - MA35_PIN(207, PN5, 0xe8, 0x14, + MA35_PIN(213, PN5, 0xe8, 0x14, MA35_MUX(0x0, "GPN5"), MA35_MUX(0x4, "I2C1_SCL"), MA35_MUX(0x6, "CCAP1_DATA5")), - MA35_PIN(208, PN6, 0xe8, 0x18, + MA35_PIN(214, PN6, 0xe8, 0x18, MA35_MUX(0x0, "GPN6"), MA35_MUX(0x3, "CAN1_RXD"), + MA35_MUX(0x4, "USBHL1_DM"), MA35_MUX(0x6, "CCAP1_DATA6")), - MA35_PIN(209, PN7, 0xe8, 0x1c, + MA35_PIN(215, PN7, 0xe8, 0x1c, MA35_MUX(0x0, "GPN7"), MA35_MUX(0x3, "CAN1_TXD"), + MA35_MUX(0x4, "USBHL1_DP"), MA35_MUX(0x6, "CCAP1_DATA7")), - MA35_PIN(210, PN10, 0xec, 0x8, - MA35_MUX(0x0, "GPN10"), - MA35_MUX(0x3, "CAN2_RXD"), - MA35_MUX(0x6, "CCAP1_SCLK")), - MA35_PIN(211, PN11, 0xec, 0xc, - MA35_MUX(0x0, "GPN11"), - MA35_MUX(0x3, "CAN2_TXD"), - MA35_MUX(0x6, "CCAP1_PIXCLK")), - MA35_PIN(212, PN12, 0xec, 0x10, - MA35_MUX(0x0, "GPN12"), - MA35_MUX(0x2, "UART6_nCTS"), - MA35_MUX(0x3, "UART12_RXD"), - MA35_MUX(0x4, "I2C5_SDA"), - MA35_MUX(0x6, "CCAP1_HSYNC")), - MA35_PIN(213, PN13, 0xec, 0x14, - MA35_MUX(0x0, "GPN13"), - MA35_MUX(0x2, "UART6_nRTS"), - MA35_MUX(0x3, "UART12_TXD"), - MA35_MUX(0x4, "I2C5_SCL"), - MA35_MUX(0x6, "CCAP1_VSYNC")), - MA35_PIN(214, PN14, 0xec, 0x18, - MA35_MUX(0x0, "GPN14"), - MA35_MUX(0x2, "UART6_RXD"), - MA35_MUX(0x3, "CAN3_RXD"), - MA35_MUX(0x5, "SPI1_SS1"), - MA35_MUX(0x6, "CCAP1_SFIELD"), - MA35_MUX(0x7, "SPI1_I2SMCLK")), - MA35_PIN(215, PN15, 0xec, 0x1c, - MA35_MUX(0x0, "GPN15"), - MA35_MUX(0x1, "EPWM2_CH4"), - MA35_MUX(0x2, "UART6_TXD"), - MA35_MUX(0x3, "CAN3_TXD"), - MA35_MUX(0x5, "I2S0_MCLK"), - MA35_MUX(0x6, "SPI1_SS1"), - MA35_MUX(0x7, "SPI1_I2SMCLK"), - MA35_MUX(0x8, "SC0_nCD"), - MA35_MUX(0x9, "EADC0_ST"), - MA35_MUX(0xa, "CLKO"), - MA35_MUX(0xb, "TM6")), MA35_PIN(216, PN8, 0xec, 0x0, MA35_MUX(0x0, "GPN8"), MA35_MUX(0x1, "EPWM2_CH4"), From 2739d234d895283b518d316c5e1df98994e56982 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 15:34:52 +0100 Subject: [PATCH 0855/3297] pinctrl: cs42l43: Fix leaked pm reference on error path [ Upstream commit 1cb73b83ab6dec8159d0280345a46fbb282c378f ] Returning directly if the regmap_update_bits() fails causes a pm runtime reference to be leaked, let things run to the end of the function instead. Fixes: e52c741907fb ("pinctrl: cirrus: cs42l43: use new GPIO line value setter callbacks") Signed-off-by: Charles Keepax Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c index 227c37c360e19..3cc1835206077 100644 --- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -499,12 +499,10 @@ static int cs42l43_gpio_set(struct gpio_chip *chip, unsigned int offset, ret = regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL1, BIT(shift), value << shift); - if (ret) - return ret; pm_runtime_put(priv->dev); - return 0; + return ret; } static int cs42l43_gpio_direction_out(struct gpio_chip *chip, From eb9280ea8dbd25e1e3be233da1729705dd042d76 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 15:34:53 +0100 Subject: [PATCH 0856/3297] pinctrl: cs42l43: Fix polarity on debounce [ Upstream commit 9da52ee80aee3ab3c69208bd1cfbb4be01371214 ] The debounce bit sets a bypass on the debounce rather than enabling it, as such the current polarity of the debounce is set incorrectly. Invert the polarity to correct this. Fixes: d5282a539297 ("pinctrl: cs42l43: Add support for the cs42l43") Signed-off-by: Charles Keepax Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/cirrus/pinctrl-cs42l43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c index 3cc1835206077..305233fc19876 100644 --- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -343,7 +343,7 @@ static int cs42l43_pin_set_db(struct cs42l43_pin *priv, unsigned int pin, return regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL2, CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin, - !!us << pin); + !us << pin); } static int cs42l43_pin_config_get(struct pinctrl_dev *pctldev, From 24639c4dc466e879640d8a0f51e1c1a265bff633 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 19 May 2026 09:39:37 +0000 Subject: [PATCH 0857/3297] init/initramfs_test: wait_for_initramfs() before running [ Upstream commit ec3f4e0443a61e68092ac07111f16dd4ca89ddb4 ] initramfs_test_extract() and friends call unpack_to_rootfs() from a kunit kthread while do_populate_rootfs() may still be running asynchronously from rootfs_initcall. unpack_to_rootfs() keeps its parser state in module-static variables (victim, byte_count, state, this_header, header_buf, name_buf, ...), so the two writers corrupt each other. On arm64 v7.0-rc5+ this oopses early in boot: Unable to handle kernel paging request at virtual address ffff80018f9f0ffc pc : do_reset+0x3c/0x98 Call trace: do_reset initramfs_test_extract kunit_try_run_case Initramfs unpacking failed: junk within compressed archive do_reset() faults because 'victim' was overwritten by the boot-time unpacker; the boot unpacker meanwhile logs the bogus "junk within compressed archive" on the real initrd because the test wrecked its state machine. Add a .suite_init callback that calls wait_for_initramfs() so the async unpack is quiescent before the first case runs. suite_init runs once per suite rather than before every individual test case. Fixes: 83c0b27266ec ("initramfs_test: kunit tests for initramfs unpacking") Signed-off-by: Jia He Link: https://patch.msgid.link/20260519093937.1064628-1-justin.he@arm.com Reviewed-by: David Disseldorp Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- init/initramfs_test.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/init/initramfs_test.c b/init/initramfs_test.c index 5d2db455e60c5..3f0241ca90759 100644 --- a/init/initramfs_test.c +++ b/init/initramfs_test.c @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include #include "initramfs_internal.h" @@ -462,8 +464,21 @@ static struct kunit_case __refdata initramfs_test_cases[] = { {}, }; -static struct kunit_suite initramfs_test_suite = { +static int __init initramfs_test_init(struct kunit_suite *suite) +{ + /* + * unpack_to_rootfs() uses module-static state (victim, byte_count, + * state, ...). The boot-time async do_populate_rootfs() may still be + * running, so wait for it to finish before we call unpack_to_rootfs() + * from the test thread, otherwise the two writers race and crash. + */ + wait_for_initramfs(); + return 0; +} + +static struct kunit_suite __refdata initramfs_test_suite = { .name = "initramfs", + .suite_init = initramfs_test_init, .test_cases = initramfs_test_cases, }; kunit_test_init_section_suites(&initramfs_test_suite); From ba3209704b3cd46961e4e081af5c52a780785648 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Tue, 26 May 2026 17:22:22 +0800 Subject: [PATCH 0858/3297] nvmet-tcp: fix page fragment cache leak in error path [ Upstream commit 4dae393956093c807212918fd91a8fc70df15338 ] In nvmet_tcp_alloc_queue(), when a connection is closed during the allocation process (e.g., nvmet_tcp_set_queue_sock() returns -ENOTCONN), the error handling jumps to out_destroy_sq and then to out_ida_remove without draining the page fragment cache. Although nvmet_tcp_free_cmd() is called in some error paths to release individual page fragments, the underlying page cache reference held by queue->pf_cache is never released. The first allocation using pf_cache is the call to nvmet_tcp_alloc_cmd() for queue->connect, which happens after ida_alloc() returns successfully. This results in a page leak each time a connection fails during allocation, which could lead to memory exhaustion over time if connections are repeatedly opened and closed. Fix this by calling page_frag_cache_drain() before freeing the queue structure in the out_ida_remove label. Fixes: 872d26a391da ("nvmet-tcp: add NVMe over TCP target driver") Reviewed-by: Christoph Hellwig Signed-off-by: Geliang Tang Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/target/tcp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 0bc66644a3e66..f5cb2dd0def33 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -2023,6 +2023,12 @@ static void nvmet_tcp_alloc_queue(struct nvmet_tcp_port *port, nvmet_tcp_free_cmd(&queue->connect); out_ida_remove: ida_free(&nvmet_tcp_queue_ida, queue->idx); + /* + * Drain the page fragment cache if any allocations were done. + * The first allocation using pf_cache is nvmet_tcp_alloc_cmd() + * for queue->connect after ida_alloc(). + */ + page_frag_cache_drain(&queue->pf_cache); out_sock: fput(queue->sock->file); out_free_queue: From cba2ee57fd302727aea7d41e9d9cd0969f5df0fb Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Tue, 26 May 2026 17:28:05 +0800 Subject: [PATCH 0859/3297] nvmet-tcp: check return value of nvmet_tcp_set_queue_sock [ Upstream commit 7ef789703e2b91775dcb36b2efa46325be31a2a0 ] The return value of nvmet_tcp_set_queue_sock() is currently ignored in nvmet_tcp_tls_handshake_done(). If it fails (e.g., due to the socket not being in TCP_ESTABLISHED state), the socket callbacks will not be properly set, leading to queue and socket leakage. Fix this by capturing the return value and calling nvmet_tcp_schedule_release_queue() on failure to ensure proper cleanup. Fixes: 675b453e0241 ("nvmet-tcp: enable TLS handshake upcall") Reviewed-by: Hannes Reinecke Reviewed-by: Chaitanya Kulkarni Signed-off-by: Geliang Tang Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/target/tcp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index f5cb2dd0def33..0b8cfa3dbab12 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1868,10 +1868,11 @@ static void nvmet_tcp_tls_handshake_done(void *data, int status, if (!status) status = nvmet_tcp_tls_key_lookup(queue, peerid); + if (!status) + status = nvmet_tcp_set_queue_sock(queue); + if (status) nvmet_tcp_schedule_release_queue(queue); - else - nvmet_tcp_set_queue_sock(queue); kref_put(&queue->kref, nvmet_tcp_release_queue); } From 140d6fff4ed266592492a23444043842b4af7a62 Mon Sep 17 00:00:00 2001 From: Nilay Shroff Date: Wed, 27 May 2026 11:50:00 +0530 Subject: [PATCH 0860/3297] nvme-multipath: fix flex array size in struct nvme_ns_head [ Upstream commit 001e57554de81aa79c25c18fd53911d8a415c304 ] struct nvme_ns_head contains a flexible array member, current_path[], which is indexed using the NUMA node ID: head->current_path[numa_node_id()] The structure is currently allocated as: size = sizeof(struct nvme_ns_head) + (num_possible_nodes() * sizeof(struct nvme_ns *)); head = kzalloc(size, GFP_KERNEL); This allocation assumes that NUMA node IDs are sequential and densely packed from 0 .. num_possible_nodes() - 1. While this assumption holds on many systems, it is not always true on some architectures such as powerpc. On some powerpc systems, NUMA node IDs can be sparse. For example: NUMA: NUMA node(s): 6 NUMA node0 CPU(s): 80-159 NUMA node8 CPU(s): 0-79 NUMA node252 CPU(s): NUMA node253 CPU(s): NUMA node254 CPU(s): NUMA node255 CPU(s): That is, the possible/online NUMA node IDs are: 0, 8, 252, 253, 254, 255 In this case: num_possible_nodes() = 6 So memory is allocated for only 6 entries in current_path[]. However, the array is later indexed using the actual NUMA node ID. As a result, accesses such as: head->current_path[8] or head->current_path[252] goes out of bounds, leading to the following KASAN splat: ================================================================== BUG: KASAN: slab-out-of-bounds in nvme_mpath_revalidate_paths+0x22c/0x290 [nvme_core] Write of size 8 at addr c00020003bda35b8 by task kworker/u641:2/1997 CPU: 1 UID: 0 PID: 1997 Comm: kworker/u641:2 Not tainted 7.1.0-rc5-dirty #14 PREEMPT(lazy) Hardware name: 8335-GTH POWER9 0x4e1202 opal:skiboot-v6.5.3-35-g1851b2a06 PowerNV Workqueue: async async_run_entry_fn Call Trace: [c000200037fa7510] [c0000000021c23d4] dump_stack_lvl+0x88/0xdc (unreliable) [c000200037fa7540] [c0000000009fda90] print_report+0x22c/0x67c [c000200037fa7630] [c0000000009fd508] kasan_report+0x108/0x220 [c000200037fa7740] [c0000000009fff48] __asan_store8+0xe8/0x120 [c000200037fa7760] [c008000018e76474] nvme_mpath_revalidate_paths+0x22c/0x290 [nvme_core] [c000200037fa7800] [c008000018e6556c] nvme_update_ns_info+0x4a4/0x5e0 [nvme_core] [c000200037fa7a50] [c008000018e66270] nvme_alloc_ns+0x6d8/0x1a70 [nvme_core] [c000200037fa7c20] [c008000018e679fc] nvme_scan_ns+0x3f4/0x630 [nvme_core] [c000200037fa7d10] [c00000000031f22c] async_run_entry_fn+0x9c/0x3a0 [c000200037fa7db0] [c0000000002fa544] process_one_work+0x414/0xa10 [c000200037fa7ec0] [c0000000002fbf00] worker_thread+0x320/0x640 [c000200037fa7f80] [c00000000030d0f8] kthread+0x278/0x290 [c000200037fa7fe0] [c00000000000ded8] start_kernel_thread+0x14/0x18 Allocated by task 1997 on cpu 1 at 35.928317s: The buggy address belongs to the object at c00020003bda3000 which belongs to the cache kmalloc-rnd-15-2k of size 2048 The buggy address is located 16 bytes to the right of allocated 1448-byte region [c00020003bda3000, c00020003bda35a8) The buggy address belongs to the physical page: Memory state around the buggy address: c00020003bda3480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c00020003bda3500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >c00020003bda3580: 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc ^ c00020003bda3600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc c00020003bda3680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Fix this by allocating the flexible array using nr_node_ids instead of num_possible_nodes(). Since nr_node_ids represents the maximum possible NUMA node IDs, indexing current_path[] using numa_node_id() becomes safe even on systems with sparse node IDs. Fixes: f333444708f8 ("nvme: take node locality into account when selecting a path") Tested-by: Mukesh Kumar Chaurasiya (IBM) Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Reviewed-by: Hannes Reinecke Reviewed-by: John Garry Reviewed-by: Christoph Hellwig Signed-off-by: Nilay Shroff Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 9a0071bd791c8..7b6b42b20641b 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3890,7 +3890,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, int ret = -ENOMEM; #ifdef CONFIG_NVME_MULTIPATH - size += num_possible_nodes() * sizeof(struct nvme_ns *); + size += nr_node_ids * sizeof(struct nvme_ns *); #endif head = kzalloc(size, GFP_KERNEL); From 3e8aed5edaeeb237f97255d711aac6b5843fc059 Mon Sep 17 00:00:00 2001 From: Mateusz Nowicki Date: Sat, 23 May 2026 08:28:16 +0000 Subject: [PATCH 0861/3297] nvme-pci: fix out-of-bounds access in nvme_setup_descriptor_pools [ Upstream commit a192b8cfa447e1b3701a13434a31c392b2e7ed29 ] nvme_setup_descriptor_pools() indexes dev->descriptor_pools[] using the numa_node forwarded from hctx->numa_node by its single caller, nvme_init_hctx_common(). On a non-NUMA kernel hctx->numa_node is NUMA_NO_NODE (-1). Because the parameter was declared 'unsigned', the value becomes UINT_MAX and the index walks off the array (sized to nr_node_ids), faulting during nvme_alloc_ns() and leaving the namespace without a /dev node. Reproduces on any NVMe controller probed by a CONFIG_NUMA=n kernel: BUG: unable to handle page fault for address: ffff889101603d38 RIP: 0010:nvme_init_hctx_common+0x5a/0x190 [nvme] Call Trace: nvme_init_hctx+0x10/0x20 [nvme] nvme_alloc_ns+0x9e/0xa10 [nvme_core] nvme_scan_ns+0x301/0x3b0 [nvme_core] nvme_scan_ns_async+0x23/0x30 [nvme_core] Switch the parameter to int and fall back to node 0 when it is NUMA_NO_NODE; node 0 is always present. Fixes: d977506f8863 ("nvme-pci: make PRP list DMA pools per-NUMA-node") Link: https://lore.kernel.org/r/20260309062840.2937858-2-iam@sung-woo.kim Reported-by: Sung-woo Kim Reviewed-by: Christoph Hellwig Signed-off-by: Mateusz Nowicki Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/pci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 8c66fd23a143c..489583393b845 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -431,11 +431,16 @@ static bool nvme_dbbuf_update_and_check_event(u16 value, __le32 *dbbuf_db, } static struct nvme_descriptor_pools * -nvme_setup_descriptor_pools(struct nvme_dev *dev, unsigned numa_node) +nvme_setup_descriptor_pools(struct nvme_dev *dev, int numa_node) { - struct nvme_descriptor_pools *pools = &dev->descriptor_pools[numa_node]; + struct nvme_descriptor_pools *pools; size_t small_align = NVME_SMALL_POOL_SIZE; + if (numa_node == NUMA_NO_NODE) + numa_node = 0; + + pools = &dev->descriptor_pools[numa_node]; + if (pools->small) return pools; /* already initialized */ From dc1470299d11d6275ace86fd9509944adec1b5c2 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Sun, 24 May 2026 08:19:56 -0700 Subject: [PATCH 0862/3297] workqueue: drop spurious '*' from print_worker_info() fn declaration [ Upstream commit 611583a76ea97991b0f65ec1ff099eac7fe0bae4 ] print_worker_info() declares its local 'fn' as work_func_t * but worker->current_func has type work_func_t (a function pointer). The extra level of indirection is wrong and only happens to be harmless today because every supported Linux architecture has sizeof(work_func_t) == sizeof(work_func_t *): copy_from_kernel_nofault() reads the correct number of bytes by accident, and %ps still resolves the printed address because the stored value is the function address regardless of declared type. On any future ABI where sizeof(void (*)()) differs from sizeof(void *), the nofault copy would transfer the wrong number of bytes and the subsequent %ps would print an incorrect address. Match the field type so the intent is explicit and the code does not silently rely on equal pointer sizes. Fixes: 3d1cb2059d93 ("workqueue: include workqueue info when printing debug dump of a worker task") Signed-off-by: Breno Leitao Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- kernel/workqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index a7793c471cfd8..20c79180fbfa9 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -6232,7 +6232,7 @@ EXPORT_SYMBOL_GPL(set_worker_desc); */ void print_worker_info(const char *log_lvl, struct task_struct *task) { - work_func_t *fn = NULL; + work_func_t fn = NULL; char name[WQ_NAME_LEN] = { }; char desc[WORKER_DESC_LEN] = { }; struct pool_workqueue *pwq = NULL; From 0db1949084e85a72d174a7dea3259b94fe5a9305 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 26 May 2026 14:55:28 +0000 Subject: [PATCH 0863/3297] ipv6: guard against possible NULL deref in __in6_dev_stats_get() [ Upstream commit 507541c2a8eeb76c02bd2511958f73a8cfa3e1bc ] dev_get_by_index_rcu() could return NULL if the original physical device is unregistered. Found by Sashiko. Fixes: e1ae5c2ea478 ("vrf: Increment Icmp6InMsgs on the original netdev") Signed-off-by: Eric Dumazet Cc: Stephen Suryaputra Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260526145529.3587126-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/addrconf.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 9e5e95988b9e5..c5a215ec14215 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -363,8 +363,11 @@ static inline struct inet6_dev *__in6_dev_get_rtnl_net(const struct net_device * static inline struct inet6_dev *__in6_dev_stats_get(const struct net_device *dev, const struct sk_buff *skb) { - if (netif_is_l3_master(dev)) + if (netif_is_l3_master(dev)) { dev = dev_get_by_index_rcu(dev_net(dev), inet6_iif(skb)); + if (!dev) + return NULL; + } return __in6_dev_get(dev); } From 10753da2d659dd425a6e620f47f86852d604f67f Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Tue, 26 May 2026 10:55:29 +0800 Subject: [PATCH 0864/3297] net/sched: cls_bpf: prevent unbounded recursion in offload rollback [ Upstream commit 27db54b90bcc7c37867fe664107fa25ea6a116e4 ] Quan Sun reported [1] a stack overflow in cls_bpf_offload_cmd(). Reproducer on netdevsim: add a skip_sw cls_bpf filter, set the bpf_tc_accept debugfs knob to 0, then `tc filter replace`. The replace calls tc_setup_cb_replace() which fails. cls_bpf_offload_cmd() then swaps prog/oldprog and recursively calls itself to roll back. But bpf_tc_accept=0 makes the rollback fail too, which triggers yet another rollback frame with the same arguments, and so on until the stack is exhausted. bpf_tc_accept is just a convenient knob for the reproducer. Any driver whose tc_setup_cb_replace() fails twice in a row can hit the same loop, so this is not a netdevsim-only issue. Two ways to fix it: 1) Have the rollback call tc_setup_cb_add() on oldprog instead of re-entering cls_bpf_offload_cmd(). 2) Mark the rollback frame with a flag and skip a second-level rollback from inside it. Go with (2). It is the smaller change and keeps the original behaviour: the rollback still goes through tc_setup_cb_replace(), so the driver gets one real chance to restore its state. If that attempt also fails, we just return the original error instead of recursing. [1]: https://lore.kernel.org/bpf/ce5a6005-3c5e-4696-9e05-eba9461dc860@std.uestc.edu.cn/T/#u Fixes: 102740bd9436 ("cls_bpf: fix offload assumptions after callback conversion") Reviewed-by: Jakub Kicinski Signed-off-by: Jiayuan Chen Link: https://patch.msgid.link/20260526025529.24382-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_bpf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index a32754a2658bb..e888d0aa9f21a 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -142,7 +142,8 @@ static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog) static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, struct cls_bpf_prog *oldprog, - struct netlink_ext_ack *extack) + struct netlink_ext_ack *extack, + bool is_rollback) { struct tcf_block *block = tp->chain->block; struct tc_cls_bpf_offload cls_bpf = {}; @@ -177,7 +178,8 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, &oldprog->in_hw_count, true); if (prog && err) { - cls_bpf_offload_cmd(tp, oldprog, prog, extack); + if (!is_rollback) + cls_bpf_offload_cmd(tp, oldprog, prog, extack, true); return err; } @@ -208,7 +210,7 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog, if (!prog && !oldprog) return 0; - return cls_bpf_offload_cmd(tp, prog, oldprog, extack); + return cls_bpf_offload_cmd(tp, prog, oldprog, extack, false); } static void cls_bpf_stop_offload(struct tcf_proto *tp, @@ -217,7 +219,7 @@ static void cls_bpf_stop_offload(struct tcf_proto *tp, { int err; - err = cls_bpf_offload_cmd(tp, NULL, prog, extack); + err = cls_bpf_offload_cmd(tp, NULL, prog, extack, false); if (err) pr_err("Stopping hardware offload failed: %d\n", err); } From dc266f6c4e2628576ab33042b6b2a12480220026 Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Sun, 17 May 2026 12:29:25 +0000 Subject: [PATCH 0865/3297] iommu/amd: Fix premature break in init_iommu_one() [ Upstream commit 283d245468a2b61c41aa8b582f25ed5615d1c304 ] In init_iommu_one(), when processing IOMMU EFR attributes, the code checks whether GASUP is enabled. If GASUP is not enabled, the code falls back to legacy guest IR mode and then breaks out of the switch statement. This break incorrectly skips the subsequent initialization steps that follow the GASUP check. These initializations are independent of GASUP support and must always be performed. Fix this by replacing the early break with a conditional else block, ensuring that the XTSUP check is only skipped when GASUP is not available. Fixes: a44092e326d4 ("iommu/amd: Use IVHD EFR for early initialization of IOMMU features") Reported-by: Sudheer Dantuluri Tested-by: Dheeraj Kumar Srivastava Signed-off-by: Vasant Hegde Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/init.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 6e5efc340c83e..32175daa3dd8a 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -1933,12 +1933,11 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h, /* XT and GAM require GA mode. */ if ((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0) { amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY; - break; + } else { + if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT)) + amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE; } - if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT)) - amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE; - if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) { pr_warn_once("Host Address Translation is not supported.\n"); amd_iommu_hatdis = true; From b7ac7ba19a0b83c3cf73321f58ab287e08999d55 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Tue, 6 Jan 2026 08:49:39 -0300 Subject: [PATCH 0866/3297] rtla: Introduce for_each_action() helper [ Upstream commit 648634d17c813b35da775982662e56ea8ce750de ] The for loop to iterate over the list of actions is used in more than one place. To avoid code duplication and improve readability, introduce a for_each_action() helper macro. Replace the open-coded for loops with the new helper. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260106133655.249887-4-wander@redhat.com Signed-off-by: Tomas Glozar Stable-dep-of: dd520daffbc9 ("rtla/actions: Restore continue flag in actions_perform()") Signed-off-by: Sasha Levin --- tools/tracing/rtla/src/actions.c | 6 ++++-- tools/tracing/rtla/src/actions.h | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index 4274fa0894b04..5f83ffc1008f8 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -32,7 +32,9 @@ void actions_destroy(struct actions *self) { /* Free any action-specific data */ - for (struct action *action = self->list; action < self->list + self->len; action++) { + struct action *action; + + for_each_action(self, action) { if (action->type == ACTION_SHELL) free(action->command); if (action->type == ACTION_TRACE_OUTPUT) @@ -226,7 +228,7 @@ actions_perform(struct actions *self) int pid, retval; const struct action *action; - for (action = self->list; action < self->list + self->len; action++) { + for_each_action(self, action) { switch (action->type) { case ACTION_TRACE_OUTPUT: retval = save_trace_to_file(self->trace_output_inst, action->trace_output); diff --git a/tools/tracing/rtla/src/actions.h b/tools/tracing/rtla/src/actions.h index a4f9b570775b5..fb77069c972ba 100644 --- a/tools/tracing/rtla/src/actions.h +++ b/tools/tracing/rtla/src/actions.h @@ -42,6 +42,11 @@ struct actions { struct tracefs_instance *trace_output_inst; }; +#define for_each_action(actions, action) \ + for ((action) = (actions)->list; \ + (action) < (actions)->list + (actions)->len; \ + (action)++) + void actions_init(struct actions *self); void actions_destroy(struct actions *self); int actions_add_trace_output(struct actions *self, const char *trace_output); From 05d85fd32e4fe9ba37ee243a49415f6557b8d162 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Tue, 26 May 2026 12:25:20 +0200 Subject: [PATCH 0867/3297] rtla/actions: Restore continue flag in actions_perform() [ Upstream commit dd520daffbc901f10d49a51a58313547d417b506 ] Currently, actions_perform() only ever sets the continue flag (when performing the continue action), but never resets it. That leads to RTLA continuing tracing even if the continue action was not performed in the current iteration. For example, the following command: $ rtla timerlat hist -T 100 --on-threshold shell,command=' echo Spike! if [ -f /tmp/a ] then exit 1 else touch /tmp/a fi' --on-threshold continue should print Spike! at most once, because after hitting the threshold for the first time, /tmp/a exists, the shell action will fail, and the continue action is not performed. However, unless /tmp/a exists before the measurement, it will print Spike! until stopped, as the continue flag stays set. Set the continue flag to false in the beginning of actions_perform() to make RTLA continue only if the action was actually performed. Fixes: 8d933d5c89e8 ("rtla/timerlat: Add continue action") Link: https://lore.kernel.org/r/20260526102523.2662391-1-tglozar@redhat.com [ correct Fixes tag to include 12 characters of hash ] Signed-off-by: Tomas Glozar Signed-off-by: Sasha Levin --- tools/tracing/rtla/src/actions.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index 5f83ffc1008f8..084c41ebfe6d7 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -228,6 +228,8 @@ actions_perform(struct actions *self) int pid, retval; const struct action *action; + self->continue_flag = false; + for_each_action(self, action) { switch (action->type) { case ACTION_TRACE_OUTPUT: From 40a2a91da02c434938f0ba53877984820800b5f0 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 17 May 2026 12:14:49 +0300 Subject: [PATCH 0868/3297] drm/tegra: gr2d/gr3d: Initialize address register map before HOST1X client is registered [ Upstream commit c4ef5ba1131346159e31f4ef858525cf377380a6 ] The host1x_client_register() function is called just prior to register map initialization loop, making the device available to userspace. This may result in userspace attempting to submits a job before the register map is initialized. Address this by moving register initialization before host1x client registration. Acked-by: Mikko Perttunen Signed-off-by: Svyatoslav Ryhel Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260517091450.46728-2-clamor95@gmail.com Stable-dep-of: ace01e2af387 ("drm/tegra: gr2d/gr3d: Contain PM in the gr*d_probe/gr*d_remove") Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gr2d.c | 8 ++++---- drivers/gpu/drm/tegra/gr3d.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 21f4dd0fa6aff..e4148b034af74 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -276,16 +276,16 @@ static int gr2d_probe(struct platform_device *pdev) if (err) return err; + /* initialize address register map */ + for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) + set_bit(gr2d_addr_regs[i], gr2d->addr_regs); + err = host1x_client_register(&gr2d->client.base); if (err < 0) { dev_err(dev, "failed to register host1x client: %d\n", err); return err; } - /* initialize address register map */ - for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) - set_bit(gr2d_addr_regs[i], gr2d->addr_regs); - return 0; } diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 42e9656ab80c9..47b0c6c56bfd0 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -506,6 +506,10 @@ static int gr3d_probe(struct platform_device *pdev) if (err) return err; + /* initialize address register map */ + for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) + set_bit(gr3d_addr_regs[i], gr3d->addr_regs); + err = host1x_client_register(&gr3d->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", @@ -513,10 +517,6 @@ static int gr3d_probe(struct platform_device *pdev) return err; } - /* initialize address register map */ - for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) - set_bit(gr3d_addr_regs[i], gr3d->addr_regs); - return 0; } From 6b617b6ccb6eb61188d91ba6f6988350218101e4 Mon Sep 17 00:00:00 2001 From: Ion Agorria Date: Sun, 17 May 2026 12:14:50 +0300 Subject: [PATCH 0869/3297] drm/tegra: gr2d/gr3d: Contain PM in the gr*d_probe/gr*d_remove [ Upstream commit ace01e2af3871343d700fb60c6f64d8f8e3180e1 ] The current power management configuration causes GR2G/GR3D to malfunction after resume. Reconfigure all PM actions to be handled within the GR*D probe and remove operations to address this. Fixes: 62fa0a985e2c ("drm/tegra: Enable runtime PM during probe") Acked-by: Mikko Perttunen Signed-off-by: Ion Agorria Signed-off-by: Svyatoslav Ryhel Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260517091450.46728-3-clamor95@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gr2d.c | 13 ++++++------- drivers/gpu/drm/tegra/gr3d.c | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index e4148b034af74..892e3450b281e 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -100,9 +100,6 @@ static int gr2d_exit(struct host1x_client *client) if (err < 0) return err; - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - host1x_client_iommu_detach(client); host1x_syncpt_put(client->syncpts[0]); host1x_channel_put(gr2d->channel); @@ -280,12 +277,18 @@ static int gr2d_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) set_bit(gr2d_addr_regs[i], gr2d->addr_regs); + pm_runtime_enable(dev); + err = host1x_client_register(&gr2d->client.base); if (err < 0) { + pm_runtime_disable(dev); dev_err(dev, "failed to register host1x client: %d\n", err); return err; } + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + return 0; } @@ -367,10 +370,6 @@ static int __maybe_unused gr2d_runtime_resume(struct device *dev) goto disable_clk; } - pm_runtime_enable(dev); - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 500); - return 0; disable_clk: diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 47b0c6c56bfd0..388e47943d5ec 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -109,9 +109,6 @@ static int gr3d_exit(struct host1x_client *client) if (err < 0) return err; - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - host1x_client_iommu_detach(client); host1x_syncpt_put(client->syncpts[0]); host1x_channel_put(gr3d->channel); @@ -510,13 +507,19 @@ static int gr3d_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) set_bit(gr3d_addr_regs[i], gr3d->addr_regs); + pm_runtime_enable(&pdev->dev); + err = host1x_client_register(&gr3d->client.base); if (err < 0) { + pm_runtime_disable(&pdev->dev); dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); return err; } + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 500); + return 0; } @@ -578,10 +581,6 @@ static int __maybe_unused gr3d_runtime_resume(struct device *dev) goto disable_clk; } - pm_runtime_enable(dev); - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 500); - return 0; disable_clk: From 8c0d3cf0d5108c96317e0eca92b60dd368867cef Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Fri, 15 May 2026 11:34:51 +0900 Subject: [PATCH 0870/3297] gpu: host1x: Allow entries in BO caches to be freed [ Upstream commit 3cbf5e3c46e66d9b3b6b91099bb720c6cb1be3bc ] When a buffer object is pinned via host1x_bo_pin() with a cache, the resulting mapping is kept in the cache so it can be reused on subsequent pins. Each mapping held a reference to the underlying host1x_bo (taken in tegra_bo_pin / gather_bo_pin), so as long as a mapping was cached, the bo itself could not be freed. However, the only way to remove the cached mapping was through the free path of the buffer object. This meant that if a bo got cached, it could never get freed again. Resolve the circularity by holding a weak reference to the bo from the cache side. This is done by having the .pin callbacks not bump the bo's refcount -- instead the common Host1x bo code does so, except for the cache reference. Also move the remove-cache-mapping-on-free code into a common function inside Host1x code. This is only called from the TegraDRM GEM buffers since those are the only ones that can be cached at the moment. Reported-by: Aaron Kling Fixes: 1f39b1dfa53c ("drm/tegra: Implement buffer object cache") Signed-off-by: Mikko Perttunen Tested-by: Aaron Kling Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260515-host1x-bocache-leak-v1-1-a0375f68aeab@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gem.c | 13 ++------ drivers/gpu/drm/tegra/submit.c | 3 +- drivers/gpu/host1x/bus.c | 60 +++++++++++++++++++++++++++++++++- include/linux/host1x.h | 7 ++++ 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 8ede07fb7a21d..fab3edbe33a6c 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -68,7 +68,7 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_ return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -169,7 +169,6 @@ static void tegra_bo_unpin(struct host1x_bo_mapping *map) kfree(map->sgt); } - host1x_bo_put(map->bo); kfree(map); } @@ -508,17 +507,9 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm, void tegra_bo_free_object(struct drm_gem_object *gem) { struct tegra_drm *tegra = gem->dev->dev_private; - struct host1x_bo_mapping *mapping, *tmp; struct tegra_bo *bo = to_tegra_bo(gem); - /* remove all mappings of this buffer object from any caches */ - list_for_each_entry_safe(mapping, tmp, &bo->base.mappings, list) { - if (mapping->cache) - host1x_bo_unpin(mapping); - else - dev_err(gem->dev->dev, "mapping %p stale for device %s\n", mapping, - dev_name(mapping->dev)); - } + host1x_bo_clear_cached_mappings(&bo->base); if (tegra->domain) { tegra_bo_iommu_unmap(tegra, bo); diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c index 2430fcc97448d..705382035f5f6 100644 --- a/drivers/gpu/drm/tegra/submit.c +++ b/drivers/gpu/drm/tegra/submit.c @@ -76,7 +76,7 @@ gather_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -117,7 +117,6 @@ static void gather_bo_unpin(struct host1x_bo_mapping *map) dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0); sg_free_table(map->sgt); kfree(map->sgt); - host1x_bo_put(map->bo); kfree(map); } diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 344cc9e741c13..8564b6aa30b03 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -876,6 +876,20 @@ int host1x_client_resume(struct host1x_client *client) } EXPORT_SYMBOL(host1x_client_resume); +/** + * host1x_bo_pin() - Create a DMA mapping for the buffer object + * @dev: Device onto which DMA map to + * @bo: Buffer object to map + * @dir: DMA direction + * @cache: Cache in which to store mapping, or NULL + * + * Creates a DMA mapping pointing to @bo for @dev. The refcount of @bo is incremented + * until host1x_bo_unpin is called. + * + * If @cache is specified, the mapping is also stored in the cache and not released + * until @bo is freed (refcount drops to zero). This improves performance when a buffer + * is pinned and unpinned frequently as in the case of display use. + */ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction dir, struct host1x_bo_cache *cache) @@ -888,6 +902,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo list_for_each_entry(mapping, &cache->mappings, entry) { if (mapping->bo == bo && mapping->direction == dir) { kref_get(&mapping->ref); + host1x_bo_get(bo); goto unlock; } } @@ -897,6 +912,8 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo if (IS_ERR(mapping)) goto unlock; + host1x_bo_get(bo); + spin_lock(&mapping->bo->lock); list_add_tail(&mapping->list, &bo->mappings); spin_unlock(&mapping->bo->lock); @@ -907,7 +924,12 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo list_add_tail(&mapping->entry, &cache->mappings); - /* bump reference count to track the copy in the cache */ + /* + * Bump the mapping reference count to track the mapping in the cache, + * but do not bump the BO's refcount. This allows the BO to still get freed, + * triggering the release of the cache mapping through + * host1x_bo_clear_cached_mappings. + */ kref_get(&mapping->ref); } @@ -937,9 +959,17 @@ static void __host1x_bo_unpin(struct kref *ref) mapping->bo->ops->unpin(mapping); } +/** + * host1x_bo_unpin() - Release an established DMA mapping of a buffer object + * @mapping: Mapping to release + * + * Unmaps the given @mapping, unless it is cached. Decreases the refcount on + * the underlying buffer object. + */ void host1x_bo_unpin(struct host1x_bo_mapping *mapping) { struct host1x_bo_cache *cache = mapping->cache; + struct host1x_bo *bo = mapping->bo; if (cache) mutex_lock(&cache->lock); @@ -948,5 +978,33 @@ void host1x_bo_unpin(struct host1x_bo_mapping *mapping) if (cache) mutex_unlock(&cache->lock); + + host1x_bo_put(bo); } EXPORT_SYMBOL(host1x_bo_unpin); + +/** + * host1x_bo_clear_cached_mappings() - Remove all cached mappings pointing at a bo + * @bo: Buffer object to release mappings of + * + * Drops references to any mappings pointing to @bo left in any caches. This must + * be called by any host1x_bo implementers that may be pinned with caching enabled + * before freeing the bo. + */ +void host1x_bo_clear_cached_mappings(struct host1x_bo *bo) +{ + struct host1x_bo_mapping *mapping, *tmp; + struct host1x_bo_cache *cache; + + list_for_each_entry_safe(mapping, tmp, &bo->mappings, list) { + cache = mapping->cache; + if (WARN_ON(!cache)) + continue; + + mutex_lock(&mapping->cache->lock); + WARN_ON(kref_read(&mapping->ref) != 1); + __host1x_bo_unpin(&mapping->ref); + mutex_unlock(&mapping->cache->lock); + } +} +EXPORT_SYMBOL(host1x_bo_clear_cached_mappings); diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 9fa9c30a34e65..42ca410594e8d 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -143,6 +143,12 @@ static inline struct host1x_bo_mapping *to_host1x_bo_mapping(struct kref *ref) return container_of(ref, struct host1x_bo_mapping, ref); } +/** + * struct host1x_bo_ops - operations implemented by a host1x_bo provider + * + * @pin: create a DMA mapping. Implementation must not touch the bo's refcount. + * @unpin: destroy a DMA mapping. Implementation must not touch the bo's refcount. + */ struct host1x_bo_ops { struct host1x_bo *(*get)(struct host1x_bo *bo); void (*put)(struct host1x_bo *bo); @@ -181,6 +187,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo enum dma_data_direction dir, struct host1x_bo_cache *cache); void host1x_bo_unpin(struct host1x_bo_mapping *map); +void host1x_bo_clear_cached_mappings(struct host1x_bo *bo); static inline void *host1x_bo_mmap(struct host1x_bo *bo) { From 142b34f7e329c6b827cc3937c323667ecefff027 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 28 Jan 2026 00:43:10 +0800 Subject: [PATCH 0871/3297] drm/tegra: dc: Fix device node reference leak in tegra_dc_has_output() [ Upstream commit 63a3998d792ab5c45304bf879e385a31fa923b61 ] The of_for_each_phandle() macro increments the reference count of the device node it iterates over. If the loop exits early, the reference must be released manually. In tegra_dc_has_output(), the function returns true immediately when a match is found, failing to release the current node's reference. Fix this by adding a call to of_node_put() before returning from the loop. Fixes: c57997bce423 ("drm/tegra: sor: Add Tegra186 support") Signed-off-by: Felix Gu Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260128-dc-v1-1-a88205826301@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/dc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6c84bd69b11ff..dc69c88736934 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -100,8 +100,10 @@ bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) int err; of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) - if (it.node == dev->of_node) + if (it.node == dev->of_node) { + of_node_put(it.node); return true; + } return false; } From 79240eee5a40014d9edfabe19f06b35ffa84e5f8 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 21 Apr 2026 13:02:36 +0900 Subject: [PATCH 0872/3297] gpu: host1x: Fix iommu_map_sgtable() return value check [ Upstream commit 18f74762013a4b6aa6f905c4459e0f506f9c5c7b ] 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. pin_job() also was incorrectly assigning to 'int', which could cause overflows into negative values. Update pin_job() to correctly check for errors from iommu_map_sgtable. Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421-iommu_map_sgtable-return-v1-1-fb484c07d2a1@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/host1x/job.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index 3ed49e1fd9332..70bda32f1ff48 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c @@ -235,6 +235,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) } if (host->domain) { + ssize_t map_err; + for_each_sgtable_sg(map->sgt, sg, j) gather_size += sg->length; @@ -248,11 +250,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) goto put; } - err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc), - map->sgt, IOMMU_READ); - if (err == 0) { + map_err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc), + map->sgt, IOMMU_READ); + if (map_err < 0) { __free_iova(&host->iova, alloc); - err = -EINVAL; + err = map_err; goto put; } From 2ab7c96d8c3fb7c9d34b1387488ff031c5831a39 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 21 Apr 2026 13:02:37 +0900 Subject: [PATCH 0873/3297] drm/tegra: Fix iommu_map_sgtable() return value check [ Upstream commit b3f349517de8f4469c385ebb7bfdfcc148790c0f ] 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. Update tegra_bo_iommu_map() to correctly check for errors from iommu_map_sgtable. Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421-iommu_map_sgtable-return-v1-2-fb484c07d2a1@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tegra/gem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index fab3edbe33a6c..abfac20d63fd2 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -233,6 +233,7 @@ static const struct host1x_bo_ops tegra_bo_ops = { static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) { int prot = IOMMU_READ | IOMMU_WRITE; + ssize_t size; int err; if (bo->mm) @@ -254,13 +255,15 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) bo->iova = bo->mm->start; - bo->size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot); - if (!bo->size) { + size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot); + if (size < 0) { dev_err(tegra->drm->dev, "failed to map buffer\n"); - err = -ENOMEM; + err = size; goto remove; } + bo->size = size; + mutex_unlock(&tegra->mm_lock); return 0; From 340936ebf5aec7652d6544d9bed299243836239f Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:35 -0500 Subject: [PATCH 0874/3297] drm/nouveau/bios: specify correct display fuse register for Ampere and Ada [ Upstream commit c1cf2d5db80ce91a85855bbaf4da85ff603e089a ] The NV_FUSE_STATUS_OPT_DISPLAY register is used to determine whether the GPU has display hardware. The current code that normally reads this register is instead hard-coded to check for GA100 vs later GPUs. Since this function is called only on pre-Hopper GPUs, and this if-statement applies only to GA100 and later, the check works because GA100 is the only non-display Ampere and Ada GPU. However, there actually is a register that can be read, so we should use it. Fixes: a34632482f1e ("drm/nouveau/bios/ga10[024]: initial support") Signed-off-by: Timur Tabi Reviewed-by: Lyude Paul Link: https://patch.msgid.link/20260430223838.2530778-8-ttabi@nvidia.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c index 023ddc7c5399a..a295e94e4cb88 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c @@ -65,13 +65,14 @@ pramin_init(struct nvkm_bios *bios, const char *name) /* we can't get the bios image pointer without PDISP */ if (device->card_type >= GA100) - addr = device->chipset == 0x170; /*XXX: find the fuse reg for this */ + addr = nvkm_rd32(device, 0x820c04); else if (device->card_type >= GM100) addr = nvkm_rd32(device, 0x021c04); else if (device->card_type >= NV_C0) addr = nvkm_rd32(device, 0x022500); + if (addr & 0x00000001) { nvkm_debug(subdev, "... display disabled\n"); return ERR_PTR(-ENODEV); From ed7ba8d048a4c15ce64d6f9aa9e09c188ba32230 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 22 May 2026 16:13:53 -0400 Subject: [PATCH 0875/3297] libbpf: Harden parse_vma_segs() path parsing [ Upstream commit fee9a38174f4c6454fb1fbaf2b9b5a1cca9070d0 ] parse_vma_segs() in tools/lib/bpf/usdt.c parses /proc//maps with two widthless scansets, "%s" into mode[16] and "%[^\n]" into line[4096]. A VMA name in maps is not limited to that local buffer; a deeply nested backing path can produce a maps record long enough to overflow the stack buffer. Bound both scansets to the declared buffer sizes ("%15s" for mode[16] and "%4095[^\n]" for line[4096]) and drain any residue past line[4094] with "%*[^\n]" before the trailing "\n". Without the drain, the residue of an over-long record would stay in the stream and break the next "%zx-%zx" parse, so the loop would exit early and silently skip later maps records. Also stop using sscanf(..., "%s") to peel the /proc//root prefix from lib_path. Parse the pid and prefix length with "%n", check for the following slash, and copy the remainder with libbpf_strlcpy(). That removes a second unbounded stack write and preserves paths containing spaces. Fixes: 74cc6311cec9 ("libbpf: Add USDT notes parsing and resolution logic") Signed-off-by: Michael Bommarito Signed-off-by: Andrii Nakryiko Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/bpf/20260522201353.1454653-1-michael.bommarito@gmail.com Signed-off-by: Sasha Levin --- tools/lib/bpf/usdt.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c index c174b40866738..63d628029ad4e 100644 --- a/tools/lib/bpf/usdt.c +++ b/tools/lib/bpf/usdt.c @@ -460,10 +460,10 @@ static int parse_elf_segs(Elf *elf, const char *path, struct elf_seg **segs, siz static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, size_t *seg_cnt) { - char path[PATH_MAX], line[PATH_MAX], mode[16]; + char path[PATH_MAX], line[4096], mode[16]; size_t seg_start, seg_end, seg_off; struct elf_seg *seg; - int tmp_pid, i, err; + int tmp_pid, n, i, err; FILE *f; *seg_cnt = 0; @@ -472,8 +472,13 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, * /proc//root/. They will be reported as just / in * /proc//maps. */ - if (sscanf(lib_path, "/proc/%d/root%s", &tmp_pid, path) == 2 && pid == tmp_pid) + /* %n is not counted in sscanf() return value, so initialize it. */ + n = 0; + if (sscanf(lib_path, "/proc/%d/root%n", &tmp_pid, &n) == 1 && + n > 0 && pid == tmp_pid && lib_path[n] == '/') { + libbpf_strlcpy(path, lib_path + n, sizeof(path)); goto proceed; + } if (!realpath(lib_path, path)) { pr_warn("usdt: failed to get absolute path of '%s' (err %s), using path as is...\n", @@ -496,8 +501,11 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, * 7f5c6f5d1000-7f5c6f5d3000 rw-p 001c7000 08:04 21238613 /usr/lib64/libc-2.17.so * 7f5c6f5d3000-7f5c6f5d8000 rw-p 00000000 00:00 0 * 7f5c6f5d8000-7f5c6f5d9000 r-xp 00000000 103:01 362990598 /data/users/andriin/linux/tools/bpf/usdt/libhello_usdt.so + * + * Some VMA names can be longer than the local buffer. Bound the + * writes, but still consume the rest of the line. */ - while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n", + while (fscanf(f, "%zx-%zx %15s %zx %*s %*d%4095[^\n]%*[^\n]\n", &seg_start, &seg_end, mode, &seg_off, line) == 5) { void *tmp; From 39e8be33387e3c5033982384c8448022a221e4d1 Mon Sep 17 00:00:00 2001 From: Siddharth Nayyar Date: Wed, 20 May 2026 09:40:44 +0000 Subject: [PATCH 0876/3297] bpftool: Fix typo in struct_ops map FD generation for light skeleton [ Upstream commit be4c6c7bc42952b71188894933946b410deadcfe ] When generating light skeletons for BPF programs containing struct_ops maps, bpftool incorrectly outputs a stray literal 't' instead of a tab character for the map file descriptor member in the links structure. This causes a compilation error when the generated light skeleton is used. Correct the format string by replacing 't' with '\t'. Fixes: 08ac454e258e ("libbpf: Auto-attach struct_ops BPF maps in BPF skeleton") Signed-off-by: Siddharth Nayyar Signed-off-by: Andrii Nakryiko Acked-by: Quentin Monnet Link: https://lore.kernel.org/bpf/20260520-struct_ops_gen_typo_fix-v1-1-4dee3771da46@google.com Signed-off-by: Sasha Levin --- tools/bpf/bpftool/gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 993c7d9484a46..bdef69c5e6cd4 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -1399,7 +1399,7 @@ static int do_skeleton(int argc, char **argv) continue; if (use_loader) - printf("t\tint %s_fd;\n", ident); + printf("\t\tint %s_fd;\n", ident); else printf("\t\tstruct bpf_link *%s;\n", ident); } From c87339cc08aa0af70e200f2255e9a8b747fe06bf Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Sat, 23 May 2026 16:27:21 +0000 Subject: [PATCH 0877/3297] libbpf: Fix UAF in strset__add_str() [ Upstream commit b23705e6afb6ac4ae6d220dcb35975698667dd76 ] strset_add_str_mem() might reallocate the strset data buffer in order to accommodate the provided string 's'. However, if 's' points to a string already present in the buffer, it becomes dangling after the realloc. This leads to a use-after-free when attempting to memcpy() the string into the new buffer. One scenario that triggers this problematic path is when resolve_btfids attempts to patch kfunc prototypes using existing BTF parameter names: | resolve_btfids: function bpf_list_push_back_impl already exists in BTF | Segmentation fault (core dumped) Compiling resolve_btfids with fsanitize=address generates a detailed report of the UAF: | ================================================================= | ERROR: AddressSanitizer: heap-use-after-free on address 0x7f4c4a500bd4 | ==1507892==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f4c4a500bd4 at pc 0x55d25155a2a8 bp 0x7ffcef879060 sp 0x7ffcef878818 | READ of size 5 at 0x7f4c4a500bd4 thread T0 | #0 0x55d25155a2a7 in memcpy (tools/bpf/resolve_btfids/resolve_btfids+0xcf2a7) | #1 0x55d2515d708e in strset__add_str tools/lib/bpf/strset.c:162:2 | #2 0x55d2515c730b in btf__add_str tools/lib/bpf/btf.c:2109:8 | #3 0x55d2515c9020 in btf__add_func_param tools/lib/bpf/btf.c:3108:14 | #4 0x55d25159f0b5 in process_kfunc_with_implicit_args tools/bpf/resolve_btfids/main.c:1196:9 | #5 0x55d25159e004 in btf2btf tools/bpf/resolve_btfids/main.c:1229:9 | #6 0x55d25159cee7 in main tools/bpf/resolve_btfids/main.c:1535:6 | #7 0x7f4c78e29f76 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 | #8 0x7f4c78e2a026 in __libc_start_main csu/../csu/libc-start.c:360:3 | #9 0x55d2514bb860 in _start (tools/bpf/resolve_btfids/resolve_btfids+0x30860) | | 0x7f4c4a500bd4 is located 13268 bytes inside of 2829000-byte region [0x7f4c4a4fd800,0x7f4c4a7b02c8) | freed by thread T0 here: | #0 0x55d25155b700 in realloc (tools/bpf/resolve_btfids/resolve_btfids+0xd0700) | #1 0x55d2515c426c in libbpf_reallocarray tools/lib/bpf/./libbpf_internal.h:220:9 | #2 0x55d2515c426c in libbpf_add_mem tools/lib/bpf/btf.c:224:13 | | previously allocated by thread T0 here: | #0 0x55d25155b2e3 in malloc (tools/bpf/resolve_btfids/resolve_btfids+0xd02e3) | #1 0x55d2515d6e7d in strset__new tools/lib/bpf/strset.c:58:20 While resolve_btfids could be refactored to avoid this call path, let's instead fix this issue at the source in strset__add_str() and avoid similar scenarios. Let's check if set->strs_data was reallocated and whether 's' points to an internal string within the old strset buffer. In such case, 's' is reconstructed to point to the new buffer. While already here, also fix strset__find_str() which suffers from the same problem by factoring out the common operations into a new helper function strset_str_append(). Fixes: 90d76d3ececc ("libbpf: Extract internal set-of-strings datastructure APIs") Suggested-by: Andrii Nakryiko Suggested-by: Mykyta Yatsenko Signed-off-by: Carlos Llamas Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20260523162722.2718940-1-cmllamas@google.com Signed-off-by: Sasha Levin --- tools/lib/bpf/strset.c | 62 ++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/tools/lib/bpf/strset.c b/tools/lib/bpf/strset.c index 2464bcbd04e03..ace73c6b3d62b 100644 --- a/tools/lib/bpf/strset.c +++ b/tools/lib/bpf/strset.c @@ -107,6 +107,41 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz) set->strs_data_len, set->strs_data_max_len, add_sz); } +static long strset_str_append(struct strset *set, const char *s) +{ + uintptr_t old_data = (uintptr_t)set->strs_data; + size_t old_data_len = set->strs_data_len; + uintptr_t old_s = (uintptr_t)s; + long len = strlen(s) + 1; + void *p; + + /* + * Hashmap keys are always offsets within set->strs_data, so to even + * look up some string from the "outside", we need to first append it + * at the end, so that it can be addressed with an offset. Luckily, + * until set->strs_data_len is incremented, that string is just a piece + * of garbage for the rest of the code, so no harm, no foul. On the + * other hand, if the string is unique, it's already appended and + * ready to be used, only a simple set->strs_data_len increment away. + */ + p = strset_add_str_mem(set, len); + if (!p) + return -ENOMEM; + + /* + * The set->strs_data might have reallocated and if 's' pointed + * to an internal string within the old buffer, then it became + * dangling and needs to be reconstructed before the copy. + */ + if (old_data && old_data != (uintptr_t)set->strs_data && + old_s >= old_data && old_s < old_data + old_data_len) + s = set->strs_data + (old_s - old_data); + + memcpy(p, s, len); + + return len; +} + /* Find string offset that corresponds to a given string *s*. * Returns: * - >0 offset into string data, if string is found; @@ -116,16 +151,12 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz) int strset__find_str(struct strset *set, const char *s) { long old_off, new_off, len; - void *p; - /* see strset__add_str() for why we do this */ - len = strlen(s) + 1; - p = strset_add_str_mem(set, len); - if (!p) - return -ENOMEM; + len = strset_str_append(set, s); + if (len < 0) + return len; new_off = set->strs_data_len; - memcpy(p, s, len); if (hashmap__find(set->strs_hash, new_off, &old_off)) return old_off; @@ -142,24 +173,13 @@ int strset__find_str(struct strset *set, const char *s) int strset__add_str(struct strset *set, const char *s) { long old_off, new_off, len; - void *p; int err; - /* Hashmap keys are always offsets within set->strs_data, so to even - * look up some string from the "outside", we need to first append it - * at the end, so that it can be addressed with an offset. Luckily, - * until set->strs_data_len is incremented, that string is just a piece - * of garbage for the rest of the code, so no harm, no foul. On the - * other hand, if the string is unique, it's already appended and - * ready to be used, only a simple set->strs_data_len increment away. - */ - len = strlen(s) + 1; - p = strset_add_str_mem(set, len); - if (!p) - return -ENOMEM; + len = strset_str_append(set, s); + if (len < 0) + return len; new_off = set->strs_data_len; - memcpy(p, s, len); /* Now attempt to add the string, but only if the string with the same * contents doesn't exist already (HASHMAP_ADD strategy). If such From e545fcba3660ce4e8a29556c82d4732957cada67 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:55 -0700 Subject: [PATCH 0878/3297] ARM: tegra: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit 1fe27b10dc97c85821dfae1e4e6f9db4472287aa ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: / { firmware { ranges; coreboot { compatible = "coreboot"; reg = <...>; ...; }; }; }; Notably, the /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi | 5 +++++ arch/arm/boot/dts/nvidia/tegra124-venice2.dts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi b/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi index 974c76f007db4..89a749cb89338 100644 --- a/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi +++ b/arch/arm/boot/dts/nvidia/tegra124-nyan.dtsi @@ -14,6 +14,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <1>; + #size-cells = <1>; + }; + /* * Note that recent version of the device tree compiler (starting with * version 1.4.2) warn about this node containing a reg property, but diff --git a/arch/arm/boot/dts/nvidia/tegra124-venice2.dts b/arch/arm/boot/dts/nvidia/tegra124-venice2.dts index df98dc2a67b85..059ee6c5b13cd 100644 --- a/arch/arm/boot/dts/nvidia/tegra124-venice2.dts +++ b/arch/arm/boot/dts/nvidia/tegra124-venice2.dts @@ -18,6 +18,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <1>; + #size-cells = <1>; + }; + memory@80000000 { reg = <0x0 0x80000000 0x0 0x80000000>; }; From afdb92d9c374d107abbe55f54481e71b3c45f177 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Apr 2026 13:06:58 -0700 Subject: [PATCH 0879/3297] arm64: tegra: Add #{address,size}-cells to Chromium-based /firmware [ Upstream commit f0fbedccae9e16624977cca02216ab2399f5a3ab ] Chromium/Depthcharge bootloaders may dynamically add a few device nodes to a system's DTB under a /firmware node. A typical DT looks something like the following: / { firmware { ranges; coreboot { compatible = "coreboot"; reg = <...>; ...; }; }; }; Notably, the /firmware node has an empty 'ranges', but does not have address/size-cells. Commit 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") started requiring #address-cells for a device's parent if we want to use the reg resource in a device node. This leads to errors like the following: [ 7.763870] coreboot_table firmware:coreboot: probe with driver coreboot_table failed with error -22 Add appropriate #{address,size}-cells to work around the problem. Note that Google has also patched the Depthcharge bootloader source to add {address,size}-cells [1], but bootloader updates are typically delivered only via Google OS updates. Not all users install Google software updates, and even if they do, Google may not produce updated binaries for all/older devices. [1] https://lore.kernel.org/all/20241209092809.GA3246424@google.com/ https://crrev.com/c/6051580 ("coreboot: Insert #address-cells and #size-cells for firmware node") Closes: https://lore.kernel.org/all/aeKlYzTiL0OB1y3g@google.com/ Fixes: 6e5773d52f4a ("of/address: Fix WARN when attempting translating non-translatable addresses") Signed-off-by: Brian Norris Reviewed-by: Douglas Anderson Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/nvidia/tegra132-norrin.dts | 5 +++++ arch/arm64/boot/dts/nvidia/tegra210-smaug.dts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts b/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts index 683ac124523b3..1f5222d43e62c 100644 --- a/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts +++ b/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts @@ -18,6 +18,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <2>; + #size-cells = <2>; + }; + memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; diff --git a/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts b/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts index dfbd1c72388c1..8cf6c04601ad6 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts +++ b/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts @@ -25,6 +25,11 @@ stdout-path = "serial0:115200n8"; }; + firmware { + #address-cells = <2>; + #size-cells = <2>; + }; + memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0xc0000000>; From 598ed7393a639c7547b28ff3e14db9f1ac37c132 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 23 Feb 2026 12:15:16 -0800 Subject: [PATCH 0880/3297] dax/kmem: account for partial discontiguous resource upon removal [ Upstream commit 8aa442cfce79e2d69e72fc8e0c0864ac2971149d ] When dev_dax_kmem_probe() partially succeeds (at least one range is mapped) but a subsequent range fails request_mem_region() or add_memory_driver_managed(), the probe silently continues, ultimately returning success, but with the corresponding range resource NULL'ed out. dev_dax_kmem_remove() iterates over all dax_device ranges regardless of if the underlying resource exists. When remove_memory() is called later, it returns 0 because the memory was never added which causes dev_dax_kmem_remove() to incorrectly assume the (nonexistent) resource can be removed and attempts cleanup on a NULL pointer. Fix this by skipping these ranges altogether, noting that these cases are considered success, such that the cleanup is still reached when all actually-added ranges are successfully removed. Link: https://lore.kernel.org/20260223201516.1517657-1-dave@stgolabs.net Fixes: 60e93dc097f7 ("device-dax: add dis-contiguous resource support") Signed-off-by: Davidlohr Bueso Reviewed-by: Ben Cheatham Reviewed-by: Alison Schofield Reviewed-by: Jonathan Cameron Cc: Dan Williams Cc: Dave Jiang Cc: Vishal Verma Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- drivers/dax/kmem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c index c036e4d0b610b..edd62e68ffb70 100644 --- a/drivers/dax/kmem.c +++ b/drivers/dax/kmem.c @@ -227,6 +227,12 @@ static void dev_dax_kmem_remove(struct dev_dax *dev_dax) if (rc) continue; + /* range was never added during probe */ + if (!data->res[i]) { + success++; + continue; + } + rc = remove_memory(range.start, range_len(&range)); if (rc == 0) { remove_resource(data->res[i]); From 070f356ea4f419e4e85e4089381f4477f41d969e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 May 2026 10:51:56 +0300 Subject: [PATCH 0881/3297] rapidio/tsi721: prevent a bad dereference in tsi721_db_dpc() [ Upstream commit fc15e3a30ddd950f009c76765331783b9af94a87 ] With a list_for_each() loop, if we don't find the item we are looking for in the list, then the loop exits with the iterator, which is "dbell" in this loop, pointing to invalid memory. This code uses the "found" variable to determine if we have found the doorbell we are looking for or not. However, the problem that the "found" variable needs to be set to false at the start of each iteration, otherwise after the first correct doorbell, then everything is marked as found. Reset the "found" to false at the start of the iteration and move the variable inside the loop. Link: https://lore.kernel.org/af2WHMZiqMwdYveO@stanley.mountain Fixes: 48618fb4e522 ("RapidIO: add mport driver for Tsi721 bridge") Signed-off-by: Dan Carpenter Cc: Alexandre Bounine Cc: Chul Kim Cc: Matt Porter Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- drivers/rapidio/devices/tsi721.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 4b84270a89061..ab64c991468de 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -394,7 +394,6 @@ static void tsi721_db_dpc(struct work_struct *work) idb_work); struct rio_mport *mport; struct rio_dbell *dbell; - int found = 0; u32 wr_ptr, rd_ptr; u64 *idb_entry; u32 regval; @@ -412,6 +411,8 @@ static void tsi721_db_dpc(struct work_struct *work) rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE; while (wr_ptr != rd_ptr) { + int found = 0; + idb_entry = (u64 *)(priv->idb_base + (TSI721_IDB_ENTRY_SIZE * rd_ptr)); rd_ptr++; From 3852478d34c7baa490ba9c7374ee901a56eea577 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Tue, 12 May 2026 10:41:15 +0800 Subject: [PATCH 0882/3297] ocfs2: don't BUG_ON an invalid journal dinode [ Upstream commit c0438198c28b1d22c272751af5e717c11d9fa8dd ] [BUG] A fuzzed OCFS2 image can corrupt the current slot journal dinode while mount is still in progress. The mount path first reports the invalid journal block and then crashes in shutdown: kernel BUG at fs/ocfs2/journal.c:1034! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ocfs2_journal_toggle_dirty+0x2d6/0x340 fs/ocfs2/journal.c:1034 Call Trace: ocfs2_journal_shutdown+0x414/0xc30 fs/ocfs2/journal.c:1116 ocfs2_mount_volume fs/ocfs2/super.c:1785 [inline] ocfs2_fill_super+0x30a9/0x3cd0 fs/ocfs2/super.c:1083 get_tree_bdev_flags+0x38b/0x640 fs/super.c:1698 get_tree_bdev+0x24/0x40 fs/super.c:1721 ocfs2_get_tree+0x21/0x30 fs/ocfs2/super.c:1184 vfs_get_tree+0x9a/0x370 fs/super.c:1758 fc_mount fs/namespace.c:1199 [inline] do_new_mount_fc fs/namespace.c:3642 [inline] do_new_mount fs/namespace.c:3718 [inline] path_mount+0x5b8/0x1ea0 fs/namespace.c:4028 do_mount fs/namespace.c:4041 [inline] __do_sys_mount fs/namespace.c:4229 [inline] __se_sys_mount fs/namespace.c:4206 [inline] __x64_sys_mount+0x282/0x320 fs/namespace.c:4206 ... [CAUSE] ocfs2_journal_toggle_dirty() used to return -EIO when journal->j_bh no longer contained a valid dinode, because the startup and shutdown paths already handled that failure. Commit 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") changed the check to a BUG_ON() under the assumption that the journal dinode had already been validated. That turns an unexpected invalid journal dinode during mount teardown into a kernel crash instead of a normal mount failure. [FIX] Replace the BUG_ON() with WARN_ON() and return -EIO. This keeps the invariant warning for debugging, but restores the original behavior of failing startup or shutdown cleanly instead of panicking the kernel. Link: https://lore.kernel.org/20260512024115.4036371-1-gality369@gmail.com Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/journal.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index e5f58ff2175f4..bbfa41d37ec90 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1027,11 +1027,8 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb, struct ocfs2_dinode *fe; fe = (struct ocfs2_dinode *)bh->b_data; - - /* The journal bh on the osb always comes from ocfs2_journal_init() - * and was validated there inside ocfs2_inode_lock_full(). It's a - * code bug if we mess it up. */ - BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); + if (WARN_ON(!OCFS2_IS_VALID_DINODE(fe))) + return -EIO; flags = le32_to_cpu(fe->id1.journal1.ij_flags); if (dirty) From ce7a2e26e144fb6121506703d5d3c3d501478cc3 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 18 May 2026 13:23:40 +0900 Subject: [PATCH 0883/3297] ocfs2: kill osb->system_file_mutex lock [ Upstream commit f5b1910e23f1233c8d4185268b2e659df2bc5dbf ] Commit 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") tried to avoid a refcount leak caused by allowing multiple threads to call igrab(inode). But addition of osb->system_file_mutex made locking dependency complicated and is causing lockdep to warn about possibility of AB-BA deadlock. Since _ocfs2_get_system_file_inode() returns the same inode for the same input arguments, we don't need to serialize _ocfs2_get_system_file_inode(). What we need to make sure is that igrab(inode) is called for only once(). Therefore, replace osb->system_file_mutex with cmpxchg()-based locking. Link: https://lore.kernel.org/fea8d1fd-afb0-4302-a560-c202e2ef7afd@I-love.SAKURA.ne.jp Fixes: 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") Signed-off-by: Tetsuo Handa Reviewed-by: Heming Zhao Acked-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/ocfs2.h | 2 -- fs/ocfs2/super.c | 2 -- fs/ocfs2/sysfile.c | 9 +++------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 6aaa94c554c12..8bdeea60742af 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -494,8 +494,6 @@ struct ocfs2_super struct rb_root osb_rf_lock_tree; struct ocfs2_refcount_tree *osb_ref_tree_lru; - struct mutex system_file_mutex; - /* * OCFS2 needs to schedule several different types of work which * require cluster locking, disk I/O, recovery waits, etc. Since these diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 53daa44824063..a4f5a868bec10 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1997,8 +1997,6 @@ static int ocfs2_initialize_super(struct super_block *sb, spin_lock_init(&osb->osb_xattr_lock); ocfs2_init_steal_slots(osb); - mutex_init(&osb->system_file_mutex); - atomic_set(&osb->alloc_stats.moves, 0); atomic_set(&osb->alloc_stats.local_data, 0); atomic_set(&osb->alloc_stats.bitmap_data, 0); diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index d53a6cc866bef..67e492f4b828b 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -98,11 +98,9 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, } else arr = get_local_system_inode(osb, type, slot); - mutex_lock(&osb->system_file_mutex); if (arr && ((inode = *arr) != NULL)) { /* get a ref in addition to the array ref */ inode = igrab(inode); - mutex_unlock(&osb->system_file_mutex); BUG_ON(!inode); return inode; @@ -112,11 +110,10 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, inode = _ocfs2_get_system_file_inode(osb, type, slot); /* add one more if putting into array for first time */ - if (arr && inode) { - *arr = igrab(inode); - BUG_ON(!*arr); + if (inode && arr && !*arr && !cmpxchg(&(*arr), NULL, inode)) { + inode = igrab(inode); + BUG_ON(!inode); } - mutex_unlock(&osb->system_file_mutex); return inode; } From 8572232ed74f54d1cc02477b0ad2a5cc1dc63dc7 Mon Sep 17 00:00:00 2001 From: Weili Qian Date: Mon, 18 May 2026 22:29:54 +0800 Subject: [PATCH 0884/3297] crypto: hisilicon/qm - disable error report before flr [ Upstream commit e71dc5602b9a29027f6aedd5990d3e8c4f638c8c ] Before function level reset, driver first disable device error report and then waits for the device reset to complete. However, when the error is recovered, the error bits will be enabled again, resulting in invalid disable. It is modified to detect that there is no error before disable error report, and then do FLR. Fixes: 7ce396fa12a9 ("crypto: hisilicon - add FLR support") Signed-off-by: Weili Qian Signed-off-by: Zongyu Wu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/hisilicon/qm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index b92ee2fcb18aa..fa808eeaf005b 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -4785,8 +4785,6 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev) u32 delay = 0; int ret; - hisi_qm_dev_err_uninit(pf_qm); - /* * Check whether there is an ECC mbit error, If it occurs, need to * wait for soft reset to fix it. @@ -4803,6 +4801,8 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev) return; } + hisi_qm_dev_err_uninit(pf_qm); + /* PF obtains the information of VF by querying the register. */ if (qm->fun_type == QM_HW_PF) qm_cmd_uninit(qm); From 5231093c082951f030f16769c280641411b17bd0 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Mon, 18 May 2026 23:24:59 +0200 Subject: [PATCH 0885/3297] crypto: inside-secure/eip93 - Add check for devm_request_threaded_irq [ Upstream commit 85a61bf9145d4097c740ffcf3aa832d930a8913b ] As the potential failure of the devm_request_threaded_irq(), it should be better to check the return value and return error if fails. Fixes: 9739f5f93b78 ("crypto: eip93 - Add Inside Secure SafeXcel EIP-93 crypto engine support") Signed-off-by: Aleksander Jan Bajkowski Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/inside-secure/eip93/eip93-main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/crypto/inside-secure/eip93/eip93-main.c b/drivers/crypto/inside-secure/eip93/eip93-main.c index 76858bb4fcc22..59b19e6ea4df7 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-main.c +++ b/drivers/crypto/inside-secure/eip93/eip93-main.c @@ -433,6 +433,8 @@ static int eip93_crypto_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(eip93->dev, eip93->irq, eip93_irq_handler, NULL, IRQF_ONESHOT, dev_name(eip93->dev), eip93); + if (ret) + return ret; eip93->ring = devm_kcalloc(eip93->dev, 1, sizeof(*eip93->ring), GFP_KERNEL); if (!eip93->ring) From 5eac10c52139682a78a9442cb4ab1eb19eb07d69 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 19 May 2026 12:22:18 +0800 Subject: [PATCH 0886/3297] crypto: tegra - Fix dma_free_coherent size error [ Upstream commit 03215b8457784540acc741e6331e355b62c6c8ab ] When freeing a coherent DMA buffer, the size must match the value that was used during the allocation. Unfortunately the size field in the tegra driver gets overwritten by this point so it no longer matches and creates a warning. Fix this by saving a copy of the size on the stack. Note that the ccm function actually mixes up the inbuf and outbuf sizes, but it doesn't matter because the two sizes are actually equal. Fixes: 1cb328da4e8f ("crypto: tegra - Do not use fixed size buffers") Reporeted-by: Patrick Talbert Signed-off-by: Herbert Xu Reviewed-by: Vladislav Dronov Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/tegra/tegra-se-aes.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 30c78afe3dea6..5086e7f140c30 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -1201,6 +1201,7 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); struct tegra_se *se = ctx->se; + unsigned int bufsize; int ret; ret = tegra_ccm_crypt_init(req, se, rctx); @@ -1210,14 +1211,15 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) rctx->key_id = ctx->key_id; /* Allocate buffers required */ - rctx->inbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; - rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->inbuf.size, + bufsize = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; + rctx->inbuf.size = bufsize; + rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); if (!rctx->inbuf.buf) goto out_finalize; - rctx->outbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen + 100; - rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->outbuf.size, + rctx->outbuf.size = bufsize; + rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); if (!rctx->outbuf.buf) { ret = -ENOMEM; @@ -1254,11 +1256,11 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) } out: - dma_free_coherent(ctx->se->dev, rctx->inbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->outbuf.buf, rctx->outbuf.addr); out_free_inbuf: - dma_free_coherent(ctx->se->dev, rctx->outbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->inbuf.buf, rctx->inbuf.addr); if (tegra_key_is_reserved(rctx->key_id)) @@ -1278,6 +1280,7 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); struct tegra_aead_reqctx *rctx = aead_request_ctx(req); + unsigned int bufsize; int ret; rctx->src_sg = req->src; @@ -1296,16 +1299,17 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) rctx->key_id = ctx->key_id; /* Allocate buffers required */ - rctx->inbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen; - rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->inbuf.size, + bufsize = rctx->assoclen + rctx->authsize + rctx->cryptlen; + rctx->inbuf.size = bufsize; + rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); if (!rctx->inbuf.buf) { ret = -ENOMEM; goto out_finalize; } - rctx->outbuf.size = rctx->assoclen + rctx->authsize + rctx->cryptlen; - rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->outbuf.size, + rctx->outbuf.size = bufsize; + rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); if (!rctx->outbuf.buf) { ret = -ENOMEM; @@ -1342,11 +1346,11 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) ret = tegra_gcm_do_verify(ctx->se, rctx); out: - dma_free_coherent(ctx->se->dev, rctx->outbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->outbuf.buf, rctx->outbuf.addr); out_free_inbuf: - dma_free_coherent(ctx->se->dev, rctx->inbuf.size, + dma_free_coherent(ctx->se->dev, bufsize, rctx->inbuf.buf, rctx->inbuf.addr); if (tegra_key_is_reserved(rctx->key_id)) From 4f3c17f14cf9085a9c9ae2145f748ea55cf6d4e9 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 20 May 2026 10:51:14 +0800 Subject: [PATCH 0887/3297] crypto: tegra - Return ENOMEM when input buffer allocation fails for ccm [ Upstream commit 690a5f9e5c972a580565ce544ed1627ccf1e84de ] Ensure the ENOMEM error value is set when the input buffer allocation fails in tegra_ccm_do_one_req. Fixes: 1e245948ca0c ("crypto: tegra - finalize crypto req on error") Reported-by: Vladislav Dronov Signed-off-by: Herbert Xu Reviewed-by: Vladislav Dronov Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/tegra/tegra-se-aes.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 5086e7f140c30..9094c03e991f6 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -1215,16 +1215,15 @@ static int tegra_ccm_do_one_req(struct crypto_engine *engine, void *areq) rctx->inbuf.size = bufsize; rctx->inbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->inbuf.addr, GFP_KERNEL); + ret = -ENOMEM; if (!rctx->inbuf.buf) goto out_finalize; rctx->outbuf.size = bufsize; rctx->outbuf.buf = dma_alloc_coherent(ctx->se->dev, bufsize, &rctx->outbuf.addr, GFP_KERNEL); - if (!rctx->outbuf.buf) { - ret = -ENOMEM; + if (!rctx->outbuf.buf) goto out_free_inbuf; - } if (!ctx->key_id) { ret = tegra_key_submit_reserved_aes(ctx->se, ctx->key, From 6d536a910717248cb3925d0e6c3595793cd1c752 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Tue, 26 May 2026 12:05:02 +0200 Subject: [PATCH 0888/3297] sched/deadline: Reject debugfs dl_server writes for offline CPUs [ Upstream commit 4043f549841619a01999bf5d4e0b7931ef87f6cc ] Writing runtime or period via the per-CPU dl_server debugfs files (/sys/kernel/debug/sched/{fair,ext}_server/cpu*/{runtime,period}) on an offline CPU can trigger two distinct kernel issues: 1) Divide-by-zero in dl_server_apply_params(): Oops: divide error: 0000 [#1] SMP NOPTI RIP: 0010:dl_server_apply_params+0x239/0x3a0 Call Trace: sched_server_write_common.isra.0+0x21a/0x3c0 full_proxy_write+0x78/0xd0 vfs_write+0xe7/0x6e0 Both __dl_sub() and __dl_add() divide by cpus internally, which can be 0 once the CPU has been removed from any active root-domain span (this has been latent since the debugfs interface was introduced). 2) WARN_ON_ONCE in dl_server_start(): WARNING: kernel/sched/deadline.c:1805 at dl_server_start+0x232/0x270 Commit ee6e44dfe6e5 ("sched/deadline: Stop dl_server before CPU goes offline") added this check to catch enqueueing the server on an offline rq. There's no meaningful semantics for re-configuring the per-CPU dl_server bandwidth while the CPU is offline, so simply reject the write with -EBUSY so userspace gets a clear error. Closes: https://lore.kernel.org/all/20260526092228.3B6891F00A3A@smtp.kernel.org/ Fixes: d741f297bcea ("sched/fair: Fair server interface") Reported-by: Sashiko Signed-off-by: Andrea Righi Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Juri Lelli Tested-by: abaci-kreproducer Link: https://patch.msgid.link/20260526100502.575774-1-arighi@nvidia.com Signed-off-by: Sasha Levin --- kernel/sched/debug.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 3504ec9bd7307..146d8b1eab671 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -376,6 +376,9 @@ static ssize_t sched_fair_server_write(struct file *filp, const char __user *ubu return -EINVAL; } + if (!cpu_online(cpu_of(rq))) + return -EBUSY; + update_rq_clock(rq); dl_server_stop(&rq->fair_server); From bdaea74abcf0cc484925bdf46a92cde73f2d4a99 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:29 +0300 Subject: [PATCH 0889/3297] drm/msm/dp: fix HPD state status bit shift value [ Upstream commit 2e6c2e81d81251623c458a60e2a57447dcbc988e ] The HPD state status is the 3 most significant bits, not 4 bits of the HPD_INT_STATUS register. Fix the bit shift macro so that the correct bits are returned in msm_dp_aux_is_link_connected(). Fixes: 19e52bcb27c2 ("drm/msm/dp: return correct connection status after suspend") Signed-off-by: Jessica Zhang Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727611/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-1-cf3ab488dd7b@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 7c44d4e2cf139..3689642b7fc06 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -68,8 +68,8 @@ #define DP_DP_IRQ_HPD_INT_ACK (0x00000002) #define DP_DP_HPD_REPLUG_INT_ACK (0x00000004) #define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008) -#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F) -#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C) +#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x00000007) +#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1D) #define REG_DP_DP_HPD_INT_MASK (0x0000000C) #define DP_DP_HPD_PLUG_INT_MASK (0x00000001) From ed5c94cf4ee9edb302c9242ddaf4218d49d185f5 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:30 +0300 Subject: [PATCH 0890/3297] drm/msm/dp: Fix the ISR_* enum values [ Upstream commit 3fbfdc3b1d48cc115a86953e5df0c76cd2efc42b ] The ISR_HPD_* enum should represent values that can be read from the REG_DP_DP_HPD_INT_STATUS register. Swap ISR_HPD_IO_GLITCH_COUNT and ISR_HPD_REPLUG_COUNT to map them correctly to register values. While we are at it, correct the spelling for ISR_HPD_REPLUG_COUNT. Fixes: 8ede2ecc3e5e ("drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets") Signed-off-by: Jessica Zhang Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727602/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-2-cf3ab488dd7b@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dp/dp_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index c79e624c11758..c6f5422b60ddb 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -38,9 +38,9 @@ enum { ISR_DISCONNECTED, ISR_CONNECT_PENDING, ISR_CONNECTED, - ISR_HPD_REPLUG_COUNT, + ISR_HPD_IO_GLITCH_COUNT, ISR_IRQ_HPD_PULSE_COUNT, - ISR_HPD_LO_GLITH_COUNT, + ISR_HPD_REPLUG_COUNT, }; /* event thread connection state */ From 9a84ced0243c74aa431ec287555054c69e6167d1 Mon Sep 17 00:00:00 2001 From: Qiuxu Zhuo Date: Fri, 3 Apr 2026 13:40:27 +0800 Subject: [PATCH 0891/3297] EDAC/igen6: Fix call trace due to missing release() [ Upstream commit ab1f9d466c7d83ab0d2a529e07984e53b5960dcd ] When unloading the igen6_edac driver, there is a call trace: Device '(null)' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst. WARNING: drivers/base/core.c:2567 at device_release+0x84/0x90, CPU#5: rmmod/127209 ... RIP: 0010:device_release+0x84/0x90 Call Trace: kobject_put+0x8c/0x220 put_device+0x17/0x30 igen6_unregister_mcis+0xa2/0xe0 [igen6_edac] igen6_remove+0x82/0xb0 [igen6_edac] ... Fix the call trace by providing empty release() functions for the memory controller devices. Fixes: 10590a9d4f23 ("EDAC/igen6: Add EDAC driver for Intel client SoCs using IBECC") Signed-off-by: Qiuxu Zhuo Signed-off-by: Tony Luck Link: https://patch.msgid.link/20260403054029.3950383-2-qiuxu.zhuo@intel.com Signed-off-by: Sasha Levin --- drivers/edac/igen6_edac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index 463bbcd484c3c..42eadece2d71b 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -1255,6 +1255,11 @@ static bool igen6_imc_absent(void __iomem *window) return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0; } +static void imc_release(struct device *dev) +{ + /* Nothing to do, the 'imc' owns the 'dev' and will also release it. */ +} + static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev) { struct edac_mc_layer layers[2]; @@ -1293,6 +1298,7 @@ static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev mci->pvt_info = &igen6_pvt->imc[mc]; imc = mci->pvt_info; + imc->dev.release = imc_release; device_initialize(&imc->dev); /* * EDAC core uses mci->pdev(pointer of structure device) as From 5da012c605fd51cbe0314aa641acc5e4a9fe3ea3 Mon Sep 17 00:00:00 2001 From: zhoumin Date: Thu, 26 Mar 2026 17:14:03 +0800 Subject: [PATCH 0892/3297] EDAC/{skx_common,skx}: Fix UBSAN shift-out-of-bounds in skx_get_dimm_info [ Upstream commit c63ed6e1f5fe648a4a099b6717f679999be482ef ] When the skx_get_dimm_attr() helper returns -EINVAL, skx_get_dimm_info() does not validate these return values before using them in a shift operation: size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3); If all three values are -22, the shift exponent becomes -66, triggering a UBSAN shift-out-of-bounds error: UBSAN: shift-out-of-bounds in drivers/edac/skx_common.c shift exponent -66 is negative Fixes: 88a242c98740 ("EDAC, skx_common: Separate common code out from skx_edac") Signed-off-by: zhoumin Signed-off-by: Tony Luck Link: https://patch.msgid.link/tencent_2A0CC835A18366643CBD2865B169948AB409@qq.com Signed-off-by: Sasha Levin --- drivers/edac/skx_common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c index 724842f512aca..ef124729bbae3 100644 --- a/drivers/edac/skx_common.c +++ b/drivers/edac/skx_common.c @@ -452,6 +452,9 @@ int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm, rows = numrow(mtr); cols = imc->hbm_mc ? 6 : numcol(mtr); + if (ranks < 0 || rows < 0 || cols < 0) + return 0; + if (imc->hbm_mc) { banks = 32; mtype = MEM_HBM2; From bae436a78a05890958e50a4e9be0944097193a16 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 11 Apr 2026 15:02:35 +0200 Subject: [PATCH 0893/3297] arm64: dts: st: Fix SAI addresses on stm32mp251 [ Upstream commit fba4a31a7f3b6b29b01c83180f83e7ed4c398738 ] The second field of SAI register addresses should be within 0x3f0 bytes from the start of the SAI register addresses, the second field describes the ID registers which are at that addrses. Currently, the second field does not match RM, fix it. Fixes: bf26d75a95f1 ("arm64: dts: st: add sai support on stm32mp251") Signed-off-by: Marek Vasut Reviewed-by: Olivier Moysan Link: https://lore.kernel.org/r/20260411130300.19603-1-marex@nabladev.com Signed-off-by: Alexandre Torgue Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/st/stm32mp251.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi index a8e6e0f77b839..e4e6f9f818d81 100644 --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi @@ -1176,7 +1176,7 @@ sai1: sai@40290000 { compatible = "st,stm32mp25-sai"; - reg = <0x40290000 0x4>, <0x4029a3f0 0x10>; + reg = <0x40290000 0x4>, <0x402903f0 0x10>; ranges = <0 0x40290000 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -1210,7 +1210,7 @@ sai2: sai@402a0000 { compatible = "st,stm32mp25-sai"; - reg = <0x402a0000 0x4>, <0x402aa3f0 0x10>; + reg = <0x402a0000 0x4>, <0x402a03f0 0x10>; ranges = <0 0x402a0000 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -1244,7 +1244,7 @@ sai3: sai@402b0000 { compatible = "st,stm32mp25-sai"; - reg = <0x402b0000 0x4>, <0x402ba3f0 0x10>; + reg = <0x402b0000 0x4>, <0x402b03f0 0x10>; ranges = <0 0x402b0000 0x400>; #address-cells = <1>; #size-cells = <1>; @@ -1336,7 +1336,7 @@ sai4: sai@40340000 { compatible = "st,stm32mp25-sai"; - reg = <0x40340000 0x4>, <0x4034a3f0 0x10>; + reg = <0x40340000 0x4>, <0x403403f0 0x10>; ranges = <0 0x40340000 0x400>; #address-cells = <1>; #size-cells = <1>; From 4779f435627b2b22757a37926b843d98e32aa97a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 29 May 2026 15:43:10 +0200 Subject: [PATCH 0894/3297] RDMA/umem: Add ib_umem_is_contiguous() stub for !CONFIG_INFINIBAND_USER_MEM [ Upstream commit 2cc10972f5f4f123e5a7658824db4f7b5abfc410 ] ib_umem_is_contiguous() is defined under #ifdef CONFIG_INFINIBAND_USER_MEM, but the #else branch lacks a stub. Add the missing inline to fix potential broken build. Fixes: c897c2c8b8e8 ("RDMA/core: Add umem "is_contiguous" and "start_dma_addr" helpers") Link: https://patch.msgid.link/r/20260529134312.2836341-15-jiri@resnulli.us Signed-off-by: Jiri Pirko Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- include/rdma/ib_umem.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 113a5a230176d..5122f78467675 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -181,6 +181,10 @@ static inline unsigned long ib_umem_find_best_pgoff(struct ib_umem *umem, { return 0; } +static inline bool ib_umem_is_contiguous(struct ib_umem *umem) +{ + return false; +} static inline struct ib_umem_dmabuf *ib_umem_dmabuf_get(struct ib_device *device, unsigned long offset, From 02558c86b6b761063e9399e6b939984500327ef1 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 18 May 2026 21:50:39 +0000 Subject: [PATCH 0895/3297] RDMA/rxe: Fix TOCTOU heap overflow in get_srq_wqe [ Upstream commit 22b8fbded65b8c441b634a185f8da67657df6c50 ] get_srq_wqe() reads wqe->dma.num_sge from the shared receive queue buffer, which is mapped into userspace. It validates num_sge against max_sge, but then re-reads the same field to calculate the memcpy size. A concurrent userspace thread can modify num_sge between validation and use, causing a heap buffer overflow when copying the WQE into qp->resp.srq_wqe. Read num_sge into a local variable and use it for both the bounds check and the size calculation. Fixes: 8700e3e7c485 ("Soft RoCE driver") Link: https://patch.msgid.link/r/20260518215040.1598586-2-tristan@talencesecurity.com Signed-off-by: Tristan Madani Reviewed-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_resp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 09ba21d0f3c4f..1266929746889 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -263,6 +263,7 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp) struct rxe_recv_wqe *wqe; struct ib_event ev; unsigned int count; + unsigned int num_sge; size_t size; unsigned long flags; @@ -278,12 +279,13 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp) } /* don't trust user space data */ - if (unlikely(wqe->dma.num_sge > srq->rq.max_sge)) { + num_sge = wqe->dma.num_sge; + if (unlikely(num_sge > srq->rq.max_sge)) { spin_unlock_irqrestore(&srq->rq.consumer_lock, flags); rxe_dbg_qp(qp, "invalid num_sge in SRQ entry\n"); return RESPST_ERR_MALFORMED_WQE; } - size = sizeof(*wqe) + wqe->dma.num_sge*sizeof(struct rxe_sge); + size = sizeof(*wqe) + num_sge * sizeof(struct rxe_sge); memcpy(&qp->resp.srq_wqe, wqe, size); qp->resp.wqe = &qp->resp.srq_wqe.wqe; From 5420eebf3b3c162bfaf965f30e61cd1d689e5732 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 18 May 2026 21:50:40 +0000 Subject: [PATCH 0896/3297] RDMA/rxe: Copy WQE to local buffer in non-SRQ receive path [ Upstream commit d6ab440240a04b8737ee4c7bb21af9182e451733 ] For non-SRQ QPs, the responder reads WQE fields directly from the shared queue buffer mapped into userspace. This allows a malicious user to modify fields like num_sge or sge entries while the kernel is processing the WQE, leading to out-of-bounds reads in rxe_resp_check_length() and copy_data(). Introduce get_recv_wqe() that validates num_sge and copies the WQE to a kernel-local buffer before processing, matching the approach already used for SRQ WQEs in get_srq_wqe(). The srq_wqe buffer is reused since SRQ and non-SRQ paths are mutually exclusive per QP. Fixes: 8700e3e7c485 ("Soft RoCE driver") Link: https://patch.msgid.link/r/20260518215040.1598586-3-tristan@talencesecurity.com Signed-off-by: Tristan Madani Reviewed-by: Zhu Yanjun Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/rxe/rxe_resp.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 1266929746889..995805e16d78b 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -309,6 +309,29 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp) return RESPST_CHK_LENGTH; } +static enum resp_states rxe_get_recv_wqe(struct rxe_qp *qp) +{ + struct rxe_queue *q = qp->rq.queue; + struct rxe_recv_wqe *wqe; + unsigned int num_sge; + size_t size; + + wqe = queue_head(q, QUEUE_TYPE_FROM_CLIENT); + if (!wqe) + return RESPST_ERR_RNR; + + num_sge = wqe->dma.num_sge; + if (unlikely(num_sge > qp->rq.max_sge)) { + rxe_dbg_qp(qp, "invalid num_sge in recv WQE\n"); + return RESPST_ERR_MALFORMED_WQE; + } + size = sizeof(*wqe) + num_sge * sizeof(struct rxe_sge); + memcpy(&qp->resp.srq_wqe, wqe, size); + + qp->resp.wqe = &qp->resp.srq_wqe.wqe; + return RESPST_CHK_LENGTH; +} + static enum resp_states check_resource(struct rxe_qp *qp, struct rxe_pkt_info *pkt) { @@ -329,9 +352,7 @@ static enum resp_states check_resource(struct rxe_qp *qp, if (srq) return get_srq_wqe(qp); - qp->resp.wqe = queue_head(qp->rq.queue, - QUEUE_TYPE_FROM_CLIENT); - return (qp->resp.wqe) ? RESPST_CHK_LENGTH : RESPST_ERR_RNR; + return rxe_get_recv_wqe(qp); } return RESPST_CHK_LENGTH; From f8f48c851a0d24e3ef4f9b95701e7774dd70ca5d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 7 Apr 2026 01:15:52 +0300 Subject: [PATCH 0897/3297] Revert "media: venus: hfi_platform: Correct supported codecs for sc7280" [ Upstream commit 3eb9ba0da0ab73e135f93ffccf07381ba11f100e ] This reverts commit c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280"). The codecs might be deprecated, but they still work (somewhat) perfectly and don't cause any issues with the rest of the system. Reenable VP8 codecs by reverting the offending commit. Tested with fluster: |Test|FFmpeg-VP8-v4l2m2m|GStreamer-VP8-V4L2| |TOTAL|50/61|50/61| |TOTAL TIME|12.171s|11.824s| Fixes: c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280") Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- .../media/platform/qcom/venus/hfi_parser.c | 6 ++--- .../media/platform/qcom/venus/hfi_platform.c | 24 ------------------- .../media/platform/qcom/venus/hfi_platform.h | 2 -- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 92765f9c88730..c4cf6cd50a9a0 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -268,7 +268,6 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) const struct hfi_plat_caps *caps = NULL; u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; - int ret; plat = hfi_platform_get(core->res->hfi_version); if (!plat) @@ -277,9 +276,8 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) if (inst) return 0; - ret = hfi_platform_get_codecs(core, &enc_codecs, &dec_codecs, &count); - if (ret) - return ret; + if (plat->codecs) + plat->codecs(core, &enc_codecs, &dec_codecs, &count); if (plat->capabilities) caps = plat->capabilities(core, &entries); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index cde7f93045ac4..f19572ab1d161 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -2,9 +2,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ -#include #include "hfi_platform.h" -#include "core.h" const struct hfi_platform *hfi_platform_get(enum hfi_version version) { @@ -73,25 +71,3 @@ hfi_platform_get_codec_lp_freq(struct venus_core *core, return freq; } - -int -hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count) -{ - const struct hfi_platform *plat; - - plat = hfi_platform_get(core->res->hfi_version); - if (!plat) - return -EINVAL; - - if (plat->codecs) - plat->codecs(core, enc_codecs, dec_codecs, count); - - if (IS_IRIS2_1(core)) { - *enc_codecs &= ~HFI_VIDEO_CODEC_VP8; - *dec_codecs &= ~HFI_VIDEO_CODEC_VP8; - } - - return 0; -} - diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 5e4f8013a6b1d..a0b6d19f3e1a0 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -74,6 +74,4 @@ unsigned long hfi_platform_get_codec_vsp_freq(struct venus_core *core, unsigned long hfi_platform_get_codec_lp_freq(struct venus_core *core, enum hfi_version version, u32 codec, u32 session_type); -int hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count); #endif From ffe754288750ae9e3b8f28227bbabaa43e3206c7 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:07 +0530 Subject: [PATCH 0898/3297] media: qcom: venus: drop extra padding in NV12 raw size calculation [ Upstream commit e1c9adabb268cc5d56723b7df1da49e59070f309 ] get_framesize_raw_nv12() currently adds SZ_4K to the UV plane size and an additional SZ_8K to the total buffer size. This inflates the calculated sizeimage and leads userspace to over-allocate buffers without a clear requirement. Remove the extra SZ_4K/SZ_8K padding and compute the NV12 size as the sum of Y and UV planes, keeping the final ALIGN(size, SZ_4K) intact. Fixes: e1cb72de702ad ("media: venus: helpers: move frame size calculations on common place") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- drivers/media/platform/qcom/venus/helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 2e4363f822317..7f370caacd9f8 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -954,8 +954,8 @@ static u32 get_framesize_raw_nv12(u32 width, u32 height) uv_sclines = ALIGN(((height + 1) >> 1), 16); y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; + uv_plane = uv_stride * uv_sclines; + size = y_plane + uv_plane; return ALIGN(size, SZ_4K); } From bc7c166cc1012fa94a343172007c727667075814 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:08 +0530 Subject: [PATCH 0899/3297] media: qcom: venus: relax encoder frame/blur dimension steps on v4 [ Upstream commit 35428ae3a6a40912f1f7b47bb6c65f1a63d0b8f8 ] Encoder HFI capabilities on v4 advertise a 16-pixel step for frame and blur dimensions. This is overly restrictive and can cause userspace caps negotiation to fail even for valid resolutions. Relax the advertised step size to 1 and keep alignment enforcement in buffer layout and size calculations. Fixes: 8b88cabef404e ("media: venus: hfi_plat_v4: Populate codecs and capabilities for v4") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- .../platform/qcom/venus/hfi_platform_v4.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c index cda888b56b5d4..e0b3652bb4409 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -136,8 +136,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_H264, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From ff4e38a37ba531b06dc5c23b9da8ffa31f9b3095 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:09 +0530 Subject: [PATCH 0900/3297] media: qcom: venus: relax encoder frame/blur step size on v6 [ Upstream commit c53e0550288b2e08b984b24035c471941b7820c7 ] Encoder HFI capabilities on v6 enforce a 16-pixel step for frame and blur dimensions, which does not reflect actual hardware requirements and can reject valid userspace configurations. Relax the step size to 1 while leaving min/max limits unchanged. Fixes: 869d77e706290 ("media: venus: hfi_plat_v6: Populate capabilities for v6") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin --- .../media/platform/qcom/venus/hfi_platform_v6.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c index d8568c08cc361..fb8d10ab34043 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From b41923dbf67690850b56359d0de8705172d78642 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:21 +0200 Subject: [PATCH 0901/3297] amba: use generic driver_override infrastructure [ Upstream commit 1947229f5f2a8d4ecf8c971aca68a1242bb7b37c ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: 3cf385713460 ("ARM: 8256/1: driver coamba: add device binding path 'driver_override'") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-2-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/amba/bus.c | 37 ++++++------------------------------- include/linux/amba/bus.h | 5 ----- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 74e34a07ef72f..d58f9550f8775 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -82,33 +82,6 @@ static void amba_put_disable_pclk(struct amba_device *pcdev) } -static ssize_t driver_override_show(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct amba_device *dev = to_amba_device(_dev); - ssize_t len; - - device_lock(_dev); - len = sprintf(buf, "%s\n", dev->driver_override); - device_unlock(_dev); - return len; -} - -static ssize_t driver_override_store(struct device *_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct amba_device *dev = to_amba_device(_dev); - int ret; - - ret = driver_set_override(_dev, &dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} -static DEVICE_ATTR_RW(driver_override); - #define amba_attr_func(name,fmt,arg...) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ @@ -126,7 +99,6 @@ amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n", static struct attribute *amba_dev_attrs[] = { &dev_attr_id.attr, &dev_attr_resource.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(amba_dev); @@ -209,10 +181,11 @@ static int amba_match(struct device *dev, const struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); const struct amba_driver *pcdrv = to_amba_driver(drv); + int ret; mutex_lock(&pcdev->periphid_lock); if (!pcdev->periphid) { - int ret = amba_read_periphid(pcdev); + ret = amba_read_periphid(pcdev); /* * Returning any error other than -EPROBE_DEFER from bus match @@ -230,8 +203,9 @@ static int amba_match(struct device *dev, const struct device_driver *drv) mutex_unlock(&pcdev->periphid_lock); /* When driver_override is set, only bind to the matching driver */ - if (pcdev->driver_override) - return !strcmp(pcdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; return amba_lookup(pcdrv->id_table, pcdev) != NULL; } @@ -439,6 +413,7 @@ static const struct dev_pm_ops amba_pm = { const struct bus_type amba_bustype = { .name = "amba", .dev_groups = amba_dev_groups, + .driver_override = true, .match = amba_match, .uevent = amba_uevent, .probe = amba_probe, diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 9946276aff737..6c54d5c0d21f7 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -71,11 +71,6 @@ struct amba_device { unsigned int cid; struct amba_cs_uci_id uci; unsigned int irq[AMBA_NR_IRQS]; - /* - * Driver name to force a match. Do not set directly, because core - * frees it. Use driver_set_override() to set or clear it. - */ - const char *driver_override; }; struct amba_driver { From d2cf52ba2803bb2b7c2423678579e28b46fc0914 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:22 +0200 Subject: [PATCH 0902/3297] cdx: use generic driver_override infrastructure [ Upstream commit d541aa1897f67f4f14c805785bff894bcc61dca1 ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: 2959ab247061 ("cdx: add the cdx bus driver") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-3-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/cdx/cdx.c | 40 +++++-------------------------------- include/linux/cdx/cdx_bus.h | 4 ---- 2 files changed, 5 insertions(+), 39 deletions(-) diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 3d50f8cd9c0bd..9d6d24f49938b 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -156,8 +156,6 @@ static int cdx_unregister_device(struct device *dev, } else { cdx_destroy_res_attr(cdx_dev, MAX_CDX_DEV_RESOURCES); debugfs_remove_recursive(cdx_dev->debugfs_dir); - kfree(cdx_dev->driver_override); - cdx_dev->driver_override = NULL; } /* @@ -268,6 +266,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) const struct cdx_driver *cdx_drv = to_cdx_driver(drv); const struct cdx_device_id *found_id = NULL; const struct cdx_device_id *ids; + int ret; if (cdx_dev->is_bus) return false; @@ -275,7 +274,8 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) ids = cdx_drv->match_id_table; /* When driver_override is set, only bind to the matching driver */ - if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name)) + ret = device_match_driver_override(dev, drv); + if (ret == 0) return false; found_id = cdx_match_id(ids, cdx_dev); @@ -289,7 +289,7 @@ static int cdx_bus_match(struct device *dev, const struct device_driver *drv) */ if (!found_id->override_only) return true; - if (cdx_dev->driver_override) + if (ret > 0) return true; ids = found_id + 1; @@ -453,36 +453,6 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(modalias); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - int ret; - - if (WARN_ON(dev->bus != &cdx_bus_type)) - return -EINVAL; - - ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cdx_device *cdx_dev = to_cdx_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", cdx_dev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -552,7 +522,6 @@ static struct attribute *cdx_dev_attrs[] = { &dev_attr_class.attr, &dev_attr_revision.attr, &dev_attr_modalias.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -653,6 +622,7 @@ ATTRIBUTE_GROUPS(cdx_bus); struct bus_type cdx_bus_type = { .name = "cdx", + .driver_override = true, .match = cdx_bus_match, .probe = cdx_probe, .remove = cdx_remove, diff --git a/include/linux/cdx/cdx_bus.h b/include/linux/cdx/cdx_bus.h index 79bb80e567908..527f8851f0cde 100644 --- a/include/linux/cdx/cdx_bus.h +++ b/include/linux/cdx/cdx_bus.h @@ -137,9 +137,6 @@ struct cdx_controller { * @enabled: is this bus enabled * @msi_dev_id: MSI Device ID associated with CDX device * @num_msi: Number of MSI's supported by the device - * @driver_override: driver name to force a match; do not set directly, - * because core frees it; use driver_set_override() to - * set or clear it. * @irqchip_lock: lock to synchronize irq/msi configuration * @msi_write_pending: MSI write pending for this device */ @@ -165,7 +162,6 @@ struct cdx_device { bool enabled; u32 msi_dev_id; u32 num_msi; - const char *driver_override; struct mutex irqchip_lock; bool msi_write_pending; }; From 0e2f0833556c8c5269deb457b89d538644a3b747 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:23 +0200 Subject: [PATCH 0903/3297] Drivers: hv: vmbus: use generic driver_override infrastructure [ Upstream commit 331d8900121a1d74ecd45cd2db742ddcb5a0a565 ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Tested-by: Michael Kelley Reviewed-by: Michael Kelley Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: d765edbb301c ("vmbus: add driver_override support") Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-4-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/hv/vmbus_drv.c | 43 ++++++++++-------------------------------- include/linux/hyperv.h | 5 ----- 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 137cf82e2292e..1a0e350c19404 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -550,34 +550,6 @@ static ssize_t device_show(struct device *dev, } static DEVICE_ATTR_RO(device); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - int ret; - - ret = driver_set_override(dev, &hv_dev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", hv_dev->driver_override); - device_unlock(dev); - - return len; -} -static DEVICE_ATTR_RW(driver_override); - /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_id.attr, @@ -608,7 +580,6 @@ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_channel_vp_mapping.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, - &dev_attr_driver_override.attr, NULL, }; @@ -720,9 +691,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * { const guid_t *guid = &dev->dev_type; const struct hv_vmbus_device_id *id; + int ret; - /* When driver_override is set, only bind to the matching driver */ - if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + /* If a driver override is set, only bind to the matching driver */ + ret = device_match_driver_override(&dev->device, &drv->driver); + if (ret == 0) return NULL; /* Look at the dynamic ids first, before the static ones */ @@ -730,8 +703,11 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(const struct hv_driver * if (!id) id = hv_vmbus_dev_match(drv->id_table, guid); - /* driver_override will always match, send a dummy id */ - if (!id && dev->driver_override) + /* + * If there's a matching driver override, this function should succeed, + * thus return a dummy device ID if no matching ID is found. + */ + if (!id && ret > 0) id = &vmbus_device_null; return id; @@ -1033,6 +1009,7 @@ static const struct dev_pm_ops vmbus_pm = { /* The one and only one */ static const struct bus_type hv_bus = { .name = "vmbus", + .driver_override = true, .match = vmbus_match, .shutdown = vmbus_shutdown, .remove = vmbus_remove, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b0502a336eb3a..be9946a2cff2a 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1272,11 +1272,6 @@ struct hv_device { u16 device_id; struct device device; - /* - * Driver name to force a match. Do not set directly, because core - * frees it. Use driver_set_override() to set or clear it. - */ - const char *driver_override; struct vmbus_channel *channel; struct kset *channels_kset; From e04e384274f83accb6af33b092152ad6ac946596 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 5 May 2026 15:37:24 +0200 Subject: [PATCH 0904/3297] rpmsg: use generic driver_override infrastructure [ Upstream commit 55ced13c42921714e90f8fae94b6ed803330dc6a ] When a driver is probed through __driver_attach(), the bus' match() callback is called without the device lock held, thus accessing the driver_override field without a lock, which can cause a UAF. Fix this by using the driver-core driver_override infrastructure taking care of proper locking internally. Note that calling match() from __driver_attach() without the device lock held is intentional. [1] Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1] Reported-by: Gui-Dong Han Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789 Fixes: e95060478244 ("rpmsg: Introduce a driver override mechanism") Reviewed-by: Mathieu Poirier Reviewed-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20260505133935.3772495-5-dakr@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/rpmsg/qcom_glink_native.c | 2 -- drivers/rpmsg/rpmsg_core.c | 43 +++++-------------------------- drivers/rpmsg/virtio_rpmsg_bus.c | 1 - include/linux/rpmsg.h | 4 --- 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 3a15d9d108089..833ff9cb8afe6 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1618,7 +1618,6 @@ static void qcom_glink_rpdev_release(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); - kfree(rpdev->driver_override); kfree(rpdev); } @@ -1870,7 +1869,6 @@ static void qcom_glink_device_release(struct device *dev) /* Release qcom_glink_alloc_channel() reference */ kref_put(&channel->refcount, qcom_glink_channel_release); - kfree(rpdev->driver_override); kfree(rpdev); } diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 96964745065b1..2b9f6d5a9a4fc 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -358,33 +358,6 @@ rpmsg_show_attr(src, src, "0x%x\n"); rpmsg_show_attr(dst, dst, "0x%x\n"); rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); -static ssize_t driver_override_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - int ret; - - ret = driver_set_override(dev, &rpdev->driver_override, buf, count); - if (ret) - return ret; - - return count; -} - -static ssize_t driver_override_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct rpmsg_device *rpdev = to_rpmsg_device(dev); - ssize_t len; - - device_lock(dev); - len = sysfs_emit(buf, "%s\n", rpdev->driver_override); - device_unlock(dev); - return len; -} -static DEVICE_ATTR_RW(driver_override); - static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -405,7 +378,6 @@ static struct attribute *rpmsg_dev_attrs[] = { &dev_attr_dst.attr, &dev_attr_src.attr, &dev_attr_announce.attr, - &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(rpmsg_dev); @@ -424,9 +396,11 @@ static int rpmsg_dev_match(struct device *dev, const struct device_driver *drv) const struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); const struct rpmsg_device_id *ids = rpdrv->id_table; unsigned int i; + int ret; - if (rpdev->driver_override) - return !strcmp(rpdev->driver_override, drv->name); + ret = device_match_driver_override(dev, drv); + if (ret >= 0) + return ret; if (ids) for (i = 0; ids[i].name[0]; i++) @@ -535,6 +509,7 @@ static const struct bus_type rpmsg_bus = { .name = "rpmsg", .match = rpmsg_dev_match, .dev_groups = rpmsg_dev_groups, + .driver_override = true, .uevent = rpmsg_uevent, .probe = rpmsg_dev_probe, .remove = rpmsg_dev_remove, @@ -560,11 +535,9 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, device_initialize(dev); if (driver_override) { - ret = driver_set_override(dev, &rpdev->driver_override, - driver_override, - strlen(driver_override)); + ret = device_set_driver_override(dev, driver_override); if (ret) { - dev_err(dev, "device_set_override failed: %d\n", ret); + dev_err(dev, "device_set_driver_override() failed: %d\n", ret); put_device(dev); return ret; } @@ -573,8 +546,6 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev, ret = device_add(dev); if (ret) { dev_err(dev, "device_add failed: %d\n", ret); - kfree(rpdev->driver_override); - rpdev->driver_override = NULL; put_device(dev); } diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 484890b4a6a74..d4fce3ac1c88d 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -372,7 +372,6 @@ static void virtio_rpmsg_release_device(struct device *dev) struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); - kfree(rpdev->driver_override); kfree(vch); } diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index fb7ab91656458..c2e3ef8480d55 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -41,9 +41,6 @@ struct rpmsg_channel_info { * rpmsg_device - device that belong to the rpmsg bus * @dev: the device struct * @id: device id (used to match between rpmsg drivers and devices) - * @driver_override: driver name to force a match; do not set directly, - * because core frees it; use driver_set_override() to - * set or clear it. * @src: local address * @dst: destination address * @ept: the rpmsg endpoint of this channel @@ -53,7 +50,6 @@ struct rpmsg_channel_info { struct rpmsg_device { struct device dev; struct rpmsg_device_id id; - const char *driver_override; u32 src; u32 dst; struct rpmsg_endpoint *ept; From b7313f23ea5a79b199a007bfad64a866cc2c22e7 Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Fri, 15 May 2026 17:30:19 +0800 Subject: [PATCH 0905/3297] md/raid10: reset read_slot when reusing r10bio for discard [ Upstream commit 6b8a26af065ddc93de2aa5c9f0df98dce9723442 ] put_all_bios() always drops devs[i].bio, but it only drops devs[i].repl_bio when r10_bio->read_slot < 0. If discard reuses an r10bio that was previously used for a read, read_slot can still be non-negative, and discard cleanup can skip bio_put() on repl_bio. Reset read_slot to -1 when preparing an r10bio for discard so the replacement bio is always released correctly. Fixes: d30588b2731f ("md/raid10: improve raid10 discard request") Signed-off-by: Chen Cheng Reviewed-by: Xiao Ni Link: https://patch.msgid.link/20260515093019.3436882-1-chencheng@fnnas.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid10.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 2815c05d1c9f0..4a8814fd75cd0 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1727,6 +1727,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio) r10_bio->mddev = mddev; r10_bio->state = 0; r10_bio->sectors = 0; + r10_bio->read_slot = -1; memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks); wait_blocked_dev(mddev, r10_bio); From 5ac9e793ba2583d72740d929e7858a6c82e22ed5 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Sat, 30 May 2026 15:14:11 +0000 Subject: [PATCH 0906/3297] raid1: fix nr_pending leak in REQ_ATOMIC bad-block error path [ Upstream commit 909d9dc3b5730c8ed7b764c68bc788342df2a07b ] In raid1_write_request(), each per-mirror loop iteration begins by incrementing rdev->nr_pending. If a REQ_ATOMIC write encounters a badblock within the requested range, the code jumps to err_handle without dropping the reference taken for the current mirror. err_handle's cleanup loop will only decrements for k < i and r1_bio->bios[k] is non-NULL. The current slot is therefore skipped, leaving its nr_pending reference leaked permanently. The reference prevents the rdev from ever being removed, since raid1_remove_conf() refuses to remove an rdev with nr_pending > 0. Fix this by calling rdev_dec_pending() before jumping to err_handle. Fixes: f2a38abf5f1c ("md/raid1: Atomic write support") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260530151411.4119-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 84dbf801e9b1b..df01070eb0c29 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1581,8 +1581,10 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, * complexity of supporting that is not worth * the benefit. */ - if (bio->bi_opf & REQ_ATOMIC) + if (bio->bi_opf & REQ_ATOMIC) { + rdev_dec_pending(rdev, mddev); goto err_handle; + } good_sectors = first_bad - r1_bio->sector; if (good_sectors < max_sectors) From a7131340d0f95df9a541257dccab5d85e6bcdd2b Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Sun, 31 May 2026 15:55:59 +0800 Subject: [PATCH 0907/3297] bpf: fix BPF_PROG_QUERY OOB write and cgroup backward compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 21c4b99b27f3f85b89256e81b3e997dec0a460d0 ] BPF_PROG_QUERY writes back the 'query.revision' field unconditionally to userspace. If userspace passes a smaller 'bpf_attr' structure (e.g. 40 bytes, which was the layout before the addition of 'query.revision'), the kernel performs an out-of-bounds write. Fix this by propagating the user-provided attribute size 'uattr_size' down to the cgroup query handlers, and conditionally skipping writing the revision field to userspace when the provided buffer size is insufficient. query.revision in bpf_mprog_query is structurally identical to the cgroup case: a late tail field, written unconditionally. But the backward-compat hazard is not the same. The min-historical-size test is per command, and bpf_mprog_query only serves attach types that were born with revision in the struct: - tcx_prog_query -> BPF_TCX_INGRESS/EGRESS - netkit_prog_query -> BPF_NETKIT_PRIMARY/PEER tcx, netkit, the revision field, and bpf_mprog_query itself all landed in the same v6.6 merge window (053c8e1f235d added the mprog query API + revision; tcx in e420bed02507, netkit in 35dfaad7188c). There has never been a tcx/netkit BPF_PROG_QUERY userspace that doesn't know about revision. So for these commands the minimum legitimate struct already covers offset 56-64 — no old binary can be broken here. Contrast with cgroup: BPF_PROG_QUERY on cgroup attach types shipped in 2017; revision write-back was bolted on years later (120933984460). That path has a real population of pre-revision callers. Fixes: 120933984460 ("bpf: Implement mprog API on top of existing cgroup progs") Cc: Maciej Żenczykowski Cc: Lorenzo Colitti Signed-off-by: Yuyang Huang Link: https://lore.kernel.org/r/20260531075600.4058207-2-yuyanghuang@google.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/linux/bpf-cgroup.h | 5 +++-- kernel/bpf/cgroup.c | 13 +++++++------ kernel/bpf/syscall.c | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index aedf573bdb426..27b207ba8bb8c 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -418,7 +418,7 @@ int cgroup_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype); int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); int cgroup_bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr); + union bpf_attr __user *uattr, u32 uattr_size); const struct bpf_func_proto * cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog); @@ -449,7 +449,8 @@ static inline int cgroup_bpf_link_attach(const union bpf_attr *attr, } static inline int cgroup_bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, + u32 uattr_size) { return -EINVAL; } diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index d5eed35b376ab..008b3a9be0c41 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1208,7 +1208,7 @@ static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, /* Must be called with cgroup_mutex held to avoid races. */ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { __u32 __user *prog_attach_flags = u64_to_user_ptr(attr->query.prog_attach_flags); bool effective_query = attr->query.query_flags & BPF_F_QUERY_EFFECTIVE; @@ -1259,7 +1259,8 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, return -EFAULT; if (!effective_query && from_atype == to_atype) revision = cgrp->bpf.revisions[from_atype]; - if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision))) + if (uattr_size >= offsetofend(union bpf_attr, query.revision) && + copy_to_user(&uattr->query.revision, &revision, sizeof(revision))) return -EFAULT; if (attr->query.prog_cnt == 0 || !prog_ids || !total_cnt) /* return early if user requested only program count + flags */ @@ -1312,12 +1313,12 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, } static int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { int ret; cgroup_lock(); - ret = __cgroup_bpf_query(cgrp, attr, uattr); + ret = __cgroup_bpf_query(cgrp, attr, uattr, uattr_size); cgroup_unlock(); return ret; } @@ -1520,7 +1521,7 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) } int cgroup_bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { struct cgroup *cgrp; int ret; @@ -1529,7 +1530,7 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr, if (IS_ERR(cgrp)) return PTR_ERR(cgrp); - ret = cgroup_bpf_query(cgrp, attr, uattr); + ret = cgroup_bpf_query(cgrp, attr, uattr, uattr_size); cgroup_put(cgrp); return ret; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ff268fd2ff8b5..c27a0d66b420e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -4625,7 +4625,7 @@ static int bpf_prog_detach(const union bpf_attr *attr) #define BPF_PROG_QUERY_LAST_FIELD query.revision static int bpf_prog_query(const union bpf_attr *attr, - union bpf_attr __user *uattr) + union bpf_attr __user *uattr, u32 uattr_size) { if (!bpf_net_capable()) return -EPERM; @@ -4664,7 +4664,7 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_CGROUP_GETSOCKOPT: case BPF_CGROUP_SETSOCKOPT: case BPF_LSM_CGROUP: - return cgroup_bpf_prog_query(attr, uattr); + return cgroup_bpf_prog_query(attr, uattr, uattr_size); case BPF_LIRC_MODE2: return lirc_prog_query(attr, uattr); case BPF_FLOW_DISSECTOR: @@ -6189,7 +6189,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size) err = bpf_prog_detach(&attr); break; case BPF_PROG_QUERY: - err = bpf_prog_query(&attr, uattr.user); + err = bpf_prog_query(&attr, uattr.user, size); break; case BPF_PROG_TEST_RUN: err = bpf_prog_test_run(&attr, uattr.user); From a441c0794ac28d546628e93daf44237ecf441104 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Sun, 31 May 2026 15:56:00 +0800 Subject: [PATCH 0908/3297] selftests/bpf: add verification for BPF_PROG_QUERY attr size boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5add3a4ad1a3bc15404e8bd338813ed0a636f5c9 ] Add a new selftest to verify that the BPF syscall (specifically BPF_PROG_QUERY) correctly handles different user-declared attribute sizes. Specifically, verify that: - For cgroup queries, a query with a size that covers 'prog_cnt' but is smaller than 'revision' (OLD_QUERY_SIZE) succeeds, but does not write to 'revision' (verifying backward compatibility). - A query with full size (FULL_QUERY_SIZE) succeeds and writes both 'prog_cnt' and 'revision'. Fixes: 120933984460 ("bpf: Implement mprog API on top of existing cgroup progs") Cc: Maciej Żenczykowski Cc: Lorenzo Colitti Signed-off-by: Yuyang Huang Link: https://lore.kernel.org/r/20260531075600.4058207-3-yuyanghuang@google.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- .../selftests/bpf/prog_tests/bpf_attr_size.c | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c b/tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c new file mode 100644 index 0000000000000..32159dc64da8b --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/bpf_attr_size.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Google LLC */ +#include +#include +#include +#include +#include +#include "cgroup_skb_direct_packet_access.skel.h" + +#define OLD_QUERY_SIZE offsetofend(union bpf_attr, query.prog_cnt) +#define FULL_QUERY_SIZE offsetofend(union bpf_attr, query.revision) + +static void test_query_size_boundaries(void) +{ + struct cgroup_skb_direct_packet_access *skel; + struct bpf_link *link = NULL; + union bpf_attr attr; + int cg_fd = -1; + int err; + + skel = cgroup_skb_direct_packet_access__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_load")) + return; + + cg_fd = test__join_cgroup("/attr_size_cg"); + if (!ASSERT_GE(cg_fd, 0, "join_cgroup")) + goto cleanup; + + link = bpf_program__attach_cgroup(skel->progs.direct_packet_access, + cg_fd); + if (!ASSERT_OK_PTR(link, "cg_attach")) + goto cleanup; + + memset(&attr, 0, sizeof(attr)); + attr.query.target_fd = cg_fd; + attr.query.attach_type = BPF_CGROUP_INET_INGRESS; + attr.query.revision = 0xdeadbeefdeadbeefULL; + + err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, OLD_QUERY_SIZE); + if (ASSERT_OK(err, "query_old_size")) { + ASSERT_EQ(attr.query.prog_cnt, 1, "prog_cnt_written_old"); + ASSERT_EQ(attr.query.revision, 0xdeadbeefdeadbeefULL, + "revision_not_written_old"); + } + + memset(&attr, 0, sizeof(attr)); + attr.query.target_fd = cg_fd; + attr.query.attach_type = BPF_CGROUP_INET_INGRESS; + + err = syscall(__NR_bpf, BPF_PROG_QUERY, &attr, FULL_QUERY_SIZE); + if (!ASSERT_OK(err, "query_full_size")) + goto cleanup; + + ASSERT_EQ(attr.query.prog_cnt, 1, "prog_cnt_written"); + ASSERT_GT(attr.query.revision, 0, "revision_written"); + +cleanup: + if (link) + bpf_link__destroy(link); + if (cg_fd >= 0) + close(cg_fd); + cgroup_skb_direct_packet_access__destroy(skel); +} + +void test_bpf_attr_size(void) +{ + if (test__start_subtest("query_size_boundaries")) + test_query_size_boundaries(); +} From f3389fbaff1a1017da0e79cb95594f80f493391d Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 29 May 2026 11:41:17 +0200 Subject: [PATCH 0909/3297] libbpf: Skip hash computation when loader generation failed [ Upstream commit 3c5e2f1a85844abbb65df4694f5ebad0a13e219c ] bpf_gen__finish() calls compute_sha_update_offsets() gated only on the gen_hash option, without first consulting gen->error. On a failed generation this is buggy: a failed realloc_data_buf() sets gen->data_start to NULL (leaving gen->data_cur dangling), so compute_sha_update_offsets() runs libbpf_sha256() over a NULL buffer with a bogus length; a failed realloc_insn_buf() likewise sets gen->insn_start to NULL and the hash immediates get patched through that NULL base. The computed program is discarded in either case, since the following "if (!gen->error)" block does not publish opts->insns once an error is set. Thus, skip the hash pass when generation has already failed. Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader") Reported-by: sashiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260529094119.307264-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index cd5c2543f54d8..b2e8b4224640f 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -398,13 +398,12 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) blob_fd_array_off(gen, i)); emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0)); emit(gen, BPF_EXIT_INSN()); - if (OPTS_GET(gen->opts, gen_hash, false)) - compute_sha_update_offsets(gen); - - pr_debug("gen: finish %s\n", errstr(gen->error)); if (!gen->error) { struct gen_loader_opts *opts = gen->opts; + if (OPTS_GET(opts, gen_hash, false)) + compute_sha_update_offsets(gen); + opts->insns = gen->insn_start; opts->insns_sz = gen->insn_cur - gen->insn_start; opts->data = gen->data_start; @@ -419,6 +418,7 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) bpf_insn_bswap(insn++); } } + pr_debug("gen: finish %s\n", errstr(gen->error)); return gen->error; } From 803087a16a4eab1980a68ceb3967e3a05e8d2035 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 29 May 2026 18:28:29 +0200 Subject: [PATCH 0910/3297] libbpf: Skip endianness swap when loader generation failed [ Upstream commit 41300d032a1b1d91a3ed996ad21905463e344beb ] bpf_gen__prog_load() byte-swaps the program insns and the {func,line}_info and CO-RE relo blobs in place for cross-endian targets. The blob offsets come from add_data(), which returns 0 on failure: realloc_data_buf() either frees and NULLs gen->data_start (realloc OOM) or returns early on an already-latched gen->error, leaving a stale, possibly too-small buffer. Neither bswap site checked for this. With gen->swapped_endian set and a failed generation, "gen->data_start + off" becomes NULL + 0. Guard the same way via !gen->error so they are skipped once generation has failed. Fixes: 8ca3323dce43 ("libbpf: Support creating light skeleton of either endianness") Reported-by: sashiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260529162829.315921-1-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index b2e8b4224640f..637b26d1100bb 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -1054,7 +1054,7 @@ void bpf_gen__prog_load(struct bpf_gen *gen, prog_idx, prog_type, insns_off, insn_cnt, license_off); /* convert blob insns to target endianness */ - if (gen->swapped_endian) { + if (gen->swapped_endian && !gen->error) { struct bpf_insn *insn = gen->data_start + insns_off; int i; @@ -1092,7 +1092,7 @@ void bpf_gen__prog_load(struct bpf_gen *gen, sizeof(struct bpf_core_relo)); /* convert all info blobs to target endianness */ - if (gen->swapped_endian) + if (gen->swapped_endian && !gen->error) info_blob_bswap(gen, func_info, line_info, core_relos, load_attr); libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name)); From ab4d04bf8b2f30032f026a6987c84119d19a6fd4 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Fri, 24 Apr 2026 18:42:01 +0800 Subject: [PATCH 0911/3297] ext4: fix LOGFLUSH shutdown ordering to allow ordered-mode data writeback [ Upstream commit d99748ef1695ce17eaf51c64b7a06952fa7cddab ] In EXT4_GOING_FLAGS_LOGFLUSH mode, the EXT4_FLAGS_SHUTDOWN flag was set before calling ext4_force_commit(). This caused ordered-mode data writeback (triggered by journal commit) to fail with -EIO, since ext4_do_writepages() checks for the shutdown flag. The journal would then be aborted prematurely before the commit could succeed. Fix this by calling ext4_force_commit() first, then setting the shutdown flag, so that pending data can be written back correctly. Note that moving ext4_force_commit() before setting the shutdown flag creates a small window in which new writes may occur and generate new journal transactions. When the journal is subsequently aborted, the new transactions will not be able to write to disk. This is intentional because LOGFLUSH's semantics are to flush pre-existing journal entries before shutdown, not to guarantee atomicity for writes that race with the ioctl. Fixes: 783d94854499 ("ext4: add EXT4_IOC_GOINGDOWN ioctl") Signed-off-by: Zhang Yi Reviewed-by: Baokun Li Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260424104201.1930823-1-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/ioctl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index acc28aa5744b1..fbf5771eca993 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -829,11 +829,17 @@ int ext4_force_shutdown(struct super_block *sb, u32 flags) bdev_thaw(sb->s_bdev); break; case EXT4_GOING_FLAGS_LOGFLUSH: + /* + * Call ext4_force_commit() before setting EXT4_FLAGS_SHUTDOWN. + * This is because in data=ordered mode, journal commit + * triggers data writeback which fails if shutdown is already + * set, causing the journal to be aborted prematurely before + * the commit succeeds. + */ + (void) ext4_force_commit(sb); set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); - if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) { - (void) ext4_force_commit(sb); + if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) jbd2_journal_abort(sbi->s_journal, -ESHUTDOWN); - } break; case EXT4_GOING_FLAGS_NOLOGFLUSH: set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); From 8ec64276ecd24ad57031ed04246569c0e4ffbc68 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 22 May 2026 20:40:48 +0800 Subject: [PATCH 0912/3297] spi: atmel: fix DMA channel and bounce buffer leaks [ Upstream commit bd7e9843ec95bffe2643c901dd625f0bab32e639 ] The original code set use_dma to false when dma_alloc_coherent() for bounce buffers failed, but DMA channels acquired earlier via atmel_spi_configure_dma() were never freed. When devm_request_irq() or clk_prepare_enable() failed later in probe, the driver also did not release DMA channels or bounce buffers already allocated. The out_free_dma error path released DMA channels but did not free the bounce buffers. Fix by moving bounce buffer allocation into atmel_spi_configure_dma() and registering the devres cleanup for DMA channels and bounce buffers. Fixes: a9889ed62d06 ("spi: atmel: Implements transfers with bounce buffer") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260522-atmel-v3-1-23f8c6e6aa43@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-atmel.c | 133 ++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 586e2fd357ed9..a9499b51b8cc2 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -559,6 +559,38 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, u8 bits_per_word) return err; } +static void atmel_spi_release_dma(void *data) +{ + struct spi_controller *host = data; + struct atmel_spi *as = spi_controller_get_devdata(host); + struct device *dev = &as->pdev->dev; + + if (host->dma_tx) { + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; + } + + if (host->dma_rx) { + dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + } + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + if (as->addr_tx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_tx_bbuf, + as->dma_addr_tx_bbuf); + as->addr_tx_bbuf = NULL; + } + if (as->addr_rx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_rx_bbuf, + as->dma_addr_rx_bbuf); + as->addr_rx_bbuf = NULL; + } + } +} + static int atmel_spi_configure_dma(struct spi_controller *host, struct atmel_spi *as) { @@ -569,7 +601,8 @@ static int atmel_spi_configure_dma(struct spi_controller *host, if (IS_ERR(host->dma_tx)) { err = PTR_ERR(host->dma_tx); dev_dbg(dev, "No TX DMA channel, DMA is disabled\n"); - goto error_clear; + host->dma_tx = NULL; + return err; } host->dma_rx = dma_request_chan(dev, "rx"); @@ -580,26 +613,45 @@ static int atmel_spi_configure_dma(struct spi_controller *host, * requested tx channel. */ dev_dbg(dev, "No RX DMA channel, DMA is disabled\n"); - goto error; + host->dma_rx = NULL; + goto err_release_dma; } err = atmel_spi_dma_slave_config(as, 8); if (err) - goto error; + goto err_release_dma; + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + as->addr_tx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_tx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_tx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + + as->addr_rx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_rx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_rx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + } + + err = devm_add_action_or_reset(dev, atmel_spi_release_dma, host); + if (err) + return err; dev_info(&as->pdev->dev, - "Using %s (tx) and %s (rx) for DMA transfers\n", - dma_chan_name(host->dma_tx), - dma_chan_name(host->dma_rx)); + "Using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(host->dma_tx), dma_chan_name(host->dma_rx)); return 0; -error: - if (!IS_ERR(host->dma_rx)) - dma_release_channel(host->dma_rx); - if (!IS_ERR(host->dma_tx)) - dma_release_channel(host->dma_tx); -error_clear: - host->dma_tx = host->dma_rx = NULL; + +err_release_dma: + atmel_spi_release_dma(host); + return err; } @@ -611,18 +663,6 @@ static void atmel_spi_stop_dma(struct spi_controller *host) dmaengine_terminate_all(host->dma_tx); } -static void atmel_spi_release_dma(struct spi_controller *host) -{ - if (host->dma_rx) { - dma_release_channel(host->dma_rx); - host->dma_rx = NULL; - } - if (host->dma_tx) { - dma_release_channel(host->dma_tx); - host->dma_tx = NULL; - } -} - /* This function is called by the DMA driver from tasklet context */ static void dma_callback(void *data) { @@ -1586,30 +1626,6 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_pdc = true; } - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_rx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_rx_bbuf) { - as->use_dma = false; - } else { - as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_tx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_tx_bbuf) { - as->use_dma = false; - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } - if (!as->use_dma) - dev_info(host->dev.parent, - " can not allocate dma coherent memory\n"); - } - if (as->caps.has_dma_support && !as->use_dma) dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); @@ -1669,13 +1685,10 @@ static int atmel_spi_probe(struct platform_device *pdev) out_free_dma: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - - if (as->use_dma) - atmel_spi_release_dma(host); - spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - clk_disable_unprepare(as->gclk); + if (as->gclk) + clk_disable_unprepare(as->gclk); out_disable_clk: clk_disable_unprepare(clk); out_free_irq: @@ -1696,18 +1709,8 @@ static void atmel_spi_remove(struct platform_device *pdev) spi_unregister_controller(host); /* reset the hardware and block queue progress */ - if (as->use_dma) { + if (as->use_dma) atmel_spi_stop_dma(host); - atmel_spi_release_dma(host); - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_tx_bbuf, - as->dma_addr_tx_bbuf); - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } spin_lock_irq(&as->lock); spi_writel(as, CR, SPI_BIT(SWRST)); From 9e565962d999ea77a2b11bb71ae45e27516cf82d Mon Sep 17 00:00:00 2001 From: John Madieu Date: Mon, 25 May 2026 11:02:14 +0000 Subject: [PATCH 0913/3297] ASoC: rsnd: Fix RSND_SOC_MASK width to single nibble [ Upstream commit c0758279367e9d82eb7d7b4959718d7d32e96b7d ] RSND_SOC_MASK was defined as (0xFF << 4), spanning bits 4-11. This is wider than needed since only nibble B (bits 7:4) is used for SoC identifiers. Narrow it to (0xF << 4) to match the intended single-nibble allocation and prevent overlap with bits 8-11 which will be used by upcoming RZ series flags. No functional change, since the only current user (RSND_SOC_E) fits within a single nibble. Fixes: ba164a49f8f7 ("ASoC: rsnd: src: Avoid a potential deadlock") Signed-off-by: John Madieu Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260525110230.4014435-3-john.madieu.xa@bp.renesas.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/renesas/rcar/rsnd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index 04c70690f7a25..3e666125959b4 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -624,7 +624,7 @@ struct rsnd_priv { #define RSND_GEN2 (2 << 0) #define RSND_GEN3 (3 << 0) #define RSND_GEN4 (4 << 0) -#define RSND_SOC_MASK (0xFF << 4) +#define RSND_SOC_MASK (0xF << 4) #define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ /* From f0742d09eb6ba81b5eadc1287d8d51e1186fb092 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 24 Mar 2026 11:18:12 -0400 Subject: [PATCH 0914/3297] NFSD: Fix delegation reference leak in nfsd4_revoke_states [ Upstream commit 625981c8f3da0cc2d236d7b46c39dd75554b8276 ] When revoking delegation state, nfsd4_revoke_states() takes an extra reference on the stid before calling unhash_delegation_locked(). If unhash_delegation_locked() returns false (the delegation was already unhashed by a concurrent path), dp is set to NULL and revoke_delegation() is skipped, but the extra reference is never released. Each occurrence permanently pins the stid in memory. The leaked reference also prevents nfs4_put_stid() from decrementing cl_admin_revoked, leaving the counter permanently inflated. Drop the extra reference in the failure path. Fixes: 8dd91e8d31fe ("nfsd: fix race between laundromat and free_stateid") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4state.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a3e6076a3e645..efa7e103cf190 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1375,7 +1375,8 @@ static void destroy_delegation(struct nfs4_delegation *dp) * stateid or it's called from a laundromat thread (nfsd4_landromat()) that * determined that this specific state has expired and needs to be revoked * (both mark state with the appropriate stid sc_status mode). It is also - * assumed that a reference was taken on the @dp state. + * assumed that a reference was taken on the @dp state. This function + * consumes that reference. * * If this function finds that the @dp state is SC_STATUS_FREED it means * that a FREE_STATEID operation for this stateid has been processed and @@ -1822,6 +1823,10 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) mutex_unlock(&stp->st_mutex); break; case SC_TYPE_DELEG: + /* Extra reference guards against concurrent + * FREE_STATEID; revoke_delegation() consumes + * it, otherwise release it directly. + */ refcount_inc(&stid->sc_count); dp = delegstateid(stid); spin_lock(&state_lock); @@ -1831,6 +1836,8 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) spin_unlock(&state_lock); if (dp) revoke_delegation(dp); + else + nfs4_put_stid(stid); break; case SC_TYPE_LAYOUT: ls = layoutstateid(stid); From 2d642797dd1c18ea26c0ce642fa6c1f6d4dfc599 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 13 May 2026 23:40:04 -0400 Subject: [PATCH 0915/3297] ARM: imx3: Fix CCM node reference leak [ Upstream commit 36d46348eb5fc4bc505cd2290ddd70c25fbe6bb3 ] of_find_compatible_node() returns a referenced device node. The i.MX31 and i.MX35 early init paths use the node to map the CCM registers with of_iomap(), but never drop the node reference. Release the node after the mapping is created. Fixes: 2cf98d12958c ("ARM: imx3: Retrieve the CCM base address from devicetree") Signed-off-by: Yuho Choi Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm/mach-imx/mm-imx3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-imx/mm-imx3.c b/arch/arm/mach-imx/mm-imx3.c index 0788c5cc7f9e6..9b0b014d7fe27 100644 --- a/arch/arm/mach-imx/mm-imx3.c +++ b/arch/arm/mach-imx/mm-imx3.c @@ -106,6 +106,7 @@ void __init imx31_init_early(void) arm_pm_idle = imx31_idle; np = of_find_compatible_node(NULL, NULL, "fsl,imx31-ccm"); mx3_ccm_base = of_iomap(np, 0); + of_node_put(np); BUG_ON(!mx3_ccm_base); } #endif /* ifdef CONFIG_SOC_IMX31 */ @@ -143,6 +144,7 @@ void __init imx35_init_early(void) arch_ioremap_caller = imx3_ioremap_caller; np = of_find_compatible_node(NULL, NULL, "fsl,imx35-ccm"); mx3_ccm_base = of_iomap(np, 0); + of_node_put(np); BUG_ON(!mx3_ccm_base); } #endif /* ifdef CONFIG_SOC_IMX35 */ From adf0eb748d21d301e6376180d18991f3779c6ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Thu, 26 Mar 2026 15:03:48 +0100 Subject: [PATCH 0916/3297] HID: wiimote: Fix table layout and whitespace errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 12b7731995ca577d86e02196e99ba9c126f47282 ] Some tab characters snuck into the data layout table for turntable extensions, which resulted in the table only looking right at a tabstop of 4, which is uncommon in the kernel. Change them to the equivalent amount of spaces, which should look correct in any editor. While at it, also fix the other whitespace errors (trailing spaces at end of line) introduced in the same commit. Fixes: 05086f3db530b3 ("HID: wiimote: Add support for the DJ Hero turntable") Reviewed-by: David Rheinsberg Signed-off-by: J. Neuschäfer Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-wiimote-modules.c | 58 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index dbccdfa639167..dccb78bb3afd6 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -2403,7 +2403,7 @@ static const struct wiimod_ops wiimod_guitar = { .in_ext = wiimod_guitar_in_ext, }; -/* +/* * Turntable * DJ Hero came with a Turntable Controller that was plugged in * as an extension. @@ -2439,15 +2439,15 @@ static const __u16 wiimod_turntable_map[] = { static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext) { __u8 be, cs, sx, sy, ed, rtt, rbg, rbr, rbb, ltt, lbg, lbr, lbb, bp, bm; - /* + /* * Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | *------+------+-----+-----+-----+-----+------+------+--------+ - * 0 | RTT<4:3> | SX <5:0> | - * 1 | RTT<2:1> | SY <5:0> | + * 0 | RTT<4:3> | SX <5:0> | + * 1 | RTT<2:1> | SY <5:0> | *------+------+-----+-----+-----+-----+------+------+--------+ * 2 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> | *------+------+-----+-----+-----+-----+------+------+--------+ - * 3 | ED<2:0> | LTT<4:0> | + * 3 | ED<2:0> | LTT<4:0> | *------+------+-----+-----+-----+-----+------+------+--------+ * 4 | 0 | 0 | LBR | B- | 0 | B+ | RBR | LTT<5> | *------+------+-----+-----+-----+-----+------+------+--------+ @@ -2458,20 +2458,20 @@ static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext) * With Motion+ enabled, it will look like this: * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | *------+------+-----+-----+-----+-----+------+------+--------+ - * 1 | RTT<4:3> | SX <5:1> | 0 | - * 2 | RTT<2:1> | SY <5:1> | 0 | + * 1 | RTT<4:3> | SX <5:1> | 0 | + * 2 | RTT<2:1> | SY <5:1> | 0 | *------+------+-----+-----+-----+-----+------+------+--------+ * 3 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> | *------+------+-----+-----+-----+-----+------+------+--------+ - * 4 | ED<2:0> | LTT<4:0> | + * 4 | ED<2:0> | LTT<4:0> | *------+------+-----+-----+-----+-----+------+------+--------+ * 5 | 0 | 0 | LBR | B- | 0 | B+ | RBR | XXXX | *------+------+-----+-----+-----+-----+------+------+--------+ * 6 | LBB | 0 | RBG | BE | LBG | RBB | XXXX | XXXX | *------+------+-----+-----+-----+-----+------+------+--------+ */ - - be = !(ext[5] & 0x10); + + be = !(ext[5] & 0x10); cs = ((ext[2] & 0x1e)); sx = ext[0] & 0x3f; sy = ext[1] & 0x3f; @@ -2499,32 +2499,32 @@ static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext) input_report_abs(wdata->extension.input, ABS_HAT1X, ltt); input_report_abs(wdata->extension.input, ABS_HAT2X, cs); input_report_abs(wdata->extension.input, ABS_HAT3X, ed); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_RIGHT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_RIGHT], rbg); input_report_key(wdata->extension.input, wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_RIGHT], rbr); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_RIGHT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_RIGHT], rbb); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_LEFT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_LEFT], lbg); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_LEFT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_LEFT], lbr); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_LEFT], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_LEFT], lbb); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_EUPHORIA], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_EUPHORIA], be); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_PLUS], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_PLUS], bp); - input_report_key(wdata->extension.input, - wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_MINUS], + input_report_key(wdata->extension.input, + wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_MINUS], bm); input_sync(wdata->extension.input); @@ -2557,7 +2557,7 @@ static void wiimod_turntable_close(struct input_dev *dev) static int wiimod_turntable_probe(const struct wiimod_ops *ops, struct wiimote_data *wdata) { - int ret, i; + int ret, i; wdata->extension.input = input_allocate_device(); if (!wdata->extension.input) @@ -2594,9 +2594,9 @@ static int wiimod_turntable_probe(const struct wiimod_ops *ops, input_set_abs_params(wdata->extension.input, ABS_HAT1X, -8, 8, 0, 0); input_set_abs_params(wdata->extension.input, - ABS_HAT2X, 0, 31, 1, 1); + ABS_HAT2X, 0, 31, 1, 1); input_set_abs_params(wdata->extension.input, - ABS_HAT3X, 0, 7, 0, 0); + ABS_HAT3X, 0, 7, 0, 0); ret = input_register_device(wdata->extension.input); if (ret) goto err_free; From eb9b89baf30875252f1cd702bcaa09553792ccbe Mon Sep 17 00:00:00 2001 From: Kwan Lai Chee Hou Date: Thu, 7 May 2026 09:53:35 +0800 Subject: [PATCH 0917/3297] wifi: ath12k: fix incorrect HT/VHT/HE/EHT MCS reporting in monitor mode [ Upstream commit 10085a654a4c2331d5f0cdc20bfc839a49fbb886 ] In monitor mode, the driver incorrectly assigns the legacy rate to the rate_idx field of the radiotap header for HT/VHT/HE/EHT frames, ignoring the actual MCS value parsed from the hardware. This causes packet analyzers (like Wireshark) to display incorrect MCS values (e.g., legacy base rates instead of the true MCS). Fix this by assigning ppdu_info->mcs as the default rate_mcs in ath12k_dp_mon_fill_rx_rate(), and remove rate_idx assignments in ath12k_dp_mon_update_radiotap() to preserve the previously calculated MCS values (including the HT NSS offset). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ Fixes: 5393dcb45209 ("wifi: ath12k: change the status update in the monitor Rx") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220864 Signed-off-by: Kwan Lai Chee Hou Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260507015336.14636-1-laicheehou9@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/dp_mon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 009c495021489..4aaaccca9412d 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1905,13 +1905,14 @@ ath12k_dp_mon_fill_rx_rate(struct ath12k *ar, bool is_cck; pkt_type = ppdu_info->preamble_type; - rate_mcs = ppdu_info->rate; + rate_mcs = ppdu_info->mcs; nss = ppdu_info->nss; sgi = ppdu_info->gi; switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: + rate_mcs = ppdu_info->rate; is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); if (rx_status->band < NUM_NL80211_BANDS) { sband = &ar->mac.sbands[rx_status->band]; @@ -2254,13 +2255,10 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ath12k_dp_mon_rx_update_radiotap_he(ppduinfo, ptr); - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->vht_flags) { rxs->encoding = RX_ENC_VHT; - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->ht_flags) { rxs->encoding = RX_ENC_HT; - rxs->rate_idx = ppduinfo->rate; } else { rxs->encoding = RX_ENC_LEGACY; sband = &ar->mac.sbands[rxs->band]; From cfcea221db933295bf2cd75a7f80d441c7a51e28 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:05 -0700 Subject: [PATCH 0918/3297] wifi: ath12k: fix NULL deref in change_sta_links for unready link [ Upstream commit 47809a7c8348bc4a332ccc26a37c7145a5f609f8 ] _ieee80211_set_active_links() calls _ieee80211_link_use_channel() for each newly-added link and WARN_ON_ONCE()s if it fails. The call uses assign_on_failure=true, which allows mac80211 to continue despite driver failures, but when a mac80211-level channel validation fails (e.g., combinations check, DFS, or no available radio), drv_assign_vif_chanctx() is never reached. Since ath12k_mac_vdev_create() is only called from that path, arvif->is_created remains false and arvif->ar remains NULL for the failed link. The subsequent drv_change_sta_links() call reaches ath12k_mac_op_change_sta_links(), which allocates an arsta and sets ahsta->links_map |= BIT(link_id) for the broken link before checking whether the link is ready. When the vdev was never created, only station_add() is skipped, but the link remains in links_map. Any subsequent operation iterating links_map and dereferencing arvif->ar without a NULL check will crash. Two observed examples are NULL deref in ath12k_mac_ml_station_remove() on disconnect and in ath12k_mac_op_set_key() when wpa_supplicant installs PTK keys. BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] Call trace: ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] ath12k_mac_op_sta_state+0xb60/0x1720 [ath12k] drv_sta_state+0x100/0xbd8 [mac80211] __sta_info_destroy_part2+0x148/0x178 [mac80211] ieee80211_set_disassoc+0x500/0x678 [mac80211] BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] Call trace: ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] drv_set_key+0x70/0x100 [mac80211] ieee80211_key_enable_hw_accel+0x78/0x260 [mac80211] ieee80211_add_key+0x16c/0x2ac [mac80211] nl80211_new_key+0x138/0x280 [cfg80211] Fix this by checking arvif->is_created before calling ath12k_mac_alloc_assign_link_sta(). This prevents the broken link from entering links_map, so all subsequent operations iterating the bitmap are protected. The reliability of arvif->is_created across all error paths is ensured by the preceding patch. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: a27fa6148dac ("wifi: ath12k: support change_sta_links() mac80211 op") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-3-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/mac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index b97469dca0467..368c377ab9b55 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7330,16 +7330,16 @@ static int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw, continue; arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); - arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arvif || !arvif->is_created) + continue; - if (!arvif || !arsta) { + arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arsta) { ath12k_hw_warn(ah, "Failed to alloc/assign link sta"); continue; } ar = arvif->ar; - if (!ar) - continue; ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) { From 617a5a67ce01673e155c3e93d4519bb3bdcdef2b Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 21 May 2026 10:33:29 -0700 Subject: [PATCH 0919/3297] ata: libata: Fix ata_exec_internal() [ Upstream commit aa0ae1c35f7b3e9afed2324bed5f5c87ad55b92c ] Some but not all ata_exec_internal() calls happen from the context of the ATA error handler. Commit c0c362b60e25 ("libata: implement cross-port EH exclusion") added ata_eh_release() and ata_eh_acquire() calls in ata_exec_internal(). Calling these functions is necessary if the caller holds the eh_mutex but is not allowed if the caller doesn't hold that mutex. Fix this by only calling ata_eh_release() and ata_eh_acquire() if the caller holds the eh_mutex. An example of an indirect caller of ata_exec_internal() that does not hold the eh_mutex is ata_host_register(). Fixes: c0c362b60e25 ("libata: implement cross-port EH exclusion") Signed-off-by: Bart Van Assche Reviewed-by: Niklas Cassel Reviewed-by: Damien Le Moal Reviewed-by: Hannes Reinecke Signed-off-by: Niklas Cassel Signed-off-by: Sasha Levin --- drivers/ata/libata-core.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5ee4adc623427..501330e91f9d3 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1526,6 +1526,7 @@ unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, { struct ata_link *link = dev->link; struct ata_port *ap = link->ap; + const bool owns_eh_mutex = ap->host->eh_owner == current; u8 command = tf->command; struct ata_queued_cmd *qc; struct scatterlist sgl; @@ -1603,11 +1604,25 @@ unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, } } - ata_eh_release(ap); + if (owns_eh_mutex) { + /* + * To prevent that the compiler complains about the + * ata_eh_release() call below. + */ + __acquire(&ap->host->eh_mutex); + ata_eh_release(ap); + } rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout)); - ata_eh_acquire(ap); + if (owns_eh_mutex) { + ata_eh_acquire(ap); + /* + * To prevent that the compiler complains about the above + * ata_eh_acquire() call. + */ + __release(&ap->host->eh_mutex); + } ata_sff_flush_pio_task(ap); From 901802925ebed22f55462e270e1faa4b68b0eb04 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 25 May 2026 00:01:58 -0400 Subject: [PATCH 0920/3297] ARM: imx31: Fix IIM mapping leak in revision check [ Upstream commit ccb4b54b8ecf1ebafef96d538cd6c5c8455bb390 ] mx31_read_cpu_rev() maps the IIM registers with of_iomap() to read the silicon revision, but returns without unmapping the MMIO mapping. Keep the normalized revision value in a local variable and route the return path through iounmap() after the revision register has been read. Fixes: 3172225d45bd ("ARM: imx31: Retrieve the IIM base address from devicetree") Signed-off-by: Yuho Choi Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm/mach-imx/cpu-imx31.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-imx/cpu-imx31.c b/arch/arm/mach-imx/cpu-imx31.c index 35c544924e509..e81ef9e36a1fa 100644 --- a/arch/arm/mach-imx/cpu-imx31.c +++ b/arch/arm/mach-imx/cpu-imx31.c @@ -36,6 +36,7 @@ static int mx31_read_cpu_rev(void) void __iomem *iim_base; struct device_node *np; u32 i, srev; + int rev = IMX_CHIP_REVISION_UNKNOWN; np = of_find_compatible_node(NULL, NULL, "fsl,imx31-iim"); iim_base = of_iomap(np, 0); @@ -48,13 +49,17 @@ static int mx31_read_cpu_rev(void) for (i = 0; i < ARRAY_SIZE(mx31_cpu_type); i++) if (srev == mx31_cpu_type[i].srev) { + rev = mx31_cpu_type[i].rev; imx_print_silicon_rev(mx31_cpu_type[i].name, mx31_cpu_type[i].rev); - return mx31_cpu_type[i].rev; + goto out; } imx_print_silicon_rev("i.MX31", IMX_CHIP_REVISION_UNKNOWN); - return IMX_CHIP_REVISION_UNKNOWN; + +out: + iounmap(iim_base); + return rev; } int mx31_revision(void) From d292b30e1b746ec22aed32f38fca65cc8e25a76e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 19 May 2026 10:35:26 -0700 Subject: [PATCH 0921/3297] x86/cpu: Keep the PROCESSOR_SELECT menu together [ Upstream commit 1df61a8b2d01c560822a0421f2a76af7fda34c1f ] Having a stray kconfig symbol in the middle of the PROCESSOR_SELECT menu (this symbol plus its dependent symbols) causes the menu dependencies not to be displayed correctly in "make {menu,n,g,x}config". Move the BROADCAST_TLB_FLUSH symbol away from the PROCESSOR_SELECT menu so that the list of processors is displayed correctly. Fixes: 767ae437a32d ("x86/mm: Add INVLPGB feature and Kconfig entry") Signed-off-by: Randy Dunlap Signed-off-by: Borislav Petkov (AMD) Link: https://patch.msgid.link/20260519173526.10985-1-rdunlap@infradead.org Signed-off-by: Sasha Levin --- arch/x86/Kconfig.cpu | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu index f928cf6e32524..755d64bb9d265 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -358,10 +358,6 @@ menuconfig PROCESSOR_SELECT This lets you choose what x86 vendor support code your kernel will include. -config BROADCAST_TLB_FLUSH - def_bool y - depends on CPU_SUP_AMD && 64BIT - config CPU_SUP_INTEL default y bool "Support Intel processors" if PROCESSOR_SELECT @@ -482,3 +478,7 @@ config CPU_SUP_VORTEX_32 makes the kernel a tiny bit smaller. If unsure, say N. + +config BROADCAST_TLB_FLUSH + def_bool y + depends on CPU_SUP_AMD && 64BIT From 5c53406098b599c420b031e6ec5ba8a2f3794c50 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Wed, 27 May 2026 19:16:22 -0700 Subject: [PATCH 0922/3297] nvdimm/btt: Handle preemption in BTT lane acquisition [ Upstream commit 8d4b989d9c9afe5f185aa5853b666fc4617afe9e ] BTT lanes serialize access to per-lane metadata and workspace state during BTT I/O. The btt-check unit test reports data mismatches during BTT writes due to a race in lane acquisition that can lead to silent data corruption. The existing lane model uses a spinlock together with a per-CPU recursion count. That recursion model stopped being valid after BTT lanes became preemptible: another task can run on the same CPU, observe a non-zero recursion count, bypass locking, and use the same lane concurrently. BTT lanes are also held across arena_write_bytes() calls. That path reaches nsio_rw_bytes(), which flushes writes with nvdimm_flush(). Some provider flush callbacks can sleep, making a spinlock the wrong primitive for the lane lifetime. Replace the spinlock-based recursion model with a dynamically allocated per-lane mutex array and take the lane lock unconditionally. Add might_sleep() to catch any future atomic-context caller. Found with the ndctl unit test btt-check.sh. Fixes: 36c75ce3bd29 ("nd_btt: Make BTT lanes preemptible") Assisted-by: Claude-Sonnet:4.5 Tested-by: Aboorva Devarajan Reviewed-by: Aboorva Devarajan Reviewed-by: Vishal Verma Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260528021625.618462-1-alison.schofield@intel.com Signed-off-by: Alison Schofield Signed-off-by: Sasha Levin --- Documentation/driver-api/nvdimm/btt.rst | 5 +- drivers/nvdimm/nd.h | 11 ++--- drivers/nvdimm/region_devs.c | 66 +++++++++---------------- 3 files changed, 29 insertions(+), 53 deletions(-) diff --git a/Documentation/driver-api/nvdimm/btt.rst b/Documentation/driver-api/nvdimm/btt.rst index 107395c042ae0..cb1afe9dc3cbb 100644 --- a/Documentation/driver-api/nvdimm/btt.rst +++ b/Documentation/driver-api/nvdimm/btt.rst @@ -161,9 +161,8 @@ process:: nlanes = min(nfree, num_cpus) A lane number is obtained at the start of any IO, and is used for indexing into -all the on-disk and in-memory data structures for the duration of the IO. If -there are more CPUs than the max number of available lanes, than lanes are -protected by spinlocks. +all the on-disk and in-memory data structures for the duration of the IO. Lanes +are protected by mutexes. d. In-memory data structure: Read Tracking Table (RTT) diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index b199eea3260ef..197e5368c0a46 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -365,11 +365,6 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd); for (res = (ndd)->dpa.child, next = res ? res->sibling : NULL; \ res; res = next, next = next ? next->sibling : NULL) -struct nd_percpu_lane { - int count; - spinlock_t lock; -}; - enum nd_label_flags { ND_LABEL_REAP, }; @@ -400,6 +395,10 @@ struct nd_mapping { struct nvdimm_drvdata *ndd; }; +struct nd_lane { + struct mutex lock; /* serialize lane access */ +} ____cacheline_aligned_in_smp; + struct nd_region { struct device dev; struct ida ns_ida; @@ -420,7 +419,7 @@ struct nd_region { struct kernfs_node *bb_state; struct badblocks bb; struct nd_interleave_set *nd_set; - struct nd_percpu_lane __percpu *lane; + struct nd_lane *lane; int (*flush)(struct nd_region *nd_region, struct bio *bio); struct nd_mapping mapping[] __counted_by(ndr_mappings); }; diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index a5ceaf5db5950..5a1c68c701cfc 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -192,7 +192,9 @@ static void nd_region_release(struct device *dev) put_device(&nvdimm->dev); } - free_percpu(nd_region->lane); + for (i = 0; i < nd_region->num_lanes; i++) + mutex_destroy(&nd_region->lane[i].lock); + kfree(nd_region->lane); if (!test_bit(ND_REGION_CXL, &nd_region->flags)) memregion_free(nd_region->id); kfree(nd_region); @@ -904,52 +906,30 @@ void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) * nd_region_acquire_lane - allocate and lock a lane * @nd_region: region id and number of lanes possible * - * A lane correlates to a BLK-data-window and/or a log slot in the BTT. - * We optimize for the common case where there are 256 lanes, one - * per-cpu. For larger systems we need to lock to share lanes. For now - * this implementation assumes the cost of maintaining an allocator for - * free lanes is on the order of the lock hold time, so it implements a - * static lane = cpu % num_lanes mapping. + * A lane correlates to a log slot in the BTT. Lanes are shared across + * CPUs using a static lane = cpu % num_lanes mapping, with a per-lane + * mutex to serialize access. * - * In the case of a BTT instance on top of a BLK namespace a lane may be - * acquired recursively. We lock on the first instance. - * - * In the case of a BTT instance on top of PMEM, we only acquire a lane - * for the BTT metadata updates. + * Callers must be in sleepable context. The only in-tree caller is + * BTT's ->submit_bio handler (btt_read_pg / btt_write_pg). */ unsigned int nd_region_acquire_lane(struct nd_region *nd_region) + __acquires(&nd_region->lane[lane].lock) { - unsigned int cpu, lane; - - migrate_disable(); - cpu = smp_processor_id(); - if (nd_region->num_lanes < nr_cpu_ids) { - struct nd_percpu_lane *ndl_lock, *ndl_count; + unsigned int lane; - lane = cpu % nd_region->num_lanes; - ndl_count = per_cpu_ptr(nd_region->lane, cpu); - ndl_lock = per_cpu_ptr(nd_region->lane, lane); - if (ndl_count->count++ == 0) - spin_lock(&ndl_lock->lock); - } else - lane = cpu; + might_sleep(); + lane = raw_smp_processor_id() % nd_region->num_lanes; + mutex_lock(&nd_region->lane[lane].lock); return lane; } EXPORT_SYMBOL(nd_region_acquire_lane); void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane) + __releases(&nd_region->lane[lane].lock) { - if (nd_region->num_lanes < nr_cpu_ids) { - unsigned int cpu = smp_processor_id(); - struct nd_percpu_lane *ndl_lock, *ndl_count; - - ndl_count = per_cpu_ptr(nd_region->lane, cpu); - ndl_lock = per_cpu_ptr(nd_region->lane, lane); - if (--ndl_count->count == 0) - spin_unlock(&ndl_lock->lock); - } - migrate_enable(); + mutex_unlock(&nd_region->lane[lane].lock); } EXPORT_SYMBOL(nd_region_release_lane); @@ -1020,17 +1000,16 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, goto err_id; } - nd_region->lane = alloc_percpu(struct nd_percpu_lane); + nd_region->num_lanes = ndr_desc->num_lanes; + if (!nd_region->num_lanes) + goto err_percpu; + nd_region->lane = kcalloc(nd_region->num_lanes, + sizeof(*nd_region->lane), GFP_KERNEL); if (!nd_region->lane) goto err_percpu; - for (i = 0; i < nr_cpu_ids; i++) { - struct nd_percpu_lane *ndl; - - ndl = per_cpu_ptr(nd_region->lane, i); - spin_lock_init(&ndl->lock); - ndl->count = 0; - } + for (i = 0; i < nd_region->num_lanes; i++) + mutex_init(&nd_region->lane[i].lock); for (i = 0; i < ndr_desc->num_mappings; i++) { struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; @@ -1047,7 +1026,6 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, } nd_region->provider_data = ndr_desc->provider_data; nd_region->nd_set = ndr_desc->nd_set; - nd_region->num_lanes = ndr_desc->num_lanes; nd_region->flags = ndr_desc->flags; nd_region->ro = ro; nd_region->numa_node = ndr_desc->numa_node; From 91ca9eab008b962111d6260dbd382d16adfa314a Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 13 May 2026 19:42:36 +0200 Subject: [PATCH 0923/3297] scsi: Revert "scsi: Fix sas_user_scan() to handle wildcard and multi-channel scans" [ Upstream commit 8c292e89bd831c8a13e92f3429ef66bbe0b83677 ] This reverts commit 37c4e72b0651e7697eb338cd1fb09feef472cc1a. Said commit causes excessive resource usage and even system freeze with some controllers, e.g. smartpqi and hisi_sas. The justification provided by the patch authors [1] was supporting a special mode of the mpi3mr and mpt3sas, so-called "Tri-mode", in which NVMe drives are exposed as SCSI devices on a separate channel. While that's useful for these drivers, it seems wrong to cause major breakage for other drivers for the sake of this feature. [1] https://lore.kernel.org/linux-scsi/CAFdVvOwjy+2ORJ6uJkspiLTPF05481U7gcS4QohFOFGPqAs8ig@mail.gmail.com/ Fixes: 37c4e72b0651 ("scsi: Fix sas_user_scan() to handle wildcard and multi-channel scans") Signed-off-by: Martin Wilck Cc: Don Brace Cc: storagedev@microchip.com Cc: Ranjan Kumar Cc: Sathya Prakash Veerichetty Cc: Kashyap Desai Cc: Sumit Saxena Cc: mpi3mr-linuxdrv.pdl@broadcom.com Cc: MPT-FusionLinux.pdl@broadcom.com Cc: Yihang Li Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260513174236.430465-3-mwilck@suse.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/scsi_scan.c | 2 +- drivers/scsi/scsi_transport_sas.c | 60 +++++++------------------------ 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 05ccd11e56a9b..1b48820a5eb41 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1897,7 +1897,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, return 0; } -EXPORT_SYMBOL(scsi_scan_host_selected); + static void scsi_sysfs_add_devices(struct Scsi_Host *shost) { struct scsi_device *sdev; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 081c168094374..351b028ef8938 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -40,8 +40,6 @@ #include #include "scsi_sas_internal.h" -#include "scsi_priv.h" - struct sas_host_attrs { struct list_head rphy_list; struct mutex lock; @@ -1685,22 +1683,6 @@ int scsi_is_sas_rphy(const struct device *dev) } EXPORT_SYMBOL(scsi_is_sas_rphy); -static void scan_channel_zero(struct Scsi_Host *shost, uint id, u64 lun) -{ - struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - struct sas_rphy *rphy; - - list_for_each_entry(rphy, &sas_host->rphy_list, list) { - if (rphy->identify.device_type != SAS_END_DEVICE || - rphy->scsi_target_id == -1) - continue; - - if (id == SCAN_WILD_CARD || id == rphy->scsi_target_id) { - scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id, - lun, SCSI_SCAN_MANUAL); - } - } -} /* * SCSI scan helper @@ -1710,41 +1692,23 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, uint id, u64 lun) { struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - int res = 0; - int i; - - switch (channel) { - case 0: - mutex_lock(&sas_host->lock); - scan_channel_zero(shost, id, lun); - mutex_unlock(&sas_host->lock); - break; - - case SCAN_WILD_CARD: - mutex_lock(&sas_host->lock); - scan_channel_zero(shost, id, lun); - mutex_unlock(&sas_host->lock); + struct sas_rphy *rphy; - for (i = 1; i <= shost->max_channel; i++) { - res = scsi_scan_host_selected(shost, i, id, lun, - SCSI_SCAN_MANUAL); - if (res) - goto exit_scan; - } - break; + mutex_lock(&sas_host->lock); + list_for_each_entry(rphy, &sas_host->rphy_list, list) { + if (rphy->identify.device_type != SAS_END_DEVICE || + rphy->scsi_target_id == -1) + continue; - default: - if (channel <= shost->max_channel) { - res = scsi_scan_host_selected(shost, channel, id, lun, - SCSI_SCAN_MANUAL); - } else { - res = -EINVAL; + if ((channel == SCAN_WILD_CARD || channel == 0) && + (id == SCAN_WILD_CARD || id == rphy->scsi_target_id)) { + scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id, + lun, SCSI_SCAN_MANUAL); } - break; } + mutex_unlock(&sas_host->lock); -exit_scan: - return res; + return 0; } From 3a0f73d27a8d379a8852a378b3c3208143e3b3b2 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jun 2026 17:02:42 +0200 Subject: [PATCH 0924/3297] bpf: Reject exclusive maps as inner maps in map-in-map [ Upstream commit 9a3c3c49c333760c8944dadacbe114c1884546ef ] An exclusive map (created with excl_prog_hash) is bound to a single program by hash: check_map_prog_compatibility() refuses to load any program whose digest does not match map->excl_prog_sha. That check only runs for maps a program references directly, i.e. its used_maps. A map reached at runtime through a map-of-maps is never in used_maps, and bpf_map_meta_equal() does not consider excl_prog_sha, so an exclusive map can be inserted into a non-exclusive outer map and then looked up and mutated by an unrelated program, bypassing the exclusivity guarantee. For the signed loader this defeats the metadata map exclusivity check added in the signed loader: the cached map->sha[] is validated against the signed hash while another program on a hostile host rewrites the frozen map's contents through the outer map. Fixes: baefdbdf6812 ("bpf: Implement exclusive map creation") Reported-by: sashiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/map_in_map.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c index 645bd30bc9a9d..d2cbab4bdf644 100644 --- a/kernel/bpf/map_in_map.c +++ b/kernel/bpf/map_in_map.c @@ -20,7 +20,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) /* Does not support >1 level map-in-map */ if (inner_map->inner_map_meta) return ERR_PTR(-EINVAL); - + if (inner_map->excl_prog_sha) + return ERR_PTR(-ENOTSUPP); if (!inner_map->ops->map_meta_equal) return ERR_PTR(-ENOTSUPP); @@ -101,6 +102,8 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map, inner_map = __bpf_map_get(f); if (IS_ERR(inner_map)) return inner_map; + if (inner_map->excl_prog_sha) + return ERR_PTR(-ENOTSUPP); inner_map_meta = map->inner_map_meta; if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map)) From b6862b6a25c6a925fb0b0e549ee4a52a8d2966fb Mon Sep 17 00:00:00 2001 From: KP Singh Date: Mon, 1 Jun 2026 17:02:44 +0200 Subject: [PATCH 0925/3297] libbpf: Reject non-exclusive metadata maps in the signed loader [ Upstream commit 0fb6c9ed6493b4af01be8bb0a384574eba7df636 ] The loader verifies map->sha against the metadata hash in its instructions. map->sha is calculated when BPF_OBJ_GET_INFO_BY_FD is called on the frozen map. While the map is frozen, the /signed loader/ must also ensure the map is exclusive, as, without exclusivity (which a hostile host could just omit when loading the loader), another BPF program with map access can mutate the contents afterwards, so the check passes on stale data. With the extra check as part of the signed loader, it now refuses to move on with map->sha validation if the host set it up wrongly. Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing") Signed-off-by: KP Singh Co-developed-by: Daniel Borkmann Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-4-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/linux/bpf.h | 1 + kernel/bpf/syscall.c | 7 +++++++ tools/lib/bpf/gen_loader.c | 17 +++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e2dd3a6d495af..f305ea93b9edd 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -294,6 +294,7 @@ struct bpf_map_owner { struct bpf_map { u8 sha[SHA256_DIGEST_SIZE]; + u32 excl; const struct bpf_map_ops *ops; struct bpf_map *inner_map_meta; #ifdef CONFIG_SECURITY diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index c27a0d66b420e..209d0575d0ab4 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1579,6 +1579,13 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr) err = -EFAULT; goto free_map; } + + /* See libbpf: emit_signature_match() */ + BUILD_BUG_ON(offsetof(struct bpf_map, excl) != SHA256_DIGEST_SIZE); + BUILD_BUG_ON(!__same_type(map->excl, u32)); + BUILD_BUG_ON(offsetof(struct bpf_map, sha) != 0); + BUILD_BUG_ON(!__same_type(map->sha, u8[SHA256_DIGEST_SIZE])); + map->excl = 1; } else if (attr->excl_prog_hash_size) { err = -EINVAL; goto free_map; diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index 637b26d1100bb..5c54913185a33 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -585,6 +585,23 @@ static void emit_signature_match(struct bpf_gen *gen) __s64 off; int i; + /* + * Reject if the metadata map is not exclusive. Without exclusivity + * the cached map->sha[] verified above can be stale: another BPF + * program with map access could have mutated the contents between + * BPF_OBJ_GET_INFO_BY_FD and loader execution. + */ + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, + 0, 0, 0, 0)); + emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, SHA256_DIGEST_LENGTH)); + off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2; + if (is_simm16(off)) { + emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL)); + emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, off)); + } else { + gen->error = -ERANGE; + } + for (i = 0; i < SHA256_DWORD_SIZE; i++) { emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, 0, 0, 0, 0)); From abe383999640fb8958bd65a10492248729797209 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jun 2026 17:02:45 +0200 Subject: [PATCH 0926/3297] libbpf: Skip initial_value override on signed loaders [ Upstream commit 61e084152328867fe2279cc790573aae39959cd5 ] bpf_gen__map_update_elem() emits code that, when the host-supplied loader ctx provides a non-NULL map_desc[idx].initial_value, overwrites the blob value with bytes read from the host (bpf_copy_from_user / bpf_probe_read_kernel) before the BPF_MAP_UPDATE_ELEM that populates the program's .data/.rodata/.bss maps. This override runs after emit_signature_match() has validated map->sha[], and initial_value is part of neither the signed loader instructions nor the hashed data blob. For a signed loader this lets an untrusted host substitute global-variable contents into a program whose code carries a valid signature, thus weakening what the signature attests to. The blob already contains the signer-provided value (added via add_data() and covered by the embedded, signed hash), so simply skip emitting the override for signed loaders (gen_hash). Runtime initialization stays available for the unsigned light-skeleton path as before. The jump offsets within the override block are internal to it, so guarding the whole block leaves them unchanged. Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader") Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-5-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 39 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index 5c54913185a33..f0898244f20ac 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -1187,27 +1187,36 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue, value = add_data(gen, pvalue, value_size); key = add_data(gen, &zero, sizeof(zero)); - /* if (map_desc[map_idx].initial_value) { + /* + * if (map_desc[map_idx].initial_value) { * if (ctx->flags & BPF_SKEL_KERNEL) * bpf_probe_read_kernel(value, value_size, initial_value); * else * bpf_copy_from_user(value, value_size, initial_value); * } + * + * The runtime initial_value comes from the host-supplied loader + * ctx and would overwrite the blob value after emit_signature_match() + * has already validated map->sha[]. For a signed loader (gen_hash) + * the attested blob value must be authoritative, so skip the override + * and leave the hashed value in place. */ - emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, - sizeof(struct bpf_loader_ctx) + - sizeof(struct bpf_map_desc) * map_idx + - offsetof(struct bpf_map_desc, initial_value))); - emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8)); - emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, - 0, 0, 0, value)); - emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size)); - emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, - offsetof(struct bpf_loader_ctx, flags))); - emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2)); - emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user)); - emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1)); - emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); + if (!OPTS_GET(gen->opts, gen_hash, false)) { + emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, + sizeof(struct bpf_loader_ctx) + + sizeof(struct bpf_map_desc) * map_idx + + offsetof(struct bpf_map_desc, initial_value))); + emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8)); + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, + 0, 0, 0, value)); + emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size)); + emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, + offsetof(struct bpf_loader_ctx, flags))); + emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2)); + emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user)); + emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1)); + emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); + } map_update_attr = add_data(gen, &attr, attr_size); pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n", From 0de14eae6de886cf86555570d0808de4d9d4a7e5 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jun 2026 17:02:46 +0200 Subject: [PATCH 0927/3297] libbpf: Skip max_entries override on signed loaders [ Upstream commit 60214435b365ecdd40b2f96d4e54564b5c927645 ] bpf_gen__map_create() lets the host-supplied loader ctx override a map's max_entries at runtime (map_desc[idx].max_entries, when non-zero). This is how the light skeleton sizes maps to the target machine, but it happens after emit_signature_match() and is covered by neither the signed loader instructions nor the hashed blob. For a signed loader this means an untrusted host can re-dimension the program's maps, outside what the signature attests to. Gate the override on gen_hash so signed loaders use the signer-provided max_entries baked into the blob. Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader") Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-6-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/lib/bpf/gen_loader.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index f0898244f20ac..d41defa1936d3 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -545,13 +545,22 @@ void bpf_gen__map_create(struct bpf_gen *gen, default: break; } - /* conditionally update max_entries */ - if (map_idx >= 0) + + /* + * Conditionally update max_entries from the host-supplied loader + * ctx. This sizes the map at runtime, but for a signed loader + * (gen_hash) it would let an untrusted host re-dimension the + * program's maps after emit_signature_match(), outside what the + * signature attests to. Keep the signer-provided max_entries + * baked into the blob in that case. + */ + if (map_idx >= 0 && !OPTS_GET(gen->opts, gen_hash, false)) move_ctx2blob(gen, attr_field(map_create_attr, max_entries), 4, sizeof(struct bpf_loader_ctx) + sizeof(struct bpf_map_desc) * map_idx + offsetof(struct bpf_map_desc, max_entries), true /* check that max_entries != 0 */); + /* emit MAP_CREATE command */ emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size); debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d", From 846052542cfa644f3b13d70cfa6dcd27d35c9769 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 30 May 2026 22:45:48 +0300 Subject: [PATCH 0928/3297] scsi: pm8001: Fix error code in non_fatal_log_show() [ Upstream commit 1b6f03b7ae9ee27054c55bb55a69d05555a78516 ] The non_fatal_log_show() function is supposed to return negative error codes on failure. But because the error codes are saved in a u32 and then cast to signed long, they end up being high positive values instead of negative. Remove the intermediary u32 variable to fix this bug. Fixes: dba2cc03b9db ("scsi: pm80xx: sysfs attribute for non fatal dump") Signed-off-by: Dan Carpenter Acked-by: Jack Wang Link: https://patch.msgid.link/ahs-bEsBJH0KhnsX@stanley.mountain Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/pm8001/pm8001_ctl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index cbfda8c04e956..c10854ec44c7b 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -588,10 +588,7 @@ static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL); static ssize_t non_fatal_log_show(struct device *cdev, struct device_attribute *attr, char *buf) { - u32 count; - - count = pm80xx_get_non_fatal_dump(cdev, attr, buf); - return count; + return pm80xx_get_non_fatal_dump(cdev, attr, buf); } static DEVICE_ATTR_RO(non_fatal_log); From d81370c6c4f5f134590d9788eb01d59738574e80 Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Wed, 27 May 2026 18:21:34 +0900 Subject: [PATCH 0929/3297] scsi: ufs: Fix wrong value printed in unexpected UPIU response case [ Upstream commit 2483ae0a56231a915c706411421c6c002a2bf83e ] In ufshcd_transfer_rsp_status(), the default case of the inner switch statement prints the UPIU response code when an unexpected response is received. However, the code was printing 'result' variable which is always 0 at that point, making the error message useless for debugging. Fix this by printing the actual UPIU response code returned by ufshcd_get_req_rsp(). Fixes: 08108d31129a ("scsi: ufs: Improve type safety") Signed-off-by: Chanwoo Lee Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260527092134.275887-1-cw9316.lee@samsung.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/ufs/core/ufshcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 5371f173e28b9..8de3d0886a121 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -5494,7 +5494,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, default: dev_err(hba->dev, "Unexpected request response code = %x\n", - result); + ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr)); result = DID_ERROR << 16; break; } From 61f19729728243c82476dee31315143ed3275e7f Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Tue, 2 Jun 2026 08:22:49 +0530 Subject: [PATCH 0930/3297] bpf: fix UAF by restoring RCU-delayed inode freeing in bpffs [ Upstream commit b93c55b4932dd7e32dca8cf34a3443cc87a02906 ] commit 4f375ade6aa9 ("bpf: Avoid RCU context warning when unpinning htab with internal structs") moved inode cleanup from ->free_inode() into ->destroy_inode() to avoid sleeping in RCU context when calling bpf_any_put(). However this removed the RCU delay on freeing the inode itself and the cached symlink body (i_link), both of which can be accessed by RCU pathwalk (pick_link, may_lookup etc.). This causes a use-after-free when a concurrent unlinkat() drops the last inode reference and destroy_inode() frees the inode immediately, while another task is still walking the path in RCU mode and reads inode->i_opflags (offset +2) inside current_time() -> is_mgtime(). KASAN reports: BUG: KASAN: slab-use-after-free in is_mgtime include/linux/fs.h:2313 Read of size 2 at addr ffff8880407e4282 (offset +2 = i_opflags) The rules (per Al Viro): ->destroy_inode() called immediately, can sleep, use for blocking cleanup e.g. bpf_any_put() ->free_inode() called after RCU grace period, use for freeing inode and anything RCU-accessible e.g. i_link Fix: split the two concerns properly: - keep bpf_any_put() in bpf_destroy_inode() since it is blocking and needs to run promptly - introduce bpf_free_inode() to handle kfree(i_link) and free_inode_nonrcu() with proper RCU delay, preventing the UAF Fixes: 4f375ade6aa9 ("bpf: Avoid RCU context warning when unpinning htab with internal structs") Reported-by: syzbot+36e50496c8ac4bcde3f9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=36e50496c8ac4bcde3f9 Suggested-by: Al Viro Link: https://lore.kernel.org/all/20260423043906.GN3518998@ZenIV/ Link: https://lore.kernel.org/all/20260602002607.110866-1-kartikey406@gmail.com/T/ [v1] Signed-off-by: Deepanshu Kartikey Acked-by: Al Viro Link: https://lore.kernel.org/r/20260602025249.113828-1-kartikey406@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/inode.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 81780bcf8d254..cf907fab3721f 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -779,10 +779,18 @@ static void bpf_destroy_inode(struct inode *inode) { enum bpf_type type; - if (S_ISLNK(inode->i_mode)) - kfree(inode->i_link); if (!bpf_inode_type(inode, &type)) bpf_any_put(inode->i_private, type); +} + +/* + * Called after RCU grace period - safe to free inode and anything + * that might be accessed by RCU pathwalk (inode fields, i_link). + */ +static void bpf_free_inode(struct inode *inode) +{ + if (S_ISLNK(inode->i_mode)) + kfree(inode->i_link); free_inode_nonrcu(inode); } @@ -791,6 +799,7 @@ const struct super_operations bpf_super_ops = { .drop_inode = inode_just_drop, .show_options = bpf_show_options, .destroy_inode = bpf_destroy_inode, + .free_inode = bpf_free_inode, }; enum { From 8d763babb2a2f67b35724308ee790f87e57bc540 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 17 Apr 2026 22:58:05 +0900 Subject: [PATCH 0931/3297] mm/fake-numa: fix under-allocation detection in uniform split [ Upstream commit 3a3fc1dfd6a958615ebaab8fb251e89fc2b3f2f2 ] When splitting NUMA node uniformly, split_nodes_size_interleave_uniform() returns the next absolute node ID, not the number of nodes created. The existing under-allocation detection logic compares next absolute node ID (ret) and request count (n), which only works when nid starts at 0. For example, on a system with 2 physical NUMA nodes (node 0: 2GB, node 1: 128MB) and numa=fake=8U, 8 fake nodes are successfully created from node 0 and split_nodes_size_interleave_uniform() returns 8. For node 1, fake node nid starts at 8, but only 4 fake nodes are created due to current FAKE_NODE_MIN_SIZE being 32MB, and split_nodes_size_interleave_uniform() returns 12. By existing under-allocation detection logic, "ret < n" (12 < 8) is false, so the under-allocation will not be detected. Fix under-allocation detection logic to compare the number of actually created nodes (ret - nid) against the request count (n). Also skip under-allocation detection logic for memoryless physical nodes where no fake nodes are created. Also, fix the outdated comment describing split_nodes_size_interleave_uniform() to match the actual return value. Signed-off-by: Sang-Heon Jeon Reported-by: Donghyeon Lee Reported-by: Munhui Chae Fixes: cc9aec03e58f ("x86/numa_emulation: Introduce uniform split capability") # 4.19 Link: https://patch.msgid.link/20260417135805.1758378-1-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin --- mm/numa_emulation.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/mm/numa_emulation.c b/mm/numa_emulation.c index 703c8fa050480..55f26b22bb0be 100644 --- a/mm/numa_emulation.c +++ b/mm/numa_emulation.c @@ -214,7 +214,7 @@ static u64 uniform_size(u64 max_addr, u64 base, u64 hole, int nr_nodes) * Sets up fake nodes of `size' interleaved over physical nodes ranging from * `addr' to `max_addr'. * - * Returns zero on success or negative on error. + * Returns node ID of the next node on success or negative error code. */ static int __init split_nodes_size_interleave_uniform(struct numa_meminfo *ei, struct numa_meminfo *pi, @@ -398,7 +398,7 @@ void __init numa_emulation(struct numa_meminfo *numa_meminfo, int numa_dist_cnt) */ if (strchr(emu_cmdline, 'U')) { unsigned long n; - int nid = 0; + int nid = 0, nr_created; n = simple_strtoul(emu_cmdline, &emu_cmdline, 0); ret = -1; @@ -416,9 +416,18 @@ void __init numa_emulation(struct numa_meminfo *numa_meminfo, int numa_dist_cnt) n, &pi.blk[0], nid); if (ret < 0) break; - if (ret < n) { + + /* + * If no memory was found for this physical node, + * skip the under-allocation check. + */ + if (ret == nid) + continue; + + nr_created = ret - nid; + if (nr_created < n) { pr_info("%s: phys: %d only got %d of %ld nodes, failing\n", - __func__, i, ret, n); + __func__, i, nr_created, n); ret = -1; break; } From ffa974b2f50ad8b911b9066d7aed84ad0afabcdb Mon Sep 17 00:00:00 2001 From: Danila Chernetsov Date: Sat, 30 May 2026 12:23:11 +0000 Subject: [PATCH 0932/3297] ext2: fix ignored return value of generic_write_sync() [ Upstream commit a4659be0bc7cb1856ffb15b67f903229ae8891ec ] Fix ext2_dio_write_iter() to propagate the error returned by generic_write_sync() instead of silently discarding it, which could cause write(2) to return success to userspace on O_SYNC/O_DSYNC files even when the sync failed. The correct pattern, already used in ext2_dax_write_iter() in the same file and in ext4, xfs, f2fs among others, is: if (ret > 0) ret = generic_write_sync(iocb, ret); Found by Linux Verification Center (linuxtesting.org) with SVACE. [JK: Reflect also filemap_write_and_wait() return value] Fixes: fb5de4358e1a ("ext2: Move direct-io to use iomap") Signed-off-by: Danila Chernetsov Link: https://patch.msgid.link/20260530122311.136803-1-listdansp@mail.ru Signed-off-by: Jan Kara Signed-off-by: Sasha Levin --- fs/ext2/file.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 76bddce462fce..c59c19e5ff72b 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -264,12 +264,15 @@ static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) endbyte = pos + status - 1; ret2 = filemap_write_and_wait_range(inode->i_mapping, pos, endbyte); - if (!ret2) + if (!ret2) { invalidate_mapping_pages(inode->i_mapping, pos >> PAGE_SHIFT, endbyte >> PAGE_SHIFT); - if (ret > 0) - generic_write_sync(iocb, ret); + if (ret > 0) + ret = generic_write_sync(iocb, ret); + } else { + ret = ret2; + } } out_unlock: From 36bdda0c86d51be003757d8d43bfa834250312cf Mon Sep 17 00:00:00 2001 From: "Guanyou.Chen" Date: Fri, 22 May 2026 21:09:59 +0800 Subject: [PATCH 0933/3297] sched: restore timer_slack_ns when resetting RT policy on fork [ Upstream commit 63c1a12bc0e09af7dee919c4fb4a300a719d5125 ] Commit ed4fb6d7ef68 ("hrtimer: Use and report correct timerslack values for realtime tasks") sets timer_slack_ns to 0 for RT tasks in __setscheduler_params(). However, when an RT task with SCHED_RESET_ON_FORK creates child threads, the children inherit timer_slack_ns=0 from the parent. sched_fork() resets the child's policy to SCHED_NORMAL but does not restore timer_slack_ns, leaving the child permanently running with zero slack. Fix this by restoring timer_slack_ns from default_timer_slack_ns in sched_fork() when resetting from RT/DL to NORMAL policy, matching the existing behavior in __setscheduler_params(). Note: this fix alone requires a correct default_timer_slack_ns to be effective. See the following patch for that fix. Fixes: ed4fb6d7ef68 ("hrtimer: Use and report correct timerslack values for realtime tasks") Reported-by: Qiaoting.Lin Signed-off-by: Guanyou.Chen Signed-off-by: Chunhui.Li Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260522131000.1664983-2-chenguanyou@xiaomi.com Signed-off-by: Sasha Levin --- kernel/sched/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d089b1f211556..b5b5897b595b4 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4706,6 +4706,7 @@ int sched_fork(u64 clone_flags, struct task_struct *p) p->policy = SCHED_NORMAL; p->static_prio = NICE_TO_PRIO(0); p->rt_priority = 0; + p->timer_slack_ns = p->default_timer_slack_ns; } else if (PRIO_TO_NICE(p->static_prio) < 0) p->static_prio = NICE_TO_PRIO(0); From 5e406928404d67a8da8aa3ae21732e1ea1a04118 Mon Sep 17 00:00:00 2001 From: liuxixin Date: Thu, 28 May 2026 18:00:01 +0800 Subject: [PATCH 0934/3297] nvme: fix FDP fdpcidx bounds check [ Upstream commit 0967074f6830718fd2597404ef119bddd0dbfd00 ] The fdpcidx bounds check sets n = NUMFDPC + 1 but used > instead of >=, incorrectly accepting fdp_idx when it equals n (i.e. NUMFDPC + 1). Fixes: 30b5f20bb2dd ("nvme: register fdp parameters with the block layer") Reviewed-by: Nitesh Shetty Reviewed-by: Christoph Hellwig Signed-off-by: liuxixin Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 7b6b42b20641b..24a5ca35aeb25 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2229,7 +2229,7 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl, } n = le16_to_cpu(h->numfdpc) + 1; - if (fdp_idx > n) { + if (fdp_idx >= n) { dev_warn(ctrl->device, "FDP index:%d out of range:%d\n", fdp_idx, n); /* Proceed without registering FDP streams */ From 0830287cc6cb73f1d7bffbf5e6b09099f6b21fa6 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 1 Jun 2026 20:56:31 -0700 Subject: [PATCH 0935/3297] driver core: Use system_percpu_wq instead of system_wq [ Upstream commit fda8355f13ea3c0f9499acdeff3024995b474948 ] Commit 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") added a use of system_wq, which is deprecated in favor of system_percpu_wq added by commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq"). An upcoming warning in the workqueue tree flags this with: workqueue: work func deferred_probe_timeout_work_func enqueued on deprecated workqueue. Use system_{percpu|dfl}_wq instead. Switch to system_percpu_wq to clear up the warning. Fixes: 1137838865bf ("driver core: Use mod_delayed_work to prevent lost deferred probe work") Signed-off-by: Nathan Chancellor Reviewed-by: Rafael J. Wysocki (Intel) Link: https://patch.msgid.link/20260601-driver-core-fix-system_wq-warning-v1-1-f9001a70ee25@kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 1ba53078703e4..dabdfc088f3f6 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -328,7 +328,7 @@ void deferred_probe_extend_timeout(void) * start a new one. */ if (delayed_work_pending(&deferred_probe_timeout_work) && - mod_delayed_work(system_wq, &deferred_probe_timeout_work, + mod_delayed_work(system_percpu_wq, &deferred_probe_timeout_work, secs_to_jiffies(driver_deferred_probe_timeout))) pr_debug("Extended deferred probe timeout by %d secs\n", driver_deferred_probe_timeout); From 5cf2c85b1231218a3e3e9f188afb4fe2e17903d5 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 2 Jun 2026 15:30:49 +0200 Subject: [PATCH 0936/3297] bpf: Reject exclusive maps for bpf_map_elem iterators [ Upstream commit 3c56ee343f9412d81918635c3e25e22a5dd6d87e ] Exclusive maps (aka excl_prog_hash) are meant to be reachable only from the single program whose hash matches. This is enforced by check_map_prog_compatibility() when the map is referenced from a program such as signed BPF loaders. A bpf_map_elem iterator, however, binds its target map at attach time in bpf_iter_attach_map() instead of referencing it from the program, so the exclusivity check is never reached. On top of that, the iterator exposes the map value as a writable buffer. Fixes: baefdbdf6812 ("bpf: Implement exclusive map creation") Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260602133052.423725-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/map_iter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/map_iter.c b/kernel/bpf/map_iter.c index 9575314f40a69..f84af5cddd431 100644 --- a/kernel/bpf/map_iter.c +++ b/kernel/bpf/map_iter.c @@ -112,6 +112,10 @@ static int bpf_iter_attach_map(struct bpf_prog *prog, map = bpf_map_get_with_uref(linfo->map.map_fd); if (IS_ERR(map)) return PTR_ERR(map); + if (map->excl_prog_sha) { + err = -EPERM; + goto put_map; + } if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || From 3777588848520f913ad2f97c110c8ffa5fbe80d7 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 8 May 2026 15:16:33 +0200 Subject: [PATCH 0937/3297] tick/sched: Fix TOCTOU in nohz idle time fetch [ Upstream commit 86db4084b4b5d1a074bcc66c108a4c9d266812d4 ] When the nohz idle time is fetched, the current clock timestamp is taken outside the seqcount, which can result in a race as reported by Sashiko: get_cpu_sleep_time_us() tick_nohz_start_idle() ----------------------- --------------------- now = ktime_get() write_seqcount_begin(idle_sleeptime_seq); idle_entrytime = ktime_get() tick_sched_flag_set(ts, TS_FLAG_IDLE_ACTIVE); write_seqcount_end(&ts->idle_sleeptime_seq); read_seqcount_begin(idle_sleeptime_seq) delta = now - idle_entrytime); //!! But now < idle_entrytime idle = *sleeptime + delta; read_seqcount_retry(&ts->idle_sleeptime_seq, seq) Here the read side fetches the timestamp before the write side and its update. As a result the time delta computed on the read side is negative (ktime_t is signed) and breaks the cputime monotonicity guarantee. This could possibly be fixed with reading the current clock timestamp inside the seqcount but the reader overhead might then increase. Also simply checking that the current timestamp is above the idle entry time is enough to prevent any issue of the like. Fixes: 620a30fa0bd1 ("timers/nohz: Protect idle/iowait sleep time under seqcount") Reported-by: Sashiko Signed-off-by: Frederic Weisbecker Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260508131647.43868-2-frederic@kernel.org Signed-off-by: Sasha Levin --- kernel/time/tick-sched.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 466e083c82721..4f04c621c9224 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -766,15 +766,16 @@ static u64 get_cpu_sleep_time_us(struct tick_sched *ts, ktime_t *sleeptime, *last_update_time = ktime_to_us(now); do { + ktime_t delta = 0; + seq = read_seqcount_begin(&ts->idle_sleeptime_seq); if (tick_sched_flag_test(ts, TS_FLAG_IDLE_ACTIVE) && compute_delta) { - ktime_t delta = ktime_sub(now, ts->idle_entrytime); - - idle = ktime_add(*sleeptime, delta); - } else { - idle = *sleeptime; + if (now > ts->idle_entrytime) + delta = ktime_sub(now, ts->idle_entrytime); } + + idle = ktime_add(*sleeptime, delta); } while (read_seqcount_retry(&ts->idle_sleeptime_seq, seq)); return ktime_to_us(idle); From 5a1168ba0a95b65961c234ae21522dfe36d348e8 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Mon, 4 May 2026 12:06:37 +0200 Subject: [PATCH 0938/3297] lib/test_meminit: use && for bools [ Upstream commit 8e0c2085c978ed6d9764d79fc785920360096f21 ] As pointed out by Dan Carpenter, test_kmemcache() was using a bitwise AND on two bools instead of a boolean AND. Fix this for the sake of code cleanliness. Link: https://lore.kernel.org/20260504100637.1535762-1-glider@google.com Fixes: 5015a300a522 ("lib: introduce test_meminit module") Signed-off-by: Alexander Potapenko Reported-by: Dan Carpenter Closes: https://lore.kernel.org/kernel-janitors/afOcIan1ap9kD26M@stanley.mountain/ Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/test_meminit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_meminit.c b/lib/test_meminit.c index 6298f66c964bb..d028a6552cd61 100644 --- a/lib/test_meminit.c +++ b/lib/test_meminit.c @@ -387,7 +387,7 @@ static int __init test_kmemcache(int *total_failures) ctor = flags & 1; rcu = flags & 2; zero = flags & 4; - if (ctor & zero) + if (ctor && zero) continue; num_tests += do_kmem_cache_size(size, ctor, rcu, zero, &failures); From efe71fbced5242de5211a59ec9693353be77abd3 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sun, 26 Apr 2026 09:34:48 +0800 Subject: [PATCH 0939/3297] riscv: dts: sophgo: sg2044: use hex for CPU unit address [ Upstream commit 207cbc477406a72952e27ace2eadbae55164f129 ] Previous the CPU unit address cpu of sg2044 use decimal, it is not following the general convention for unit addresses of the OF. Convent the unit address to hex to resolve this problem. The introduces a small change for the CPU node name, but it should nothing since there is no direct full-path reference to these CPU nodes. Fixes: 967a94a92aaa ("riscv: dts: add initial Sophgo SG2042 SoC device tree") Reviewed-by: Chen Wang Reviewed-by: Guo Ren Link: https://patch.msgid.link/20260426013449.694435-2-inochiama@gmail.com Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi | 236 ++++++++++---------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi index 523799a1a8b82..0943db4988467 100644 --- a/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2044-cpus.dtsi @@ -14,7 +14,7 @@ cpu0: cpu@0 { compatible = "thead,c920", "riscv"; - reg = <0>; + reg = <0x0>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -50,7 +50,7 @@ cpu1: cpu@1 { compatible = "thead,c920", "riscv"; - reg = <1>; + reg = <0x1>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -86,7 +86,7 @@ cpu2: cpu@2 { compatible = "thead,c920", "riscv"; - reg = <2>; + reg = <0x2>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -122,7 +122,7 @@ cpu3: cpu@3 { compatible = "thead,c920", "riscv"; - reg = <3>; + reg = <0x3>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -158,7 +158,7 @@ cpu4: cpu@4 { compatible = "thead,c920", "riscv"; - reg = <4>; + reg = <0x4>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -194,7 +194,7 @@ cpu5: cpu@5 { compatible = "thead,c920", "riscv"; - reg = <5>; + reg = <0x5>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -230,7 +230,7 @@ cpu6: cpu@6 { compatible = "thead,c920", "riscv"; - reg = <6>; + reg = <0x6>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -266,7 +266,7 @@ cpu7: cpu@7 { compatible = "thead,c920", "riscv"; - reg = <7>; + reg = <0x7>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -302,7 +302,7 @@ cpu8: cpu@8 { compatible = "thead,c920", "riscv"; - reg = <8>; + reg = <0x8>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -338,7 +338,7 @@ cpu9: cpu@9 { compatible = "thead,c920", "riscv"; - reg = <9>; + reg = <0x9>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -372,9 +372,9 @@ }; }; - cpu10: cpu@10 { + cpu10: cpu@a { compatible = "thead,c920", "riscv"; - reg = <10>; + reg = <0xa>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -408,9 +408,9 @@ }; }; - cpu11: cpu@11 { + cpu11: cpu@b { compatible = "thead,c920", "riscv"; - reg = <11>; + reg = <0xb>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -444,9 +444,9 @@ }; }; - cpu12: cpu@12 { + cpu12: cpu@c { compatible = "thead,c920", "riscv"; - reg = <12>; + reg = <0xc>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -480,9 +480,9 @@ }; }; - cpu13: cpu@13 { + cpu13: cpu@d { compatible = "thead,c920", "riscv"; - reg = <13>; + reg = <0xd>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -516,9 +516,9 @@ }; }; - cpu14: cpu@14 { + cpu14: cpu@e { compatible = "thead,c920", "riscv"; - reg = <14>; + reg = <0xe>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -552,9 +552,9 @@ }; }; - cpu15: cpu@15 { + cpu15: cpu@f { compatible = "thead,c920", "riscv"; - reg = <15>; + reg = <0xf>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -588,9 +588,9 @@ }; }; - cpu16: cpu@16 { + cpu16: cpu@10 { compatible = "thead,c920", "riscv"; - reg = <16>; + reg = <0x10>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -624,9 +624,9 @@ }; }; - cpu17: cpu@17 { + cpu17: cpu@11 { compatible = "thead,c920", "riscv"; - reg = <17>; + reg = <0x11>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -660,9 +660,9 @@ }; }; - cpu18: cpu@18 { + cpu18: cpu@12 { compatible = "thead,c920", "riscv"; - reg = <18>; + reg = <0x12>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -696,9 +696,9 @@ }; }; - cpu19: cpu@19 { + cpu19: cpu@13 { compatible = "thead,c920", "riscv"; - reg = <19>; + reg = <0x13>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -732,9 +732,9 @@ }; }; - cpu20: cpu@20 { + cpu20: cpu@14 { compatible = "thead,c920", "riscv"; - reg = <20>; + reg = <0x14>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -768,9 +768,9 @@ }; }; - cpu21: cpu@21 { + cpu21: cpu@15 { compatible = "thead,c920", "riscv"; - reg = <21>; + reg = <0x15>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -804,9 +804,9 @@ }; }; - cpu22: cpu@22 { + cpu22: cpu@16 { compatible = "thead,c920", "riscv"; - reg = <22>; + reg = <0x16>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -840,9 +840,9 @@ }; }; - cpu23: cpu@23 { + cpu23: cpu@17 { compatible = "thead,c920", "riscv"; - reg = <23>; + reg = <0x17>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -876,9 +876,9 @@ }; }; - cpu24: cpu@24 { + cpu24: cpu@18 { compatible = "thead,c920", "riscv"; - reg = <24>; + reg = <0x18>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -912,9 +912,9 @@ }; }; - cpu25: cpu@25 { + cpu25: cpu@19 { compatible = "thead,c920", "riscv"; - reg = <25>; + reg = <0x19>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -948,9 +948,9 @@ }; }; - cpu26: cpu@26 { + cpu26: cpu@1a { compatible = "thead,c920", "riscv"; - reg = <26>; + reg = <0x1a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -984,9 +984,9 @@ }; }; - cpu27: cpu@27 { + cpu27: cpu@1b { compatible = "thead,c920", "riscv"; - reg = <27>; + reg = <0x1b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1020,9 +1020,9 @@ }; }; - cpu28: cpu@28 { + cpu28: cpu@1c { compatible = "thead,c920", "riscv"; - reg = <28>; + reg = <0x1c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1056,9 +1056,9 @@ }; }; - cpu29: cpu@29 { + cpu29: cpu@1d { compatible = "thead,c920", "riscv"; - reg = <29>; + reg = <0x1d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1092,9 +1092,9 @@ }; }; - cpu30: cpu@30 { + cpu30: cpu@1e { compatible = "thead,c920", "riscv"; - reg = <30>; + reg = <0x1e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1128,9 +1128,9 @@ }; }; - cpu31: cpu@31 { + cpu31: cpu@1f { compatible = "thead,c920", "riscv"; - reg = <31>; + reg = <0x1f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1164,9 +1164,9 @@ }; }; - cpu32: cpu@32 { + cpu32: cpu@20 { compatible = "thead,c920", "riscv"; - reg = <32>; + reg = <0x20>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1200,9 +1200,9 @@ }; }; - cpu33: cpu@33 { + cpu33: cpu@21 { compatible = "thead,c920", "riscv"; - reg = <33>; + reg = <0x21>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1236,9 +1236,9 @@ }; }; - cpu34: cpu@34 { + cpu34: cpu@22 { compatible = "thead,c920", "riscv"; - reg = <34>; + reg = <0x22>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1272,9 +1272,9 @@ }; }; - cpu35: cpu@35 { + cpu35: cpu@23 { compatible = "thead,c920", "riscv"; - reg = <35>; + reg = <0x23>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1308,9 +1308,9 @@ }; }; - cpu36: cpu@36 { + cpu36: cpu@24 { compatible = "thead,c920", "riscv"; - reg = <36>; + reg = <0x24>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1344,9 +1344,9 @@ }; }; - cpu37: cpu@37 { + cpu37: cpu@25 { compatible = "thead,c920", "riscv"; - reg = <37>; + reg = <0x25>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1380,9 +1380,9 @@ }; }; - cpu38: cpu@38 { + cpu38: cpu@26 { compatible = "thead,c920", "riscv"; - reg = <38>; + reg = <0x26>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1416,9 +1416,9 @@ }; }; - cpu39: cpu@39 { + cpu39: cpu@27 { compatible = "thead,c920", "riscv"; - reg = <39>; + reg = <0x27>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1452,9 +1452,9 @@ }; }; - cpu40: cpu@40 { + cpu40: cpu@28 { compatible = "thead,c920", "riscv"; - reg = <40>; + reg = <0x28>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1488,9 +1488,9 @@ }; }; - cpu41: cpu@41 { + cpu41: cpu@29 { compatible = "thead,c920", "riscv"; - reg = <41>; + reg = <0x29>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1524,9 +1524,9 @@ }; }; - cpu42: cpu@42 { + cpu42: cpu@2a { compatible = "thead,c920", "riscv"; - reg = <42>; + reg = <0x2a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1560,9 +1560,9 @@ }; }; - cpu43: cpu@43 { + cpu43: cpu@2b { compatible = "thead,c920", "riscv"; - reg = <43>; + reg = <0x2b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1596,9 +1596,9 @@ }; }; - cpu44: cpu@44 { + cpu44: cpu@2c { compatible = "thead,c920", "riscv"; - reg = <44>; + reg = <0x2c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1632,9 +1632,9 @@ }; }; - cpu45: cpu@45 { + cpu45: cpu@2d { compatible = "thead,c920", "riscv"; - reg = <45>; + reg = <0x2d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1668,9 +1668,9 @@ }; }; - cpu46: cpu@46 { + cpu46: cpu@2e { compatible = "thead,c920", "riscv"; - reg = <46>; + reg = <0x2e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1704,9 +1704,9 @@ }; }; - cpu47: cpu@47 { + cpu47: cpu@2f { compatible = "thead,c920", "riscv"; - reg = <47>; + reg = <0x2f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1740,9 +1740,9 @@ }; }; - cpu48: cpu@48 { + cpu48: cpu@30 { compatible = "thead,c920", "riscv"; - reg = <48>; + reg = <0x30>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1776,9 +1776,9 @@ }; }; - cpu49: cpu@49 { + cpu49: cpu@31 { compatible = "thead,c920", "riscv"; - reg = <49>; + reg = <0x31>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1812,9 +1812,9 @@ }; }; - cpu50: cpu@50 { + cpu50: cpu@32 { compatible = "thead,c920", "riscv"; - reg = <50>; + reg = <0x32>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1848,9 +1848,9 @@ }; }; - cpu51: cpu@51 { + cpu51: cpu@33 { compatible = "thead,c920", "riscv"; - reg = <51>; + reg = <0x33>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1884,9 +1884,9 @@ }; }; - cpu52: cpu@52 { + cpu52: cpu@34 { compatible = "thead,c920", "riscv"; - reg = <52>; + reg = <0x34>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1920,9 +1920,9 @@ }; }; - cpu53: cpu@53 { + cpu53: cpu@35 { compatible = "thead,c920", "riscv"; - reg = <53>; + reg = <0x35>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1956,9 +1956,9 @@ }; }; - cpu54: cpu@54 { + cpu54: cpu@36 { compatible = "thead,c920", "riscv"; - reg = <54>; + reg = <0x36>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1992,9 +1992,9 @@ }; }; - cpu55: cpu@55 { + cpu55: cpu@37 { compatible = "thead,c920", "riscv"; - reg = <55>; + reg = <0x37>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2028,9 +2028,9 @@ }; }; - cpu56: cpu@56 { + cpu56: cpu@38 { compatible = "thead,c920", "riscv"; - reg = <56>; + reg = <0x38>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2064,9 +2064,9 @@ }; }; - cpu57: cpu@57 { + cpu57: cpu@39 { compatible = "thead,c920", "riscv"; - reg = <57>; + reg = <0x39>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2100,9 +2100,9 @@ }; }; - cpu58: cpu@58 { + cpu58: cpu@3a { compatible = "thead,c920", "riscv"; - reg = <58>; + reg = <0x3a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2136,9 +2136,9 @@ }; }; - cpu59: cpu@59 { + cpu59: cpu@3b { compatible = "thead,c920", "riscv"; - reg = <59>; + reg = <0x3b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2172,9 +2172,9 @@ }; }; - cpu60: cpu@60 { + cpu60: cpu@3c { compatible = "thead,c920", "riscv"; - reg = <60>; + reg = <0x3c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2208,9 +2208,9 @@ }; }; - cpu61: cpu@61 { + cpu61: cpu@3d { compatible = "thead,c920", "riscv"; - reg = <61>; + reg = <0x3d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2244,9 +2244,9 @@ }; }; - cpu62: cpu@62 { + cpu62: cpu@3e { compatible = "thead,c920", "riscv"; - reg = <62>; + reg = <0x3e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2280,9 +2280,9 @@ }; }; - cpu63: cpu@63 { + cpu63: cpu@3f { compatible = "thead,c920", "riscv"; - reg = <63>; + reg = <0x3f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; From 778bb4939d45768f4ec5bde32fcc361e95de1cab Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sun, 26 Apr 2026 09:34:49 +0800 Subject: [PATCH 0940/3297] riscv: dts: sophgo: sg2042: use hex for CPU unit address [ Upstream commit a7e658907686528fe06a11828b04a3e42df9ef18 ] Previous the CPU unit address cpu of sg2042 use decimal, it is not following the general convention for unit addresses of the OF. Convent the unit address to hex to resolve this problem. The introduces a small change for the CPU node name, but it should affect nothing since there is no direct full-path reference to these CPU nodes. Fixes: ae5bac370ed4 ("riscv: dts: sophgo: Add initial device tree of Sophgo SRD3-10") Tested-by: Chen Wang # Pioneerbox. Reviewed-by: Guo Ren Reviewed-by: Chen Wang Acked-by: Conor Dooley Tested-by: Chen Wang on Pioneerbox. Link: https://patch.msgid.link/20260426013449.694435-3-inochiama@gmail.com Signed-off-by: Inochi Amaoto Signed-off-by: Chen Wang Signed-off-by: Sasha Levin --- arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi | 236 ++++++++++---------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi index 94a4b71acad32..e8143a7f4dbaa 100644 --- a/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi +++ b/arch/riscv/boot/dts/sophgo/sg2042-cpus.dtsi @@ -263,7 +263,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <0>; + reg = <0x0>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -291,7 +291,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <1>; + reg = <0x1>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -319,7 +319,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <2>; + reg = <0x2>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -347,7 +347,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <3>; + reg = <0x3>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -375,7 +375,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <4>; + reg = <0x4>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -403,7 +403,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <5>; + reg = <0x5>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -431,7 +431,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <6>; + reg = <0x6>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -459,7 +459,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <7>; + reg = <0x7>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -487,7 +487,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <8>; + reg = <0x8>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -515,7 +515,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <9>; + reg = <0x9>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -533,7 +533,7 @@ }; }; - cpu10: cpu@10 { + cpu10: cpu@a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -543,7 +543,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <10>; + reg = <0xa>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -561,7 +561,7 @@ }; }; - cpu11: cpu@11 { + cpu11: cpu@b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -571,7 +571,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <11>; + reg = <0xb>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -589,7 +589,7 @@ }; }; - cpu12: cpu@12 { + cpu12: cpu@c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -599,7 +599,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <12>; + reg = <0xc>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -617,7 +617,7 @@ }; }; - cpu13: cpu@13 { + cpu13: cpu@d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -627,7 +627,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <13>; + reg = <0xd>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -645,7 +645,7 @@ }; }; - cpu14: cpu@14 { + cpu14: cpu@e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -655,7 +655,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <14>; + reg = <0xe>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -673,7 +673,7 @@ }; }; - cpu15: cpu@15 { + cpu15: cpu@f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -683,7 +683,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <15>; + reg = <0xf>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -701,7 +701,7 @@ }; }; - cpu16: cpu@16 { + cpu16: cpu@10 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -711,7 +711,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <16>; + reg = <0x10>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -729,7 +729,7 @@ }; }; - cpu17: cpu@17 { + cpu17: cpu@11 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -739,7 +739,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <17>; + reg = <0x11>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -757,7 +757,7 @@ }; }; - cpu18: cpu@18 { + cpu18: cpu@12 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -767,7 +767,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <18>; + reg = <0x12>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -785,7 +785,7 @@ }; }; - cpu19: cpu@19 { + cpu19: cpu@13 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -795,7 +795,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <19>; + reg = <0x13>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -813,7 +813,7 @@ }; }; - cpu20: cpu@20 { + cpu20: cpu@14 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -823,7 +823,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <20>; + reg = <0x14>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -841,7 +841,7 @@ }; }; - cpu21: cpu@21 { + cpu21: cpu@15 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -851,7 +851,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <21>; + reg = <0x15>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -869,7 +869,7 @@ }; }; - cpu22: cpu@22 { + cpu22: cpu@16 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -879,7 +879,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <22>; + reg = <0x16>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -897,7 +897,7 @@ }; }; - cpu23: cpu@23 { + cpu23: cpu@17 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -907,7 +907,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <23>; + reg = <0x17>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -925,7 +925,7 @@ }; }; - cpu24: cpu@24 { + cpu24: cpu@18 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -935,7 +935,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <24>; + reg = <0x18>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -953,7 +953,7 @@ }; }; - cpu25: cpu@25 { + cpu25: cpu@19 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -963,7 +963,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <25>; + reg = <0x19>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -981,7 +981,7 @@ }; }; - cpu26: cpu@26 { + cpu26: cpu@1a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -991,7 +991,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <26>; + reg = <0x1a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1009,7 +1009,7 @@ }; }; - cpu27: cpu@27 { + cpu27: cpu@1b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1019,7 +1019,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <27>; + reg = <0x1b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1037,7 +1037,7 @@ }; }; - cpu28: cpu@28 { + cpu28: cpu@1c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1047,7 +1047,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <28>; + reg = <0x1c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1065,7 +1065,7 @@ }; }; - cpu29: cpu@29 { + cpu29: cpu@1d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1075,7 +1075,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <29>; + reg = <0x1d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1093,7 +1093,7 @@ }; }; - cpu30: cpu@30 { + cpu30: cpu@1e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1103,7 +1103,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <30>; + reg = <0x1e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1121,7 +1121,7 @@ }; }; - cpu31: cpu@31 { + cpu31: cpu@1f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1131,7 +1131,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <31>; + reg = <0x1f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1149,7 +1149,7 @@ }; }; - cpu32: cpu@32 { + cpu32: cpu@20 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1159,7 +1159,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <32>; + reg = <0x20>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1177,7 +1177,7 @@ }; }; - cpu33: cpu@33 { + cpu33: cpu@21 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1187,7 +1187,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <33>; + reg = <0x21>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1205,7 +1205,7 @@ }; }; - cpu34: cpu@34 { + cpu34: cpu@22 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1215,7 +1215,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <34>; + reg = <0x22>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1233,7 +1233,7 @@ }; }; - cpu35: cpu@35 { + cpu35: cpu@23 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1243,7 +1243,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <35>; + reg = <0x23>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1261,7 +1261,7 @@ }; }; - cpu36: cpu@36 { + cpu36: cpu@24 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1271,7 +1271,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <36>; + reg = <0x24>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1289,7 +1289,7 @@ }; }; - cpu37: cpu@37 { + cpu37: cpu@25 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1299,7 +1299,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <37>; + reg = <0x25>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1317,7 +1317,7 @@ }; }; - cpu38: cpu@38 { + cpu38: cpu@26 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1327,7 +1327,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <38>; + reg = <0x26>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1345,7 +1345,7 @@ }; }; - cpu39: cpu@39 { + cpu39: cpu@27 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1355,7 +1355,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <39>; + reg = <0x27>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1373,7 +1373,7 @@ }; }; - cpu40: cpu@40 { + cpu40: cpu@28 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1383,7 +1383,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <40>; + reg = <0x28>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1401,7 +1401,7 @@ }; }; - cpu41: cpu@41 { + cpu41: cpu@29 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1411,7 +1411,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <41>; + reg = <0x29>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1429,7 +1429,7 @@ }; }; - cpu42: cpu@42 { + cpu42: cpu@2a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1439,7 +1439,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <42>; + reg = <0x2a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1457,7 +1457,7 @@ }; }; - cpu43: cpu@43 { + cpu43: cpu@2b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1467,7 +1467,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <43>; + reg = <0x2b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1485,7 +1485,7 @@ }; }; - cpu44: cpu@44 { + cpu44: cpu@2c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1495,7 +1495,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <44>; + reg = <0x2c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1513,7 +1513,7 @@ }; }; - cpu45: cpu@45 { + cpu45: cpu@2d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1523,7 +1523,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <45>; + reg = <0x2d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1541,7 +1541,7 @@ }; }; - cpu46: cpu@46 { + cpu46: cpu@2e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1551,7 +1551,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <46>; + reg = <0x2e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1569,7 +1569,7 @@ }; }; - cpu47: cpu@47 { + cpu47: cpu@2f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1579,7 +1579,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <47>; + reg = <0x2f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1597,7 +1597,7 @@ }; }; - cpu48: cpu@48 { + cpu48: cpu@30 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1607,7 +1607,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <48>; + reg = <0x30>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1625,7 +1625,7 @@ }; }; - cpu49: cpu@49 { + cpu49: cpu@31 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1635,7 +1635,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <49>; + reg = <0x31>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1653,7 +1653,7 @@ }; }; - cpu50: cpu@50 { + cpu50: cpu@32 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1663,7 +1663,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <50>; + reg = <0x32>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1681,7 +1681,7 @@ }; }; - cpu51: cpu@51 { + cpu51: cpu@33 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1691,7 +1691,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <51>; + reg = <0x33>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1709,7 +1709,7 @@ }; }; - cpu52: cpu@52 { + cpu52: cpu@34 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1719,7 +1719,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <52>; + reg = <0x34>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1737,7 +1737,7 @@ }; }; - cpu53: cpu@53 { + cpu53: cpu@35 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1747,7 +1747,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <53>; + reg = <0x35>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1765,7 +1765,7 @@ }; }; - cpu54: cpu@54 { + cpu54: cpu@36 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1775,7 +1775,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <54>; + reg = <0x36>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1793,7 +1793,7 @@ }; }; - cpu55: cpu@55 { + cpu55: cpu@37 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1803,7 +1803,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <55>; + reg = <0x37>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1821,7 +1821,7 @@ }; }; - cpu56: cpu@56 { + cpu56: cpu@38 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1831,7 +1831,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <56>; + reg = <0x38>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1849,7 +1849,7 @@ }; }; - cpu57: cpu@57 { + cpu57: cpu@39 { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1859,7 +1859,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <57>; + reg = <0x39>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1877,7 +1877,7 @@ }; }; - cpu58: cpu@58 { + cpu58: cpu@3a { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1887,7 +1887,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <58>; + reg = <0x3a>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1905,7 +1905,7 @@ }; }; - cpu59: cpu@59 { + cpu59: cpu@3b { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1915,7 +1915,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <59>; + reg = <0x3b>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1933,7 +1933,7 @@ }; }; - cpu60: cpu@60 { + cpu60: cpu@3c { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1943,7 +1943,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <60>; + reg = <0x3c>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1961,7 +1961,7 @@ }; }; - cpu61: cpu@61 { + cpu61: cpu@3d { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1971,7 +1971,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <61>; + reg = <0x3d>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -1989,7 +1989,7 @@ }; }; - cpu62: cpu@62 { + cpu62: cpu@3e { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -1999,7 +1999,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <62>; + reg = <0x3e>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; @@ -2017,7 +2017,7 @@ }; }; - cpu63: cpu@63 { + cpu63: cpu@3f { compatible = "thead,c920", "riscv"; device_type = "cpu"; riscv,isa = "rv64imafdc"; @@ -2027,7 +2027,7 @@ "zifencei", "zihpm", "zfh", "xtheadvector"; thead,vlenb = <16>; - reg = <63>; + reg = <0x3f>; i-cache-block-size = <64>; i-cache-size = <65536>; i-cache-sets = <512>; From c3b073a209a9baa691b744318ac929fecdd8847c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 00:29:37 -0400 Subject: [PATCH 0941/3297] configfs_lookup(): don't leave ->s_dentry dangling on failure [ Upstream commit 10da12d352b7b2bb330a8609fdda9a58bf0e9856 ] Normally ->s_dentry is cleared when dentry it's pointing to becomes negative (on eviction, realistically). However, that only happens if dentry gets to be positive in the first place; in case of inode allocation failure dentry never becomes positive, so ->d_iput() is not called at all. We do part of what normally would've been done by configfs_d_iput() (dropping the reference to configfs_dirent) manually, but we do not clear ->s_dentry there. Sloppy as it is, it does not matter in case of configfs_create_{dir,link}() - there configfs_dirent does not survive dropping the sole reference to it. However, for configfs_lookup() it *does* survive, with a dangling pointer to soon to be freed dentry sitting it its ->s_dentry. Subsequent getdents(2) in that directory will end up dereferencing that pointer in order to pick the inode number. Use after free... This is the minimal fix; the right approach is to set the linkage between dentry and configfs_dirent only after we know that we have an inode, but that takes more surgery and the bug had been there since 2006, so... Fixes: 3d0f89bb1694 ("configfs: Add permission and ownership to configfs objects") # 2.6.16-rc3 Reviewed-by: Jan Kara Reviewed-by: Breno Leitao Signed-off-by: Al Viro Signed-off-by: Sasha Levin --- fs/configfs/dir.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 81f4f06bc87e7..2898973f202a1 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -480,6 +480,9 @@ static struct dentry * configfs_lookup(struct inode *dir, inode = configfs_create(dentry, mode); if (IS_ERR(inode)) { + spin_lock(&configfs_dirent_lock); + sd->s_dentry = NULL; + spin_unlock(&configfs_dirent_lock); configfs_put(sd); return ERR_CAST(inode); } From 8d5ed4810e4794e7de5a727a33e515c37917dea2 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 23 May 2026 20:51:22 +0200 Subject: [PATCH 0942/3297] lockdep/selftests: Restore migrate_disable() state on PREEMPT_RT [ Upstream commit d8c897b20bf4d4cbb1e935a8ceb666bcc0f82580 ] The lockdep selftests deliberately run unbalanced locking patterns. dotest() restores the task state they leave behind before running the next testcase. On PREEMPT_RT, spin_lock() uses migrate_disable() instead of disabling preemption. dotest() cleans up the resulting migration-disabled state, but that cleanup is still guarded by CONFIG_SMP. That used to match the scheduler data model, where migration_disabled was also CONFIG_SMP-only. The commit referenced below made SMP scheduler state unconditional, so CONFIG_SMP=n PREEMPT_RT kernels with CONFIG_DEBUG_LOCKING_API_SELFTESTS=y report success from the selftests and then trip over stale current->migration_disabled state: releasing a pinned lock bad: scheduling from the idle thread! Kernel panic - not syncing: Fatal exception Save and restore current->migration_disabled for every PREEMPT_RT build. Fixes: cac5cefbade9 ("sched/smp: Make SMP unconditional") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260523185123.17482-2-kmehltretter@gmail.com Signed-off-by: Sasha Levin --- lib/locking-selftest.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index ed99344317f50..7605a0da4eb61 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -1431,9 +1431,7 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) { int saved_preempt_count = preempt_count(); #ifdef CONFIG_PREEMPT_RT -#ifdef CONFIG_SMP int saved_mgd_count = current->migration_disabled; -#endif int saved_rcu_count = current->rcu_read_lock_nesting; #endif @@ -1471,10 +1469,8 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) preempt_count_set(saved_preempt_count); #ifdef CONFIG_PREEMPT_RT -#ifdef CONFIG_SMP while (current->migration_disabled > saved_mgd_count) migrate_enable(); -#endif while (current->rcu_read_lock_nesting > saved_rcu_count) rcu_read_unlock(); From 8cbd587e8cdb6eb1bc304df87faf272e90019ecb Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 23 May 2026 20:51:23 +0200 Subject: [PATCH 0943/3297] lockdep/selftests: Restore sched_rt_mutex state on PREEMPT_RT [ Upstream commit 06961d60a0e410bf8df69ccff7eb1bd824912b8f ] The WW-mutex selftests deliberately exercise failing lock paths. On PREEMPT_RT, some of those paths enter the RT-mutex scheduler helpers. The change referenced by the Fixes tag made those helpers track RT-mutex scheduling state in current->sched_rt_mutex. The bit is normally cleared by the matching post-schedule helper, but some WW-mutex selftests disable the runtime debug_locks flag before that happens. With debug_locks cleared, lockdep_assert() does not evaluate the expression that clears the bit, leaving stale state for the next testcase. With CONFIG_PREEMPT_RT=y and CONFIG_DEBUG_LOCKING_API_SELFTESTS=y, that stale state produces warnings such as: WARNING: kernel/sched/core.c:7557 at rt_mutex_pre_schedule+0x26/0x2d RIP: 0010:rt_mutex_pre_schedule+0x26/0x2d Save and restore current->sched_rt_mutex around each testcase, matching the existing PREEMPT_RT cleanup for task-local migration and RCU state. Fixes: d14f9e930b90 ("locking/rtmutex: Use rt_mutex specific scheduler helpers") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260523185123.17482-3-kmehltretter@gmail.com Signed-off-by: Sasha Levin --- lib/locking-selftest.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 7605a0da4eb61..7f74e0549c32f 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -1433,6 +1433,7 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) #ifdef CONFIG_PREEMPT_RT int saved_mgd_count = current->migration_disabled; int saved_rcu_count = current->rcu_read_lock_nesting; + int saved_sched_rt_mutex = current->sched_rt_mutex; #endif WARN_ON(irqs_disabled()); @@ -1469,6 +1470,8 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) preempt_count_set(saved_preempt_count); #ifdef CONFIG_PREEMPT_RT + current->sched_rt_mutex = saved_sched_rt_mutex; + while (current->migration_disabled > saved_mgd_count) migrate_enable(); From e82d515092a0cf2bca92f399d5c330c324879cea Mon Sep 17 00:00:00 2001 From: Li Chen Date: Wed, 13 May 2026 16:58:17 +0800 Subject: [PATCH 0944/3297] ext4: fix fast commit wait/wake bit mapping on 64-bit [ Upstream commit 8b3bc93fee6771775243665a0cf31857d6659775 ] On 64-bit, ext4 dynamic inode states live in the upper half of i_flags, and ext4_test_inode_state() applies the corresponding +32 offset. The fast-commit wait and wake paths open-coded the wait key with the raw EXT4_STATE_* value. Add small helpers for the state wait word and bit, and use them for the FC_COMMITTING and FC_FLUSHING_DATA waits so the wait key follows the same mapping as the state helpers. Fixes: 857d32f26181 ("ext4: rework fast commit commit path") Reported-by: Sashiko AI review Signed-off-by: Li Chen Reviewed-by: Baokun Li Reviewed-by: Zhang Yi Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260513085818.552432-1-me@linux.beauty Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/ext4.h | 20 +++++++++++++++++ fs/ext4/fast_commit.c | 50 ++++++++++++++++--------------------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 9bf82b568cfe2..54be698b9a1c1 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1990,6 +1990,8 @@ EXT4_INODE_BIT_FNS(flag, flags, 0) static inline int ext4_test_inode_state(struct inode *inode, int bit); static inline void ext4_set_inode_state(struct inode *inode, int bit); static inline void ext4_clear_inode_state(struct inode *inode, int bit); +static inline unsigned long *ext4_inode_state_wait_word(struct inode *inode); +static inline int ext4_inode_state_wait_bit(int bit); #if (BITS_PER_LONG < 64) EXT4_INODE_BIT_FNS(state, state_flags, 0) @@ -2005,6 +2007,24 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) /* We depend on the fact that callers will set i_flags */ } #endif + +static inline unsigned long *ext4_inode_state_wait_word(struct inode *inode) +{ +#if (BITS_PER_LONG < 64) + return &EXT4_I(inode)->i_state_flags; +#else + return &EXT4_I(inode)->i_flags; +#endif +} + +static inline int ext4_inode_state_wait_bit(int bit) +{ +#if (BITS_PER_LONG < 64) + return bit; +#else + return bit + 32; +#endif +} #else /* Assume that user mode programs are passing in an ext4fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index bab522ca1573d..2c4e628acb8d1 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -233,6 +233,8 @@ void ext4_fc_del(struct inode *inode) struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_fc_dentry_update *fc_dentry; wait_queue_head_t *wq; + unsigned long *wait_word = ext4_inode_state_wait_word(inode); + int wait_bit = ext4_inode_state_wait_bit(EXT4_STATE_FC_FLUSHING_DATA); int alloc_ctx; if (ext4_fc_disabled(inode->i_sb)) @@ -262,17 +264,9 @@ void ext4_fc_del(struct inode *inode) WARN_ON(ext4_test_inode_state(inode, EXT4_STATE_FC_COMMITTING) && !ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE)); while (ext4_test_inode_state(inode, EXT4_STATE_FC_FLUSHING_DATA)) { -#if (BITS_PER_LONG < 64) - DEFINE_WAIT_BIT(wait, &ei->i_state_flags, - EXT4_STATE_FC_FLUSHING_DATA); - wq = bit_waitqueue(&ei->i_state_flags, - EXT4_STATE_FC_FLUSHING_DATA); -#else - DEFINE_WAIT_BIT(wait, &ei->i_flags, - EXT4_STATE_FC_FLUSHING_DATA); - wq = bit_waitqueue(&ei->i_flags, - EXT4_STATE_FC_FLUSHING_DATA); -#endif + DEFINE_WAIT_BIT(wait, wait_word, wait_bit); + + wq = bit_waitqueue(wait_word, wait_bit); prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE); if (ext4_test_inode_state(inode, EXT4_STATE_FC_FLUSHING_DATA)) { ext4_fc_unlock(inode->i_sb, alloc_ctx); @@ -552,6 +546,8 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); wait_queue_head_t *wq; + unsigned long *wait_word = ext4_inode_state_wait_word(inode); + int wait_bit = ext4_inode_state_wait_bit(EXT4_STATE_FC_COMMITTING); int ret; if (S_ISDIR(inode->i_mode)) @@ -577,17 +573,9 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode) lockdep_assert_not_held(&ei->i_data_sem); while (ext4_test_inode_state(inode, EXT4_STATE_FC_COMMITTING)) { -#if (BITS_PER_LONG < 64) - DEFINE_WAIT_BIT(wait, &ei->i_state_flags, - EXT4_STATE_FC_COMMITTING); - wq = bit_waitqueue(&ei->i_state_flags, - EXT4_STATE_FC_COMMITTING); -#else - DEFINE_WAIT_BIT(wait, &ei->i_flags, - EXT4_STATE_FC_COMMITTING); - wq = bit_waitqueue(&ei->i_flags, - EXT4_STATE_FC_COMMITTING); -#endif + DEFINE_WAIT_BIT(wait, wait_word, wait_bit); + + wq = bit_waitqueue(wait_word, wait_bit); prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE); if (ext4_test_inode_state(inode, EXT4_STATE_FC_COMMITTING)) schedule(); @@ -1050,6 +1038,8 @@ static int ext4_fc_perform_commit(journal_t *journal) int ret = 0; u32 crc = 0; int alloc_ctx; + int flushing_wait_bit = + ext4_inode_state_wait_bit(EXT4_STATE_FC_FLUSHING_DATA); /* * Step 1: Mark all inodes on s_fc_q[MAIN] with @@ -1075,11 +1065,8 @@ static int ext4_fc_perform_commit(journal_t *journal) list_for_each_entry(iter, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) { ext4_clear_inode_state(&iter->vfs_inode, EXT4_STATE_FC_FLUSHING_DATA); -#if (BITS_PER_LONG < 64) - wake_up_bit(&iter->i_state_flags, EXT4_STATE_FC_FLUSHING_DATA); -#else - wake_up_bit(&iter->i_flags, EXT4_STATE_FC_FLUSHING_DATA); -#endif + wake_up_bit(ext4_inode_state_wait_word(&iter->vfs_inode), + flushing_wait_bit); } /* @@ -1295,6 +1282,8 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid) struct ext4_inode_info *ei; struct ext4_fc_dentry_update *fc_dentry; int alloc_ctx; + int committing_wait_bit = + ext4_inode_state_wait_bit(EXT4_STATE_FC_COMMITTING); if (full && sbi->s_fc_bh) sbi->s_fc_bh = NULL; @@ -1331,11 +1320,8 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid) * barrier in prepare_to_wait() in ext4_fc_track_inode(). */ smp_mb(); -#if (BITS_PER_LONG < 64) - wake_up_bit(&ei->i_state_flags, EXT4_STATE_FC_COMMITTING); -#else - wake_up_bit(&ei->i_flags, EXT4_STATE_FC_COMMITTING); -#endif + wake_up_bit(ext4_inode_state_wait_word(&ei->vfs_inode), + committing_wait_bit); } while (!list_empty(&sbi->s_fc_dentry_q[FC_Q_MAIN])) { From 4beed798daf4ddcbfa723d8d28a4e02ca62ed6bc Mon Sep 17 00:00:00 2001 From: Yunxiang Li Date: Mon, 1 Jun 2026 15:15:06 -0400 Subject: [PATCH 0945/3297] drm/amdgpu: set sub_block_index for mca ras sub-blocks [ Upstream commit baa286df5fb366e4aee5b747443dfc7194fdaa59 ] The mca ras sub-blocks (mp0, mp1, mpio) all share the AMDGPU_RAS_BLOCK__MCA block id and are distinguished only by sub_block_index. The ras manager object for an mca block is selected with: con->objs[AMDGPU_RAS_BLOCK__LAST + head->sub_block_index] Since the rework in commit 7f544c5488cf ("drm/amdgpu: Rework mca ras sw_init") moved the ras_comm setup into amdgpu_mca_mp*_ras_sw_init() but left sub_block_index unset, mp0/mp1/mpio all default to index 0 and collide on the same object slot. mp0 grabs the slot and creates its sysfs node first; mp1 (and mpio) then find the slot already in use, so amdgpu_ras_block_late_init() -> amdgpu_ras_sysfs_create() returns -EINVAL: amdgpu: mca.mp1 failed to execute ras_block_late_init_default! ret:-22 amdgpu: amdgpu_ras_late_init failed -22 amdgpu: amdgpu_device_ip_late_init failed amdgpu: Fatal error during GPU init The error is currently masked because amdgpu_ras_late_init() does not check the return value of amdgpu_ras_block_late_init_default(), but it already leaves mp1/mpio without their sysfs nodes and becomes a fatal init failure as soon as that return value is honored. Restore the per-sub-block sub_block_index assignment so each mca sub-block maps to its own object slot. Fixes: 7f544c5488cf ("drm/amdgpu: Rework mca ras sw_init") Signed-off-by: Yunxiang Li Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c index 3ca03b5e0f913..e1e4a61b1301c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c @@ -99,6 +99,7 @@ int amdgpu_mca_mp0_ras_sw_init(struct amdgpu_device *adev) strcpy(ras->ras_block.ras_comm.name, "mca.mp0"); ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA; + ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MP0; ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; adev->mca.mp0.ras_if = &ras->ras_block.ras_comm; @@ -123,6 +124,7 @@ int amdgpu_mca_mp1_ras_sw_init(struct amdgpu_device *adev) strcpy(ras->ras_block.ras_comm.name, "mca.mp1"); ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA; + ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MP1; ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; adev->mca.mp1.ras_if = &ras->ras_block.ras_comm; @@ -147,6 +149,7 @@ int amdgpu_mca_mpio_ras_sw_init(struct amdgpu_device *adev) strcpy(ras->ras_block.ras_comm.name, "mca.mpio"); ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA; + ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MPIO; ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; adev->mca.mpio.ras_if = &ras->ras_block.ras_comm; From 73f9f54d71749f7cdd51285e9d92b8ca4e9fd977 Mon Sep 17 00:00:00 2001 From: Woojin Ji Date: Wed, 3 Jun 2026 09:33:39 +0900 Subject: [PATCH 0946/3297] bpftool: Use libbpf error code for flow dissector query [ Upstream commit 8a7f2bff2165e53595d1e91c160b340f978c0ab7 ] bpf_prog_query() returns a negative errno on failure. query_flow_dissector() currently closes the namespace fd and then reads errno to decide whether -EINVAL means that the running kernel does not support flow dissector queries. That errno check controls behavior, not just diagnostics: -EINVAL is handled as a non-fatal old-kernel case, while any other error makes bpftool net fail. The namespace fd is opened read-only, so close() is not expected to commonly fail in normal use. Still, the BPF_PROG_QUERY error is already available in err, and reading errno after an intervening close() is fragile. If close() does change errno, the compatibility branch may be based on close()'s error instead of the BPF_PROG_QUERY result. This was reproduced with an LD_PRELOAD fault injector that forced BPF_PROG_QUERY for BPF_FLOW_DISSECTOR to fail with EINVAL and then forced close() on the netns fd to fail with EIO. The unpatched bpftool reported "can't query prog: Input/output error". With this change, the same injected failure is handled as the intended non-fatal EINVAL compatibility case. Use the libbpf-returned error code instead. Keep the existing errno reset in the non-fatal path to preserve batch mode behavior. The success path is unchanged. Fixes: 7f0c57fec80f ("bpftool: show flow_dissector attachment status") Signed-off-by: Woojin Ji Signed-off-by: Andrii Nakryiko Acked-by: Leon Hwang Acked-by: Yonghong Song Acked-by: Quentin Monnet Link: https://lore.kernel.org/bpf/20260603003339.33791-1-random6.xyz@gmail.com Assisted-by: ChatGPT:gpt-5.5 Signed-off-by: Sasha Levin --- tools/bpf/bpftool/net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c index 1a06b0b5eef35..f669716a588b4 100644 --- a/tools/bpf/bpftool/net.c +++ b/tools/bpf/bpftool/net.c @@ -603,14 +603,14 @@ static int query_flow_dissector(struct bpf_attach_info *attach_info) &attach_flags, prog_ids, &prog_cnt); close(fd); if (err) { - if (errno == EINVAL) { + if (err == -EINVAL) { /* Older kernel's don't support querying * flow dissector programs. */ errno = 0; return 0; } - p_err("can't query prog: %s", strerror(errno)); + p_err("can't query prog: %s", strerror(-err)); return -1; } From 7f229d27bf27c7e589eca690d8612763a7a4801f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 2 Mar 2026 03:51:49 -0500 Subject: [PATCH 0947/3297] vhost: fix vhost_get_avail_idx for a non empty ring [ Upstream commit 09861858a68342f851f71c669ac0f69865c32151 ] vhost_get_avail_idx is supposed to report whether it has updated vq->avail_idx. Instead, it returns whether all entries have been consumed, which is usually the same. But not always - in drivers/vhost/net.c and when mergeable buffers have been enabled, the driver checks whether the combined entries are big enough to store an incoming packet. If not, the driver re-enables notifications with available entries still in the ring. The incorrect return value from vhost_get_avail_idx propagates through vhost_enable_notify and causes the host to livelock if the guest is not making progress, as vhost will immediately disable notifications and retry using the available entries. This goes back to commit d3bb267bbdcb ("vhost: cache avail index in vhost_enable_notify()") which changed vhost_enable_notify() to compare the freshly read avail index against vq->last_avail_idx instead of the previously cached vq->avail_idx. Commit 7ad472397667 ("vhost: move smp_rmb() into vhost_get_avail_idx()") then carried over the same comparison when refactoring vhost_enable_notify() to call the unified vhost_get_avail_idx(). The obvious fix is to make vhost_get_avail_idx do what the comment says it does and report whether new entries have been added. Reported-by: ShuangYu Fixes: d3bb267bbdcb ("vhost: cache avail index in vhost_enable_notify()") Cc: Stefan Hajnoczi Acked-by: Jason Wang Reviewed-by: Stefano Garzarella Signed-off-by: Michael S. Tsirkin Message-Id: <559b04ae6ce52973c535dc47e461638b7f4c3d63.1772441455.git.mst@redhat.com> Signed-off-by: Sasha Levin --- drivers/vhost/vhost.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index bccdc9eab267a..6a7b22650aa72 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1526,6 +1526,7 @@ static void vhost_dev_unlock_vqs(struct vhost_dev *d) static inline int vhost_get_avail_idx(struct vhost_virtqueue *vq) { __virtio16 idx; + u16 avail_idx; int r; r = vhost_get_avail(vq, idx, &vq->avail->idx); @@ -1536,17 +1537,19 @@ static inline int vhost_get_avail_idx(struct vhost_virtqueue *vq) } /* Check it isn't doing very strange thing with available indexes */ - vq->avail_idx = vhost16_to_cpu(vq, idx); - if (unlikely((u16)(vq->avail_idx - vq->last_avail_idx) > vq->num)) { + avail_idx = vhost16_to_cpu(vq, idx); + if (unlikely((u16)(avail_idx - vq->last_avail_idx) > vq->num)) { vq_err(vq, "Invalid available index change from %u to %u", - vq->last_avail_idx, vq->avail_idx); + vq->last_avail_idx, avail_idx); return -EINVAL; } /* We're done if there is nothing new */ - if (vq->avail_idx == vq->last_avail_idx) + if (avail_idx == vq->avail_idx) return 0; + vq->avail_idx = avail_idx; + /* * We updated vq->avail_idx so we need a memory barrier between * the index read above and the caller reading avail ring entries. From f5102e0fc3c6dc8685549891a96e4589fdb3e211 Mon Sep 17 00:00:00 2001 From: Pranjal Shrivastava Date: Thu, 4 Jun 2026 14:03:10 +0800 Subject: [PATCH 0948/3297] iommu/vt-d: Fix RB-tree corruption in probe error path [ Upstream commit 43bd9e6d5513cb1edbafdeef146a1edc3aaced56 ] The info->node RB-tree member is zero-initialized via kzalloc. If a device does not support ATS, the device_rbtree_insert() call is skipped. If a subsequent probe step fails, the error path jumps to device_rbtree_remove(), which misinterprets the zeroed node as a tree root and corrupts the device RB-tree. Fix this by explicitly initializing the RB-node as empty using RB_CLEAR_NODE() during initialization and guarding the removal with RB_EMPTY_NODE(). Fixes: 4f1492efb495 ("iommu/vt-d: Revert ATS timing change to fix boot failure") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260525205628.CD4431F000E9@smtp.kernel.org/ Suggested-by: Baolu Lu Signed-off-by: Pranjal Shrivastava Link: https://lore.kernel.org/r/20260531170254.60493-2-praan@google.com Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/intel/iommu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index db0cf436c8395..cee1851b69245 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -164,7 +164,10 @@ static void device_rbtree_remove(struct device_domain_info *info) unsigned long flags; spin_lock_irqsave(&iommu->device_rbtree_lock, flags); - rb_erase(&info->node, &iommu->device_rbtree); + if (!RB_EMPTY_NODE(&info->node)) { + rb_erase(&info->node, &iommu->device_rbtree); + RB_CLEAR_NODE(&info->node); + } spin_unlock_irqrestore(&iommu->device_rbtree_lock, flags); } @@ -3787,6 +3790,7 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev) info->dev = dev; info->iommu = iommu; + RB_CLEAR_NODE(&info->node); if (dev_is_pci(dev)) { if (ecap_dev_iotlb_support(iommu->ecap) && pci_ats_supported(pdev) && From 4e18e9361aab05fadf114c1ed8746be1388c0f39 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 1 Jun 2026 20:28:46 +0530 Subject: [PATCH 0949/3297] perf/x86/amd/core: Always use the NMI latency mitigation [ Upstream commit 73a4c02f94a98d94480c3e5c81450215a4da05ba ] Commit df4d29732fda ("perf/x86/amd: Change/fix NMI latency mitigation to use a timestamp") fixed handling of late-arriving NMIs but limited the mitigation to processors having X86_FEATURE_PERFCTR_CORE. However, it is unclear if processors without this feature are also affected. When Mediated vPMU is enabled on affected hardware, it is also possible to bypass the fix inside KVM guests if X86_FEATURE_PERFCTR_CORE is removed from the guest CPUID (e.g. using "-cpu host,-perfctr-core" with QEMU). Hence, use the mitigation at all times. Fixes: df4d29732fda ("perf/x86/amd: Change/fix NMI latency mitigation to use a timestamp") Signed-off-by: Sandipan Das Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/29a3c970da289ab8f24282933bdb36545c0403e8.1780325517.git.sandipan.das@amd.com Signed-off-by: Sasha Levin --- arch/x86/events/amd/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index 8868f5f5379ba..e7d30908f25e5 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -1411,12 +1411,12 @@ static int __init amd_core_pmu_init(void) u64 even_ctr_mask = 0ULL; int i; - if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) - return 0; - /* Avoid calculating the value each time in the NMI handler */ perf_nmi_window = msecs_to_jiffies(100); + if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) + return 0; + /* * If core performance counter extensions exists, we must use * MSR_F15H_PERF_CTL/MSR_F15H_PERF_CTR msrs. See also From 58cbb1c2aadf84309521d8fc2165500f55b89645 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Tue, 2 Jun 2026 07:49:02 -0700 Subject: [PATCH 0950/3297] perf/x86/intel/uncore: Fix discovery unit lookup for multi-die systems [ Upstream commit 63f48abd55d0417996bca86022925c853a2b436b ] In uncore_find_add_unit(), PMON units with the same unit ID may be added to the uncore discovery RB-tree for different dies. These units are distinguished by node->die. However, intel_generic_uncore_box_ctl() uses a fixed die ID of -1 when looking up the discovery unit, which may retrieve the wrong node on multi-die systems. Use box->dieid instead so the correct discovery unit is selected. No functional issue has been observed so far because currently supported platforms happen to use the same unit control register for such units. Remove WARN_ON_ONCE() because with the above change a NULL unit can be expected, e.g. when a CPU die is offline during uncore enumeration and the unit is not added to the RB-tree. In this case, intel_uncore_find_discovery_unit() returns NULL once the die becomes online, and it is expected that the PMU box is not functional for that die. Fixes: b1d9ea2e1ca4 ("perf/x86/uncore: Apply the unit control RB tree to MSR uncore units") Signed-off-by: Zide Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Link: https://patch.msgid.link/20260602144908.263680-2-zide.chen@intel.com Signed-off-by: Sasha Levin --- arch/x86/events/intel/uncore_discovery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c index c5adbe4409047..7dd3910d8de3a 100644 --- a/arch/x86/events/intel/uncore_discovery.c +++ b/arch/x86/events/intel/uncore_discovery.c @@ -480,8 +480,8 @@ static u64 intel_generic_uncore_box_ctl(struct intel_uncore_box *box) struct intel_uncore_discovery_unit *unit; unit = intel_uncore_find_discovery_unit(box->pmu->type->boxes, - -1, box->pmu->pmu_idx); - if (WARN_ON_ONCE(!unit)) + box->dieid, box->pmu->pmu_idx); + if (!unit) return 0; return unit->addr; From ac9e29b191a039e886f4a8108364fa452f2edb52 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 1 Jun 2026 17:43:05 +0530 Subject: [PATCH 0951/3297] perf/x86/amd/uncore: Use Node ID to identify DF and UMC domains [ Upstream commit 67d27727854def4a7e2b386429941f5c4741ccc4 ] For DF and UMC PMUs, a single context is shared across all CPUs that are connected to the same Data Fabric (DF) instance. Currently, the Package ID, which also happens to be the Socket ID, is used to identify DF instances. This approach works for configurations having a single IO Die (IOD) but fails in the following cases. * Older Zen 1 processors, where each chiplet has its own DF instance. * Any configurations with multiple DF instances or multiple IODs in the same package. The correct way to identify DF instances is through the Node ID (not to be confused with NUMA Node ID). This is available in ECX[7:0] of CPUID leaf 0x8000001e and returned via topology_amd_node_id(). Hence, replace usage of topology_logical_package_id() with topology_amd_node_id(). Fixes: 07888daa056e ("perf/x86/amd/uncore: Move discovery and registration") Signed-off-by: Sandipan Das Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/e7a71a727c6a7b118c23d3e469929c538c4665aa.1780315832.git.sandipan.das@amd.com Signed-off-by: Sasha Levin --- arch/x86/events/amd/uncore.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c index 9293ce50574da..9a13a9f21d2f8 100644 --- a/arch/x86/events/amd/uncore.c +++ b/arch/x86/events/amd/uncore.c @@ -700,7 +700,7 @@ void amd_uncore_df_ctx_scan(struct amd_uncore *uncore, unsigned int cpu) info.split.aux_data = 0; info.split.num_pmcs = NUM_COUNTERS_NB; info.split.gid = 0; - info.split.cid = topology_logical_package_id(cpu); + info.split.cid = topology_amd_node_id(cpu); if (pmu_version >= 2) { ebx.full = cpuid_ebx(EXT_PERFMON_DEBUG_FEATURES); @@ -999,8 +999,8 @@ void amd_uncore_umc_ctx_scan(struct amd_uncore *uncore, unsigned int cpu) cpuid(EXT_PERFMON_DEBUG_FEATURES, &eax, &ebx.full, &ecx, &edx); info.split.aux_data = ecx; /* stash active mask */ info.split.num_pmcs = ebx.split.num_umc_pmc; - info.split.gid = topology_logical_package_id(cpu); - info.split.cid = topology_logical_package_id(cpu); + info.split.gid = topology_amd_node_id(cpu); + info.split.cid = topology_amd_node_id(cpu); *per_cpu_ptr(uncore->info, cpu) = info; } From b64120d54278e87a9c21dca7c85b75d4d72d1d59 Mon Sep 17 00:00:00 2001 From: Antony Antony Date: Tue, 26 May 2026 21:07:01 +0200 Subject: [PATCH 0952/3297] xfrm: fix NAT-related field inheritance in SA migration [ Upstream commit 364e165e0b63e8142e76de83e96ae8e36c3b955a ] During SA migration via xfrm_state_clone_and_setup(), nat_keepalive_interval was silently dropped and never copied to the new SA. mapping_maxage was unconditionally copied even when migrating to a non-encapsulated SA. Both fields are only meaningful when UDP encapsulation (NAT-T) is in use. Move mapping_maxage and add nat_keepalive_interval inside the existing if (encap) block, so both are inherited when migrating with encapsulation and correctly absent when migrating without it. Fixes: f531d13bdfe3 ("xfrm: support sending NAT keepalives in ESP in UDP states") Reviewed-by: Sabrina Dubroca Signed-off-by: Antony Antony Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_state.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 898cddff498c7..efbe0135d2776 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2020,6 +2020,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, if (!x->encap) goto error; + x->mapping_maxage = orig->mapping_maxage; + x->nat_keepalive_interval = orig->nat_keepalive_interval; } if (orig->security) @@ -2054,7 +2056,6 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, x->km.seq = orig->km.seq; x->replay = orig->replay; x->preplay = orig->preplay; - x->mapping_maxage = orig->mapping_maxage; x->lastused = orig->lastused; x->new_mapping = 0; x->new_mapping_sport = 0; From b8d15e85596ad8993d42e0703a9741961a2f03eb Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 15:12:03 -0700 Subject: [PATCH 0953/3297] cxl/fwctl: Fix __fortify_panic [ Upstream commit 6c9d2e87df40d606f1c85143e9acb1ecff463d5e ] Fix a runtime assertion in cxlctl_get_supported_features(). Fortify complains that it is potentially overflowing the entries array per __counted_by_le(num_entries). Quiet the false positive by initializing @num_entries earlier. memcpy: detected buffer overflow: 48 byte write of buffer size 0 WARNING: lib/string_helpers.c:1036 at __fortify_report+0x4d/0xa0, CPU#7: fwctl/1398 RIP: 0010:__fortify_report+0x50/0xa0 Call Trace: __fortify_panic+0xd/0xf cxlctl_get_supported_features.cold+0x23/0x35 [cxl_core] Fixes: 4d1c09cef2c2 ("cxl: Add support for fwctl RPC command to enable CXL feature commands") Signed-off-by: Dan Williams Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519221204.1517773-2-djbw@kernel.org Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/features.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 4bc484b46f439..c3cb2060bbeac 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -423,6 +423,7 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, rpc_out->size = struct_size(feat_out, ents, requested); feat_out = &rpc_out->get_sup_feats_out; + feat_out->num_entries = cpu_to_le16(requested); for (i = start, pos = &feat_out->ents[0]; i < cxlfs->entries->num_features; i++, pos++) { @@ -444,7 +445,6 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, } } - feat_out->num_entries = cpu_to_le16(requested); feat_out->supported_feats = cpu_to_le16(cxlfs->entries->num_features); rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; *out_len = out_size; From 9a030fcb4b192a508113787ef28fb97e27dcf6f6 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 6 May 2026 16:50:42 -0400 Subject: [PATCH 0954/3297] drm/amdkfd: always resume_all after suspend_all [ Upstream commit 56ae73c92e200e630c2bdf1e98c88b86c8483b37 ] Need to restore any good queues even if the suspend_all failed for some. Always run remove_queue as that will schedule a GPU reset is removing the queue fails. v2: move resume_all after remove Fixes: eb067d65c33e ("drm/amdkfd: Update BadOpcode Interrupt handling with MES") Reviewed-by: Amber Lin Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- .../drm/amd/amdkfd/kfd_device_queue_manager.c | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 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 fa183a9db09bd..18d03f33c6202 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3073,32 +3073,24 @@ int kfd_dqm_suspend_bad_queue_mes(struct kfd_node *knode, u32 pasid, u32 doorbel list_for_each_entry(q, &qpd->queues_list, list) { if (q->doorbell_id == doorbell_id && q->properties.is_active) { - ret = suspend_all_queues_mes(dqm); - if (ret) { - dev_err(dev, "Suspending all queues failed"); - goto out; - } + /* suspend all queues will save any good queues and mark the rest as bad */ + suspend_all_queues_mes(dqm); q->properties.is_evicted = true; q->properties.is_active = false; decrement_queue_count(dqm, qpd, q); + /* this will remove the bad queue and sched a GPU reset if needed */ ret = remove_queue_mes(dqm, q, qpd); - if (ret) { - dev_err(dev, "Removing bad queue failed"); - goto out; - } - - ret = resume_all_queues_mes(dqm); if (ret) - dev_err(dev, "Resuming all queues failed"); - + dev_err(dev, "Removing bad queue failed"); + /* resume the good queues */ + resume_all_queues_mes(dqm); break; } } } -out: dqm_unlock(dqm); kfd_unref_process(p); return ret; From 9af58d10d0d8c08b822de5fc99e61f31a87ede8d Mon Sep 17 00:00:00 2001 From: Wandun Chen Date: Thu, 4 Jun 2026 09:53:32 +0800 Subject: [PATCH 0955/3297] of: reserved_mem: avoid post-init UAF when alloc_reserved_mem_array() fails [ Upstream commit e1686ca81dbf3edbde589b7daf312b45cbf76e03 ] The global pointer 'reserved_mem' continues to reference the reserved_mem_array which lives in __initdata if alloc_reserved_mem_array() fails. of_reserved_mem_lookup() is exported for post-init use, that would dereference freed memory and trigger a use-after-free. So reset reserved_mem_count to 0 when alloc_reserved_mem_array() fails. Fixes: 00c9a452a235 ("of: reserved_mem: Add code to dynamically allocate reserved_mem array") Signed-off-by: Wandun Chen Link: https://patch.msgid.link/20260604015332.3669384-1-chenwandun1@gmail.com Signed-off-by: Rob Herring (Arm) Signed-off-by: Sasha Levin --- drivers/of/of_reserved_mem.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index fe111d1ea7397..39d8305a16800 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -71,29 +71,32 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, * the initial static array is copied over to this new array and * the new array is used from this point on. */ -static void __init alloc_reserved_mem_array(void) +static int __init alloc_reserved_mem_array(void) { struct reserved_mem *new_array; size_t alloc_size, copy_size, memset_size; + int ret; + + if (!total_reserved_mem_cnt) + return 0; alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array)); if (alloc_size == SIZE_MAX) { - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + ret = -EOVERFLOW; + goto fail; } new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES); if (!new_array) { - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM); - return; + ret = -ENOMEM; + goto fail; } copy_size = array_size(reserved_mem_count, sizeof(*new_array)); if (copy_size == SIZE_MAX) { memblock_free(new_array, alloc_size); - total_reserved_mem_cnt = MAX_RESERVED_REGIONS; - pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); - return; + ret = -EOVERFLOW; + goto fail; } memset_size = alloc_size - copy_size; @@ -102,6 +105,12 @@ static void __init alloc_reserved_mem_array(void) memset(new_array + reserved_mem_count, 0, memset_size); reserved_mem = new_array; + return 0; + +fail: + pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret); + reserved_mem_count = 0; + return ret; } static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem); @@ -250,7 +259,8 @@ void __init fdt_scan_reserved_mem_reg_nodes(void) } /* Attempt dynamic allocation of a new reserved_mem array */ - alloc_reserved_mem_array(); + if (alloc_reserved_mem_array()) + return; if (__reserved_mem_check_root(node)) { pr_err("Reserved memory: unsupported node format, ignoring\n"); From bb44a7690a4d553da705919cf666a80f5ca9011c Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Mon, 25 May 2026 12:17:26 +0800 Subject: [PATCH 0956/3297] ocfs2: rebase copied fsdlm LVB pointers in locking_state [ Upstream commit 93612d48fa42b3d1a637eb9279e15281c611c000 ] The locking_state debugfs iterator snapshots struct ocfs2_lock_res by value under ocfs2_dlm_tracking_lock and later formats that copy in ocfs2_dlm_seq_show(). That is fine for the inline fields, but the userspace fsdlm stack stores the LVB through lksb_fsdlm.sb_lvbptr. Once the iterator drops the tracking lock, a copied non-NULL sb_lvbptr still points into the original lockres owner, so teardown can free that container before the debugfs dump walks the raw LVB bytes. Rebase the copied sb_lvbptr to the copied l_lksb before dumping the raw LVB. The seq snapshot already carries the inline LVB storage reserved in struct ocfs2_dlm_lksb, so the debugfs reader can dump the copied bytes without borrowing the original lockres lifetime. The buggy scenario involves two paths, with each column showing the order within that path: locking_state reader: lockres teardown: 1. ocfs2_dlm_seq_start()/next() 1. file release or another owner copies struct ocfs2_lock_res teardown reaches 2. ocfs2_dlm_seq_show() formats ocfs2_lock_res_free() the copied row 2. the lockres is removed from the 3. ocfs2_dlm_lvb() follows the tracking list copied sb_lvbptr 3. the owner frees the original lockres container Validation reproduced this kernel report: KASAN slab-use-after-free in ocfs2_dlm_seq_show+0x1bd/0x430 RIP: 0033:0x7f8ec4b1e29d The buggy address belongs to the object at ffff88810a1e0800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 368 bytes inside of freed 1024-byte region [ffff88810a1e0800, ffff88810a1e0c00) Read of size 1 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ocfs2_dlm_seq_show+0x1bd/0x430 (fs/ocfs2/dlmglue.c:3137) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 seq_read_iter+0x29d/0x790 seq_read+0x20a/0x280 find_held_lock+0x2b/0x80 rcu_read_unlock+0x18/0x70 full_proxy_read+0x9e/0xd0 vfs_read+0x12c/0x590 ksys_read+0xd2/0x170 do_user_addr_fault+0x65a/0x890 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 ocfs2_file_open+0x13e/0x300 do_dentry_open+0x233/0x7f0 vfs_open+0x5a/0x1b0 path_openat+0x66d/0x1540 do_file_open+0x186/0x2b0 do_sys_openat2+0xce/0x150 __x64_sys_openat+0xd0/0x140 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 ocfs2_file_release+0x138/0x260 __fput+0x1df/0x4b0 fput_close_sync+0xd2/0x170 __x64_sys_close+0x55/0x90 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Link: https://lore.kernel.org/20260525041726.4112882-1-rollkingzzc@gmail.com Fixes: cf4d8d75d8ab ("ocfs2: add fsdlm to stackglue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/dlmglue.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 92a6149da9c10..8ade51f7ffa8b 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3134,6 +3134,22 @@ static void *ocfs2_dlm_seq_next(struct seq_file *m, void *v, loff_t *pos) * - Add last pr/ex unlock times and first lock wait time in usecs */ #define OCFS2_DLM_DEBUG_STR_VERSION 4 + +/* + * The debug iterator snapshots lockres by value, so a userspace-stack LVB + * pointer copied from the original lockres must be rebased to the copied + * lksb before the dump walks the raw bytes. + */ +static void ocfs2_dlm_seq_rebase_lvb(struct ocfs2_lock_res *lockres) +{ + if (!ocfs2_stack_supports_plocks()) + return; + + if (lockres->l_lksb.lksb_fsdlm.sb_lvbptr) + lockres->l_lksb.lksb_fsdlm.sb_lvbptr = + (char *)&lockres->l_lksb + sizeof(struct dlm_lksb); +} + static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) { int i; @@ -3191,6 +3207,7 @@ static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) lockres->l_blocking); /* Dump the raw LVB */ + ocfs2_dlm_seq_rebase_lvb(lockres); lvb = ocfs2_dlm_lvb(&lockres->l_lksb); for(i = 0; i < DLM_LVB_LEN; i++) seq_printf(m, "0x%x\t", lvb[i]); From 3fe2d0d21c8ae2c37770e5aee9bf51cb13ba526e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 26 May 2026 18:43:40 +0200 Subject: [PATCH 0957/3297] lib: kunit_iov_iter: repeatedly call alloc_pages_bulk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9ac9a08e4ac4bc063e56e1aff266c5e8aa5c6c03 ] alloc_pages_bulk() is not guaranteed to return all requested pages in a single call. Call it repeatedly until all pages have been allocated or no more progress is being made. Link: https://lore.kernel.org/20260526-kunit_iov_iter-alloc_bulk-v2-1-24fbcd995c61@weissschuh.net Fixes: 2d71340ff1d4 ("iov_iter: Kunit tests for copying to/from an iterator") Signed-off-by: Thomas Weißschuh Cc: "Christian A. Ehrhardt" Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/tests/kunit_iov_iter.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c index 43e63bf4c0956..0ee92536e5e7b 100644 --- a/lib/tests/kunit_iov_iter.c +++ b/lib/tests/kunit_iov_iter.c @@ -50,14 +50,22 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, size_t npages) { struct page **pages; - unsigned long got; + unsigned long got, last; void *buffer; pages = kzalloc_objs(struct page *, npages, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); *ppages = pages; - got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + got = 0; + while (true) { + last = got; + got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + + if (last == got || got == npages) + break; + } + if (got != npages) { release_pages(pages, got); kvfree(pages); From 0e389fc290c350c67591abf4c367119f4689f310 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Fri, 29 May 2026 12:41:28 +0300 Subject: [PATCH 0958/3297] ocfs2: fix buffer head management in ocfs2_read_blocks() [ Upstream commit 6371a07148ee979af22a9d6f4c277462953a9a4a ] In ocfs2_read_blocks(), caller should't assume that buffer head returned by 'sb_getblk()' is exclusively owned and so 'put_bh()' always drops b_count from 1 to 0. If it is not so, buffer head remains on hold and likely to be returned by the next call to 'sb_getblk()' unchanged - that is, with BH_Uptodate bit set even if it has failed validation previously, thus allowing to insert that buffer head into OCFS2 metadata cache and submit it to upper layers. To avoid such a scenario, BH_Uptodate should be cleared immediately after 'validate()' callback has detected some data inconsistency. Link: https://lore.kernel.org/20260529094128.494293-1-dmantipov@yandex.ru Fixes: cf76c78595ca ("ocfs2: don't put and assigning null to bh allocated outside") Signed-off-by: Dmitry Antipov Reported-by: syzbot+caacd220635a9cc3bac9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=caacd220635a9cc3bac9 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/buffer_head_io.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c index 8f714406528d6..e52e202857d13 100644 --- a/fs/ocfs2/buffer_head_io.c +++ b/fs/ocfs2/buffer_head_io.c @@ -350,8 +350,6 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, wait_on_buffer(bh); put_bh(bh); bhs[i] = NULL; - } else if (bh && buffer_uptodate(bh)) { - clear_buffer_uptodate(bh); } continue; } @@ -380,8 +378,11 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, BUG_ON(buffer_jbd(bh)); clear_buffer_needs_validate(bh); status = validate(sb, bh); - if (status) + if (status) { + if (buffer_uptodate(bh)) + clear_buffer_uptodate(bh); goto read_failure; + } } } From 3fa7139b5f42731a61f78c42433adae13f9adc21 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 23:12:47 +0800 Subject: [PATCH 0959/3297] ocfs2: reject FITRIM ranges shorter than a cluster [ Upstream commit ca1afd88f5eaaff9168e1466e5401385edf59543 ] ocfs2_trim_mainbm() trims the global bitmap in cluster units, but its too-short range validation only checks sb->s_blocksize. On filesystems with a cluster size larger than the block size, a FITRIM range that is at least one block but shorter than one cluster is accepted and shifted down to len == 0. The later start + len - 1 and len -= ... arithmetic then underflows and can drive trimming past the requested range. Reject ranges shorter than s_clustersize instead. That preserves the existing -EINVAL behavior for requests that cannot discard even one allocation unit and keeps zero-cluster trims out of the group walk. Link: https://lore.kernel.org/20260528151247.361854-1-rollkingzzc@gmail.com Fixes: aa89762c5480 ("ocfs2: return EINVAL if the given range to discard is less than block size") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index a0ced11e0c24a..fb7d8dc2129be 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -7564,7 +7564,7 @@ int ocfs2_trim_mainbm(struct super_block *sb, struct fstrim_range *range) len = range->len >> osb->s_clustersize_bits; minlen = range->minlen >> osb->s_clustersize_bits; - if (minlen >= osb->bitmap_cpg || range->len < sb->s_blocksize) + if (minlen >= osb->bitmap_cpg || range->len < osb->s_clustersize) return -EINVAL; trace_ocfs2_trim_mainbm(start, len, minlen); From a087b2d3411e7f9df71ba3293596923ee2c70d65 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 31 May 2026 12:47:14 +0800 Subject: [PATCH 0960/3297] ocfs2/dlm: require a ref for locking_state debugfs open [ Upstream commit 03ad858ce8064861ea580021976dc19b7aabb549 ] debug_lockres_open() copies inode->i_private into struct debug_lockres and debug_lockres_release() later drops that pointer with dlm_put(). That only works if open successfully pins the struct dlm_ctxt. Today open calls dlm_grab(dlm) but ignores its return value. Once the last domain unregister has removed the context from dlm_domains, dlm_grab() returns NULL, yet open still stores the raw pointer and returns success. The later release path is outside the debugfs removal barrier, so it can call dlm_put() after dlm_free_ctxt_mem() has freed the context. KASAN reports this as a slab-use-after-free in dlm_put() called from debug_lockres_release(). Fail the open when dlm_grab() cannot acquire the reference and unwind the seq_file private state before returning. That keeps locking_state from handing out a file descriptor whose release path does not own the dlm_ctxt. The buggy scenario involves two paths, with each column showing the order within that path: locking_state debugfs open: last domain unregister: 1. debug_lockres_open() reads 1. dlm_unregister_domain() calls inode->i_private. dlm_complete_dlm_shutdown(). 2. debug_lockres_open() calls 2. shutdown removes the dlm_ctxt from dlm_grab(dlm) and gets NULL. dlm_domains. 3. open still stores the raw dlm 3. final teardown reaches pointer in dl->dl_ctxt and dlm_free_ctxt_mem() and frees it. returns success. 4. debug_lockres_release() later calls dlm_put(dl->dl_ctxt). Validation reproduced this kernel report: KASAN slab-use-after-free in dlm_put+0x82/0x200 RIP: 0033:0x7f4d349bc9e0 The buggy address belongs to the object at ffff888103a3c000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 816 bytes inside of freed 2048-byte region [ffff888103a3c000, ffff888103a3c800) Write of size 4 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) dlm_put+0x82/0x200 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) kasan_check_range+0x105/0x1b0 (?:?) debug_lockres_release+0x53/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) dlm_put+0x9/0x200 (?:?) debug_lockres_release+0x5c/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) full_proxy_release+0x67/0x90 (?:?) __fput+0x1df/0x4b0 (?:?) do_raw_spin_lock+0x10f/0x1b0 (?:?) fput_close_sync+0xd2/0x170 (?:?) __x64_sys_close+0x55/0x90 (?:?) do_syscall_64+0x10c/0x640 (arch/x86/entry/syscall_64.c:87) irqentry_exit+0xac/0x6e0 (?:?) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Freed by task stack: kasan_save_stack+0x33/0x60 (?:?) kasan_save_track+0x14/0x30 (?:?) kasan_save_free_info+0x3b/0x60 (?:?) __kasan_slab_free+0x5f/0x80 (?:?) kfree+0x30f/0x580 (?:?) dlm_put+0x1ce/0x200 (?:?) dlm_unregister_domain+0xf6/0xb30 (?:?) o2cb_cluster_disconnect+0x6b/0x90 (?:?) ocfs2_cluster_disconnect+0x41/0x70 (?:?) ocfs2_dlm_shutdown+0x1c4/0x220 (?:?) ocfs2_dismount_volume+0x38a/0x550 (?:?) generic_shutdown_super+0xc3/0x220 (?:?) kill_block_super+0x29/0x60 (?:?) deactivate_locked_super+0x66/0xe0 (?:?) cleanup_mnt+0x13d/0x210 (?:?) task_work_run+0xfa/0x170 (?:?) exit_to_user_mode_loop+0xd6/0x430 (?:?) do_syscall_64+0x3cb/0x640 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Link: https://lore.kernel.org/20260531044714.1640172-1-rollkingzzc@gmail.com Fixes: 4e3d24ed1a12 ("ocfs2/dlm: Dumps the lockres' into a debugfs file") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/dlm/dlmdebug.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index fe4fdd09bae39..5645673586201 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -560,6 +560,7 @@ static int debug_lockres_open(struct inode *inode, struct file *file) struct dlm_ctxt *dlm = inode->i_private; struct debug_lockres *dl; void *buf; + int status = -ENOMEM; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) @@ -572,16 +573,23 @@ static int debug_lockres_open(struct inode *inode, struct file *file) dl->dl_len = PAGE_SIZE; dl->dl_buf = buf; - dlm_grab(dlm); - dl->dl_ctxt = dlm; + /* ->release uses dl_ctxt after open, so it needs a real pin. */ + dl->dl_ctxt = dlm_grab(dlm); + if (!dl->dl_ctxt) { + status = -ENOENT; + goto bailseq; + } return 0; +bailseq: + seq_release_private(inode, file); bailfree: kfree(buf); bail: - mlog_errno(-ENOMEM); - return -ENOMEM; + if (status != -ENOENT) + mlog_errno(status); + return status; } static int debug_lockres_release(struct inode *inode, struct file *file) From c3ebf67cf8a9629b815aa8a8000979e3563ff963 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Mon, 1 Jun 2026 20:16:18 +0800 Subject: [PATCH 0961/3297] ocfs2: fix race between ocfs2_control_install_private() and ocfs2_control_release() [ Upstream commit 57dcfd9049d497c31151787a0696d59f0a98f8e6 ] Move atomic_inc(&ocfs2_control_opened) and the handshake state update inside ocfs2_control_lock to close a race window where ocfs2_control_release() can observe ocfs2_control_opened dropping to zero (resetting ocfs2_control_this_node and running_proto) while ocfs2_control_install_private() is about to bump the counter and mark the connection valid. Link: https://lore.kernel.org/20260601121618.1263346-1-joseph.qi@linux.alibaba.com Fixes: 3cfd4ab6b6b4 ("ocfs2: Add the local node id to the handshake.") Signed-off-by: Joseph Qi Reported-by: Ginger Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/stack_user.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index be0a5758bd40f..f5d83b4f56dd7 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -327,18 +327,14 @@ static int ocfs2_control_install_private(struct file *file) ocfs2_control_this_node = p->op_this_node; running_proto.pv_major = p->op_proto.pv_major; running_proto.pv_minor = p->op_proto.pv_minor; - } - -out_unlock: - mutex_unlock(&ocfs2_control_lock); - - if (!rc && set_p) { - /* We set the global values successfully */ atomic_inc(&ocfs2_control_opened); ocfs2_control_set_handshake_state(file, OCFS2_CONTROL_HANDSHAKE_VALID); } +out_unlock: + mutex_unlock(&ocfs2_control_lock); + return rc; } From 7b819a84f1d5fd18fc07734aaba525161f2010b9 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 25 May 2026 17:35:54 +0200 Subject: [PATCH 0962/3297] netfilter: nfnetlink_osf: fix mss parsing on big-endian architectures [ Upstream commit a625c94144c9b66d32e1f374f909f38db46161c1 ] The MSS calculation in nf_osf_match_one() manually shifts bytes to construct a 16-bit value before passing it to ntohs(). This works on little-endian hosts but it does not work on big-endian as the bytes are being always shifted and set in the same way for all architectures. Use get_unaligned_be16() to fix this on big-endian systems. It also simplifies the code. Fixes: 11eeef41d5f6 ("netfilter: passive OS fingerprint xtables match") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nfnetlink_osf.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index c89efb951994a..b9a0257fa2bef 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -95,11 +95,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb, switch (*optp) { case OSFOPT_MSS: - mss = optp[3]; - mss <<= 8; - mss |= optp[2]; - - mss = ntohs((__force __be16)mss); + mss = get_unaligned_be16(&optp[2]); break; case OSFOPT_TS: break; From dce1e3cf735d1fa5b71c9889d442f7c5388590cf Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 26 May 2026 18:40:44 +0200 Subject: [PATCH 0963/3297] netfilter: nfnetlink_cthelper: use {READ,WRITE}_ONCE for accessing helper flags [ Upstream commit f8bf5edf7157984bb8e288c8b04fdb041223b80c ] Conntrack helper flags are accessed from packet and netlink dump path. Concurrent update of userspace helper flags is not possible, because the nfnl_mutex in held on updates. These flags are only used by userspace helpers. Use {READ,WRITE}_ONCE() to access this flags from lockless paths. Fixes: 12f7a505331e ("netfilter: add user-space connection tracking helper infrastructure") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_core.c | 4 +++- net/netfilter/nfnetlink_cthelper.c | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 344f88295976d..18e52dcd22990 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -2211,6 +2211,7 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, { const struct nf_conntrack_helper *helper; const struct nf_conn_help *help; + unsigned int helper_flags; int protoff; help = nfct_help(ct); @@ -2221,7 +2222,8 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, if (!helper) return NF_ACCEPT; - if (!(helper->flags & NF_CT_HELPER_F_USERSPACE)) + helper_flags = READ_ONCE(helper->flags); + if (!(helper_flags & NF_CT_HELPER_F_USERSPACE)) return NF_ACCEPT; switch (nf_ct_l3num(ct)) { diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 71a248cca746a..18a57af99d27e 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -41,8 +41,9 @@ static int nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - const struct nf_conn_help *help; struct nf_conntrack_helper *helper; + const struct nf_conn_help *help; + unsigned int helper_flags; help = nfct_help(ct); if (help == NULL) @@ -53,8 +54,10 @@ nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, if (helper == NULL) return NF_DROP; + helper_flags = READ_ONCE(helper->flags); + /* This is a user-space helper not yet configured, skip. */ - if ((helper->flags & + if ((helper_flags & (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) == NF_CT_HELPER_F_USERSPACE) return NF_ACCEPT; @@ -406,10 +409,10 @@ nfnl_cthelper_update(const struct nlattr * const tb[], switch(status) { case NFCT_HELPER_STATUS_ENABLED: - helper->flags |= NF_CT_HELPER_F_CONFIGURED; + WRITE_ONCE(helper->flags, helper->flags | NF_CT_HELPER_F_CONFIGURED); break; case NFCT_HELPER_STATUS_DISABLED: - helper->flags &= ~NF_CT_HELPER_F_CONFIGURED; + WRITE_ONCE(helper->flags, helper->flags & ~NF_CT_HELPER_F_CONFIGURED); break; } } @@ -531,8 +534,8 @@ static int nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, struct nf_conntrack_helper *helper) { - struct nlmsghdr *nlh; unsigned int flags = portid ? NLM_F_MULTI : 0; + struct nlmsghdr *nlh; int status; event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event); @@ -556,7 +559,7 @@ nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len))) goto nla_put_failure; - if (helper->flags & NF_CT_HELPER_F_CONFIGURED) + if (READ_ONCE(helper->flags) & NF_CT_HELPER_F_CONFIGURED) status = NFCT_HELPER_STATUS_ENABLED; else status = NFCT_HELPER_STATUS_DISABLED; @@ -577,6 +580,7 @@ static int nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { struct nf_conntrack_helper *cur, *last; + unsigned int helper_flags; rcu_read_lock(); last = (struct nf_conntrack_helper *)cb->args[1]; @@ -585,8 +589,10 @@ nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[cb->args[0]], hnode) { + helper_flags = READ_ONCE(cur->flags); + /* skip non-userspace conntrack helpers. */ - if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) + if (!(helper_flags & NF_CT_HELPER_F_USERSPACE)) continue; if (cb->args[1]) { From 4dbb71c046f72a953bb4acdfe382f301bc18d7f7 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:27 +0200 Subject: [PATCH 0964/3297] netfilter: synproxy: drop packets if timestamp adjustment fails [ Upstream commit 63d29ee95c4ab5976d660182d2e4733bb4a091d8 ] If a packet was malformed or if skb_ensure_writable() failed, the synproxy_tstamp_adjust() function returned 0 indicating an error but it was ignored on the callers. Make the function return a boolean instead to clarify the result and drop the packet if synproxy_tstamp_adjust() failed due to ENOMEM from skb_ensure_writable(). In addition, if there are malformed options, skip the tstamp update but do not drop the packet as that should be done by the policy directly. Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_synproxy_core.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index a277b2bd3275d..a90511238ec37 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -183,7 +183,7 @@ synproxy_check_timestamp_cookie(struct synproxy_options *opts) opts->options |= opts->tsecr & (1 << 5) ? NF_SYNPROXY_OPT_ECN : 0; } -static unsigned int +static bool synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, struct tcphdr *th, struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -193,13 +193,13 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, __be32 *ptr, old; if (synproxy->tsoff == 0) - return 1; + return true; optoff = protoff + sizeof(struct tcphdr); optend = protoff + th->doff * 4; if (skb_ensure_writable(skb, optend)) - return 0; + return false; th = (struct tcphdr *)(skb->data + protoff); @@ -208,7 +208,7 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, switch (op[0]) { case TCPOPT_EOL: - return 1; + return true; case TCPOPT_NOP: optoff++; continue; @@ -216,7 +216,7 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, if (optoff + 1 == optend || optoff + op[1] > optend || op[1] < 2) - return 0; + return true; if (op[0] == TCPOPT_TIMESTAMP && op[1] == TCPOLEN_TIMESTAMP) { if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { @@ -232,12 +232,12 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, } inet_proto_csum_replace4(&th->check, skb, old, *ptr, false); - return 1; + return true; } optoff += op[1]; } } - return 1; + return true; } #ifdef CONFIG_PROC_FS @@ -748,7 +748,9 @@ ipv4_synproxy_hook(void *priv, struct sk_buff *skb, break; } - synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); + if (!synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy)) + return NF_DROP_REASON(skb, SKB_DROP_REASON_NETFILTER_DROP, ENOMEM); + return NF_ACCEPT; } EXPORT_SYMBOL_GPL(ipv4_synproxy_hook); @@ -1176,7 +1178,9 @@ ipv6_synproxy_hook(void *priv, struct sk_buff *skb, break; } - synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); + if (!synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy)) + return NF_DROP_REASON(skb, SKB_DROP_REASON_NETFILTER_DROP, ENOMEM); + return NF_ACCEPT; } EXPORT_SYMBOL_GPL(ipv6_synproxy_hook); From b171119082bab57ff3a715fd8593be8c44af0fa6 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:28 +0200 Subject: [PATCH 0965/3297] netfilter: synproxy: adjust duplicate timestamp options [ Upstream commit 22bb132cfb9b94847d52d73614284b8c5ea8d36e ] RFC 9293 does not mention anything about duplicated options and each networking stack handles it in their own way. Currently, Linux kernel is processing options sequentially and in case of duplicated timestamp options, the value from the latest one overrides the others. As SYNPROXY is modifying only the first timestamp option found, a packet can reach the backend server and it might parse the wrong timestamp value. Let's just continue parsing the following options and in case a duplicated timestamp is found, adjust it too. Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_synproxy_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index a90511238ec37..3c021fdec2b57 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -232,7 +232,6 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, } inet_proto_csum_replace4(&th->check, skb, old, *ptr, false); - return true; } optoff += op[1]; } From 5c9c67cf7a3d16051dfb90e98836f530964dfb8e Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:29 +0200 Subject: [PATCH 0966/3297] netfilter: synproxy: fix unaligned memory access in timestamp adjustment [ Upstream commit 992c20bc8a4aba220c8b95b467d049289778dad6 ] Use get_unaligned_be32() and put_unaligned_be32() to safely read and write the timestamp fields. This prevents performance degradation due to unaligned memory access or even a crash on strict alignment architectures. This follows the implementation of timestamp parsing in the networking stack at tcp_parse_options() and synproxy_parse_options(). Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_synproxy_core.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 3c021fdec2b57..69f84c44801dc 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -190,7 +190,7 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, const struct nf_conn_synproxy *synproxy) { unsigned int optoff, optend; - __be32 *ptr, old; + u32 new, old; if (synproxy->tsoff == 0) return true; @@ -220,18 +220,17 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, if (op[0] == TCPOPT_TIMESTAMP && op[1] == TCPOLEN_TIMESTAMP) { if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { - ptr = (__be32 *)&op[2]; - old = *ptr; - *ptr = htonl(ntohl(*ptr) - - synproxy->tsoff); + old = get_unaligned_be32(&op[2]); + new = old - synproxy->tsoff; + put_unaligned_be32(new, &op[2]); } else { - ptr = (__be32 *)&op[6]; - old = *ptr; - *ptr = htonl(ntohl(*ptr) + - synproxy->tsoff); + old = get_unaligned_be32(&op[6]); + new = old + synproxy->tsoff; + put_unaligned_be32(new, &op[6]); } inet_proto_csum_replace4(&th->check, skb, - old, *ptr, false); + cpu_to_be32(old), + cpu_to_be32(new), false); } optoff += op[1]; } From 8ebc31b86dccdef1c222ecf1ff6efa27dade05fe Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:30 +0200 Subject: [PATCH 0967/3297] netfilter: synproxy: protect nf_ct_seqadj_init() with conntrack lock [ Upstream commit 9e37388b8070afe73d4ab2d973b28593ed65f3ad ] nf_ct_seqadj_init() is called without holding the ct lock. This can race with nf_ct_seq_adjust() when a connection is in CLOSE state due to an RST or connection reopening. In addition for SYN_RECV state, concurrent processing of packets can trigger nf_ct_seq_adjust() too. These situations create a read/write data race. As synproxy is the only user of nf_ct_seqadj_init() at the moment, fix this by holding ct->lock inside nf_ct_seqadj_init() until all is done. Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_seqadj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index 7ab2b25b57bcc..b7e99f34dfce8 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -17,12 +17,14 @@ int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, if (off == 0) return 0; + spin_lock_bh(&ct->lock); set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); seqadj = nfct_seqadj(ct); this_way = &seqadj->seq[dir]; this_way->offset_before = off; this_way->offset_after = off; + spin_unlock_bh(&ct->lock); return 0; } EXPORT_SYMBOL_GPL(nf_ct_seqadj_init); From dd0d22fdae4cb6b2016c70c0ef3cbfeede678e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 5 Jun 2026 01:14:40 -0300 Subject: [PATCH 0968/3297] ALSA: usb-audio: qcom: Initialize offload control return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2b5632d72fca0841bea283da2e3a478d24118508 ] snd_usb_offload_create_ctl() returns ret after walking the USB PCM list, but ret is only assigned after a playback stream passes the endpoint and PCM-index filters. If all playback streams are skipped, for example because there is no playback endpoint or because all PCM indexes exceed the 0xff control range, the function returns an uninitialized stack value. Initialize ret to 0 so the no-control-created path returns deterministic success, while preserving the existing negative error return when snd_ctl_add() fails. Fixes: a67656f011d1 ("ALSA: usb-audio: qcom: Add USB offload route kcontrol") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260605-alsa-usb-qcom-offload-ret-init-v1-1-dc72fcc4bd3b@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/mixer_usb_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/qcom/mixer_usb_offload.c b/sound/usb/qcom/mixer_usb_offload.c index 2adeb64f4d33f..005138714f724 100644 --- a/sound/usb/qcom/mixer_usb_offload.c +++ b/sound/usb/qcom/mixer_usb_offload.c @@ -113,7 +113,7 @@ int snd_usb_offload_create_ctl(struct snd_usb_audio *chip, struct device *bedev) struct snd_usb_substream *subs; struct snd_usb_stream *as; char ctl_name[48]; - int ret; + int ret = 0; list_for_each_entry(as, &chip->pcm_list, list) { subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK]; From e6665d36b37b4edc5c50c1a94894a6082249ca11 Mon Sep 17 00:00:00 2001 From: Junxiao Chang Date: Sat, 6 Jun 2026 10:15:14 +0800 Subject: [PATCH 0969/3297] x86/cpu: Remove obsolete aperfmperf_get_khz() declaration [ Upstream commit a5f28da54f36f1a8b289d9bdd3e780b2ede0da6f ] aperfmperf_get_khz() was replaced by arch_freq_get_on_cpu(). The remaining declaration in the header file is no longer used and should be removed. Fixes: f3eca381bd49 ("x86/aperfmperf: Replace arch_freq_get_on_cpu()") Signed-off-by: Junxiao Chang Signed-off-by: Ingo Molnar Reviewed-by: Nikolay Borisov Link: https://patch.msgid.link/20260606021514.1433619-1-junxiao.chang@intel.com Signed-off-by: Sasha Levin --- arch/x86/kernel/cpu/cpu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index bc38b2d56f26a..92032422a8298 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h @@ -84,7 +84,6 @@ static inline struct amd_northbridge *amd_init_l3_cache(int index) } #endif -unsigned int aperfmperf_get_khz(int cpu); void cpu_select_mitigations(void); extern void x86_spec_ctrl_setup_ap(void); From d53eecbca16f056abba274075cb15120ab062518 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 4 Jun 2026 08:21:12 +0200 Subject: [PATCH 0970/3297] netfilter: conntrack: revert ct extension genid infrastructure [ Upstream commit 35e21a4dccc5c255ba59ccfbfeb4629ed21da972 ] This infrastructure is not used anymore after moving ct timeout and helper to use datapath refcount to track object use. Revert commit c56716c69ce1 ("netfilter: extensions: introduce extension genid count") this patch disables all ct extensions (leading to NULL) for unconfirmed conntracks, when this is only targeted at ct helper and ct timeout. There is also codebase that dereferences the ct extension without checking for NULL which could lead to crash. Fixes: c56716c69ce1 ("netfilter: extensions: introduce extension genid count") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_extend.h | 12 ---- net/netfilter/nf_conntrack_core.c | 61 +-------------------- net/netfilter/nf_conntrack_extend.c | 32 +---------- 3 files changed, 2 insertions(+), 103 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 0b247248b0323..fd5c4dbf72caf 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -38,7 +38,6 @@ enum nf_ct_ext_id { struct nf_ct_ext { u8 offset[NF_CT_EXT_NUM]; u8 len; - unsigned int gen_id; char data[] __aligned(8); }; @@ -52,8 +51,6 @@ static inline bool nf_ct_ext_exist(const struct nf_conn *ct, u8 id) return (ct->ext && __nf_ct_ext_exist(ct->ext, id)); } -void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id); - static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id) { struct nf_ct_ext *ext = ct->ext; @@ -61,19 +58,10 @@ static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id) if (!ext || !__nf_ct_ext_exist(ext, id)) return NULL; - if (unlikely(ext->gen_id)) - return __nf_ct_ext_find(ext, id); - return (void *)ct->ext + ct->ext->offset[id]; } /* Add this type, returns pointer to data or NULL. */ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp); -/* ext genid. if ext->id != ext_genid, extensions cannot be used - * anymore unless conntrack has CONFIRMED bit set. - */ -extern atomic_t nf_conntrack_ext_genid; -void nf_ct_ext_bump_genid(void); - #endif /* _NF_CONNTRACK_EXTEND_H */ diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 18e52dcd22990..3bb08880f3a4d 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -836,33 +836,6 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct, &nf_conntrack_hash[reply_hash]); } -static bool nf_ct_ext_valid_pre(const struct nf_ct_ext *ext) -{ - /* if ext->gen_id is not equal to nf_conntrack_ext_genid, some extensions - * may contain stale pointers to e.g. helper that has been removed. - * - * The helper can't clear this because the nf_conn object isn't in - * any hash and synchronize_rcu() isn't enough because associated skb - * might sit in a queue. - */ - return !ext || ext->gen_id == atomic_read(&nf_conntrack_ext_genid); -} - -static bool nf_ct_ext_valid_post(struct nf_ct_ext *ext) -{ - if (!ext) - return true; - - if (ext->gen_id != atomic_read(&nf_conntrack_ext_genid)) - return false; - - /* inserted into conntrack table, nf_ct_iterate_cleanup() - * will find it. Disable nf_ct_ext_find() id check. - */ - WRITE_ONCE(ext->gen_id, 0); - return true; -} - int nf_conntrack_hash_check_insert(struct nf_conn *ct) { @@ -878,9 +851,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) zone = nf_ct_zone(ct); - if (!nf_ct_ext_valid_pre(ct->ext)) - return -EAGAIN; - local_bh_disable(); do { sequence = read_seqcount_begin(&nf_conntrack_generation); @@ -914,18 +884,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) goto chaintoolong; } - /* If genid has changed, we can't insert anymore because ct - * extensions could have stale pointers and nf_ct_iterate_destroy - * might have completed its table scan already. - * - * Increment of the ext genid right after this check is fine: - * nf_ct_iterate_destroy blocks until locks are released. - */ - if (!nf_ct_ext_valid_post(ct->ext)) { - err = -EAGAIN; - goto out; - } - smp_wmb(); /* The caller holds a reference to this object */ refcount_set(&ct->ct_general.use, 2); @@ -1253,11 +1211,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) return NF_DROP; } - if (!nf_ct_ext_valid_pre(ct->ext)) { - NF_CT_STAT_INC(net, insert_failed); - goto dying; - } - /* We have to check the DYING flag after unlink to prevent * a race against nf_ct_get_next_corpse() possibly called from * user context, else we insert an already 'dead' hash, blocking @@ -1320,16 +1273,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) nf_conntrack_double_unlock(hash, reply_hash); local_bh_enable(); - /* ext area is still valid (rcu read lock is held, - * but will go out of scope soon, we need to remove - * this conntrack again. - */ - if (!nf_ct_ext_valid_post(ct->ext)) { - nf_ct_kill(ct); - NF_CT_STAT_INC_ATOMIC(net, drop); - return NF_DROP; - } - help = nfct_help(ct); if (help && help->helper) nf_conntrack_event_cache(IPCT_HELPER, ct); @@ -2436,13 +2379,11 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data) */ synchronize_net(); - nf_ct_ext_bump_genid(); iter_data.data = data; nf_ct_iterate_cleanup(iter, &iter_data); /* Another cpu might be in a rcu read section with - * rcu protected pointer cleared in iter callback - * or hidden via nf_ct_ext_bump_genid() above. + * rcu protected pointer cleared in iter callback. * * Wait until those are done. */ diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index dd62cc12e7750..0da105e1ded93 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -27,8 +27,6 @@ #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ -atomic_t nf_conntrack_ext_genid __read_mostly = ATOMIC_INIT(1); - static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = { [NF_CT_EXT_HELPER] = sizeof(struct nf_conn_help), #if IS_ENABLED(CONFIG_NF_NAT) @@ -118,10 +116,8 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) if (!new) return NULL; - if (!ct->ext) { + if (!ct->ext) memset(new->offset, 0, sizeof(new->offset)); - new->gen_id = atomic_read(&nf_conntrack_ext_genid); - } new->offset[id] = newoff; new->len = newlen; @@ -131,29 +127,3 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) return (void *)new + newoff; } EXPORT_SYMBOL(nf_ct_ext_add); - -/* Use nf_ct_ext_find wrapper. This is only useful for unconfirmed entries. */ -void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id) -{ - unsigned int gen_id = atomic_read(&nf_conntrack_ext_genid); - unsigned int this_id = READ_ONCE(ext->gen_id); - - if (!__nf_ct_ext_exist(ext, id)) - return NULL; - - if (this_id == 0 || ext->gen_id == gen_id) - return (void *)ext + ext->offset[id]; - - return NULL; -} -EXPORT_SYMBOL(__nf_ct_ext_find); - -void nf_ct_ext_bump_genid(void) -{ - unsigned int value = atomic_inc_return(&nf_conntrack_ext_genid); - - if (value == UINT_MAX) - atomic_set(&nf_conntrack_ext_genid, 1); - - msleep(HZ); -} From 77b4bfc1ce32a63340c382428425574cd4dd5e4f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 4 Jun 2026 08:21:13 +0200 Subject: [PATCH 0971/3297] netfilter: conntrack: call nf_ct_gre_keymap_destroy() if master helper is pptp [ Upstream commit b0f02608fbcd607b5131cceb91fc0a035264e61c ] For GRE flows, validate that the ct master helper (if any) is pptp before calling nf_ct_gre_keymap_destroy(), so the helper data area can be accessed safely. Note that only the pptp helper provides a .destroy callback. Fixes: e56894356f60 ("netfilter: conntrack: remove l4proto destroy hook") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conntrack_core.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 3bb08880f3a4d..a877f9ad1cf2d 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -565,9 +565,23 @@ static void destroy_gre_conntrack(struct nf_conn *ct) { #ifdef CONFIG_NF_CT_PROTO_GRE struct nf_conn *master = ct->master; + struct nf_conn_help *help; + + if (!master) + return; + + help = nfct_help(master); + if (help) { + struct nf_conntrack_helper *helper; - if (master) - nf_ct_gre_keymap_destroy(master); + rcu_read_lock(); + helper = rcu_dereference(help->helper); + /* Only pptp helper has a destroy callback. */ + if (helper && helper->destroy) + nf_ct_gre_keymap_destroy(master); + + rcu_read_unlock(); + } #endif } From fe5414d6b39959dfa1207be7b05b7e0beecf820a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 2 Jun 2026 16:04:34 +0200 Subject: [PATCH 0972/3297] RDMA/hfi1: Open-code rvt_set_ibdev_name() [ Upstream commit 0ee8ac903e5aa100a70bef8d0afc19a336ffa775 ] clang warns about a function missing a printf attribute: include/rdma/rdma_vt.h:457:47: error: diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'rvt_set_ibdev_name' [-Werror,-Wmissing-format-attribute] 447 | static inline void rvt_set_ibdev_name(struct rvt_dev_info *rdi, | __attribute__((format(printf, 2, 3))) 448 | const char *fmt, const char *name, 449 | const int unit) The helper was originally added as an abstraction for the hfi1 and qib drivers needing the same thing, but now qib is gone, and hfi1 is the only remaining user of rdma_vt. Avoid the warning and allow the compiler to check the format string by open-coding the helper and directly assigning the device name. Fixes: 5084c8ff21f2 ("IB/{rdmavt, hfi1, qib}: Self determine driver name") Link: https://patch.msgid.link/r/20260602140453.3542427-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Kees Cook Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hfi1/init.c | 13 ++++++++++++- include/rdma/rdma_vt.h | 20 -------------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index b35f92e7d865f..7e0298c62882e 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1208,6 +1208,7 @@ static struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, size_t extra) { struct hfi1_devdata *dd; + struct ib_device *ibdev; int ret, nports; /* extra is * number of ports */ @@ -1229,7 +1230,17 @@ static struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, "Could not allocate unit ID: error %d\n", -ret); goto bail; } - rvt_set_ibdev_name(&dd->verbs_dev.rdi, "%s_%d", class_name(), dd->unit); + + /* + * FIXME: rvt and its users want to touch the ibdev before + * registration and have things like the name work. We don't have the + * infrastructure in the core to support this directly today, hack it + * to work by setting the name manually here. + */ + ibdev = &dd->verbs_dev.rdi.ibdev; + dev_set_name(&ibdev->dev, "%s_%d", class_name(), dd->unit); + strscpy(ibdev->name, dev_name(&ibdev->dev), IB_DEVICE_NAME_MAX); + /* * If the BIOS does not have the NUMA node information set, select * NUMA 0 so we get consistent performance. diff --git a/include/rdma/rdma_vt.h b/include/rdma/rdma_vt.h index c429d6ddb1292..d69df6ebc80a7 100644 --- a/include/rdma/rdma_vt.h +++ b/include/rdma/rdma_vt.h @@ -428,26 +428,6 @@ struct rvt_dev_info { struct rvt_wss *wss; }; -/** - * rvt_set_ibdev_name - Craft an IB device name from client info - * @rdi: pointer to the client rvt_dev_info structure - * @name: client specific name - * @unit: client specific unit number. - */ -static inline void rvt_set_ibdev_name(struct rvt_dev_info *rdi, - const char *fmt, const char *name, - const int unit) -{ - /* - * FIXME: rvt and its users want to touch the ibdev before - * registration and have things like the name work. We don't have the - * infrastructure in the core to support this directly today, hack it - * to work by setting the name manually here. - */ - dev_set_name(&rdi->ibdev.dev, fmt, name, unit); - strscpy(rdi->ibdev.name, dev_name(&rdi->ibdev.dev), IB_DEVICE_NAME_MAX); -} - /** * rvt_get_ibdev_name - return the IB name * @rdi: rdmavt device From 47831b503ecb7643ec2af63b9c286f0b5e14ee6f Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 2 Jun 2026 16:37:28 -0300 Subject: [PATCH 0973/3297] IB/cm: Fix av cm device leak on an error path in cm_init_av_by_path() [ Upstream commit 9b2207bc5cdb955bdae34b3eec80f04979e17081 ] Codex pointed out that cm_init_av_by_path() can call cm_set_av_port() which takes a reference on the cm device, but then can immediately return error if ib_init_ah_attr_from_path() fails. Since callers like ib_send_cm_req() put the av on the stack this leaks that cm device reference. Re-order cm_init_av_by_path() so it doesn't touch the av until it has done all its failable work, and then update the av in one shot so it is either left alone or fully init'd. Sashiko also pointed out that the cm_destroy_av() prior to cm_init_av_by_path() is harmful as it leaves the AV broken in the error case and thus the REJ won't send. Since cm_init_av_by_path() is now atomic it is safe to delete the cm_destroy_av(). On succees the av from cm_init_av_for_response() is cleaned up by cm_init_av_by_path(), on failure the 'goto rejected' guarentees the av is destroyed during ib_destroy_cm_id(). Fixes: 76039ac9095f ("IB/cm: Protect cm_dev, cm_ports and mad_agent with kref and lock") Link: https://patch.msgid.link/r/0-v1-38292501f539+14f-ib_cm_av_leak_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/core/cm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 01bede8ba1055..11a3423082862 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -531,6 +531,7 @@ static int cm_init_av_by_path(struct sa_path_rec *path, struct rdma_ah_attr new_ah_attr; struct cm_device *cm_dev; struct cm_port *port; + u16 pkey_index; int ret; port = get_cm_port_from_path(path, sgid_attr); @@ -539,12 +540,10 @@ static int cm_init_av_by_path(struct sa_path_rec *path, cm_dev = port->cm_dev; ret = ib_find_cached_pkey(cm_dev->ib_device, port->port_num, - be16_to_cpu(path->pkey), &av->pkey_index); + be16_to_cpu(path->pkey), &pkey_index); if (ret) return ret; - cm_set_av_port(av, port); - /* * av->ah_attr might be initialized based on wc or during * request processing time which might have reference to sgid_attr. @@ -559,6 +558,8 @@ static int cm_init_av_by_path(struct sa_path_rec *path, if (ret) return ret; + av->pkey_index = pkey_index; + cm_set_av_port(av, port); av->timeout = path->packet_life_time + 1; rdma_move_ah_attr(&av->ah_attr, &new_ah_attr); return 0; @@ -2185,8 +2186,10 @@ static int cm_req_handler(struct cm_work *work) cm_id_priv->av.ah_attr.roce.dmac); work->path[0].hop_limit = grh->hop_limit; - /* This destroy call is needed to pair with cm_init_av_for_response */ - cm_destroy_av(&cm_id_priv->av); + /* + * cm_init_av_by_path() will internally pair with the above + * cm_init_av_for_response() if it succeeds. + */ ret = cm_init_av_by_path(&work->path[0], gid_attr, &cm_id_priv->av); if (ret) { int err; From 844a1ae78e220e922882e1e25699234170b2dd59 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 29 May 2026 17:23:59 +0200 Subject: [PATCH 0974/3297] ALSA: hda: fix Kconfig dependency of HD Audio PCI [ Upstream commit 1516134cb65526aba5319bb446c296fc8a192f84 ] With commit 2d9223d2d64c ("ALSA: hda: Move controller drivers into sound/hda/controllers directory") the HD Audio drivers have been moved from linux/sound/pci/hda to linux/sound/hda. But the Kconfig dependency for SND_HDA_INTEL stayed on SND_PCI instead of depending on PCI directly. To make the "HD Audio PCI" configuration entry visible it is currently needed to enable "PCI sound devices" although no PCI device in the submenu needs to be selected. Make SND_HDA_INTEL directly depending on hardware/architecture like the other entries in this Kconfig. Fixes: 2d9223d2d64c ("ALSA: hda: Move controller drivers into sound/hda/controllers directory") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260529-hda-kconfig-v1-1-4a2c6a0efd56@hartkopp.net Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/hda/controllers/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig index 34721f50b055a..39b934365aee3 100644 --- a/sound/hda/controllers/Kconfig +++ b/sound/hda/controllers/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config SND_HDA_INTEL tristate "HD Audio PCI" - depends on SND_PCI + depends on PCI select SND_HDA select SND_INTEL_DSP_CONFIG help From 54cab78df0375196aaec4e3109191653d21751df Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 2 Jun 2026 21:44:23 +0000 Subject: [PATCH 0975/3297] RDMA/irdma: Fix OOB read during CQ MR registration [ Upstream commit 4385ddd654d90245eeb83b3cb539670ab5c85ba4 ] Sashiko pointed out an unrelated bug during a previous patch: https://sashiko.dev/#/patchset/20260512183852.614045-1-jmoroni%40google.com This change fixes the bug by eliminating the cqmr->split field which was not being set properly and instead just checks the CQ resize feature flag directly. The cqmr->split field essentially tracks whether IRDMA_FEATURE_CQ_RESIZE is set, but it was not being set until CQ creation time, which is _after_ CQ memory registration (the only other place where it is referenced). As a result, it would always be false during MR registration and would therefore cause irdma_handle_q_mem to populate cqmr->shadow even for GEN_2 HW and beyond: cqmr->shadow = (dma_addr_t)arr[req->cq_pages]; The issue is that for GEN_2 and beyond, req->cq_pages may be exactly equal to iwmr->page_cnt and therefore equal to the size of arr, which would cause an OOB read by one. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://patch.msgid.link/r/20260602214423.1315105-2-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 4 ++-- drivers/infiniband/hw/irdma/verbs.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 6c9925f175711..80e9cdef5baff 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -2563,7 +2563,6 @@ static int irdma_create_cq(struct ib_cq *ibcq, } cqmr_shadow = &iwpbl_shadow->cq_mr; info.shadow_area_pa = cqmr_shadow->cq_pbl.addr; - cqmr->split = true; } else { info.shadow_area_pa = cqmr->shadow; } @@ -2965,7 +2964,8 @@ static int irdma_handle_q_mem(struct irdma_device *iwdev, case IRDMA_MEMREG_TYPE_CQ: hmc_p = &cqmr->cq_pbl; - if (!cqmr->split) + if (!(iwdev->rf->sc_dev.hw_attrs.uk_attrs.feature_flags & + IRDMA_FEATURE_CQ_RESIZE)) cqmr->shadow = (dma_addr_t)arr[req->cq_pages]; if (lvl) diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h index aabbb3442098b..289ebc9b23ca7 100644 --- a/drivers/infiniband/hw/irdma/verbs.h +++ b/drivers/infiniband/hw/irdma/verbs.h @@ -65,7 +65,6 @@ struct irdma_hmc_pble { struct irdma_cq_mr { struct irdma_hmc_pble cq_pbl; dma_addr_t shadow; - bool split; }; struct irdma_srq_mr { From f9173e0fc026c882fc89878c803861549f96c9ae Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 4 Jun 2026 15:41:04 +0000 Subject: [PATCH 0976/3297] RDMA/irdma: Initialize iwmr->access during MR registration [ Upstream commit 1521d560b7a1a39e437d37fffd9b55435d329ad1 ] Initialize iwmr->access during initial user mem registration so that it contains a valid value during a subsequent rereg_mr. Otherwise, a rereg_mr that doesn't set IB_MR_REREG_ACCESS (for example, one that only changes the PD) ends up clearing the access flags in HW since iwmr->access is zero-initialized, which is not intended. Fixes: 5ac388db27c4 ("RDMA/irdma: Add support to re-register a memory region") Link: https://patch.msgid.link/r/20260604154104.4035581-1-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 80e9cdef5baff..2b85e32134a5b 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3312,6 +3312,7 @@ static int irdma_reg_user_mr_type_mem(struct irdma_mr *iwmr, int access, int err; lvl = iwmr->page_cnt != 1 ? PBLE_LEVEL_1 | PBLE_LEVEL_2 : PBLE_LEVEL_0; + iwmr->access = access; err = irdma_setup_pbles(iwdev->rf, iwmr, lvl); if (err) From ab4b5a07e1c1a7ebfbb32f5e1a6a3c98e9bb0df6 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Wed, 13 May 2026 15:25:31 +0200 Subject: [PATCH 0977/3297] arm64: dts: imx8mp-kontron: Reduce EERAM SPI clock frequency [ Upstream commit 95f5bc1632ab8f243e517357b5da0dd28d1c6c92 ] There is an onboard level shifter for the SPI signals that causes additional propagation delay and renders the SPI transmission unreliable at 20 MHz. Reduce the clock frequency to a safe value. Fixes: 946ab10e3f40 ("arm64: dts: Add support for Kontron OSM-S i.MX8MP SoM and BL carrier board") Signed-off-by: Frieder Schrempf Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts index 0924ac50fd2db..75ae466427821 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts @@ -63,7 +63,7 @@ eeram@0 { compatible = "microchip,48l640"; reg = <0>; - spi-max-frequency = <20000000>; + spi-max-frequency = <16000000>; }; }; From b5087fc4ef1f8faf2c09d4078718a7ae68ae0949 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 20 May 2026 15:22:28 +0800 Subject: [PATCH 0978/3297] arm64: dts: imx95: Correct PCIe outbound address space configuration [ Upstream commit dbecf38e60d4ef08c59836b7bc16e8efca01fc47 ] Fix the PCIe outbound memory ranges for both pcie0 and pcie1 controllers on i.MX95. The memory window size was incorrectly set to 256MB during initial bring-up, but the hardware supports up to 4GB of outbound address space per controller. Additionally, the ECAM region cannot be mapped as I/O space. Use a memory-mapped region for I/O space instead, and relocate the 1MB I/O region to immediately follow the memory region at offset 0xf0000000 within each window. Update the outbound address space layout per controller as follows: - 3.5GB 64-bit prefetchable memory - 256MB 32-bit non-prefetchable memory - 1MB I/O Fixes: 3b1d5deb29ff ("arm64: dts: imx95: add pcie[0,1] and pcie-ep[0,1] support") Signed-off-by: Richard Zhu Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx95.dtsi | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi index 583c5f7a84b54..47fd5a5862b4f 100644 --- a/arch/arm64/boot/dts/freescale/imx95.dtsi +++ b/arch/arm64/boot/dts/freescale/imx95.dtsi @@ -1823,8 +1823,9 @@ <0 0x4c360000 0 0x10000>, <0 0x4c340000 0 0x4000>; reg-names = "dbi", "config", "atu", "app"; - ranges = <0x81000000 0x0 0x00000000 0x0 0x6ff00000 0 0x00100000>, - <0x82000000 0x0 0x10000000 0x9 0x10000000 0 0x10000000>; + ranges = <0x43000000 0x9 0x00000000 0x9 0x00000000 0x0 0xe0000000>, + <0x82000000 0x0 0xe0000000 0x9 0xe0000000 0x0 0x10000000>, + <0x81000000 0x0 0x00000000 0x9 0xf0000000 0x0 0x00100000>; #address-cells = <3>; #size-cells = <2>; device_type = "pci"; @@ -1898,8 +1899,9 @@ <0 0x4c3e0000 0 0x10000>, <0 0x4c3c0000 0 0x4000>; reg-names = "dbi", "config", "atu", "app"; - ranges = <0x81000000 0 0x00000000 0x8 0x8ff00000 0 0x00100000>, - <0x82000000 0 0x10000000 0xa 0x10000000 0 0x10000000>; + ranges = <0x43000000 0xa 0x00000000 0xa 0x00000000 0x0 0xe0000000>, + <0x82000000 0x0 0xe0000000 0xa 0xe0000000 0x0 0x10000000>, + <0x81000000 0x0 0x00000000 0xa 0xf0000000 0x0 0x00100000>; #address-cells = <3>; #size-cells = <2>; device_type = "pci"; From f3ef944c55991b6b6c85e9be6a7a807e79c17113 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Sun, 24 May 2026 16:54:41 +0200 Subject: [PATCH 0979/3297] arm64: dts: lx2162a-clearfog: use rev2 SoC dtsi [ Upstream commit 13a37b30e464503515625ba891018aec0264c7d2 ] LX2160A and LX2162A are different packages of the same silicon. While LX2160A had two revisions, LX2162A was released later based on LX2160A revision 2. Commit a8fe6c8dfc40 ("arm64: dts: fsl-lx2160a: add rev2 support") has added a new soc dtsi for revision 2. Update LX2162A Clearfog description to use revision 2 dtsi. Fixes: 5093b190f9ce ("arm64: dts: freescale: Add support for LX2162 SoM & Clearfog Board") # no-stable Signed-off-by: Josua Mayer Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts index 8920326a06735..5563c9c47163b 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts +++ b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts @@ -6,7 +6,7 @@ /dts-v1/; -#include "fsl-lx2160a.dtsi" +#include "fsl-lx2160a-rev2.dtsi" #include "fsl-lx2162a-sr-som.dtsi" / { From e6ab22200e449223e3bc8313108f3c95a1f775cf Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 2 Jun 2026 11:33:30 +0200 Subject: [PATCH 0980/3297] arm64: dts: tqma8mpql-mba8mpxl: configure sai clock in audio codec as well [ Upstream commit 748835fcb2eb2120234aef9556d282272501b96b ] With deferrable card binding the sound card driver tries to get the mclk configuration before it is setup in sai3 node. Fix this by setting the sai clock config for the audio codec as well. Fixes: d8f9d8126582 ("arm64: dts: imx8mp: Add analog audio output on i.MX8MP TQMa8MPxL/MBa8MPxL") Signed-off-by: Alexander Stein Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts index ac05c05193c5b..be33cf691adf6 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts @@ -519,6 +519,9 @@ reset-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>; iov-supply = <®_vcc_1v8>; ldoin-supply = <®_vcc_3v3>; + assigned-clocks = <&clk IMX8MP_CLK_SAI3>; + assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; + assigned-clock-rates = <12288000>; }; se97_1c: temperature-sensor@1c { From 04255bda8d795cf69adb2c31c7d6fcd7a1250f85 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 28 May 2026 12:15:56 +0200 Subject: [PATCH 0981/3297] arm64: dts: imx8mp-kontron: Fix GPIO for display power switch [ Upstream commit 5b19ca527471903920d713bc48518a036a95bf6b ] The GPIO that controls the power supply for the LVDS display connector has changed between early prototypes and the current production design of the hardware. Reflect this change in the devicetree to properly switch on the panel supply. This was working before even with the wrong GPIO due to the bidirectional level shifter used on the board which drives the EN signal high even when the input has a (weak) pull down configured as reset condition of the SoC pad. As a result the display was working but the supply was always on. Tested on BL i.MX8MP to show the correct voltage level on the level shifter input. Fixes: 946ab10e3f40 ("arm64: dts: Add support for Kontron OSM-S i.MX8MP SoM and BL carrier board") Signed-off-by: Frieder Schrempf Signed-off-by: Frank Li Signed-off-by: Sasha Levin --- .../boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts index 75ae466427821..29ce863403b88 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts @@ -49,7 +49,9 @@ reg_vcc_panel: regulator-vcc-panel { compatible = "regulator-fixed"; - gpio = <&gpio4 3 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_vcc_panel>; + gpio = <&gpio5 3 GPIO_ACTIVE_HIGH>; enable-active-high; regulator-max-microvolt = <3300000>; regulator-min-microvolt = <3300000>; @@ -172,7 +174,7 @@ &gpio5 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio5>; - gpio-line-names = "I2S_BITCLK", "I2S_A_DATA_OUT", "I2S_MCLK", "PWM_2", + gpio-line-names = "I2S_BITCLK", "I2S_A_DATA_OUT", "I2S_MCLK", "VCC_PANEL_EN", "PWM_1", "PWM_0", "SPI_A_SCK", "CAN_ADDR1", "CAN_ADDR0", "SPI_A_CS0", "SPI_B_SCK", "SPI_B_SDO", "SPI_B_SDI", "SPI_B_CS0", "I2C_A_SCL", "I2C_A_SDA", @@ -329,4 +331,10 @@ MX8MP_IOMUXC_ECSPI1_MISO__GPIO5_IO08 0x46 /* CAN_ADR1 */ >; }; + + pinctrl_reg_vcc_panel: regvccpanelgrp { + fsl,pins = < + MX8MP_IOMUXC_SPDIF_TX__GPIO5_IO03 0x46 + >; + }; }; From f6183983ce1ff254d629a333739082b39d7c5eb6 Mon Sep 17 00:00:00 2001 From: Bernard Metzler Date: Thu, 4 Jun 2026 18:08:08 +0200 Subject: [PATCH 0982/3297] RDMA/siw: Fix endpoint/socket association handling [ Upstream commit ea4f6f6c53577fb3f05dbd78b15e586772d49831 ] Disassociating a socket from an endpoint via siw_socket_disassoc() may release the last reference on that endpoint and free it. Therefore, don't clear the endpoints socket pointer after calling that function, but within. This fixes a: BUG: KASAN: slab-use-after-free in siw_cm_work_handler (drivers/infiniband/sw/siw/siw_cm.c:1053 drivers/infiniband/sw/siw/siw_cm.c:1075) which occurred after processing a malformed MPA request during connection establishment, causing the new endpoint to be closed. Fixes: 6c52fdc244b5c ("rdma/siw: connection management") Link: https://patch.msgid.link/r/20260604160808.30948-1-bernard.metzler@linux.dev Reported-by: Shuangpeng Bai Signed-off-by: Bernard Metzler Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/siw/siw_cm.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index 708b13993fdfd..49ff121f77fe4 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -89,6 +89,7 @@ static void siw_socket_disassoc(struct socket *s) cep = sk_to_cep(sk); if (cep) { siw_sk_restore_upcalls(sk, cep); + cep->sock = NULL; siw_cep_put(cep); } else { pr_warn("siw: cannot restore sk callbacks: no ep\n"); @@ -369,10 +370,11 @@ static void siw_free_cm_id(struct siw_cep *cep) static void siw_destroy_cep_sock(struct siw_cep *cep) { - if (cep->sock) { - siw_socket_disassoc(cep->sock); - sock_release(cep->sock); - cep->sock = NULL; + struct socket *s = cep->sock; + + if (s) { + siw_socket_disassoc(s); + sock_release(s); } } @@ -1001,7 +1003,6 @@ static void siw_accept_newconn(struct siw_cep *cep) if (new_s) { siw_socket_disassoc(new_s); sock_release(new_s); - new_cep->sock = NULL; } siw_dbg_cep(cep, "error %d\n", rv); } @@ -1153,6 +1154,8 @@ static void siw_cm_work_handler(struct work_struct *w) WARN(1, "Undefined CM work type: %d\n", work->type); } if (release_cep) { + struct socket *s = cep->sock; + siw_dbg_cep(cep, "release: timer=%s, QP[%u]\n", cep->mpa_timer ? "y" : "n", @@ -1178,10 +1181,9 @@ static void siw_cm_work_handler(struct work_struct *w) cep->qp = NULL; siw_qp_put(qp); } - if (cep->sock) { - siw_socket_disassoc(cep->sock); - sock_release(cep->sock); - cep->sock = NULL; + if (s) { + siw_socket_disassoc(s); + sock_release(s); } if (cep->cm_id) { siw_free_cm_id(cep); @@ -1511,7 +1513,6 @@ int siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) if (cep) { siw_socket_disassoc(s); sock_release(s); - cep->sock = NULL; cep->qp = NULL; @@ -1886,7 +1887,6 @@ int siw_create_listen(struct iw_cm_id *id, int backlog) siw_cep_set_inuse(cep); siw_free_cm_id(cep); - cep->sock = NULL; siw_socket_disassoc(s); cep->state = SIW_EPSTATE_CLOSED; @@ -1908,6 +1908,7 @@ static void siw_drop_listeners(struct iw_cm_id *id) */ list_for_each_safe(p, tmp, (struct list_head *)id->provider_data) { struct siw_cep *cep = list_entry(p, struct siw_cep, listenq); + struct socket *s = cep->sock; list_del(p); @@ -1916,10 +1917,9 @@ static void siw_drop_listeners(struct iw_cm_id *id) siw_cep_set_inuse(cep); siw_free_cm_id(cep); - if (cep->sock) { - siw_socket_disassoc(cep->sock); - sock_release(cep->sock); - cep->sock = NULL; + if (s) { + siw_socket_disassoc(s); + sock_release(s); } cep->state = SIW_EPSTATE_CLOSED; siw_cep_set_free_and_put(cep); From 2eb39de4962f842d653e96818ae372665cd481fd Mon Sep 17 00:00:00 2001 From: Kaitao Cheng Date: Fri, 5 Jun 2026 17:41:43 +0800 Subject: [PATCH 0983/3297] bpf: Clear rb node linkage when freeing bpf_rb_root [ Upstream commit 4a7910ee060d8ce55612f5b3cc267f3a265a3cec ] bpf_rb_root_free() detaches the root by copying the current rb_root_cached and then replacing the live root with RB_ROOT_CACHED. It then walks the copied root and drops each object contained in the tree. This leaves the rb node state intact while dropping the object. If the object is refcounted and survives the drop, its bpf_rb_node_kern still contains an owner pointer to the freed root and stale rb tree linkage. If a later bpf_rb_root allocation reuses the same address, bpf_rbtree_remove() can incorrectly pass the owner check and call rb_erase_cached() on a node whose rb pointers belong to the old tree. Mirror the list draining behavior by marking nodes as busy while the root is being detached, then clear the rb node and release the owner before dropping the containing object. This makes surviving nodes unowned and safe to reject from remove or accept for a later add. Fixes: 9c395c1b99bd ("bpf: Add basic bpf_rb_{root,node} support") Signed-off-by: Kaitao Cheng Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260605094143.5509-1-kaitao.cheng@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/helpers.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index b596452424481..a62cbdf5a3cde 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2178,6 +2178,7 @@ void bpf_rb_root_free(const struct btf_field *field, void *rb_root, struct bpf_spin_lock *spin_lock) { struct rb_root_cached orig_root, *root = rb_root; + struct bpf_rb_node_kern *node; struct rb_node *pos, *n; void *obj; @@ -2186,14 +2187,20 @@ void bpf_rb_root_free(const struct btf_field *field, void *rb_root, __bpf_spin_lock_irqsave(spin_lock); orig_root = *root; + bpf_rbtree_postorder_for_each_entry_safe(pos, n, &orig_root.rb_root) { + node = rb_entry(pos, struct bpf_rb_node_kern, rb_node); + WRITE_ONCE(node->owner, BPF_PTR_POISON); + } *root = RB_ROOT_CACHED; __bpf_spin_unlock_irqrestore(spin_lock); bpf_rbtree_postorder_for_each_entry_safe(pos, n, &orig_root.rb_root) { obj = pos; obj -= field->graph_root.node_offset; - - + node = rb_entry(pos, struct bpf_rb_node_kern, rb_node); + RB_CLEAR_NODE(pos); + /* Ensure __bpf_rbtree_add() sees the node as unlinked. */ + smp_store_release(&node->owner, NULL); __bpf_obj_drop_impl(obj, field->graph_root.value_rec, false); } } From 58513d6d1241015bed216f6284afdb4e76dda14e Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Fri, 5 Jun 2026 23:52:47 +0800 Subject: [PATCH 0984/3297] bpf: Check tail zero of bpf_map_info [ Upstream commit e2a49fdb1beed150125b4104c90eb2a96ec7f63a ] Since there're 4 bytes padding at the end of struct bpf_map_info, they won't be checked by bpf_check_uarg_tail_zero(). pahole -C bpf_map_info ./vmlinux struct bpf_map_info { ... __u64 hash __attribute__((__aligned__(8))); /* 88 8 */ __u32 hash_size; /* 96 4 */ /* size: 104, cachelines: 2, members: 18 */ /* padding: 4 */ /* forced alignments: 1 */ /* last cacheline: 40 bytes */ } __attribute__((__aligned__(8))); If a future kernel extension adds a new 4-byte field, older userspace programs allocating this structure on the stack might inadvertently pass uninitialized stack garbage into the new field, permanently breaking backward compatibility. -- sashiko [1] Fix it by changing sizeof(info) to offsetofend(struct bpf_map_info, hash_size). And, add "__u32 :32" to the tail of struct bpf_map_info. [1] https://lore.kernel.org/bpf/20260513224823.6494FC19425@smtp.kernel.org/ Fixes: ea2e6467ac36 ("bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD") Acked-by: Mykyta Yatsenko Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260605155249.20772-2-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 5 +++-- tools/include/uapi/linux/bpf.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 6829936d33f58..b39e5b9570788 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -6684,6 +6684,7 @@ struct bpf_map_info { __u64 map_extra; __aligned_u64 hash; __u32 hash_size; + __u32 :32; } __attribute__((aligned(8))); struct bpf_btf_info { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 209d0575d0ab4..01d88c57270a6 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -5282,10 +5282,11 @@ static int bpf_map_get_info_by_fd(struct file *file, { struct bpf_map_info __user *uinfo = u64_to_user_ptr(attr->info.info); struct bpf_map_info info; - u32 info_len = attr->info.info_len; + u32 info_len = attr->info.info_len, len; int err; - err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len); + len = offsetofend(struct bpf_map_info, hash_size); + err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), len, info_len); if (err) return err; info_len = min_t(u32, sizeof(info), info_len); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 6829936d33f58..b39e5b9570788 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -6684,6 +6684,7 @@ struct bpf_map_info { __u64 map_extra; __aligned_u64 hash; __u32 hash_size; + __u32 :32; } __attribute__((aligned(8))); struct bpf_btf_info { From efe57b72196ae5b243e9ce70561a0c17bab79fbd Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Fri, 5 Jun 2026 23:52:48 +0800 Subject: [PATCH 0985/3297] bpf: Check tail zero of bpf_prog_info [ Upstream commit 786be2b05980a5828e67fc564ad7517e2adbe9bd ] Since there're 4 bytes padding at the end of struct bpf_prog_info, they won't be checked by bpf_check_uarg_tail_zero(). pahole -C bpf_prog_info ./vmlinux struct bpf_prog_info { ... __u32 attach_btf_obj_id; /* 220 4 */ __u32 attach_btf_id; /* 224 4 */ /* size: 232, cachelines: 4, members: 38 */ /* sum members: 224 */ /* sum bitfield members: 1 bits, bit holes: 1, sum bit holes: 31 bits */ /* padding: 4 */ /* forced alignments: 9 */ /* last cacheline: 40 bytes */ } __attribute__((__aligned__(8))); If a future kernel extension adds a new 4-byte field, older userspace programs allocating this structure on the stack might inadvertently pass uninitialized stack garbage into the new field, permanently breaking backward compatibility. -- sashiko [1] Fix it by changing sizeof(info) to offsetofend(struct bpf_prog_info, attach_btf_id). And, add "__u32 :32" to the tail of struct bpf_prog_info. [1] https://lore.kernel.org/bpf/20260513224823.6494FC19425@smtp.kernel.org/ Fixes: aba64c7da983 ("bpf: Add verified_insns to bpf_prog_info and fdinfo") Acked-by: Mykyta Yatsenko Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260605155249.20772-3-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/uapi/linux/bpf.h | 1 + kernel/bpf/syscall.c | 5 +++-- tools/include/uapi/linux/bpf.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index b39e5b9570788..a4250e713112f 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -6663,6 +6663,7 @@ struct bpf_prog_info { __u32 verified_insns; __u32 attach_btf_obj_id; __u32 attach_btf_id; + __u32 :32; } __attribute__((aligned(8))); struct bpf_map_info { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 01d88c57270a6..e92fd2bec98c7 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -4997,10 +4997,11 @@ static int bpf_prog_get_info_by_fd(struct file *file, u32 info_len = attr->info.info_len; struct bpf_prog_kstats stats; char __user *uinsns; - u32 ulen; + u32 ulen, len; int err; - err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len); + len = offsetofend(struct bpf_prog_info, attach_btf_id); + err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), len, info_len); if (err) return err; info_len = min_t(u32, sizeof(info), info_len); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index b39e5b9570788..a4250e713112f 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -6663,6 +6663,7 @@ struct bpf_prog_info { __u32 verified_insns; __u32 attach_btf_obj_id; __u32 attach_btf_id; + __u32 :32; } __attribute__((aligned(8))); struct bpf_map_info { From 495e7e832c670a718caa8906c6f589f34f739715 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Tue, 2 Jun 2026 23:09:30 +0800 Subject: [PATCH 0986/3297] bpf: Update transport_header when encapsulating UDP tunnel in lwt [ Upstream commit 82d7d0adbc678064543e9d254864f6b4ea4a388c ] Currently, bpf_lwt_push_ip_encap() does not update skb->transport_header. When a driver, e.g. ice, reuses the stale skb->transport_header to offload checksum computation to NIC hardware, VxLAN packets encapsulated by bpf_lwt_push_encap() helper may be dropped due to incorrect checksum. Update skb->transport_header in bpf_lwt_push_ip_encap() whenever the encapsulated packet uses UDP, so checksum offload works correctly. Fixes: 52f278774e79 ("bpf: implement BPF_LWT_ENCAP_IP mode in bpf_lwt_push_encap") Cc: Leon Hwang Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260602150931.49629-2-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/lwt_bpf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index 9f40be0c3e71d..2efcda93d1397 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -595,6 +595,7 @@ static int handle_gso_encap(struct sk_buff *skb, bool ipv4, int encap_len) int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress) { + bool is_udp_tunnel; struct iphdr *iph; bool ipv4; int err; @@ -608,10 +609,16 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress) ipv4 = true; if (unlikely(len < iph->ihl * 4)) return -EINVAL; + is_udp_tunnel = iph->protocol == IPPROTO_UDP; + if (unlikely(is_udp_tunnel && len < iph->ihl * 4 + sizeof(struct udphdr))) + return -EINVAL; } else if (iph->version == 6) { ipv4 = false; if (unlikely(len < sizeof(struct ipv6hdr))) return -EINVAL; + is_udp_tunnel = ((struct ipv6hdr *)iph)->nexthdr == NEXTHDR_UDP; + if (unlikely(is_udp_tunnel && len < sizeof(struct ipv6hdr) + sizeof(struct udphdr))) + return -EINVAL; } else { return -EINVAL; } @@ -633,6 +640,11 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress) if (ingress) skb_postpush_rcsum(skb, iph, len); skb_reset_network_header(skb); + if (is_udp_tunnel) { + size_t iph_sz = ipv4 ? iph->ihl * 4 : sizeof(struct ipv6hdr); + + skb_set_transport_header(skb, skb_network_offset(skb) + iph_sz); + } memcpy(skb_network_header(skb), hdr, len); bpf_compute_data_pointers(skb); skb_clear_hash(skb); From 1b5d8a248c3afa640bcc99fa95abcd1e36f3ee18 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:16 +0000 Subject: [PATCH 0987/3297] wifi: wcn36xx: fix heap overflow from oversized firmware HAL response [ Upstream commit 88a240d86d3d64521f9194abe185ac71cc74d0bd ] The firmware response dispatcher copies all synchronous HAL responses into the 4096-byte hal_buf without validating the response length. A response exceeding WCN36XX_HAL_BUF_SIZE causes a heap buffer overflow with firmware-controlled content. Add a bounds check on the response length. Fixes: 8e84c2582169 ("wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-2-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/wcn36xx/smd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 2cf86fc3f8fe0..f550eb8630132 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -3353,6 +3353,10 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, case WCN36XX_HAL_EXIT_IMPS_RSP: case WCN36XX_HAL_UPDATE_CHANNEL_LIST_RSP: case WCN36XX_HAL_ADD_BCN_FILTER_RSP: + if (len > WCN36XX_HAL_BUF_SIZE) { + wcn36xx_warn("HAL response too large: %d\n", len); + break; + } memcpy(wcn->hal_buf, buf, len); wcn->hal_rsp_len = len; complete(&wcn->hal_rsp_compl); From f03782f7f41f2afee5076a1ef08ced5649218771 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:17 +0000 Subject: [PATCH 0988/3297] wifi: wcn36xx: fix OOB read from firmware count in PRINT_REG_INFO indication [ Upstream commit df2187acfca6c6cca372c5d35f42394d9c270b09 ] The firmware-controlled rsp->count field is used as the loop bound for indexing into the flexible rsp->regs[] array without validation against the message length. A count exceeding the actual data causes out-of- bounds reads from the heap-allocated message buffer. Add a check that count fits within the received message. Fixes: 43efa3c0f241 ("wcn36xx: Implement print_reg indication") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-3-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/wcn36xx/smd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index f550eb8630132..0cea465edf327 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2865,6 +2865,12 @@ static int wcn36xx_smd_print_reg_info_ind(struct wcn36xx *wcn, return -EIO; } + if (rsp->count > (len - sizeof(*rsp)) / sizeof(rsp->regs[0])) { + wcn36xx_warn("Truncated print reg info indication: count %u, len %zu\n", + rsp->count, len); + return -EIO; + } + wcn36xx_dbg(WCN36XX_DBG_HAL, "reginfo indication, scenario: 0x%x reason: 0x%x\n", rsp->scenario, rsp->reason); From af8f0ea1f0a3a5fb5ed2b8fed3f1501d644597ee Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:18 +0000 Subject: [PATCH 0989/3297] wifi: wcn36xx: fix OOB read from short trigger BA firmware response [ Upstream commit b5e6f21923ca89d90256e7346301056f6502691e ] The firmware response length is only checked against sizeof(*rsp) (20 bytes), but when candidate_cnt >= 1, a 22-byte candidate struct is read at buf + 20 without verifying the response contains it. This causes an out-of-bounds read of stale heap data, corrupting the BA session state. Add validation that the response includes the candidate data. Fixes: 16be1ac55944 ("wcn36xx: Parse trigger_ba response properly") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-4-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/wcn36xx/smd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 0cea465edf327..f7457dcd8980c 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2659,6 +2659,9 @@ static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len, struct add_ba_info *ba if (rsp->candidate_cnt < 1) return rsp->status ? rsp->status : -EINVAL; + if (len < sizeof(*rsp) + sizeof(*candidate)) + return -EINVAL; + candidate = (struct wcn36xx_hal_trigger_ba_rsp_candidate *)(buf + sizeof(*rsp)); for (i = 0; i < STACFG_MAX_TC; i++) { From 4b2b6bc7f5ebe75f5d4d4421896594f6a71e3e87 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 6 Jun 2026 13:09:13 +0900 Subject: [PATCH 0990/3297] ALSA: seq: Fix partial userptr event expansion [ Upstream commit 2b7bd6f548292aec92a386deebe62324d21d62a9 ] snd_seq_expand_var_event_at() clamps the number of bytes to copy to the remaining variable-event length, but passes the original buffer size to expand_var_event(). For SNDRV_SEQ_EXT_USRPTR events, expand_var_event() copies exactly the size argument from userspace. On the final chunk, when the remaining event data is shorter than the caller's buffer, this can read past the declared event data and can spuriously fail with -EFAULT if the extra bytes cross an unmapped page. Pass the clamped length instead. The chained and kernel-backed paths already reclamp in dump_var_event(), but the user-pointer path handles the size directly. Fixes: ea46f79709b6 ("ALSA: seq: Add snd_seq_expand_var_event_at() helper") Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260606040913.230213-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index e7edea10027f3..60afe351bbca8 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -211,7 +211,7 @@ int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count, len -= offset; if (len > count) len = count; - err = expand_var_event(event, offset, count, buf, true); + err = expand_var_event(event, offset, len, buf, true); if (err < 0) return err; return len; From 834d4cc067fa6c7c7262b715045f3ceeaef08c14 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 0991/3297] riscv: cpu_ops: Change return value type of cpu_is_stopped() to bool [ Upstream commit 2b2b207e1162e577cd6208e184d3d3a0fcfa9cca ] In the original sbi_cpu_is_stopped(), if rc doesn't equal to the SBI_HSM_STATE_STOPPED, it will return rc to the caller directly. But there is a hidden problem, the rc could be SBI_HSM_STATE_STARTED, if so, this function will report cpu stopped while the cpu isn't really stopped. Furthermore, from the name of cpu_is_stopped(), it gives a sense the return value is a bool type, true means the cpu is stopped, conversely false means the cpu is not stopped. Here change the return value type to bool and change the callers accordingly. This could fix the above two issues. Fixes: f1e58583b9c7c ("RISC-V: Support cpu hotplug") Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260413123515.48423-1-hui.wang@canonical.com [pjw@kernel.org: cleaned up some of the pr_warn() messages] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/include/asm/cpu_ops.h | 2 +- arch/riscv/kernel/cpu-hotplug.c | 4 ++-- arch/riscv/kernel/cpu_ops_sbi.c | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/arch/riscv/include/asm/cpu_ops.h b/arch/riscv/include/asm/cpu_ops.h index 176b570ef9827..065811fca594d 100644 --- a/arch/riscv/include/asm/cpu_ops.h +++ b/arch/riscv/include/asm/cpu_ops.h @@ -24,7 +24,7 @@ struct cpu_operations { struct task_struct *tidle); #ifdef CONFIG_HOTPLUG_CPU void (*cpu_stop)(void); - int (*cpu_is_stopped)(unsigned int cpu); + bool (*cpu_is_stopped)(unsigned int cpu); #endif }; diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index 3f50d3dd76c6f..4570ff4b4e8ad 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -58,8 +58,8 @@ void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu) /* Verify from the firmware if the cpu is really stopped*/ if (cpu_ops->cpu_is_stopped) ret = cpu_ops->cpu_is_stopped(cpu); - if (ret) - pr_warn("CPU%u may not have stopped: %d\n", cpu, ret); + if (!ret) + pr_warn("CPU%u may not have stopped\n", cpu); } /* diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index 87d6559448039..05b9fe3a35a41 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -88,16 +88,19 @@ static void sbi_cpu_stop(void) pr_crit("Unable to stop the cpu %u (%d)\n", smp_processor_id(), ret); } -static int sbi_cpu_is_stopped(unsigned int cpuid) +static bool sbi_cpu_is_stopped(unsigned int cpuid) { int rc; unsigned long hartid = cpuid_to_hartid_map(cpuid); rc = sbi_hsm_hart_get_status(hartid); - if (rc == SBI_HSM_STATE_STOPPED) - return 0; - return rc; + if (rc != SBI_HSM_STATE_STOPPED) { + pr_warn("HART%lu isn't stopped; status %d\n", hartid, rc); + return false; + } + + return true; } #endif From c04e0cde2fa38acda5b4a1ac087379d6e245f70b Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Sat, 6 Jun 2026 20:17:59 -0600 Subject: [PATCH 0992/3297] riscv: stacktrace: Remove bogus -0x4 offset in non-FP walk_stackframe [ Upstream commit 8ac35bac70e7e581d673b76878f7691cdadc33b8 ] In the non-frame-pointer version of walk_stackframe, each value read from the stack is treated as a potential return address and has 0x4 subtracted before being used as the program counter. This was intended to convert the return address (the instruction after a call) back to the call site, but it is incorrect: 1. RISC-V has variable-length instructions due to the RVC (compressed instruction) extension. A call instruction can be either 4 bytes (regular) or 2 bytes (compressed, e.g. c.jal). Subtracting a fixed 0x4 assumes all call instructions are 4 bytes, which is wrong for compressed instructions. 2. Stack traces conventionally report return addresses, not call sites. Other architectures (ARM64, x86, ARM) do not subtract instruction size from return addresses in their stack unwinding code. 3. The frame-pointer version of walk_stackframe already dropped the -0x4 offset. Commit b785ec129bd9 ("riscv/ftrace: Add HAVE_FUNCTION_GRAPH_RET_ADDR_PTR support") replaced "pc = frame->ra - 0x4" with ftrace_graph_ret_addr(), and the commit message explicitly noted that "the original calculation, pc = frame->ra - 4, is buggy when the instruction at the return address happened to be a compressed inst." The non-FP version was simply overlooked. Remove the bogus -0x4 offset to match the FP version and the conventions used by other architectures. Fixes: 5d8544e2d007 ("RISC-V: Generic library routines and assembly") Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260603115329.791603-2-qirui.001@bytedance.com Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index b41b6255751cb..31fd3abb57d32 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -129,7 +129,7 @@ void notrace walk_stackframe(struct task_struct *task, while (!kstack_end(ksp)) { if (__kernel_text_address(pc) && unlikely(!fn(arg, pc))) break; - pc = READ_ONCE_NOCHECK(*ksp++) - 0x4; + pc = READ_ONCE_NOCHECK(*ksp++); } } From 74ac1ce1f4afdb3b80b6742fa28fb86c8c51d31b Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Sat, 6 Jun 2026 17:41:29 -0700 Subject: [PATCH 0993/3297] ALSA: seq: Clear variable event pointer on read [ Upstream commit 705dd6dcbc0ea87351c660c1a6443f85f1001c76 ] snd_seq_read() copies a queued variable-length event header to userspace before expanding the payload. Queued variable-length events use SNDRV_SEQ_EXT_CHAINED internally, and data.ext.ptr points at the first extension cell. The read side strips SNDRV_SEQ_EXT_* bits from data.ext.len before the copy, but it leaves data.ext.ptr untouched. A userspace sequencer client can therefore write a direct variable event to itself and read back the extension-cell kernel address from the returned header. Clear the temporary header pointer before copy_to_user(). The original queued event remains unchanged and is still passed to snd_seq_expand_var_event(), so payload expansion keeps using the internal chain. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kyle Zeng Link: https://patch.msgid.link/20260607004129.61345-1-kylebot@openai.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_clientmgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 46a1ded7eb068..b8b04fc79f489 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -441,6 +441,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count, memcpy(&tmpev, &cell->event, aligned_size); tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; + tmpev.data.ext.ptr = NULL; if (copy_to_user(buf, &tmpev, aligned_size)) { err = -EFAULT; break; From 8b0510cc3a4a000d4ed1a56cd96231f3d3ba94c5 Mon Sep 17 00:00:00 2001 From: Mykyta Yatsenko Date: Sun, 7 Jun 2026 13:30:41 -0700 Subject: [PATCH 0994/3297] bpf: Fix NMI/tracepoint re-entry deadlock on lru locks [ Upstream commit 89edbdfc5d0308cef57b71359331de5c4ddbf763 ] NMI and tracepoint BPF programs can re-enter the per-CPU or global LRU lock that bpf_lru_pop_free()/push_free() already hold on the same CPU, AA-deadlocking. Lockdep reports "inconsistent {INITIAL USE} -> {IN-NMI}" on &l->lock (syzbot c69a0a2c816716f1e0d5) and "possible recursive locking detected" on &loc_l->lock (syzbot 18b26edb69b2e19f3b33). Prior trylock and rqspinlock based fixes (see links) were nacked because compromised on reliability. This patch converts every LRU lock site to rqspinlock_t and adds a recovery path for some failure windows to avoid node leaks. Failure recovery: - *_pop_free top-level: return NULL; prealloc_lru_pop() already treats that as no-free-element (-ENOMEM). - Cross-CPU steal: skip the victim's locked loc_l, try next CPU. - Post-steal local lock fail: publish stolen node to lockless per-CPU free_llist; next pop on this CPU picks it up. - push_free fail: mark node pending_free=1. __local_list_flush(), __local_list_pop_pending() reclaim the node from pending_list. __bpf_lru_list_shrink_inactive() reclaims the node from inactive list. Nodes from active list are reclaimed by __bpf_lru_list_shrink() or after __bpf_lru_list_rotate_active() demotes it to the inactive. Fixes: 3a08c2fd7634 ("bpf: LRU List") Reported-by: syzbot+c69a0a2c816716f1e0d5@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c69a0a2c816716f1e0d5 Reported-by: syzbot+18b26edb69b2e19f3b33@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=18b26edb69b2e19f3b33 Link: https://lore.kernel.org/bpf/CAPPBnEYO4R+m+SpVc2gNj_x31R6fo1uJvj2bK2YS1P09GWT6kQ@mail.gmail.com/ Link: https://lore.kernel.org/bpf/CAPPBnEZmFA3ab8Uc=PEm0bdojZy=7T_F5_+eyZSHyZR3MBG4Vw@mail.gmail.com/ Link: https://lore.kernel.org/bpf/20251030030010.95352-1-dongml2@chinatelecom.cn/ Link: https://lore.kernel.org/bpf/20260119142120.28170-1-leon.hwang@linux.dev/ Signed-off-by: Mykyta Yatsenko Link: https://lore.kernel.org/r/20260607-lru_map_spin-v3-1-bcd9332e911b@meta.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/bpf_lru_list.c | 165 +++++++++++++++++++++++--------------- kernel/bpf/bpf_lru_list.h | 25 ++++-- 2 files changed, 119 insertions(+), 71 deletions(-) diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c index e7a2fc60523f6..5ed7cb4b98c00 100644 --- a/kernel/bpf/bpf_lru_list.c +++ b/kernel/bpf/bpf_lru_list.c @@ -13,23 +13,8 @@ #define PERCPU_FREE_TARGET (4) #define PERCPU_NR_SCANS PERCPU_FREE_TARGET -/* Helpers to get the local list index */ -#define LOCAL_LIST_IDX(t) ((t) - BPF_LOCAL_LIST_T_OFFSET) -#define LOCAL_FREE_LIST_IDX LOCAL_LIST_IDX(BPF_LRU_LOCAL_LIST_T_FREE) -#define LOCAL_PENDING_LIST_IDX LOCAL_LIST_IDX(BPF_LRU_LOCAL_LIST_T_PENDING) #define IS_LOCAL_LIST_TYPE(t) ((t) >= BPF_LOCAL_LIST_T_OFFSET) -/* Local list helpers */ -static struct list_head *local_free_list(struct bpf_lru_locallist *loc_l) -{ - return &loc_l->lists[LOCAL_FREE_LIST_IDX]; -} - -static struct list_head *local_pending_list(struct bpf_lru_locallist *loc_l) -{ - return &loc_l->lists[LOCAL_PENDING_LIST_IDX]; -} - /* bpf_lru_node helpers */ static bool bpf_lru_node_is_ref(const struct bpf_lru_node *node) { @@ -72,6 +57,7 @@ static void __bpf_lru_node_move_to_free(struct bpf_lru_list *l, bpf_lru_list_count_dec(l, node->type); node->type = tgt_free_type; + WRITE_ONCE(node->pending_free, 0); list_move(&node->list, free_list); } @@ -87,6 +73,9 @@ static void __bpf_lru_node_move_in(struct bpf_lru_list *l, bpf_lru_list_count_inc(l, tgt_type); node->type = tgt_type; bpf_lru_node_clear_ref(node); + /* Reset pending_free only when moving to the free list */ + if (tgt_type == BPF_LRU_LIST_T_FREE) + WRITE_ONCE(node->pending_free, 0); list_move(&node->list, &l->lists[tgt_type]); } @@ -212,9 +201,11 @@ __bpf_lru_list_shrink_inactive(struct bpf_lru *lru, unsigned int i = 0; list_for_each_entry_safe_reverse(node, tmp_node, inactive, list) { - if (bpf_lru_node_is_ref(node)) { + if (bpf_lru_node_is_ref(node) && + !READ_ONCE(node->pending_free)) { __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_ACTIVE); - } else if (lru->del_from_htab(lru->del_arg, node)) { + } else if (READ_ONCE(node->pending_free) || + lru->del_from_htab(lru->del_arg, node)) { __bpf_lru_node_move_to_free(l, node, free_list, tgt_free_type); if (++nshrinked == tgt_nshrink) @@ -273,7 +264,8 @@ static unsigned int __bpf_lru_list_shrink(struct bpf_lru *lru, list_for_each_entry_safe_reverse(node, tmp_node, force_shrink_list, list) { - if (lru->del_from_htab(lru->del_arg, node)) { + if (READ_ONCE(node->pending_free) || + lru->del_from_htab(lru->del_arg, node)) { __bpf_lru_node_move_to_free(l, node, free_list, tgt_free_type); return 1; @@ -290,8 +282,10 @@ static void __local_list_flush(struct bpf_lru_list *l, struct bpf_lru_node *node, *tmp_node; list_for_each_entry_safe_reverse(node, tmp_node, - local_pending_list(loc_l), list) { - if (bpf_lru_node_is_ref(node)) + &loc_l->pending_list, list) { + if (READ_ONCE(node->pending_free)) + __bpf_lru_node_move_in(l, node, BPF_LRU_LIST_T_FREE); + else if (bpf_lru_node_is_ref(node)) __bpf_lru_node_move_in(l, node, BPF_LRU_LIST_T_ACTIVE); else __bpf_lru_node_move_in(l, node, @@ -307,9 +301,12 @@ static void bpf_lru_list_push_free(struct bpf_lru_list *l, if (WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(node->type))) return; - raw_spin_lock_irqsave(&l->lock, flags); + if (raw_res_spin_lock_irqsave(&l->lock, flags)) { + WRITE_ONCE(node->pending_free, 1); + return; + } __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE); - raw_spin_unlock_irqrestore(&l->lock, flags); + raw_res_spin_unlock_irqrestore(&l->lock, flags); } static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, @@ -318,8 +315,10 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, struct bpf_lru_list *l = &lru->common_lru.lru_list; struct bpf_lru_node *node, *tmp_node; unsigned int nfree = 0; + LIST_HEAD(tmp_free); - raw_spin_lock(&l->lock); + if (raw_res_spin_lock(&l->lock)) + return; __local_list_flush(l, loc_l); @@ -327,7 +326,7 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, list_for_each_entry_safe(node, tmp_node, &l->lists[BPF_LRU_LIST_T_FREE], list) { - __bpf_lru_node_move_to_free(l, node, local_free_list(loc_l), + __bpf_lru_node_move_to_free(l, node, &tmp_free, BPF_LRU_LOCAL_LIST_T_FREE); if (++nfree == lru->target_free) break; @@ -335,10 +334,19 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, if (nfree < lru->target_free) __bpf_lru_list_shrink(lru, l, lru->target_free - nfree, - local_free_list(loc_l), + &tmp_free, BPF_LRU_LOCAL_LIST_T_FREE); - raw_spin_unlock(&l->lock); + raw_res_spin_unlock(&l->lock); + + /* + * Transfer the harvested nodes from the temporary list_head into + * the lockless per-CPU free llist. + */ + list_for_each_entry_safe(node, tmp_node, &tmp_free, list) { + list_del(&node->list); + llist_add(&node->llist, &loc_l->free_llist); + } } static void __local_list_add_pending(struct bpf_lru *lru, @@ -350,22 +358,21 @@ static void __local_list_add_pending(struct bpf_lru *lru, *(u32 *)((void *)node + lru->hash_offset) = hash; node->cpu = cpu; node->type = BPF_LRU_LOCAL_LIST_T_PENDING; + WRITE_ONCE(node->pending_free, 0); bpf_lru_node_clear_ref(node); - list_add(&node->list, local_pending_list(loc_l)); + list_add(&node->list, &loc_l->pending_list); } static struct bpf_lru_node * __local_list_pop_free(struct bpf_lru_locallist *loc_l) { - struct bpf_lru_node *node; + struct llist_node *llnode; - node = list_first_entry_or_null(local_free_list(loc_l), - struct bpf_lru_node, - list); - if (node) - list_del(&node->list); + llnode = llist_del_first(&loc_l->free_llist); + if (!llnode) + return NULL; - return node; + return container_of(llnode, struct bpf_lru_node, llist); } static struct bpf_lru_node * @@ -376,10 +383,10 @@ __local_list_pop_pending(struct bpf_lru *lru, struct bpf_lru_locallist *loc_l) ignore_ref: /* Get from the tail (i.e. older element) of the pending list. */ - list_for_each_entry_reverse(node, local_pending_list(loc_l), - list) { + list_for_each_entry_reverse(node, &loc_l->pending_list, list) { if ((!bpf_lru_node_is_ref(node) || force) && - lru->del_from_htab(lru->del_arg, node)) { + (READ_ONCE(node->pending_free) || + lru->del_from_htab(lru->del_arg, node))) { list_del(&node->list); return node; } @@ -404,7 +411,8 @@ static struct bpf_lru_node *bpf_percpu_lru_pop_free(struct bpf_lru *lru, l = per_cpu_ptr(lru->percpu_lru, cpu); - raw_spin_lock_irqsave(&l->lock, flags); + if (raw_res_spin_lock_irqsave(&l->lock, flags)) + return NULL; __bpf_lru_list_rotate(lru, l); @@ -420,7 +428,7 @@ static struct bpf_lru_node *bpf_percpu_lru_pop_free(struct bpf_lru *lru, __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_INACTIVE); } - raw_spin_unlock_irqrestore(&l->lock, flags); + raw_res_spin_unlock_irqrestore(&l->lock, flags); return node; } @@ -437,7 +445,8 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru, loc_l = per_cpu_ptr(clru->local_list, cpu); - raw_spin_lock_irqsave(&loc_l->lock, flags); + if (raw_res_spin_lock_irqsave(&loc_l->lock, flags)) + return NULL; node = __local_list_pop_free(loc_l); if (!node) { @@ -448,17 +457,22 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru, if (node) __local_list_add_pending(lru, loc_l, cpu, node, hash); - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + raw_res_spin_unlock_irqrestore(&loc_l->lock, flags); if (node) return node; - /* No free nodes found from the local free list and + /* + * No free nodes found from the local free list and * the global LRU list. * * Steal from the local free/pending list of the * current CPU and remote CPU in RR. It starts * with the loc_l->next_steal CPU. + * + * Acquire the victim's lock before touching either list. On + * acquisition failure (rqspinlock AA or timeout) skip the victim + * and try the next CPU. */ first_steal = loc_l->next_steal; @@ -466,24 +480,36 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru, do { steal_loc_l = per_cpu_ptr(clru->local_list, steal); - raw_spin_lock_irqsave(&steal_loc_l->lock, flags); - - node = __local_list_pop_free(steal_loc_l); - if (!node) - node = __local_list_pop_pending(lru, steal_loc_l); - - raw_spin_unlock_irqrestore(&steal_loc_l->lock, flags); + if (!raw_res_spin_lock_irqsave(&steal_loc_l->lock, flags)) { + node = __local_list_pop_free(steal_loc_l); + if (!node) + node = __local_list_pop_pending(lru, steal_loc_l); + raw_res_spin_unlock_irqrestore(&steal_loc_l->lock, flags); + } steal = cpumask_next_wrap(steal, cpu_possible_mask); } while (!node && steal != first_steal); loc_l->next_steal = steal; - if (node) { - raw_spin_lock_irqsave(&loc_l->lock, flags); - __local_list_add_pending(lru, loc_l, cpu, node, hash); - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + if (!node) + return NULL; + + if (raw_res_spin_lock_irqsave(&loc_l->lock, flags)) { + /* + * The local pending lock can't be acquired (rqspinlock AA + * or timeout). Return the stolen node to the per-CPU + * free_llist instead of orphaning it; the next pop_free on + * this CPU will pick it up. + */ + node->type = BPF_LRU_LOCAL_LIST_T_FREE; + bpf_lru_node_clear_ref(node); + WRITE_ONCE(node->pending_free, 0); + llist_add(&node->llist, &loc_l->free_llist); + return NULL; } + __local_list_add_pending(lru, loc_l, cpu, node, hash); + raw_res_spin_unlock_irqrestore(&loc_l->lock, flags); return node; } @@ -511,18 +537,24 @@ static void bpf_common_lru_push_free(struct bpf_lru *lru, loc_l = per_cpu_ptr(lru->common_lru.local_list, node->cpu); - raw_spin_lock_irqsave(&loc_l->lock, flags); + if (raw_res_spin_lock_irqsave(&loc_l->lock, flags)) { + WRITE_ONCE(node->pending_free, 1); + return; + } if (unlikely(node->type != BPF_LRU_LOCAL_LIST_T_PENDING)) { - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + raw_res_spin_unlock_irqrestore(&loc_l->lock, + flags); goto check_lru_list; } node->type = BPF_LRU_LOCAL_LIST_T_FREE; bpf_lru_node_clear_ref(node); - list_move(&node->list, local_free_list(loc_l)); + list_del(&node->list); + + raw_res_spin_unlock_irqrestore(&loc_l->lock, flags); - raw_spin_unlock_irqrestore(&loc_l->lock, flags); + llist_add(&node->llist, &loc_l->free_llist); return; } @@ -538,11 +570,14 @@ static void bpf_percpu_lru_push_free(struct bpf_lru *lru, l = per_cpu_ptr(lru->percpu_lru, node->cpu); - raw_spin_lock_irqsave(&l->lock, flags); + if (raw_res_spin_lock_irqsave(&l->lock, flags)) { + WRITE_ONCE(node->pending_free, 1); + return; + } __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE); - raw_spin_unlock_irqrestore(&l->lock, flags); + raw_res_spin_unlock_irqrestore(&l->lock, flags); } void bpf_lru_push_free(struct bpf_lru *lru, struct bpf_lru_node *node) @@ -565,6 +600,7 @@ static void bpf_common_lru_populate(struct bpf_lru *lru, void *buf, node = (struct bpf_lru_node *)(buf + node_offset); node->type = BPF_LRU_LIST_T_FREE; + node->pending_free = 0; bpf_lru_node_clear_ref(node); list_add(&node->list, &l->lists[BPF_LRU_LIST_T_FREE]); buf += elem_size; @@ -594,6 +630,7 @@ static void bpf_percpu_lru_populate(struct bpf_lru *lru, void *buf, node = (struct bpf_lru_node *)(buf + node_offset); node->cpu = cpu; node->type = BPF_LRU_LIST_T_FREE; + node->pending_free = 0; bpf_lru_node_clear_ref(node); list_add(&node->list, &l->lists[BPF_LRU_LIST_T_FREE]); i++; @@ -618,14 +655,12 @@ void bpf_lru_populate(struct bpf_lru *lru, void *buf, u32 node_offset, static void bpf_lru_locallist_init(struct bpf_lru_locallist *loc_l, int cpu) { - int i; - - for (i = 0; i < NR_BPF_LRU_LOCAL_LIST_T; i++) - INIT_LIST_HEAD(&loc_l->lists[i]); + INIT_LIST_HEAD(&loc_l->pending_list); + init_llist_head(&loc_l->free_llist); loc_l->next_steal = cpu; - raw_spin_lock_init(&loc_l->lock); + raw_res_spin_lock_init(&loc_l->lock); } static void bpf_lru_list_init(struct bpf_lru_list *l) @@ -640,7 +675,7 @@ static void bpf_lru_list_init(struct bpf_lru_list *l) l->next_inactive_rotation = &l->lists[BPF_LRU_LIST_T_INACTIVE]; - raw_spin_lock_init(&l->lock); + raw_res_spin_lock_init(&l->lock); } int bpf_lru_init(struct bpf_lru *lru, bool percpu, u32 hash_offset, diff --git a/kernel/bpf/bpf_lru_list.h b/kernel/bpf/bpf_lru_list.h index fe2661a58ea94..8d0ee61622af0 100644 --- a/kernel/bpf/bpf_lru_list.h +++ b/kernel/bpf/bpf_lru_list.h @@ -6,11 +6,11 @@ #include #include -#include +#include +#include #define NR_BPF_LRU_LIST_T (3) #define NR_BPF_LRU_LIST_COUNT (2) -#define NR_BPF_LRU_LOCAL_LIST_T (2) #define BPF_LOCAL_LIST_T_OFFSET NR_BPF_LRU_LIST_T enum bpf_lru_list_type { @@ -22,10 +22,22 @@ enum bpf_lru_list_type { }; struct bpf_lru_node { - struct list_head list; + /* + * A node is in at most one list at a time. The free path on the + * per-CPU locallist uses an llist, so share storage via a union. + */ + union { + struct list_head list; + struct llist_node llist; + }; u16 cpu; u8 type; u8 ref; + /* + * Marks nodes whose *_push_free() lock acquire failed; reclaimed + * by flush/shrink which honor the flag instead of del_from_htab(). + */ + u8 pending_free; }; struct bpf_lru_list { @@ -34,13 +46,14 @@ struct bpf_lru_list { /* The next inactive list rotation starts from here */ struct list_head *next_inactive_rotation; - raw_spinlock_t lock ____cacheline_aligned_in_smp; + rqspinlock_t lock ____cacheline_aligned_in_smp; }; struct bpf_lru_locallist { - struct list_head lists[NR_BPF_LRU_LOCAL_LIST_T]; + struct list_head pending_list; + struct llist_head free_llist; u16 next_steal; - raw_spinlock_t lock; + rqspinlock_t lock; }; struct bpf_common_lru { From 8daa1a64711ed68d1c9bf59392e30d63dc4a8fce Mon Sep 17 00:00:00 2001 From: David Gow Date: Sat, 6 Jun 2026 10:03:15 +0800 Subject: [PATCH 0995/3297] kunit:tool: Don't write to stdout when it should be disabled [ Upstream commit 29afed142d64e181749214072315c976f8510bd7 ] The kunit_parser module accepts a 'printer' object which is used as a destination for all output. This is typically set to stdout, so that the parsed results are visible, but can be set to a special 'null_printer' to implement options where not all results are always printed. However, there are a few places where use of stdout is hardcoded, notably in handling crashed tests and in outputting the colour escape sequences. Properly use the specified printer for all output. This is okay for the colour handling (as this is already gated behind isatty() anyway), and also for the crash handling, as cases where printer != stdout are separately printed afterwards. Link: https://lore.kernel.org/r/20260606020317.264178-1-david@davidgow.net Fixes: 062a9dd9bad7 ("kunit: tool: Only print the summary") Signed-off-by: David Gow Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- tools/testing/kunit/kunit_parser.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index 333cd3a4a56b6..e07c9d062252a 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -17,7 +17,7 @@ from enum import Enum, auto from typing import Iterable, Iterator, List, Optional, Tuple -from kunit_printer import Printer, stdout +from kunit_printer import Printer class Test: """ @@ -57,7 +57,7 @@ def __repr__(self) -> str: def add_error(self, printer: Printer, error_message: str) -> None: """Records an error that occurred while parsing this test.""" self.counts.errors += 1 - printer.print_with_timestamp(stdout.red('[ERROR]') + f' Test: {self.name}: {error_message}') + printer.print_with_timestamp(printer.red('[ERROR]') + f' Test: {self.name}: {error_message}') def ok_status(self) -> bool: """Returns true if the status was ok, i.e. passed or skipped.""" @@ -544,7 +544,7 @@ def format_test_result(test: Test, printer: Printer) -> str: return printer.yellow('[NO TESTS RUN] ') + test.name if test.status == TestStatus.TEST_CRASHED: print_log(test.log, printer) - return stdout.red('[CRASHED] ') + test.name + return printer.red('[CRASHED] ') + test.name print_log(test.log, printer) return printer.red('[FAILED] ') + test.name @@ -651,11 +651,11 @@ def print_summary_line(test: Test, printer: Printer) -> None: printer - Printer object to output results """ if test.status == TestStatus.SUCCESS: - color = stdout.green + color = printer.green elif test.status in (TestStatus.SKIPPED, TestStatus.NO_TESTS): - color = stdout.yellow + color = printer.yellow else: - color = stdout.red + color = printer.red printer.print_with_timestamp(color(f'Testing complete. {test.counts}')) # Summarize failures that might have gone off-screen since we had a lot From 84932636d020b5d235ee0a38b7d8895075feef16 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Jun 2026 10:26:26 +0200 Subject: [PATCH 0996/3297] powerpc/8xx: implement get_direction() in cpm1 [ Upstream commit 052f67a1ae6ffa6e43c82193bf2e6c0dfd2e6d8f ] The lack of get_direction() callbacks in this driver causes GPIOLIB to emit a warning. Implement them for 16- and 32-bit variants. Reported-by: Christophe Leroy Closes: https://lore.kernel.org/all/63487206f6e5a93eaf9f41784317fe99d394312f.1780399750.git.chleroy@kernel.org/ Signed-off-by: Bartosz Golaszewski Fixes: e623c4303ed1 ("gpiolib: sanitize the return value of gpio_chip::get_direction()") Tested-by: Christophe Leroy (CS GROUP) Reviewed-by: Linus Walleij Reviewed-by: Christophe Leroy (CS GROUP) [Maddy: Fixed the Fixes tag] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260603-powerpc-8xx-cpm1-get-dir-v1-1-2ae1c9a5b992@oss.qualcomm.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/8xx/cpm1.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/powerpc/platforms/8xx/cpm1.c b/arch/powerpc/platforms/8xx/cpm1.c index 7433be7d66ee9..55cdf3b8e7566 100644 --- a/arch/powerpc/platforms/8xx/cpm1.c +++ b/arch/powerpc/platforms/8xx/cpm1.c @@ -472,6 +472,18 @@ static int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio16_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport16 __iomem *iop = cpm1_gc->regs; + u16 pin_mask = 1 << (15 - gpio); + + if (in_be16(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add16(struct device *dev) { struct device_node *np = dev->of_node; @@ -498,6 +510,7 @@ int cpm1_gpiochip_add16(struct device *dev) gc->ngpio = 16; gc->direction_input = cpm1_gpio16_dir_in; gc->direction_output = cpm1_gpio16_dir_out; + gc->get_direction = cpm1_gpio16_get_direction; gc->get = cpm1_gpio16_get; gc->set = cpm1_gpio16_set; gc->to_irq = cpm1_gpio16_to_irq; @@ -604,6 +617,18 @@ static int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int cpm1_gpio32_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(gc); + struct cpm_ioport32b __iomem *iop = cpm1_gc->regs; + u32 pin_mask = 1 << (31 - gpio); + + if (in_be32(&iop->dir) & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + int cpm1_gpiochip_add32(struct device *dev) { struct device_node *np = dev->of_node; @@ -621,6 +646,7 @@ int cpm1_gpiochip_add32(struct device *dev) gc->ngpio = 32; gc->direction_input = cpm1_gpio32_dir_in; gc->direction_output = cpm1_gpio32_dir_out; + gc->get_direction = cpm1_gpio32_get_direction; gc->get = cpm1_gpio32_get; gc->set = cpm1_gpio32_set; gc->parent = dev; From b7474f4432dd9559ada03b01a8cd3472cd02c61d Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Mon, 8 Jun 2026 05:00:00 +0000 Subject: [PATCH 0997/3297] bpf: Fix NULL pointer dereference in bpf_task_from_vpid() [ Upstream commit 50dff00615522f3ec03449680ca23beb4cfc549c ] bpf_task_from_vpid() looks up a task in the pid namespace of the current task, via find_task_by_vpid(): find_task_by_vpid(vpid) find_task_by_pid_ns(vpid, task_active_pid_ns(current)) find_pid_ns(nr, ns) -> idr_find(&ns->idr, nr) cgroup_skb programs run in softirq, which may interrupt a task that is itself in do_exit(). Once that task has passed exit_notify() -> release_task() -> __unhash_process(), its thread_pid is cleared, so task_active_pid_ns(current) returns NULL and find_pid_ns() dereferences &NULL->idr: BUG: kernel NULL pointer dereference, address: 0000000000000050 RIP: 0010:idr_find+0x11/0x30 lib/idr.c:176 Call Trace: find_pid_ns kernel/pid.c:370 [inline] find_task_by_pid_ns+0x3b/0xe0 kernel/pid.c:485 bpf_task_from_vpid+0x5b/0x200 kernel/bpf/helpers.c:2916 bpf_prog_run_array_cg+0x17e/0x530 kernel/bpf/cgroup.c:81 __cgroup_bpf_run_filter_skb+0x12b/0x250 kernel/bpf/cgroup.c:1612 sk_filter_trim_cap+0x1dc/0x4c0 net/core/filter.c:148 tcp_v4_rcv+0x18d1/0x2200 net/ipv4/tcp_ipv4.c:2223 do_exit+0xa63/0x1270 kernel/exit.c:1010 get_signal+0x141c/0x1530 kernel/signal.c:3037 Bail out when current has no pid namespace. Fixes: 675c3596ff32 ("bpf: Add bpf_task_from_vpid() kfunc") Signed-off-by: Sechang Lim Acked-by: Leon Hwang Link: https://lore.kernel.org/bpf/20260608050001.2545245-1-rhkrqnwk98@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- kernel/bpf/helpers.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index a62cbdf5a3cde..7357ddc1fc928 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2672,11 +2672,13 @@ __bpf_kfunc struct task_struct *bpf_task_from_vpid(s32 vpid) { struct task_struct *p; - rcu_read_lock(); + guard(rcu)(); + if (!task_active_pid_ns(current)) + return NULL; + p = find_task_by_vpid(vpid); if (p) p = bpf_task_acquire(p); - rcu_read_unlock(); return p; } From 6cb635ad1006dacb8d616e66703105c3f002440f Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 3 Jun 2026 12:31:08 -0400 Subject: [PATCH 0998/3297] ACPI: IPMI: Fix message kref handling on dead device [ Upstream commit 63320db6a5d84ec3fed8b3d36ba5244d07ddd108 ] acpi_ipmi_space_handler() takes an extra reference on tx_msg before checking whether the selected IPMI device is dead. The reference belongs to the tx_msg_list entry and is normally dropped by ipmi_cancel_tx_msg() or ipmi_flush_tx_msg() after the message is removed from the list. On the dead-device path, the message has not been queued yet, but the error path still calls ipmi_msg_release() directly. That bypasses kref_put() and frees tx_msg while the queued-message reference is still recorded in the kref count. Take the queued-message reference only after the dead-device check succeeds, immediately before adding tx_msg to the list. Fixes: 7b9844772237 ("ACPI / IPMI: Add reference counting for ACPI IPMI transfers") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260603163108.2149359-1-dbgh9129@gmail.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpi_ipmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index 5fba4dab5d088..c672abc3a6dbb 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -550,7 +550,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, return AE_TYPE; } - acpi_ipmi_msg_get(tx_msg); mutex_lock(&driver_data.ipmi_lock); /* Do not add a tx_msg that can not be flushed. */ if (ipmi_device->dead) { @@ -558,6 +557,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, ipmi_msg_release(tx_msg); return AE_NOT_EXIST; } + acpi_ipmi_msg_get(tx_msg); spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); From dde04550fd6ff7cfee5655b9030e2d869673470e Mon Sep 17 00:00:00 2001 From: Pengjie Zhang Date: Wed, 3 Jun 2026 13:56:35 +0800 Subject: [PATCH 0999/3297] cpufreq: Documentation: fix conservative governor freq_step description [ Upstream commit 03120e1425262c7d11f93362a88e579a1720390b ] The conservative governor documentation incorrectly states that setting freq_step to 0 will use the default 5% frequency step. In reality, since at least commit 8e677ce83bf4 ("[CPUFREQ] conservative: fixup governor to function more like ondemand logic"), freq_step=0 has always caused the governor to skip frequency updates entirely. Correct the documentation to reflect the actual behavior: freq_step=0 disables frequency changes by the governor entirely. Fixes: 2a0e49279850 ("cpufreq: User/admin documentation update and consolidation") Signed-off-by: Pengjie Zhang Reviewed-by: Zhongqiu Han [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20260603055635.1549943-1-zhangpengjie2@huawei.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- Documentation/admin-guide/pm/cpufreq.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index 33122fbe9f6a0..9909c7d5c6172 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -586,8 +586,8 @@ This governor exposes the following tunables: 100 (5 by default). This is how much the frequency is allowed to change in one go. Setting - it to 0 will cause the default frequency step (5 percent) to be used - and setting it to 100 effectively causes the governor to periodically + it to 0 disables frequency changes by the governor entirely and setting + it to 100 effectively causes the governor to periodically switch the frequency between the ``scaling_min_freq`` and ``scaling_max_freq`` policy limits. From 920dcf1cb8dae867fe464946e0ce8c81f4003fb2 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 5 Jun 2026 18:52:06 +0000 Subject: [PATCH 1000/3297] thermal: testing: reject missing command arguments [ Upstream commit ef3e98b0aa4b348f44065d7130251273c83bd204 ] The thermal testing debugfs command parser splits commands at ':' and passes the right-hand side to the command implementation. Commands such as deltz, tzaddtrip, tzreg, and tzunreg require a zone id, but writing one of those command names without ':' leaves the argument pointer NULL. The command implementations parse the id with sscanf(arg, "%d", ...), so the missing-argument form dereferences a NULL pointer from the debugfs write path. Reject missing arguments in tt_command_exec() before calling handlers that require an id. Fixes: f6a034f2df42 ("thermal: Introduce a debugfs-based testing facility") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Link: https://patch.msgid.link/20260605185212.2491144-1-sam.moelius@trailofbits.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/testing/command.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c index 1159ecea57e7c..fbf7ab9729b5a 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -116,18 +116,30 @@ static int tt_command_exec(int index, const char *arg) break; case TT_CMD_DELTZ: + if (!arg || !*arg) + return -EINVAL; + ret = tt_del_tz(arg); break; case TT_CMD_TZADDTRIP: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_add_trip(arg); break; case TT_CMD_TZREG: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_reg(arg); break; case TT_CMD_TZUNREG: + if (!arg || !*arg) + return -EINVAL; + ret = tt_zone_unreg(arg); break; From c213b71a2d41635e386bd6432256533b817456e6 Mon Sep 17 00:00:00 2001 From: Mark Harmstone Date: Wed, 22 Apr 2026 15:03:35 +0100 Subject: [PATCH 1001/3297] btrfs: don't force DIO writes to be serialized [ Upstream commit 14f9161e872072075284b8afa4c3f929e199a0f6 ] Before btrfs switched to the new mount API in 2023, we were setting SB_NOSEC in btrfs_mount_root(). This flag tells the VFS that the filesystem may have files which don't have security xattrs, enabling it to do some optimizations. Unfortunately this was missed in the transition, meaning that IS_NOSEC will always return false for a btrfs inode. This means that btrfs_direct_write() calls will always get the inode lock exclusively, meaning that DIO writes to the same file will be serialized. On my machine, this one-line change results in a ~59% improvement in DIO throughput: Before patch: test: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=io_uring, iodepth=64 ... fio-3.39 Starting 32 processes test: Laying out IO file (1 file / 1024MiB) Jobs: 32 (f=32): [w(32)][100.0%][w=764MiB/s][w=195k IOPS][eta 00m:00s] test: (groupid=0, jobs=32): err= 0: pid=586: Wed Apr 22 13:03:04 2026 write: IOPS=202k, BW=787MiB/s (826MB/s)(46.1GiB/60012msec); 0 zone resets bw ( KiB/s): min=498714, max=1199892, per=100.00%, avg=806659.03, stdev=4229.94, samples=3808 iops : min=124677, max=299971, avg=201661.82, stdev=1057.49, samples=3808 cpu : usr=0.32%, sys=1.27%, ctx=8329204, majf=0, minf=1163 IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=100.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0% issued rwts: total=0,12094328,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=64 Run status group 0 (all jobs): WRITE: bw=787MiB/s (826MB/s), 787MiB/s-787MiB/s (826MB/s-826MB/s), io=46.1GiB (49.5GB), run=60012-60012msec After patch: test: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=io_uring, iodepth=64 ... fio-3.39 Starting 32 processes test: Laying out IO file (1 file / 1024MiB) Jobs: 32 (f=32): [w(32)][100.0%][w=1255MiB/s][w=321k IOPS][eta 00m:00s] test: (groupid=0, jobs=32): err= 0: pid=572: Wed Apr 22 13:13:46 2026 write: IOPS=320k, BW=1250MiB/s (1311MB/s)(73.3GiB/60003msec); 0 zone resets bw ( MiB/s): min= 619, max= 2289, per=100.00%, avg=1251.28, stdev= 9.64, samples=3808 iops : min=158538, max=586025, avg=320320.80, stdev=2468.97, samples=3808 cpu : usr=0.35%, sys=11.50%, ctx=1584847, majf=0, minf=1160 IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=100.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0% issued rwts: total=0,19203309,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=64 Run status group 0 (all jobs): WRITE: bw=1250MiB/s (1311MB/s), 1250MiB/s-1250MiB/s (1311MB/s-1311MB/s), io=73.3GiB (78.7GB), run=60003-60003msec The script to reproduce that: #!/bin/bash mkfs.btrfs -f /dev/nvme0n1 mount /dev/nvme0n1 /mnt/test mkdir /mnt/test/nocow chattr +C /mnt/test/nocow fio /root/test.fio # cat /root/test.fio [global] rw=randwrite ioengine=io_uring iodepth=64 size=1g direct=1 startdelay=20 force_async=4 ramp_time=5 runtime=60 group_reporting=1 numjobs=32 time_based disk_util=0 clat_percentiles=0 disable_lat=1 disable_clat=1 disable_slat=1 filename=/mnt/test/nocow/fiofile [test] name=test bs=4k stonewall This was on a VM with 8 cores and 8GB of RAM, with a real NVMe exposed through PCI passthrough. The figures for XFS and ext4 in comparison are both about ~3GB/s. Fixes: ad21f15b0f79 ("btrfs: switch to the new mount API") Signed-off-by: Mark Harmstone Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index f1bfe97beacf1..35903afdd1e5b 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1874,6 +1874,7 @@ static int btrfs_get_tree_super(struct fs_context *fc) fs_info->fs_devices = fs_devices; mutex_unlock(&uuid_mutex); + fc->sb_flags |= SB_NOSEC; sb = sget_fc(fc, btrfs_fc_test_super, set_anon_super_fc); if (IS_ERR(sb)) { From f5657d399b7ef9f4adc5e427eae413fe45ccc5cc Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:40 -0300 Subject: [PATCH 1002/3297] IB/mlx5: Don't take the rereg_mr fallback without a new translation [ Upstream commit 55d339d200c908de83a408d023cfb7be779b0dd7 ] Jumping to mlx5_ib_reg_user_mr() without IB_MR_REREG_TRANS set will use garbage values for start, length, and iova. Recovering the original mr parameters for ODP and DMABUF to properly recreate it is too hard in this flow, so just fail it. Fixes: ef3642c4f54d ("RDMA/mlx5: Fix error unwinds for rereg_mr") Link: https://patch.msgid.link/r/1-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 47f720c0be59a..374e40758359d 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1918,7 +1918,7 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, } /* DM or ODP MR's don't have a normal umem so we can't re-use it */ if (!mr->umem || is_odp_mr(mr) || is_dmabuf_mr(mr)) - goto recreate; + return ERR_PTR(-EOPNOTSUPP); /* * Only one active MR can refer to a umem at one time, revoke From d4f84bfa089fe71f775d25beb29303e844494c25 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:42 -0300 Subject: [PATCH 1003/3297] IB/mlx5: Properly support implicit ODP rereg_mr [ Upstream commit ee7a8335069150c3f1893a697ab30bbeca00d796 ] Due to all the child mkeys in the implicit ODP configuration we cannot change anything in place for the parent mkey. Instead the whole thing needs to be rebuilt if any change is requested. If the user does not specify a translation then force the implicit values which will then fall through the logic into mlx5_ib_reg_user_mr() to allocate a completely new MR. Since implicit children were also touching the mr->pd, this removes another case where the access was racy. Fixes: ef3642c4f54d ("RDMA/mlx5: Fix error unwinds for rereg_mr") Link: https://sashiko.dev/#/patchset/20260427-security-bug-fixes-v3-0-4621fa52de0e%40nvidia.com?part=4 Link: https://patch.msgid.link/r/3-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 374e40758359d..c581ac46e0c99 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1904,6 +1904,21 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, if (!(flags & IB_MR_REREG_PD)) new_pd = ib_mr->pd; + if (mr->is_odp_implicit && !(flags & IB_MR_REREG_TRANS)) { + if (!(new_access_flags & IB_ACCESS_ON_DEMAND)) + return ERR_PTR(-EOPNOTSUPP); + + /* + * Due to all the child mkeys we cannot actually change an + * implicit MR in place. If the user did not specify a new + * translation then force the fixed implicit MR values. + */ + start = 0; + iova = 0; + length = U64_MAX; + flags |= IB_MR_REREG_TRANS; + } + if (!(flags & IB_MR_REREG_TRANS)) { struct ib_umem *umem; From 8119fe468b01f9854f2d01e4e32da588ad7346e5 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:44 -0300 Subject: [PATCH 1004/3297] IB/mlx5: Remove unused mkc bits in mlx5r_umr_update_mr_page_shift() [ Upstream commit 4910483bd7ee2b40d0cc8ebd537fa33c95562029 ] The HW only processes mkc fields selected by mkey_mask. pd, qpn and mkey_7_0 are never selected so they can be left as zero. This removes a racy read of mr->pd. Fixes: e73242aa14d2 ("RDMA/mlx5: Optimize DMABUF mkey page size") Link: https://patch.msgid.link/r/5-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/umr.c | 16 +++------------- drivers/infiniband/hw/mlx5/umr.h | 3 +-- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 71f331ce6d894..3e2624cfd17f7 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -937,8 +937,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, * pinned and the HW can switch from 4K to huge-page alignment). */ int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, - unsigned int page_shift, - bool dd) + unsigned int page_shift) { struct mlx5_ib_dev *dev = mr_to_mdev(mr); struct mlx5r_umr_wqe wqe = {}; @@ -953,16 +952,8 @@ int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, /* Fill mkey segment with the new page size, keep the rest unchanged */ MLX5_SET(mkc, &wqe.mkey_seg, log_page_size, page_shift); - if (dd) - MLX5_SET(mkc, &wqe.mkey_seg, pd, dev->ddr.pdn); - else - MLX5_SET(mkc, &wqe.mkey_seg, pd, to_mpd(mr->ibmr.pd)->pdn); - MLX5_SET64(mkc, &wqe.mkey_seg, start_addr, mr->ibmr.iova); MLX5_SET64(mkc, &wqe.mkey_seg, len, mr->ibmr.length); - MLX5_SET(mkc, &wqe.mkey_seg, qpn, 0xffffff); - MLX5_SET(mkc, &wqe.mkey_seg, mkey_7_0, - mlx5_mkey_variant(mr->mmkey.key)); err = mlx5r_umr_post_send_wait(dev, mr->mmkey.key, &wqe, false); if (!err) @@ -1049,7 +1040,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, * non-present. */ if (*nblocks) { - err = mlx5r_umr_update_mr_page_shift(mr, max_page_shift, dd); + err = mlx5r_umr_update_mr_page_shift(mr, max_page_shift); if (err) { mr->page_shift = old_page_shift; return err; @@ -1114,8 +1105,7 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, goto err; } - err = mlx5r_umr_update_mr_page_shift(mr, mr->page_shift, - mr->data_direct); + err = mlx5r_umr_update_mr_page_shift(mr, mr->page_shift); if (err) goto err; err = _mlx5r_dmabuf_umr_update_pas(mr, xlt_flags, 0, zapped_blocks, diff --git a/drivers/infiniband/hw/mlx5/umr.h b/drivers/infiniband/hw/mlx5/umr.h index e9361f0140e7b..2797dda2b2bef 100644 --- a/drivers/infiniband/hw/mlx5/umr.h +++ b/drivers/infiniband/hw/mlx5/umr.h @@ -105,8 +105,7 @@ int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags); int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int page_shift, int flags); int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, - unsigned int page_shift, - bool dd); + unsigned int page_shift); int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, unsigned int page_shift); From fd284b12810e4fb7eea8334c6e38af78e3d1237d Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:45 -0300 Subject: [PATCH 1005/3297] IB/mlx5: Pull the pdn out of the depths of the umr machinery [ Upstream commit f387a9f06ea4f05e607a91b161963c0b19436652 ] Instead of getting the pdn deep inside the umr code, pass it in from the top. to_mpd(mr->ibmr.pd)->pdn is not safe due to the rereg races, so all the call sites need some revision to obtain the pdn in a safe way. Mark them with mlx5_mr_pdn(); following patches will go through and remove these. Cases where the XLT flags are known and do not require the PDN can pass 0, such as for mlx5_ib_dmabuf_invalidate_cb(). Also extract the DMABUF data_direct special case from inside the UMR code and into the only place that needs it, pagefault_dmabuf_mr(). The actual mr was created directly without using the UMR flow. Ultimately this will be moved into mlx5_ib_init_dmabuf_mr(). Link: https://patch.msgid.link/r/6-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Assisted-by: Codex:gpt-5-5 Signed-off-by: Jason Gunthorpe Stable-dep-of: 8e0a02a989c1 ("IB/mlx5: Don't mangle the mr->pd inside the rereg callback") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 9 ++++ drivers/infiniband/hw/mlx5/mr.c | 8 +-- drivers/infiniband/hw/mlx5/odp.c | 12 +++-- drivers/infiniband/hw/mlx5/umr.c | 75 ++++++++++++++-------------- drivers/infiniband/hw/mlx5/umr.h | 6 +-- 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index fbccb0362590b..ba55e6db3bca7 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -331,6 +331,10 @@ struct mlx5_ib_flow_db { #define MLX5_IB_QPT_DCT IB_QPT_RESERVED4 #define MLX5_IB_WR_UMR IB_WR_RESERVED1 +/* + * A valid pdn is required when flags include MLX5_IB_UPD_XLT_ENABLE, + * MLX5_IB_UPD_XLT_PD or MLX5_IB_UPD_XLT_ACCESS. + */ #define MLX5_IB_UPD_XLT_ZAP BIT(0) #define MLX5_IB_UPD_XLT_ENABLE BIT(1) #define MLX5_IB_UPD_XLT_ATOMIC BIT(2) @@ -1281,6 +1285,11 @@ static inline struct mlx5_ib_pd *to_mpd(struct ib_pd *ibpd) return container_of(ibpd, struct mlx5_ib_pd, ibpd); } +static inline u32 mlx5_mr_pdn(struct mlx5_ib_mr *mr) +{ + return to_mpd(mr->ibmr.pd)->pdn; +} + static inline struct mlx5_ib_srq *to_msrq(struct ib_srq *ibsrq) { return container_of(ibsrq, struct mlx5_ib_srq, ibsrq); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index c581ac46e0c99..3159f6692a4a5 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1505,7 +1505,8 @@ static struct ib_mr *create_real_mr(struct ib_pd *pd, struct ib_umem *umem, * configured properly but left disabled. It is safe to go ahead * and configure it again via UMR while enabling it. */ - err = mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ENABLE); + err = mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ENABLE, + to_mpd(pd)->pdn); if (err) { mlx5_ib_dereg_mr(&mr->ibmr, NULL); return ERR_PTR(err); @@ -1614,7 +1615,8 @@ static void mlx5_ib_dmabuf_invalidate_cb(struct dma_buf_attachment *attach) if (!umem_dmabuf->sgt || !mr) return; - mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ZAP); + /* MLX5_IB_UPD_XLT_ZAP does not change the pdn */ + mlx5r_umr_update_mr_pas(mr, MLX5_IB_UPD_XLT_ZAP, 0); ib_umem_dmabuf_unmap_pages(umem_dmabuf); } @@ -1858,7 +1860,7 @@ static int umr_rereg_pas(struct mlx5_ib_mr *mr, struct ib_pd *pd, mr->ibmr.length = new_umem->length; mr->page_shift = order_base_2(page_size); mr->umem = new_umem; - err = mlx5r_umr_update_mr_pas(mr, upd_flags); + err = mlx5r_umr_update_mr_pas(mr, upd_flags, mlx5_mr_pdn(mr)); if (err) { /* * The MR is revoked at this point so there is no issue to free diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 0e8ae85af5a62..47bb42576ad5d 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -836,12 +836,14 @@ static int pagefault_dmabuf_mr(struct mlx5_ib_mr *mr, size_t bcnt, u32 *bytes_mapped, u32 flags) { struct ib_umem_dmabuf *umem_dmabuf = to_ib_umem_dmabuf(mr->umem); + struct mlx5_ib_dev *dev = mr_to_mdev(mr); int access_mode = mr->data_direct ? MLX5_MKC_ACCESS_MODE_KSM : MLX5_MKC_ACCESS_MODE_MTT; unsigned int old_page_shift = mr->page_shift; unsigned int page_shift; unsigned long page_size; u32 xlt_flags = 0; + u32 pdn = 0; int err; if (flags & MLX5_PF_FLAGS_ENABLE) @@ -860,8 +862,12 @@ static int pagefault_dmabuf_mr(struct mlx5_ib_mr *mr, size_t bcnt, err = -EINVAL; } else { page_shift = order_base_2(page_size); + if (mr->data_direct) + pdn = dev->ddr.pdn; + else + pdn = mlx5_mr_pdn(mr); if (page_shift != mr->page_shift && mr->dmabuf_faulted) { - err = mlx5r_umr_dmabuf_update_pgsz(mr, xlt_flags, + err = mlx5r_umr_dmabuf_update_pgsz(mr, xlt_flags, pdn, page_shift); } else { mr->page_shift = page_shift; @@ -869,8 +875,8 @@ static int pagefault_dmabuf_mr(struct mlx5_ib_mr *mr, size_t bcnt, err = mlx5r_umr_update_data_direct_ksm_pas( mr, xlt_flags); else - err = mlx5r_umr_update_mr_pas(mr, - xlt_flags); + err = mlx5r_umr_update_mr_pas(mr, xlt_flags, + pdn); } } dma_resv_unlock(umem_dmabuf->attach->dmabuf->resv); diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 3e2624cfd17f7..1452cbb825ed6 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -603,11 +603,11 @@ mlx5r_umr_set_update_xlt_ctrl_seg(struct mlx5_wqe_umr_ctrl_seg *ctrl_seg, static void mlx5r_umr_set_update_xlt_mkey_seg(struct mlx5_ib_dev *dev, struct mlx5_mkey_seg *mkey_seg, - struct mlx5_ib_mr *mr, + struct mlx5_ib_mr *mr, u32 pdn, unsigned int page_shift) { mlx5r_umr_set_access_flags(dev, mkey_seg, mr->access_flags); - MLX5_SET(mkc, mkey_seg, pd, to_mpd(mr->ibmr.pd)->pdn); + MLX5_SET(mkc, mkey_seg, pd, pdn); MLX5_SET64(mkc, mkey_seg, start_addr, mr->ibmr.iova); MLX5_SET64(mkc, mkey_seg, len, mr->ibmr.length); MLX5_SET(mkc, mkey_seg, log_page_size, page_shift); @@ -670,23 +670,22 @@ static void mlx5r_umr_final_update_xlt(struct mlx5_ib_dev *dev, wqe->data_seg.byte_count = cpu_to_be32(sg->length); } -static void -_mlx5r_umr_init_wqe(struct mlx5_ib_mr *mr, struct mlx5r_umr_wqe *wqe, - struct ib_sge *sg, unsigned int flags, - unsigned int page_shift, bool dd) +static void _mlx5r_umr_init_wqe(struct mlx5_ib_mr *mr, + struct mlx5r_umr_wqe *wqe, struct ib_sge *sg, + unsigned int flags, u32 pdn, + unsigned int page_shift) { struct mlx5_ib_dev *dev = mr_to_mdev(mr); mlx5r_umr_set_update_xlt_ctrl_seg(&wqe->ctrl_seg, flags, sg); - mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe->mkey_seg, mr, page_shift); - if (dd) /* Use the data direct internal kernel PD */ - MLX5_SET(mkc, &wqe->mkey_seg, pd, dev->ddr.pdn); + mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe->mkey_seg, mr, pdn, + page_shift); mlx5r_umr_set_update_xlt_data_seg(&wqe->data_seg, sg); } -static int -_mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, bool dd, - size_t start_block, size_t nblocks) +static int _mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, + u32 pdn, bool dd, size_t start_block, + size_t nblocks) { size_t ent_size = dd ? sizeof(struct mlx5_ksm) : sizeof(struct mlx5_mtt); struct mlx5_ib_dev *dev = mr_to_mdev(mr); @@ -720,7 +719,7 @@ _mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, bool dd, orig_sg_length = sg.length; - _mlx5r_umr_init_wqe(mr, &wqe, &sg, flags, mr->page_shift, dd); + _mlx5r_umr_init_wqe(mr, &wqe, &sg, flags, pdn, mr->page_shift); /* Set initial translation offset to start_block */ offset = (u64)start_block * ent_size; @@ -811,7 +810,8 @@ int mlx5r_umr_update_data_direct_ksm_pas_range(struct mlx5_ib_mr *mr, !(flags & MLX5_IB_UPD_XLT_KEEP_PGSZ))) return -EINVAL; - return _mlx5r_umr_update_mr_pas(mr, flags, true, start_block, nblocks); + return _mlx5r_umr_update_mr_pas(mr, flags, mr_to_mdev(mr)->ddr.pdn, + true, start_block, nblocks); } int mlx5r_umr_update_data_direct_ksm_pas(struct mlx5_ib_mr *mr, @@ -821,12 +821,13 @@ int mlx5r_umr_update_data_direct_ksm_pas(struct mlx5_ib_mr *mr, } int mlx5r_umr_update_mr_pas_range(struct mlx5_ib_mr *mr, unsigned int flags, - size_t start_block, size_t nblocks) + u32 pdn, size_t start_block, size_t nblocks) { if (WARN_ON(mr->umem->is_odp)) return -EINVAL; - return _mlx5r_umr_update_mr_pas(mr, flags, false, start_block, nblocks); + return _mlx5r_umr_update_mr_pas(mr, flags, pdn, false, start_block, + nblocks); } /* @@ -834,9 +835,9 @@ int mlx5r_umr_update_mr_pas_range(struct mlx5_ib_mr *mr, unsigned int flags, * Dmabuf MR is handled in a similar way, except that the MLX5_IB_UPD_XLT_ZAP * flag may be used. */ -int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags) +int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, u32 pdn) { - return mlx5r_umr_update_mr_pas_range(mr, flags, 0, 0); + return mlx5r_umr_update_mr_pas_range(mr, flags, pdn, 0, 0); } static bool umr_can_use_indirect_mkey(struct mlx5_ib_dev *dev) @@ -861,6 +862,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, size_t orig_sg_length; size_t pages_iter; struct ib_sge sg; + u32 pdn = mlx5_mr_pdn(mr); int err = 0; void *xlt; @@ -895,7 +897,8 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, } mlx5r_umr_set_update_xlt_ctrl_seg(&wqe.ctrl_seg, flags, &sg); - mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe.mkey_seg, mr, page_shift); + mlx5r_umr_set_update_xlt_mkey_seg(dev, &wqe.mkey_seg, mr, pdn, + page_shift); mlx5r_umr_set_update_xlt_data_seg(&wqe.data_seg, &sg); for (pages_mapped = 0; @@ -962,17 +965,18 @@ int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, return err; } -static inline int -_mlx5r_dmabuf_umr_update_pas(struct mlx5_ib_mr *mr, unsigned int flags, - size_t start_block, size_t nblocks, bool dd) +static inline int _mlx5r_dmabuf_umr_update_pas(struct mlx5_ib_mr *mr, + unsigned int flags, u32 pdn, + size_t start_block, + size_t nblocks, bool dd) { if (dd) return mlx5r_umr_update_data_direct_ksm_pas_range(mr, flags, start_block, nblocks); else - return mlx5r_umr_update_mr_pas_range(mr, flags, start_block, - nblocks); + return mlx5r_umr_update_mr_pas_range(mr, flags, pdn, + start_block, nblocks); } /** @@ -986,11 +990,9 @@ _mlx5r_dmabuf_umr_update_pas(struct mlx5_ib_mr *mr, unsigned int flags, * Return: On success, returns the number of entries that were zapped. * On error, returns a negative error code. */ -static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, - unsigned int flags, - unsigned int page_shift, - size_t *nblocks, - bool dd) +static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, unsigned int flags, + unsigned int page_shift, size_t *nblocks, + u32 pdn, bool dd) { unsigned int old_page_shift = mr->page_shift; struct mlx5_ib_dev *dev = mr_to_mdev(mr); @@ -1030,7 +1032,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, */ if (*nblocks) mr->page_shift = max_page_shift; - err = _mlx5r_dmabuf_umr_update_pas(mr, flags, 0, *nblocks, dd); + err = _mlx5r_dmabuf_umr_update_pas(mr, flags, pdn, 0, *nblocks, dd); if (err) { mr->page_shift = old_page_shift; return err; @@ -1055,6 +1057,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, * entries accordingly * @mr: The memory region to update * @xlt_flags: Translation table update flags + * @pdn: Protection domain number * @page_shift: The new (optimized) page shift to use * * This function updates the page size and mkey translation entries for a DMABUF @@ -1074,7 +1077,7 @@ static int _mlx5r_umr_zap_mkey(struct mlx5_ib_mr *mr, * * Returns 0 on success or a negative error code on failure. */ -int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, +int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, u32 pdn, unsigned int page_shift) { unsigned int old_page_shift = mr->page_shift; @@ -1083,7 +1086,7 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, int err; err = _mlx5r_umr_zap_mkey(mr, xlt_flags, page_shift, &zapped_blocks, - mr->data_direct); + pdn, mr->data_direct); if (err) return err; @@ -1096,10 +1099,8 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, * the page size in the mkey yet. */ err = _mlx5r_dmabuf_umr_update_pas( - mr, - xlt_flags | MLX5_IB_UPD_XLT_KEEP_PGSZ, - zapped_blocks, - total_blocks - zapped_blocks, + mr, xlt_flags | MLX5_IB_UPD_XLT_KEEP_PGSZ, pdn, + zapped_blocks, total_blocks - zapped_blocks, mr->data_direct); if (err) goto err; @@ -1108,7 +1109,7 @@ int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, err = mlx5r_umr_update_mr_page_shift(mr, mr->page_shift); if (err) goto err; - err = _mlx5r_dmabuf_umr_update_pas(mr, xlt_flags, 0, zapped_blocks, + err = _mlx5r_dmabuf_umr_update_pas(mr, xlt_flags, pdn, 0, zapped_blocks, mr->data_direct); if (err) goto err; diff --git a/drivers/infiniband/hw/mlx5/umr.h b/drivers/infiniband/hw/mlx5/umr.h index 2797dda2b2bef..1db1662378e77 100644 --- a/drivers/infiniband/hw/mlx5/umr.h +++ b/drivers/infiniband/hw/mlx5/umr.h @@ -100,13 +100,13 @@ int mlx5r_umr_update_data_direct_ksm_pas_range(struct mlx5_ib_mr *mr, size_t nblocks); int mlx5r_umr_update_data_direct_ksm_pas(struct mlx5_ib_mr *mr, unsigned int flags); int mlx5r_umr_update_mr_pas_range(struct mlx5_ib_mr *mr, unsigned int flags, - size_t start_block, size_t nblocks); -int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags); + u32 pdn, size_t start_block, size_t nblocks); +int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, u32 pdn); int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int page_shift, int flags); int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr, unsigned int page_shift); -int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, +int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, u32 pdn, unsigned int page_shift); #endif /* _MLX5_IB_UMR_H */ From e123f0ab02d054ad15e4777d5f924fbf9b497a9c Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 3 Jun 2026 22:27:46 -0300 Subject: [PATCH 1006/3297] IB/mlx5: Don't mangle the mr->pd inside the rereg callback [ Upstream commit 8e0a02a989c156ce17f06a645d5f075277e06b95 ] The rereg protocol expects the core code to change mr->pd and synchronize that change with the atomics and syncs. The driver should not touch it. mlx5 needed to update it in umr_rereg_pas() because mlx5r_umr_update_mr_pas() required the updated mr->pd to build the UMR. Simply switch mlx5r_umr_update_mr_pas() to use the pdn directly from the new pd and remove the mr->pd update. Fixes: 56e11d628c5d ("IB/mlx5: Added support for re-registration of MRs") Link: https://patch.msgid.link/r/7-v1-29ebd2c229b5+fd5-ib_mr_pd_jgg@nvidia.com Assisted-by: Codex:gpt-5-5 Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/mr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 3159f6692a4a5..bd5a7d1ad14c3 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1847,10 +1847,8 @@ static int umr_rereg_pas(struct mlx5_ib_mr *mr, struct ib_pd *pd, if (err) return err; - if (flags & IB_MR_REREG_PD) { - mr->ibmr.pd = pd; + if (flags & IB_MR_REREG_PD) upd_flags |= MLX5_IB_UPD_XLT_PD; - } if (flags & IB_MR_REREG_ACCESS) { mr->access_flags = access_flags; upd_flags |= MLX5_IB_UPD_XLT_ACCESS; @@ -1860,7 +1858,7 @@ static int umr_rereg_pas(struct mlx5_ib_mr *mr, struct ib_pd *pd, mr->ibmr.length = new_umem->length; mr->page_shift = order_base_2(page_size); mr->umem = new_umem; - err = mlx5r_umr_update_mr_pas(mr, upd_flags, mlx5_mr_pdn(mr)); + err = mlx5r_umr_update_mr_pas(mr, upd_flags, to_mpd(pd)->pdn); if (err) { /* * The MR is revoked at this point so there is no issue to free From e8d89baf921703c281d1181e1e562db518357851 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 29 May 2026 23:31:06 +0800 Subject: [PATCH 1007/3297] spi: ep93xx: fix double-free of zeropage on DMA setup failure [ Upstream commit 7886054b06f762f62054957a8f6de0f14e6b7541 ] If DMA setup fails after allocating the zeropage, the error path frees the page but leaves espi->zeropage dangling. A subsequent call to ep93xx_spi_release_dma() sees the non-NULL pointer and frees the page again. Clear the pointer after freeing in the error path of ep93xx_spi_setup_dma(). Fixes: 626a96db1169 ("spi/ep93xx: add DMA support") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260529-ep93xx-v1-1-9185070ca1fc@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-ep93xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 52d2f8920cd75..a8ddd3d388de0 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -600,6 +600,7 @@ static int ep93xx_spi_setup_dma(struct device *dev, struct ep93xx_spi *espi) espi->dma_rx = NULL; fail_free_page: free_page((unsigned long)espi->zeropage); + espi->zeropage = NULL; return ret; } From 1279bdab5fa1f28c168e54215db506e87c6fac1e Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Thu, 28 May 2026 16:21:09 +0800 Subject: [PATCH 1008/3297] ASoC: amd: acp-sdw-legacy: Bound DAI link iteration [ Upstream commit d49ecdf327cc91062d2f80996a163cf65fda1e60 ] create_sdw_dailinks() walks soc_dais until it finds an entry with initialised cleared, but soc_dais is allocated with exactly num_ends entries. If all entries are initialised, the loop reads past the end of the array. This was reported by KASAN: BUG: KASAN: slab-out-of-bounds in mc_probe+0x26b3/0x2774 [snd_acp_sdw_legacy_mach] Read of size 1 Pass the allocated entry count to create_sdw_dailinks() and stop before reading past the array. Fixes: 2981d9b0789c ("ASoC: amd: acp: add soundwire machine driver for legacy stack") Signed-off-by: Aaron Ma Link: https://patch.msgid.link/20260528082110.915549-1-aaron.ma@canonical.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index e87d9e9991e1c..ac955a9b51dc3 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -287,13 +287,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, static int create_sdw_dailinks(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, int *be_id, - struct asoc_sdw_dailink *soc_dais, + struct asoc_sdw_dailink *soc_dais, int num_dais, struct snd_soc_codec_conf **codec_conf) { struct device *dev = card->dev; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private; struct snd_soc_dai_link_component *sdw_platform_component; + int i; int ret; sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), @@ -313,7 +314,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card, } /* generate DAI links by each sdw link */ - while (soc_dais->initialised) { + for (i = 0; i < num_dais && soc_dais->initialised; i++) { int current_be_id = 0; ret = create_sdw_dailink(card, soc_dais, dai_links, @@ -438,7 +439,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) /* SDW */ if (sdw_be_num) { ret = create_sdw_dailinks(card, &dai_links, &be_id, - soc_dais, &codec_conf); + soc_dais, num_ends, &codec_conf); if (ret) return ret; } From 9e82497138abea7d5c6e65015663d907fbcbf6b4 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Thu, 28 May 2026 16:21:10 +0800 Subject: [PATCH 1009/3297] ASoC: amd: acp-sdw-sof: Bound DAI link iteration [ Upstream commit 4d992e63f52d58f52b724606c60ae7b37a1c582f ] create_sdw_dailinks() walks sof_dais until it finds an entry with initialised cleared, but sof_dais is allocated with exactly num_ends entries. If all entries are initialised, the loop reads past the end of the array. Pass the allocated entry count to create_sdw_dailinks() and stop before reading past the array. Fixes: 6d8348ddc56e ("ASoC: amd: acp: refactor SoundWire machine driver code") Signed-off-by: Aaron Ma Link: https://patch.msgid.link/20260528082110.915549-2-aaron.ma@canonical.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/acp/acp-sdw-sof-mach.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index d055582a3bf1a..14d6e31d50408 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -220,13 +220,14 @@ static int create_sdw_dailink(struct snd_soc_card *card, static int create_sdw_dailinks(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, int *be_id, - struct asoc_sdw_dailink *sof_dais, + struct asoc_sdw_dailink *sof_dais, int num_dais, struct snd_soc_codec_conf **codec_conf) { + int i; int ret; /* generate DAI links by each sdw link */ - while (sof_dais->initialised) { + for (i = 0; i < num_dais && sof_dais->initialised; i++) { int current_be_id = 0; ret = create_sdw_dailink(card, sof_dais, dai_links, @@ -326,7 +327,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) /* SDW */ if (sdw_be_num) { ret = create_sdw_dailinks(card, &dai_links, &be_id, - sof_dais, &codec_conf); + sof_dais, num_ends, &codec_conf); if (ret) return ret; } From f25d6e4ec4c257030592bd671f113cf9584c52f0 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 29 May 2026 15:09:06 +0000 Subject: [PATCH 1010/3297] firmware_loader: Fix recursive lock in device_cache_fw_images() [ Upstream commit d3ec78f8f8d48a04a9fac38d47275c34645e5103 ] A recursive locking deadlock can occur in the firmware loader's power management notification handler. During system suspend or hibernation preparation, fw_pm_notify() calls device_cache_fw_images(). This function acquires fw_lock to set the firmware cache state to FW_LOADER_START_CACHE and then iterates over all devices using dpm_for_each_dev() while still holding the lock. For each device, dev_cache_fw_image() schedules asynchronous work to cache the firmware. If memory allocation for the async work entry fails (e.g., in out-of-memory conditions), async_schedule_node_domain() falls back to executing the work function synchronously in the current thread. The synchronous execution path (__async_dev_cache_fw_image() -> cache_firmware() -> request_firmware() -> assign_fw()) attempts to acquire fw_lock again. Since the current thread already holds fw_lock, this results in a recursive locking deadlock. Fix this by releasing fw_lock immediately after updating the cache state and before calling dpm_for_each_dev(). The lock is only needed to protect the state update. Concurrent firmware requests will correctly see the FW_LOADER_START_CACHE state and use the piggyback mechanism, which is independently protected by its own fwc->name_lock. Fixes: ac39b3ea73aa ("firmware loader: let caching firmware piggyback on loading firmware") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+e70e4c6f6eee43357ba7@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e70e4c6f6eee43357ba7 Link: https://syzkaller.appspot.com/ai_job?id=8b4af9fd-24af-423f-8acb-1159fd34c1a5 Signed-off-by: Dmitry Vyukov Link: https://patch.msgid.link/48b092a5-f49d-48a4-95f4-f65bebfc6bc3@mail.kernel.org Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- drivers/base/firmware_loader/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 6942c62fa59d1..d4552101cbea2 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -1512,9 +1512,10 @@ static void device_cache_fw_images(void) mutex_lock(&fw_lock); fwc->state = FW_LOADER_START_CACHE; - dpm_for_each_dev(NULL, dev_cache_fw_image); mutex_unlock(&fw_lock); + dpm_for_each_dev(NULL, dev_cache_fw_image); + /* wait for completion of caching firmware for all devices */ async_synchronize_full_domain(&fw_cache_domain); From 637ef4961470e04455102b34ac484a34d8eca0a4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 30 May 2026 03:48:34 -0400 Subject: [PATCH 1011/3297] configfs: fix lockless traversals of ->s_children [ Upstream commit 9b9e8bb81c41fd27e7b57a1c936fde140548535f ] Having the parent directory locked protects entries from removal by another thread, but it does *not* protect cursors from being moved around by lseek() - or freed, for that matter. Fixes: 6f6107640625 ("configfs: Introduce configfs_dirent_lock") Reviewed-by: Jan Kara Signed-off-by: Al Viro Signed-off-by: Sasha Levin --- fs/configfs/dir.c | 54 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 2898973f202a1..68203952766e6 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -235,15 +235,16 @@ static int configfs_dirent_exists(struct dentry *dentry) const unsigned char *new = dentry->d_name.name; struct configfs_dirent *sd; + spin_lock(&configfs_dirent_lock); list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { if (sd->s_element) { - const unsigned char *existing = configfs_get_name(sd); - if (strcmp(existing, new)) - continue; - else + if (strcmp(configfs_get_name(sd), new) == 0) { + spin_unlock(&configfs_dirent_lock); return -EEXIST; + } } } + spin_unlock(&configfs_dirent_lock); return 0; } @@ -569,11 +570,28 @@ static void configfs_detach_rollback(struct dentry *dentry) configfs_detach_rollback(sd->s_dentry); } +/* + * Find the next non-cursor. configfs_dirent_lock held by caller. + */ +static struct configfs_dirent *next_dirent(struct configfs_dirent *parent, + struct configfs_dirent *last) +{ + struct configfs_dirent *s; + + s = list_prepare_entry(last, &parent->s_children, s_sibling); + + list_for_each_entry_continue(s, &parent->s_children, s_sibling) { + if (s->s_element) + return s; + } + return NULL; +} + static void detach_attrs(struct config_item * item) { struct dentry * dentry = dget(item->ci_dentry); - struct configfs_dirent * parent_sd; - struct configfs_dirent * sd, * tmp; + struct configfs_dirent *parent_sd; + struct configfs_dirent *sd, *next; if (!dentry) return; @@ -582,15 +600,19 @@ static void detach_attrs(struct config_item * item) dentry->d_name.name); parent_sd = dentry->d_fsdata; - list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { - if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED)) + + spin_lock(&configfs_dirent_lock); + for (sd = next_dirent(parent_sd, NULL); sd; sd = next) { + next = next_dirent(parent_sd, sd); + if (!(sd->s_type & CONFIGFS_NOT_PINNED)) continue; - spin_lock(&configfs_dirent_lock); list_del_init(&sd->s_sibling); spin_unlock(&configfs_dirent_lock); configfs_drop_dentry(sd, dentry); configfs_put(sd); + spin_lock(&configfs_dirent_lock); } + spin_unlock(&configfs_dirent_lock); /** * Drop reference from dget() on entrance. @@ -649,18 +671,20 @@ static void detach_groups(struct config_group *group) struct dentry * dentry = dget(group->cg_item.ci_dentry); struct dentry *child; struct configfs_dirent *parent_sd; - struct configfs_dirent *sd, *tmp; + struct configfs_dirent *sd, *next; if (!dentry) return; parent_sd = dentry->d_fsdata; - list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { - if (!sd->s_element || - !(sd->s_type & CONFIGFS_USET_DEFAULT)) + spin_lock(&configfs_dirent_lock); + for (sd = next_dirent(parent_sd, NULL); sd; sd = next) { + next = next_dirent(parent_sd, sd); + if (!(sd->s_type & CONFIGFS_USET_DEFAULT)) continue; child = sd->s_dentry; + spin_unlock(&configfs_dirent_lock); inode_lock(d_inode(child)); @@ -672,7 +696,9 @@ static void detach_groups(struct config_group *group) d_delete(child); dput(child); + spin_lock(&configfs_dirent_lock); } + spin_unlock(&configfs_dirent_lock); /** * Drop reference from dget() on entrance. @@ -1124,6 +1150,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd, struct configfs_dirent *p; struct configfs_dirent *ret = NULL; + spin_lock(&configfs_dirent_lock); list_for_each_entry(p, &root_sd->s_children, s_sibling) { if (p->s_type & CONFIGFS_DIR && p->s_element == subsys_item) { @@ -1131,6 +1158,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd, break; } } + spin_unlock(&configfs_dirent_lock); return ret; } From 298821692d447c2f7b1bc9ef41cd88a31bf56436 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 1 Jun 2026 15:20:05 -0400 Subject: [PATCH 1012/3297] watchdog: unregister PM notifier on watchdog unregister [ Upstream commit a298c7302ee9584a7a1ac1e8acbede8d98ab51a4 ] watchdog_register_device() registers wdd->pm_nb when WDOG_NO_PING_ON_SUSPEND is set, but watchdog_unregister_device() does not remove it. This leaves an embedded notifier block on the PM notifier chain after the watchdog device has been unregistered. A later suspend/resume notification can then call watchdog_pm_notifier() with a stale watchdog_device pointer, or at minimum after wdd->wd_data has been cleared by watchdog_dev_unregister(). Unregister the PM notifier before tearing down the watchdog device. Fixes: 60bcd91aafd2 ("watchdog: introduce watchdog_dev_suspend/resume") Signed-off-by: Yuho Choi Link: https://lore.kernel.org/r/20260601192005.1970805-1-dbgh9129@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/watchdog_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 6152dba4b52cb..4c1a090ee4965 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -390,6 +390,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) unregister_reboot_notifier(&wdd->reboot_nb); + if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) + unregister_pm_notifier(&wdd->pm_nb); + watchdog_dev_unregister(wdd); ida_free(&watchdog_ida, wdd->id); } From 0404baeb9e43008ef8ace970a250666759312b69 Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Fri, 29 May 2026 18:25:45 +0530 Subject: [PATCH 1013/3297] pinctrl: qcom: Fix resolving register base address from device node [ Upstream commit 4e31ab47997540d0ff4c9de801741bed03c1ab3f ] Commit 56ffb63749f4 ("pinctrl: qcom: add multi TLMM region option parameter") added reg-names property based register reading. However multiple platforms are not using the reg-names as they have only single TLMM register region. Commit tried to handle this using the default_region module parameter, however this condition is unreachable as the error return precedes it by just checking if reg-names property exists or not, making it impossible to use tlmm-test for the SoCs (x1e80100) which don't have reg-names property in TLMM device. Fix this by moving the default_region check at the start of the tlmm_reg_base(). Fixes: 56ffb63749f4 ("pinctrl: qcom: add multi TLMM region option parameter") Signed-off-by: Sneh Mankad Reviewed-by: Dmitry Baryshkov Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/qcom/tlmm-test.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/pinctrl/qcom/tlmm-test.c b/drivers/pinctrl/qcom/tlmm-test.c index 7d7fff538755a..4ac96538a417f 100644 --- a/drivers/pinctrl/qcom/tlmm-test.c +++ b/drivers/pinctrl/qcom/tlmm-test.c @@ -581,6 +581,9 @@ static int tlmm_reg_base(struct device_node *tlmm, struct resource *res) int ret; int i; + if (!strcmp(tlmm_reg_name, "default_region")) + return of_address_to_resource(tlmm, 0, res); + count = of_property_count_strings(tlmm, "reg-names"); if (count <= 0) { pr_err("failed to find tlmm reg name\n"); @@ -597,18 +600,14 @@ static int tlmm_reg_base(struct device_node *tlmm, struct resource *res) return -EINVAL; } - if (!strcmp(tlmm_reg_name, "default_region")) { - ret = of_address_to_resource(tlmm, 0, res); - } else { - for (i = 0; i < count; i++) { - if (!strcmp(reg_names[i], tlmm_reg_name)) { - ret = of_address_to_resource(tlmm, i, res); - break; - } + for (i = 0; i < count; i++) { + if (!strcmp(reg_names[i], tlmm_reg_name)) { + ret = of_address_to_resource(tlmm, i, res); + break; } - if (i == count) - ret = -EINVAL; } + if (i == count) + ret = -EINVAL; kfree(reg_names); From c2bd9fdb448d6f367b2dcc6c274df18cb6ed49c3 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 5 Jun 2026 22:16:47 +1000 Subject: [PATCH 1014/3297] scsi: target: Fix hexadecimal CHAP_I handling [ Upstream commit 7e161211f1dd5288b4ea802b30e70ef919ebc3da ] A mutual CHAP handshake requires target processing of an initiator-sent CHAP_I identifier. The RFC 3720 specification states: 11.1.4. Challenge Handshake Authentication Protocol (CHAP) ... CHAP_A= CHAP_I= CHAP_C= ... Where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, Algorithm, Identifier, Challenge, and Response as defined in [RFC1994], N is a text string, A,A1,A2, and I are numbers CHAP_I parsing currently calls extract_param(), which returns the @identifier string (stripped of any 0b/0B or 0x/0X prefix) and a @type which indicates DECIMAL, HEX, or BASE64 encoding (based on any stripped prefix). Any HEX encoded CHAP_I string is further processed via: ret = kstrtoul(&identifier[2], 0, &id); This is incorrect for two reasons: * The @identifier string has already been stripped of the 0x/0X prefix, so skipping the first two bytes omits part of the number. * The kstrtoul() call specifies a base of 0, which will see &identifier[2] parsed as a decimal, unless a '0x' or (octal) '0' is erroneously present at that offset. Fix this by passing the (zero-offset) identifier string to kstrtoul() along with a base=16 parameter. Also add an explicit error handler for BASE64 encoding. Hex-encoded CHAP_I handling can be testing using the libiscsi EncodedI test linked below. Reported-by: Sashiko (gemini/gemini-3.1-pro-preview) Link: https://sashiko.dev/#/patchset/20260521151121.808477-1-hossu.alexandru%40gmail.com Link: https://github.com/sahlberg/libiscsi/pull/473 Fixes: 85db7391310b ("scsi: target: iscsi: Validate CHAP_R length before base64 decode") Signed-off-by: David Disseldorp Reviewed-by: Lee Duncan Reviewed-by: John Garry Link: https://patch.msgid.link/20260605122019.24146-2-ddiss@suse.de Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/iscsi/iscsi_target_auth.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 02a4c9aff98d5..41e4def46ede6 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -437,9 +437,11 @@ static int chap_server_compute_hash( } if (type == HEX) - ret = kstrtoul(&identifier[2], 0, &id); + ret = kstrtoul(identifier, 16, &id); + else if (type == DECIMAL) + ret = kstrtoul(identifier, 10, &id); else - ret = kstrtoul(identifier, 0, &id); + ret = -EINVAL; if (ret < 0) { pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret); From f775e7bda9a4f53d06315595935394f0d3540165 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sat, 30 May 2026 00:23:47 -0500 Subject: [PATCH 1015/3297] scsi: target: Remove tcm_loop target reset handling [ Upstream commit 7c08d430835a90414cd962e3a9602e5b002dee3b ] tcm_loop_target_reset is supposed to handle all the LUNs on a target but it's only doing a TMR_LUN_RESET so only that one LUN is handled. This will cause us to return early while IOs to other LUNs are still hung in lower layers. This just removes the target reset handler for the driver because LIO doesn't support target resets and for the common case where this is run from the scsi-ml error hamdler we have already tried an abort and lun reset so waiting again is most likely useless. Fixes: 1333eee56cdf ("scsi: target: tcm_loop: Drain commands in target_reset handler") Signed-off-by: Mike Christie Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260530052349.5134-1-michael.christie@oracle.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/loopback/tcm_loop.c | 64 ------------------------------ 1 file changed, 64 deletions(-) diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 156f934049f19..c6b64000983c0 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -269,69 +269,6 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; } -static bool tcm_loop_flush_work_iter(struct request *rq, void *data) -{ - struct scsi_cmnd *sc = blk_mq_rq_to_pdu(rq); - struct tcm_loop_cmd *tl_cmd = scsi_cmd_priv(sc); - struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; - - flush_work(&se_cmd->work); - return true; -} - -static int tcm_loop_target_reset(struct scsi_cmnd *sc) -{ - struct tcm_loop_hba *tl_hba; - struct tcm_loop_tpg *tl_tpg; - struct Scsi_Host *sh = sc->device->host; - int ret; - - /* - * Locate the tcm_loop_hba_t pointer - */ - tl_hba = *(struct tcm_loop_hba **)shost_priv(sh); - if (!tl_hba) { - pr_err("Unable to perform device reset without active I_T Nexus\n"); - return FAILED; - } - /* - * Locate the tl_tpg pointer from TargetID in sc->device->id - */ - tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; - if (!tl_tpg) - return FAILED; - - /* - * Issue a LUN_RESET to drain all commands that the target core - * knows about. This handles commands not yet marked CMD_T_COMPLETE. - */ - ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun, 0, TMR_LUN_RESET); - if (ret != TMR_FUNCTION_COMPLETE) - return FAILED; - - /* - * Flush any deferred target core completion work that may still be - * queued. Commands that already had CMD_T_COMPLETE set before the TMR - * are skipped by the TMR drain, but their async completion work - * (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done) - * may still be pending in target_completion_wq. - * - * The SCSI EH will reuse in-flight scsi_cmnd structures for recovery - * commands (e.g. TUR) immediately after this handler returns SUCCESS — - * if deferred work is still pending, the memset in queuecommand would - * zero the se_cmd while the work accesses it, leaking the LUN - * percpu_ref and hanging configfs unlink forever. - * - * Use blk_mq_tagset_busy_iter() to find all started requests and - * flush_work() on each — the same pattern used by mpi3mr, scsi_debug, - * and other SCSI drivers to drain outstanding commands during reset. - */ - blk_mq_tagset_busy_iter(&sh->tag_set, tcm_loop_flush_work_iter, NULL); - - tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; - return SUCCESS; -} - static const struct scsi_host_template tcm_loop_driver_template = { .show_info = tcm_loop_show_info, .proc_name = "tcm_loopback", @@ -340,7 +277,6 @@ static const struct scsi_host_template tcm_loop_driver_template = { .change_queue_depth = scsi_change_queue_depth, .eh_abort_handler = tcm_loop_abort_task, .eh_device_reset_handler = tcm_loop_device_reset, - .eh_target_reset_handler = tcm_loop_target_reset, .this_id = -1, .sg_tablesize = 256, .max_sectors = 0xFFFF, From 44cff0737127b609c2c40bf41f439bdd031a217d Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Sun, 31 May 2026 17:22:30 +0100 Subject: [PATCH 1016/3297] pinctrl: mediatek: mt8516: Fix Schmitt trigger register offset of pins 34-39 [ Upstream commit 1c3044cab23a056ea28da47da1cdd667a39df0b8 ] The correct Schmitt trigger register offset for pins 34-39 is 0xA00. Signed-off-by: Luca Leonardo Scorcia Fixes: 264667112ef0 ("pinctrl: mediatek: Add MT8516 Pinctrl driver") Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-mt8516.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8516.c b/drivers/pinctrl/mediatek/pinctrl-mt8516.c index abda75d4354e2..68d6638e7f4b1 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8516.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8516.c @@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8516_smt_set[] = { MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12), MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0), MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1), - MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2), + MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2), MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11), MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10), MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11), From 648a3960e366410d8c7117b06bfae993d563e2c3 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Sun, 31 May 2026 17:23:32 +0100 Subject: [PATCH 1017/3297] pinctrl: mediatek: mt8167: Fix Schmitt trigger register offset of pins 34-39 [ Upstream commit 439bc91d20188901dac698bed4921caac76d9074 ] The correct Schmitt trigger register offset for pins 34-39 is 0xA00. Value was verified with SoC data sheet. Signed-off-by: Luca Leonardo Scorcia Fixes: 82d70627e94a ("pinctrl: mediatek: Add MT8167 Pinctrl driver") Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-mt8167.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8167.c b/drivers/pinctrl/mediatek/pinctrl-mt8167.c index 143c266222725..c812d614e9d45 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8167.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8167.c @@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8167_smt_set[] = { MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12), MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0), MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1), - MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2), + MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2), MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11), MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10), MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11), From bbd664b7c77f6488cd5652231c0fe01d069a73ea Mon Sep 17 00:00:00 2001 From: Hui Zhu Date: Fri, 29 May 2026 09:41:30 +0800 Subject: [PATCH 1018/3297] vmalloc: fix NULL pointer dereference in is_vm_area_hugepages() [ Upstream commit c55dd3b46c1208d6d2ea737a8aefef4aa4c70cb8 ] find_vm_area() can return NULL if the given address is not a valid vmalloc area. Check the return value before dereferencing it to avoid a kernel crash. Link: https://lore.kernel.org/20260529014130.671291-1-hui.zhu@linux.dev Fixes: 121e6f3258fe ("mm/vmalloc: hugepage vmalloc mappings") Signed-off-by: Hui Zhu Reviewed-by: Dev Jain Reviewed-by: Uladzislau Rezki (Sony) Cc: Nicholas Piggin Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- include/linux/vmalloc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index eb54b7b3202f4..380dcc5a470e8 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -261,7 +261,9 @@ static inline bool is_vm_area_hugepages(const void *addr) * allocated in the vmalloc layer. */ #ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC - return find_vm_area(addr)->page_order > 0; + struct vm_struct *area = find_vm_area(addr); + + return area && area->page_order > 0; #else return false; #endif From a2e8b5264f92e6c4b5080304ab9c726e93960514 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 12 May 2026 11:13:03 +0200 Subject: [PATCH 1019/3297] hwspinlock: qcom: avoid uninitialized struct members [ Upstream commit 8752c396ce3b2136b3d4c906fe103f6efb6782d9 ] The reg_field is allocated on stack, so using the REG_FIELD macro will ensure that unused members do not have uninitialized values. Fixes: 19a0f61224d2 ("hwspinlock: qcom: Add support for Qualcomm HW Mutex block") Link: https://sashiko.dev/#/patchset/20260319105947.6237-1-wsa%2Brenesas%40sang-engineering.com Signed-off-by: Wolfram Sang Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260512091339.31085-2-wsa+renesas@sang-engineering.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/hwspinlock/qcom_hwspinlock.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 0390979fd765d..712003a4640cc 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -202,7 +202,6 @@ static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, static int qcom_hwspinlock_probe(struct platform_device *pdev) { struct hwspinlock_device *bank; - struct reg_field field; struct regmap *regmap; size_t array_size; u32 stride; @@ -224,9 +223,7 @@ static int qcom_hwspinlock_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bank); for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) { - field.reg = base + i * stride; - field.lsb = 0; - field.msb = 31; + struct reg_field field = REG_FIELD(base + i * stride, 0, 31); bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, regmap, field); From c788617705c3a6b9702e7cd51d95d395ad687ddc Mon Sep 17 00:00:00 2001 From: Hongyan Xia Date: Fri, 5 Jun 2026 09:43:39 +0000 Subject: [PATCH 1020/3297] sched/fair: Fix cpu_util runnable_avg arithmetic [ Upstream commit 29922fdfc2a4008d66418bedd0ebf5038fc54efa ] If we take runnable_avg in max(runnable_avg, util_avg) in cpu_util(), we should then add or subtract task runnable_avg, but the arithmetic below is still with task util_avg. This mixes runnable_avg with util_avg which is incorrect. Fix by always doing arithmetic with runnable_avg and only take max(runnable_avg, util_avg) at the last step. Fixes: 7d0583cf9ec7 ("sched/fair, cpufreq: Introduce 'runnable boosting'") Signed-off-by: Hongyan Xia Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Vincent Guittot Link: https://patch.msgid.link/20260605094318.37931-1-hongyan.xia@transsion.com Signed-off-by: Sasha Levin --- kernel/sched/fair.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b2f569d6c022d..ac5f08cd01a83 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8047,25 +8047,32 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target) static unsigned long cpu_util(int cpu, struct task_struct *p, int dst_cpu, int boost) { + bool add_task = p && task_cpu(p) != cpu && dst_cpu == cpu; + bool sub_task = p && task_cpu(p) == cpu && dst_cpu != cpu; struct cfs_rq *cfs_rq = &cpu_rq(cpu)->cfs; unsigned long util = READ_ONCE(cfs_rq->avg.util_avg); unsigned long runnable; - if (boost) { - runnable = READ_ONCE(cfs_rq->avg.runnable_avg); - util = max(util, runnable); - } - /* * If @dst_cpu is -1 or @p migrates from @cpu to @dst_cpu remove its * contribution. If @p migrates from another CPU to @cpu add its * contribution. In all the other cases @cpu is not impacted by the * migration so its util_avg is already correct. */ - if (p && task_cpu(p) == cpu && dst_cpu != cpu) - lsub_positive(&util, task_util(p)); - else if (p && task_cpu(p) != cpu && dst_cpu == cpu) + if (add_task) util += task_util(p); + else if (sub_task) + lsub_positive(&util, task_util(p)); + + if (boost) { + runnable = READ_ONCE(cfs_rq->avg.runnable_avg); + if (add_task) + runnable += READ_ONCE(p->se.avg.runnable_avg); + else if (sub_task) + lsub_positive(&runnable, + READ_ONCE(p->se.avg.runnable_avg)); + util = max(util, runnable); + } if (sched_feat(UTIL_EST)) { unsigned long util_est; From ce9d5a021cfca451a40f8d2a5efe11ae6a844656 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 3 Jun 2026 09:27:27 +0200 Subject: [PATCH 1021/3297] ARM: configs: Drop duplicated CONFIG_EXT4_FS [ Upstream commit ae371a58117d30a496e3be27cce8d9d13acdd740 ] Remove redundant, duplicated CONFIG_EXT4_FS to fix warnings like: axm55xx_defconfig:198:warning: override: reassigning to symbol EXT4_FS Fixes: c065b6046b34 ("Use CONFIG_EXT4_FS instead of CONFIG_EXT3_FS in all of the defconfigs") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Richard Cheng Link: https://lore.kernel.org/r/20260603072726.19404-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/configs/axm55xx_defconfig | 1 - arch/arm/configs/dove_defconfig | 1 - arch/arm/configs/ep93xx_defconfig | 1 - arch/arm/configs/mmp2_defconfig | 1 - arch/arm/configs/mv78xx0_defconfig | 1 - 5 files changed, 5 deletions(-) diff --git a/arch/arm/configs/axm55xx_defconfig b/arch/arm/configs/axm55xx_defconfig index 242a61208a0f8..631236c3ec3b5 100644 --- a/arch/arm/configs/axm55xx_defconfig +++ b/arch/arm/configs/axm55xx_defconfig @@ -195,7 +195,6 @@ CONFIG_PL320_MBOX=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y -CONFIG_EXT4_FS=y CONFIG_AUTOFS_FS=y CONFIG_FUSE_FS=y CONFIG_CUSE=y diff --git a/arch/arm/configs/dove_defconfig b/arch/arm/configs/dove_defconfig index bb6c4748bfc80..c7d0d531f0e0b 100644 --- a/arch/arm/configs/dove_defconfig +++ b/arch/arm/configs/dove_defconfig @@ -97,7 +97,6 @@ CONFIG_MV_XOR=y CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y # CONFIG_EXT4_FS_XATTR is not set -CONFIG_EXT4_FS=y CONFIG_ISO9660_FS=y CONFIG_JOLIET=y CONFIG_UDF_FS=m diff --git a/arch/arm/configs/ep93xx_defconfig b/arch/arm/configs/ep93xx_defconfig index 7f3756d8b086d..cd168dd85191a 100644 --- a/arch/arm/configs/ep93xx_defconfig +++ b/arch/arm/configs/ep93xx_defconfig @@ -105,7 +105,6 @@ CONFIG_EP93XX_DMA=y CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y # CONFIG_EXT4_FS_XATTR is not set -CONFIG_EXT4_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_JFFS2_FS=y diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig index f67e9cda73e24..82cc5e32e12cf 100644 --- a/arch/arm/configs/mmp2_defconfig +++ b/arch/arm/configs/mmp2_defconfig @@ -54,7 +54,6 @@ CONFIG_RTC_DRV_MAX8925=y # CONFIG_RESET_CONTROLLER is not set CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y -CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set CONFIG_MSDOS_FS=y CONFIG_FAT_DEFAULT_CODEPAGE=437 diff --git a/arch/arm/configs/mv78xx0_defconfig b/arch/arm/configs/mv78xx0_defconfig index 55f4ab67a3068..5f6272f06dacc 100644 --- a/arch/arm/configs/mv78xx0_defconfig +++ b/arch/arm/configs/mv78xx0_defconfig @@ -93,7 +93,6 @@ CONFIG_RTC_DRV_M41T80=y CONFIG_EXT2_FS=y CONFIG_EXT4_FS=y # CONFIG_EXT4_FS_XATTR is not set -CONFIG_EXT4_FS=m CONFIG_ISO9660_FS=m CONFIG_JOLIET=y CONFIG_UDF_FS=m From 3f0ea6d14fa4492d5d25a4e1eb023a6c47ef455f Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 23:33:36 +0900 Subject: [PATCH 1022/3297] wifi: mt76: mt7925: clean up DMA on probe failure [ Upstream commit 9629f31f505d74e76ac0d7a9492fd06c0316fc5d ] mt7925_pci_probe() initializes DMA before registering the device. If mt7925_register_device() fails, probe returns through err_free_irq without tearing down DMA state. That leaves the TX NAPI instance enabled and skips the DMA queue cleanup that the normal remove path performs through mt7925e_unregister_device(). Add a dedicated unwind label for failures after mt7925_dma_init() succeeds. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260426143728.41534-1-pakmyeonghun@bagmyeonghun-ui-MacBookPro.local Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 8eb1fe1082d15..d6732f50784cf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -414,10 +414,12 @@ static int mt7925_pci_probe(struct pci_dev *pdev, ret = mt7925_register_device(dev); if (ret) - goto err_free_irq; + goto err_free_dma; return 0; +err_free_dma: + mt792x_dma_cleanup(dev); err_free_irq: devm_free_irq(&pdev->dev, pdev->irq, dev); err_free_dev: From c7a83899203ed36696a2d35ddd03c0f522874a63 Mon Sep 17 00:00:00 2001 From: Rajat Gupta Date: Wed, 6 May 2026 21:35:31 -0700 Subject: [PATCH 1023/3297] wifi: mt76: use kfree_rcu for offchannel link in mt76_put_vif_phy_link [ Upstream commit 7fae097aa9a56c30febf539d72ef3773165d3aa3 ] mt76_put_vif_phy_link() frees the offchannel mlink with plain kfree() after rcu_assign_pointer(NULL). However, rcu_assign_pointer only prevents future RCU readers from obtaining the pointer -- it does not wait for existing readers that already hold it via rcu_dereference. The TX datapath (e.g. mt7996_mac_write_txwi) dereferences mlink->wcid and mlink->idx under rcu_read_lock. If a TX softirq obtained the pointer via rcu_dereference just before the NULL assignment, it will dereference freed memory after the kfree. struct mt76_vif_link already contains an rcu_head field that is unused at this free site -- a developer oversight, since the adjacent kfree_rcu_mightsleep call for rx_sc in the same function shows the pattern was understood. Replace kfree(mlink) with kfree_rcu(mlink, rcu_head). Fixes: a8f424c1287c ("wifi: mt76: add multi-radio remain_on_channel functions") Signed-off-by: Rajat Gupta Link: https://patch.msgid.link/20260507043531.492-1-rajat.gupta@oss.qualcomm.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index eccaee1fd434f..6adaeaf63e46a 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -311,7 +311,7 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->offchannel_link, NULL); dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); - kfree(mlink); + kfree_rcu(mlink, rcu_head); } void mt76_roc_complete(struct mt76_phy *phy) From bd3b91ff13006661afa19f8bea78d414fdfe0114 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 24 Mar 2026 15:49:04 +0000 Subject: [PATCH 1024/3297] wifi: mt76: mt7996: add missing max_remain_on_channel_duration [ Upstream commit ac41612e0044fa29cf9bc45b6808dda6d87ac2da ] Having this unset breaks remain-on-channel and mgmt TX. Move setting it to mt76 core to keep it in one place. Fixes: 69d54ce7491d0 ("wifi: mt76: mt7996: switch to single multi-radio wiphy") Link: https://patch.msgid.link/20260324154904.2555603-2-nbd@nbd.name Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt7615/init.c | 1 - drivers/net/wireless/mediatek/mt76/mt792x_core.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 2d24f9fa51a50..f6a642fd29d2d 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -449,6 +449,8 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL); + if (!wiphy->max_remain_on_channel_duration) + wiphy->max_remain_on_channel_duration = 5000; if (!wiphy->available_antennas_tx) wiphy->available_antennas_tx = phy->antenna_mask; if (!wiphy->available_antennas_rx) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c index 3e7af3e58736c..c4f5f6ee916af 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -195,7 +195,6 @@ mt7615_check_offload_capability(struct mt7615_dev *dev) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); wiphy->flags &= ~WIPHY_FLAG_4ADDR_STATION; - wiphy->max_remain_on_channel_duration = 5000; wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 9cad572c34a38..a5bdccae92c5b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -657,7 +657,6 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); - wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = From b8bf7c221b36451e9a88a33daa0bbbadcb73ad54 Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:51 -0500 Subject: [PATCH 1025/3297] wifi: mt76: mt7925: fix stale pointer comparisons in change_vif_links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9f1accf3069a0cd42c14ca6c7d44d5b17cc48a80 ] In the error path of mt7925_change_vif_links(), the free: label iterates over link_ids to clean up, but compares against `mconf` and `mlink` which hold stale values from the last loop iteration rather than the current link_id being freed. Use array-indexed access (mconfs[link_id] / mlinks[link_id]) to compare against the correct per-link pointers. Fixes: 69acd6d910b0 ("wifi: mt76: mt7925: add mt7925_change_vif_links") Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 9a8ef3449eea1..6c0dc72efc392 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2066,9 +2066,9 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->link_conf[link_id], NULL); rcu_assign_pointer(mvif->sta.link[link_id], NULL); - if (mconf != &mvif->bss_conf) + if (mconfs[link_id] != &mvif->bss_conf) devm_kfree(dev->mt76.dev, mconfs[link_id]); - if (mlink != &mvif->sta.deflink) + if (mlinks[link_id] != &mvif->sta.deflink) devm_kfree(dev->mt76.dev, mlinks[link_id]); } From a10e4959a73be5b70f59e1f7678bed78ea720906 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 10:47:19 -0500 Subject: [PATCH 1026/3297] wifi: mt76: mt7925: keep TX BA state in the primary WCID [ Upstream commit d3c854068bad22a25db6515f12784f64c663fed2 ] For MLO, the same TID can run over different links. Keeping TX BA state in a link WCID makes the state depend on which link starts aggregation first. Store it in the primary WCID instead, so the BA state stays stable across links. Fixes: 44eb173bdd4f ("wifi: mt76: mt7925: add link handling in mt7925_txwi_free") Tested-by: Yao Ting Hsieh Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425154721.738101-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 0608f0c1fd2c2..86de9ba6cde4f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -840,7 +840,6 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_link_sta *link_sta; - struct mt792x_link_sta *mlink; struct mt792x_sta *msta; bool is_8023; u16 fc, tid; @@ -879,14 +878,14 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, msta = (struct mt792x_sta *)sta->drv_priv; - if (sta->mlo && msta->deflink_id != IEEE80211_LINK_UNSPECIFIED) - mlink = rcu_dereference(msta->link[msta->deflink_id]); - else - mlink = &msta->deflink; - - if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) { + /* Packets belonging to the same TID can be transmitted over multiple + * links. Keep the TX BA session state in the primary link so all links + * share the same AMPDU bookkeeping. + */ + if (!test_and_set_bit(tid, &msta->deflink.wcid.ampdu_state)) { if (ieee80211_start_tx_ba_session(sta, tid, 0)) - clear_bit(tid, &mlink->wcid.ampdu_state); + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + } } From 856fa6a21586f86c4db058a7e7aeff36f9254231 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 09:46:48 -0500 Subject: [PATCH 1027/3297] wifi: mt76: mt792x: skip MLD header rewrite for 802.3 encap TX [ Upstream commit a1152244702bb31b64650e5ca8308142286c0e4a ] mt792x_tx() rewrites addr1/addr2/addr3 by treating skb->data as an 802.11 header for MLD traffic. That is only valid for native 802.11 frames. Direct 802.3 TX can also reach this path with IEEE80211_TX_CTL_HW_80211_ENCAP set, where skb->data is not an 802.11 header. Skip the MLD header rewrite for HW-encap packets to avoid corrupting 802.3 frame contents. Fixes: ebb1406813c6 ("wifi: mt76: mt7925: add link handling to txwi") Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425144648.734030-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt792x_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index a5bdccae92c5b..96c50a501c1a9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -105,7 +105,8 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, wcid = &mvif->sta.deflink.wcid; } - if (vif && control->sta && ieee80211_vif_is_mld(vif)) { + if (vif && control->sta && ieee80211_vif_is_mld(vif) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_link_sta *link_sta; struct ieee80211_bss_conf *conf; From c386e90a7ce8ddec9f038e9437661a2821b0ce89 Mon Sep 17 00:00:00 2001 From: Aviel Zohar Date: Mon, 13 Apr 2026 06:31:34 +0300 Subject: [PATCH 1028/3297] wifi: mt76: mt7925: validate skb length in testmode query [ Upstream commit c7369a00860a0704461d440e7c3bf9b49bfdbaee ] In mt7925_tm_query(), the response skb from mt76_mcu_send_and_get_msg() is used in a memcpy without validating its length: memcpy(evt_resp, skb->data + 8, MT7925_EVT_RSP_LEN); where MT7925_EVT_RSP_LEN is 512. If the firmware returns a response shorter than 520 bytes (8 + 512), this reads beyond the skb data buffer. The over-read data is then returned to userspace via nla_put() in mt7925_testmode_dump(). Add a length check before the memcpy to ensure the skb contains sufficient data. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Aviel Zohar Link: https://patch.msgid.link/20260413033136.5417-2-avielzohar123@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/testmode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c index a3c97164ba21e..329185d72fcc5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c @@ -105,6 +105,11 @@ mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req, if (ret) goto out; + if (skb->len < MT7925_EVT_RSP_LEN + 8) { + ret = -EINVAL; + goto out; + } + memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN); out: From 06e65d6cf80490bc0457d339595d1ed5a89e8899 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 11:10:59 +0200 Subject: [PATCH 1029/3297] wifi: mt76: mt7996: Fix possible token leak in mt7996_tx_prepare_skb() [ Upstream commit 831074096d0450308357271fc0ffd3f600a2487e ] If link_conf or link_sta lookup fails in mt7996_tx_prepare_skb routine, mt7996 driver leaks an already allocated tx token. Fix the issue releasing the token in case of error. Fixes: 7ef0c7ad735b0 ("wifi: mt76: mt7996: Implement MLD address translation for EAPOL") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt7996_tx_prepare_skb-token-leack-v1-1-2b9c9f59ceb1@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 8 ++++++-- drivers/net/wireless/mediatek/mt76/tx.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 149d8d711fc4c..989cf23f11142 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1106,11 +1106,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, link_conf = rcu_dereference(vif->link_conf[wcid->link_id]); if (!link_conf) - return -EINVAL; + goto error_release_token; link_sta = rcu_dereference(sta->link[wcid->link_id]); if (!link_sta) - return -EINVAL; + goto error_release_token; dma_sync_single_for_cpu(mdev->dma_dev, tx_info->buf[1].addr, tx_info->buf[1].len, DMA_TO_DEVICE); @@ -1215,6 +1215,10 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, tx_info->nbuf = MT_CT_DMA_BUF_NUM; return 0; + +error_release_token: + mt76_token_release(mdev, id, NULL); + return -EINVAL; } u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index b78ae6a34b658..9fb0cca5524a4 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -903,7 +903,7 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake) #endif } - if (dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && + if (wake && dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && dev->phy.q_tx[0]->blocked) *wake = true; From dfb27e5dd9e4d34fbb74fa834c644dcef0be2be9 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 11:38:57 +0200 Subject: [PATCH 1030/3297] wifi: mt76: mt7996: Fix possible NULL pointer dereference in mt7996_mac_write_txwi_80211() [ Upstream commit 61370e6674b5253de5686813ceeceebc35a7d3e5 ] For injected frames (e.g. via radiotap), mac80211 can pass info->control.vif = NULL, as explicitly noted in struct ieee80211_tx_info. Check vif pointer before executing ieee80211_vif_is_mld() in mt7996_mac_write_txwi_80211 routine in order to avoid a possible NULL pointer dereference. Fixes: f0b0b239b8f36 ("wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt7996_mac_write_txwi_80211-null-ptr-deref-v1-1-6dd38e1d3422@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 989cf23f11142..907e312562ab7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -796,6 +796,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, bool multicast = is_multicast_ether_addr(hdr->addr1); u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; __le16 fc = hdr->frame_control, sc = hdr->seq_ctrl; + struct ieee80211_vif *vif = info->control.vif; u16 seqno = le16_to_cpu(sc); bool hw_bigtk = false; u8 fc_type, fc_stype; @@ -858,7 +859,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); } - if (multicast && ieee80211_vif_is_mld(info->control.vif)) { + if (multicast && vif && ieee80211_vif_is_mld(vif)) { val = MT_TXD3_SN_VALID | FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); txwi[3] |= cpu_to_le32(val); @@ -878,12 +879,12 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); } - if (ieee80211_vif_is_mld(info->control.vif) && + if (vif && ieee80211_vif_is_mld(vif) && (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))) txwi[5] |= cpu_to_le32(MT_TXD5_FL); if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) && - ieee80211_vif_is_mld(info->control.vif)) { + vif && ieee80211_vif_is_mld(vif)) { txwi[5] |= cpu_to_le32(MT_TXD5_FL); txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); } From 1a399103cacc902058888e1254578c0c9f340f5b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 10:55:04 +0200 Subject: [PATCH 1031/3297] wifi: mt76: mt7996: fix reading zeroed info->control.flags after mt76_tx_status_skb_add() [ Upstream commit 729c83a3330c0a56662cd0d8e40db96d41c00a54 ] mt76_tx_status_skb_add() zeroes the mt76_tx_cb struct stored at info->status.status_driver_data via memset(). Since info->control and info->status are members of the same union in ieee80211_tx_info, this overwrites info->control.flags. In mt7996_tx_prepare_skb(), mt76_tx_status_skb_add() is called before mt7996_mac_write_txwi(), which re-reads info->control.flags to extract IEEE80211_TX_CTRL_MLO_LINK. Because the field has been zeroed, the link_id always resolves to 0 for frames using global_wcid, leading to incorrect TXWI configuration. Fix this by passing link_id as an explicit parameter to mt7996_mac_write_txwi(). In mt7996_tx_prepare_skb(), the link_id is already extracted from info->control.flags before the destructive mt76_tx_status_skb_add() call. For the beacon and inband discovery callers in mcu.c, use link_conf->link_id directly. Fixes: f0b0b239b8f36 ("wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt76_tx_status_skb_add-overwrite-fix-v2-1-b73c4b4a9798@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 14 ++++---------- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5 +++-- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 3 ++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 907e312562ab7..57f3f358393aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -896,7 +896,8 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed) + enum mt76_txq_id qid, u32 changed, + unsigned int link_id) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -906,7 +907,6 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; struct mt76_vif_link *mlink = NULL; struct mt7996_vif *mvif; - unsigned int link_id; u16 tx_count = 15; u32 val; bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | @@ -914,17 +914,11 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool beacon = !!(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); - if (wcid != &dev->mt76.global_wcid) - link_id = wcid->link_id; - else - link_id = u32_get_bits(info->control.flags, - IEEE80211_TX_CTRL_MLO_LINK); - mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; if (mvif) { if (wcid->offchannel) mlink = rcu_dereference(mvif->mt76.offchannel_link); - if (!mlink) + if (!mlink && link_id != IEEE80211_LINK_UNSPECIFIED) mlink = rcu_dereference(mvif->mt76.link[link_id]); } @@ -1136,7 +1130,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, /* Transmit non qos data by 802.11 header and need to fill txd by host*/ if (!is_8023 || pid >= MT_PACKET_ID_FIRST) mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key, - pid, qid, 0); + pid, qid, 0, link_id); /* MT7996 and MT7992 require driver to provide the MAC TXP for AddBA * req diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 38b8743409a0c..a3f813be107df 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2763,7 +2763,7 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, buf = (u8 *)bcn + sizeof(*bcn); mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, - BSS_CHANGED_BEACON); + BSS_CHANGED_BEACON, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); } @@ -2906,7 +2906,8 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, buf = (u8 *)tlv + sizeof(*discov); - mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, changed); + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, + changed, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 6793161ed198c..b8ffa42c5a1da 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -813,7 +813,8 @@ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed); + enum mt76_txq_id qid, u32 changed, + unsigned int link_id); void mt7996_mac_update_beacons(struct mt7996_phy *phy); void mt7996_mac_set_coverage_class(struct mt7996_phy *phy); void mt7996_mac_work(struct work_struct *work); From 42f34c478fcdf7f3c1a9dfcc211abfd3efd86f01 Mon Sep 17 00:00:00 2001 From: Dylan Eskew Date: Wed, 8 Apr 2026 07:50:58 -0700 Subject: [PATCH 1032/3297] wifi: mt76: mt7996: limit work in set_bitrate_mask [ Upstream commit 5fd3385505600934f5faa9635e5b30fa38e548b9 ] Calls to mt7996_set_bitrate_mask() would propagate work for all stations on the ieee80211_hw regardless of the vif specified in the call. To prevent unnecessary work in FW, limit setting the sta_rate to only the specified vif in mt7996_sta_rate_ctrl_update(). Fixes: afff4325548f0 ("wifi: mt76: mt7996: Use proper link_id in link_sta_rc_update callback") Signed-off-by: Dylan Eskew Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260408145057.2356878-2-dylan.eskew@candelatech.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index d714b7f3efe56..20da0c10669a9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1795,7 +1795,11 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_sta_link *msta_link; - u32 *changed = data; + struct mt7996_vif *mvif = data; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + + if (msta->vif != mvif) + return; msta_link = rcu_dereference(msta->link[msta->deflink_id]); if (msta_link) @@ -1808,7 +1812,6 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; mvif->deflink.bitrate_mask = *mask; @@ -1821,7 +1824,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * then multiple MCS setting (MCS 4,5,6) is not supported. */ ieee80211_iterate_stations_atomic(hw, mt7996_sta_rate_ctrl_update, - &changed); + mvif); ieee80211_queue_work(hw, &dev->rc_work); return 0; From 6356a829a1edcaa6b973f298373db47c34674e2e Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Tue, 31 Mar 2026 22:05:47 +0000 Subject: [PATCH 1033/3297] wifi: mt76: fix argument to ieee80211_is_first_frag() [ Upstream commit 5832743279da8c6ae72f715bad2f7141eca6f4b8 ] ieee80211_is_first_frag() operates on the seq_ctrl not the frame_control header field. Pass the correct one in; otherwise the results may vary. Sponsored by: The FreeBSD Foundation Fixes: 30ce7f4456ae4 ("mt76: validate rx CCMP PN") Link: https://cgit.freebsd.org/src/commit/sys/contrib/dev/mediatek/mt76/mac80211.c?id=c67fd35e58c6ee1e19877a7fe5998885683abedc Signed-off-by: Bjoern A. Zeeb Link: https://patch.msgid.link/83s4psnr-popo-8789-757o-npr2n9n7rs2o@SerrOFQ.bet Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index f6a642fd29d2d..7348ac5cf7e60 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1316,7 +1316,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb) * All further fragments will be validated by mac80211 only. */ if (ieee80211_is_frag(hdr) && - !ieee80211_is_first_frag(hdr->frame_control)) + !ieee80211_is_first_frag(hdr->seq_ctrl)) return; } From 6b8e35685c18c7d5298121503e432bf846c9076f Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:03 -0700 Subject: [PATCH 1034/3297] wifi: mt76: mt7915: fix potential tx_retries underflow [ Upstream commit 05e72b6167970043348bfbe8f72a3b67a38a9f1c ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 943e4fb96e6f ("wifi: mt76: mt7915: report tx retries/failed counts for non-WED path") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-1-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 9364e8673eb9d..50af0d9f240b6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -912,16 +912,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) } if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT_V3, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT_V3, info); } else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT, info); } From 3b6e6fefa57f4f0afc94bd72a55a91dbca288368 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:04 -0700 Subject: [PATCH 1035/3297] wifi: mt76: mt7921: fix potential tx_retries underflow [ Upstream commit 3c5671ed81b1fff97fa868dae771690599db94f7 ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 9aecfa754c7f ("wifi: mt76: mt7921e: report tx retries/failed counts in tx free event") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-2-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index bce26389ab18f..d2731e9953bf8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -530,8 +530,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len) stat = FIELD_GET(MT_TX_FREE_STATUS, info); if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TX_FREE_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!stat; } From ad12fdaaed16c6e75f2f826c0ea7c2b19c57da63 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:05 -0700 Subject: [PATCH 1036/3297] wifi: mt76: mt7925: fix potential tx_retries underflow [ Upstream commit 1e1fd84571e62a2961cea44c053340ec5c99b2cb ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-3-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 86de9ba6cde4f..d951a46e9d48c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1139,8 +1139,9 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len) if (info & MT_TXFREE_INFO_HEADER) { if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!FIELD_GET(MT_TXFREE_INFO_STAT, info); } From 7ec839c7c0bc10ccb7a17ac4fada0910b674c5a4 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:06 -0700 Subject: [PATCH 1037/3297] wifi: mt76: mt7996: fix potential tx_retries underflow [ Upstream commit 4d8bba99d645bcb46a442b18eb42402610cba03a ] When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 2461599f835e ("wifi: mt76: mt7996: get tx_retries and tx_failed from txfree") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-4-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 57f3f358393aa..29f599a7ad017 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1398,13 +1398,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) cur_info++; continue; } else if (info & MT_TXFREE_INFO_HEADER) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (!wcid) continue; - tx_retries = - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TXFREE_INFO_STAT, info); From 3b15d02be05e74321adb1e0ae0cb4ccfba7c6cb1 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 21 May 2026 15:19:37 +0100 Subject: [PATCH 1038/3297] btrfs: fix invalid pointer dereference in __btrfs_run_delayed_refs() [ Upstream commit 486f8298b6188ff11ef1f4be7f1d5d2e4d1b1fae ] In the beginning of the loop, we try to obtain a locked delayed ref head, if 'locked_ref' is currently NULL, by calling btrfs_select_ref_head(), which can return an error pointer. If the error pointer is -EAGAIN we do a continue and go back to the beginning of the loop, which will not try again to call btrfs_select_ref_head() since 'locked_ref' is no longer NULL but it's ERR_PTR(-EAGAIN), and then we do: spin_lock(&locked_ref->lock); against a ERR_PTR(-EAGAIN) value, generating an invalid pointer dereference. Fix this by ensuring that 'locked_ref' is set to NULL when btrfs_select_ref_head() returns ERR_PTR(-EAGAIN) and incrementing 'count' as well, to prevent infinite looping. We do this by doing a goto to the bottom of the loop that already sets 'locked_ref' to NULL and does a cond_resched(), with an increment to 'count' right before the goto. These measures were in place before the refactoring in commit 0110a4c43451 ("btrfs: refactor __btrfs_run_delayed_refs loop") but were unintentionally lost afterwards. Reported-by: Dan Carpenter Link: https://lore.kernel.org/linux-btrfs/ag8ARRwykv8bpJ87@stanley.mountain/ Fixes: 0110a4c43451 ("btrfs: refactor __btrfs_run_delayed_refs loop") Reviewed-by: Boris Burkov Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/extent-tree.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index ffe6c80495666..6ef7cc116bfcc 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2022,7 +2022,8 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, locked_ref = btrfs_select_ref_head(fs_info, delayed_refs); if (IS_ERR_OR_NULL(locked_ref)) { if (PTR_ERR(locked_ref) == -EAGAIN) { - continue; + count++; + goto again; } else { break; } @@ -2070,7 +2071,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, * Either success case or btrfs_run_delayed_refs_for_head * returned -EAGAIN, meaning we need to select another head */ - +again: locked_ref = NULL; cond_resched(); } while ((min_bytes != U64_MAX && bytes_processed < min_bytes) || From 90a9c909c5b75e26c83132ce6fd851dff79e63ad Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jun 2026 09:49:04 +0200 Subject: [PATCH 1039/3297] ALSA: aloop: Drop superfluous break [ Upstream commit 123fd13f35ccaf7d2b98f5a8cc6c8a3de378568d ] At converting the spinlock to guard(), a break statement was put in the scoped_guard block in loopback_jiffies_timer_function(), but it's obviously superfluous (although it's harmless). Better to drop it for avoiding confusion. Fixes: 1ef2cb6b29c2 ("ALSA: aloop: Use guard() for spin locks") Link: https://patch.msgid.link/20260609074907.726593-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/drivers/aloop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index a37a1695f51c7..3f8488716a082 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -728,7 +728,6 @@ static void loopback_jiffies_timer_function(struct timer_list *t) if (dpcm->period_update_pending) { dpcm->period_update_pending = 0; period_elapsed = true; - break; } } } From 47e5302722e09860bef18cd4e075bb823448cf0c Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Tue, 9 Jun 2026 05:11:18 +0200 Subject: [PATCH 1040/3297] gpio: mt7621: fix interrupt banks mapping on gpio chips [ Upstream commit a46f2e5720f5670feda145709d1f0d20be5c7263 ] The GPIO controller's registers are organized as sets of eight 32-bit registers with each set controlling a bank of up to 32 pins. A single interrupt is shared for all of the banks handled by the controller. The driver implements this using three gpio chip instances every one with its own irq chip. Every single pin can generate interrupts having a total of 96 possible interrupts here. It looks like there is a problem with interrupts being properly mapped to the gpio bank using this solution. This problem report is in the following lore's link [0]. Device tree is using two cells for this, so only the interrupt pin and the interrupt type are described there. Changing to have three cells to setup also the bank and implement 'of_node_instance_match()' would also work but this would be an ABI breakage and also a bit incoherent since gpios itself are also using two cells and properly mapped in desired bank using through its pin number on 'of_xlate()'. That said, register a linear IRQ domain of the total of 96 interrupts shared with the three gpio chip instances so the bank and the interrupt is properly decoded and devices using gpio IRQs properly work. [0]: https://lore.kernel.org/linux-gpio/CAAMcf8C_A9dJ_v4QRKtb9eGNOpJ7BZNOGsFP4i2WFOZxOVBPnQ@mail.gmail.com/T/#u Fixes: 4ba9c3afda41 ("gpio: mt7621: Add a driver for MT7621") Co-developed-by: Vicente Bergas Signed-off-by: Vicente Bergas Tested-by: Vicente Bergas Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260609031118.2275735-1-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mt7621.c | 282 ++++++++++++++++++++++++++++--------- 1 file changed, 216 insertions(+), 66 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 91230be515879..a814885ccd5d1 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -29,8 +29,8 @@ #define GPIO_REG_EDGE 0xA0 struct mtk_gc { - struct irq_chip irq_chip; struct gpio_generic_chip chip; + struct mtk *parent_priv; int bank; u32 rising; u32 falling; @@ -41,20 +41,32 @@ struct mtk_gc { /** * struct mtk - state container for * data of the platform driver. It is 3 - * separate gpio-chip each one with its - * own irq_chip. - * @dev: device instance + * separate gpio-chip having an IRQ + * linear domain shared for all of them + * @pdev: platform device instance * @base: memory base address + * @irq_domain: IRQ linear domain shared across the three gpio chips * @gpio_irq: irq number from the device tree + * @num_gpios: total number of gpio pins on the three gpio chips * @gc_map: array of the gpio chips */ struct mtk { - struct device *dev; + struct platform_device *pdev; void __iomem *base; + struct irq_domain *irq_domain; int gpio_irq; + int num_gpios; struct mtk_gc gc_map[MTK_BANK_CNT]; }; +static inline struct mtk * +mt7621_gpio_gc_to_priv(struct gpio_chip *gc) +{ + struct mtk_gc *bank = gpiochip_get_data(gc); + + return bank->parent_priv; +} + static inline struct mtk_gc * to_mediatek_gpio(struct gpio_chip *chip) { @@ -67,7 +79,7 @@ static inline void mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; gpio_generic_write_reg(&rg->chip, mtk->base + offset, val); @@ -77,41 +89,62 @@ static inline u32 mtk_gpio_r32(struct mtk_gc *rg, u32 offset) { struct gpio_chip *gc = &rg->chip.gc; - struct mtk *mtk = gpiochip_get_data(gc); + struct mtk *mtk = mt7621_gpio_gc_to_priv(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; return gpio_generic_read_reg(&rg->chip, mtk->base + offset); } -static irqreturn_t -mediatek_gpio_irq_handler(int irq, void *data) +static void +mt7621_gpio_irq_bank_handler(struct mtk_gc *bank) { - struct gpio_chip *gc = data; - struct mtk_gc *rg = to_mediatek_gpio(gc); - irqreturn_t ret = IRQ_NONE; + struct mtk *priv = bank->parent_priv; + struct irq_domain *domain = priv->irq_domain; + int hwbase = bank->chip.gc.offset; unsigned long pending; - int bit; + unsigned int offset; + + pending = mtk_gpio_r32(bank, GPIO_REG_STAT); + if (!pending) + return; + + mtk_gpio_w32(bank, GPIO_REG_STAT, pending); + + for_each_set_bit(offset, &pending, MTK_BANK_WIDTH) + generic_handle_domain_irq(domain, hwbase + offset); +} + +static void +mt7621_gpio_irq_handler(struct irq_desc *desc) +{ + struct mtk *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int i; - pending = mtk_gpio_r32(rg, GPIO_REG_STAT); + chained_irq_enter(chip, desc); + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; - for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) { - generic_handle_domain_irq(gc->irq.domain, bit); - mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit)); - ret |= IRQ_HANDLED; + mt7621_gpio_irq_bank_handler(bank); } + chained_irq_exit(chip, desc); +} - return ret; +static int +mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank) +{ + return hwirq - bank->chip.gc.offset; } static void mediatek_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; - gpiochip_enable_irq(gc, d->hwirq); + gpiochip_enable_irq(gc, mask); guard(gpio_generic_lock_irqsave)(&rg->chip); @@ -119,18 +152,18 @@ mediatek_gpio_irq_unmask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising)); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(mask) & rg->rising)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(mask) & rg->falling)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(mask) & rg->hlevel)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(mask) & rg->llevel)); } static void mediatek_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); u32 rise, fall, high, low; scoped_guard(gpio_generic_lock_irqsave, &rg->chip) { @@ -138,22 +171,21 @@ mediatek_gpio_irq_mask(struct irq_data *d) fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); high = mtk_gpio_r32(rg, GPIO_REG_HLVL); low = mtk_gpio_r32(rg, GPIO_REG_LLVL); - mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin)); - mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin)); + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(mask)); + mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(mask)); } - gpiochip_disable_irq(gc, d->hwirq); + gpiochip_disable_irq(gc, mask); } static int mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct mtk_gc *rg = to_mediatek_gpio(gc); - int pin = d->hwirq; - u32 mask = BIT(pin); + struct mtk_gc *rg = gpiochip_get_data(gc); + u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg)); if (type == IRQ_TYPE_PROBE) { if ((rg->rising | rg->falling | @@ -190,6 +222,26 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) return 0; } +static int +mt7621_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + return gpiochip_reqres_irq(gc, irq); +} + +static void +mt7621_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct mtk_gc *rg = gpiochip_get_data(gc); + unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg); + + gpiochip_relres_irq(gc, irq); +} + static int mediatek_gpio_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) @@ -208,14 +260,123 @@ mediatek_gpio_xlate(struct gpio_chip *chip, static const struct irq_chip mt7621_irq_chip = { .name = "mt7621-gpio", + .irq_request_resources = mt7621_gpio_irq_reqres, + .irq_release_resources = mt7621_gpio_irq_relres, .irq_mask_ack = mediatek_gpio_irq_mask, .irq_mask = mediatek_gpio_irq_mask, .irq_unmask = mediatek_gpio_irq_unmask, .irq_set_type = mediatek_gpio_irq_type, .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void +mt7621_gpio_remove(struct platform_device *pdev) +{ + struct mtk *priv = platform_get_drvdata(pdev); + int offset, virq; + + if (priv->gpio_irq > 0) + irq_set_chained_handler_and_data(priv->gpio_irq, NULL, NULL); + + /* Remove all IRQ mappings and delete the domain */ + if (priv->irq_domain) { + for (offset = 0; offset < priv->num_gpios; offset++) { + virq = irq_find_mapping(priv->irq_domain, offset); + irq_dispose_mapping(virq); + } + irq_domain_remove(priv->irq_domain); + } +} + +static struct mtk_gc * +mt7621_gpio_hwirq_to_bank(struct mtk *priv, irq_hw_number_t hwirq) +{ + int i; + + for (i = 0; i < MTK_BANK_CNT; i++) { + struct mtk_gc *bank = &priv->gc_map[i]; + + if (hwirq >= bank->chip.gc.offset && + hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio)) + return bank; + } + + return NULL; +} + +static int +mt7621_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mtk *priv = d->host_data; + struct mtk_gc *bank = mt7621_gpio_hwirq_to_bank(priv, hwirq); + struct platform_device *pdev = priv->pdev; + int ret; + + if (!bank) + return -EINVAL; + + dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n", + irq, (int)hwirq, bank->bank); + + ret = irq_set_chip_data(irq, &bank->chip.gc); + if (ret < 0) + return ret; + + irq_set_chip_and_handler(irq, &mt7621_irq_chip, handle_simple_irq); + irq_set_noprobe(irq); + + return 0; +} + +static void +mt7621_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops mt7621_gpio_irq_domain_ops = { + .map = mt7621_gpio_irq_map, + .unmap = mt7621_gpio_irq_unmap, + .xlate = irq_domain_xlate_twocell, }; +static int +mt7621_gpio_irq_setup(struct platform_device *pdev, + struct mtk *priv) +{ + struct device *dev = &pdev->dev; + + priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev), + priv->num_gpios, + &mt7621_gpio_irq_domain_ops, + priv); + if (!priv->irq_domain) { + dev_err(dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } + + irq_set_chained_handler_and_data(priv->gpio_irq, + mt7621_gpio_irq_handler, priv); + irq_set_status_flags(priv->gpio_irq, IRQ_DISABLE_UNLAZY); + + return 0; +} + +static int +mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct mtk *priv = mt7621_gpio_gc_to_priv(gc); + /* gc_offset is relative to this gpio_chip; want real offset */ + int hwirq = offset + gc->offset; + + if (hwirq >= priv->num_gpios) + return -ENXIO; + + return irq_create_mapping(priv->irq_domain, hwirq); +} + static int mediatek_gpio_bank_probe(struct device *dev, int bank) { @@ -228,6 +389,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg = &mtk->gc_map[bank]; memset(rg, 0, sizeof(*rg)); + rg->parent_priv = mtk; rg->bank = bank; dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE); @@ -253,41 +415,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank) rg->chip.gc.of_gpio_n_cells = 2; rg->chip.gc.of_xlate = mediatek_gpio_xlate; + rg->chip.gc.ngpio = MTK_BANK_WIDTH; rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", dev_name(dev), bank); if (!rg->chip.gc.label) return -ENOMEM; rg->chip.gc.offset = bank * MTK_BANK_WIDTH; + if (mtk->gpio_irq > 0) + rg->chip.gc.to_irq = mt7621_gpio_to_irq; - if (mtk->gpio_irq) { - struct gpio_irq_chip *girq; - - /* - * Directly request the irq here instead of passing - * a flow-handler because the irq is shared. - */ - ret = devm_request_irq(dev, mtk->gpio_irq, - mediatek_gpio_irq_handler, IRQF_SHARED, - rg->chip.gc.label, &rg->chip.gc); - - if (ret) { - dev_err(dev, "Error requesting IRQ %d: %d\n", - mtk->gpio_irq, ret); - return ret; - } - - girq = &rg->chip.gc.irq; - gpio_irq_chip_set_chip(girq, &mt7621_irq_chip); - /* This will let us handle the parent IRQ in the driver */ - girq->parent_handler = NULL; - girq->num_parents = 0; - girq->parents = NULL; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_simple_irq; - } - - ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk); + ret = devm_gpiochip_add_data(dev, &rg->chip.gc, rg); if (ret < 0) { dev_err(dev, "Could not register gpio %d, ret=%d\n", rg->chip.gc.ngpio, ret); @@ -322,7 +460,8 @@ mediatek_gpio_probe(struct platform_device *pdev) if (mtk->gpio_irq < 0) return mtk->gpio_irq; - mtk->dev = dev; + mtk->pdev = pdev; + mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT; platform_set_drvdata(pdev, mtk); for (i = 0; i < MTK_BANK_CNT; i++) { @@ -331,7 +470,17 @@ mediatek_gpio_probe(struct platform_device *pdev) return ret; } + if (mtk->gpio_irq > 0) { + ret = mt7621_gpio_irq_setup(pdev, mtk); + if (ret) + goto fail; + } + return 0; + +fail: + mt7621_gpio_remove(pdev); + return ret; } static const struct of_device_id mediatek_gpio_match[] = { @@ -342,6 +491,7 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match); static struct platform_driver mediatek_gpio_driver = { .probe = mediatek_gpio_probe, + .remove = mt7621_gpio_remove, .driver = { .name = "mt7621_gpio", .of_match_table = mediatek_gpio_match, From 0678fed27def92ab3b6af88e5b8df18eb8117bb2 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 4 Jun 2026 15:28:31 +0530 Subject: [PATCH 1041/3297] wifi: ath12k: enable IEEE80211_VHT_EXT_NSS_BW_CAPABLE when NSS ratio is reported [ Upstream commit 63abe299b12b317dfee5bcd09037da4668a4431a ] When firmware reports NSS ratio support, SUPPORTS_VHT_EXT_NSS_BW is enabled in ath12k. However, IEEE80211_VHT_EXT_NSS_BW_CAPABLE must also be set to make the advertisement valid. According to IEEE Std 802.11-2024, Subclause 9.4.2.156.3 (Supported VHT-MCS and NSS Set subfields), the VHT Extended NSS BW Capable bit indicates whether a STA is capable of interpreting the Extended NSS BW Support subfield of the VHT capabilities information field. Advertising extended NSS BW support without setting this capability bit is therefore invalid. Without this change, mac80211 detects the inconsistency and logs: ieee80211 phy0: copying sband (band 1) due to VHT EXT NSS BW flag This indicates that mac80211 implicitly aligns IEEE80211_VHT_EXT_NSS_BW_CAPABLE during ieee80211_register_hw(). Explicitly setting the bit in ath12k avoids this fixup and ensures capabilities are advertised correctly by the driver. This change follows the same approach as the existing ath11k fix. https://lore.kernel.org/all/20211013073704.15888-1-wgong@codeaurora.org/ Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Fixes: 18ab9d038fad ("wifi: ath12k: add support for 160 MHz bandwidth") Signed-off-by: Wen Gong Signed-off-by: Maharaja Kennadyrajan Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260604095831.2674298-1-maharaja.kennadyrajan@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 368c377ab9b55..4bb4482e27343 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -7672,6 +7672,10 @@ ath12k_create_vht_cap(struct ath12k *ar, u32 rate_cap_tx_chainmask, vht_cap.vht_supported = 1; vht_cap.cap = ar->pdev->cap.vht_cap; + if (ar->pdev->cap.nss_ratio_enabled) + vht_cap.vht_mcs.tx_highest |= + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); + ath12k_set_vht_txbf_cap(ar, &vht_cap.cap); /* 80P80 is not supported */ From de590cdf7efec8a0b6da90ae2ab5fc5df26810b9 Mon Sep 17 00:00:00 2001 From: David Laight Date: Mon, 8 Jun 2026 13:42:42 +0100 Subject: [PATCH 1042/3297] fbdev: sm501fb: Fix buffer errors in OF binding code [ Upstream commit d8421e09382cfe0bd2a044c8b0a822f64855dd4e ] The code that gets the frame buffer mode from OF has 'use after free', 'buffer overrun' and memory leaks. info->edid_data isn't free if the probe functions fail or if pd->def_mode is set. If both the CRT and PANEL are enabled info->edid_data is used after being freed and is freed twice. The string returned by of_get_property(np, "mode", &len) is just written over either the static "640x480-16@60" or the module parameter string without any regard for the length (which is most likely longer). Use kstrump() for the OF mode and free everything before freeing 'info. Fixes: 4295f9bf74a88 ("video, sm501: add OF binding to support SM501") Signed-off-by: David Laight Signed-off-by: Helge Deller Signed-off-by: Sasha Levin --- drivers/video/fbdev/sm501fb.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c index ed6f4f43e2d52..aaa3fef0dcb24 100644 --- a/drivers/video/fbdev/sm501fb.c +++ b/drivers/video/fbdev/sm501fb.c @@ -96,6 +96,7 @@ struct sm501fb_info { void __iomem *fbmem; /* remapped framebuffer */ size_t fbmem_len; /* length of remapped region */ u8 *edid_data; + char *fb_mode; }; /* per-framebuffer private data */ @@ -1793,12 +1794,11 @@ static int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head, fb->var.yres_virtual = fb->var.yres; } else { if (info->edid_data) { - ret = fb_find_mode(&fb->var, fb, fb_mode, + ret = fb_find_mode(&fb->var, fb, + info->fb_mode ?: fb_mode, fb->monspecs.modedb, fb->monspecs.modedb_len, &sm501_default_mode, default_bpp); - /* edid_data is no longer needed, free it */ - kfree(info->edid_data); } else { ret = fb_find_mode(&fb->var, fb, NULL, NULL, 0, NULL, 8); @@ -1974,7 +1974,7 @@ static int sm501fb_probe(struct platform_device *pdev) /* Get EDID */ cp = of_get_property(np, "mode", &len); if (cp) - strcpy(fb_mode, cp); + info->fb_mode = kstrdup(cp, GFP_KERNEL); prop = of_get_property(np, "edid", &len); if (prop && len == EDID_LENGTH) { info->edid_data = kmemdup(prop, EDID_LENGTH, @@ -2031,6 +2031,12 @@ static int sm501fb_probe(struct platform_device *pdev) goto err_started_crt; } + /* These aren't needed any more */ + kfree(info->edid_data); + kfree(info->fb_mode); + info->edid_data = NULL; + info->fb_mode = NULL; + /* we registered, return ok */ return 0; @@ -2048,6 +2054,8 @@ static int sm501fb_probe(struct platform_device *pdev) framebuffer_release(info->fb[HEAD_CRT]); err_alloc: + kfree(info->edid_data); + kfree(info->fb_mode); kfree(info); return ret; From ebb579c5c0f0f13bd9814eda136337c605e18558 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 29 Jan 2026 16:47:43 -0500 Subject: [PATCH 1043/3297] vfs: add FS_USERNS_DELEGATABLE flag and set it for NFS [ Upstream commit c5d6cac28646b0d5d81ef632be748ae93c1f36c7 ] Commit e1c5ae59c0f2 ("fs: don't allow non-init s_user_ns for filesystems without FS_USERNS_MOUNT") prevents the mount of any filesystem inside a container that doesn't have FS_USERNS_MOUNT set. This broke NFS mounts in our containerized environment. We have a daemon somewhat like systemd-mountfsd running in the init_ns. A process does a fsopen() inside the container and passes it to the daemon via unix socket. The daemon then vets that the request is for an allowed NFS server and performs the mount. This now fails because the fc->user_ns is set to the value in the container and NFS doesn't set FS_USERNS_MOUNT. We don't want to add FS_USERNS_MOUNT to NFS since that would allow the container to mount any NFS server (even malicious ones). Add a new FS_USERNS_DELEGATABLE flag, and enable it on NFS. Fixes: e1c5ae59c0f2 ("fs: don't allow non-init s_user_ns for filesystems without FS_USERNS_MOUNT") Signed-off-by: Jeff Layton Link: https://patch.msgid.link/20260129-twmount-v1-1-4874ed2a15c4@kernel.org Acked-by: Anna Schumaker Reviewed-by: Alexander Mikhalitsyn Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/nfs/fs_context.c | 8 ++++++-- fs/super.c | 11 ++++++----- include/linux/fs.h | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index b4679b7161b09..128ebd48b4f4b 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -1768,7 +1768,9 @@ struct file_system_type nfs_fs_type = { .init_fs_context = nfs_init_fs_context, .parameters = nfs_fs_parameters, .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | + FS_BINARY_MOUNTDATA | + FS_USERNS_DELEGATABLE, }; MODULE_ALIAS_FS("nfs"); EXPORT_SYMBOL_GPL(nfs_fs_type); @@ -1780,7 +1782,9 @@ struct file_system_type nfs4_fs_type = { .init_fs_context = nfs_init_fs_context, .parameters = nfs_fs_parameters, .kill_sb = nfs_kill_super, - .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, + .fs_flags = FS_RENAME_DOES_D_MOVE | + FS_BINARY_MOUNTDATA | + FS_USERNS_DELEGATABLE, }; MODULE_ALIAS_FS("nfs4"); MODULE_ALIAS("nfs4"); diff --git a/fs/super.c b/fs/super.c index 4c79f170ac0d2..ce6e5b4ff3c5f 100644 --- a/fs/super.c +++ b/fs/super.c @@ -737,12 +737,13 @@ struct super_block *sget_fc(struct fs_context *fc, int err; /* - * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT is - * not set, as the filesystem is likely unprepared to handle it. - * This can happen when fsconfig() is called from init_user_ns with - * an fs_fd opened in another user namespace. + * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT or + * FS_USERNS_DELEGATABLE is not set, as the filesystem is likely + * unprepared to handle it. This can happen when fsconfig() is called + * from init_user_ns with an fs_fd opened in another user namespace. */ - if (user_ns != &init_user_ns && !(fc->fs_type->fs_flags & FS_USERNS_MOUNT)) { + if (user_ns != &init_user_ns && + !(fc->fs_type->fs_flags & (FS_USERNS_MOUNT | FS_USERNS_DELEGATABLE))) { errorfc(fc, "VFS: Mounting from non-initial user namespace is not allowed"); return ERR_PTR(-EPERM); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 39269c7d167e6..6374649060ba3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2693,6 +2693,7 @@ struct file_system_type { #define FS_MGTIME 64 /* FS uses multigrain timestamps */ #define FS_LBS 128 /* FS supports LBS */ #define FS_POWER_FREEZE 256 /* Always freeze on suspend/hibernate */ +#define FS_USERNS_DELEGATABLE 1024 /* Can be mounted inside userns from outside */ #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ int (*init_fs_context)(struct fs_context *); const struct fs_parameter_spec *parameters; From bd5e90b0f5a0915325d6d2b86be5cd29d859c81c Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Fri, 29 May 2026 17:18:36 +0300 Subject: [PATCH 1044/3297] hwmon: (it87) Clamp negative values to zero in set_fan() [ Upstream commit 7f8581c70a3bd50a932d3d2d253e99c5ec3eda74 ] set_fan() parses user input with kstrtol() and passes the resulting value to FAN16_TO_REG() on chips with 16-bit fan support. Negative fan speeds are not meaningful and should be rejected before conversion. Worst scenario, one may be able to abuse undefined behaviour of signed overflow to possibly induce rpm * 2 == 0 in FAN16_TO_REG(), thus causing a division by zero. Instead, clamp val < 0 to zero and keep the conversion in its valid input domain, avoiding unsafe arithmetic in the register conversion path. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 17d648bf5786 ("it87: Add support for the IT8716F") Signed-off-by: Nikita Zhandarovich Link: https://lore.kernel.org/r/20260529141839.1639287-1-n.zhandarovich@fintech.ru Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/it87.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5cfb98a0512f0..c0c9826d44a2e 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1401,6 +1401,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; + if (val < 0) + val = 0; + err = it87_lock(data); if (err) return err; From ba641829c11cba5b89fcd9000dbea300fa1b7502 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 22 May 2026 11:02:46 +0200 Subject: [PATCH 1045/3297] btrfs: zoned: don't account data relocation space-info in statfs free space [ Upstream commit 52416a27aabc43eab3792fd0ca9f5dabeab58f31 ] Don't account the free space in a data relocation space-info sub-group as usable free space in statfs. This is misleading as no user allocations can be made in this space-info sub-group. It is only a target for relocation. Fixes: f92ee31e031c ("btrfs: introduce btrfs_space_info sub-group") Reviewed-by: Boris Burkov Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 35903afdd1e5b..736575f925179 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1741,7 +1741,8 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) int mixed = 0; list_for_each_entry(found, &fs_info->space_info, list) { - if (found->flags & BTRFS_BLOCK_GROUP_DATA) { + if (found->flags & BTRFS_BLOCK_GROUP_DATA && + found->subgroup_id != BTRFS_SUB_GROUP_DATA_RELOC) { int i; total_free_data += found->disk_total - found->disk_used; From 943f5917c53ca5aab980b0c9faebf7770978d7b3 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 22 May 2026 19:14:07 +0100 Subject: [PATCH 1046/3297] Revert "btrfs: fix the file offset calculation inside btrfs_decompress_buf2page()" [ Upstream commit 0279bed34c22dd5ebff12e5af8ef940de93c5523 ] It seems that af566bdaff54 was tested against a tree which did not contain commit 12851bd921d4 ("fs: Turn page_offset() into a wrapper around folio_pos()). Unfortunately it has a bug of its own; on 32-bit systems, shifting by PAGE_SHIFT will overflow on files larger than 4GiB. Since page_offset() is now fixed, just revert af566bdaff54. Fixes: af566bdaff54 (btrfs: fix the file offset calculation inside btrfs_decompress_buf2page()) Signed-off-by: Matthew Wilcox (Oracle) Reviewed-by: Qu Wenruo Reviewed-by: Boris Burkov Tested-by: Boris Burkov Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/compression.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 8c3899832a1aa..fbf1ff5965994 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -1187,22 +1187,6 @@ void __cold btrfs_exit_compress(void) bioset_exit(&btrfs_compressed_bioset); } -/* - * The bvec is a single page bvec from a bio that contains folios from a filemap. - * - * Since the folio may be a large one, and if the bv_page is not a head page of - * a large folio, then page->index is unreliable. - * - * Thus we need this helper to grab the proper file offset. - */ -static u64 file_offset_from_bvec(const struct bio_vec *bvec) -{ - const struct page *page = bvec->bv_page; - const struct folio *folio = page_folio(page); - - return (page_pgoff(folio, page) << PAGE_SHIFT) + bvec->bv_offset; -} - /* * Copy decompressed data from working buffer to pages. * @@ -1255,7 +1239,7 @@ int btrfs_decompress_buf2page(const char *buf, u32 buf_len, * cb->start may underflow, but subtracting that value can still * give us correct offset inside the full decompressed extent. */ - bvec_offset = file_offset_from_bvec(&bvec) - cb->start; + bvec_offset = page_offset(bvec.bv_page) + bvec.bv_offset - cb->start; /* Haven't reached the bvec range, exit */ if (decompressed + buf_len <= bvec_offset) From 18285888cb41a87262e8bd90d3093530cee1df08 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 22 May 2026 11:22:12 +0200 Subject: [PATCH 1047/3297] btrfs: zoned: always set max_active_zones for zoned devices [ Upstream commit 21a3533b99b8a53a026cc9f5041b10795c1f3ae8 ] When a block device does not report a maximum number of open or active zones, currently assign BTRFS_DEFAULT_MAX_ACTIVE_ZONES (128) to the internal limit, if the device has more than BTRFS_DEFAULT_MAX_ACTIVE_ZONES zones. But if the device has less than BTRFS_DEFAULT_MAX_ACTIVE_ZONES the internal max_active_zones limit will stay at 0, even if the device has zone resource limits. Furthermore, if the device has a total number of zones that is less than BTRFS_DEFAULT_MAX_ACTIVE_ZONE, max_active_zones should be set to at most the number of zones. Also move the max_active_zone calculation and setting into a dedicated helper, to shrink btrfs_get_dev_zone_info(). Fixes: 04147d8394e8 ("btrfs: zoned: limit active zones to max_open_zones") Reviewed-by: Damien Le Moal Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/zoned.c | 64 +++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 16818dbf48a46..b8fc97e33009b 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -352,12 +352,33 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info) return ret; } +static int btrfs_get_max_active_zones(struct btrfs_device *device, + struct btrfs_zoned_device_info *zone_info) +{ + struct block_device *bdev = device->bdev; + int max_active_zones; + + if (unlikely(zone_info->nr_zones < BTRFS_MIN_ACTIVE_ZONES)) { + btrfs_err(device->fs_info, "zoned: not enough zones to mount filesystem: %u < %d", + zone_info->nr_zones, BTRFS_MIN_ACTIVE_ZONES); + return -EINVAL; + } + + max_active_zones = min_not_zero(bdev_max_active_zones(bdev), + bdev_max_open_zones(bdev)); + if (max_active_zones == 0) + max_active_zones = min(zone_info->nr_zones / 4, + BTRFS_DEFAULT_MAX_ACTIVE_ZONES); + + zone_info->max_active_zones = max(max_active_zones, BTRFS_MIN_ACTIVE_ZONES); + return 0; +} + int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache) { struct btrfs_fs_info *fs_info = device->fs_info; struct btrfs_zoned_device_info *zone_info = NULL; struct block_device *bdev = device->bdev; - unsigned int max_active_zones; unsigned int nactive; sector_t nr_sectors; sector_t sector = 0; @@ -422,19 +443,9 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache) if (!IS_ALIGNED(nr_sectors, zone_sectors)) zone_info->nr_zones++; - max_active_zones = min_not_zero(bdev_max_active_zones(bdev), - bdev_max_open_zones(bdev)); - if (!max_active_zones && zone_info->nr_zones > BTRFS_DEFAULT_MAX_ACTIVE_ZONES) - max_active_zones = BTRFS_DEFAULT_MAX_ACTIVE_ZONES; - if (max_active_zones && max_active_zones < BTRFS_MIN_ACTIVE_ZONES) { - btrfs_err(fs_info, -"zoned: %s: max active zones %u is too small, need at least %u active zones", - rcu_dereference(device->name), max_active_zones, - BTRFS_MIN_ACTIVE_ZONES); - ret = -EINVAL; + ret = btrfs_get_max_active_zones(device, zone_info); + if (ret) goto out; - } - zone_info->max_active_zones = max_active_zones; zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL); if (!zone_info->seq_zones) { @@ -514,26 +525,29 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache) goto out; } - if (max_active_zones) { - if (unlikely(nactive > max_active_zones)) { - if (bdev_max_active_zones(bdev) == 0) { - max_active_zones = 0; - zone_info->max_active_zones = 0; - goto validate; - } + if (unlikely(nactive > zone_info->max_active_zones)) { + if (bdev_max_active_zones(bdev) > 0) { btrfs_err(device->fs_info, - "zoned: %u active zones on %s exceeds max_active_zones %u", - nactive, rcu_dereference(device->name), - max_active_zones); + "zoned: %u active zones on %s exceeds max_active_zones %u", + nactive, rcu_dereference(device->name), + zone_info->max_active_zones); ret = -EIO; goto out; } + + /* + * This is for backward compatibility with old filesystems that + * have a lot of active zones because the device doesn't report + * a maximum number of zones and we previously didn't care for + * the limit. + */ + zone_info->max_active_zones = 0; + } else { atomic_set(&zone_info->active_zones_left, - max_active_zones - nactive); + zone_info->max_active_zones - nactive); set_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &fs_info->flags); } -validate: /* Validate superblock log */ nr_zones = BTRFS_NR_SB_LOG_ZONES; for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { From f85410ebf20bba5880698837e8edfc8798536421 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 1 Apr 2026 10:21:53 +0800 Subject: [PATCH 1048/3297] btrfs: annotate lockless read of defrag_bytes in should_nocow() [ Upstream commit 89c0dc3de7a73e8aba5e9bfef543eee047a3d0d2 ] should_nocow() reads inode->defrag_bytes without holding inode->lock, while btrfs_set_delalloc_extent() and btrfs_clear_delalloc_extent() update it under that spinlock. This is a data race. The read is a quick check used to decide whether to fall back to COW for a NOCOW inode: if defrag_bytes is non-zero and the range is tagged EXTENT_DEFRAG, we force COW so that defragmentation can rewrite the extent. Reading a stale value is harmless because: - A missed increment may skip COW once, but the defrag pass will redo the extent later. - A stale non-zero may force an unnecessary COW, which is a minor efficiency loss, not a correctness issue. On 64-bit platforms an aligned u64 load is naturally atomic so tearing cannot happen. On 32-bit platforms u64 may tear, but we only test for zero vs non-zero, so the heuristic stays correct regardless. Use data_race() annotation. Fixes: 47059d930f0e ("Btrfs: make defragment work with nodatacow option") Signed-off-by: Cen Zhang [ Use data_race() instead of READ_ONCXE() ] Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index b972d4cba9802..d37e694b3b38c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2332,7 +2332,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode, static bool should_nocow(struct btrfs_inode *inode, u64 start, u64 end) { if (inode->flags & (BTRFS_INODE_NODATACOW | BTRFS_INODE_PREALLOC)) { - if (inode->defrag_bytes && + if (data_race(inode->defrag_bytes) && btrfs_test_range_bit_exists(&inode->io_tree, start, end, EXTENT_DEFRAG)) return false; return true; From 2aa37c8ef1092c6f088e23a90bffefc672831c09 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 26 May 2026 14:44:30 +0100 Subject: [PATCH 1049/3297] btrfs: fix deadlock cloning inline extent when using flushoncommit [ Upstream commit 532085d00eb54c074bdeae648b194765239f4d11 ] In commit b48c980b6a7e ("btrfs: fix deadlock between reflink and transaction commit when using flushoncommit") a deadlock was fixed between reflinks and transaction commits when the fs is mounted with the flushoncommit option. This happened when we had to copy an inline extent's data to the destination file. However the issue was fixed only for the case where the destination offset is 0, it missed the case when the offset is greater than zero. Fix this by ensuring we get i_size update whenever we copied an inline extent's data into the destination file. Syzbot reported this with the following trace: INFO: task kworker/u8:3:57 blocked for more than 143 seconds. Not tainted syzkaller #0 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:3 state:D stack:21600 pid:57 tgid:57 ppid:2 task_flags:0x4208160 flags:0x00080000 Workqueue: writeback wb_workfn (flush-btrfs-129) Call Trace: context_switch kernel/sched/core.c:5402 [inline] __schedule+0x16f9/0x5500 kernel/sched/core.c:7204 __schedule_loop kernel/sched/core.c:7283 [inline] schedule+0x164/0x360 kernel/sched/core.c:7298 wait_extent_bit fs/btrfs/extent-io-tree.c:905 [inline] btrfs_lock_extent_bits+0x59c/0x700 fs/btrfs/extent-io-tree.c:2008 btrfs_lock_extent fs/btrfs/extent-io-tree.h:152 [inline] btrfs_invalidate_folio+0x440/0xc00 fs/btrfs/inode.c:7718 extent_writepage fs/btrfs/extent_io.c:1848 [inline] extent_write_cache_pages fs/btrfs/extent_io.c:2552 [inline] btrfs_writepages+0x12f3/0x2410 fs/btrfs/extent_io.c:2684 do_writepages+0x32e/0x550 mm/page-writeback.c:2571 __writeback_single_inode+0x133/0x10e0 fs/fs-writeback.c:1764 writeback_sb_inodes+0x97f/0x1980 fs/fs-writeback.c:2056 wb_writeback+0x445/0xb00 fs/fs-writeback.c:2241 wb_do_writeback fs/fs-writeback.c:2388 [inline] wb_workfn+0x3fd/0xf20 fs/fs-writeback.c:2428 process_one_work+0x98b/0x1630 kernel/workqueue.c:3318 process_scheduled_works kernel/workqueue.c:3401 [inline] worker_thread+0xb49/0x1140 kernel/workqueue.c:3482 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 INFO: task syz.0.145:8523 blocked for more than 143 seconds. Not tainted syzkaller #0 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:syz.0.145 state:D stack:22752 pid:8523 tgid:8522 ppid:5850 task_flags:0x400140 flags:0x00080002 Call Trace: context_switch kernel/sched/core.c:5402 [inline] __schedule+0x16f9/0x5500 kernel/sched/core.c:7204 __schedule_loop kernel/sched/core.c:7283 [inline] schedule+0x164/0x360 kernel/sched/core.c:7298 wb_wait_for_completion+0x3e8/0x790 fs/fs-writeback.c:227 __writeback_inodes_sb_nr+0x24c/0x2d0 fs/fs-writeback.c:2847 try_to_writeback_inodes_sb+0x9a/0xc0 fs/fs-writeback.c:2895 btrfs_start_delalloc_flush fs/btrfs/transaction.c:2182 [inline] btrfs_commit_transaction+0x813/0x2fc0 fs/btrfs/transaction.c:2371 btrfs_sync_file+0xdf4/0x1230 fs/btrfs/file.c:1822 generic_write_sync include/linux/fs.h:2663 [inline] btrfs_do_write_iter+0x6a9/0x840 fs/btrfs/file.c:1473 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x629/0xba0 fs/read_write.c:688 ksys_write+0x156/0x270 fs/read_write.c:740 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f5a0bdece59 RSP: 002b:00007f5a0b446028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 00007f5a0c065fa0 RCX: 00007f5a0bdece59 RDX: 000000000000029f RSI: 0000200000000200 RDI: 0000000000000004 RBP: 00007f5a0be82d6f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f5a0c066038 R14: 00007f5a0c065fa0 R15: 00007ffe149206b8 INFO: task syz.0.145:8539 blocked for more than 143 seconds. Not tainted syzkaller #0 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:syz.0.145 state:D stack:23704 pid:8539 tgid:8522 ppid:5850 task_flags:0x400140 flags:0x00080002 Call Trace: context_switch kernel/sched/core.c:5402 [inline] __schedule+0x16f9/0x5500 kernel/sched/core.c:7204 __schedule_loop kernel/sched/core.c:7283 [inline] schedule+0x164/0x360 kernel/sched/core.c:7298 wait_current_trans+0x39f/0x590 fs/btrfs/transaction.c:536 start_transaction+0xbd8/0x1820 fs/btrfs/transaction.c:716 clone_copy_inline_extent fs/btrfs/reflink.c:299 [inline] btrfs_clone+0x1316/0x2540 fs/btrfs/reflink.c:574 btrfs_clone_files+0x271/0x3f0 fs/btrfs/reflink.c:795 btrfs_remap_file_range+0x76b/0x1320 fs/btrfs/reflink.c:948 vfs_clone_file_range+0x435/0x7b0 fs/remap_range.c:403 ioctl_file_clone fs/ioctl.c:239 [inline] ioctl_file_clone_range fs/ioctl.c:257 [inline] do_vfs_ioctl+0xe15/0x1540 fs/ioctl.c:544 __do_sys_ioctl fs/ioctl.c:595 [inline] __se_sys_ioctl+0x82/0x170 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f5a0bdece59 RSP: 002b:00007f5a0b425028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 00007f5a0c066090 RCX: 00007f5a0bdece59 RDX: 00002000000000c0 RSI: 000000004020940d RDI: 0000000000000004 RBP: 00007f5a0be82d6f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f5a0c066128 R14: 00007f5a0c066090 R15: 00007ffe149206b8 Reported-by: syzbot+c7443384724bb0f9e913@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/6a150a09.820a0220.e7972.0006.GAE@google.com/ Fixes: 05a5a7621ce6 ("Btrfs: implement full reflink support for inline extents") Reviewed-by: Boris Burkov Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/reflink.c | 101 +++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index b5f7b20c585a0..8356e3b74cd37 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -178,10 +178,12 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode, struct btrfs_drop_extents_args drop_args = { 0 }; int ret; struct btrfs_key key; + bool copied_inline_to_page = false; if (new_key->offset > 0) { ret = copy_inline_to_page(inode, new_key->offset, inline_data, size, datal, comp_type); + copied_inline_to_page = (ret == 0); goto out; } @@ -287,6 +289,60 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode, btrfs_abort_transaction(trans, ret); out: if (!ret && !trans) { + if (copied_inline_to_page && + new_key->offset + datal > i_size_read(&inode->vfs_inode)) { + /* + * If we copied the inline extent data to a page/folio + * beyond the i_size of the destination inode, then we + * need to increase the i_size before we start a + * transaction to update the inode item. This is to + * prevent a deadlock when the flushoncommit mount + * option is used, which happens like this: + * + * 1) Task A clones an inline extent from inode X to an + * offset of inode Y that is beyond Y's current + * i_size. This means we copied the inline extent's + * data to a folio of inode Y that is beyond its EOF, + * using the call above to copy_inline_to_page(); + * + * 2) Task B starts a transaction commit and calls + * btrfs_start_delalloc_flush() to flush delalloc; + * + * 3) The delalloc flushing sees the new dirty folio of + * inode Y and when it attempts to flush it, it ends + * up at extent_writepage() and sees that the offset + * of the folio is beyond the i_size of inode Y, so + * it attempts to invalidate the folio by calling + * folio_invalidate(), which ends up at btrfs' folio + * invalidate callback - btrfs_invalidate_folio(). + * There it tries to lock the folio's range in inode + * Y's extent io tree, but it blocks since it's + * currently locked by task A - during reflink we + * lock the inodes and the source and destination + * ranges after flushing all delalloc and waiting for + * ordered extent completion - after that we don't + * expect to have dirty folios in the ranges, the + * exception is if we have to copy an inline extent's + * data (because the destination offset is not zero); + * + * 4) Task A then does the 'goto out' below and attempts + * to start a transaction to update the inode item, + * and then it's blocked since the current + * transaction is in the TRANS_STATE_COMMIT_START + * state. Therefore task A has to wait for the + * current transaction to become unblocked (its + * state >= TRANS_STATE_UNBLOCKED). + * + * This leads to a deadlock - the task committing the + * transaction waiting for the delalloc flushing which + * is blocked during folio invalidation on the inode's + * extent lock and the reflink task waiting for the + * current transaction to be unblocked so that it can + * start a new one to update the inode item (while + * holding the extent lock). + */ + i_size_write(&inode->vfs_inode, new_key->offset + datal); + } /* * No transaction here means we copied the inline extent into a * page of the destination inode. @@ -319,50 +375,7 @@ static int clone_copy_inline_extent(struct btrfs_inode *inode, ret = copy_inline_to_page(inode, new_key->offset, inline_data, size, datal, comp_type); - - /* - * If we copied the inline extent data to a page/folio beyond the i_size - * of the destination inode, then we need to increase the i_size before - * we start a transaction to update the inode item. This is to prevent a - * deadlock when the flushoncommit mount option is used, which happens - * like this: - * - * 1) Task A clones an inline extent from inode X to an offset of inode - * Y that is beyond Y's current i_size. This means we copied the - * inline extent's data to a folio of inode Y that is beyond its EOF, - * using the call above to copy_inline_to_page(); - * - * 2) Task B starts a transaction commit and calls - * btrfs_start_delalloc_flush() to flush delalloc; - * - * 3) The delalloc flushing sees the new dirty folio of inode Y and when - * it attempts to flush it, it ends up at extent_writepage() and sees - * that the offset of the folio is beyond the i_size of inode Y, so - * it attempts to invalidate the folio by calling folio_invalidate(), - * which ends up at btrfs' folio invalidate callback - - * btrfs_invalidate_folio(). There it tries to lock the folio's range - * in inode Y's extent io tree, but it blocks since it's currently - * locked by task A - during reflink we lock the inodes and the - * source and destination ranges after flushing all delalloc and - * waiting for ordered extent completion - after that we don't expect - * to have dirty folios in the ranges, the exception is if we have to - * copy an inline extent's data (because the destination offset is - * not zero); - * - * 4) Task A then does the 'goto out' below and attempts to start a - * transaction to update the inode item, and then it's blocked since - * the current transaction is in the TRANS_STATE_COMMIT_START state. - * Therefore task A has to wait for the current transaction to become - * unblocked (its state >= TRANS_STATE_UNBLOCKED). - * - * This leads to a deadlock - the task committing the transaction - * waiting for the delalloc flushing which is blocked during folio - * invalidation on the inode's extent lock and the reflink task waiting - * for the current transaction to be unblocked so that it can start a - * a new one to update the inode item (while holding the extent lock). - */ - if (ret == 0 && new_key->offset + datal > i_size_read(&inode->vfs_inode)) - i_size_write(&inode->vfs_inode, new_key->offset + datal); + copied_inline_to_page = (ret == 0); goto out; } From 701ea71c17c925ddf42479c26ba686a6029ec54f Mon Sep 17 00:00:00 2001 From: KhaiWenTan Date: Fri, 24 Apr 2026 15:59:07 +0800 Subject: [PATCH 1050/3297] igc: skip RX timestamp header for frame preemption verification [ Upstream commit 38b7a274cf84af9b1f4b602b8e2741565b81947b ] When RX hardware timestamping is enabled, a 16-byte inline timestamp header is added to the start of the packet buffer, causing FPE handshake verification to fail. Because an incorrect packet buffer is passed to igc_fpe_handle_mpacket(), the mem_is_zero() check inspects the timestamp metadata instead of the actual mPacket payload. As a result, valid Verify/Response mPackets can be missed when inline RX timestamps are present. Pass pktbuf + pkt_offset to igc_fpe_handle_mpacket() so it inspects the actual mPacket payload instead of the timestamp header. Fixes: 5422570c0010 ("igc: add support for frame preemption verification") Co-developed-by: Faizal Rahim Signed-off-by: Faizal Rahim Signed-off-by: KhaiWenTan Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 104d6ab2ce5fa..ebc00ce2e3a6b 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -2649,7 +2649,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) } if (igc_fpe_is_pmac_enabled(adapter) && - igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf)) { + igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf + pkt_offset)) { /* Advance the ring next-to-clean */ igc_is_non_eop(rx_ring, rx_desc); cleaned_count++; From e6d83f877d5ab47202a16b6c321e724964caa709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 9 Jun 2026 09:03:56 -0300 Subject: [PATCH 1051/3297] ASoC: sma1307: Fix uevent string leaks in fault worker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a750ca72af72dde9744468fdca6eda0b698a1cfc ] sma1307_check_fault_worker() stores dynamically allocated uevent strings in envp[0]. Several fault conditions are checked in sequence, so a later fault can overwrite envp[0] before the final kfree() and leak the previous allocation. The same flow can leave an OT1 volume entry in envp[1] while envp[0] has been overwritten by a later non-OT1 fault, causing an inconsistent uevent payload. Use static STATUS strings and a stack buffer for the optional VOLUME entry. This removes the allocations from the worker and keeps VOLUME tied only to the OT1 events that produce it. Fixes: 576c57e6b4c1 ("ASoC: sma1307: Add driver for Iron Device SMA1307") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260609-asoc-sma1307-uevent-leak-v1-1-cd7f5b062ab7@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/sma1307.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index 1b5c13f505a88..c6b6cbadf202b 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1612,6 +1612,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) struct sma1307_priv *sma1307 = container_of(work, struct sma1307_priv, check_fault_work.work); unsigned int status1_val, status2_val; + char volume[sizeof("VOLUME=0x12345678")]; char *envp[3] = { NULL, NULL, NULL }; if (sma1307->tsdw_cnt) @@ -1627,7 +1628,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) if (~status1_val & SMA1307_OT1_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT1(Over Temperature Level 1)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1"); + envp[0] = "STATUS=OT1"; if (sma1307->sw_ot1_prot) { /* Volume control (Current Volume -3dB) */ if ((sma1307->cur_vol + 6) <= 0xFA) { @@ -1635,8 +1636,9 @@ static void sma1307_check_fault_worker(struct work_struct *work) regmap_write(sma1307->regmap, SMA1307_0A_SPK_VOL, sma1307->cur_vol); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + snprintf(volume, sizeof(volume), + "VOLUME=0x%02X", sma1307->cur_vol); + envp[1] = volume; } } sma1307->tsdw_cnt++; @@ -1645,48 +1647,53 @@ static void sma1307_check_fault_worker(struct work_struct *work) SMA1307_0A_SPK_VOL, sma1307->init_vol); sma1307->tsdw_cnt = 0; sma1307->cur_vol = sma1307->init_vol; - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR"); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + envp[0] = "STATUS=OT1_CLEAR"; + snprintf(volume, sizeof(volume), "VOLUME=0x%02X", + sma1307->cur_vol); + envp[1] = volume; } if (~status1_val & SMA1307_OT2_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT2(Over Temperature Level 2)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2"); + envp[0] = "STATUS=OT2"; + envp[1] = NULL; } if (status1_val & SMA1307_UVLO_STATUS) { dev_crit(sma1307->dev, "%s: UVLO(Under Voltage Lock Out)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO"); + envp[0] = "STATUS=UVLO"; + envp[1] = NULL; } if (status1_val & SMA1307_OVP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OVP_BST(Over Voltage Protection)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST"); + envp[0] = "STATUS=OVP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_SPK_STATUS) { dev_crit(sma1307->dev, "%s: OCP_SPK(Over Current Protect SPK)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK"); + envp[0] = "STATUS=OCP_SPK"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OCP_BST(Over Current Protect Boost)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST"); + envp[0] = "STATUS=OCP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_CLK_MON_STATUS) { dev_crit(sma1307->dev, "%s: CLK_FAULT(No clock input)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT"); + envp[0] = "STATUS=CLK_FAULT"; + envp[1] = NULL; } if (envp[0] != NULL) { if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp)) dev_err(sma1307->dev, "%s: Error sending uevent\n", __func__); - kfree(envp[0]); - kfree(envp[1]); } if (sma1307->check_fault_status) { From 37e85be551c4de3da45048ddd48f5aaee2e25939 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 5 Jun 2026 08:53:35 -0300 Subject: [PATCH 1052/3297] IB/mlx4: Fill in the access_flags if IB_MR_REREG_ACCESS is not specified [ Upstream commit bade9a3150d44ed20b8c6484c4c8a943b7289abb ] Sashiko noticed mlx4 was using whatever random access flags were provided when IB_MR_REREG_ACCESS is not used. Since IB_MR_REREG_TRANS needs access_flags it used the random ones which means it doesn't work sensibly if userspace provides only IB_MR_REREG_TRANS. Keep track of the current access_flag of the MR and use it if the user does not specify one. Also fixup a little confusion around mmr.access, it is the HW access flags so the convert_access() was missing. But nothing reads this by the time rereg_mr can happen. Fixes: 9376932d0c26 ("IB/mlx4_ib: Add support for user MR re-registration") Link: https://patch.msgid.link/r/0-v1-29ca7a402625+ddd6-mlx4_rereg_flags_jgg@nvidia.com Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx4/mlx4_ib.h | 1 + drivers/infiniband/hw/mlx4/mr.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 5df5b955114ee..44ef41aa7df9e 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -137,6 +137,7 @@ struct mlx4_ib_mr { dma_addr_t page_map; u32 npages; u32 max_pages; + int access_flags; struct mlx4_mr mmr; struct ib_umem *umem; size_t page_map_size; diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 56ff98a7ea03c..62485337aed6b 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -181,6 +181,7 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (err) goto err_mr; + mr->access_flags = access_flags; mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key; mr->ibmr.page_size = 1U << shift; @@ -241,6 +242,8 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, if (err) goto release_mpt_entry; + } else { + mr_access_flags = mmr->access_flags; } if (flags & IB_MR_REREG_TRANS) { @@ -282,8 +285,10 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start, * return a failure. But dereg_mr will free the resources. */ err = mlx4_mr_hw_write_mpt(dev->dev, &mmr->mmr, pmpt_entry); - if (!err && flags & IB_MR_REREG_ACCESS) - mmr->mmr.access = mr_access_flags; + if (!err && flags & IB_MR_REREG_ACCESS) { + mmr->access_flags = mr_access_flags; + mmr->mmr.access = convert_access(mr_access_flags); + } release_mpt_entry: mlx4_mr_hw_put_mpt(dev->dev, pmpt_entry); From da6f86ff4f2dd490bea52419a49e19680efd5847 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 19 Apr 2026 14:53:00 -0400 Subject: [PATCH 1053/3297] NFSD: Handle layout stid in nfsd4_drop_revoked_stid() [ Upstream commit 86b9898920a6d02b4149f4fef9efd77b8aa3b9ca ] nfsd4_drop_revoked_stid() has no SC_TYPE_LAYOUT case, so when a client sends FREE_STATEID for an admin-revoked layout stid, the default branch releases cl_lock and returns without unhashing or releasing the stid. The stid remains in the IDR and on the per-client list until the client is destroyed. Remove the layout stid from the per-client list and call nfs4_put_stid() to drop the creation reference. When the refcount reaches zero, nfsd4_free_layout_stateid() handles the remaining cleanup: cancelling the fence worker, removing from the per-file list, and freeing the slab object. Fixes: 1e33e1414bec ("nfsd: allow layout state to be admin-revoked.") 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 efa7e103cf190..c9d9a7d46e7b2 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -5049,6 +5049,7 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s) { struct nfs4_client *cl = s->sc_client; LIST_HEAD(reaplist); + struct nfs4_layout_stateid *ls; struct nfs4_ol_stateid *stp; struct nfs4_delegation *dp; bool unhashed; @@ -5074,6 +5075,12 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s) spin_unlock(&cl->cl_lock); nfs4_put_stid(s); break; + case SC_TYPE_LAYOUT: + ls = layoutstateid(s); + list_del_init(&ls->ls_perclnt); + spin_unlock(&cl->cl_lock); + nfs4_put_stid(s); + break; default: spin_unlock(&cl->cl_lock); } From ecb9be4fc8be04abe5bfd39218f118b2393635df Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 13:26:47 +0800 Subject: [PATCH 1054/3297] spi: meson-spifc: fix runtime PM leak on remove [ Upstream commit 606c0826bd90384a54571c0c5475ca41f50164ea ] pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error. meson_spifc_remove() uses it to resume the controller before disabling runtime PM, but never drops the usage counter again. Balance the get with pm_runtime_put_noidle() after disabling runtime PM, matching the teardown pattern used by other SPI controller drivers. Found by static analysis. I do not have hardware to test this. Fixes: c3e4bc5434d2 ("spi: meson: Add support for Amlogic Meson SPIFC") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260609052647.5-1-ruoyuw560@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-meson-spifc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index ef7efeaeee97e..e19ca48a2b653 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -352,6 +352,7 @@ static void meson_spifc_remove(struct platform_device *pdev) { pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM_SLEEP From 3d56f3fb201ff6c0d60d2ee3822ce7638cb39a83 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 29 May 2026 17:05:13 -0300 Subject: [PATCH 1055/3297] ASoC: codecs: aw88261: fix incorrect masks for boost regs [ Upstream commit 79c053a1ff9d3ab31cefbc791e8d7816ba830491 ] The boost-related register fields used in aw88261_reg_force_set use the exact same definitions as the rest of the fields, where the mask must be inverted when passing it to regmap_update_bits, but they weren't inverted here. Fixes: 028a2ae25691 ("ASoC: codecs: Add aw88261 amplifier driver") Signed-off-by: Val Packett Tested-by: Luca Weiss Link: https://patch.msgid.link/20260529200550.529719-7-val@packett.cool Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/aw88261.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 124c0a58d08bd..3270319c32a1f 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -283,22 +283,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261) if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) { /* set FORCE_PWM */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG, - AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + ~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); /* set BOOST_OS_WIDTH */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG, - AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + ~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); /* set BURST_LOOPR */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG, - AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + ~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); /* set RSQN_DLY */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG, - AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + ~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); /* set BURST_SSMODE */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG, - AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + ~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); /* set BST_BURST */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG, - AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + ~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); } else { dev_dbg(aw88261->aw_pa->dev, "needn't set reg value"); } From a2d0a57538fd0b3b3ab75d64bb64f4cd2fab13a2 Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Fri, 8 May 2026 17:46:59 +0800 Subject: [PATCH 1056/3297] vduse: hold vduse_lock across IDR lookup in open path [ Upstream commit e440e077748939839d9f76e24383b76b785f80ce ] vduse_dev_open() looks up struct vduse_dev through the IDR and then acquires dev->lock only after vduse_lock has been dropped. This leaves a window where a concurrent VDUSE_DESTROY_DEV can remove the same object from the IDR and free it before the open path locks the device, leading to a use-after-free. Close this race by keeping vduse_lock held until dev->lock has been acquired in the open path, matching the lock ordering already used by the destroy path. Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Signed-off-by: Qihang Tang Signed-off-by: Michael S. Tsirkin Message-ID: <20260508094659.94647-1-q.h.hack.winter@gmail.com> Signed-off-by: Sasha Levin --- drivers/vdpa/vdpa_user/vduse_dev.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index e7bced0b55422..458fe9b8addbc 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -1438,26 +1438,18 @@ static int vduse_dev_release(struct inode *inode, struct file *file) return 0; } -static struct vduse_dev *vduse_dev_get_from_minor(int minor) +static int vduse_dev_open(struct inode *inode, struct file *file) { + int ret = -EBUSY; struct vduse_dev *dev; mutex_lock(&vduse_lock); - dev = idr_find(&vduse_idr, minor); - mutex_unlock(&vduse_lock); - - return dev; -} - -static int vduse_dev_open(struct inode *inode, struct file *file) -{ - int ret; - struct vduse_dev *dev = vduse_dev_get_from_minor(iminor(inode)); - - if (!dev) + dev = idr_find(&vduse_idr, iminor(inode)); + if (!dev) { + mutex_unlock(&vduse_lock); return -ENODEV; + } - ret = -EBUSY; mutex_lock(&dev->lock); if (dev->connected) goto unlock; @@ -1467,6 +1459,7 @@ static int vduse_dev_open(struct inode *inode, struct file *file) file->private_data = dev; unlock: mutex_unlock(&dev->lock); + mutex_unlock(&vduse_lock); return ret; } From 1f5f94c6c6b2e4eaa5b45815509e21d0c6cfa81e Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Fri, 8 May 2026 15:58:21 +0800 Subject: [PATCH 1057/3297] vhost/vdpa: validate virtqueue index in mmap and fault paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 929e4f044621c8cc30b612fb74e1410bef09e41b ] vhost_vdpa_mmap() and vhost_vdpa_fault() use vma->vm_pgoff as a virtqueue index for get_vq_notification(), but they do not validate that the index is smaller than v->nvqs. The ioctl path already performs both a bounds check and array_index_nospec(), but the mmap/fault path only checks that the index fits in u16. This allows an out-of-range queue index to reach driver-specific get_vq_notification() callbacks. Fix this by extracting a unified vhost_vdpa_get_vq_notification() helper that validates the queue index against v->nvqs and applies array_index_nospec() before calling the driver callback. Both the mmap and fault paths use this helper, and the bounds checking is consolidated into a single location. From source inspection, the most defensible impact is out-of-bounds access in the callback path, potentially leading to invalid PFN remaps and crash/DoS. Fixes: ddd89d0a059d ("vhost_vdpa: support doorbell mapping via mmap") Acked-by: Eugenio Pérez Acked-by: Michael S. Tsirkin Signed-off-by: Qihang Tang Signed-off-by: Michael S. Tsirkin Message-ID: <20260508075821.92656-1-q.h.hack.winter@gmail.com> Signed-off-by: Sasha Levin --- drivers/vhost/vdpa.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 7e51eec842b8c..a6c6c28bf2d9d 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -1480,16 +1480,32 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep) } #ifdef CONFIG_MMU -static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf) +static int +vhost_vdpa_get_vq_notification(struct vhost_vdpa *v, unsigned long index, + struct vdpa_notification_area *notify) { - struct vhost_vdpa *v = vmf->vma->vm_file->private_data; struct vdpa_device *vdpa = v->vdpa; const struct vdpa_config_ops *ops = vdpa->config; + + if (index > 65535 || index >= v->nvqs) + return -EINVAL; + + index = array_index_nospec(index, v->nvqs); + + *notify = ops->get_vq_notification(vdpa, index); + + return 0; +} + +static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf) +{ + struct vhost_vdpa *v = vmf->vma->vm_file->private_data; struct vdpa_notification_area notify; struct vm_area_struct *vma = vmf->vma; - u16 index = vma->vm_pgoff; + unsigned long index = vma->vm_pgoff; - notify = ops->get_vq_notification(vdpa, index); + if (vhost_vdpa_get_vq_notification(v, index, ¬ify)) + return VM_FAULT_SIGBUS; return vmf_insert_pfn(vma, vmf->address & PAGE_MASK, PFN_DOWN(notify.addr)); } @@ -1512,8 +1528,6 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; if (vma->vm_flags & VM_READ) return -EINVAL; - if (index > 65535) - return -EINVAL; if (!ops->get_vq_notification) return -ENOTSUPP; @@ -1521,7 +1535,8 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma) * support the doorbell which sits on the page boundary and * does not share the page with other registers. */ - notify = ops->get_vq_notification(vdpa, index); + if (vhost_vdpa_get_vq_notification(v, index, ¬ify)) + return -EINVAL; if (notify.addr & (PAGE_SIZE - 1)) return -EINVAL; if (vma->vm_end - vma->vm_start != notify.size) From 79366023aa891ca31376021a7bccff6384ca1ff1 Mon Sep 17 00:00:00 2001 From: Jia Jia Date: Thu, 7 May 2026 20:08:01 +0800 Subject: [PATCH 1058/3297] virtio: rtc: tear down old virtqueues before restore [ Upstream commit 548d2208455f14e6121404c6e30e997bfe0cd264 ] virtio_device_restore() resets the device and restores the negotiated features before calling ->restore(). viortc_freeze() intentionally leaves the existing virtqueues in place so the alarm queue can still wake the system, but viortc_restore() immediately calls viortc_init_vqs() without first deleting those old queues. If virtqueue reinitialization fails on virtio-pci, the transport error path can run vp_del_vqs() against a newly allocated vp_dev->vqs array while vdev->vqs still contains the old virtqueues. vp_del_vqs() then looks up queue state through the new array and can dereference a NULL info pointer in vp_del_vq(), crashing the guest kernel during restore. This can also happen during a non-faulty reinitialization, when one of the vp_find_vqs_msix() attempts is unsuccessful before a later attempt would succeed. Delete the stale virtqueues before rebuilding them. If restore fails before virtio_device_ready(), reuse the remove path to stop the device. Once the device is ready, return errors directly instead of deleting the virtqueues again. Fixes: 0623c7592768 ("virtio_rtc: Add module and driver core") Signed-off-by: Jia Jia Reviewed-by: Peter Hilber Signed-off-by: Michael S. Tsirkin Message-ID: <20260507120801.3677552-1-physicalmtea@gmail.com> Signed-off-by: Sasha Levin --- drivers/virtio/virtio_rtc_driver.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc_driver.c index a57d5e06e19d2..4419735b0f0dc 100644 --- a/drivers/virtio/virtio_rtc_driver.c +++ b/drivers/virtio/virtio_rtc_driver.c @@ -1257,6 +1257,15 @@ static int viortc_init_vqs(struct viortc_dev *viortc) return 0; } +static void __viortc_remove(struct viortc_dev *viortc) +{ + struct virtio_device *vdev = viortc->vdev; + + viortc_clocks_deinit(viortc); + virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); +} + /** * viortc_probe() - probe a virtio_rtc virtio device * @vdev: virtio device @@ -1282,7 +1291,7 @@ static int viortc_probe(struct virtio_device *vdev) ret = viortc_init_vqs(viortc); if (ret) - return ret; + goto err_reset_vdev; virtio_device_ready(vdev); @@ -1329,10 +1338,7 @@ static void viortc_remove(struct virtio_device *vdev) { struct viortc_dev *viortc = vdev->priv; - viortc_clocks_deinit(viortc); - - virtio_reset_device(vdev); - vdev->config->del_vqs(vdev); + __viortc_remove(viortc); } static int viortc_freeze(struct virtio_device *dev) @@ -1353,9 +1359,11 @@ static int viortc_restore(struct virtio_device *dev) bool notify = false; int ret; + dev->config->del_vqs(dev); + ret = viortc_init_vqs(viortc); if (ret) - return ret; + goto err_remove; alarm_viortc_vq = &viortc->vqs[VIORTC_ALARMQ]; alarm_vq = alarm_viortc_vq->vq; @@ -1364,7 +1372,7 @@ static int viortc_restore(struct virtio_device *dev) ret = viortc_populate_vq(viortc, alarm_viortc_vq, VIORTC_ALARMQ_BUF_CAP, false); if (ret) - return ret; + goto err_remove; notify = virtqueue_kick_prepare(alarm_vq); } @@ -1372,8 +1380,12 @@ static int viortc_restore(struct virtio_device *dev) virtio_device_ready(dev); if (notify && !virtqueue_notify(alarm_vq)) - ret = -EIO; + return -EIO; + + return 0; +err_remove: + __viortc_remove(viortc); return ret; } From f9d922023445112cb56442c4aa057cdbc13dc11f Mon Sep 17 00:00:00 2001 From: Filip Hejsek Date: Mon, 23 Feb 2026 18:37:02 +0100 Subject: [PATCH 1059/3297] virtio_console: read size from config space during device init [ Upstream commit b3592a32b34f37874dc94aa1a0d15c4334ed86ca ] Previously, the size was only read upon receiving the config interrupt. This interrupt is sent when the size changes. However, we also need to read the initial size. Also make sure to only read the size from config if F_SIZE is enabled. Fixes: 9778829cffd4 ("virtio: console: Store each console's size in the console structure") Signed-off-by: Filip Hejsek Signed-off-by: Michael S. Tsirkin Message-ID: <20260223-virtio-console-fix-v1-1-0cf08303b428@gmail.com> Signed-off-by: Sasha Levin --- drivers/char/virtio_console.c | 52 +++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 088182e54debd..c355f6d392748 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1771,32 +1771,40 @@ static void config_intr(struct virtio_device *vdev) schedule_work(&portdev->config_work); } -static void config_work_handler(struct work_struct *work) +static void update_size_from_config(struct ports_device *portdev) { - struct ports_device *portdev; + struct virtio_device *vdev; + struct port *port; + u16 rows, cols; - portdev = container_of(work, struct ports_device, config_work); - if (!use_multiport(portdev)) { - struct virtio_device *vdev; - struct port *port; - u16 rows, cols; + vdev = portdev->vdev; - vdev = portdev->vdev; - virtio_cread(vdev, struct virtio_console_config, cols, &cols); - virtio_cread(vdev, struct virtio_console_config, rows, &rows); + /* + * We'll use this way of resizing only for legacy support. + * For multiport devices, use control messages to indicate + * console size changes so that it can be done per-port. + * + * Don't test F_SIZE at all if we're rproc: not a valid feature. + */ + if (is_rproc_serial(vdev) || + use_multiport(portdev) || + !virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + return; - port = find_port_by_id(portdev, 0); - set_console_size(port, rows, cols); + virtio_cread(vdev, struct virtio_console_config, cols, &cols); + virtio_cread(vdev, struct virtio_console_config, rows, &rows); - /* - * We'll use this way of resizing only for legacy - * support. For newer userspace - * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages - * to indicate console size changes so that it can be - * done per-port. - */ - resize_console(port); - } + port = find_port_by_id(portdev, 0); + set_console_size(port, rows, cols); + resize_console(port); +} + +static void config_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + + portdev = container_of(work, struct ports_device, config_work); + update_size_from_config(portdev); } static int init_vqs(struct ports_device *portdev) @@ -2054,6 +2062,8 @@ static int virtcons_probe(struct virtio_device *vdev) __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); + update_size_from_config(portdev); + return 0; free_chrdev: From 646614dcb16070745d65957adaddadfc000519a4 Mon Sep 17 00:00:00 2001 From: Zhang Tianci Date: Thu, 26 Feb 2026 19:55:49 +0800 Subject: [PATCH 1060/3297] vduse: Requeue failed read to send_list head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 373ec43ded742b2f3aecf14731ffe1a57f438f38 ] When copy_to_iter() fails in vduse_dev_read_iter(), put the message back at the head of send_list to preserve FIFO ordering and retry the oldest pending request first. Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Reported-by: Michael S. Tsirkin Suggested-by: Xie Yongji Signed-off-by: Zhang Tianci Reviewed-by: Xie Yongji Acked-by: Jason Wang Acked-by: Eugenio Pérez Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260226115550.1814-2-zhangtianci.1997@bytedance.com> Signed-off-by: Sasha Levin --- drivers/vdpa/vdpa_user/vduse_dev.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 458fe9b8addbc..ecc3c5b57634b 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -194,6 +194,12 @@ static void vduse_enqueue_msg(struct list_head *head, list_add_tail(&msg->list, head); } +static void vduse_enqueue_msg_head(struct list_head *head, + struct vduse_dev_msg *msg) +{ + list_add(&msg->list, head); +} + static void vduse_dev_broken(struct vduse_dev *dev) { struct vduse_dev_msg *msg, *tmp; @@ -354,7 +360,7 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) spin_lock(&dev->msg_lock); if (ret != size) { ret = -EFAULT; - vduse_enqueue_msg(&dev->send_list, msg); + vduse_enqueue_msg_head(&dev->send_list, msg); goto unlock; } vduse_enqueue_msg(&dev->recv_list, msg); From 321c73baf54d971ce3771fea275c98a247f7ee35 Mon Sep 17 00:00:00 2001 From: Qing Ming Date: Mon, 1 Jun 2026 18:43:00 +0800 Subject: [PATCH 1061/3297] vhost/net: complete zerocopy ubufs only once [ Upstream commit 8f6898fe80794f2d7c3d38c1158c806e4074a1c4 ] vhost-net initializes one ubuf_info per outstanding zerocopy TX descriptor and hands it to the backend socket. The networking stack may then clone a zerocopy skb before all skb references are released. For example, batman-adv fragmentation reaches skb_split(), which calls skb_zerocopy_clone() and increments the same ubuf_info refcount. vhost_zerocopy_complete() currently treats every ubuf callback as a completed vhost descriptor. It dereferences ubuf->ctx, writes the descriptor completion state, and drops the vhost_net_ubuf_ref even when the callback only releases a cloned skb reference. A backend reset can therefore wait for and free the vhost_net_ubuf_ref while another cloned skb still carries the same ubuf_info. A later completion then dereferences the freed ubufs pointer. KASAN reports the stale completion as: BUG: KASAN: slab-use-after-free in vhost_zerocopy_complete+0x1d7/0x1f0 BUG: KASAN: slab-use-after-free in vhost_zerocopy_complete+0x101/0x1f0 vhost_zerocopy_complete skb_copy_ubufs __dev_forward_skb2 veth_xmit The freed object was allocated from vhost_net_ioctl() while setting the backend and freed through kfree_rcu()/kvfree_rcu_bulk after backend removal, while delayed skb completion still reached vhost_zerocopy_complete(). Honor the generic ubuf_info refcount before touching vhost state, and run the vhost descriptor completion only for the final ubuf reference. This matches the msg_zerocopy_complete() ownership rule for cloned zerocopy skbs. Fixes: bab632d69ee4 ("vhost: vhost TX zero-copy support") Signed-off-by: Qing Ming Signed-off-by: Michael S. Tsirkin Message-ID: <20260601104300.197210-1-a0yami@mailbox.org> Signed-off-by: Sasha Levin --- drivers/vhost/net.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index e11b46a1c9374..b2b90732b1d59 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -392,13 +392,20 @@ static void vhost_zerocopy_signal_used(struct vhost_net *net, static void vhost_zerocopy_complete(struct sk_buff *skb, struct ubuf_info *ubuf_base, bool success) { - struct ubuf_info_msgzc *ubuf = uarg_to_msgzc(ubuf_base); - struct vhost_net_ubuf_ref *ubufs = ubuf->ctx; - struct vhost_virtqueue *vq = ubufs->vq; + struct ubuf_info_msgzc *ubuf; + struct vhost_net_ubuf_ref *ubufs; + struct vhost_virtqueue *vq; int cnt; - rcu_read_lock_bh(); + /* Only the final cloned skb reference completes the vhost descriptor. */ + if (!refcount_dec_and_test(&ubuf_base->refcnt)) + return; + + ubuf = uarg_to_msgzc(ubuf_base); + ubufs = ubuf->ctx; + vq = ubufs->vq; + rcu_read_lock_bh(); /* set len to mark this desc buffers done DMA */ vq->heads[ubuf->desc].len = success ? VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN; From 86e0b37738deab1456205ca89ecbb3693559b390 Mon Sep 17 00:00:00 2001 From: longlong yan Date: Fri, 5 Jun 2026 10:14:45 +0800 Subject: [PATCH 1062/3297] tools/virtio: check mmap return value in vringh_test [ Upstream commit ec6177dfe98b9be1c3ede6c0dfe4394ea2a76959 ] In parallel_test(), the return values of mmap() for both host_map and guest_map are not checked against MAP_FAILED. If mmap() fails, the subsequent code will dereference the invalid pointer, leading to a segmentation fault. Add MAP_FAILED checks after both mmap() calls, using err() to report the error and exit, consistent with the existing error handling style in this file (e.g., the open() call on line 149). Fixes: 1515c5ce26ae ("tools/virtio: add vring_test.") Signed-off-by: longlong yan Signed-off-by: Michael S. Tsirkin Message-ID: <20260605021446.1611-1-yanlonglong@kylinos.cn> Signed-off-by: Sasha Levin --- tools/virtio/vringh_test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index b9591223437a6..5ea6d29bc992d 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c @@ -159,7 +159,12 @@ static int parallel_test(u64 features, /* Parent and child use separate addresses, to check our mapping logic! */ host_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (host_map == MAP_FAILED) + err(1, "mmap host_map"); + guest_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (guest_map == MAP_FAILED) + err(1, "mmap guest_map"); pipe_ret = pipe(to_guest); assert(!pipe_ret); From 54556d53943823df656bc27f388fc6771d20930c Mon Sep 17 00:00:00 2001 From: Srujana Challa Date: Tue, 24 Feb 2026 15:22:23 +0530 Subject: [PATCH 1063/3297] vdpa/octeon_ep: Fix PF->VF mailbox data address calculation [ Upstream commit 74dc530f4c505d61f0f3620e59fe56c325ae3437 ] The mailbox address was computed assuming 1 ring per VF. Read the actual rings-per-VF from OCTEP_EPF_RINFO and use it when calculating OCTEP_PF_MBOX_DATA offsets, fixing VF initialization when rings per VF > 1. Fixes: 8b6c724cdab8 ("virtio: vdpa: vDPA driver for Marvell OCTEON DPU devices") Signed-off-by: Srujana Challa Signed-off-by: Michael S. Tsirkin Message-ID: <20260224095226.1001151-2-schalla@marvell.com> Signed-off-by: Sasha Levin --- drivers/vdpa/octeon_ep/octep_vdpa_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/vdpa/octeon_ep/octep_vdpa_main.c b/drivers/vdpa/octeon_ep/octep_vdpa_main.c index 31a02e7fd7f27..9946480ee7046 100644 --- a/drivers/vdpa/octeon_ep/octep_vdpa_main.c +++ b/drivers/vdpa/octeon_ep/octep_vdpa_main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (C) 2024 Marvell. */ +#include #include #include #include @@ -722,6 +723,8 @@ static int octep_sriov_enable(struct pci_dev *pdev, int num_vfs) bool done = false; int index = 0; int ret, i; + u8 rpvf; + u64 val; ret = pci_enable_sriov(pdev, num_vfs); if (ret) @@ -741,9 +744,11 @@ static int octep_sriov_enable(struct pci_dev *pdev, int num_vfs) } } + val = readq(addr + OCTEP_EPF_RINFO(0)); + rpvf = FIELD_GET(GENMASK_ULL(35, 32), val); if (done) { for (i = 0; i < pf->enabled_vfs; i++) - writeq(OCTEP_DEV_READY_SIGNATURE, addr + OCTEP_PF_MBOX_DATA(i)); + writeq(OCTEP_DEV_READY_SIGNATURE, addr + OCTEP_PF_MBOX_DATA(i * rpvf)); } return num_vfs; From 3ef0cfa77a3d526591be069850d186c255e3f0cc Mon Sep 17 00:00:00 2001 From: Srujana Challa Date: Tue, 24 Feb 2026 15:22:26 +0530 Subject: [PATCH 1064/3297] vdpa/octeon_ep: fix IRQ-to-ring mapping in interrupt handler [ Upstream commit 0d21a1d6375a05274291e32c1ab7cd57dbb69513 ] Look up the IRQ index in oct_hw->irqs instead of assuming irq - irqs[0]. This supports non-contiguous IRQ numbers and avoids incorrect ring indexing when irqs[0] is not the base. Fixes: 26f8ce06af64 ("vdpa/octeon_ep: enable support for multiple interrupts per device") Signed-off-by: Srujana Challa Signed-off-by: Michael S. Tsirkin Message-ID: <20260224095226.1001151-5-schalla@marvell.com> Signed-off-by: Sasha Levin --- drivers/vdpa/octeon_ep/octep_vdpa_main.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/vdpa/octeon_ep/octep_vdpa_main.c b/drivers/vdpa/octeon_ep/octep_vdpa_main.c index 9946480ee7046..df8af6c1454cc 100644 --- a/drivers/vdpa/octeon_ep/octep_vdpa_main.c +++ b/drivers/vdpa/octeon_ep/octep_vdpa_main.c @@ -48,7 +48,7 @@ static struct octep_hw *vdpa_to_octep_hw(struct vdpa_device *vdpa_dev) static irqreturn_t octep_vdpa_intr_handler(int irq, void *data) { struct octep_hw *oct_hw = data; - int i; + int i, start_ring_idx = -1; /* Each device has multiple interrupts (nb_irqs) shared among rings * (nr_vring). Device interrupts are mapped to the rings in a @@ -61,7 +61,16 @@ static irqreturn_t octep_vdpa_intr_handler(int irq, void *data) * 7 -> 7, 15, 23, 31, 39, 47, 55, 63; */ - for (i = irq - oct_hw->irqs[0]; i < oct_hw->nr_vring; i += oct_hw->nb_irqs) { + for (i = 0; i < oct_hw->nb_irqs; i++) { + if (oct_hw->irqs[i] == irq) { + start_ring_idx = i; + break; + } + } + if (start_ring_idx == -1) + return IRQ_NONE; + + for (i = start_ring_idx; i < oct_hw->nr_vring; i += oct_hw->nb_irqs) { if (ioread8(oct_hw->vqs[i].cb_notify_addr)) { /* Acknowledge the per ring notification to the device */ iowrite8(0, oct_hw->vqs[i].cb_notify_addr); From 8b55e7ec116ca3466cacf80507481ba63cd8e670 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jun 2026 10:34:30 +0100 Subject: [PATCH 1065/3297] ASoC: cs35l56: Fix missing calls to wm_adsp2_remove() [ Upstream commit 85f7bf03632bfcdd6cedfb3945b7e387d9487d73 ] Call wm_adsp2_remove() in cs35l56_remove() and the error path of cs35l56_common_probe(). Depends on commit 7d3fb78b5503 ("ASoC: wm_adsp: Fix NULL dereference when removing firmware controls"). The call to wm_halo_init() during driver probe should be paired with a call to wm_adsp2_remove() but this was missing. The consequence would be a memory leak of the control lists in the cs_dsp driver. Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260610093432.557375-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 193adba0cd1a4..f6d61bdfd24ef 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1437,11 +1437,14 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); - goto err; + goto err_remove_wm_adsp; } return 0; +err_remove_wm_adsp: + wm_adsp2_remove(&cs35l56->dsp); + err: gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); @@ -1544,6 +1547,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56) destroy_workqueue(cs35l56->dsp_wq); + wm_adsp2_remove(&cs35l56->dsp); + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); pm_runtime_suspend(cs35l56->base.dev); pm_runtime_disable(cs35l56->base.dev); From 88cb304c0be0c67b25314ed4583657680370f874 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 10 Jun 2026 11:55:56 +0100 Subject: [PATCH 1066/3297] ASoC: cs35l56: Don't leave parent IRQ disabled if system_suspend fails [ Upstream commit 53cebeb017164254cde5e31c94d8deef9e4fff97 ] In cs35l56_system_suspend() re-enable the parent IRQ if the call to pm_runtime_force_suspend() returns an error. Fixes: f9dc6b875ec0 ("ASoC: cs35l56: Add basic system suspend handling") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260610105556.612830-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index f6d61bdfd24ef..7b57ef2336ea8 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1057,6 +1057,7 @@ static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev) int cs35l56_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; dev_dbg(dev, "system_suspend\n"); @@ -1072,7 +1073,11 @@ int cs35l56_system_suspend(struct device *dev) if (cs35l56->base.irq) disable_irq(cs35l56->base.irq); - return pm_runtime_force_suspend(dev); + ret = pm_runtime_force_suspend(dev); + if ((ret < 0) && cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + return ret; } EXPORT_SYMBOL_GPL(cs35l56_system_suspend); From 484b3b9aa79867dfff53c99c64309d06885bfde6 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Thu, 4 Jun 2026 15:36:47 +0800 Subject: [PATCH 1067/3297] ext4: fix ERR_PTR(0) in ext4_mkdir() [ Upstream commit 8e1c43af7cf5091d99db38b7c8129e394d7f45b5 ] When mkdir succeeds, ext4_mkdir() returns ERR_PTR(0) which is incorrect. It should return NULL instead for success and ERR_PTR() only with negative error codes for failure. Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Signed-off-by: Hongling Zeng Reviewed-by: Jan Kara Reviewed-by: Baokun Li Link: https://patch.msgid.link/20260604073647.211279-1-zenghongling@kylinos.cn Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 2cd36f59c9e36..2e35453a56f28 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3049,7 +3049,7 @@ static struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir, out_retry: if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; } /* From 6b2c271d2c394194159cfbd7fc26a2f6b97a8214 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:26 +0200 Subject: [PATCH 1068/3297] tools: missed broadcast_neigh if_link uapi header [ Upstream commit 0134432215f0e0d4526544ac63dabe20e8a6951e ] Add missing IFLA_BOND_BROADCAST_NEIGH in if_link uapi header. Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-2-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Stable-dep-of: 32b0b8953343 ("bonding: 3ad: add lacp_strict configuration knob") Signed-off-by: Sasha Levin --- tools/include/uapi/linux/if_link.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 7e46ca4cd31bb..97a2d4411534a 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -1526,6 +1526,7 @@ enum { IFLA_BOND_MISSED_MAX, IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, + IFLA_BOND_BROADCAST_NEIGH, __IFLA_BOND_MAX, }; From 47636f0a70b3637621640e7681eb296047794f35 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:27 +0200 Subject: [PATCH 1069/3297] netlink: specs: rt-link: missed broadcast-neigh [ Upstream commit 363037983cc503eb0b5a5c0ab80bf1434cfd168a ] Add missed broadcast-neigh. Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-3-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Stable-dep-of: 32b0b8953343 ("bonding: 3ad: add lacp_strict configuration knob") Signed-off-by: Sasha Levin --- Documentation/netlink/specs/rt-link.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index 2a23e9699c0b6..0a1f957961f80 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1336,6 +1336,9 @@ attribute-sets: - name: coupled-control type: u8 + - + name: broadcast-neigh + type: u8 - name: bond-ad-info-attrs name-prefix: ifla-bond-ad-info- From cb20a9b50efe06620936cf3a761c37c299abdf9f Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:28 +0200 Subject: [PATCH 1070/3297] bonding: 3ad: add lacp_strict configuration knob [ Upstream commit 32b0b8953343eaceaa816a9ead3b6bb66355c64e ] When an 802.3ad (LACP) bonding interface has no slaves in the collecting/distributing state, the bonding master still reports carrier as up as long as at least 'min_links' slaves have carrier. In this situation, only one slave is effectively used for TX/RX, while traffic received on other slaves is dropped. Upper-layer daemons therefore consider the interface operational, even though traffic may be blackholed if the lack of LACP negotiation means the partner is not ready to deal with traffic. Introduce a configuration knob to control this behavior. It allows the bonding master to assert carrier only when at least 'min_links' slaves are in Collecting_Distributing state. The default mode preserves the existing behavior. This patch only introduces the knob; its behavior is implemented in the subsequent commit. Fixes: 655f8919d549 ("bonding: add min links parameter to 802.3ad") Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-4-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/netlink/specs/rt-link.yaml | 3 +++ Documentation/networking/bonding.rst | 23 +++++++++++++++++++++ drivers/net/bonding/bond_main.c | 1 + drivers/net/bonding/bond_netlink.c | 16 +++++++++++++++ drivers/net/bonding/bond_options.c | 26 ++++++++++++++++++++++++ include/net/bond_options.h | 1 + include/net/bonding.h | 1 + include/uapi/linux/if_link.h | 1 + tools/include/uapi/linux/if_link.h | 1 + 9 files changed, 73 insertions(+) diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index 0a1f957961f80..ae4db7e032770 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1339,6 +1339,9 @@ attribute-sets: - name: broadcast-neigh type: u8 + - + name: lacp-strict + type: u8 - name: bond-ad-info-attrs name-prefix: ifla-bond-ad-info- diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index e700bf1d095c3..33ca5afafdf6d 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -619,6 +619,29 @@ min_links aggregator cannot be active without at least one available link, setting this option to 0 or to 1 has the exact same effect. +lacp_strict + + Specifies the fallback behavior of a bonding when LACP negotiation + fails on all slave links, i.e. when no slave is in the + Collecting_Distributing state, while at least `min_links` link still + reports carrier up. + + This option is only applicable to 802.3ad mode (mode 4). + + Valid values are: + + off or 0 + One interface of the bond is selected to be active, in order to + facilitate communication with peer devices that do not implement + LACP. + + on or 1 + Interfaces are only permitted to be made active if they have an + active LACP partner and have successfully reached + Collecting_Distributing state. + + The default value is 0 (off). + mode Specifies one of the bonding policies. The default is diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 00df78d497fad..e38cae22fe025 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -6422,6 +6422,7 @@ static int __init bond_check_params(struct bond_params *params) params->ad_user_port_key = ad_user_port_key; params->coupled_control = 1; params->broadcast_neighbor = 0; + params->lacp_strict = 0; if (packets_per_slave > 0) { params->reciprocal_packets_per_slave = reciprocal_value(packets_per_slave); diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index c7d3e0602c831..08d4d0814f67a 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -143,6 +143,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_NS_IP6_TARGET] = { .type = NLA_NESTED }, [IFLA_BOND_COUPLED_CONTROL] = { .type = NLA_U8 }, [IFLA_BOND_BROADCAST_NEIGH] = { .type = NLA_U8 }, + [IFLA_BOND_LACP_STRICT] = { .type = NLA_U8 }, }; static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = { @@ -599,6 +600,16 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[], return err; } + if (data[IFLA_BOND_LACP_STRICT]) { + int fallback_mode = nla_get_u8(data[IFLA_BOND_LACP_STRICT]); + + bond_opt_initval(&newval, fallback_mode); + err = __bond_opt_set(bond, BOND_OPT_LACP_STRICT, &newval, + data[IFLA_BOND_LACP_STRICT], extack); + if (err) + return err; + } + return 0; } @@ -671,6 +682,7 @@ static size_t bond_get_size(const struct net_device *bond_dev) nla_total_size(sizeof(struct in6_addr)) * BOND_MAX_NS_TARGETS + nla_total_size(sizeof(u8)) + /* IFLA_BOND_COUPLED_CONTROL */ nla_total_size(sizeof(u8)) + /* IFLA_BOND_BROADCAST_NEIGH */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_LACP_STRICT */ 0; } @@ -838,6 +850,10 @@ static int bond_fill_info(struct sk_buff *skb, bond->params.broadcast_neighbor)) goto nla_put_failure; + if (nla_put_u8(skb, IFLA_BOND_LACP_STRICT, + bond->params.lacp_strict)) + goto nla_put_failure; + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info info; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index adc216df43459..08dc024c6f2f3 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -67,6 +67,8 @@ static int bond_option_lacp_active_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_lacp_rate_set(struct bonding *bond, const struct bond_opt_value *newval); +static int bond_option_lacp_strict_set(struct bonding *bond, + const struct bond_opt_value *newval); static int bond_option_ad_select_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_queue_id_set(struct bonding *bond, @@ -161,6 +163,12 @@ static const struct bond_opt_value bond_lacp_rate_tbl[] = { { NULL, -1, 0}, }; +static const struct bond_opt_value bond_lacp_strict_tbl[] = { + { "off", 0, BOND_VALFLAG_DEFAULT}, + { "on", 1, 0}, + { NULL, -1, 0 } +}; + static const struct bond_opt_value bond_ad_select_tbl[] = { { "stable", BOND_AD_STABLE, BOND_VALFLAG_DEFAULT}, { "bandwidth", BOND_AD_BANDWIDTH, 0}, @@ -362,6 +370,14 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = { .values = bond_lacp_rate_tbl, .set = bond_option_lacp_rate_set }, + [BOND_OPT_LACP_STRICT] = { + .id = BOND_OPT_LACP_STRICT, + .name = "lacp_strict", + .desc = "Define the LACP fallback mode when no slaves have negotiated", + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_8023AD)), + .values = bond_lacp_strict_tbl, + .set = bond_option_lacp_strict_set + }, [BOND_OPT_MINLINKS] = { .id = BOND_OPT_MINLINKS, .name = "min_links", @@ -1683,6 +1699,16 @@ static int bond_option_lacp_rate_set(struct bonding *bond, return 0; } +static int bond_option_lacp_strict_set(struct bonding *bond, + const struct bond_opt_value *newval) +{ + netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n", + newval->string, newval->value); + bond->params.lacp_strict = newval->value; + + return 0; +} + static int bond_option_ad_select_set(struct bonding *bond, const struct bond_opt_value *newval) { diff --git a/include/net/bond_options.h b/include/net/bond_options.h index e6eedf23aea1a..52b966e927938 100644 --- a/include/net/bond_options.h +++ b/include/net/bond_options.h @@ -79,6 +79,7 @@ enum { BOND_OPT_COUPLED_CONTROL, BOND_OPT_BROADCAST_NEIGH, BOND_OPT_ACTOR_PORT_PRIO, + BOND_OPT_LACP_STRICT, BOND_OPT_LAST }; diff --git a/include/net/bonding.h b/include/net/bonding.h index 99c1bdadcd11a..45069ed6e527f 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -132,6 +132,7 @@ struct bond_params { int peer_notif_delay; int lacp_active; int lacp_fast; + int lacp_strict; unsigned int min_links; int ad_select; char primary[IFNAMSIZ]; diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 69f19759b79d5..857b83a04b28e 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -1538,6 +1538,7 @@ enum { IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, IFLA_BOND_BROADCAST_NEIGH, + IFLA_BOND_LACP_STRICT, __IFLA_BOND_MAX, }; diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 97a2d4411534a..757ce5e9426e9 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -1527,6 +1527,7 @@ enum { IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, IFLA_BOND_BROADCAST_NEIGH, + IFLA_BOND_LACP_STRICT, __IFLA_BOND_MAX, }; From f0ada4846d11b08996b0c68b6dc77429eb2cf7da Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:29 +0200 Subject: [PATCH 1071/3297] bonding: 3ad: fix carrier when no usable slaves [ Upstream commit 0bd695db23c6262e9cb980017a6273925172ec5b ] Apply the "lacp_strict" configuration from the previous commit. "lacp_strict" mode "on" asserts that the bonding master carrier is up only when at least 'min_links' slaves are in the Collecting_Distributing state. Fixes: 655f8919d549 ("bonding: add min links parameter to 802.3ad") Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-5-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_3ad.c | 26 +++++++++++++++++++++++--- drivers/net/bonding/bond_options.c | 1 + 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index f1ef99f7515b0..bc8efdea5c290 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -733,6 +733,21 @@ static void __set_agg_ports_ready(struct aggregator *aggregator, int val) } } +static int __agg_usable_ports(struct aggregator *agg) +{ + struct port *port; + int valid = 0; + + for (port = agg->lag_ports; port; + port = port->next_port_in_aggregator) { + if (port->actor_oper_port_state & LACP_STATE_COLLECTING && + port->actor_oper_port_state & LACP_STATE_DISTRIBUTING) + valid++; + } + + return valid; +} + static int __agg_active_ports(struct aggregator *agg) { struct port *port; @@ -1164,10 +1179,10 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) switch (port->sm_mux_state) { case AD_MUX_DETACHED: port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; - ad_disable_collecting_distributing(port, - update_slave_arr); port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; + ad_disable_collecting_distributing(port, + update_slave_arr); port->ntt = true; break; case AD_MUX_WAITING: @@ -2090,6 +2105,7 @@ static void ad_disable_distributing(struct port *port, bool *update_slave_arr) port->actor_port_number, aggregator->aggregator_identifier); __disable_distributing_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs an update */ *update_slave_arr = true; } @@ -2113,6 +2129,7 @@ static void ad_enable_collecting_distributing(struct port *port, port->actor_port_number, aggregator->aggregator_identifier); __enable_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs update */ *update_slave_arr = true; /* Should notify peers if possible */ @@ -2136,6 +2153,7 @@ static void ad_disable_collecting_distributing(struct port *port, port->actor_port_number, aggregator->aggregator_identifier); __disable_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs an update */ *update_slave_arr = true; } @@ -2815,7 +2833,9 @@ int bond_3ad_set_carrier(struct bonding *bond) active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator)); if (active) { /* are enough slaves available to consider link up? */ - if (__agg_active_ports(active) < bond->params.min_links) { + if ((bond->params.lacp_strict ? __agg_usable_ports(active) + : __agg_active_ports(active)) < + bond->params.min_links) { if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); goto out; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 08dc024c6f2f3..1b2cb2c80d916 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1705,6 +1705,7 @@ static int bond_option_lacp_strict_set(struct bonding *bond, netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n", newval->string, newval->value); bond->params.lacp_strict = newval->value; + bond_3ad_set_carrier(bond); return 0; } From c998a09c7144e0bcf7c1d1b57b0b9f8bcb866939 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 3 Jun 2026 17:03:30 +0200 Subject: [PATCH 1072/3297] bonding: 3ad: fix mux port state on oper down [ Upstream commit 807afc7544b865d4d09068a415fd5b71bf5665cc ] When the bonding interface has carrier down due to the absence of usable slaves and a slave transitions from down to up, the bonding interface briefly goes carrier up, then down again, and finally up once LACP negotiates collecting and distributing on the port. When lacp_strict mode is on, the interface should not transition to carrier up until LACP negotiation is complete. This happens because the actor and partner port states remain in Collecting_Distributing when the port goes down. When the port comes back up, it temporarily remains in this state until LACP renegotiation occurs. Previously this was mostly cosmetic, but since the bonding carrier state may depend on the LACP negotiation state, it causes the interface to flap. According to IEEE 802.3ad-2000 and IEEE 802.1ax-2014, Collecting and Distributing should be reset when a port goes down: - In the Receive state machine, port_enabled == FALSE causes a transition to the PORT_DISABLED state, which is expected to clear Partner_Oper_Port_State.Synchronization. - In the Mux state machine, Partner_Oper_Port_State.Synchronization == FALSE causes a transition to the ATTACHED state, which disables Collecting and Distributing. However, Partner_Oper_Port_State.Synchronization is not cleared in the PORT_DISABLED state. Clear Partner_Oper_Port_State.Synchronization in the Receive PORT_DISABLED state. Fixes: 655f8919d549 ("bonding: add min links parameter to 802.3ad") Signed-off-by: Louis Scalbert Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260603150331.1919611-6-louis.scalbert@6wind.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_3ad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index bc8efdea5c290..7148dd19710a8 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1322,6 +1322,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) fallthrough; case AD_RX_PORT_DISABLED: port->sm_vars &= ~AD_PORT_MATCHED; + port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION; break; case AD_RX_LACP_DISABLED: port->sm_vars &= ~AD_PORT_SELECTED; From f00f5c0dd55319bc33b76f72c853bda0e0a32eda Mon Sep 17 00:00:00 2001 From: Aditya Prakash Srivastava Date: Mon, 8 Jun 2026 06:52:27 +0000 Subject: [PATCH 1073/3297] ext4: fix kernel BUG in ext4_write_inline_data_end [ Upstream commit ad09aa45965d3fafaf9963bc78109b73c0f9ac8d ] When the data=journal mount option is used, the ext4_journalled_write_end() function incorrectly calls ext4_write_inline_data_end() without checking if the EXT4_STATE_MAY_INLINE_DATA flag is still set on the inode. If a previous attempt to convert the inline data to an extent failed (e.g. due to ENOSPC), the EXT4_STATE_MAY_INLINE_DATA flag is cleared, but the EXT4_INODE_INLINE_DATA flag remains set. In this scenario, the next call to ext4_write_begin() will not prepare the inline data xattr for writing, but ext4_journalled_write_end() will incorrectly attempt to write to it, triggering a BUG_ON(pos + len > EXT4_I(inode)->i_inline_size) in ext4_write_inline_data() since i_inline_size was not expanded. Fix this by ensuring that ext4_journalled_write_end() only calls ext4_write_inline_data_end() if the EXT4_STATE_MAY_INLINE_DATA flag is set, mirroring the behavior of ext4_write_end() and ext4_da_write_end(). Reported-by: syzbot+0c89d865531d053abb2d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0c89d865531d053abb2d Fixes: 3fdcfb668fd7 ("ext4: add journalled write support for inline data") Signed-off-by: Aditya Prakash Srivastava Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260608065227.3018-1-aditya.ansh182@gmail.com Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin --- fs/ext4/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 3ed0c2656e2e1..c38ca1441b25f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1561,7 +1561,8 @@ static int ext4_journalled_write_end(const struct kiocb *iocb, BUG_ON(!ext4_handle_valid(handle)); - if (ext4_has_inline_data(inode)) + if (ext4_has_inline_data(inode) && + ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) return ext4_write_inline_data_end(inode, pos, len, copied, folio); From 91ad3088ee1b7c939f95bfc586231e55e2c06a94 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 9 Jun 2026 22:18:31 -0700 Subject: [PATCH 1074/3297] selftests/bpf: Fix bpf_iter/task_vma test [ Upstream commit 2e8ad1ff712d2a397e407c9fde60901f68d077dc ] For selftest bpf_iter/task_vma, I got a failure like below on my qemu run: test_task_vma_common:FAIL:compare_output unexpected compare_output: actual '561593546000-561593585000r--p0000000000:241256579534/root/devshare/bpf-next/tools/testing/selftests/bpf/test_progs' != expected '561593546000-561593585000r--p0000000000:245551546830/root/devshare/bpf-next/tools/testing/selftests/bpf/test_progs' Further debugging found out file->f_inode->i_ino value may exceed 32bit, e.g., i_ino = 0x14c2eae35, but the format string is '%u'. This caused inode mismatch between bpf iter and proc result. Fix the issue by using format string '%llu' to accommodate 64bit i_ino. Fixes: e8168840e16c ("selftests/bpf: Add test for bpf_iter_task_vma") Signed-off-by: Yonghong Song Acked-by: Leon Hwang Link: https://lore.kernel.org/r/20260610051831.1346659-1-yonghong.song@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c b/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c index d64ba7ddaed54..d7fb561ed4fb9 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_task_vmas.c @@ -52,7 +52,7 @@ SEC("iter/task_vma") int proc_maps(struct bpf_iter__task_vma *ctx) bpf_d_path(&file->f_path, d_path_buf, D_PATH_BUF_SIZE); BPF_SEQ_PRINTF(seq, "%08llx ", vma->vm_pgoff << 12); - BPF_SEQ_PRINTF(seq, "%02x:%02x %u", MAJOR(dev), MINOR(dev), + BPF_SEQ_PRINTF(seq, "%02x:%02x %llu", MAJOR(dev), MINOR(dev), file->f_inode->i_ino); BPF_SEQ_PRINTF(seq, "\t%s\n", d_path_buf); } else { From 42a9a76f314ef1eb54039fc3e2a63d7773d5ec06 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 10:12:38 -0700 Subject: [PATCH 1075/3297] cxl/test: Fix integer overflow in mock LSA bounds checks [ Upstream commit 81eafcada109b653977c4dfbd2b6a72470025a01 ] Pre-existing issue discovered by sashiko-bot. mock_get_lsa() and mock_set_lsa() validate the requested LSA range with "offset + length > LSA_SIZE". Both offset and length are u32 and, in mock_get_lsa(), both are taken directly from the user-supplied payload. The addition is evaluated modulo 2^32, so a large offset combined with a small length wraps around and passes the check. Rewrite the checks to first bound offset, then compare length against the remaining LSA size. Suggested-by: sashiko-bot Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Link: https://lore.kernel.org/linux-cxl/20260605143748.235271F00893@smtp.kernel.org/ Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index d533481672b78..4474d5d74faf2 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1054,7 +1054,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(get_lsa->offset); length = le32_to_cpu(get_lsa->length); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; if (length > cmd->size_out) return -EINVAL; @@ -1074,7 +1074,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(set_lsa->offset); length = cmd->size_in - sizeof(*set_lsa); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; memcpy(lsa + offset, &set_lsa->data[0], length); From d2acea4f4747576547566869186f1ef71884e2de Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 11:44:26 -0700 Subject: [PATCH 1076/3297] cxl/test: Zero out LSA backing memory to avoid leaking to user [ Upstream commit 60f065dbaf46e65830da62a0041761f0c039e086 ] Memory through vmalloc() is not zeroed out. When this memory is copied into output payload, it leaks memory content to user. Use vzalloc() instead to zero out the memory. Suggested-by: sashiko-bot Link: https://lore.kernel.org/linux-cxl/20260605173146.2B9A31F00893@smtp.kernel.org/ Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260605184426.4070913-1-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 4474d5d74faf2..2b5c78b7a2d9c 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1701,7 +1701,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, mdata); - mdata->lsa = vmalloc(LSA_SIZE); + mdata->lsa = vzalloc(LSA_SIZE); if (!mdata->lsa) return -ENOMEM; mdata->fw = vmalloc(FW_SIZE); From 1201dbb260507fac155a43b83b7e7080396cf4ca Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Wed, 29 Apr 2026 23:14:39 +0300 Subject: [PATCH 1077/3297] of: cpu: add check in __of_find_n_match_cpu_property() [ Upstream commit 5901eda2ed99ba0d3661da6eb265970559323bb3 ] In __of_find_n_match_cpu_property(), checking the variable ac for 0 won't prevent a possible overflow when multiplying it by sizeof(*cell). Besides, of_read_number() (called in the *for* loop) can't return correct result if that variable (which equals the #address-cells prop's value) exceeds 2, so additionally checking for that seems logical... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: f3cea45a77c8 ("of: Fix iteration bug over CPU reg properties") Signed-off-by: Sergey Shtylyov Link: https://patch.msgid.link/0c7bf7e9-887c-42d5-bcfb-0ba7fe1e70b6@auroraos.dev Signed-off-by: Rob Herring (Arm) Signed-off-by: Sasha Levin --- drivers/of/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/of/cpu.c b/drivers/of/cpu.c index 5214dc3d05ae1..bd0e918d6f290 100644 --- a/drivers/of/cpu.c +++ b/drivers/of/cpu.c @@ -60,7 +60,7 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun, cell = of_get_property(cpun, prop_name, &prop_len); if (!cell && !ac && arch_match_cpu_phys_id(cpu, 0)) return true; - if (!cell || !ac) + if (!cell || !ac || ac > 2) return false; prop_len /= sizeof(*cell) * ac; for (tid = 0; tid < prop_len; tid++) { From d416dcefdbac90d96b22485fd93f28229ad9984b Mon Sep 17 00:00:00 2001 From: Giovanni Cabiddu Date: Mon, 8 Jun 2026 16:12:57 +0100 Subject: [PATCH 1078/3297] vfio/qat: fix f_pos race in qat_vf_resume_write() [ Upstream commit 4ec5e932e636896e97e4c6a8205b0ac76d52421a ] qat_vf_resume_write() checks filp->f_pos before taking migf->lock, but copies into the migration-state buffer after taking the lock and re-reading the shared file position. Two concurrent writers could therefore pass the bounds check with the old offset, then have the second writer copy after the first advanced f_pos, writing past the end of the migration-state buffer. Take migf->lock before doing the boundary checks. Fixes: bb208810b1ab ("vfio/qat: Add vfio_pci driver for Intel QAT SR-IOV VF devices") Reviewed-by: Ahsan Atta Signed-off-by: Giovanni Cabiddu Link: https://lore.kernel.org/r/20260608151317.136613-1-giovanni.cabiddu@intel.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin --- drivers/vfio/pci/qat/main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/vfio/pci/qat/main.c b/drivers/vfio/pci/qat/main.c index a19b68043eb2e..c6f5f2edc0dd4 100644 --- a/drivers/vfio/pci/qat/main.c +++ b/drivers/vfio/pci/qat/main.c @@ -303,14 +303,18 @@ static ssize_t qat_vf_resume_write(struct file *filp, const char __user *buf, return -ESPIPE; offs = &filp->f_pos; - if (*offs < 0 || - check_add_overflow(len, *offs, &end)) - return -EOVERFLOW; + mutex_lock(&migf->lock); - if (end > mig_dev->state_size) - return -ENOMEM; + if (*offs < 0 || check_add_overflow(len, *offs, &end)) { + done = -EOVERFLOW; + goto out_unlock; + } + + if (end > mig_dev->state_size) { + done = -ENOMEM; + goto out_unlock; + } - mutex_lock(&migf->lock); if (migf->disabled) { done = -ENODEV; goto out_unlock; From 46fbafe3d2d569d828e8d24a7dbe1659f63685cf Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 10 Jun 2026 12:55:38 +0200 Subject: [PATCH 1079/3297] bpf: Tighten cgroup storage cookie checks for prog arrays [ Upstream commit 10627ddc0167aab5c1c390a10ef461e9937aba08 ] The fix in commit abad3d0bad72 ("bpf: Fix oob access in cgroup local storage") is still incomplete. The prog-array compatibility check treats a program with no cgroup storage as compatible with any stored storage cookie. This allows a storage-less program to bridge a tail call chain between an entry program and a storage-using callee even though cgroup local storage at runtime still follows the caller's context, that is, A -> B(no storage) -> C(storage) path. Requiring exact cookie equality would break the legitimate case of a storage-less leaf program being tail called from a storage-using one. Instead, only accept a zero storage cookie if the program cannot perform tail calls itself. This keeps A -> B(no storage) working while rejecting the A -> B(no storage) -> C(storage) bridge. Fixes: abad3d0bad72 ("bpf: Fix oob access in cgroup local storage") Reported-by: Lin Ma Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260610105539.705887-1-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index b102704f89bc7..c99d3f396fdc0 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2468,7 +2468,7 @@ static bool __bpf_prog_map_compatible(struct bpf_map *map, cookie = aux->cgroup_storage[i] ? aux->cgroup_storage[i]->cookie : 0; ret = map->owner->storage_cookie[i] == cookie || - !cookie; + (!cookie && !aux->tail_call_reachable); } if (ret && map->owner->attach_func_proto != aux->attach_func_proto) { From 0234e8fc296e7c9c778a0cd96e1b8e5b87b3902d Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 27 Mar 2026 11:30:04 +0000 Subject: [PATCH 1080/3297] pinctrl: sunxi: a523: Remove unneeded IRQ remuxing flag [ Upstream commit eaf84ff673409fa3dfc390c6afb53b641ee5acba ] The Allwinner A10 and H3 SoCs cannot read the state of a GPIO line when that line is muxed for IRQ triggering (muxval 6), but only if it's explicitly muxed for GPIO input (muxval 0). Other SoCs do not show this behaviour, so we added a optional workaround, triggered by a quirk bit, which triggers remuxing the pin when it's configured for IRQ, while we need to read its value. For some reasons this quirk flag was copied over to newer SoCs, even though they don't show this behaviour, and the GPIO data register reflects the true GPIO state even with a pin muxed to IRQ trigger. Remove the unneeded quirk from the A523 family, where it's definitely not needed (confirmed by experiments), and where it actually breaks, because the workaround is not compatible with the newer generation pinctrl IP used in that chip. Together with a DT change this fixes GPIO IRQ operation on the A523 family of SoCs, as for instance used for the SD card detection. Signed-off-by: Andre Przywara Fixes: b8a51e95b376 ("pinctrl: sunxi: Add support for the secondary A523 GPIO ports") Reviewed-by: Jernej Skrabec Acked-by: Chen-Yu Tsai Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c | 1 - drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c index 69cd2b4ebd7d7..462aa1c4a5fa6 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523-r.c @@ -26,7 +26,6 @@ static const u8 a523_r_irq_bank_muxes[SUNXI_PINCTRL_MAX_BANKS] = static struct sunxi_pinctrl_desc a523_r_pinctrl_data = { .irq_banks = ARRAY_SIZE(a523_r_irq_bank_map), .irq_bank_map = a523_r_irq_bank_map, - .irq_read_needs_mux = true, .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, .pin_base = PL_BASE, }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c index 7d2308c37d29e..b6f78f1f30ac4 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c @@ -26,7 +26,6 @@ static const u8 a523_irq_bank_muxes[SUNXI_PINCTRL_MAX_BANKS] = static struct sunxi_pinctrl_desc a523_pinctrl_data = { .irq_banks = ARRAY_SIZE(a523_irq_bank_map), .irq_bank_map = a523_irq_bank_map, - .irq_read_needs_mux = true, .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_SEL, }; From db3cd694ded4c8d492b62c7228e9c0d9230b13d0 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 7 Nov 2025 00:57:04 +0100 Subject: [PATCH 1081/3297] pinctrl: airoha: generalize pins/group/function/confs handling [ Upstream commit 4043b0c45f8555a079bdac69a19ed08695a47a7b ] In preparation for support of Airoha AN7583, generalize pins/group/function/confs handling and move them in match_data. Inner function will base the values on the pinctrl priv struct instead of relying on hardcoded struct. This permits to use different PIN data while keeping the same logic. Signed-off-by: Christian Marangi Signed-off-by: Linus Walleij Stable-dep-of: bdc95d7e8de3 ("pinctrl: airoha: an7581: add missed gpio32 pin group") Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 567 ++++++++++++---------- 1 file changed, 318 insertions(+), 249 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index f1cf2578fe423..32e5c1b32d507 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -30,15 +30,15 @@ #include "../pinconf.h" #include "../pinmux.h" -#define PINCTRL_PIN_GROUP(id) \ - PINCTRL_PINGROUP(#id, id##_pins, ARRAY_SIZE(id##_pins)) +#define PINCTRL_PIN_GROUP(id, table) \ + PINCTRL_PINGROUP(id, table##_pins, ARRAY_SIZE(table##_pins)) -#define PINCTRL_FUNC_DESC(id) \ +#define PINCTRL_FUNC_DESC(id, table) \ { \ - .desc = PINCTRL_PINFUNCTION(#id, id##_groups, \ - ARRAY_SIZE(id##_groups)), \ - .groups = id##_func_group, \ - .group_size = ARRAY_SIZE(id##_func_group), \ + .desc = PINCTRL_PINFUNCTION(#id, table##_groups, \ + ARRAY_SIZE(table##_groups)),\ + .groups = table##_func_group, \ + .group_size = ARRAY_SIZE(table##_func_group), \ } #define PINCTRL_CONF_DESC(p, offset, mask) \ @@ -357,16 +357,46 @@ struct airoha_pinctrl_gpiochip { u32 irq_type[AIROHA_NUM_PINS]; }; +struct airoha_pinctrl_confs_info { + const struct airoha_pinctrl_conf *confs; + unsigned int num_confs; +}; + +enum airoha_pinctrl_confs_type { + AIROHA_PINCTRL_CONFS_PULLUP, + AIROHA_PINCTRL_CONFS_PULLDOWN, + AIROHA_PINCTRL_CONFS_DRIVE_E2, + AIROHA_PINCTRL_CONFS_DRIVE_E4, + AIROHA_PINCTRL_CONFS_PCIE_RST_OD, + + AIROHA_PINCTRL_CONFS_MAX, +}; + struct airoha_pinctrl { struct pinctrl_dev *ctrl; + struct pinctrl_desc desc; + const struct pingroup *grps; + const struct airoha_pinctrl_func *funcs; + const struct airoha_pinctrl_confs_info *confs_info; + struct regmap *chip_scu; struct regmap *regmap; struct airoha_pinctrl_gpiochip gpiochip; }; -static struct pinctrl_pin_desc airoha_pinctrl_pins[] = { +struct airoha_pinctrl_match_data { + const struct pinctrl_pin_desc *pins; + const unsigned int num_pins; + const struct pingroup *grps; + const unsigned int num_grps; + const struct airoha_pinctrl_func *funcs; + const unsigned int num_funcs; + const struct airoha_pinctrl_confs_info confs_info[AIROHA_PINCTRL_CONFS_MAX]; +}; + +static struct pinctrl_pin_desc en7581_pinctrl_pins[] = { PINCTRL_PIN(0, "uart1_txd"), PINCTRL_PIN(1, "uart1_rxd"), PINCTRL_PIN(2, "i2c_scl"), @@ -427,172 +457,172 @@ static struct pinctrl_pin_desc airoha_pinctrl_pins[] = { PINCTRL_PIN(63, "pcie_reset2"), }; -static const int pon_pins[] = { 49, 50, 51, 52, 53, 54 }; -static const int pon_tod_1pps_pins[] = { 46 }; -static const int gsw_tod_1pps_pins[] = { 46 }; -static const int sipo_pins[] = { 16, 17 }; -static const int sipo_rclk_pins[] = { 16, 17, 43 }; -static const int mdio_pins[] = { 14, 15 }; -static const int uart2_pins[] = { 48, 55 }; -static const int uart2_cts_rts_pins[] = { 46, 47 }; -static const int hsuart_pins[] = { 28, 29 }; -static const int hsuart_cts_rts_pins[] = { 26, 27 }; -static const int uart4_pins[] = { 38, 39 }; -static const int uart5_pins[] = { 18, 19 }; -static const int i2c0_pins[] = { 2, 3 }; -static const int i2c1_pins[] = { 14, 15 }; -static const int jtag_udi_pins[] = { 16, 17, 18, 19, 20 }; -static const int jtag_dfd_pins[] = { 16, 17, 18, 19, 20 }; -static const int i2s_pins[] = { 26, 27, 28, 29 }; -static const int pcm1_pins[] = { 22, 23, 24, 25 }; -static const int pcm2_pins[] = { 18, 19, 20, 21 }; -static const int spi_quad_pins[] = { 32, 33 }; -static const int spi_pins[] = { 4, 5, 6, 7 }; -static const int spi_cs1_pins[] = { 34 }; -static const int pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 }; -static const int pcm_spi_int_pins[] = { 14 }; -static const int pcm_spi_rst_pins[] = { 15 }; -static const int pcm_spi_cs1_pins[] = { 43 }; -static const int pcm_spi_cs2_pins[] = { 40 }; -static const int pcm_spi_cs2_p128_pins[] = { 40 }; -static const int pcm_spi_cs2_p156_pins[] = { 40 }; -static const int pcm_spi_cs3_pins[] = { 41 }; -static const int pcm_spi_cs4_pins[] = { 42 }; -static const int emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 }; -static const int pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; -static const int gpio0_pins[] = { 13 }; -static const int gpio1_pins[] = { 14 }; -static const int gpio2_pins[] = { 15 }; -static const int gpio3_pins[] = { 16 }; -static const int gpio4_pins[] = { 17 }; -static const int gpio5_pins[] = { 18 }; -static const int gpio6_pins[] = { 19 }; -static const int gpio7_pins[] = { 20 }; -static const int gpio8_pins[] = { 21 }; -static const int gpio9_pins[] = { 22 }; -static const int gpio10_pins[] = { 23 }; -static const int gpio11_pins[] = { 24 }; -static const int gpio12_pins[] = { 25 }; -static const int gpio13_pins[] = { 26 }; -static const int gpio14_pins[] = { 27 }; -static const int gpio15_pins[] = { 28 }; -static const int gpio16_pins[] = { 29 }; -static const int gpio17_pins[] = { 30 }; -static const int gpio18_pins[] = { 31 }; -static const int gpio19_pins[] = { 32 }; -static const int gpio20_pins[] = { 33 }; -static const int gpio21_pins[] = { 34 }; -static const int gpio22_pins[] = { 35 }; -static const int gpio23_pins[] = { 36 }; -static const int gpio24_pins[] = { 37 }; -static const int gpio25_pins[] = { 38 }; -static const int gpio26_pins[] = { 39 }; -static const int gpio27_pins[] = { 40 }; -static const int gpio28_pins[] = { 41 }; -static const int gpio29_pins[] = { 42 }; -static const int gpio30_pins[] = { 43 }; -static const int gpio31_pins[] = { 44 }; -static const int gpio33_pins[] = { 46 }; -static const int gpio34_pins[] = { 47 }; -static const int gpio35_pins[] = { 48 }; -static const int gpio36_pins[] = { 49 }; -static const int gpio37_pins[] = { 50 }; -static const int gpio38_pins[] = { 51 }; -static const int gpio39_pins[] = { 52 }; -static const int gpio40_pins[] = { 53 }; -static const int gpio41_pins[] = { 54 }; -static const int gpio42_pins[] = { 55 }; -static const int gpio43_pins[] = { 56 }; -static const int gpio44_pins[] = { 57 }; -static const int gpio45_pins[] = { 58 }; -static const int gpio46_pins[] = { 59 }; -static const int pcie_reset0_pins[] = { 61 }; -static const int pcie_reset1_pins[] = { 62 }; -static const int pcie_reset2_pins[] = { 63 }; - -static const struct pingroup airoha_pinctrl_groups[] = { - PINCTRL_PIN_GROUP(pon), - PINCTRL_PIN_GROUP(pon_tod_1pps), - PINCTRL_PIN_GROUP(gsw_tod_1pps), - PINCTRL_PIN_GROUP(sipo), - PINCTRL_PIN_GROUP(sipo_rclk), - PINCTRL_PIN_GROUP(mdio), - PINCTRL_PIN_GROUP(uart2), - PINCTRL_PIN_GROUP(uart2_cts_rts), - PINCTRL_PIN_GROUP(hsuart), - PINCTRL_PIN_GROUP(hsuart_cts_rts), - PINCTRL_PIN_GROUP(uart4), - PINCTRL_PIN_GROUP(uart5), - PINCTRL_PIN_GROUP(i2c0), - PINCTRL_PIN_GROUP(i2c1), - PINCTRL_PIN_GROUP(jtag_udi), - PINCTRL_PIN_GROUP(jtag_dfd), - PINCTRL_PIN_GROUP(i2s), - PINCTRL_PIN_GROUP(pcm1), - PINCTRL_PIN_GROUP(pcm2), - PINCTRL_PIN_GROUP(spi), - PINCTRL_PIN_GROUP(spi_quad), - PINCTRL_PIN_GROUP(spi_cs1), - PINCTRL_PIN_GROUP(pcm_spi), - PINCTRL_PIN_GROUP(pcm_spi_int), - PINCTRL_PIN_GROUP(pcm_spi_rst), - PINCTRL_PIN_GROUP(pcm_spi_cs1), - PINCTRL_PIN_GROUP(pcm_spi_cs2_p128), - PINCTRL_PIN_GROUP(pcm_spi_cs2_p156), - PINCTRL_PIN_GROUP(pcm_spi_cs2), - PINCTRL_PIN_GROUP(pcm_spi_cs3), - PINCTRL_PIN_GROUP(pcm_spi_cs4), - PINCTRL_PIN_GROUP(emmc), - PINCTRL_PIN_GROUP(pnand), - PINCTRL_PIN_GROUP(gpio0), - PINCTRL_PIN_GROUP(gpio1), - PINCTRL_PIN_GROUP(gpio2), - PINCTRL_PIN_GROUP(gpio3), - PINCTRL_PIN_GROUP(gpio4), - PINCTRL_PIN_GROUP(gpio5), - PINCTRL_PIN_GROUP(gpio6), - PINCTRL_PIN_GROUP(gpio7), - PINCTRL_PIN_GROUP(gpio8), - PINCTRL_PIN_GROUP(gpio9), - PINCTRL_PIN_GROUP(gpio10), - PINCTRL_PIN_GROUP(gpio11), - PINCTRL_PIN_GROUP(gpio12), - PINCTRL_PIN_GROUP(gpio13), - PINCTRL_PIN_GROUP(gpio14), - PINCTRL_PIN_GROUP(gpio15), - PINCTRL_PIN_GROUP(gpio16), - PINCTRL_PIN_GROUP(gpio17), - PINCTRL_PIN_GROUP(gpio18), - PINCTRL_PIN_GROUP(gpio19), - PINCTRL_PIN_GROUP(gpio20), - PINCTRL_PIN_GROUP(gpio21), - PINCTRL_PIN_GROUP(gpio22), - PINCTRL_PIN_GROUP(gpio23), - PINCTRL_PIN_GROUP(gpio24), - PINCTRL_PIN_GROUP(gpio25), - PINCTRL_PIN_GROUP(gpio26), - PINCTRL_PIN_GROUP(gpio27), - PINCTRL_PIN_GROUP(gpio28), - PINCTRL_PIN_GROUP(gpio29), - PINCTRL_PIN_GROUP(gpio30), - PINCTRL_PIN_GROUP(gpio31), - PINCTRL_PIN_GROUP(gpio33), - PINCTRL_PIN_GROUP(gpio34), - PINCTRL_PIN_GROUP(gpio35), - PINCTRL_PIN_GROUP(gpio36), - PINCTRL_PIN_GROUP(gpio37), - PINCTRL_PIN_GROUP(gpio38), - PINCTRL_PIN_GROUP(gpio39), - PINCTRL_PIN_GROUP(gpio40), - PINCTRL_PIN_GROUP(gpio41), - PINCTRL_PIN_GROUP(gpio42), - PINCTRL_PIN_GROUP(gpio43), - PINCTRL_PIN_GROUP(gpio44), - PINCTRL_PIN_GROUP(gpio45), - PINCTRL_PIN_GROUP(gpio46), - PINCTRL_PIN_GROUP(pcie_reset0), - PINCTRL_PIN_GROUP(pcie_reset1), - PINCTRL_PIN_GROUP(pcie_reset2), +static const int en7581_pon_pins[] = { 49, 50, 51, 52, 53, 54 }; +static const int en7581_pon_tod_1pps_pins[] = { 46 }; +static const int en7581_gsw_tod_1pps_pins[] = { 46 }; +static const int en7581_sipo_pins[] = { 16, 17 }; +static const int en7581_sipo_rclk_pins[] = { 16, 17, 43 }; +static const int en7581_mdio_pins[] = { 14, 15 }; +static const int en7581_uart2_pins[] = { 48, 55 }; +static const int en7581_uart2_cts_rts_pins[] = { 46, 47 }; +static const int en7581_hsuart_pins[] = { 28, 29 }; +static const int en7581_hsuart_cts_rts_pins[] = { 26, 27 }; +static const int en7581_uart4_pins[] = { 38, 39 }; +static const int en7581_uart5_pins[] = { 18, 19 }; +static const int en7581_i2c0_pins[] = { 2, 3 }; +static const int en7581_i2c1_pins[] = { 14, 15 }; +static const int en7581_jtag_udi_pins[] = { 16, 17, 18, 19, 20 }; +static const int en7581_jtag_dfd_pins[] = { 16, 17, 18, 19, 20 }; +static const int en7581_i2s_pins[] = { 26, 27, 28, 29 }; +static const int en7581_pcm1_pins[] = { 22, 23, 24, 25 }; +static const int en7581_pcm2_pins[] = { 18, 19, 20, 21 }; +static const int en7581_spi_quad_pins[] = { 32, 33 }; +static const int en7581_spi_pins[] = { 4, 5, 6, 7 }; +static const int en7581_spi_cs1_pins[] = { 34 }; +static const int en7581_pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 }; +static const int en7581_pcm_spi_int_pins[] = { 14 }; +static const int en7581_pcm_spi_rst_pins[] = { 15 }; +static const int en7581_pcm_spi_cs1_pins[] = { 43 }; +static const int en7581_pcm_spi_cs2_pins[] = { 40 }; +static const int en7581_pcm_spi_cs2_p128_pins[] = { 40 }; +static const int en7581_pcm_spi_cs2_p156_pins[] = { 40 }; +static const int en7581_pcm_spi_cs3_pins[] = { 41 }; +static const int en7581_pcm_spi_cs4_pins[] = { 42 }; +static const int en7581_emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 }; +static const int en7581_pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; +static const int en7581_gpio0_pins[] = { 13 }; +static const int en7581_gpio1_pins[] = { 14 }; +static const int en7581_gpio2_pins[] = { 15 }; +static const int en7581_gpio3_pins[] = { 16 }; +static const int en7581_gpio4_pins[] = { 17 }; +static const int en7581_gpio5_pins[] = { 18 }; +static const int en7581_gpio6_pins[] = { 19 }; +static const int en7581_gpio7_pins[] = { 20 }; +static const int en7581_gpio8_pins[] = { 21 }; +static const int en7581_gpio9_pins[] = { 22 }; +static const int en7581_gpio10_pins[] = { 23 }; +static const int en7581_gpio11_pins[] = { 24 }; +static const int en7581_gpio12_pins[] = { 25 }; +static const int en7581_gpio13_pins[] = { 26 }; +static const int en7581_gpio14_pins[] = { 27 }; +static const int en7581_gpio15_pins[] = { 28 }; +static const int en7581_gpio16_pins[] = { 29 }; +static const int en7581_gpio17_pins[] = { 30 }; +static const int en7581_gpio18_pins[] = { 31 }; +static const int en7581_gpio19_pins[] = { 32 }; +static const int en7581_gpio20_pins[] = { 33 }; +static const int en7581_gpio21_pins[] = { 34 }; +static const int en7581_gpio22_pins[] = { 35 }; +static const int en7581_gpio23_pins[] = { 36 }; +static const int en7581_gpio24_pins[] = { 37 }; +static const int en7581_gpio25_pins[] = { 38 }; +static const int en7581_gpio26_pins[] = { 39 }; +static const int en7581_gpio27_pins[] = { 40 }; +static const int en7581_gpio28_pins[] = { 41 }; +static const int en7581_gpio29_pins[] = { 42 }; +static const int en7581_gpio30_pins[] = { 43 }; +static const int en7581_gpio31_pins[] = { 44 }; +static const int en7581_gpio33_pins[] = { 46 }; +static const int en7581_gpio34_pins[] = { 47 }; +static const int en7581_gpio35_pins[] = { 48 }; +static const int en7581_gpio36_pins[] = { 49 }; +static const int en7581_gpio37_pins[] = { 50 }; +static const int en7581_gpio38_pins[] = { 51 }; +static const int en7581_gpio39_pins[] = { 52 }; +static const int en7581_gpio40_pins[] = { 53 }; +static const int en7581_gpio41_pins[] = { 54 }; +static const int en7581_gpio42_pins[] = { 55 }; +static const int en7581_gpio43_pins[] = { 56 }; +static const int en7581_gpio44_pins[] = { 57 }; +static const int en7581_gpio45_pins[] = { 58 }; +static const int en7581_gpio46_pins[] = { 59 }; +static const int en7581_pcie_reset0_pins[] = { 61 }; +static const int en7581_pcie_reset1_pins[] = { 62 }; +static const int en7581_pcie_reset2_pins[] = { 63 }; + +static const struct pingroup en7581_pinctrl_groups[] = { + PINCTRL_PIN_GROUP("pon", en7581_pon), + PINCTRL_PIN_GROUP("pon_tod_1pps", en7581_pon_tod_1pps), + PINCTRL_PIN_GROUP("gsw_tod_1pps", en7581_gsw_tod_1pps), + PINCTRL_PIN_GROUP("sipo", en7581_sipo), + PINCTRL_PIN_GROUP("sipo_rclk", en7581_sipo_rclk), + PINCTRL_PIN_GROUP("mdio", en7581_mdio), + PINCTRL_PIN_GROUP("uart2", en7581_uart2), + PINCTRL_PIN_GROUP("uart2_cts_rts", en7581_uart2_cts_rts), + PINCTRL_PIN_GROUP("hsuart", en7581_hsuart), + PINCTRL_PIN_GROUP("hsuart_cts_rts", en7581_hsuart_cts_rts), + PINCTRL_PIN_GROUP("uart4", en7581_uart4), + PINCTRL_PIN_GROUP("uart5", en7581_uart5), + PINCTRL_PIN_GROUP("i2c0", en7581_i2c0), + PINCTRL_PIN_GROUP("i2c1", en7581_i2c1), + PINCTRL_PIN_GROUP("jtag_udi", en7581_jtag_udi), + PINCTRL_PIN_GROUP("jtag_dfd", en7581_jtag_dfd), + PINCTRL_PIN_GROUP("i2s", en7581_i2s), + PINCTRL_PIN_GROUP("pcm1", en7581_pcm1), + PINCTRL_PIN_GROUP("pcm2", en7581_pcm2), + PINCTRL_PIN_GROUP("spi", en7581_spi), + PINCTRL_PIN_GROUP("spi_quad", en7581_spi_quad), + PINCTRL_PIN_GROUP("spi_cs1", en7581_spi_cs1), + PINCTRL_PIN_GROUP("pcm_spi", en7581_pcm_spi), + PINCTRL_PIN_GROUP("pcm_spi_int", en7581_pcm_spi_int), + PINCTRL_PIN_GROUP("pcm_spi_rst", en7581_pcm_spi_rst), + PINCTRL_PIN_GROUP("pcm_spi_cs1", en7581_pcm_spi_cs1), + PINCTRL_PIN_GROUP("pcm_spi_cs2_p128", en7581_pcm_spi_cs2_p128), + PINCTRL_PIN_GROUP("pcm_spi_cs2_p156", en7581_pcm_spi_cs2_p156), + PINCTRL_PIN_GROUP("pcm_spi_cs2", en7581_pcm_spi_cs2), + PINCTRL_PIN_GROUP("pcm_spi_cs3", en7581_pcm_spi_cs3), + PINCTRL_PIN_GROUP("pcm_spi_cs4", en7581_pcm_spi_cs4), + PINCTRL_PIN_GROUP("emmc", en7581_emmc), + PINCTRL_PIN_GROUP("pnand", en7581_pnand), + PINCTRL_PIN_GROUP("gpio0", en7581_gpio0), + PINCTRL_PIN_GROUP("gpio1", en7581_gpio1), + PINCTRL_PIN_GROUP("gpio2", en7581_gpio2), + PINCTRL_PIN_GROUP("gpio3", en7581_gpio3), + PINCTRL_PIN_GROUP("gpio4", en7581_gpio4), + PINCTRL_PIN_GROUP("gpio5", en7581_gpio5), + PINCTRL_PIN_GROUP("gpio6", en7581_gpio6), + PINCTRL_PIN_GROUP("gpio7", en7581_gpio7), + PINCTRL_PIN_GROUP("gpio8", en7581_gpio8), + PINCTRL_PIN_GROUP("gpio9", en7581_gpio9), + PINCTRL_PIN_GROUP("gpio10", en7581_gpio10), + PINCTRL_PIN_GROUP("gpio11", en7581_gpio11), + PINCTRL_PIN_GROUP("gpio12", en7581_gpio12), + PINCTRL_PIN_GROUP("gpio13", en7581_gpio13), + PINCTRL_PIN_GROUP("gpio14", en7581_gpio14), + PINCTRL_PIN_GROUP("gpio15", en7581_gpio15), + PINCTRL_PIN_GROUP("gpio16", en7581_gpio16), + PINCTRL_PIN_GROUP("gpio17", en7581_gpio17), + PINCTRL_PIN_GROUP("gpio18", en7581_gpio18), + PINCTRL_PIN_GROUP("gpio19", en7581_gpio19), + PINCTRL_PIN_GROUP("gpio20", en7581_gpio20), + PINCTRL_PIN_GROUP("gpio21", en7581_gpio21), + PINCTRL_PIN_GROUP("gpio22", en7581_gpio22), + PINCTRL_PIN_GROUP("gpio23", en7581_gpio23), + PINCTRL_PIN_GROUP("gpio24", en7581_gpio24), + PINCTRL_PIN_GROUP("gpio25", en7581_gpio25), + PINCTRL_PIN_GROUP("gpio26", en7581_gpio26), + PINCTRL_PIN_GROUP("gpio27", en7581_gpio27), + PINCTRL_PIN_GROUP("gpio28", en7581_gpio28), + PINCTRL_PIN_GROUP("gpio29", en7581_gpio29), + PINCTRL_PIN_GROUP("gpio30", en7581_gpio30), + PINCTRL_PIN_GROUP("gpio31", en7581_gpio31), + PINCTRL_PIN_GROUP("gpio33", en7581_gpio33), + PINCTRL_PIN_GROUP("gpio34", en7581_gpio34), + PINCTRL_PIN_GROUP("gpio35", en7581_gpio35), + PINCTRL_PIN_GROUP("gpio36", en7581_gpio36), + PINCTRL_PIN_GROUP("gpio37", en7581_gpio37), + PINCTRL_PIN_GROUP("gpio38", en7581_gpio38), + PINCTRL_PIN_GROUP("gpio39", en7581_gpio39), + PINCTRL_PIN_GROUP("gpio40", en7581_gpio40), + PINCTRL_PIN_GROUP("gpio41", en7581_gpio41), + PINCTRL_PIN_GROUP("gpio42", en7581_gpio42), + PINCTRL_PIN_GROUP("gpio43", en7581_gpio43), + PINCTRL_PIN_GROUP("gpio44", en7581_gpio44), + PINCTRL_PIN_GROUP("gpio45", en7581_gpio45), + PINCTRL_PIN_GROUP("gpio46", en7581_gpio46), + PINCTRL_PIN_GROUP("pcie_reset0", en7581_pcie_reset0), + PINCTRL_PIN_GROUP("pcie_reset1", en7581_pcie_reset1), + PINCTRL_PIN_GROUP("pcie_reset2", en7581_pcie_reset2), }; static const char *const pon_groups[] = { "pon" }; @@ -1955,33 +1985,33 @@ static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = { }, }; -static const struct airoha_pinctrl_func airoha_pinctrl_funcs[] = { - PINCTRL_FUNC_DESC(pon), - PINCTRL_FUNC_DESC(tod_1pps), - PINCTRL_FUNC_DESC(sipo), - PINCTRL_FUNC_DESC(mdio), - PINCTRL_FUNC_DESC(uart), - PINCTRL_FUNC_DESC(i2c), - PINCTRL_FUNC_DESC(jtag), - PINCTRL_FUNC_DESC(pcm), - PINCTRL_FUNC_DESC(spi), - PINCTRL_FUNC_DESC(pcm_spi), - PINCTRL_FUNC_DESC(i2s), - PINCTRL_FUNC_DESC(emmc), - PINCTRL_FUNC_DESC(pnand), - PINCTRL_FUNC_DESC(pcie_reset), - PINCTRL_FUNC_DESC(pwm), - PINCTRL_FUNC_DESC(phy1_led0), - PINCTRL_FUNC_DESC(phy2_led0), - PINCTRL_FUNC_DESC(phy3_led0), - PINCTRL_FUNC_DESC(phy4_led0), - PINCTRL_FUNC_DESC(phy1_led1), - PINCTRL_FUNC_DESC(phy2_led1), - PINCTRL_FUNC_DESC(phy3_led1), - PINCTRL_FUNC_DESC(phy4_led1), +static const struct airoha_pinctrl_func en7581_pinctrl_funcs[] = { + PINCTRL_FUNC_DESC("pon", pon), + PINCTRL_FUNC_DESC("tod_1pps", tod_1pps), + PINCTRL_FUNC_DESC("sipo", sipo), + PINCTRL_FUNC_DESC("mdio", mdio), + PINCTRL_FUNC_DESC("uart", uart), + PINCTRL_FUNC_DESC("i2c", i2c), + PINCTRL_FUNC_DESC("jtag", jtag), + PINCTRL_FUNC_DESC("pcm", pcm), + PINCTRL_FUNC_DESC("spi", spi), + PINCTRL_FUNC_DESC("pcm_spi", pcm_spi), + PINCTRL_FUNC_DESC("i2s", i2s), + PINCTRL_FUNC_DESC("emmc", emmc), + PINCTRL_FUNC_DESC("pnand", pnand), + PINCTRL_FUNC_DESC("pcie_reset", pcie_reset), + PINCTRL_FUNC_DESC("pwm", pwm), + PINCTRL_FUNC_DESC("phy1_led0", phy1_led0), + PINCTRL_FUNC_DESC("phy2_led0", phy2_led0), + PINCTRL_FUNC_DESC("phy3_led0", phy3_led0), + PINCTRL_FUNC_DESC("phy4_led0", phy4_led0), + PINCTRL_FUNC_DESC("phy1_led1", phy1_led1), + PINCTRL_FUNC_DESC("phy2_led1", phy2_led1), + PINCTRL_FUNC_DESC("phy3_led1", phy3_led1), + PINCTRL_FUNC_DESC("phy4_led1", phy4_led1), }; -static const struct airoha_pinctrl_conf airoha_pinctrl_pullup_conf[] = { +static const struct airoha_pinctrl_conf en7581_pinctrl_pullup_conf[] = { PINCTRL_CONF_DESC(0, REG_I2C_SDA_PU, UART1_TXD_PU_MASK), PINCTRL_CONF_DESC(1, REG_I2C_SDA_PU, UART1_RXD_PU_MASK), PINCTRL_CONF_DESC(2, REG_I2C_SDA_PU, I2C_SDA_PU_MASK), @@ -2042,7 +2072,7 @@ static const struct airoha_pinctrl_conf airoha_pinctrl_pullup_conf[] = { PINCTRL_CONF_DESC(63, REG_I2C_SDA_PU, PCIE2_RESET_PU_MASK), }; -static const struct airoha_pinctrl_conf airoha_pinctrl_pulldown_conf[] = { +static const struct airoha_pinctrl_conf en7581_pinctrl_pulldown_conf[] = { PINCTRL_CONF_DESC(0, REG_I2C_SDA_PD, UART1_TXD_PD_MASK), PINCTRL_CONF_DESC(1, REG_I2C_SDA_PD, UART1_RXD_PD_MASK), PINCTRL_CONF_DESC(2, REG_I2C_SDA_PD, I2C_SDA_PD_MASK), @@ -2103,7 +2133,7 @@ static const struct airoha_pinctrl_conf airoha_pinctrl_pulldown_conf[] = { PINCTRL_CONF_DESC(63, REG_I2C_SDA_PD, PCIE2_RESET_PD_MASK), }; -static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e2_conf[] = { +static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e2_conf[] = { PINCTRL_CONF_DESC(0, REG_I2C_SDA_E2, UART1_TXD_E2_MASK), PINCTRL_CONF_DESC(1, REG_I2C_SDA_E2, UART1_RXD_E2_MASK), PINCTRL_CONF_DESC(2, REG_I2C_SDA_E2, I2C_SDA_E2_MASK), @@ -2164,7 +2194,7 @@ static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e2_conf[] = { PINCTRL_CONF_DESC(63, REG_I2C_SDA_E2, PCIE2_RESET_E2_MASK), }; -static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e4_conf[] = { +static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e4_conf[] = { PINCTRL_CONF_DESC(0, REG_I2C_SDA_E4, UART1_TXD_E4_MASK), PINCTRL_CONF_DESC(1, REG_I2C_SDA_E4, UART1_RXD_E4_MASK), PINCTRL_CONF_DESC(2, REG_I2C_SDA_E4, I2C_SDA_E4_MASK), @@ -2225,7 +2255,7 @@ static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e4_conf[] = { PINCTRL_CONF_DESC(63, REG_I2C_SDA_E4, PCIE2_RESET_E4_MASK), }; -static const struct airoha_pinctrl_conf airoha_pinctrl_pcie_rst_od_conf[] = { +static const struct airoha_pinctrl_conf en7581_pinctrl_pcie_rst_od_conf[] = { PINCTRL_CONF_DESC(61, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK), PINCTRL_CONF_DESC(62, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK), PINCTRL_CONF_DESC(63, REG_PCIE_RESET_OD, PCIE2_RESET_OD_MASK), @@ -2546,12 +2576,17 @@ airoha_pinctrl_get_conf_reg(const struct airoha_pinctrl_conf *conf, } static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl, - const struct airoha_pinctrl_conf *conf, - int conf_size, int pin, u32 *val) + enum airoha_pinctrl_confs_type conf_type, + int pin, u32 *val) { + const struct airoha_pinctrl_confs_info *confs_info; const struct airoha_pinctrl_reg *reg; - reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); + confs_info = &pinctrl->confs_info[conf_type]; + + reg = airoha_pinctrl_get_conf_reg(confs_info->confs, + confs_info->num_confs, + pin); if (!reg) return -EINVAL; @@ -2564,12 +2599,17 @@ static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl, } static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl, - const struct airoha_pinctrl_conf *conf, - int conf_size, int pin, u32 val) + enum airoha_pinctrl_confs_type conf_type, + int pin, u32 val) { + const struct airoha_pinctrl_confs_info *confs_info; const struct airoha_pinctrl_reg *reg = NULL; - reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); + confs_info = &pinctrl->confs_info[conf_type]; + + reg = airoha_pinctrl_get_conf_reg(confs_info->confs, + confs_info->num_confs, + pin); if (!reg) return -EINVAL; @@ -2582,44 +2622,34 @@ static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl, } #define airoha_pinctrl_get_pullup_conf(pinctrl, pin, val) \ - airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pullup_conf, \ - ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ + airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLUP, \ (pin), (val)) #define airoha_pinctrl_get_pulldown_conf(pinctrl, pin, val) \ - airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ - ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ + airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLDOWN, \ (pin), (val)) #define airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, val) \ - airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ - ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ + airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E2, \ (pin), (val)) #define airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, val) \ - airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ - ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ + airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E4, \ (pin), (val)) #define airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, val) \ - airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ - ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ + airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PCIE_RST_OD, \ (pin), (val)) #define airoha_pinctrl_set_pullup_conf(pinctrl, pin, val) \ - airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pullup_conf, \ - ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ + airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLUP, \ (pin), (val)) #define airoha_pinctrl_set_pulldown_conf(pinctrl, pin, val) \ - airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ - ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ + airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLDOWN, \ (pin), (val)) #define airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, val) \ - airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ - ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ + airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E2, \ (pin), (val)) #define airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, val) \ - airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ - ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ + airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E4, \ (pin), (val)) #define airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, val) \ - airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ - ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ + airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PCIE_RST_OD, \ (pin), (val)) static int airoha_pinconf_get_direction(struct pinctrl_dev *pctrl_dev, u32 p) @@ -2796,12 +2826,13 @@ static int airoha_pinconf_set(struct pinctrl_dev *pctrl_dev, static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev, unsigned int group, unsigned long *config) { + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); u32 cur_config = 0; int i; - for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { + for (i = 0; i < pinctrl->grps[group].npins; i++) { if (airoha_pinconf_get(pctrl_dev, - airoha_pinctrl_groups[group].pins[i], + pinctrl->grps[group].pins[i], config)) return -ENOTSUPP; @@ -2818,13 +2849,14 @@ static int airoha_pinconf_group_set(struct pinctrl_dev *pctrl_dev, unsigned int group, unsigned long *configs, unsigned int num_configs) { + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); int i; - for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { + for (i = 0; i < pinctrl->grps[group].npins; i++) { int err; err = airoha_pinconf_set(pctrl_dev, - airoha_pinctrl_groups[group].pins[i], + pinctrl->grps[group].pins[i], configs, num_configs); if (err) return err; @@ -2850,23 +2882,16 @@ static const struct pinctrl_ops airoha_pctlops = { .dt_free_map = pinconf_generic_dt_free_map, }; -static const struct pinctrl_desc airoha_pinctrl_desc = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, - .pctlops = &airoha_pctlops, - .pmxops = &airoha_pmxops, - .confops = &airoha_confops, - .pins = airoha_pinctrl_pins, - .npins = ARRAY_SIZE(airoha_pinctrl_pins), -}; - static int airoha_pinctrl_probe(struct platform_device *pdev) { + const struct airoha_pinctrl_match_data *data; struct device *dev = &pdev->dev; struct airoha_pinctrl *pinctrl; struct regmap *map; int err, i; + data = device_get_match_data(dev); + pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL); if (!pinctrl) return -ENOMEM; @@ -2881,14 +2906,23 @@ static int airoha_pinctrl_probe(struct platform_device *pdev) pinctrl->chip_scu = map; - err = devm_pinctrl_register_and_init(dev, &airoha_pinctrl_desc, + /* Init pinctrl desc struct */ + pinctrl->desc.name = KBUILD_MODNAME; + pinctrl->desc.owner = THIS_MODULE, + pinctrl->desc.pctlops = &airoha_pctlops, + pinctrl->desc.pmxops = &airoha_pmxops, + pinctrl->desc.confops = &airoha_confops, + pinctrl->desc.pins = data->pins, + pinctrl->desc.npins = data->num_pins, + + err = devm_pinctrl_register_and_init(dev, &pinctrl->desc, pinctrl, &pinctrl->ctrl); if (err) return err; /* build pin groups */ - for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_groups); i++) { - const struct pingroup *grp = &airoha_pinctrl_groups[i]; + for (i = 0; i < data->num_grps; i++) { + const struct pingroup *grp = &data->grps[i]; err = pinctrl_generic_add_group(pinctrl->ctrl, grp->name, grp->pins, grp->npins, @@ -2901,10 +2935,10 @@ static int airoha_pinctrl_probe(struct platform_device *pdev) } /* build functions */ - for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_funcs); i++) { + for (i = 0; i < data->num_funcs; i++) { const struct airoha_pinctrl_func *func; - func = &airoha_pinctrl_funcs[i]; + func = &data->funcs[i]; err = pinmux_generic_add_pinfunction(pinctrl->ctrl, &func->desc, (void *)func); @@ -2915,6 +2949,10 @@ static int airoha_pinctrl_probe(struct platform_device *pdev) } } + pinctrl->grps = data->grps; + pinctrl->funcs = data->funcs; + pinctrl->confs_info = data->confs_info; + err = pinctrl_enable(pinctrl->ctrl); if (err) return err; @@ -2923,8 +2961,39 @@ static int airoha_pinctrl_probe(struct platform_device *pdev) return airoha_pinctrl_add_gpiochip(pinctrl, pdev); } +static const struct airoha_pinctrl_match_data en7581_pinctrl_match_data = { + .pins = en7581_pinctrl_pins, + .num_pins = ARRAY_SIZE(en7581_pinctrl_pins), + .grps = en7581_pinctrl_groups, + .num_grps = ARRAY_SIZE(en7581_pinctrl_groups), + .funcs = en7581_pinctrl_funcs, + .num_funcs = ARRAY_SIZE(en7581_pinctrl_funcs), + .confs_info = { + [AIROHA_PINCTRL_CONFS_PULLUP] = { + .confs = en7581_pinctrl_pullup_conf, + .num_confs = ARRAY_SIZE(en7581_pinctrl_pullup_conf), + }, + [AIROHA_PINCTRL_CONFS_PULLDOWN] = { + .confs = en7581_pinctrl_pulldown_conf, + .num_confs = ARRAY_SIZE(en7581_pinctrl_pulldown_conf), + }, + [AIROHA_PINCTRL_CONFS_DRIVE_E2] = { + .confs = en7581_pinctrl_drive_e2_conf, + .num_confs = ARRAY_SIZE(en7581_pinctrl_drive_e2_conf), + }, + [AIROHA_PINCTRL_CONFS_DRIVE_E4] = { + .confs = en7581_pinctrl_drive_e4_conf, + .num_confs = ARRAY_SIZE(en7581_pinctrl_drive_e4_conf), + }, + [AIROHA_PINCTRL_CONFS_PCIE_RST_OD] = { + .confs = en7581_pinctrl_pcie_rst_od_conf, + .num_confs = ARRAY_SIZE(en7581_pinctrl_pcie_rst_od_conf), + }, + }, +}; + static const struct of_device_id airoha_pinctrl_of_match[] = { - { .compatible = "airoha,en7581-pinctrl" }, + { .compatible = "airoha,en7581-pinctrl", .data = &en7581_pinctrl_match_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, airoha_pinctrl_of_match); From 5985ddfd3e83fee2e021ed6908d4026516f47278 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:32 +0300 Subject: [PATCH 1082/3297] pinctrl: airoha: an7581: add missed gpio32 pin group [ Upstream commit bdc95d7e8de3eefa9fc062302625259c0b79136d ] gpio32 pin group is missed for an7581 SoC. This patch add it. Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index 32e5c1b32d507..acc010bdf3777 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -522,6 +522,7 @@ static const int en7581_gpio28_pins[] = { 41 }; static const int en7581_gpio29_pins[] = { 42 }; static const int en7581_gpio30_pins[] = { 43 }; static const int en7581_gpio31_pins[] = { 44 }; +static const int en7581_gpio32_pins[] = { 45 }; static const int en7581_gpio33_pins[] = { 46 }; static const int en7581_gpio34_pins[] = { 47 }; static const int en7581_gpio35_pins[] = { 48 }; @@ -606,6 +607,7 @@ static const struct pingroup en7581_pinctrl_groups[] = { PINCTRL_PIN_GROUP("gpio29", en7581_gpio29), PINCTRL_PIN_GROUP("gpio30", en7581_gpio30), PINCTRL_PIN_GROUP("gpio31", en7581_gpio31), + PINCTRL_PIN_GROUP("gpio32", en7581_gpio32), PINCTRL_PIN_GROUP("gpio33", en7581_gpio33), PINCTRL_PIN_GROUP("gpio34", en7581_gpio34), PINCTRL_PIN_GROUP("gpio35", en7581_gpio35), From ad6963c3bb458fc1b722627405800c32e4cd840b Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sat, 6 Jun 2026 05:03:34 +0300 Subject: [PATCH 1083/3297] pinctrl: airoha: an7581: fix misprint in gpio19 pinconf [ Upstream commit 08a39a0617ff32a7c3962bbc38a9eee41b14659a ] Pin 32 (gpio19) duplicate pinconf settings of pin 31. Fix it using a proper bit number in the configuration register. Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") Signed-off-by: Mikhail Kshevetskiy Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index acc010bdf3777..ae769ef0cdc6b 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -2041,7 +2041,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_pullup_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_PU, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_PU, BIT(22)), @@ -2102,7 +2102,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_pulldown_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_PD, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_PD, BIT(22)), @@ -2163,7 +2163,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e2_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_E2, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_E2, BIT(22)), @@ -2224,7 +2224,7 @@ static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e4_conf[] = { PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(16)), PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(17)), PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(18)), - PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(18)), + PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(19)), PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(20)), PINCTRL_CONF_DESC(34, REG_GPIO_L_E4, BIT(21)), PINCTRL_CONF_DESC(35, REG_GPIO_L_E4, BIT(22)), From c83255f3cf22d13c5b9736668cbe38a098fd6d5a Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 27 Mar 2026 11:30:06 +0000 Subject: [PATCH 1084/3297] arm64: dts: allwinner: a523: Add missing GPIO interrupt [ Upstream commit 6b81aa0c8a4f038712fa549e4d44d8279eeb0440 ] Even though the Allwinner A523 SoC implements 10 GPIO banks, it has actually registers for 11 IRQ banks, and even an interrupt assigned to the first, non-implemented IRQ bank. Add that first interrupt to the list of GPIO interrupts, to correct the association between IRQs and GPIO banks. This fixes GPIO IRQ operation on boards with A523 SoCs, as seen by broken SD card detect functionality, for instance. Signed-off-by: Andre Przywara Fixes: 35ac96f79664 ("arm64: dts: allwinner: Add Allwinner A523 .dtsi file") Reviewed-by: Chen-Yu Tsai Reviewed-by: Jernej Skrabec Link: https://patch.msgid.link/20260327113006.3135663-4-andre.przywara@arm.com Signed-off-by: Chen-Yu Tsai Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi index 7b36c47a3a139..95cc70854ac51 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi @@ -128,7 +128,8 @@ pio: pinctrl@2000000 { compatible = "allwinner,sun55i-a523-pinctrl"; reg = <0x2000000 0x800>; - interrupts = , + interrupts = , + , , , , From 0eab19ab9cb1be241347c6525a6a17f0e8cbaf9b Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 11 Jun 2026 14:22:21 +0100 Subject: [PATCH 1085/3297] ASoC: cs35l56: Fix possible uninitialized value in cs35l56_spi_system_reset() [ Upstream commit 007699d278a655871b07d45a1268761260d03124 ] In cs35l56_spi_system_reset() initialize val to zero before using it in the read_poll_timeout(). This prevents testing an uninitialized value if the regmap_read_bypassed() returns an error. Read errors are intentionally ignored during this loop because the device is resetting (though SPI can't really detect that so shouldn't fail because of that, it's safer to ignore errors and keep polling). Because of this, val must be initialized to something in case the first read fails. The polling loop is looking for a non-zero value, so initializing val to 0 will ensure that the loop continues until a valid state is read from the device or it times out. Fixes: 769c1b79295c ("ASoC: cs35l56: Prevent races when soft-resetting using SPI control") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260611132221.1100497-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56-shared.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index a13e8eaf277d4..646bb4fbca025 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -515,6 +515,7 @@ static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base) * The regmap must remain in cache-only until the chip has * booted, so use a bypassed read. */ + val = 0; ret = read_poll_timeout(regmap_read_bypassed, read_ret, (val > 0) && (val < 0xffffffff), CS35L56_HALO_STATE_POLL_US, From c86c861c64b586720ab34e125445253aaf9f7285 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 8 Jun 2026 16:19:08 +0200 Subject: [PATCH 1086/3297] s390/process: Fix kernel thread function pointer type [ Upstream commit d0478f5d3cba1095bfdeb43a9b063c10cdebef14 ] In case of a kernel thread __ret_from_fork() calls the specified function indirectly. Fix the kernel thread function pointer, since kernel threads return an int instead of void. Fixes: 56e62a737028 ("s390: convert to generic entry") Reviewed-by: Alexander Gordeev Signed-off-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Sasha Levin --- arch/s390/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index b107dbca4ed7d..f18d7716c4342 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -51,7 +51,7 @@ void ret_from_fork(void) asm("ret_from_fork"); void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs) { - void (*func)(void *arg); + int (*func)(void *arg); schedule_tail(prev); From e284bb94ad4512ec0bcd9b91bda99e67c507f7f8 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Mon, 1 Jun 2026 04:30:56 -0700 Subject: [PATCH 1087/3297] Bluetooth: hci_qca: fix NULL pointer dereference in qca_dmp_hdr() for non-serdev device [ Upstream commit 6b8cbcf08de0db62254d1981f83db0f94681ccd9 ] hu->serdev is NULL for hci_uart attached via non-serdev paths, but qca_dmp_hdr() unconditionally dereferences hu->serdev->dev.driver->name, causing a NULL pointer dereference. Fix by guarding the dereference with a NULL check and falling back to "hci_ldisc_qca" for the non-serdev case. Fixes: 06d3fdfcdf5c ("Bluetooth: hci_qca: Add qcom devcoredump support") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/hci_qca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index d37a51101ca76..c649a3f702c04 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1023,7 +1023,7 @@ static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb) skb_put_data(skb, buf, strlen(buf)); snprintf(buf, sizeof(buf), "Driver: %s\n", - hu->serdev->dev.driver->name); + hu->serdev ? hu->serdev->dev.driver->name : "hci_ldisc_qca"); skb_put_data(skb, buf, strlen(buf)); } From f1b4df9c260c51726da2e86e19322825fddeefd0 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 3 Jun 2026 01:06:21 +0800 Subject: [PATCH 1088/3297] Bluetooth: eir: Fix stack OOB write when prepending the Flags AD [ Upstream commit 6f5fb689fdf80bdd143f22a502f9eb1f3c85e286 ] eir_create_adv_data() builds the advertising data into a fixed-size buffer ("size", 31 for the legacy path). It may prepend a 3-byte "Flags" AD structure (LE_AD_NO_BREDR on an LE-only controller) and then copies the per-instance data without checking that it still fits: memcpy(ptr, adv->adv_data, adv->adv_data_len); tlv_data_max_len() only reserves those 3 bytes when the user-supplied flags carry a managed-flags bit, so an instance added with flags == 0 is accepted with adv_data_len up to the full buffer. At advertise time the flags are still prepended, and the memcpy() writes 3 + adv_data_len bytes into the size-byte buffer: BUG: KASAN: stack-out-of-bounds in eir_create_adv_data (net/bluetooth/eir.c:301) Write of size 31 at addr ffff88800a547bdc by task kworker/u9:0/65 Workqueue: hci0 hci_cmd_sync_work __asan_memcpy (mm/kasan/shadow.c:106) eir_create_adv_data (net/bluetooth/eir.c:301) hci_update_adv_data_sync (net/bluetooth/hci_sync.c:1310) hci_schedule_adv_instance_sync (net/bluetooth/hci_sync.c:1817) hci_cmd_sync_work (net/bluetooth/hci_sync.c:332) This frame has 1 object: [32, 64) 'cp' The "Flags" structure is added by the kernel, not requested by userspace, so only prepend it when it fits together with the instance advertising data; when there is no room for both, drop the flags rather than the user-provided data. Reachable by a local user with CAP_NET_ADMIN owning an LE-only controller on the legacy advertising path. Fixes: b44133ff03be ("Bluetooth: Support the "discoverable" adv flag") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reported-by: Xiang Mei Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/eir.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c index 3f72111ba651f..1de5f9df6eec0 100644 --- a/net/bluetooth/eir.c +++ b/net/bluetooth/eir.c @@ -283,10 +283,12 @@ u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size) if (!flags) flags |= mgmt_get_adv_discov_flags(hdev); - /* If flags would still be empty, then there is no need to - * include the "Flags" AD field". + /* Only add the "Flags" if it fits together with the instance + * advertising data; drop it rather than overflow the buffer. */ - if (flags && (ad_len + eir_precalc_len(1) <= size)) { + if (flags && + (ad_len + eir_precalc_len(1) + + (adv ? adv->adv_data_len : 0) <= size)) { ptr[0] = 0x02; ptr[1] = EIR_FLAGS; ptr[2] = flags; From e8815ae9dcdc563d9a770388cee874442255bc58 Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Tue, 2 Jun 2026 15:00:32 +0800 Subject: [PATCH 1089/3297] Bluetooth: hci_event: fix simultaneous discovery stuck in FINDING [ Upstream commit 96d006ae6445679436b945593950fd465eba7e76 ] When hci_inquiry_complete_evt is called between le_scan_disable and le_set_scan_enable_complete and no remote name needs to be resolved, the interleaved discovery with SIMULTANEOUS quirk gets stuck in DISCOVERY_FINDING. le_set_scan_enable_complete does not check inquiry state. No one sets DISCOVERY_STOPPED in this process. Add state check in le_set_scan_enable_complete and change state if the state is DISCOVERY_FINDING. Tested with AX201 (8087:0026) in Dell Vostro 13. Discovering disabled MGMT Event below is reported when running into the above condition. @ MGMT Command: Start Discovery (0x0023) {0x0001} [hci0] 10885.970873 Address type: 0x07 BR/EDR LE Public LE Random ... < HCI Command: LE Set Extended Scan Enable #38205 [hci0] 10886.131438 Extended scan: Enabled (0x01) Filter duplicates: Enabled (0x01) Duration: 0 msec (0x0000) Period: 0.00 sec (0x0000) > HCI Event: Command Complete (0x0e) plen 4 #38206 [hci0] 10886.133295 LE Set Extended Scan Enable (0x08|0x0042) ncmd 2 Status: Success (0x00) @ MGMT Event: Discovering (0x0013) plen 2 {0x0001} [hci0] 10886.133414 Address type: 0x07 BR/EDR LE Public LE Random Discovery: Enabled (0x01) < HCI Command: Inquiry (0x01|0x0001) plen 5 #38207 [hci0] 10886.133528 Access code: 0x9e8b33 (General Inquiry) Length: 10.24s (0x08) Num responses: 0 > HCI Event: Command Status (0x0f) plen 4 #38208 [hci0] 10886.141333 Inquiry (0x01|0x0001) ncmd 2 Status: Success (0x00) ... < HCI Command: LE Set Extended Scan Enable #38242 [hci0] 10896.381802 Extended scan: Disabled (0x00) Filter duplicates: Disabled (0x00) Duration: 0 msec (0x0000) Period: 0.00 sec (0x0000) > HCI Event: Inquiry Complete (0x01) plen 1 #38243 [hci0] 10896.383419 Status: Success (0x00) > HCI Event: Command Complete (0x0e) plen 4 #38244 [hci0] 10896.394378 LE Set Extended Scan Enable (0x08|0x0042) ncmd 2 Status: Success (0x00) @ MGMT Event: Device Found (0x0012) plen 22 {0x0001} [hci0] 10896.394497 LE Address: 88:12:AC:92:43:69 RSSI: -101 dBm (0x9b) Flags: 0x00000004 Not Connectable Data length: 8 Company: Xiaomi Inc. (911) Data[0]: 16-bit Service UUIDs (complete): 1 entry Xiaomi Inc. (0xfdaa) @ MGMT Event: Discovering (0x0013) plen 2 {0x0001} [hci0] 10896.394506 Address type: 0x07 BR/EDR LE Public LE Random Discovery: Disabled (0x00) Fixes: 8ffde2a73f2c ("Bluetooth: Convert le_scan_disable timeout to hci_sync") Signed-off-by: Jiajia Liu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_event.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b6b52b81b7b9c..396aafa609c9a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1765,6 +1765,13 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable) hci_dev_clear_flag(hdev, HCI_LE_SCAN); + if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && + hci_test_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY) && + !test_bit(HCI_INQUIRY, &hdev->flags) && + hdev->discovery.state == DISCOVERY_FINDING) { + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + } + /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we * interrupted scanning due to a connect request. Mark * therefore discovery as stopped. From a0fd1086a57b982f8c24ae4ab165c2af39fe1735 Mon Sep 17 00:00:00 2001 From: Jordan Walters Date: Wed, 3 Jun 2026 04:50:47 -0400 Subject: [PATCH 1090/3297] Bluetooth: hci_core: Fix UAF in hci_unregister_dev() [ Upstream commit 5edcc018fa6e80b2c478454a4a8229c23d67c181 ] hci_unregister_dev() does not disable cmd_timer and ncmd_timer before the hci_dev structure is freed. If a timeout fires during device teardown, the callback dereferences freed memory (including the hdev->reset function pointer), leading to a use-after-free. Add disable_delayed_work_sync() calls alongside the existing disable_work_sync() calls to ensure both timers are fully quiesced before teardown proceeds. Fixes: 0d151a103775 ("Bluetooth: hci_core: cancel all works upon hci_unregister_dev()") Signed-off-by: Jordan Walters Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0f86b81b39730..a1d7e7985f76a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2707,6 +2707,8 @@ void hci_unregister_dev(struct hci_dev *hdev) disable_work_sync(&hdev->tx_work); disable_work_sync(&hdev->power_on); disable_work_sync(&hdev->error_reset); + disable_delayed_work_sync(&hdev->cmd_timer); + disable_delayed_work_sync(&hdev->ncmd_timer); hci_cmd_sync_clear(hdev); From 7f206a8d8d82296aa2d30bf5f3e5bb73fc44591d Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Thu, 4 Jun 2026 19:46:40 +0800 Subject: [PATCH 1091/3297] Bluetooth: btmtk: fix URB leak in alloc_mtk_intr_urb error path [ Upstream commit f396f4005180928cd9e15e352a6512865d3bc908 ] When btmtk_isopkt_pad() fails, the previously allocated URB is not freed, leaking the urb structure. Add usb_free_urb() before returning the error. Fixes: ceac1cb0259d ("Bluetooth: btusb: mediatek: add ISO data transmission functions") Signed-off-by: Zhao Dongdong Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btmtk.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 38ba8f2bd2f96..a908e7b46ecaf 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -1051,8 +1051,10 @@ struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct sk_buff *skb, if (!urb) return ERR_PTR(-ENOMEM); - if (btmtk_isopkt_pad(hdev, skb)) + if (btmtk_isopkt_pad(hdev, skb)) { + usb_free_urb(urb); return ERR_PTR(-EINVAL); + } pipe = usb_sndintpipe(btmtk_data->udev, btmtk_data->isopkt_tx_ep->bEndpointAddress); From ec4d352747a62c1082f16c11a74b37d6eb85a5a3 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 8 Jun 2026 23:56:28 +0000 Subject: [PATCH 1092/3297] Bluetooth: hci: validate codec capability element length [ Upstream commit c38fbcdc407925c7088f7e5f11c1fff73d2d35a2 ] Read Local Codec Capabilities returns a sequence of capability elements. Each element starts with a one-byte length followed by that many payload bytes. hci_read_codec_capabilities() checks that the skb contains the length byte, but then validates only caps->len against the remaining skb length. A malformed controller response with one remaining byte and caps->len set to one passes that check even though the element needs two bytes. The parser then records a two-byte capability and copies one byte beyond the advertised response payload into the codec list. Validate the full element size, including the length byte, before adding it to the accumulated capability length. This preserves all well-formed capability elements and drops only truncated controller responses. Fixes: 8961987f3f5f ("Bluetooth: Enumerate local supported codec and cache details") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_codec.c b/net/bluetooth/hci_codec.c index 3cc135bb1d30c..5bc5003c387c6 100644 --- a/net/bluetooth/hci_codec.c +++ b/net/bluetooth/hci_codec.c @@ -100,7 +100,7 @@ static void hci_read_codec_capabilities(struct hci_dev *hdev, __u8 transport, caps = (void *)skb->data; if (skb->len < sizeof(*caps)) goto error; - if (skb->len < caps->len) + if (skb->len < sizeof(caps->len) + caps->len) goto error; len += sizeof(caps->len) + caps->len; skb_pull(skb, sizeof(caps->len) + caps->len); From 7e694ac97591c4f4b793d4741b91709cdbbda819 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 8 Jun 2026 23:58:23 +0000 Subject: [PATCH 1093/3297] Bluetooth: vhci: validate devcoredump state before side effects [ Upstream commit 88c2404a3c59c3126453919388dbd5ed98ed01bd ] The VHCI force_devcoredump debugfs hook accepts a small test record from userspace. It validates the requested terminal state only after registering, initializing and appending a Bluetooth devcoredump. As a result, an invalid state returns -EINVAL but still leaves queued devcoredump work behind. With a non-zero timeout field, the rejected write can still emit a devcoredump after the timeout expires. Reject unsupported states before allocating the skb or changing the HCI devcoredump state machine. Fixes: ab4e4380d4e1 ("Bluetooth: Add vhci devcoredump support") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/hci_vhci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 2fef08254d78d..22edae3558080 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -337,7 +337,17 @@ static ssize_t force_devcd_write(struct file *file, const char __user *user_buf, if (copy_from_user(&dump_data, user_buf, count)) return -EFAULT; + switch (dump_data.state) { + case HCI_DEVCOREDUMP_DONE: + case HCI_DEVCOREDUMP_ABORT: + case HCI_DEVCOREDUMP_TIMEOUT: + break; + default: + return -EINVAL; + } + data_size = count - offsetof(struct devcoredump_test_data, data); + skb = alloc_skb(data_size, GFP_ATOMIC); if (!skb) return -ENOMEM; From 3a1687e0506be10475db73bf9287635410741669 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 4 Jun 2026 22:52:51 -0500 Subject: [PATCH 1094/3297] fs: efs: remove unneeded debug prints [ Upstream commit 89009392c80da5da00876c8334ff20028e6e3eb6 ] The current code uses debug prints conditionally compiled with #ifdef DEBUG. However, that code, when compiled, causes compiler errors due to incompatible formatters and undefined variables, notably: fs/efs/file.c: In function `efs_get_block': fs/efs/file.c:26:35: error: `block' undeclared (first use in this function); did you mean `iblock'? 26 | __func__, block, inode->i_blocks, inode->i_size); | ^~~~~ and: fs/efs/file.c: In function `efs_bmap': ./include/linux/kern_levels.h:5:25: error: format `%ld' expects argument of type `long int', but argument 4 has type `blkcnt_t' {aka `long long unsigned int'} [-Werror=format=] 5 | #define KERN_SOH "\001" /* ASCII Start Of Header */ | ^~~~~~ which also extends to the other formatters. As this part of the code has been dead for just about 14 years now, it has not been modernized to stay compatible with the most recent gcc compilers. Fix these issues by removing the debug prints. Link: https://lore.kernel.org/20260605035251.89305-2-m32285159@gmail.com Fixes: f403d1dbac6d ("fs/efs: add pr_fmt / use __func__") Signed-off-by: Maxwell Doose Suggested-by: Andrew Morton Cc: Fabian Frederick Cc: Christian Brauner Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/efs/file.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/fs/efs/file.c b/fs/efs/file.c index 9e641da6fab27..9153dfe79bbcc 100644 --- a/fs/efs/file.c +++ b/fs/efs/file.c @@ -18,16 +18,9 @@ int efs_get_block(struct inode *inode, sector_t iblock, if (create) return error; - if (iblock >= inode->i_blocks) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (iblock >= inode->i_blocks) return 0; - } + phys = efs_map_block(inode, iblock); if (phys) map_bh(bh_result, inode->i_sb, phys); @@ -42,16 +35,8 @@ int efs_bmap(struct inode *inode, efs_block_t block) { } /* are we about to read past the end of a file ? */ - if (!(block < inode->i_blocks)) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (!(block < inode->i_blocks)) return 0; - } return efs_map_block(inode, block); } From f704db4b0318a80d973e5e69e56b37e6e9ea093a Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Sun, 7 Jun 2026 21:18:08 +0300 Subject: [PATCH 1095/3297] RDMA/mlx5: Remove DCT restrack tracking [ Upstream commit b136a7af41f796a48665afc6a55907488a3d5500 ] DCT restrack tracking wasn't working to begin with as it was only tracking the first DCT which was added, since at creation the DCT number isn't yet initialized because the DCT FW object is only created during modify. The following DCT additions were failing silently. Since the fix isn't trivial and there were no users that required or complained about this issue we are dropping this for now instead of fixing. Fixes: fd3af5e21866 ("RDMA/mlx5: Track DCT, DCI and REG_UMR QPs as diver_detail resources.") Link: https://patch.msgid.link/r/20260607-restrack-uaf-fix-v1-1-d72e45eb76c2@nvidia.com Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/qp.c | 1 + drivers/infiniband/hw/mlx5/restrack.c | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 88724d15705d4..67294cac625bf 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -3111,6 +3111,7 @@ static int create_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, switch (qp->type) { case MLX5_IB_QPT_DCT: + rdma_restrack_no_track(&qp->ibqp.res); err = create_dct(dev, pd, qp, params); break; case MLX5_IB_QPT_DCI: diff --git a/drivers/infiniband/hw/mlx5/restrack.c b/drivers/infiniband/hw/mlx5/restrack.c index 67841922c7b87..00a9bcb2603f0 100644 --- a/drivers/infiniband/hw/mlx5/restrack.c +++ b/drivers/infiniband/hw/mlx5/restrack.c @@ -178,9 +178,6 @@ static int fill_res_qp_entry(struct sk_buff *msg, struct ib_qp *ibqp) ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE, "REG_UMR"); break; - case MLX5_IB_QPT_DCT: - ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE, "DCT"); - break; case MLX5_IB_QPT_DCI: ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE, "DCI"); break; From 1bc1487f7a7f579ddd61e27b2fd226a1418df037 Mon Sep 17 00:00:00 2001 From: Patrisious Haddad Date: Sun, 7 Jun 2026 21:18:09 +0300 Subject: [PATCH 1096/3297] RDMA/mlx5: Remove raw RSS QP restrack tracking [ Upstream commit 666031fed8f0fdfc29b20d125a628c1b0a04cdaf ] Raw RSS QP restrack tracking wasn't working to begin with as it was only tracking the first raw RSS QP which was added, since at creation the raw RSS QP number is reserved so the QP number for this qp type was always zero. The following raw RSS QP additions were always failing silently. Since the fix isn't trivial and there were no users that required or complained about this issue we are dropping this for now instead of fixing. Fixes: 968f0b6f9c01 ("RDMA/mlx5: Consolidate into special function all create QP calls") Link: https://patch.msgid.link/r/20260607-restrack-uaf-fix-v1-2-d72e45eb76c2@nvidia.com Signed-off-by: Patrisious Haddad Reviewed-by: Michael Guralnik Signed-off-by: Edward Srouji Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/qp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 67294cac625bf..49566479f7047 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -3105,6 +3105,7 @@ static int create_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, int err; if (params->is_rss_raw) { + rdma_restrack_no_track(&qp->ibqp.res); err = create_rss_raw_qp_tir(dev, pd, qp, params); goto out; } From 4b87a2497276a72fd63028e7419abf0fb7ed837b Mon Sep 17 00:00:00 2001 From: Maher Sanalla Date: Thu, 11 Jun 2026 15:50:42 +0300 Subject: [PATCH 1097/3297] RDMA/mlx5: Fix undefined shift of user RQ WQE size [ Upstream commit d881d60223aac8fdc12b227d89c76e131e92a9cd ] set_rq_size() computes the RQ WQE size as "1 << rq_wqe_shift" based on the user-provided rq_wqe_shift, which is only checked to be greater than 32, so shifts of 32 are still accepted. A shift of 31 also overflows a signed integer, leading to undefined behavior. Use check_shl_overflow() to compute the RQ WQE size and reject any invalid values. Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") Link: https://patch.msgid.link/r/20260611-maher-sec-fixes-v1-1-cd8eb2542869@nvidia.com Signed-off-by: Maher Sanalla Signed-off-by: Edward Srouji Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/qp.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 49566479f7047..02a0f4920cabf 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -451,16 +451,13 @@ static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap, if (ucmd) { qp->rq.wqe_cnt = ucmd->rq_wqe_count; - if (ucmd->rq_wqe_shift > BITS_PER_BYTE * sizeof(ucmd->rq_wqe_shift)) - return -EINVAL; qp->rq.wqe_shift = ucmd->rq_wqe_shift; - if ((1 << qp->rq.wqe_shift) / - sizeof(struct mlx5_wqe_data_seg) < - wq_sig) + if (check_shl_overflow(1, qp->rq.wqe_shift, &wqe_size)) + return -EINVAL; + if (wqe_size / sizeof(struct mlx5_wqe_data_seg) < wq_sig) return -EINVAL; qp->rq.max_gs = - (1 << qp->rq.wqe_shift) / - sizeof(struct mlx5_wqe_data_seg) - + wqe_size / sizeof(struct mlx5_wqe_data_seg) - wq_sig; qp->rq.max_post = qp->rq.wqe_cnt; } else { From d3ff718c0c7153e2641e6a09507bace14fc5c402 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Thu, 11 Jun 2026 13:20:15 +0300 Subject: [PATCH 1098/3297] =?UTF-8?q?RDMA/mlx5:=20Release=20the=20HW?= =?UTF-8?q?=E2=80=91provided=20UAR=20index=20rather=20than=20the=20SW=20on?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 449ae7927152e46acbe5f19f97eafdae6d3a96b1 ] Free the UAR index returned by the hardware. Fixes: 4ed131d0bb15 ("IB/mlx5: Expose dynamic mmap allocation") Link: https://patch.msgid.link/r/20260611-fix-uar-release-v1-1-f5464d845dbf@nvidia.com Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mlx5/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 012386e8cdd4a..09709ae5bd471 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2460,7 +2460,7 @@ static int uar_mmap(struct mlx5_ib_dev *dev, enum mlx5_ib_mmap_cmd cmd, if (!dyn_uar) return err; - mlx5_cmd_uar_dealloc(dev->mdev, idx, context->devx_uid); + mlx5_cmd_uar_dealloc(dev->mdev, uar_index, context->devx_uid); free_bfreg: mlx5_ib_free_bfreg(dev, bfregi, bfreg_dyn_idx); From cb527e063a32ee422dc2766a9aca620bf903ffbf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:23:06 +0200 Subject: [PATCH 1099/3297] ASoC: SOF: Intel: hda-sdw-bpt: select SND_SOF_SOF_HDA_SDW_BPT properly [ Upstream commit 999ec4c29d73ac85b639a31bbc85ad2ec00eb9d7 ] When SND_SOC_SOF_INTEL_LNL is set, SND_SOF_SOF_HDA_SDW_BPT must also be enabled, in order to let the soundwire support call into it. However, there are configurations with SND_SOF_SOF_HDA_SDW_BPT=m and SND_SOF_SOF_HDA_SDW_BPT=m but SOUNDWIRE_INTEL=y, which still lead to a link failure: aarch64-linux-ld: drivers/soundwire/intel_ace2x.o: in function `intel_ace2x_bpt_wait': intel_ace2x.c:(.text+0xfc8): undefined reference to `hda_sdw_bpt_wait' aarch64-linux-ld: drivers/soundwire/intel_ace2x.o: in function `intel_ace2x_bpt_send_async': intel_ace2x.c:(.text+0x1ff8): undefined reference to `hda_sdw_bpt_get_buf_size_alignment' Address this by moving the 'select SND_SOF_SOF_HDA_SDW_BPT' into SND_SOC_SOF_HDA_GENERIC. Fixes: 614d416dd8ae ("ASoC: SOF: Intel: hda-sdw-bpt: fix SND_SOF_SOF_HDA_SDW_BPT dependencies") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260611132310.137688-2-arnd@kernel.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 4f27f8c8debf8..b299bcd80904d 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -266,10 +266,8 @@ config SND_SOC_SOF_METEORLAKE config SND_SOC_SOF_INTEL_LNL tristate - select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE - select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE != n select SND_SOC_SOF_IPC4 select SND_SOC_SOF_INTEL_MTL @@ -312,6 +310,8 @@ config SND_SOC_SOF_HDA_GENERIC select SND_SOC_SOF_HDA_LINK_BASELINE select SND_SOC_SOF_HDA_PROBES select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK + select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_LNL != n && \ + SND_SOC_SOF_INTEL_SOUNDWIRE !=n help This option is not user-selectable but automagically handled by 'select' statements at a higher level. From 9131e4b023e0db5764680034bdc94aeae0b0f33d Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 9 Jun 2026 21:43:13 +0900 Subject: [PATCH 1100/3297] ASoC: codecs: hdac_hdmi: Validate written enum value [ Upstream commit 0b08baeccdcf52fad328ad645f5b4fbee04eea34 ] hdac_hdmi_set_pin_port_mux() uses the written enum value to index the texts array before calling snd_soc_dapm_put_enum_double(), which validates that the value is within the enum item range. An out-of-range value can therefore make the driver read past the texts array before the helper rejects the write. Move the lookup after the helper has accepted the value. Fixes: 4a3478debf36 ("ASoC: hdac_hdmi: Add jack reporting") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260609124317.38046-2-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/hdac_hdmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e50afd0bfcecc..2385e18127a33 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -907,12 +907,14 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; - const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + const char *cvt_name; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); if (ret < 0) return ret; + cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + if (port == NULL) return -EINVAL; From 0f1510e84d7bfc3eb9538efa65c6ea0aadf1078c Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 9 Jun 2026 21:43:15 +0900 Subject: [PATCH 1101/3297] ASoC: fsl: fsl_audmix: Validate written enum values [ Upstream commit 3cd17e4e2871114d5579fa7bc8da66faf7fc1930 ] fsl_audmix_put_mix_clk_src() and fsl_audmix_put_out_src() convert the user-provided enum item with snd_soc_enum_item_to_val() before checking whether the item is within the enum's item count. The generic snd_soc_put_enum_double() helper performs that validation, but these callbacks use the converted value first: the clock-source path tests it with BIT(), and the output-source path indexes the prms transition table with it. Reject out-of-range enum items before converting them. Fixes: be1df61cf06e ("ASoC: fsl: Add Audio Mixer CPU DAI driver") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260609124317.38046-4-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/fsl/fsl_audmix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 7981d598ba139..d9b0bd61755d5 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -117,6 +117,9 @@ static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol, unsigned int *item = ucontrol->value.enumerated.item; unsigned int reg_val, val, mix_clk; + if (item[0] >= e->items) + return -EINVAL; + /* Get current state */ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK) @@ -157,6 +160,9 @@ static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol, unsigned int reg_val, val, mask = 0, ctr = 0; int ret; + if (item[0] >= e->items) + return -EINVAL; + /* Get current state */ reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR); From e098c9c6477dfa3cb363282100108484ceba5cc5 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 9 Jun 2026 21:43:16 +0900 Subject: [PATCH 1102/3297] ASoC: tegra: tegra210_ahub: Validate written enum value [ Upstream commit 1d8aabb413b5638670dfd1162169edc0ba276a2e ] tegra_ahub_put_value_enum() reads e->values[item[0]] before checking whether item[0] is within the enum item range. The existing check therefore happens too late to prevent an out-of-range read of the values array. Move the check before the array access. Fixes: 16e1bcc2caf4 ("ASoC: tegra: Add Tegra210 based AHUB driver") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260609124317.38046-5-sammiee5311@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/tegra/tegra210_ahub.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 01d60a74ad1c3..0f756ea474ce9 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -60,13 +60,15 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl, struct soc_enum *e = (struct soc_enum *)kctl->private_value; struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { }; unsigned int *item = uctl->value.enumerated.item; - unsigned int value = e->values[item[0]]; + unsigned int value; unsigned int i, bit_pos, reg_idx = 0, reg_val = 0; int change = 0; if (item[0] >= e->items) return -EINVAL; + value = e->values[item[0]]; + if (value) { /* Get the register index and value to set */ reg_idx = (value - 1) / (8 * cmpnt->val_bytes); From 10e05634ddc19953d7357a39eebee4e142dc8397 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 8 Jun 2026 11:22:08 +0400 Subject: [PATCH 1103/3297] net: dsa: qca8k: fix led devicename when using external mdio bus [ Upstream commit 0b7b378ce6cafbb948786cb6f17f406d94016c8c ] The qca8k dsa switch can use either an external or internal mdio bus. This depends on whether the mdio node is defined under the switch node itself. Upon registering the internal mdio bus, the internal_mdio_bus of the dsa switch is assigned to this bus. When an external mdio bus is used, the driver still uses the internal_mdio_bus id which is used to create the device names of the leds. This leads to the leds being prefixed with '(efault)' as the internal_mii_bus is null. So let's fix this by adding a null check and use the devicename of the external bus instead when an external bus is configured. Fixes: 1e264f9d2918 ("net: dsa: qca8k: add LEDs basic support") Signed-off-by: George Moussalem Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260608-qca8k-leds-fix-v3-1-a915bb2f37ae@outlook.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/qca/qca8k-leds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/qca/qca8k-leds.c b/drivers/net/dsa/qca/qca8k-leds.c index 43ac68052baf9..ef496e345a4e7 100644 --- a/drivers/net/dsa/qca/qca8k-leds.c +++ b/drivers/net/dsa/qca/qca8k-leds.c @@ -429,7 +429,8 @@ qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int p init_data.fwnode = led; init_data.devname_mandatory = true; init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d", - priv->internal_mdio_bus->id, + priv->internal_mdio_bus ? + priv->internal_mdio_bus->id : priv->bus->id, port_num); if (!init_data.devicename) { fwnode_handle_put(led); From 0a8b5b74f0e6b6b9ce453bcfa4baa502c4c7577a Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 10 Jun 2026 06:18:39 -0400 Subject: [PATCH 1104/3297] net/sched: cls_flow: Dont expose folded kernel pointers [ Upstream commit f294fc71c4a0fa4964f6428a1b4e7929c1d83125 ] The flow classifier falls back to addr_fold() for fields that are missing from packet headers. In map mode, userspace controls mask, xor, rshift, addend and divisor, and can observe the resulting classid through class statistics. This allows a tc classifier in a user/network namespace to recover the 32-bit folded value of skb->sk, skb_dst() or skb_nfct(). Align with standard kernel practices for pointer hashing and replace the XOR folding with a keyed siphash (which is cryptographically secure) Fixes: e5dfb815181f ("[NET_SCHED]: Add flow classifier") Reported-by: Kyle Zeng Tested-by: Kyle Zeng Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260610101839.14135-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_flow.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index edf1252c1fde7..9b472578bf75f 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -57,11 +58,15 @@ struct flow_filter { struct rcu_work rwork; }; +static siphash_aligned_key_t flow_keys_secret __read_mostly; + static inline u32 addr_fold(void *addr) { - unsigned long a = (unsigned long)addr; - - return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0); +#ifdef CONFIG_64BIT + return (u32)siphash_1u64((u64)addr, &flow_keys_secret); +#else + return (u32)siphash_1u32((u32)addr, &flow_keys_secret); +#endif } static u32 flow_get_src(const struct sk_buff *skb, const struct flow_keys *flow) @@ -596,6 +601,7 @@ static int flow_init(struct tcf_proto *tp) return -ENOBUFS; INIT_LIST_HEAD(&head->filters); rcu_assign_pointer(tp->root, head); + net_get_random_once(&flow_keys_secret, sizeof(flow_keys_secret)); return 0; } From bb4a5b3c91af3c8d705bb2e9f6f8069a70db26fc Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 10 Jun 2026 06:17:19 +0000 Subject: [PATCH 1105/3297] net: fib_rules: Don't dump dying fib_rule in fib_rules_dump(). [ Upstream commit 2821e85c058f81c9948a2fb1a634f7b47457d51c ] rocker_router_fib_event() calls fib_rule_get() during RCU dump. If the fib_rule is dying, refcount_inc() will complain about it. Let's call refcount_inc_not_zero() in fib_rules_dump(). Fixes: 5d7bfd141924 ("ipv4: fib_rules: Dump FIB rules when registering FIB notifier") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Reviewed-by: David Ahern Link: https://patch.msgid.link/20260610061744.2030996-3-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/fib_rules.h | 5 +++++ net/core/fib_rules.c | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 6e68e359ad18c..7dee0ae616e32 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -111,6 +111,11 @@ static inline void fib_rule_get(struct fib_rule *rule) refcount_inc(&rule->refcnt); } +static inline bool fib_rule_get_safe(struct fib_rule *rule) +{ + return refcount_inc_not_zero(&rule->refcnt); +} + static inline void fib_rule_put(struct fib_rule *rule) { if (refcount_dec_and_test(&rule->refcnt)) diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 8ca634964e363..cf374c2087325 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -349,7 +349,7 @@ int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, if (err != -EAGAIN) { if ((arg->flags & FIB_LOOKUP_NOREF) || - likely(refcount_inc_not_zero(&rule->refcnt))) { + likely(fib_rule_get_safe(rule))) { arg->rule = rule; goto out; } @@ -410,8 +410,12 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family, if (!ops) return -EAFNOSUPPORT; list_for_each_entry_rcu(rule, &ops->rules_list, list) { + if (!fib_rule_get_safe(rule)) + continue; + err = call_fib_rule_notifier(nb, FIB_EVENT_RULE_ADD, rule, family, extack); + fib_rule_put(rule); if (err) break; } From a090880c1f544589427e5b7050c40fb211ccecb4 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 8 Jun 2026 23:51:16 -0700 Subject: [PATCH 1106/3297] bridge: cfm: reject invalid CCM interval at configuration time [ Upstream commit f3e02edd8322b31b8e6517faa6ba053bf29d1e26 ] ccm_tx_work_expired() re-arms itself via queue_delayed_work() using the configured exp_interval converted by interval_to_us(). When exp_interval is BR_CFM_CCM_INTERVAL_NONE or out of range, interval_to_us() returns 0, causing the worker to fire immediately in a tight loop that allocates skbs until OOM. Fix this by validating exp_interval at configuration time: - Constrain IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL to the valid range [BR_CFM_CCM_INTERVAL_3_3_MS, BR_CFM_CCM_INTERVAL_10_MIN] in the netlink policy so userspace cannot set an invalid value. - Reject starting CCM TX in br_cfm_cc_ccm_tx() when exp_interval has not yet been configured (defaults to 0 from kzalloc). Fixes: 2be665c3940d ("bridge: cfm: Netlink SET configuration Interface.") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260609065116.2818837-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_cfm.c | 6 ++++++ net/bridge/br_cfm_netlink.c | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c index f4ca77d9b0e96..e1de280bacc1b 100644 --- a/net/bridge/br_cfm.c +++ b/net/bridge/br_cfm.c @@ -805,6 +805,12 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance, goto save; } + if (!interval_to_us(mep->cc_config.exp_interval)) { + NL_SET_ERR_MSG_MOD(extack, + "Invalid CCM interval"); + return -EINVAL; + } + /* Start delayed work to transmit CCM frames. It is done with zero delay * to send first frame immediately */ diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c index 2faab44652e7c..91b9922dc3f25 100644 --- a/net/bridge/br_cfm_netlink.c +++ b/net/bridge/br_cfm_netlink.c @@ -34,7 +34,9 @@ br_cfm_cc_config_policy[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1] = { [IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NLA_U32 }, [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NLA_U32 }, - [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = + NLA_POLICY_RANGE(NLA_U32, BR_CFM_CCM_INTERVAL_3_3_MS, + BR_CFM_CCM_INTERVAL_10_MIN), [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = { .type = NLA_BINARY, .len = CFM_MAID_LENGTH }, }; From ed8605c6f39b9b84f9a4631db6deb25e7f1e973c Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 9 Jun 2026 18:14:28 -0400 Subject: [PATCH 1107/3297] sctp: validate embedded address parameter length [ Upstream commit e9361d0ca55c4af12aac09e2572852fa91046229 ] sctp_verify_asconf() and sctp_verify_param() only validate ADD_IP, DEL_IP, and SET_PRIMARY parameters against a fixed minimum size of sizeof(struct sctp_addip_param) + sizeof(struct sctp_paramhdr). This ensures the outer parameter is large enough to contain an embedded address parameter header, but does not verify that the embedded address parameter's declared length fits within the bounds of the outer parameter. Later, sctp_process_param() and sctp_process_asconf_param() extract the embedded address parameter and pass it to af->from_addr_param(), which uses the address parameter length to parse the variable-length address payload. A malformed peer can therefore advertise an embedded address parameter length that exceeds the remaining bytes in the enclosing parameter. Validate that addr_param->p.length does not exceed the space available after the sctp_addip_param header before processing the embedded address parameter. Reject malformed parameters when the embedded address length extends beyond the enclosing parameter bounds. This prevents out-of-bounds reads when parsing malformed parameters carried in INIT or ASCONF processing paths. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: sashiko Signed-off-by: Xin Long Link: https://patch.msgid.link/7838b86b69f52add28808fb59034c8f992e97b2d.1781043268.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/sm_make_chunk.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 51affa4fd396b..6fb18b07b6b01 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2641,6 +2641,9 @@ static int sctp_process_param(struct sctp_association *asoc, goto fall_through; addr_param = param.v + sizeof(struct sctp_addip_param); + if (ntohs(addr_param->p.length) > + ntohs(param.p->length) - sizeof(struct sctp_addip_param)) + break; af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (!af) @@ -3039,13 +3042,16 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, union sctp_addr addr; struct sctp_af *af; - addr_param = (void *)asconf_param + sizeof(*asconf_param); - if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP && asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP && asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY) return SCTP_ERROR_UNKNOWN_PARAM; + addr_param = (void *)asconf_param + sizeof(*asconf_param); + if (ntohs(addr_param->p.length) > + ntohs(asconf_param->param_hdr.length) - sizeof(*asconf_param)) + return SCTP_ERROR_PROTO_VIOLATION; + switch (addr_param->p.type) { case SCTP_PARAM_IPV6_ADDRESS: if (!asoc->peer.ipv6_address) From 51a1d9836acc76a1bd16170b2f4b35f11036593f Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 9 Jun 2026 23:22:45 +0000 Subject: [PATCH 1108/3297] net: pfcp: allocate per-cpu tstats for PFCP netdevs [ Upstream commit 24041543da8cd84eb5d8ae738c534372fff54820 ] PFCP uses dev_get_tstats64() as its ndo_get_stats64 callback, but pfcp_link_setup() does not request NETDEV_PCPU_STAT_TSTATS. The net core therefore leaves dev->tstats NULL for PFCP devices. Creating a PFCP rtnetlink device can immediately ask the new netdev for stats while building the RTM_NEWLINK notification. That reaches dev_get_tstats64() and dereferences the NULL dev->tstats pointer. Set pcpu_stat_type to NETDEV_PCPU_STAT_TSTATS during PFCP link setup so the net core allocates the storage expected by dev_get_tstats64(). Fixes: 76c8764ef36a ("pfcp: add PFCP module") Signed-off-by: Samuel Moelius Reviewed-by: Alexander Lobakin Link: https://patch.msgid.link/20260609232244.1602027.c569f6c530f6.pfcp-missing-tstats-link-create-oops@trailofbits.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/pfcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c index 28e6bc4a1f14c..a6aa30ae0af76 100644 --- a/drivers/net/pfcp.c +++ b/drivers/net/pfcp.c @@ -148,6 +148,7 @@ static void pfcp_link_setup(struct net_device *dev) dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->priv_flags |= IFF_NO_QUEUE; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; netif_keep_dst(dev); } From 66dbb13eeb2fc339f8f548a9076be4c1a94857b0 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 10:28:24 -0300 Subject: [PATCH 1109/3297] net/sched: sch_hfsc: Don't make class passive twice [ Upstream commit 90b662ea25f5e83bb3b8ccec5b93ced810b92fb8 ] update_vf() is called from two places for the same class during a single dequeue when the class's child qdisc (e.g. codel/fq_codel) drops its last packets while dequeuing: 1. The child calls qdisc_tree_reduce_backlog(), which, now that the child is empty, invokes hfsc_qlen_notify() -> update_vf(cl, 0, 0) and turns the class passive (cl_nactive is decremented up the hierarchy). 2. hfsc_dequeue() then calls update_vf(cl, qdisc_pkt_len(skb), cur_time) to charge the dequeued bytes. On the second call the class is already passive, but its child qdisc is still empty, so update_vf() arms go_passive again: if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC) go_passive = 1; The leaf is then skipped by the cl_nactive == 0 check inside the loop, which does not clear go_passive, so the stale go_passive propagates to the parent and decrements its cl_nactive a second time. A parent that still has other active children is driven to cl_nactive == 0 and removed from the vttree, even though those siblings are still backlogged. They are never dequeued again and the qdisc stalls. Fix this by only arming go_passive when the class is actually active, so an already-passive class no longer triggers a second passive transition. The byte accounting (cl->cl_total += len) still runs for every ancestor, so dequeued bytes continue to be counted exactly once. Fixes: 51eb3b65544c ("sch_hfsc: make hfsc_qlen_notify() idempotent") Reported-by: Anirudh Gupta Closes: https://lore.kernel.org/netdev/CAN2cbVe79oj0O9==m4+4x3v+O+qzRagA=2=wkrp9i9=CqYvyZA@mail.gmail.com/ Tested-by: Anirudh Gupta Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610132824.3027549-1-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_hfsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 9c6e6d2b47f8e..601f99dd989ae 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -753,7 +753,7 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time) u64 f; /* , myf_bound, delta; */ int go_passive = 0; - if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC) + if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC && cl->cl_nactive) go_passive = 1; for (; cl->cl_parent != NULL; cl = cl->cl_parent) { From cebaefe1aceb650d5c99a4c0e1a4dde09e211818 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 10 Jun 2026 08:40:01 -0400 Subject: [PATCH 1110/3297] tipc: require net admin for TIPCv2 netlink mutators [ Upstream commit 86b0c540e2ea397cde021eecd24145f7c16a3d4e ] TIPCv2 registers mutating generic-netlink operations without admin permission flags. Generic netlink only checks CAP_NET_ADMIN when an operation sets GENL_ADMIN_PERM or GENL_UNS_ADMIN_PERM, so a local unprivileged process can currently change TIPC state through commands such as TIPC_NL_NET_SET, TIPC_NL_KEY_SET, TIPC_NL_KEY_FLUSH, and bearer enable/disable. The legacy TIPC netlink API already checks netlink_net_capable(..., CAP_NET_ADMIN) for administrative commands. Give the TIPCv2 mutators the equivalent generic-netlink gate. Use GENL_UNS_ADMIN_PERM, which maps to the same namespace-aware CAP_NET_ADMIN check that netlink_net_capable() performs, so the behaviour matches the legacy path and keeps working for CAP_NET_ADMIN holders in a non-initial user namespace (containers). A QEMU/KASAN repro run as uid/gid 65534 with zero effective capabilities previously succeeded in changing the network id and node identity, setting and flushing key material, and enabling/disabling a UDP bearer. With this patch applied the same operations fail with -EPERM. Fixes: 0655f6a8635b ("tipc: add bearer disable/enable to new netlink api") Link: https://lore.kernel.org/all/20260604163102.2658553-1-dominik.czarnota@trailofbits.com/ Signed-off-by: Michael Bommarito Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260610124003.3831170-2-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/netlink.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 1a9a5bdaccf4f..8336a9664703f 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -152,11 +152,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_DISABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_disable, }, { .cmd = TIPC_NL_BEARER_ENABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_enable, }, { @@ -168,11 +170,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_ADD, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_add, }, { .cmd = TIPC_NL_BEARER_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_set, }, { @@ -197,11 +201,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_LINK_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_link, }, { .cmd = TIPC_NL_LINK_RESET_STATS, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_reset_link_stats, }, { @@ -213,6 +219,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MEDIA_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_media_set, }, { @@ -228,6 +235,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_NET_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_net_set, }, { @@ -238,6 +246,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MON_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_monitor, }, { @@ -255,6 +264,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_PEER_REMOVE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_peer_rm, }, #ifdef CONFIG_TIPC_MEDIA_UDP @@ -269,11 +279,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_KEY_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_key, }, { .cmd = TIPC_NL_KEY_FLUSH, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_flush_key, }, #endif From 3cfa3d8e0dc167850edeb5bf5a07757db83fd54e Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 10 Jun 2026 08:40:02 -0400 Subject: [PATCH 1111/3297] tipc: prevent snt_unacked underflow on CONN_ACK [ Upstream commit ab3e10b44ba5411779aac7afd2477917dd77750f ] tipc_sk_conn_proto_rcv() subtracts the peer-supplied connection ack count from the unsigned 16-bit send counter snt_unacked without checking that it does not exceed the number of messages actually outstanding: tsk->snt_unacked -= msg_conn_ack(hdr); msg_conn_ack() is read straight from a received CONN_MANAGER/CONN_ACK message. If the ack count is larger than snt_unacked, the subtraction wraps to a near-maximum value, leaving tsk_conn_cong() permanently true and starving the connection of further transmits. Validate the ACK count at the start of the CONN_ACK block and drop the message if it acknowledges more messages than are outstanding. A peer (or, for a local connection, the connected peer socket) can otherwise wedge a TIPC connection's send side by sending an oversized connection ack. Fixes: 10724cc7bb78 ("tipc: redesign connection-level flow control") Signed-off-by: Michael Bommarito Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260610124003.3831170-3-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/socket.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 3d5de693a2227..edaf44b512190 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1362,6 +1362,9 @@ static void tipc_sk_conn_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb, __skb_queue_tail(xmitq, skb); return; } else if (mtyp == CONN_ACK) { + if (tsk->snt_unacked < msg_conn_ack(hdr)) + goto exit; + was_cong = tsk_conn_cong(tsk); tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr)); tsk->snt_unacked -= msg_conn_ack(hdr); From 7e401233f9bb74a626e70698d0d62f3a94ac0676 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 10 Jun 2026 08:40:03 -0400 Subject: [PATCH 1112/3297] tipc: reject inverted service ranges from peer bindings [ Upstream commit 2afb648f7b99216c687db1f89739c995e1144153 ] tipc_update_nametbl() inserts a binding advertised by a peer node using the lower and upper service-range bounds taken directly from the wire, without checking that lower <= upper. The local bind path validates the ordering (tipc_uaddr_valid()), but the name-distribution path does not. A binding with lower > upper is inserted at the far end of the service-range rbtree (keyed on lower) where no lookup or withdrawal can ever match it (service_range_foreach_match() requires sr->lower <= end). The publication, its service_range node and the augmented rbtree entry are then leaked for the lifetime of the namespace, and there is no per-peer cap equivalent to TIPC_MAX_PUBL on locally created bindings. Reject inverted ranges in the network path as well. A peer node can otherwise leak unbounded binding-table memory by sending PUBLICATION items with lower > upper. Fixes: 37922ea4a310 ("tipc: permit overlapping service ranges in name table") Signed-off-by: Michael Bommarito Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260610124003.3831170-4-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/name_distr.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 190b49c5cbc3e..ba4f4906e13b7 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -280,12 +280,21 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, u32 node, u32 dtype) { struct publication *p = NULL; + u32 lower = ntohl(i->lower); + u32 upper = ntohl(i->upper); struct tipc_socket_addr sk; - struct tipc_uaddr ua; u32 key = ntohl(i->key); + struct tipc_uaddr ua; + + /* A peer-advertised binding with lower > upper can never be matched + * or withdrawn and would leak the publication; the local bind path + * rejects such ranges, so reject ranges learned from the network too. + */ + if (lower > upper) + return false; tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_CLUSTER_SCOPE, - ntohl(i->type), ntohl(i->lower), ntohl(i->upper)); + ntohl(i->type), lower, upper); sk.ref = ntohl(i->port); sk.node = node; From a27481516d32f222b71fa78363c2d31070f2e0b0 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:55 -0700 Subject: [PATCH 1113/3297] cxl/test: Unregister cxl_acpi in cxl_test_init() error path [ Upstream commit 50cc34be04a0ea7522b739c9c7a71367cfbc489c ] In cxl_test_init(), Once cxl_mock_platform_device_add() succeeds, all error paths after needs to call platform_device_unregister() instead of platform_device_put() to clean up. Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reported-by: sashiko-bot Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230355.198912-1-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/cxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 5771c75bc5889..a126a207126b4 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -1526,7 +1526,7 @@ static __init int cxl_test_init(void) return 0; err_root: - platform_device_put(cxl_acpi); + platform_device_unregister(cxl_acpi); err_rch: cxl_rch_topo_exit(); err_single: From 4941205f5fa391c845671a6a655fe72451df29e7 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:05 -0700 Subject: [PATCH 1114/3297] cxl/test: Add check after kzalloc() memory in alloc_mock_res() [ Upstream commit dfe28c8592538152e9611341dae6f7be1735b3f1 ] alloc_mock_res() calls kzalloc() without checking the return value. Add scope based resource management to deal with the allocated memory cleanly. Reported-by: sashiko-bot Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230305.197390-1-dave.jiang@intel.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/cxl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index a126a207126b4..3e5ae5caae94d 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -403,12 +403,16 @@ static void depopulate_all_mock_resources(void) static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) { - struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL); struct genpool_data_align data = { .align = align, }; unsigned long phys; + struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res), + GFP_KERNEL); + if (!res) + return NULL; + INIT_LIST_HEAD(&res->list); phys = gen_pool_alloc_algo(cxl_mock_pool, size, gen_pool_first_fit_align, &data); @@ -423,7 +427,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) list_add(&res->list, &mock_res); mutex_unlock(&mock_res_lock); - return res; + return no_free_ptr(res); } static int populate_cedt(void) From 5f99a396f706afc749448659d1565991330e4f71 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 2 Jun 2026 22:38:26 +0800 Subject: [PATCH 1115/3297] crypto: marvell/octeontx - fix DMA cleanup using wrong loop index [ Upstream commit 7891c64c0520519782470ba29bac8a5761e295d8 ] The sg_cleanup path used list[i] instead of list[j] when unmapping DMA buffers, leaking successfully mapped entries and repeatedly unmapping the failed one. Fixes: 10b4f09491bf ("crypto: marvell - add the Virtual Function driver for CPT") Signed-off-by: Felix Gu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c index c80baf1ad90b2..89030e2711ce4 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c @@ -157,8 +157,8 @@ static inline int setup_sgio_components(struct pci_dev *pdev, sg_cleanup: for (j = 0; j < i; j++) { if (list[j].dma_addr) { - dma_unmap_single(&pdev->dev, list[i].dma_addr, - list[i].size, DMA_BIDIRECTIONAL); + dma_unmap_single(&pdev->dev, list[j].dma_addr, + list[j].size, DMA_BIDIRECTIONAL); } list[j].dma_addr = 0; From fb4d57b83356d4bd411b45ede3024e0ff42c9b5e Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 2 Jun 2026 22:55:35 +0800 Subject: [PATCH 1116/3297] crypto: cavium/cpt - fix DMA cleanup using wrong loop index [ Upstream commit 9dbf173bd32d5f81b005008b682bfb50aa093455 ] The sg_cleanup error path used list[i] instead of list[j] when unmapping DMA buffers, leaking successfully mapped entries and repeatedly unmapping the failed one. Fixes: c694b233295b ("crypto: cavium - Add the Virtual Function driver for CPT") Signed-off-by: Felix Gu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/cavium/cpt/cptvf_reqmanager.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c index fb59bb2824557..f35286bcbd664 100644 --- a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c +++ b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c @@ -108,8 +108,8 @@ static int setup_sgio_components(struct cpt_vf *cptvf, struct buf_ptr *list, sg_cleanup: for (j = 0; j < i; j++) { if (list[j].dma_addr) { - dma_unmap_single(&pdev->dev, list[i].dma_addr, - list[i].size, DMA_BIDIRECTIONAL); + dma_unmap_single(&pdev->dev, list[j].dma_addr, + list[j].size, DMA_BIDIRECTIONAL); } list[j].dma_addr = 0; From 2ba237315193604f41fb5109c06bf0fc61e2c4e4 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 4 Jun 2026 12:30:54 +0800 Subject: [PATCH 1117/3297] crypto: rng - Free default RNG on module exit [ Upstream commit 606ba888b98e0d26a2c4e5c8dc0542e3ad8f0f3a ] When the rng module is removed the default RNG will be leaked. Call crypto_del_default_rng to free it if possible. Fixes: 7cecadb7cca8 ("crypto: rng - Do not free default RNG when it becomes unused") Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- crypto/rng.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crypto/rng.c b/crypto/rng.c index ee1768c5a4005..b021936094b07 100644 --- a/crypto/rng.c +++ b/crypto/rng.c @@ -226,5 +226,16 @@ void crypto_unregister_rngs(struct rng_alg *algs, int count) } EXPORT_SYMBOL_GPL(crypto_unregister_rngs); +static void __exit rng_exit(void) +{ + int err; + + err = crypto_del_default_rng(); + if (err) + pr_err("Failed delete default RNG: %d\n", err); +} + +module_exit(rng_exit); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Random Number Generator"); From 1749fef4bda0f4b6a6c8391cada8e87b6b39397d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 11 Jun 2026 10:57:22 -0300 Subject: [PATCH 1118/3297] ALSA: usb-audio: qcom: Guard sideband endpoint removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2773023abb381e36ce02d364022d901f6f7a416d ] qmi_stop_session() conditionally looks up the cached data and sync endpoints, but removes each endpoint unconditionally. The data endpoint is always present for an active offload stream, while the sync endpoint is optional. When no sync endpoint exists, ep still refers to the data endpoint and the code attempts to remove that endpoint a second time. The current sideband implementation rejects the duplicate removal, but the teardown path should not pass an unrelated endpoint for an absent sync endpoint. Only look up and remove an endpoint when its cached pipe exists, check the lookup result, and clear the cached pipe after handling it. This matches the normal stream-disable path. Fixes: 326bbc348298 ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260611-alsa-usb-qcom-guard-sideband-endpoint-removal-v1-1-00e73787c156@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index e70649c4afcdc..962b5f27f0447 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -794,15 +794,23 @@ static void qmi_stop_session(void) continue; } /* Release XHCI endpoints */ - if (info->data_ep_pipe) + if (info->data_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, info->data_ep_pipe); - xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep); + if (ep) + xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, + ep); + info->data_ep_pipe = 0; + } - if (info->sync_ep_pipe) + if (info->sync_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, info->sync_ep_pipe); - xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep); + if (ep) + xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, + ep); + info->sync_ep_pipe = 0; + } disable_audio_stream(subs); } From dae23c545eb5a2be3b27a82fd0f611894fb8ab69 Mon Sep 17 00:00:00 2001 From: HanQuan Date: Fri, 12 Jun 2026 10:32:22 +0000 Subject: [PATCH 1119/3297] ALSA: seq: Fix kernel heap address leak in bounce_error_event() [ Upstream commit efc86691e4d8083d9e380ea95042c2cf679f65fd ] The comment above bounce_error_event() documents that user clients should receive SNDRV_SEQ_EVENT_BOUNCE with the original event embedded as variable-length data, while kernel clients should receive SNDRV_SEQ_EVENT_KERNEL_ERROR with a quoted kernel pointer. However, the implementation unconditionally uses SNDRV_SEQ_EVENT_KERNEL_ERROR with data.quote.event set to the raw struct snd_seq_event pointer for all clients. When a bounce error event is delivered to a USER_CLIENT via snd_seq_read(), the kernel heap address in data.quote.event is exposed to userspace through copy_to_user() in the fixed-length branch. This is a distinct leak path from the one addressed by commit 705dd6dcbc0e ("ALSA: seq: Clear variable event pointer on read"), which sanitizes data.ext.ptr in the variable-length branch of snd_seq_read(). The bounce_error_event() leak uses fixed-length events that take the else branch where no sanitization occurs. Differentiate the bounce event by client type. For USER_CLIENT, send SNDRV_SEQ_EVENT_BOUNCE with SNDRV_SEQ_EVENT_LENGTH_VARIABLE and data.ext pointing to the original event. The variable-length path in snd_seq_event_dup() copies the event data into chained cells, and snd_seq_expand_var_event() copies only the content -- never the pointer -- to userspace. For KERNEL_CLIENT, keep the existing SNDRV_SEQ_EVENT_KERNEL_ERROR behavior with the quoted pointer. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: HanQuan Link: https://patch.msgid.link/20260612103222.2528305-1-eilaimemedsnaimel@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_clientmgr.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index b8b04fc79f489..0c31b19dd948b 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -538,16 +538,38 @@ static int bounce_error_event(struct snd_seq_client *client, /* set up quoted error */ memset(&bounce_ev, 0, sizeof(bounce_ev)); - bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; - bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + + if (client->type == USER_CLIENT) { + /* + * For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the + * original event embedded as variable-length data. This + * avoids exposing data.quote.event (a kernel pointer) to + * userspace. The variable-length path in snd_seq_event_dup() + * copies the event data from data.ext.ptr into chained cells, + * and snd_seq_expand_var_event() copies only the data content + * -- never the pointer -- to userspace. + */ + bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE; + bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + bounce_ev.data.ext.len = sizeof(struct snd_seq_event); + bounce_ev.data.ext.ptr = (char *)event; + } else { + /* + * For kernel clients, quote the event pointer directly. + * Kernel consumers can safely dereference the pointer. + */ + bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; + bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + bounce_ev.data.quote.origin = event->dest; + bounce_ev.data.quote.event = event; + bounce_ev.data.quote.value = -err; /* use positive value */ + } + bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT; bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; bounce_ev.dest.client = client->number; bounce_ev.dest.port = event->source.port; - bounce_ev.data.quote.origin = event->dest; - bounce_ev.data.quote.event = event; - bounce_ev.data.quote.value = -err; /* use positive value */ result = snd_seq_deliver_single_event(NULL, &bounce_ev, atomic, hop + 1); if (result < 0) { client->event_lost++; From 1c9246a199e19b2fd36e94feed60e55f27103a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20P=C3=B6schel?= Date: Fri, 12 Jun 2026 12:52:44 +0200 Subject: [PATCH 1120/3297] spi: xilinx: use FIFO occupancy register to determine buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 47f3b5365536e8c38f264824ab15fdb74454e066 ] The method the driver uses to determine the size of the FIFO has a problem. What it currently does is this: It stops the SPI hardware and writes to the TX FIFO register until TX FIFO FULL asserts in the status register. But the hardware does not only have the FIFO, it also has a shift register which can hold a byte. This can be seen, when writing a byte to the FIFO (while the SPI hardware is stopped,) the TX FIFO EMPTY is still empty. So, if we have a FIFO size of 16 for example, the current method returns a 17. This is a problem, at least when using the driver in irq mode. The same size determined for the TX FIFO is also assumed for the RX FIFO. When a SPI transaction wants to write the amount of the FIFO size or more bytes, the following happens, for example with 16 bytes FIFO size: The driver stops the SPI hardware and writes 17 bytes to the TX FIFO and starts the SPI hardware and goes sleep. The hardware then shifts out 17 bytes (FIFO + shift register) and simultaneously reads bytes into the RX FIFO, but it only has 16 places, so it looses one byte. Then TX FIFO empty asserts, wakes the driver again, which has a fast path and reads 16 bytes from the RX FIFO, but before reading the last 17th byte (which is lost) it does this: sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); if (!(sr & XSPI_SR_RX_EMPTY_MASK)) { xilinx_spi_rx(xspi); rx_words--; } It reads the status register and checks if the RX FIFO is not empty. But it is empty in our case. So this check spins in a while loop forever locking the driver. This patch fixes the logic to determine the FIFO size. Fixes: 4c9a761402d7 ("spi/xilinx: Simplify spi_fill_tx_fifo") Signed-off-by: Lars Pöschel Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260612105244.9076-1-lars.poeschel.linux@edag.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-xilinx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index c86dc56f38b45..8085571913c8b 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -371,11 +371,18 @@ static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi) xspi->regs + XIPIF_V123B_RESETR_OFFSET); /* Fill the Tx FIFO with as many words as possible */ - do { + while (1) { xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); + if (sr & XSPI_SR_TX_FULL_MASK) + break; + n_words++; - } while (!(sr & XSPI_SR_TX_FULL_MASK)); + } + + /* Handle the NO FIFO case separately */ + if (!n_words) + return 1; return n_words; } From 0c22c009243595cc67d2d296f8512d2e54d2115d Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 3 Jun 2026 14:26:55 -0700 Subject: [PATCH 1121/3297] iommu: Avoid copying the user array twice in the full-array copy helper [ Upstream commit e28bee5b445178390d63f7a93a5a219063c6434e ] iommu_copy_struct_from_full_user_array() copies a whole user array into a kernel buffer. In the common case, where user entry_len equals destination entry size, it takes a fast path and copies the whole array with a single copy_from_user(). That fast path does not return, so it falls through into the item-by-item copy_struct_from_user() loop and copies every entry a second time. For an equal entry_len that loop is just a copy_from_user() of the same bytes, so the whole array is copied twice for no benefit. Return right after the bulk copy. The per-item loop then runs only on the slow path, where entry_len differs and each entry needs size adaption. Fixes: 4f2e59ccb698 ("iommu: Add iommu_copy_struct_from_full_user_array helper") Link: https://patch.msgid.link/r/6c9eca4ff584cb977661e97799ac6fe934e7f51c.1780521606.git.nicolinc@nvidia.com Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Nicolin Chen Reviewed-by: Lu Baolu Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- include/linux/iommu.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 66e4abb2df0dc..84cf41f51f05d 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -544,6 +544,7 @@ iommu_copy_struct_from_full_user_array(void *kdst, size_t kdst_entry_size, user_array->entry_num * user_array->entry_len)) return -EFAULT; + return 0; } /* Copy item by item */ From 7f4aa81f5bb276cfe1800f3f0f0a082cadcbb662 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 4 Jun 2026 20:55:20 +0800 Subject: [PATCH 1122/3297] ASoC: adau1372: Clear PLL_EN on failed PLL lock without reset GPIO [ Upstream commit 69b4141b428bcf2cf7a863950c0d6e5c5ae89ac1 ] The PLL lock failure path in adau1372_set_power() unwinds by putting the regmap back in cache-only mode, asserting the optional power-down GPIO and disabling mclk. adau1372_enable_pll() enables CLK_CTRL.PLL_EN before polling the PLL lock bit. If the lock fails on a board without a power-down GPIO, the error path disables mclk and returns an error, but leaves PLL_EN set in the hardware register. The normal power-off path already handles the no-GPIO case by explicitly clearing PLL_EN. Mirror that cleanup in the PLL lock failure path and clear PLL_EN while the regmap is still live, before switching it back to cache-only mode. Fixes: bfe6a264effc ("ASoC: adau1372: Fix clock leak on PLL lock failure") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260604125520.1428905-1-lgs201920130244@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/adau1372.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c index d7363f9d53bb3..879afeb81c422 100644 --- a/sound/soc/codecs/adau1372.c +++ b/sound/soc/codecs/adau1372.c @@ -813,6 +813,11 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable) if (adau1372->use_pll) { ret = adau1372_enable_pll(adau1372); if (ret) { + if (!adau1372->pd_gpio) + regmap_update_bits(adau1372->regmap, + ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_PLL_EN, + 0); regcache_cache_only(adau1372->regmap, true); if (adau1372->pd_gpio) gpiod_set_value(adau1372->pd_gpio, 1); From d0503357653ee62188987a26baa2d7f3689f367e Mon Sep 17 00:00:00 2001 From: Lucas Tsai Date: Tue, 9 Jun 2026 19:44:03 +0800 Subject: [PATCH 1123/3297] power: supply: core: fix supplied_from allocations [ Upstream commit ba61aed9a34671222d1149acfc2f0179a9ce7e80 ] If dts property power-supplies has multiple values, then accessing to psy->supplied_from[i-1] in __power_supply_populate_supplied_from will overrun supplied_from array. Fixes: f6e0b081fb30 ("power_supply: Populate supplied_from hierarchy from the device tree") Signed-off-by: Lucas Tsai Link: https://patch.msgid.link/20260609114403.3896073-1-lucas_tsai@richtek.com Signed-off-by: Sebastian Reichel Signed-off-by: Sasha Levin --- drivers/power/supply/power_supply_core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 9a28381e2607d..42ad333394d94 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -292,18 +292,13 @@ static int power_supply_check_supplies(struct power_supply *psy) if (cnt == 1) return 0; - /* All supplies found, allocate char ** array for filling */ - psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(*psy->supplied_from), + /* All supplies found, allocate char * array for filling */ + psy->supplied_from = devm_kcalloc(&psy->dev, + cnt - 1, sizeof(*psy->supplied_from), GFP_KERNEL); if (!psy->supplied_from) return -ENOMEM; - *psy->supplied_from = devm_kcalloc(&psy->dev, - cnt - 1, sizeof(**psy->supplied_from), - GFP_KERNEL); - if (!*psy->supplied_from) - return -ENOMEM; - return power_supply_populate_supplied_from(psy); } #else From 67cec2f1eb9e58719d622e92e2278ceda72dbd85 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 9 Jun 2026 10:18:31 -0400 Subject: [PATCH 1124/3297] handshake: Require admin permission for DONE command [ Upstream commit 81246a65303d9635266b1334490142caaf86a11f ] ACCEPT and DONE are the two downcalls of the handshake genl family, both intended for use by the trusted handshake agent (tlshd). ACCEPT already requires GENL_ADMIN_PERM; DONE has no privilege check at all. The fd-lookup in handshake_nl_done_doit() only confirms that some pending handshake request exists for the supplied sockfd; it does not authenticate the sender. An unprivileged process that guesses or observes a valid sockfd can therefore submit a DONE with HANDSHAKE_A_DONE_STATUS == 0, leaving the kernel consumer to proceed as if the handshake succeeded. A non-zero status on a forged DONE tears down a legitimate in-flight handshake before tlshd can report its real result. Fixes: 3b3009ea8abb ("net/handshake: Create a NETLINK service for handling handshake requests") Reviewed-by: Jeff Layton Reviewed-by: Hannes Reinecke Signed-off-by: Chuck Lever Link: https://patch.msgid.link/20260609141831.90694-1-cel@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/netlink/specs/handshake.yaml | 1 + net/handshake/genl.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml index 1024297b38513..ffec12b467597 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -125,6 +125,7 @@ operations: name: done doc: Handler reports handshake completion attribute-set: done + flags: [admin-perm] do: request: attributes: diff --git a/net/handshake/genl.c b/net/handshake/genl.c index a5fa8b27f2242..7ec805ae3d7d6 100644 --- a/net/handshake/genl.c +++ b/net/handshake/genl.c @@ -37,7 +37,7 @@ static const struct genl_split_ops handshake_nl_ops[] = { .doit = handshake_nl_done_doit, .policy = handshake_done_nl_policy, .maxattr = HANDSHAKE_A_DONE_REMOTE_AUTH, - .flags = GENL_CMD_CAP_DO, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, }; From ce311bd2e36596f0aa2c92ca86fb3e019ac57eae Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Thu, 11 Jun 2026 09:29:18 +0000 Subject: [PATCH 1125/3297] tcp: clear sock_ops cb flags before force-closing a child socket [ Upstream commit 990348e5bb457697c2f1f7f7b65154a3334d9d2b ] A child socket inherits the listener's bpf_sock_ops_cb_flags via sk_clone_lock(). If its setup fails in tcp_v4_syn_recv_sock() / tcp_v6_syn_recv_sock(), the child is freed through put_and_exit, where inet_csk_prepare_forced_close() drops the socket lock and tcp_done() runs without it. If BPF_SOCK_OPS_STATE_CB_FLAG was inherited, tcp_done() -> tcp_set_state() calls tcp_call_bpf(), which expects the lock and trips sock_owned_by_me(): WARNING: include/net/sock.h:1799 at tcp_set_state+0x433/0x550 RIP: 0010:tcp_set_state+0x433/0x550 include/net/sock.h:1799 Call Trace: tcp_done+0xba/0x250 net/ipv4/tcp.c:5095 tcp_v4_syn_recv_sock+0x850/0xa50 net/ipv4/tcp_ipv4.c:1787 tcp_check_req+0xf30/0x1360 net/ipv4/tcp_minisocks.c:926 tcp_v4_rcv+0x1047/0x1b50 net/ipv4/tcp_ipv4.c:2164 The child is freed before it is ever established, so it should run no sock_ops callback. Clear its cb flags in inet_csk_prepare_for_destroy_sock(), the common point for the IPv4, IPv6 and chtls forced-close paths and for the MPTCP ->syn_recv_sock() failure path (dispose_child), which reaches tcp_done() on a child that was never established too. Suggested-by: Jiayuan Chen Fixes: d44874910a26 ("bpf: Add BPF_SOCK_OPS_STATE_CB") Signed-off-by: Sechang Lim Reviewed-by: Jiayuan Chen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260611092923.1895982-1-rhkrqnwk98@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/tcp.h | 9 +++++++++ net/ipv4/inet_connection_sock.c | 1 + 2 files changed, 10 insertions(+) diff --git a/include/net/tcp.h b/include/net/tcp.h index f460d2c391deb..5999786c42e1e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2863,6 +2863,11 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, return tcp_call_bpf(sk, op, 3, args); } +static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk) +{ + tcp_sk(sk)->bpf_sock_ops_cb_flags = 0; +} + #else static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args) { @@ -2880,6 +2885,10 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, return -EPERM; } +static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk) +{ +} + #endif static inline u32 tcp_timeout_init(struct sock *sk) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 4aa5cdef8e42c..1679bfefa560d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1301,6 +1301,7 @@ EXPORT_SYMBOL(inet_csk_destroy_sock); void inet_csk_prepare_for_destroy_sock(struct sock *sk) { /* The below has to be done to allow calling inet_csk_destroy_sock */ + tcp_clear_sock_ops_cb_flags(sk); sock_set_flag(sk, SOCK_DEAD); tcp_orphan_count_inc(); } From a05d638b607460514bd0a8afae64be71df15a04d Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 11 Jun 2026 18:36:48 +0200 Subject: [PATCH 1126/3297] virtio_net: do not allow tunnel csum offload for non GSO packets [ Upstream commit 86c51f0f23136ea5ef5541f607287e07150cd23f ] Fiona reports broken connectivity for virtio net setup using UDP tunnel inside the guest and NIC with not UDP tunnel TSO support in the host. Currently the virtio_net driver exposes csum offload for UDP-tunneled, TCP non GSO packets. Such packet reach the host as CSUM_PARTIAL ones with the 'encapsulation' flag cleared, as the virtio specification do not support this specific kind of offload. HW NICs with UDP tunnel TSO support - and those drivers directly accessing skb->csum_start/csum_offset - are still capable of computing the needed csum correctly, but otherwise the packets reach the wire with bad csum on both the inner and outer transport header. Address the issue explicitly disabling csum offload for UDP tunneled, non GSO packets via the ndo_features_check op. Fixes: 56a06bd40fab ("virtio_net: enable gso over UDP tunnel support.") Reported-by: Fiona Ebner Closes: https://bugzilla.proxmox.com/show_bug.cgi?id=7627 Tested-by: Fiona Ebner Tested-by: Gabriel Goller Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Abeni Reviewed-by: Gabriel Goller Tested-by: Gabriel Goller Link: https://patch.msgid.link/6c3b6c47fb05c100f384630dc48f3975cf37b67a.1781195144.git.pabeni@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index aed65dbf3fca0..8f7872d65a616 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -6280,6 +6280,19 @@ static void virtnet_free_irq_moder(struct virtnet_info *vi) rtnl_unlock(); } +static netdev_features_t virtnet_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + /* Inner csum offload is only available for GSO packets. */ + if (skb->encapsulation && + (!skb_is_gso(skb) || netif_needs_gso(skb, features))) + return features & ~NETIF_F_CSUM_MASK; + + /* Passthru. */ + return features; +} + static const struct net_device_ops virtnet_netdev = { .ndo_open = virtnet_open, .ndo_stop = virtnet_close, @@ -6293,7 +6306,7 @@ static const struct net_device_ops virtnet_netdev = { .ndo_bpf = virtnet_xdp, .ndo_xdp_xmit = virtnet_xdp_xmit, .ndo_xsk_wakeup = virtnet_xsk_wakeup, - .ndo_features_check = passthru_features_check, + .ndo_features_check = virtnet_features_check, .ndo_get_phys_port_name = virtnet_get_phys_port_name, .ndo_set_features = virtnet_set_features, .ndo_tx_timeout = virtnet_tx_timeout, From 0500af8630c3253f0dde879bd3a73a06bb2f2b3f Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 16:28:52 -0300 Subject: [PATCH 1127/3297] net/sched: sch_fq_codel: Do not call qdisc_tree_reduce_backlog during peek before restoring qlen [ Upstream commit 097f6fc7b1ae362dd7a9444b2572162fda73b284 ] Whenever fq_codel drops packets during peek, it calls qdisc_tree_reduce_backlog. An issue arises because it calls qdisc_tree_reduce_backlog before it reincrements the qlen. If qlen drops to zero, but peek returns an skb, the parent's qlen_notify callback will be executed even though fq_codel still has 1 packet on the queue and, thus, will mistakenly deactivate the parent's class causing issues like a recent report [1] and a wild memory access in qfq: [ 29.371146][ T360] Oops: general protection fault, probably for non-canonical address 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI [ 29.371666][ T360] KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127] [ 29.371987][ T360] CPU: 6 UID: 0 PID: 360 Comm: tc Not tainted 7.1.0-rc5-00285-gc530e5b2dbc6-dirty #82 PREEMPT(full) [ 29.372384][ T360] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 29.372620][ T360] RIP: 0010:qfq_deactivate_agg (include/linux/list.h:1029 (discriminator 2) include/linux/list.h:1043 (discriminator 2) net/sched/sch_qfq.c:1369 (discriminator 2) net/sched/sch_qfq.c:1395 (discriminator 2)) sch_qfq [ 29.373544][ T360] RSP: 0018:ffff888102417370 EFLAGS: 00010216 [ 29.373800][ T360] RAX: 0000000000000000 RBX: ffff88811224d568 RCX: dffffc0000000000 [ 29.374079][ T360] RDX: 1ffff11021fe1543 RSI: ffff88810ff0aa00 RDI: dffffc0000000000 [ 29.374368][ T360] RBP: ffff88811224c280 R08: dead000000000122 R09: 1bd5a00000000024 [ 29.374649][ T360] R10: fffffbfff7940329 R11: fffffbfff7940329 R12: 0000000000000000 [ 29.374926][ T360] R13: dead000000000100 R14: ffff88811224d580 R15: ffff88811224d578 [ 29.375207][ T360] FS: 00007f5b794e5780(0000) GS:ffff88815d1e9000(0000) knlGS:0000000000000000 [ 29.375545][ T360] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 29.375823][ T360] CR2: 000055ffb091f000 CR3: 000000010a305000 CR4: 0000000000750ef0 [ 29.376103][ T360] PKRU: 55555554 [ 29.376258][ T360] Call Trace: [ 29.376401][ T360] ... [ 29.376885][ T360] qfq_reset_qdisc (net/sched/sch_qfq.c:357 net/sched/sch_qfq.c:1487) sch_qfq [ 29.377074][ T360] qdisc_reset (net/sched/sch_generic.c:1057) [ 29.377414][ T360] __qdisc_destroy (net/sched/sch_generic.c:1096) [ 29.377600][ T360] qdisc_graft (net/sched/sch_api.c:1062 net/sched/sch_api.c:1053 net/sched/sch_api.c:1159) [ 29.378593][ T360] tc_get_qdisc (net/sched/sch_api.c:1528 net/sched/sch_api.c:1556) Fix this by only calling qdisc_tree_reduce_backlog in peek after the qlen is restored. [1] http://lore.kernel.org/netdev/CAN2cbVe79oj0O9==m4+4x3v+O+qzRagA=2=wkrp9i9=CqYvyZA@mail.gmail.com/ Fixes: 342debc12183 ("codel: remove sch->q.qlen check before qdisc_tree_reduce_backlog()") Reported-by: Anirudh Gupta Closes: https://lore.kernel.org/netdev/CAN2cbVe79oj0O9==m4+4x3v+O+qzRagA=2=wkrp9i9=CqYvyZA@mail.gmail.com/ Tested-by: Anirudh Gupta Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610192855.3121513-2-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_fq_codel.c | 41 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 90e1dfac6f594..3548985bbd422 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -279,7 +279,7 @@ static void drop_func(struct sk_buff *skb, void *ctx) qdisc_qstats_drop(sch); } -static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) +static struct sk_buff *__fq_codel_dequeue(struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -316,12 +316,49 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) qdisc_bstats_update(sch, skb); flow->deficit -= qdisc_pkt_len(skb); + return skb; +} + +static void fq_codel_dequeue_drop(struct Qdisc *sch) +{ + struct fq_codel_sched_data *q = qdisc_priv(sch); + if (q->cstats.drop_count) { qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len); q->cstats.drop_count = 0; q->cstats.drop_len = 0; } +} + +static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __fq_codel_dequeue(sch); + + fq_codel_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *fq_codel_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __fq_codel_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + fq_codel_dequeue_drop(sch); + } + return skb; } @@ -723,7 +760,7 @@ static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = { .priv_size = sizeof(struct fq_codel_sched_data), .enqueue = fq_codel_enqueue, .dequeue = fq_codel_dequeue, - .peek = qdisc_peek_dequeued, + .peek = fq_codel_peek, .init = fq_codel_init, .reset = fq_codel_reset, .destroy = fq_codel_destroy, From 755108bb7a5083e911294c416cfea605dc75632f Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 16:28:53 -0300 Subject: [PATCH 1128/3297] net/sched: sch_codel: Do not call qdisc_tree_reduce_backlog during peek before restoring qlen [ Upstream commit 52f1da34c9f4d5bdc1e8b44242da5c7ba8db85f3 ] Whenever codel drops packets during peek, it calls qdisc_tree_reduce_backlog. An issue arises because it calls qdisc_tree_reduce_backlog before it reincrements the qlen. If qlen drops to zero, but peek returns an skb, the parent's qlen_notify callback will be executed even though codel still has 1 packet on the queue and, thus, will mistakenly deactivate the parent's class causing issues like a wild memory access when qfq has codel as a child: [ 36.339843][ T370] Oops: general protection fault, probably for non-canonical address 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI [ 36.340408][ T370] KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127] [ 36.340737][ T370] CPU: 2 UID: 0 PID: 370 Comm: tc Not tainted 7.1.0-rc5-00287-g66e13b626592 #87 PREEMPT(full) [ 36.341113][ T370] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 36.341357][ T370] RIP: 0010:qfq_deactivate_agg (include/linux/list.h:1029 (discriminator 2) include/linux/list.h:1043 (discriminator 2) net/sched/sch_qfq.c:1369 (discriminator 2) net/sched/sch_qfq.c:1395 (discriminator 2)) sch_qfq [ 36.342221][ T370] RSP: 0018:ffff8881100ef370 EFLAGS: 00010216 [ 36.342422][ T370] RAX: 0000000000000000 RBX: ffff8881058a9568 RCX: dffffc0000000000 [ 36.342664][ T370] RDX: 1ffff11021064dc3 RSI: ffff888108326e00 RDI: dffffc0000000000 [ 36.342905][ T370] RBP: ffff8881058a8280 R08: dead000000000122 R09: 1bd5a00000000024 [ 36.343140][ T370] R10: fffffbfff2940329 R11: fffffbfff2940329 R12: 0000000000000000 [ 36.343383][ T370] R13: dead000000000100 R14: ffff8881058a9580 R15: ffff8881058a9578 [ 36.343631][ T370] FS: 00007fc04b0ca780(0000) GS:ffff888184fef000(0000) knlGS:0000000000000000 [ 36.343911][ T370] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 36.344116][ T370] CR2: 0000557c02c02000 CR3: 000000010e0ba000 CR4: 0000000000750ef0 [ 36.344359][ T370] PKRU: 55555554 [ 36.344481][ T370] Call Trace: ... [ 36.345054][ T370] qfq_reset_qdisc (net/sched/sch_qfq.c:357 net/sched/sch_qfq.c:1487) sch_qfq [ 36.345222][ T370] qdisc_reset (net/sched/sch_generic.c:1057) [ 36.345503][ T370] __qdisc_destroy (net/sched/sch_generic.c:1096) [ 36.345677][ T370] qdisc_graft (net/sched/sch_api.c:1062 net/sched/sch_api.c:1053 net/sched/sch_api.c:1159) [ 36.346335][ T370] tc_get_qdisc (net/sched/sch_api.c:1528 net/sched/sch_api.c:1556) Fix this by only calling qdisc_tree_reduce_backlog in peek after the qlen is restored. Fixes: 342debc12183 ("codel: remove sch->q.qlen check before qdisc_tree_reduce_backlog()") Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610192855.3121513-3-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_codel.c | 48 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c index fa0314679e434..b284f8eda8753 100644 --- a/net/sched/sch_codel.c +++ b/net/sched/sch_codel.c @@ -56,7 +56,7 @@ static void drop_func(struct sk_buff *skb, void *ctx) qdisc_qstats_drop(sch); } -static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) +static struct sk_buff *__codel_qdisc_dequeue(struct Qdisc *sch) { struct codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -65,13 +65,51 @@ static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) &q->stats, qdisc_pkt_len, codel_get_enqueue_time, drop_func, dequeue_func); + if (skb) + qdisc_bstats_update(sch, skb); + return skb; +} + +static void codel_dequeue_drop(struct Qdisc *sch) +{ + struct codel_sched_data *q = qdisc_priv(sch); + if (q->stats.drop_count) { - qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len); + qdisc_tree_reduce_backlog(sch, q->stats.drop_count, + q->stats.drop_len); q->stats.drop_count = 0; q->stats.drop_len = 0; } - if (skb) - qdisc_bstats_update(sch, skb); +} + +static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __codel_qdisc_dequeue(sch); + + codel_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *codel_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __codel_qdisc_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + codel_dequeue_drop(sch); + } + return skb; } @@ -256,7 +294,7 @@ static struct Qdisc_ops codel_qdisc_ops __read_mostly = { .enqueue = codel_qdisc_enqueue, .dequeue = codel_qdisc_dequeue, - .peek = qdisc_peek_dequeued, + .peek = codel_peek, .init = codel_init, .reset = codel_reset, .change = codel_change, From bd851b10daee7199a658458b8ce250e95a334500 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 10 Jun 2026 16:28:54 -0300 Subject: [PATCH 1129/3297] net/sched: sch_dualpi2: Do not call qdisc_tree_reduce_backlog during peek before restoring qlen [ Upstream commit 15cd0c93bf4f892d66bc7a93667e2357b5673365 ] Whenever dualpi2 drops packets during peek, it calls qdisc_tree_reduce_backlog. An issue arises because it calls qdisc_tree_reduce_backlog before it reincrements the qlen. If qlen drops to zero, but peek returns an skb, the parent's qlen_notify callback will be executed even though dualpi2 still has 1 packet on the queue and, thus, mistakenly deactivates the parent's class which leads to a null-ptr-deref: [ 101.427314][ T599] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000009: 0000 [#1] SMP KASAN NOPTI [ 101.427755][ T599] KASAN: null-ptr-deref in range [0x0000000000000048-0x000000000000004f] [ 101.428048][ T599] CPU: 2 UID: 0 PID: 599 Comm: ping Not tainted 7.1.0-rc5-00284-gbce53c430ed7 #102 PREEMPT(full) [ 101.428400][ T599] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 101.428608][ T599] RIP: 0010:qfq_dequeue (net/sched/sch_qfq.c:1150) sch_qfq [ 101.428821][ T599] Code: 00 fc ff df 80 3c 02 00 0f 85 46 0c 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 2d 0c 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b All code [ 101.429348][ T599] RSP: 0018:ffff8881110df4f0 EFLAGS: 00010216 [ 101.429541][ T599] RAX: dffffc0000000000 RBX: 0000000000000000 RCX: dffffc0000000000 [ 101.429763][ T599] RDX: 0000000000000009 RSI: 00000024c0000000 RDI: ffff88811436c2b0 [ 101.429985][ T599] RBP: ffff88811436c000 R08: ffff88811436c280 R09: 1ffff11021277523 [ 101.430206][ T599] R10: 1ffff11021277526 R11: 1ffff11021277527 R12: 00000024c0000000 [ 101.430423][ T599] R13: ffff88811436c2b8 R14: 0000000000000048 R15: 0000000020000000 [ 101.430642][ T599] FS: 00007f61813e1c40(0000) GS:ffff8881691ef000(0000) knlGS:0000000000000000 [ 101.430913][ T599] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 101.431100][ T599] CR2: 00005651650850a8 CR3: 000000010ca0b000 CR4: 0000000000750ef0 [ 101.431320][ T599] PKRU: 55555554 [ 101.431433][ T599] Call Trace: [ 101.431544][ T599] [ 101.431628][ T599] __qdisc_run (net/sched/sch_generic.c:322 net/sched/sch_generic.c:427 net/sched/sch_generic.c:445) [ 101.431792][ T599] ? dev_qdisc_enqueue (./include/trace/events/qdisc.h:49 (discriminator 22) net/core/dev.c:4176 (discriminator 22)) [ 101.431941][ T599] __dev_queue_xmit (./include/net/pkt_sched.h:120 ./include/net/pkt_sched.h:117 net/core/dev.c:4292 net/core/dev.c:4831) Fix this by only calling qdisc_tree_reduce_backlog in peek after the qlen is restored. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Acked-by: Jamal Hadi Salim Signed-off-by: Victor Nogueira Link: https://patch.msgid.link/20260610192855.3121513-4-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_dualpi2.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 6beb6f5bbff1a..b4a57e1d9513a 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -579,7 +579,7 @@ static void drop_and_retry(struct dualpi2_sched_data *q, struct sk_buff *skb, qdisc_qstats_drop(sch); } -static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) +static struct sk_buff *__dualpi2_qdisc_dequeue(struct Qdisc *sch) { struct dualpi2_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -607,12 +607,49 @@ static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) break; } + return skb; +} + +static void dualpi2_dequeue_drop(struct Qdisc *sch) +{ + struct dualpi2_sched_data *q = qdisc_priv(sch); + if (q->deferred_drops_cnt) { qdisc_tree_reduce_backlog(sch, q->deferred_drops_cnt, q->deferred_drops_len); q->deferred_drops_cnt = 0; q->deferred_drops_len = 0; } +} + +static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __dualpi2_qdisc_dequeue(sch); + + dualpi2_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *dualpi2_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __dualpi2_qdisc_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + dualpi2_dequeue_drop(sch); + } + return skb; } @@ -1165,7 +1202,7 @@ static struct Qdisc_ops dualpi2_qdisc_ops __read_mostly = { .priv_size = sizeof(struct dualpi2_sched_data), .enqueue = dualpi2_qdisc_enqueue, .dequeue = dualpi2_qdisc_dequeue, - .peek = qdisc_peek_dequeued, + .peek = dualpi2_peek, .init = dualpi2_init, .destroy = dualpi2_destroy, .reset = dualpi2_reset, From 62ce489acb428a936bdf243e7b92b3eb341447cf Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 8 Jun 2026 03:13:40 -0700 Subject: [PATCH 1130/3297] net: mana: initialize gdma queue id to INVALID_QUEUE_ID [ Upstream commit 5985474e1cb4034680fac2145497a94b0860be50 ] mana_gd_create_mana_wq_cq() leaves queue->id as 0 (from kzalloc_obj()) until mana_create_wq_obj() assigns the firmware-returned id. If creation fails before that, cleanup calls mana_gd_destroy_cq() with id 0, NULLing gc->cq_table[0] and silently breaking whichever real CQ owns that slot. Initialize queue->id to INVALID_QUEUE_ID right after allocation, matching mana_gd_create_eq(). The existing (id >= max_num_cqs) guard then short-circuits cleanly. Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Aditya Garg Reviewed-by: Dipayaan Roy Reviewed-by: Haiyang Zhang Link: https://patch.msgid.link/20260608101345.2267320-2-gargaditya@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/gdma_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 45ee0774522a1..243f8d11be684 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -1119,6 +1119,8 @@ int mana_gd_create_mana_wq_cq(struct gdma_dev *gd, if (!queue) return -ENOMEM; + queue->id = INVALID_QUEUE_ID; + gmi = &queue->mem_info; err = mana_gd_alloc_memory(gc, spec->queue_size, gmi); if (err) { From 697db22a9dcc4b48451b1194ebbc9cd1e7a8bd00 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 8 Jun 2026 03:13:41 -0700 Subject: [PATCH 1131/3297] net: mana: guard TX wq object destroy with INVALID_MANA_HANDLE check [ Upstream commit f8fd56977eeea3d6939b1a9cd8bd36f1779b3ad0 ] mana_create_txq() has several error paths (after mana_alloc_queues() or mana_create_wq_obj() failure) where tx_qp[i].tx_object stays as the INVALID_MANA_HANDLE sentinel set at allocation. mana_destroy_txq() then unconditionally calls mana_destroy_wq_obj() with (u64)-1, which firmware rejects and logs an error. Mirror the RX-side pattern in mana_destroy_rxq() and skip the destroy when the handle is still INVALID_MANA_HANDLE. Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Aditya Garg Reviewed-by: Dipayaan Roy Reviewed-by: Haiyang Zhang Link: https://patch.msgid.link/20260608101345.2267320-3-gargaditya@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/mana_en.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 1f723c0ea1283..62418f164dbac 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2304,7 +2304,8 @@ static void mana_destroy_txq(struct mana_port_context *apc) netif_napi_del_locked(napi); apc->tx_qp[i].txq.napi_initialized = false; } - mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i].tx_object); + if (apc->tx_qp[i].tx_object != INVALID_MANA_HANDLE) + mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i].tx_object); mana_deinit_cq(apc, &apc->tx_qp[i].tx_cq); From 446fe8ce699ce0a4d702f7b0fcdb50de340b9260 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 11 Jun 2026 15:27:37 +0000 Subject: [PATCH 1132/3297] net: watchdog: fix refcount tracking races [ Upstream commit 8eed5519e496b7a07f441a0f579cb228a33189f7 ] Blamed commit converted the untracked dev_hold()/dev_put() calls in the watchdog code to use the tracked dev_hold_track()/dev_put_track() (which were later renamed/interfaced to netdev_hold() and netdev_put()). By introducing dev->watchdog_dev_tracker to store the reference tracking information without adding synchronization between netdev_watchdog_up() and dev_watchdog(), it enabled the race condition where this pointer could be overwritten or freed concurrently, leading to the list corruption crash syzbot reported: list_del corruption, ffff888114a18c00->next is NULL kernel BUG at lib/list_debug.c:52 ! Oops: invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 1 UID: 0 PID: 91 Comm: kworker/u8:5 Not tainted syzkaller #0 PREEMPT(lazy) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026 Workqueue: events_unbound linkwatch_event RIP: 0010:__list_del_entry_valid_or_report.cold+0x22/0x2a lib/list_debug.c:52 Call Trace: __list_del_entry_valid include/linux/list.h:132 [inline] __list_del_entry include/linux/list.h:246 [inline] list_move_tail include/linux/list.h:341 [inline] ref_tracker_free+0x1a7/0x6c0 lib/ref_tracker.c:329 netdev_tracker_free include/linux/netdevice.h:4491 [inline] netdev_put include/linux/netdevice.h:4508 [inline] netdev_put include/linux/netdevice.h:4504 [inline] netdev_watchdog_down net/sched/sch_generic.c:600 [inline] dev_deactivate_many+0x28c/0xfe0 net/sched/sch_generic.c:1363 dev_deactivate+0x109/0x1d0 net/sched/sch_generic.c:1397 linkwatch_do_dev net/core/link_watch.c:184 [inline] linkwatch_do_dev+0xd3/0x120 net/core/link_watch.c:166 __linkwatch_run_queue+0x3a5/0x810 net/core/link_watch.c:240 linkwatch_event+0x8f/0xc0 net/core/link_watch.c:314 process_one_work+0xa0e/0x1980 kernel/workqueue.c:3314 process_scheduled_works kernel/workqueue.c:3397 [inline] worker_thread+0x5ef/0xe50 kernel/workqueue.c:3478 kthread+0x370/0x450 kernel/kthread.c:436 ret_from_fork+0x69a/0xc80 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 This patch has three coordinated parts: 1) Add dev->watchdog_lock and dev->watchdog_ref_held to serialize watchdog operations. 2) Remove netdev_watchdog_up() call from netif_carrier_on(): This ensures netdev_watchdog_up() is only called from process/BH context (via linkwatch workqueue dev_activate()), allowing us to use spin_lock_bh() for synchronization. 3) Synchronize watchdog up and watchdog timer: Protect netdev_watchdog_up() with tx_global_lock and watchdog_lock. Only allocate a new tracker in netdev_watchdog_up() if one is not already present. In dev_watchdog(), ensure we don't release the tracker if the timer was rescheduled either by dev_watchdog() itself or concurrently by netdev_watchdog_up(). Fixes: f12bf6f3f942 ("net: watchdog: add net device refcount tracker") Reported-by: syzbot+381d82bbf0253710b35d@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a26b751.c25708ab.1b19ef.0013.GAE@google.com/T/#u Tested-by: syzbot+3479efbc2821cb2a79f2@syzkaller.appspotmail.com Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260611152737.2580480-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/linux/netdevice.h | 4 ++++ net/core/dev.c | 3 ++- net/sched/sch_generic.c | 44 +++++++++++++++++++++++++++++---------- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fc55157a44860..43ea96ecedc3a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1949,6 +1949,8 @@ enum netdev_reg_state { * @qdisc_hash: qdisc hash table * @watchdog_timeo: Represents the timeout that is used by * the watchdog (see dev_watchdog()) + * @watchdog_lock: protect watchdog_ref_held + * @watchdog_ref_held: True if the watchdog device ref is taken. * @watchdog_timer: List of timers * * @proto_down_reason: reason a netdev interface is held down @@ -2360,6 +2362,8 @@ struct net_device { /* These may be needed for future network-power-down code. */ struct timer_list watchdog_timer; int watchdog_timeo; + spinlock_t watchdog_lock; + bool watchdog_ref_held; u32 proto_down_reason; diff --git a/net/core/dev.c b/net/core/dev.c index 5853aada258ca..8dee703a0916e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11138,7 +11138,8 @@ static int netif_alloc_netdev_queues(struct net_device *dev) netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL); spin_lock_init(&dev->tx_global_lock); - + spin_lock_init(&dev->watchdog_lock); + dev->watchdog_ref_held = false; return 0; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 30d77ad7b81d2..d44cee56a58e3 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -543,16 +543,24 @@ static void dev_watchdog(struct timer_list *t) dev->netdev_ops->ndo_tx_timeout(dev, i); netif_unfreeze_queues(dev); } - if (!mod_timer(&dev->watchdog_timer, - round_jiffies(oldest_start + - dev->watchdog_timeo))) - release = false; + spin_lock(&dev->watchdog_lock); + mod_timer(&dev->watchdog_timer, + round_jiffies(oldest_start + + dev->watchdog_timeo)); + release = false; + spin_unlock(&dev->watchdog_lock); } } spin_unlock(&dev->tx_global_lock); - if (release) + spin_lock(&dev->watchdog_lock); + if (timer_pending(&dev->watchdog_timer)) + release = false; + if (release && dev->watchdog_ref_held) { netdev_put(dev, &dev->watchdog_dev_tracker); + dev->watchdog_ref_held = false; + } + spin_unlock(&dev->watchdog_lock); } void netdev_watchdog_up(struct net_device *dev) @@ -561,18 +569,34 @@ void netdev_watchdog_up(struct net_device *dev) return; if (dev->watchdog_timeo <= 0) dev->watchdog_timeo = 5*HZ; + spin_lock_bh(&dev->tx_global_lock); + + spin_lock(&dev->watchdog_lock); if (!mod_timer(&dev->watchdog_timer, - round_jiffies(jiffies + dev->watchdog_timeo))) - netdev_hold(dev, &dev->watchdog_dev_tracker, - GFP_ATOMIC); + round_jiffies(jiffies + dev->watchdog_timeo))) { + if (!dev->watchdog_ref_held) { + netdev_hold(dev, &dev->watchdog_dev_tracker, + GFP_ATOMIC); + dev->watchdog_ref_held = true; + } + } + spin_unlock(&dev->watchdog_lock); + + spin_unlock_bh(&dev->tx_global_lock); } EXPORT_SYMBOL_GPL(netdev_watchdog_up); static void netdev_watchdog_down(struct net_device *dev) { netif_tx_lock_bh(dev); - if (timer_delete(&dev->watchdog_timer)) + + spin_lock(&dev->watchdog_lock); + if (timer_delete(&dev->watchdog_timer)) { netdev_put(dev, &dev->watchdog_dev_tracker); + dev->watchdog_ref_held = false; + } + spin_unlock(&dev->watchdog_lock); + netif_tx_unlock_bh(dev); } @@ -589,8 +613,6 @@ void netif_carrier_on(struct net_device *dev) return; atomic_inc(&dev->carrier_up_count); linkwatch_fire_event(dev); - if (netif_running(dev)) - netdev_watchdog_up(dev); } } EXPORT_SYMBOL(netif_carrier_on); From 6d585d0dc674351e234a9695f9ad6511ab1f1237 Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Thu, 11 Jun 2026 23:00:51 +0800 Subject: [PATCH 1133/3297] net: ethernet: mtk_wed: fix loading WO firmware for MT7986 [ Upstream commit 9192a18f6de2f5e3eb3813ecd2895ac0f5c008a9 ] MT7986 requires a different mask for second WO firmware. Without this, WO would timeout after loading FW. The correct mask was removed when adding WED for MT7988. Add it back and add a WED version check to fix it. This can be reproduced with a MT7986 + MT7916 board. Fixes: e2f64db13aa1 ("net: ethernet: mtk_wed: introduce WED support for MT7988") Signed-off-by: Zhi-Jun You Link: https://patch.msgid.link/20260611150051.586-1-hujy652@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c index fa6b216034169..0d38183c6ba70 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c @@ -367,8 +367,12 @@ mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) /* wo firmware reset */ wo_w32(MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00); - val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) | - MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; + val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR); + + if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index) + val |= MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK; + else + val |= MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; wo_w32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val); out: release_firmware(fw); From 450e48271827bdd3b11102796821e402e336f254 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Thu, 11 Jun 2026 17:58:49 -0300 Subject: [PATCH 1134/3297] net/sched: sch_dualpi2: Add missing module alias [ Upstream commit ee1ba0add3fbd5a28fa5423be373acd147f1e344 ] When a qdisc is added by name, the kernel tries to autoload its module via request_qdisc_module(), which calls: request_module(NET_SCH_ALIAS_PREFIX "%s", name); i.e. it asks modprobe to resolve the "net-sch-" alias (e.g. "net-sch-dualpi2") rather than the module's file name. Since dualpi2 was shipped without this alias, the autoload fails: tc qdisc add dev lo root handle 1: dualpi2 Error: Specified qdisc kind is unknown. Fix this by adding the missing alias so the qdisc is autoloaded on demand like the others. Fixes: 320d031ad6e4 ("sched: Struct definition and parsing of dualpi2 qdisc") Signed-off-by: Victor Nogueira Reviewed-by: Pedro Tammela Link: https://patch.msgid.link/20260611205849.3287640-1-victor@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_dualpi2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index b4a57e1d9513a..02d3c6c45aa3f 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -1211,6 +1211,7 @@ static struct Qdisc_ops dualpi2_qdisc_ops __read_mostly = { .dump_stats = dualpi2_dump_stats, .owner = THIS_MODULE, }; +MODULE_ALIAS_NET_SCH("dualpi2"); static int __init dualpi2_module_init(void) { From 8d5f4be1348829d4482156bf77a96450f65138fa Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Fri, 12 Jun 2026 19:40:31 +0800 Subject: [PATCH 1135/3297] bpf: Run generic devmap egress prog on private skb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6001896f00984d317fb75160ba05c4a885fbe2a0 ] Generic XDP devmap multi redirect uses skb_clone() for intermediate destinations and sends the last destination with the original skb. This can leave multiple destinations sharing the same packet data. This becomes visible after generic devmap egress-program support was added: a devmap egress program may mutate packet data, and another destination sharing the same data can observe that mutation. Native XDP broadcast redirect does not have this issue because xdpf_clone() copies the frame data for each destination. Generic XDP should provide the same per-destination isolation before running a devmap egress program. Fix this by making cloned skbs private before running the generic devmap egress program. Use skb_copy() instead of skb_unshare() so allocation failure does not consume the skb and the existing caller error paths keep their ownership semantics. Fixes: 2ea5eabaf04a ("bpf: devmap: Implement devmap prog execution for generic XDP") Suggested-by: Jiayuan Chen Suggested-by: Jakub Kicinski Reviewed-by: Toke Høiland-Jørgensen Signed-off-by: Sun Jian Link: https://lore.kernel.org/r/20260612114032.244616-2-sun.jian.kdev@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/devmap.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 5b9eac5342a90..dc7b859e8bbfd 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -710,6 +710,18 @@ int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb, if (unlikely(err)) return err; + if (dst->xdp_prog && skb_cloned(skb)) { + struct sk_buff *nskb; + + nskb = skb_copy(skb, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; + + nskb->mac_len = skb->mac_len; + consume_skb(skb); + skb = nskb; + } + /* Redirect has already succeeded semantically at this point, so we just * return 0 even if packet is dropped. Helper below takes care of * freeing skb. From 3d90b15fb1918074c54e07161bc8a8f127614306 Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 11 Jun 2026 16:52:30 +0300 Subject: [PATCH 1136/3297] net/mlx5: Check max_macs devlink param value against max capability [ Upstream commit d7b0413b35715d7b32cb12d4d424613eff85ed2b ] The max_macs devlink param is checked against the FW max value only at param register time (driver load) and inside the validate callback (devlink param set). The stored DRIVERINIT value persists across FW resets and devlink reloads without any further checks against the max. If the FW link type changes from Ethernet to IB and a FW reset happens, the MAX cap for log_max_current_uc_list will become zero, but the previously stored max_macs value remains and is unconditionally programmed into the HCA caps in handle_hca_cap(). FW will then return a syndrome during SET_HCA_CAP: mlx5_cmd_out_err:839:(pid 3831): SET_HCA_CAP(0x109) op_mod(0x0) failed, status bad parameter(0x3), syndrome (0x537801), err(-22) set_hca_cap:907:(pid 3831): handle_hca_cap failed This results in a failure to register the RDMA device. This patch skips programming log_max_current_uc_list when the MAX capability is 0 (in case of IB). Fixes: 8680a60fc1fc ("net/mlx5: Let user configure max_macs generic param") Signed-off-by: Dragos Tatulea Reviewed-by: Yael Chemla Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Reviewed-by: Alexander Lobakin Link: https://patch.msgid.link/20260611135230.534513-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 6e10a6de8ebcc..22bdefe5696c9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -580,7 +580,6 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) { struct mlx5_profile *prof = &dev->profile; void *set_hca_cap; - int max_uc_list; int err; err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL); @@ -663,10 +662,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) MLX5_SET(cmd_hca_cap, set_hca_cap, roce, mlx5_is_roce_on(dev)); - max_uc_list = max_uc_list_get_devlink_param(dev); - if (max_uc_list > 0) - MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_current_uc_list, - ilog2(max_uc_list)); + if (MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) { + int max_uc_list = max_uc_list_get_devlink_param(dev); + + if (max_uc_list > 0) + MLX5_SET(cmd_hca_cap, set_hca_cap, + log_max_current_uc_list, ilog2(max_uc_list)); + } /* enable absolute native port num */ if (MLX5_CAP_GEN_MAX(dev, abs_native_port_num)) From 417bd36a085d7a833bb00325de32447f45e273eb Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Wed, 10 Jun 2026 20:17:23 +0000 Subject: [PATCH 1137/3297] bpf: Fix setting retval to -EPERM for cgroup hooks not returning errno [ Upstream commit 4c71303c837449158815c521fcee4ec3b8721dbd ] When a cgroup BPF program exits with 0, bpf_prog_run_array_cg() sets the hook return value to -EPERM if it is not a valid errno. This is correct for errno-based hooks, which return 0 on success and negative errno on failure, but wrong for boolean and void LSM hooks. Boolean LSM hooks should only return true or false, and void LSM hooks have no return value at all. Fix it by skipping setting -EPERM for hooks not returning errno. Fixes: 69fd337a975c ("bpf: per-cgroup lsm flavor") Signed-off-by: Xu Kuohai Link: https://lore.kernel.org/r/20260610201724.733943-2-xukuohai@huaweicloud.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- include/linux/bpf_lsm.h | 6 ++++++ kernel/bpf/bpf_lsm.c | 20 ++++++++++++++++++ kernel/bpf/cgroup.c | 47 +++++++++++++++++++++++++++++------------ 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index 643809cc78c33..143775a27a2a0 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -52,6 +52,7 @@ int bpf_set_dentry_xattr_locked(struct dentry *dentry, const char *name__str, const struct bpf_dynptr *value_p, int flags); int bpf_remove_dentry_xattr_locked(struct dentry *dentry, const char *name__str); bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog); +bool bpf_lsm_hook_returns_errno(u32 btf_id); #else /* !CONFIG_BPF_LSM */ @@ -104,6 +105,11 @@ static inline bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog) { return false; } + +static inline bool bpf_lsm_hook_returns_errno(u32 btf_id) +{ + return true; +} #endif /* CONFIG_BPF_LSM */ #endif /* _LINUX_BPF_LSM_H */ diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 2eead789b898b..554985ce9025a 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -421,6 +421,26 @@ BTF_ID(func, bpf_lsm_audit_rule_known) BTF_ID(func, bpf_lsm_inode_xattr_skipcap) BTF_SET_END(bool_lsm_hooks) +/* hooks returning void */ +#define LSM_HOOK_void(DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME) +#define LSM_HOOK_int(DEFAULT, NAME, ...) /* nothing */ +#define LSM_HOOK(RET, DEFAULT, NAME, ...) LSM_HOOK_##RET(DEFAULT, NAME, __VA_ARGS__) +BTF_SET_START(void_lsm_hooks) +#include +#undef LSM_HOOK +#undef LSM_HOOK_void +#undef LSM_HOOK_int +BTF_SET_END(void_lsm_hooks) + +bool bpf_lsm_hook_returns_errno(u32 btf_id) +{ + if (btf_id_set_contains(&bool_lsm_hooks, btf_id)) + return false; + if (btf_id_set_contains(&void_lsm_hooks, btf_id)) + return false; + return true; +} + int bpf_lsm_get_retval_range(const struct bpf_prog *prog, struct bpf_retval_range *retval_range) { diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 008b3a9be0c41..c00e440fb36b6 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -55,6 +55,28 @@ void __init cgroup_bpf_lifetime_notifier_init(void) &cgroup_bpf_lifetime_nb)); } +#ifdef CONFIG_BPF_LSM +struct cgroup_lsm_atype { + u32 attach_btf_id; + int refcnt; + bool returns_errno; +}; + +static struct cgroup_lsm_atype cgroup_lsm_atype[CGROUP_LSM_NUM]; + +static bool cgroup_bpf_hook_returns_errno(enum cgroup_bpf_attach_type atype) +{ + if (atype >= CGROUP_LSM_START && atype <= CGROUP_LSM_END) + return READ_ONCE(cgroup_lsm_atype[atype - CGROUP_LSM_START].returns_errno); + return true; +} +#else +static bool cgroup_bpf_hook_returns_errno(enum cgroup_bpf_attach_type atype) +{ + return true; +} +#endif + /* __always_inline is necessary to prevent indirect call through run_prog * function pointer. */ @@ -83,7 +105,8 @@ bpf_prog_run_array_cg(const struct cgroup_bpf *cgrp, *(ret_flags) |= (func_ret >> 1); func_ret &= 1; } - if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval)) + if (!func_ret && cgroup_bpf_hook_returns_errno(atype) && + !IS_ERR_VALUE((long)run_ctx.retval)) run_ctx.retval = -EPERM; item++; } @@ -156,13 +179,6 @@ unsigned int __cgroup_bpf_run_lsm_current(const void *ctx, } #ifdef CONFIG_BPF_LSM -struct cgroup_lsm_atype { - u32 attach_btf_id; - int refcnt; -}; - -static struct cgroup_lsm_atype cgroup_lsm_atype[CGROUP_LSM_NUM]; - static enum cgroup_bpf_attach_type bpf_cgroup_atype_find(enum bpf_attach_type attach_type, u32 attach_btf_id) { @@ -191,10 +207,13 @@ void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype) lockdep_assert_held(&cgroup_mutex); - WARN_ON_ONCE(cgroup_lsm_atype[i].attach_btf_id && - cgroup_lsm_atype[i].attach_btf_id != attach_btf_id); - - cgroup_lsm_atype[i].attach_btf_id = attach_btf_id; + if (!cgroup_lsm_atype[i].attach_btf_id) { + cgroup_lsm_atype[i].attach_btf_id = attach_btf_id; + WRITE_ONCE(cgroup_lsm_atype[i].returns_errno, + bpf_lsm_hook_returns_errno(attach_btf_id)); + } else { + WARN_ON_ONCE(cgroup_lsm_atype[i].attach_btf_id != attach_btf_id); + } cgroup_lsm_atype[i].refcnt++; } @@ -203,8 +222,10 @@ void bpf_cgroup_atype_put(int cgroup_atype) int i = cgroup_atype - CGROUP_LSM_START; cgroup_lock(); - if (--cgroup_lsm_atype[i].refcnt <= 0) + if (--cgroup_lsm_atype[i].refcnt <= 0) { + WRITE_ONCE(cgroup_lsm_atype[i].returns_errno, true); cgroup_lsm_atype[i].attach_btf_id = 0; + } WARN_ON_ONCE(cgroup_lsm_atype[i].refcnt < 0); cgroup_unlock(); } From 1d072cc3ba4338d6b2acb85d50a3af5cf2b12a3e Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Wed, 10 Jun 2026 07:53:44 +0530 Subject: [PATCH 1138/3297] octeontx2-af: npc: Fix size of entry2cntr_map [ Upstream commit f9cd6fabe0e7c7f6fc30c6c192c7ed72aba37232 ] KASAN prints below splat. This is caused by allocating counter for reserved mcam entry for cpt 2nd pass entry. But mcam->entry2cntr_map is not allocated for reserved entries. BUG: KASAN: slab-out-of-bounds in npc_map_mcam_entry_and_cntr+0xb0/0x1a0 Write of size 2 at addr ffff0001033e7ffe by task kworker/0:1/14 CPU: 0 PID: 14 Comm: kworker/0:1 Not tainted 6.1.67 #1 Hardware name: Marvell CN106XX board (DT) Workqueue: events work_for_cpu_fn Call trace: dump_backtrace.part.0+0xe4/0xf0 show_stack+0x18/0x30 dump_stack_lvl+0x88/0xb4 print_report+0x154/0x458 kasan_report+0xb8/0x194 __asan_store2+0x7c/0xa0 npc_map_mcam_entry_and_cntr+0xb0/0x1a0 rvu_mbox_handler_npc_mcam_write_entry+0x268/0x280 npc_install_flow+0x840/0xfe0 rvu_npc_install_cpt_pass2_entry+0x138/0x190 rvu_nix_init+0x148c/0x2880 rvu_probe+0x1800/0x30b0 local_pci_probe+0x78/0xe0 work_for_cpu_fn+0x30/0x50 process_one_work+0x4cc/0x97c worker_thread+0x360/0x630 kthread+0x1a0/0x1b0 ret_from_fork+0x10/0x20 Fixes: 55307fcb9258 ("octeontx2-af: Add mbox messages to install and delete MCAM rules") Cc: Subbaraya Sundeep Signed-off-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260610022344.969774-1-rkannoth@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../ethernet/marvell/octeontx2/af/rvu_npc.c | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 65aa6aeab8e78..fbe7dbe2fea5f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -1921,7 +1921,7 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) /* Alloc memory for MCAM entry to counter mapping and for tracking * counter's reference count. */ - mcam->entry2cntr_map = kcalloc(mcam->bmap_entries, sizeof(u16), + mcam->entry2cntr_map = kcalloc(mcam->total_entries, sizeof(u16), GFP_KERNEL); if (!mcam->entry2cntr_map) goto free_cntr_map; @@ -1937,10 +1937,11 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) if (!mcam->entry2target_pffunc) goto free_cntr_refcnt; - for (index = 0; index < mcam->bmap_entries; index++) { + for (index = 0; index < mcam->bmap_entries; index++) mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP; + + for (index = 0; index < mcam->total_entries; index++) mcam->entry2cntr_map[index] = NPC_MCAM_INVALID_MAP; - } for (cntr = 0; cntr < mcam->counters.max; cntr++) mcam->cntr2pfvf_map[cntr] = NPC_MCAM_INVALID_MAP; @@ -3090,7 +3091,7 @@ static int __npc_mcam_free_counter(struct rvu *rvu, struct msg_rsp *rsp) { struct npc_mcam *mcam = &rvu->hw->mcam; - u16 index, entry = 0; + u16 index; int blkaddr, err; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -3106,20 +3107,16 @@ static int __npc_mcam_free_counter(struct rvu *rvu, mcam->cntr2pfvf_map[req->cntr] = NPC_MCAM_INVALID_MAP; rvu_free_rsrc(&mcam->counters, req->cntr); - /* Disable all MCAM entry's stats which are using this counter */ - while (entry < mcam->bmap_entries) { + /* Disable all MCAM entry's stats which are using this counter. + * Scan the full MCAM index range: AF-reserved rules (e.g. CPT pass-2) + */ + for (index = 0; index < mcam->total_entries; index++) { if (!mcam->cntr_refcnt[req->cntr]) break; - - index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); - if (index >= mcam->bmap_entries) - break; - entry = index + 1; if (mcam->entry2cntr_map[index] != req->cntr) continue; - - npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, - index, req->cntr); + npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, + req->cntr); } return 0; @@ -3186,7 +3183,7 @@ int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp) { struct npc_mcam *mcam = &rvu->hw->mcam; - u16 index, entry = 0; + u16 index; int blkaddr, rc; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -3209,20 +3206,13 @@ int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, } /* Disable all MCAM entry's stats which are using this counter */ - while (entry < mcam->bmap_entries) { + for (index = 0; index < mcam->total_entries; index++) { if (!mcam->cntr_refcnt[req->cntr]) break; - - index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); - if (index >= mcam->bmap_entries) - break; - entry = index + 1; - if (mcam->entry2cntr_map[index] != req->cntr) continue; - - npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, - index, req->cntr); + npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, + req->cntr); } exit: mutex_unlock(&mcam->lock); From 34bd255dba321c6ba34313be827417b0382cbc51 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Fri, 12 Jun 2026 14:45:01 +0800 Subject: [PATCH 1139/3297] net: ethernet: mtk_wed: debugfs: correct index in wed_amsdu_show() [ Upstream commit 14a8bc41ce9edae42d56466063a7f2c84a16c45c ] WED_MON_AMSDU_ENG_CNT point to different entry by 'base+n*offset' mode, correct the wed amsdu entry number in wed_amsdu_show(). Fixes: 3f3de094e8342 ("net: ethernet: mtk_wed: debugfs: add WED 3.0 debugfs entries") Signed-off-by: Wentao Guan Link: https://patch.msgid.link/20260612064501.203058-1-guanwentao@uniontech.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_wed_debugfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c index 781c691473e14..519c364e87d19 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c @@ -310,9 +310,9 @@ wed_amsdu_show(struct seq_file *s, void *data) WED_AMSDU_ENG_MAX_QGPP_CNT), DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_CUR_ENTRY), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_MAX_BUF_MERGED), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_MAX_MSDU_MERGED), DUMP_STR("WED AMDSU ENG2 INFO"), @@ -414,7 +414,7 @@ wed_amsdu_show(struct seq_file *s, void *data) WED_AMSDU_ENG_CUR_ENTRY), DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), WED_AMSDU_ENG_MAX_BUF_MERGED), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), WED_AMSDU_ENG_MAX_MSDU_MERGED), DUMP_STR("WED AMDSU ENG8 INFO"), From f7aebaee2961bfcb86f282202d3dbd9b69db1a7c Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Fri, 12 Jun 2026 11:56:13 +0800 Subject: [PATCH 1140/3297] net: wwan: t7xx: check skb_clone in control TX [ Upstream commit 05f789fa90d95d5771230e78453cedff2486039d ] t7xx_port_ctrl_tx() clones each skb fragment before passing it to the port transmit path. The clone is used immediately to set cloned->len, so an skb_clone() failure results in a NULL pointer dereference. Check the clone before using it. If previous fragments were already queued, preserve the driver's existing partial-write behavior by returning the number of bytes submitted so far. Fixes: 36bd28c1cb0d ("wwan: core: Support slicing in port TX flow of WWAN subsystem") Signed-off-by: Ruoyu Wang Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260612035613.1192486-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/wwan/t7xx/t7xx_port_wwan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c index 7fc569565ff99..d2529df7592a7 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c @@ -106,6 +106,8 @@ static int t7xx_port_ctrl_tx(struct t7xx_port *port, struct sk_buff *skb) while (cur) { cloned = skb_clone(cur, GFP_KERNEL); + if (!cloned) + return cnt ? cnt : -ENOMEM; cloned->len = skb_headlen(cur); ret = t7xx_port_send_skb(port, cloned, 0, 0); if (ret) { From ebe4bd3560a7a01aa20ed6849a6cc20c714e978d Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Tue, 3 Feb 2026 18:39:57 +0100 Subject: [PATCH 1141/3297] dpll: Support dynamic pin index allocation [ Upstream commit 711696b3e168cea4165e7b7f051f3f442a347430 ] Allow drivers to register DPLL pins without manually specifying a pin index. Currently, drivers must provide a unique pin index when calling dpll_pin_get(). This works well for hardware-mapped pins but creates friction for drivers handling virtual pins or those without a strict hardware indexing scheme. Introduce DPLL_PIN_IDX_UNSPEC (U32_MAX). When a driver passes this value as the pin index: 1. The core allocates a unique index using an IDA 2. The allocated index is mapped to a range starting above `INT_MAX` This separation ensures that dynamically allocated indices never collide with standard driver-provided hardware indices, which are assumed to be within the `0` to `INT_MAX` range. The index is automatically freed when the pin is released in dpll_pin_put(). Reviewed-by: Aleksandr Loktionov Signed-off-by: Ivan Vecera Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260203174002.705176-5-ivecera@redhat.com Signed-off-by: Paolo Abeni Stable-dep-of: 32239d600236 ("dpll: fix stale iteration in dpll_pin_on_pin_unregister()") Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 48 ++++++++++++++++++++++++++++++++++++++-- include/linux/dpll.h | 2 ++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index b05fe2ba46d91..59081cf2c73ae 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,7 @@ DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC); DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC); static RAW_NOTIFIER_HEAD(dpll_notifier_chain); +static DEFINE_IDA(dpll_pin_idx_ida); static u32 dpll_device_xa_id; static u32 dpll_pin_xa_id; @@ -464,6 +466,36 @@ void dpll_device_unregister(struct dpll_device *dpll, } EXPORT_SYMBOL_GPL(dpll_device_unregister); +static int dpll_pin_idx_alloc(u32 *pin_idx) +{ + int ret; + + if (!pin_idx) + return -EINVAL; + + /* Alloc unique number from IDA. Number belongs to <0, INT_MAX> range */ + ret = ida_alloc(&dpll_pin_idx_ida, GFP_KERNEL); + if (ret < 0) + return ret; + + /* Map the value to dynamic pin index range */ + *pin_idx = (u32)ret + INT_MAX + 1; + + return 0; +} + +static void dpll_pin_idx_free(u32 pin_idx) +{ + if (pin_idx <= INT_MAX) + return; /* Not a dynamic pin index */ + + /* Map the index value from dynamic pin index range to IDA range and + * free it. + */ + pin_idx -= (u32)INT_MAX + 1; + ida_free(&dpll_pin_idx_ida, pin_idx); +} + static void dpll_pin_prop_free(struct dpll_pin_properties *prop) { kfree(prop->package_label); @@ -521,9 +553,18 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, struct dpll_pin *pin; int ret; + if (pin_idx == DPLL_PIN_IDX_UNSPEC) { + ret = dpll_pin_idx_alloc(&pin_idx); + if (ret) + return ERR_PTR(ret); + } else if (pin_idx > INT_MAX) { + return ERR_PTR(-EINVAL); + } pin = kzalloc(sizeof(*pin), GFP_KERNEL); - if (!pin) - return ERR_PTR(-ENOMEM); + if (!pin) { + ret = -ENOMEM; + goto err_pin_alloc; + } pin->pin_idx = pin_idx; pin->clock_id = clock_id; pin->module = module; @@ -551,6 +592,8 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, dpll_pin_prop_free(&pin->prop); err_pin_prop: kfree(pin); +err_pin_alloc: + dpll_pin_idx_free(pin_idx); return ERR_PTR(ret); } @@ -654,6 +697,7 @@ void dpll_pin_put(struct dpll_pin *pin) xa_destroy(&pin->ref_sync_pins); dpll_pin_prop_free(&pin->prop); fwnode_handle_put(pin->fwnode); + dpll_pin_idx_free(pin->pin_idx); kfree_rcu(pin, rcu); } mutex_unlock(&dpll_lock); diff --git a/include/linux/dpll.h b/include/linux/dpll.h index 480ea3cbe709b..dd86d9d9d0689 100644 --- a/include/linux/dpll.h +++ b/include/linux/dpll.h @@ -235,6 +235,8 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type, void dpll_device_unregister(struct dpll_device *dpll, const struct dpll_device_ops *ops, void *priv); +#define DPLL_PIN_IDX_UNSPEC U32_MAX + struct dpll_pin * dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module, const struct dpll_pin_properties *prop); From 20575400fc1baedb8285683a0481e5610e9a7031 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Tue, 3 Feb 2026 18:39:59 +0100 Subject: [PATCH 1142/3297] dpll: Enhance and consolidate reference counting logic [ Upstream commit 729f5e0153bda8c0423fb7c5795865b6b77ca050 ] Refactor the reference counting mechanism for DPLL devices and pins to improve consistency and prevent potential lifetime issues. Introduce internal helpers __dpll_{device,pin}_{hold,put}() to centralize reference management. Update the internal XArray reference helpers (dpll_xa_ref_*) to automatically grab a reference to the target object when it is added to a list, and release it when removed. This ensures that objects linked internally (e.g., pins referenced by parent pins) are properly kept alive without relying on the caller to manually manage the count. Consequently, remove the now redundant manual `refcount_inc/dec` calls in dpll_pin_on_pin_{,un}register()`, as ownership is now correctly handled by the dpll_xa_ref_* functions. Additionally, ensure that dpll_device_{,un}register()` takes/releases a reference to the device, ensuring the device object remains valid for the duration of its registration. Reviewed-by: Aleksandr Loktionov Signed-off-by: Ivan Vecera Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260203174002.705176-7-ivecera@redhat.com Signed-off-by: Paolo Abeni Stable-dep-of: 32239d600236 ("dpll: fix stale iteration in dpll_pin_on_pin_unregister()") Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 74 +++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 59081cf2c73ae..f6ab4f0cad84d 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -83,6 +83,45 @@ void dpll_pin_notify(struct dpll_pin *pin, unsigned long action) call_dpll_notifiers(action, &info); } +static void __dpll_device_hold(struct dpll_device *dpll) +{ + refcount_inc(&dpll->refcount); +} + +static void __dpll_device_put(struct dpll_device *dpll) +{ + if (refcount_dec_and_test(&dpll->refcount)) { + ASSERT_DPLL_NOT_REGISTERED(dpll); + WARN_ON_ONCE(!xa_empty(&dpll->pin_refs)); + xa_destroy(&dpll->pin_refs); + xa_erase(&dpll_device_xa, dpll->id); + WARN_ON(!list_empty(&dpll->registration_list)); + kfree(dpll); + } +} + +static void __dpll_pin_hold(struct dpll_pin *pin) +{ + refcount_inc(&pin->refcount); +} + +static void dpll_pin_idx_free(u32 pin_idx); +static void dpll_pin_prop_free(struct dpll_pin_properties *prop); + +static void __dpll_pin_put(struct dpll_pin *pin) +{ + if (refcount_dec_and_test(&pin->refcount)) { + xa_erase(&dpll_pin_xa, pin->id); + xa_destroy(&pin->dpll_refs); + xa_destroy(&pin->parent_refs); + xa_destroy(&pin->ref_sync_pins); + dpll_pin_prop_free(&pin->prop); + fwnode_handle_put(pin->fwnode); + dpll_pin_idx_free(pin->pin_idx); + kfree_rcu(pin, rcu); + } +} + struct dpll_device *dpll_device_get_by_id(int id) { if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED)) @@ -152,6 +191,7 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin, reg->ops = ops; reg->priv = priv; reg->cookie = cookie; + __dpll_pin_hold(pin); if (ref_exists) refcount_inc(&ref->refcount); list_add_tail(®->list, &ref->registration_list); @@ -174,6 +214,7 @@ static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin, if (WARN_ON(!reg)) return -EINVAL; list_del(®->list); + __dpll_pin_put(pin); kfree(reg); if (refcount_dec_and_test(&ref->refcount)) { xa_erase(xa_pins, i); @@ -231,6 +272,7 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll, reg->ops = ops; reg->priv = priv; reg->cookie = cookie; + __dpll_device_hold(dpll); if (ref_exists) refcount_inc(&ref->refcount); list_add_tail(®->list, &ref->registration_list); @@ -253,6 +295,7 @@ dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll, if (WARN_ON(!reg)) return; list_del(®->list); + __dpll_device_put(dpll); kfree(reg); if (refcount_dec_and_test(&ref->refcount)) { xa_erase(xa_dplls, i); @@ -323,8 +366,8 @@ dpll_device_get(u64 clock_id, u32 device_idx, struct module *module) if (dpll->clock_id == clock_id && dpll->device_idx == device_idx && dpll->module == module) { + __dpll_device_hold(dpll); ret = dpll; - refcount_inc(&ret->refcount); break; } } @@ -347,14 +390,7 @@ EXPORT_SYMBOL_GPL(dpll_device_get); void dpll_device_put(struct dpll_device *dpll) { mutex_lock(&dpll_lock); - if (refcount_dec_and_test(&dpll->refcount)) { - ASSERT_DPLL_NOT_REGISTERED(dpll); - WARN_ON_ONCE(!xa_empty(&dpll->pin_refs)); - xa_destroy(&dpll->pin_refs); - xa_erase(&dpll_device_xa, dpll->id); - WARN_ON(!list_empty(&dpll->registration_list)); - kfree(dpll); - } + __dpll_device_put(dpll); mutex_unlock(&dpll_lock); } EXPORT_SYMBOL_GPL(dpll_device_put); @@ -416,6 +452,7 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type, reg->ops = ops; reg->priv = priv; dpll->type = type; + __dpll_device_hold(dpll); first_registration = list_empty(&dpll->registration_list); list_add_tail(®->list, &dpll->registration_list); if (!first_registration) { @@ -455,6 +492,7 @@ void dpll_device_unregister(struct dpll_device *dpll, return; } list_del(®->list); + __dpll_device_put(dpll); kfree(reg); if (!list_empty(&dpll->registration_list)) { @@ -666,8 +704,8 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module, if (pos->clock_id == clock_id && pos->pin_idx == pin_idx && pos->module == module) { + __dpll_pin_hold(pos); ret = pos; - refcount_inc(&ret->refcount); break; } } @@ -690,16 +728,7 @@ EXPORT_SYMBOL_GPL(dpll_pin_get); void dpll_pin_put(struct dpll_pin *pin) { mutex_lock(&dpll_lock); - if (refcount_dec_and_test(&pin->refcount)) { - xa_erase(&dpll_pin_xa, pin->id); - xa_destroy(&pin->dpll_refs); - xa_destroy(&pin->parent_refs); - xa_destroy(&pin->ref_sync_pins); - dpll_pin_prop_free(&pin->prop); - fwnode_handle_put(pin->fwnode); - dpll_pin_idx_free(pin->pin_idx); - kfree_rcu(pin, rcu); - } + __dpll_pin_put(pin); mutex_unlock(&dpll_lock); } EXPORT_SYMBOL_GPL(dpll_pin_put); @@ -740,8 +769,8 @@ struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode) mutex_lock(&dpll_lock); xa_for_each(&dpll_pin_xa, index, pin) { if (pin->fwnode == fwnode) { + __dpll_pin_hold(pin); ret = pin; - refcount_inc(&ret->refcount); break; } } @@ -893,7 +922,6 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv, pin); if (ret) goto unlock; - refcount_inc(&pin->refcount); xa_for_each(&parent->dpll_refs, i, ref) { ret = __dpll_pin_register(ref->dpll, pin, ops, priv, parent); if (ret) { @@ -913,7 +941,6 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, parent); dpll_pin_delete_ntf(pin); } - refcount_dec(&pin->refcount); dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); unlock: mutex_unlock(&dpll_lock); @@ -940,7 +967,6 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, mutex_lock(&dpll_lock); dpll_pin_delete_ntf(pin); dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); - refcount_dec(&pin->refcount); xa_for_each(&pin->dpll_refs, i, ref) __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); mutex_unlock(&dpll_lock); From f1e1c6eb82482f1226b6349ec85136d326a235ac Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:35 +0200 Subject: [PATCH 1143/3297] dpll: fix stale iteration in dpll_pin_on_pin_unregister() [ Upstream commit 32239d600236a986c8e6d16aa814d3d91066b244 ] Neither parent->dpll_refs nor pin->dpll_refs on its own is a correct iteration target at unregister time: - pin->dpll_refs includes DPLLs the child was registered against via a different parent or directly; blind unregister WARNs on the cookie miss in dpll_xa_ref_pin_del(). - parent->dpll_refs reflects the parent's current attachments, not those at child-register time. Another driver may have (un)reg'd the parent against additional DPLLs in the meantime, so we miss registrations that exist and visit DPLLs that have none. Walk pin->dpll_refs and use dpll_pin_registration_find() to filter to entries whose cookie is this parent. Symmetric with dpll_pin_on_pin_register(), correct under any subsequent change to parent->dpll_refs. Fixes: 9431063ad323 ("dpll: core: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Link: https://patch.msgid.link/20260607183045.1213735-4-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index f6ab4f0cad84d..241d0e906a6e8 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -961,14 +961,19 @@ EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register); void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv) { + struct dpll_pin_registration *reg; struct dpll_pin_ref *ref; unsigned long i; mutex_lock(&dpll_lock); dpll_pin_delete_ntf(pin); dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); - xa_for_each(&pin->dpll_refs, i, ref) + xa_for_each(&pin->dpll_refs, i, ref) { + reg = dpll_pin_registration_find(ref, ops, priv, parent); + if (!reg) + continue; __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); + } mutex_unlock(&dpll_lock); } EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister); From 6564ce3a2f9c2511547a215229a74c3b727f5d84 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:36 +0200 Subject: [PATCH 1144/3297] dpll: send delete notification before unregister in on-pin rollback [ Upstream commit e83b403eb142be18d223fc599c0ac45519053671 ] The rollback path in dpll_pin_on_pin_register() called __dpll_pin_unregister() before dpll_pin_delete_ntf(). When the unregister dropped the pin's last DPLL reference it cleared the DPLL_REGISTERED mark in dpll_pin_xa, so the subsequent dpll_pin_event_send() failed dpll_pin_available() and aborted with -ENODEV. As a result userspace was never notified of the rollback deletion and remained out of sync with the kernel. Send the delete notification first, matching the order used by dpll_pin_unregister() and dpll_pin_on_pin_unregister(). Fixes: 9d71b54b65b1 ("dpll: netlink: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Link: https://patch.msgid.link/20260607183045.1213735-5-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 241d0e906a6e8..9fdfb7617126c 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -937,9 +937,9 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, dpll_unregister: xa_for_each(&parent->dpll_refs, i, ref) if (i < stop) { + dpll_pin_delete_ntf(pin); __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); - dpll_pin_delete_ntf(pin); } dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); unlock: From 8008ef973f0123192fdd29cbc6852850a0ab96d9 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:37 +0200 Subject: [PATCH 1145/3297] dpll: emit per-dpll delete notifications in dpll_pin_on_pin_unregister() [ Upstream commit df0ba51ccf873e533669578104981109217d8201 ] dpll_pin_on_pin_register() emits a creation notification for every parent->dpll_refs entry, but dpll_pin_on_pin_unregister() emitted only one deletion notification outside the loop. When a pin is registered against multiple parent dplls, userspace sees N creates but a single delete and leaks per-dpll state. Move dpll_pin_delete_ntf() into the loop and call it before __dpll_pin_unregister() so the DPLL_REGISTERED mark is still set when dpll_pin_available() is consulted. Fixes: 9d71b54b65b1 ("dpll: netlink: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260607183045.1213735-6-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 9fdfb7617126c..08b5055ee09cf 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -966,14 +966,14 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, unsigned long i; mutex_lock(&dpll_lock); - dpll_pin_delete_ntf(pin); - dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); xa_for_each(&pin->dpll_refs, i, ref) { reg = dpll_pin_registration_find(ref, ops, priv, parent); if (!reg) continue; + dpll_pin_delete_ntf(pin); __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); } + dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); mutex_unlock(&dpll_lock); } EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister); From 77a1ea975c8774764dc52f2167f4febd90b46953 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:38 +0200 Subject: [PATCH 1146/3297] dpll: guard sync-pair removal on full pin unregister [ Upstream commit 0a5c720a7d57d2287d5566c4ad93ee26b7c06845 ] __dpll_pin_unregister() wiped the global sync-pair state on every (dpll, ops, priv, cookie) tuple removed from a pin. When a pin is registered multiple times and only one registration is being torn down, this dropped sync-pair pairings still in use by the surviving registrations. Move dpll_pin_ref_sync_pair_del() inside the xa_empty(&pin->dpll_refs) branch so it only runs when the last registration is gone, alongside clearing the DPLL_REGISTERED mark. Fixes: 58256a26bfb3 ("dpll: add reference sync get/set") Signed-off-by: Grzegorz Nitka Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260607183045.1213735-7-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 08b5055ee09cf..c457c7b03fce4 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -856,11 +856,12 @@ __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv, void *cookie) { ASSERT_DPLL_PIN_REGISTERED(pin); - dpll_pin_ref_sync_pair_del(pin->id); dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie); - if (xa_empty(&pin->dpll_refs)) + if (xa_empty(&pin->dpll_refs)) { + dpll_pin_ref_sync_pair_del(pin->id); xa_clear_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED); + } } /** From 1bb02353e79f7218160d44b5375bb69125142030 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Sun, 7 Jun 2026 20:30:39 +0200 Subject: [PATCH 1147/3297] dpll: balance create/delete notifications in __dpll_pin_(un)register [ Upstream commit 1a2292101c0dc422466c673031de03d2e871adbe ] __dpll_pin_register() emits dpll_pin_create_ntf() internally, but __dpll_pin_unregister() left the matching delete to its callers. The counts then diverge on dpll_pin_on_pin_register() rollback and on dpll_pin_on_pin_unregister(), leaking stale notifications. Emit dpll_pin_delete_ntf() inside __dpll_pin_unregister() and drop the now-redundant call in dpll_pin_unregister(). Fixes: 9431063ad323 ("dpll: core: Add DPLL framework base functions") Signed-off-by: Grzegorz Nitka Reviewed-by: Arkadiusz Kubalewski Link: https://patch.msgid.link/20260607183045.1213735-8-grzegorz.nitka@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/dpll/dpll_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index c457c7b03fce4..6fa6a0d8e430d 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -856,6 +856,7 @@ __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv, void *cookie) { ASSERT_DPLL_PIN_REGISTERED(pin); + dpll_pin_delete_ntf(pin); dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie); if (xa_empty(&pin->dpll_refs)) { @@ -883,7 +884,6 @@ void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, return; mutex_lock(&dpll_lock); - dpll_pin_delete_ntf(pin); __dpll_pin_unregister(dpll, pin, ops, priv, NULL); mutex_unlock(&dpll_lock); } From b91b241a4eefedc7fddd3f12b35cb93f97c57d9d Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Tue, 9 Jun 2026 23:15:10 +0200 Subject: [PATCH 1148/3297] landlock: Fix unmarked concurrent access to socket family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0ce4243509d1580349dd0d50624036d6b097e958 ] Socket family is read (twice) in a context where the socket is not locked, so another thread can setsockopt(IPV6_ADDRFORM) to write it concurrently. Add needed READ_ONCE() annotation. Use the proper macro to access __sk_common.skc_family like everywhere else. Fixes: fff69fb03dde ("landlock: Support network rules with TCP bind and connect") Signed-off-by: Matthieu Buffet Link: https://patch.msgid.link/20260609211511.85630-1-matthieu@buffet.re Link: https://patch.msgid.link/20260609211511.85630-2-matthieu@buffet.re [mic: Squash two patches, move variable to ease backport, fix comment formatting] Signed-off-by: Mickaël Salaün Signed-off-by: Sasha Levin --- security/landlock/net.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/security/landlock/net.c b/security/landlock/net.c index f309ee2def32a..8bce78cf3d6d0 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -46,6 +46,7 @@ static int current_check_access_socket(struct socket *const sock, const int addrlen, access_mask_t access_request) { + unsigned short sock_family; __be16 port; layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {}; const struct landlock_rule *rule; @@ -69,6 +70,12 @@ static int current_check_access_socket(struct socket *const sock, if (addrlen < offsetofend(typeof(*address), sa_family)) return -EINVAL; + /* + * The socket is not locked, so sk_family can change concurrently due to + * e.g. setsockopt(IPV6_ADDRFORM). + */ + sock_family = READ_ONCE(sock->sk->sk_family); + switch (address->sa_family) { case AF_UNSPEC: if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { @@ -105,7 +112,7 @@ static int current_check_access_socket(struct socket *const sock, * these checks, but it is safer to return a proper * error and test consistency thanks to kselftest. */ - if (sock->sk->__sk_common.skc_family == AF_INET) { + if (sock_family == AF_INET) { const struct sockaddr_in *const sockaddr = (struct sockaddr_in *)address; @@ -183,7 +190,7 @@ static int current_check_access_socket(struct socket *const sock, * check, but it is safer to return a proper error and test * consistency thanks to kselftest. */ - if (address->sa_family != sock->sk->__sk_common.skc_family && + if (address->sa_family != sock_family && address->sa_family != AF_UNSPEC) return -EINVAL; From 36edab340a0675e1a8856cb9b7dae845acb733e9 Mon Sep 17 00:00:00 2001 From: Ovidiu Panait Date: Wed, 10 Jun 2026 08:52:38 +0000 Subject: [PATCH 1149/3297] net: bcmgenet: Use weighted round-robin TX DMA arbitration [ Upstream commit fd615abd53110f0f815984e99e7cc51ca6b7d979 ] Under heavy network traffic, we observed sporadic TX queue timeouts on the Raspberry Pi 4. The timeouts can be reproduced by stress testing the TX path with multiple concurrent iperf UDP streams: iperf3 -c -u -b0 -P16 -t60 NETDEV WATCHDOG: CPU: 0: transmit queue 0 timed out 2044 ms NETDEV WATCHDOG: CPU: 3: transmit queue 0 timed out 2004 ms Investigation showed that the timeouts are caused by the priority-based arbiter. Under heavy load the highest priority queue starves the lower priority ones, causing timeouts. The TX strict priority arbiter is not suitable for the default use case where all the traffic gets spread across all the TX queues. Therefore, to fix this, switch the TX DMA arbiter to Weighted Round-Robin, which services all queues, so they do not stall. The weights were chosen to follow the existing priority scheme: q0 gets the smallest weight, while q1-4 get the bulk of the TX bandwidth. Fixes: 1c1008c793fa ("net: bcmgenet: add main driver file") Signed-off-by: Ovidiu Panait Link: https://patch.msgid.link/20260610085238.56300-1-ovidiu.panait.rb@renesas.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../net/ethernet/broadcom/genet/bcmgenet.c | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index abf6a6451cb1c..fc5c8a4ae41eb 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -41,9 +41,8 @@ #include "bcmgenet.h" -/* Default highest priority queue for multi queue support */ -#define GENET_Q1_PRIORITY 0 -#define GENET_Q0_PRIORITY 1 +#define GENET_Q0_WEIGHT 1 +#define GENET_Q1_WEIGHT 4 #define GENET_Q0_RX_BD_CNT \ (TOTAL_DESC - priv->hw_params->rx_queues * priv->hw_params->rx_bds_per_q) @@ -2125,13 +2124,6 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) int i; index = skb_get_queue_mapping(skb); - /* Mapping strategy: - * queue_mapping = 0, unclassified, packet xmited through ring 0 - * queue_mapping = 1, goes to ring 1. (highest priority queue) - * queue_mapping = 2, goes to ring 2. - * queue_mapping = 3, goes to ring 3. - * queue_mapping = 4, goes to ring 4. - */ ring = &priv->tx_rings[index]; txq = netdev_get_tx_queue(dev, index); @@ -2877,8 +2869,9 @@ static int bcmgenet_rdma_disable(struct bcmgenet_priv *priv) /* Initialize Tx queues * - * Queues 1-4 are priority-based, each one has 32 descriptors, - * with queue 1 being the highest priority queue. + * Queues 1-4 are the priority queues, each one has 32 descriptors. + * The weighted round-robin arbiter gives them a larger share of TX + * bandwidth than the default queue 0. * * Queue 0 is the default Tx queue with * GENET_Q0_TX_BD_CNT = 256 - 4 * 32 = 128 descriptors. @@ -2896,8 +2889,8 @@ static void bcmgenet_init_tx_queues(struct net_device *dev) unsigned int start = 0, end = GENET_Q0_TX_BD_CNT; u32 i, ring_mask, dma_priority[3] = {0, 0, 0}; - /* Enable strict priority arbiter mode */ - bcmgenet_tdma_writel(priv, DMA_ARBITER_SP, DMA_ARB_CTRL); + /* Enable Weighted Round-Robin arbiter mode */ + bcmgenet_tdma_writel(priv, DMA_ARBITER_WRR, DMA_ARB_CTRL); /* Initialize Tx priority queues */ for (i = 0; i <= priv->hw_params->tx_queues; i++) { @@ -2905,7 +2898,7 @@ static void bcmgenet_init_tx_queues(struct net_device *dev) start = end; end += priv->hw_params->tx_bds_per_q; dma_priority[DMA_PRIO_REG_INDEX(i)] |= - (i ? GENET_Q1_PRIORITY : GENET_Q0_PRIORITY) + (i ? GENET_Q1_WEIGHT : GENET_Q0_WEIGHT) << DMA_PRIO_REG_SHIFT(i); } From dcac6e4221f39f4f80adc7fb761c358a612fca62 Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Fri, 12 Jun 2026 07:09:13 +0800 Subject: [PATCH 1150/3297] net: airoha: Fix register index for Tx-fwd counter configuration [ Upstream commit 1402ecccf5630a0b7fa4749d7d2e72abc3f3d73d ] In airoha_qdma_init_qos_stats(), the Tx-fwd counter configuration register uses the same index (i << 1) as the Tx-cpu counter, which overwrites the Tx-cpu configuration. The Tx-fwd counter value register correctly uses (i << 1) + 1, so the configuration register should use the same index. Fix the REG_CNTR_CFG index from (i << 1) to ((i << 1) + 1) so that the Tx-fwd counter is properly configured instead of clobbering the Tx-cpu counter config. Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support") Signed-off-by: Wayen.Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/6a2b40e7.4dd82583.3a5c46.e566@mx.google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 5bf725a785924..7e7256d92fdb0 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1275,7 +1275,7 @@ static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma) FIELD_PREP(CNTR_CHAN_MASK, i)); /* Tx-fwd transferred count */ airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0); - airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), + airoha_qdma_wr(qdma, REG_CNTR_CFG((i << 1) + 1), CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | CNTR_ALL_DSCP_RING_EN_MASK | FIELD_PREP(CNTR_SRC_MASK, 1) | From 4d48c08a0bf6c6ab8c07df37ee8794da891eec80 Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Fri, 12 Jun 2026 07:09:56 +0800 Subject: [PATCH 1151/3297] net: airoha: Fix debugfs new-tuple display for IPv4 ROUTE entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1c3a77471afbb3981af28f7f7c8b2487558e4b00 ] In airoha_ppe_debugfs_foe_show(), the second switch statement falls through from PPE_PKT_TYPE_IPV4_HNAPT/DSLITE to PPE_PKT_TYPE_IPV4_ROUTE, accessing hwe->ipv4.new_tuple for all three types. However, IPv4 ROUTE (3-tuple) entries do not contain a valid new_tuple — this field is only meaningful for NATted flows (HNAPT/DSLITE). For ROUTE entries, the memory at the new_tuple offset holds routing information, not NAT data, so displaying "new=" produces garbage output. Display new_tuple only for HNAPT and DSLITE, and let IPV4_ROUTE fall through to the default case. Fixes: 3fe15c640f38 ("net: airoha: Introduce PPE debugfs support") Link: https://lore.kernel.org/6a2b40ea.4dd82583.3a5c46.e5a2@mx.google.com Signed-off-by: Wayen.Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/6a2be54b.ef98c1b2.3c3224.2ed8@mx.google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_ppe_debugfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c index 0112c41150bb0..e46a98514486f 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c @@ -121,8 +121,6 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private, case PPE_PKT_TYPE_IPV4_DSLITE: src_port = &hwe->ipv4.new_tuple.src_port; dest_port = &hwe->ipv4.new_tuple.dest_port; - fallthrough; - case PPE_PKT_TYPE_IPV4_ROUTE: src_addr = &hwe->ipv4.new_tuple.src_ip; dest_addr = &hwe->ipv4.new_tuple.dest_ip; seq_puts(m, " new="); From f01fb6138f8eb606b56ce9158e2d8b72352c53f4 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 11 Jun 2026 13:35:43 +0800 Subject: [PATCH 1152/3297] kcm: use WRITE_ONCE() when changing lower socket callbacks [ Upstream commit 47186409c092cd7dd70350999186c700233e854d ] kcm_attach() replaces a live lower TCP socket's sk_data_ready and sk_write_space callbacks with KCM handlers, and kcm_unattach() restores them later. Those callback-pointer updates are still plain stores even though the same fields can be read and invoked concurrently on other CPUs. If another CPU observes an older callback snapshot after the live field has already been restored, callback execution can run with a mismatched target and sk_user_data state, leading to stale or misdirected wakeups. Use WRITE_ONCE() for the callback replacement and restore operations so these shared callback fields follow the same visibility contract already established by the earlier 4022 fixes. Fixes: ab7ac4eb9832 ("kcm: Kernel Connection Multiplexor module") Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260611053543.2429462-1-runyu.xiao@seu.edu.cn Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/kcm/kcmsock.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index f6d44481954cd..dc126f7f030c4 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1304,8 +1304,8 @@ static int kcm_attach(struct socket *sock, struct socket *csock, psock->save_write_space = csk->sk_write_space; psock->save_state_change = csk->sk_state_change; csk->sk_user_data = psock; - csk->sk_data_ready = psock_data_ready; - csk->sk_write_space = psock_write_space; + WRITE_ONCE(csk->sk_data_ready, psock_data_ready); + WRITE_ONCE(csk->sk_write_space, psock_write_space); csk->sk_state_change = psock_state_change; write_unlock_bh(&csk->sk_callback_lock); @@ -1381,8 +1381,8 @@ static void kcm_unattach(struct kcm_psock *psock) */ write_lock_bh(&csk->sk_callback_lock); csk->sk_user_data = NULL; - csk->sk_data_ready = psock->save_data_ready; - csk->sk_write_space = psock->save_write_space; + WRITE_ONCE(csk->sk_data_ready, psock->save_data_ready); + WRITE_ONCE(csk->sk_write_space, psock->save_write_space); csk->sk_state_change = psock->save_state_change; strp_stop(&psock->strp); From 287d506d4e0865918cec82bb1361f283a08c979b Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 14 Jun 2026 08:48:00 +0800 Subject: [PATCH 1153/3297] ALSA: seq: oss: Serialize readq reset state with q->lock [ Upstream commit 49ce92d207820f588b0406add82f053decfbe5d9 ] snd_seq_oss_readq_clear() resets qlen, head, and tail without q->lock even though the normal reader and producer paths serialize the same ring state under that spinlock. A reset can therefore race snd_seq_oss_readq_free() or snd_seq_oss_readq_put_event() and leave stale records in the queue, drop freshly queued ones, or report the wrong readiness after wakeup. KCSAN reports a data race between snd_seq_oss_readq_clear() and snd_seq_oss_readq_free(). Take q->lock while clearing the ring and resetting input_time. Factor the enqueue logic into a caller-locked helper so snd_seq_oss_readq_put_timestamp() updates its suppression state under the same lock instead of racing the reset path. The buggy scenario involves two paths, with each column showing the order within that path: reset path: locked readq updater: 1. snd_seq_oss_reset() or 1. A reader or callback producer release reaches takes q->lock on the same queue. snd_seq_oss_readq_clear(). 2. snd_seq_oss_readq_clear() 2. The updater tests or modifies resets qlen, head, tail, qlen, head, and tail. and input_time. 3. snd_seq_oss_readq_clear() 3. The updater completes its wakes sleepers on read-modify-write sequence. q->midi_sleep. 4. Without q->lock, the reset 4. The resulting ring state drives can overlap the locked later reads and readiness. update. KCSAN reports: BUG: KCSAN: data-race in snd_seq_oss_readq_clear / snd_seq_oss_readq_free write to 0xffff8881069fe608 of 4 bytes by task 120516 on cpu 0: snd_seq_oss_readq_free+0x6c/0x80 snd_seq_oss_read+0xcb/0x250 odev_read+0x38/0x60 vfs_read+0xff/0x600 ksys_read+0xb4/0x140 __x64_sys_read+0x46/0x60 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f read to 0xffff8881069fe608 of 4 bytes by task 120517 on cpu 1: snd_seq_oss_readq_clear+0x1f/0x90 snd_seq_oss_reset+0xa7/0xf0 snd_seq_oss_ioctl+0x6f6/0x7e0 odev_ioctl+0x56/0xc0 __x64_sys_ioctl+0xd1/0x120 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f value changed: 0x00000001 -> 0x00000000 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260614004801.3507773-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/oss/seq_oss_readq.c | 77 ++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index bbaf72e70b359..b7cdb1c61f265 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -73,13 +73,17 @@ snd_seq_oss_readq_delete(struct seq_oss_readq *q) void snd_seq_oss_readq_clear(struct seq_oss_readq *q) { - if (q->qlen) { - q->qlen = 0; - q->head = q->tail = 0; + scoped_guard(spinlock_irqsave, &q->lock) { + if (q->qlen) { + q->qlen = 0; + q->head = 0; + q->tail = 0; + } + q->input_time = (unsigned long)-1; } + /* if someone sleeping, wake'em up */ wake_up(&q->midi_sleep); - q->input_time = (unsigned long)-1; } /* @@ -136,11 +140,11 @@ int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev, /* * copy an event to input queue: * return zero if enqueued + * caller must hold lock */ -int -snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev) +static int snd_seq_oss_readq_put_event_locked(struct seq_oss_readq *q, + union evrec *ev) { - guard(spinlock_irqsave)(&q->lock); if (q->qlen >= q->maxlen - 1) return -ENOMEM; @@ -148,12 +152,27 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev) q->tail = (q->tail + 1) % q->maxlen; q->qlen++; - /* wake up sleeper */ - wake_up(&q->midi_sleep); - return 0; } +/* + * copy an event to input queue: + * return zero if enqueued + */ +int +snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev) +{ + int rc; + + scoped_guard(spinlock_irqsave, &q->lock) { + rc = snd_seq_oss_readq_put_event_locked(q, ev); + if (!rc) + wake_up(&q->midi_sleep); + } + + return rc; +} + /* * pop queue @@ -209,23 +228,31 @@ snd_seq_oss_readq_poll(struct seq_oss_readq *q, struct file *file, poll_table *w int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int seq_mode) { - if (curt != q->input_time) { - union evrec rec; - memset(&rec, 0, sizeof(rec)); - switch (seq_mode) { - case SNDRV_SEQ_OSS_MODE_SYNTH: - rec.echo = (curt << 8) | SEQ_WAIT; - snd_seq_oss_readq_put_event(q, &rec); - break; - case SNDRV_SEQ_OSS_MODE_MUSIC: - rec.t.code = EV_TIMING; - rec.t.cmd = TMR_WAIT_ABS; - rec.t.time = curt; - snd_seq_oss_readq_put_event(q, &rec); - break; + int queued = 0; + + scoped_guard(spinlock_irqsave, &q->lock) { + if (curt != q->input_time) { + union evrec rec; + + memset(&rec, 0, sizeof(rec)); + switch (seq_mode) { + case SNDRV_SEQ_OSS_MODE_SYNTH: + rec.echo = (curt << 8) | SEQ_WAIT; + queued = !snd_seq_oss_readq_put_event_locked(q, &rec); + break; + case SNDRV_SEQ_OSS_MODE_MUSIC: + rec.t.code = EV_TIMING; + rec.t.cmd = TMR_WAIT_ABS; + rec.t.time = curt; + queued = !snd_seq_oss_readq_put_event_locked(q, &rec); + break; + } + q->input_time = curt; } - q->input_time = curt; } + if (queued) + wake_up(&q->midi_sleep); + return 0; } From 98a965cb1e767b54b6bbd831b6cea26f521a8f51 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 14 Jun 2026 08:48:01 +0800 Subject: [PATCH 1154/3297] ALSA: seq: avoid stale FIFO cells during resize [ Upstream commit e546128291f8d688dcb931827e2efd2aa6c0734d ] snd_seq_fifo_resize() still needs to publish the replacement pool before it waits for FIFO users. A blocking snd_seq_read() holds f->use_lock while it sleeps, so concurrent senders must be able to queue to the new pool and wake that reader instead of failing against a closing old pool. However, snd_seq_fifo_event_in() duplicates an event before it takes f->lock, and snd_seq_read() can dequeue a cell and later call snd_seq_fifo_cell_putback() if copy_to_user() or snd_seq_expand_var_event() fails. If resize swaps f->pool and detaches oldhead in between, either path can relink an old-pool cell after the snapshot. That stale cell sits outside the drained oldhead list, keeps oldpool->counter elevated, and can leave snd_seq_pool_delete() waiting for the retired pool to drain. Keep the existing swap-before-wait ordering in snd_seq_fifo_resize(), but reject stale cells before any FIFO relink. Revalidate event-in cells under f->lock and retry them against the published replacement pool, and free stale putback cells instead of linking them back into the FIFO. The buggy scenario involves two paths, with each column showing the order within that path: resize path: relink path: 1. Allocate newpool. 1. Take f->use_lock. 2. Swap f->pool to newpool and 2. Duplicate or dequeue an old-pool detach oldhead. cell before oldpool closes. 3. Mark oldpool closing and 3. Reach a later relink point after wait for FIFO users. resize published newpool. 4. Free oldhead and delete 4. Relink the old-pool cell after oldpool. resize detached oldhead. 5. Drop f->use_lock. The reproducer reports a resize ioctl blocked in the expected pool teardown path: signal: resize iteration=98 target_pool=4 exceeded 250ms (elapsed=251ms) diagnostic: resize_tid=651 wchan=snd_seq_pool_done diagnostic: resize_tid=651 stack= snd_seq_pool_done+0x5b/0x140 snd_seq_pool_delete+0x7a/0x90 snd_seq_fifo_resize+0x193/0x1e0 snd_seq_ioctl_set_client_pool+0x214/0x260 snd_seq_ioctl+0x119/0x540 __x64_sys_ioctl+0xd1/0x120 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f A second run with larger pools hit the same target path: signal: resize iteration=32 target_pool=64 exceeded 250ms (elapsed=251ms) diagnostic: resize_tid=663 wchan=snd_seq_pool_done diagnostic: resize_tid=663 stack= snd_seq_pool_done+0x5b/0x140 snd_seq_pool_delete+0x7a/0x90 snd_seq_fifo_resize+0x193/0x1e0 snd_seq_ioctl_set_client_pool+0x214/0x260 snd_seq_ioctl+0x119/0x540 __x64_sys_ioctl+0xd1/0x120 do_syscall_64+0xbb/0x2f0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 2d7d54002e39 ("ALSA: seq: Fix race during FIFO resize") Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260614004801.3507773-2-zzzccc427@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_fifo.c | 52 ++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 91cce18901114..81fa4320ddf6e 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -101,13 +101,17 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, struct snd_seq_event *event) { struct snd_seq_event_cell *cell; + struct snd_seq_pool *pool; + bool linked; int err; if (snd_BUG_ON(!f)) return -EINVAL; guard(snd_seq_fifo)(f); - err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */ +retry: + pool = READ_ONCE(f->pool); + err = snd_seq_event_dup(pool, event, &cell, 1, NULL, NULL); /* always non-blocking */ if (err < 0) { if ((err == -ENOMEM) || (err == -EAGAIN)) atomic_inc(&f->overflow); @@ -115,14 +119,24 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, } /* append new cells to fifo */ + linked = false; scoped_guard(spinlock_irqsave, &f->lock) { - if (f->tail != NULL) - f->tail->next = cell; - f->tail = cell; - if (f->head == NULL) - f->head = cell; - cell->next = NULL; - f->cells++; + if (cell->pool == f->pool) { + if (f->tail) + f->tail->next = cell; + f->tail = cell; + if (!f->head) + f->head = cell; + cell->next = NULL; + f->cells++; + linked = true; + } + } + + if (!linked) { + /* Retry against the replacement pool after resize publishes it. */ + snd_seq_cell_free(cell); + goto retry; } /* wakeup client */ @@ -194,13 +208,21 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, struct snd_seq_event_cell *cell) { + bool linked = false; + if (cell) { - guard(spinlock_irqsave)(&f->lock); - cell->next = f->head; - f->head = cell; - if (!f->tail) - f->tail = cell; - f->cells++; + scoped_guard(spinlock_irqsave, &f->lock) { + if (cell->pool == f->pool) { + cell->next = f->head; + f->head = cell; + if (!f->tail) + f->tail = cell; + f->cells++; + linked = true; + } + } + if (!linked) + snd_seq_cell_free(cell); } } @@ -237,7 +259,7 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) oldpool = f->pool; oldhead = f->head; /* exchange pools */ - f->pool = newpool; + WRITE_ONCE(f->pool, newpool); f->head = NULL; f->tail = NULL; f->cells = 0; From 87d1eaffeec41b6d6735c33391b7294bbde15f85 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 5 Jun 2026 15:11:19 +0200 Subject: [PATCH 1155/3297] netfilter: nf_conncount: callers must hold rcu read lock [ Upstream commit 64d7d5abe2160bba369b4a8f06bdf5630573bab0 ] rcu_derefence_raw() should not have been used here, it concealed this bug. Its used because struct rb_node lacks __rcu annotated pointers, so plain rcu_derefence causes sparse warnings. The major tradeoff is that rcu_derefence_raw() doesn't warn when the caller isn't in a rcu read section. Extend the rcu read lock scope accordingly and cause sparse warnings, those warnings are the lesser evil. Fixes: 11efd5cb04a1 ("openvswitch: Support conntrack zone limit") Closes: https://sashiko.dev/#/patchset/20260603230610.7900-1-fw%40strlen.de Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conncount.c | 6 +++--- net/openvswitch/conntrack.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index 14e62b3263cd9..e880e4b3cd82d 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -499,7 +499,7 @@ count_tree(struct net *net, hash = jhash2(key, data->keylen, conncount_rnd) % CONNCOUNT_SLOTS; root = &data->root[hash]; - parent = rcu_dereference_raw(root->rb_node); + parent = rcu_dereference(root->rb_node); while (parent) { int diff; @@ -507,9 +507,9 @@ count_tree(struct net *net, diff = key_diff(key, rbconn->key, data->keylen); if (diff < 0) { - parent = rcu_dereference_raw(parent->rb_left); + parent = rcu_dereference(parent->rb_left); } else if (diff > 0) { - parent = rcu_dereference_raw(parent->rb_right); + parent = rcu_dereference(parent->rb_right); } else { int ret; diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index a0811e1fba656..f63c8ac74e4a8 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -1800,10 +1800,10 @@ static int ovs_ct_limit_get_zone_limit(struct net *net, } else { rcu_read_lock(); limit = ct_limit_get(info, zone); - rcu_read_unlock(); err = __ovs_ct_limit_get_zone_limit( net, info->data, zone, limit, reply); + rcu_read_unlock(); if (err) return err; } From 7a59146cb9ade235eddd7326764524b7fb2b54bd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 14 Jun 2026 11:05:05 +0200 Subject: [PATCH 1156/3297] ALSA: core: Fix unintuitive behavior of snd_power_ref_and_wait() [ Upstream commit b113a891252c3fa4fab11ec8c2894a22ecaf278c ] snd_power_ref_and_wait() takes the power refcount and doesn't leave it no matter whether it returns an error or not. However, the majority of callers don't expect but just returns without unreferencing in the caller side upon errors. For addressing the potential refcount unbalance, rather correct the behavior of snd_power_ref_wait() to unreference upon returning an error. Note that the problem above is likely negligible; the function returns an error only when the sound card is being shutdown, hence it doesn't matter about the power refcount any longer at such a state. Fixes: e94fdbd7b25d ("ALSA: control: Track in-flight control read/write/tlv accesses") Reported-by: WenTao Liang Closes: https://lore.kernel.org/20260612022121.14329-1-vulab@iscas.ac.cn Link: https://patch.msgid.link/20260614090507.772540-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/init.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index c372b3228785e..c8f992cff4a43 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1131,7 +1131,7 @@ EXPORT_SYMBOL(snd_card_file_remove); * typically around calling control ops. * * The caller needs to pull down the refcount via snd_power_unref() later - * no matter whether the error is returned from this function or not. + * when this function returns 0. * * Return: Zero if successful, or a negative error code. */ @@ -1144,7 +1144,11 @@ int snd_power_ref_and_wait(struct snd_card *card) card->shutdown || snd_power_get_state(card) == SNDRV_CTL_POWER_D0, snd_power_unref(card), snd_power_ref(card)); - return card->shutdown ? -ENODEV : 0; + if (card->shutdown) { + snd_power_unref(card); + return -ENODEV; + } + return 0; } EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); @@ -1161,7 +1165,8 @@ int snd_power_wait(struct snd_card *card) int ret; ret = snd_power_ref_and_wait(card); - snd_power_unref(card); + if (!ret) + snd_power_unref(card); return ret; } EXPORT_SYMBOL(snd_power_wait); From 21303c4a2b7275e626c6b67de0f45f2d5b9bb3e7 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Tue, 19 May 2026 18:44:22 +0800 Subject: [PATCH 1157/3297] cifs: remove all cifs files before kill super [ Upstream commit 6d9a4aaaa8b2612b5ef9d581e2f286a458b71ee1 ] Cifs files may be put into fileinfo_put_wq during umounting cifs. After umount done, cifsFileInfo_put_final is called, which cause following BUG: BUG: kernel NULL pointer dereference, address: 0000000000000000 ... [ 134.222152] list_lru_add+0x64/0x1a0 [ 134.222399] ? cifs_put_tcon+0x171/0x340 [cifs] [ 134.222772] d_lru_add+0x44/0x60 [ 134.222997] dput+0x1fc/0x210 [ 134.223213] cifsFileInfo_put_final+0x11a/0x140 [cifs] [ 134.223576] process_one_work+0x17c/0x320 [ 134.223843] worker_thread+0x188/0x280 [ 134.224084] ? __pfx_worker_thread+0x10/0x10 [ 134.224366] kthread+0xcc/0x100 [ 134.224576] ? __pfx_kthread+0x10/0x10 [ 134.224827] ret_from_fork+0x30/0x50 [ 134.225063] ? __pfx_kthread+0x10/0x10 [ 134.225328] ret_from_fork_asm+0x1b/0x30 This can be reproduce by following: unshare -n bash -c " mkdir -p ${CIFS_MNT} ip netns attach root 1 ip link add eth0 type veth peer veth0 netns root ip link set eth0 up ip -n root link set veth0 up ip addr add 192.168.0.2/24 dev eth0 ip -n root addr add 192.168.0.1/24 dev veth0 ip route add default via 192.168.0.1 dev eth0 ip netns exec root sysctl net.ipv4.ip_forward=1 ip netns exec root iptables -t nat -A POSTROUTING -s 192.168.0.2 -o ${DEV} -j MASQUERADE mount -t cifs ${CIFS_PATH} ${CIFS_MNT} -o vers=3.0,sec=ntlmssp,credentials=${CIFS_CRED},rsize=65536,wsize=65536,cache=none,echo_interval=1 touch ${CIFS_MNT}/a.txt ip netns exec root iptables -t nat -D POSTROUTING -s 192.168.0.2 -o ${DEV} -j MASQUERADE " umount ${CIFS_MNT} Fixes: 340cea84f691 ("cifs: open files should not hold ref on superblock") Signed-off-by: Jian Zhang Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/connect.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 7bc912d00030c..2ee2199d2a6a2 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -4170,6 +4170,9 @@ cifs_umount(struct cifs_sb_info *cifs_sb) } spin_unlock(&cifs_sb->tlink_tree_lock); + flush_workqueue(serverclose_wq); + flush_workqueue(fileinfo_put_wq); + kfree(cifs_sb->prepath); call_rcu(&cifs_sb->rcu, delayed_free); } From 74badb5e2b00ab67774b7340773ac0b70e2a7785 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Mon, 8 Jun 2026 23:57:29 +0800 Subject: [PATCH 1158/3297] smb/client: always return a value for FS_IOC_GETFLAGS [ Upstream commit 7acbaa16b99edaf8ef432229d4b7a6f3b666767d ] Currently, repeated lsattr calls on a regular CIFS file without the compressed attribute may show random flags: $ touch test.bin $ lsattr test.bin s-S-ia-A-EjI---------m test.bin $ lsattr test.bin ------d-cEjI---------m test.bin The lsattr reproducer depends on the previous contents of its userspace buffer, so it may not reproduce on every setup. A deterministic reproducer is to initialize the ioctl argument before FS_IOC_GETFLAGS on a file without the compressed attribute: int flags = 0x7fffffff; ioctl(fd, FS_IOC_GETFLAGS, &flags); On an affected kernel, flags remains 0x7fffffff. With the fix, it is set to 0. This happens because when the cached inode does not have the compressed bit set, the CIFS fallback path in FS_IOC_GETFLAGS returns success without calling put_user() to write the zero flags value into the user buffer. As a result, the caller observes stale contents from its own buffer. Fix this by always writing the visible flags value back to the user buffer before returning success, even when the value is zero. Fixes: 64a5cfa6db94 ("Allow setting per-file compression via SMB2/3") Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/ioctl.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c index 1a6ce837e26af..4607d007d9504 100644 --- a/fs/smb/client/ioctl.c +++ b/fs/smb/client/ioctl.c @@ -393,13 +393,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) } #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_POSIX */ - rc = 0; - if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { - /* add in the compressed bit */ - ExtAttrBits = FS_COMPR_FL; - rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, - (int __user *)arg); - } + if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED) + ExtAttrBits |= FS_COMPR_FL; + + rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, + (int __user *)arg); break; case FS_IOC_SETFLAGS: if (pSMBFile == NULL) From 9f32d4c2de85fdcfc5a385437da64d3bf4fe520c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 11 Jun 2026 13:42:30 +0200 Subject: [PATCH 1159/3297] selftests/bpf: Fix typo in verify_umulti_link_info [ Upstream commit df29003c55115737a8fb4f8a60c6c2bba4c4a484 ] We verify info.uprobe_multi.flags against wrong kprobe-multi flag (BPF_F_KPROBE_MULTI_RETURN). It's the same value as the correct flag (BPF_F_UPROBE_MULTI_RETURN), so there's not functional change. Fixes: 147c69307bcf ("selftests/bpf: Add link_info test for uprobe_multi link") Signed-off-by: Jiri Olsa Link: https://lore.kernel.org/r/20260611114230.950379-8-jolsa@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/fill_link_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c index e401146207510..f589eefbf9fbd 100644 --- a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c +++ b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c @@ -469,7 +469,7 @@ verify_umulti_link_info(int fd, bool retprobe, __u64 *offsets, ASSERT_EQ(info.uprobe_multi.pid, getpid(), "info.uprobe_multi.pid"); ASSERT_EQ(info.uprobe_multi.count, 3, "info.uprobe_multi.count"); - ASSERT_EQ(info.uprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN, + ASSERT_EQ(info.uprobe_multi.flags & BPF_F_UPROBE_MULTI_RETURN, retprobe, "info.uprobe_multi.flags.retprobe"); ASSERT_EQ(info.uprobe_multi.path_size, strlen(path) + 1, "info.uprobe_multi.path_size"); ASSERT_STREQ(path_buf, path, "info.uprobe_multi.path"); From e803077769248591a4ce230703050883917125fd Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 2 Jun 2026 15:47:15 +0100 Subject: [PATCH 1160/3297] selftests/bpf: Initialize operation name before use [ Upstream commit 55ffbe8a15b1254f44d56952fb425a10e3f15c31 ] ASAN reports stack-buffer-overflow due to the uninitialized op_name. Initialize it to fix the issue. Fixes: 054b6c7866c7 ("selftests/bpf: Add verifier log tests for BPF_BTF_LOAD command") Signed-off-by: Leo Yan Acked-by: Ihor Solodrai Link: https://lore.kernel.org/r/20260602-tools_build_fix_zero_init_bpf_only-v2-6-c76e5250ea1c@arm.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/prog_tests/verifier_log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/prog_tests/verifier_log.c b/tools/testing/selftests/bpf/prog_tests/verifier_log.c index 8337c6bc5b95b..abc473c3741f5 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier_log.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier_log.c @@ -317,6 +317,7 @@ static void verif_btf_log_subtest(bool bad_btf) res = load_btf(&opts, true); ASSERT_EQ(res, -ENOSPC, "half_log_fd"); ASSERT_EQ(strlen(logs.buf), 24, "log_fixed_25"); + strscpy(op_name, "log_fixed", sizeof(op_name)); ASSERT_STRNEQ(logs.buf, logs.reference, 24, op_name); /* validate rolling verifier log logic: try all variations of log buf From 5e8627b7a7b7ce338c5008fbef3219a9b3c9e1ae Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Sun, 14 Jun 2026 00:24:42 +0800 Subject: [PATCH 1161/3297] bpf: Fix bpf_get/setsockopt to tos for ipv4-mapped ipv6 socket [ Upstream commit ca0f587c029afa66227f7b932450b1c417403394 ] When TCP over IPv4 via INET6 API, bpf_get/setsockopt with ipv4 will fail, because sk->sk_family is AF_INET6. With ipv6 will success, not take effect, because inet_csk(sk)->icsk_af_ops is ipv6_mapped and use ip_queue_xmit, inet_sk(sk)->tos. To relax this restriction, allow getting/setting tos for those possible ipv4-mapped ipv6 sockets. Fixes: ee7f1e1302f5 ("bpf: Change bpf_setsockopt(SOL_IP) to reuse do_ip_setsockopt()") Signed-off-by: Feng Zhou Signed-off-by: Leon Hwang Link: https://lore.kernel.org/r/20260613162443.60515-2-leon.hwang@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/net/core/filter.c b/net/core/filter.c index 6dd9bdbef1996..0e7e85cba3d3a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5562,11 +5562,24 @@ static int sol_tcp_sockopt(struct sock *sk, int optname, KERNEL_SOCKPTR(optval), *optlen); } +static bool sk_allows_sol_ip_sockopt(struct sock *sk) +{ + switch (sk->sk_family) { + case AF_INET: + return true; + case AF_INET6: + /* Allow getting/setting sockopt for possible ipv4-mapped ipv6 socket. */ + return sk->sk_type != SOCK_RAW && !ipv6_only_sock(sk); + default: + return false; + } +} + static int sol_ip_sockopt(struct sock *sk, int optname, char *optval, int *optlen, bool getopt) { - if (sk->sk_family != AF_INET) + if (!sk_allows_sol_ip_sockopt(sk)) return -EINVAL; switch (optname) { From 264d6a79c96ee3fd21eb3a95504e4c390675e5bf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 11 Feb 2026 15:11:28 -0500 Subject: [PATCH 1162/3297] udf: fix nls leak on udf_fill_super() failure [ Upstream commit 462bdd08fbdf41db223c6117d907c8fd68d666ea ] On all failure exits that go to error_out there we have already moved the nls reference from uopt->nls_map to sbi->s_nls_map, leaving NULL behind. Fixes: c4e89cc674ac ("udf: convert to new mount API") Acked-by: Jan Kara Signed-off-by: Al Viro Signed-off-by: Sasha Levin --- fs/udf/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 62a311d88f2d6..5a82ae2af93ec 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -2329,7 +2329,7 @@ static int udf_fill_super(struct super_block *sb, struct fs_context *fc) error_out: iput(sbi->s_vat_inode); - unload_nls(uopt->nls_map); + unload_nls(sbi->s_nls_map); if (lvid_open) udf_close_lvid(sb); brelse(sbi->s_lvid_bh); From 4e40056bb5c829f0423f0a6694a0477726d2147e Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Mon, 15 Jun 2026 10:19:54 +0800 Subject: [PATCH 1163/3297] bpf, sockmap: reject overflowing copy + len in bpf_msg_push_data() [ Upstream commit 0c0a8ed85349dae298712d79cb276acfeb794d82 ] When the scatterlist ring is full or nearly full, bpf_msg_push_data() enters a copy fallback path and computes copy + len for the page allocation size. Since len comes from BPF with arg3_type = ARG_ANYTHING and both are u32, a crafted len can wrap the sum to a small value, causing an undersized allocation followed by an out-of-bounds memcpy. BUG: unable to handle page fault for address: ffffed104089a402 Oops: Oops: 0000 [#1] SMP KASAN NOPTI Call Trace: __asan_memcpy (mm/kasan/shadow.c:105) bpf_msg_push_data (net/core/filter.c:2852 net/core/filter.c:2788) bpf_prog_9ed8b5711920a7d7+0x2e/0x36 sk_psock_msg_verdict (net/core/skmsg.c:934) tcp_bpf_sendmsg (net/ipv4/tcp_bpf.c:421 net/ipv4/tcp_bpf.c:584) __sys_sendto (net/socket.c:2206) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) Add an overflow check before the allocation. Link: https://lore.kernel.org/all/20260424155913.A19FDC19425@smtp.kernel.org Fixes: 6fff607e2f14 ("bpf: sk_msg program helper bpf_msg_push_data") Tested-by: Xiang Mei Tested-by: Xinyu Ma Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: Kuniyuki Iwashima Signed-off-by: Weiming Shi Signed-off-by: Jiayuan Chen Link: https://lore.kernel.org/r/20260615021959.140010-2-jiayuan.chen@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index 0e7e85cba3d3a..5643f8f34bde6 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2833,6 +2833,9 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, if (!space || (space == 1 && start != offset)) copy = msg->sg.data[i].length; + if (unlikely(copy + len < copy)) + return -EINVAL; + page = alloc_pages(__GFP_NOWARN | GFP_ATOMIC | __GFP_COMP, get_order(copy + len)); if (unlikely(!page)) From 073d95725269676c8b1dd6edbc156897067ef683 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Feb 2026 15:11:20 +0000 Subject: [PATCH 1164/3297] net: remove addr_len argument of recvmsg() handlers [ Upstream commit 8341c989ac77d712c7d6e2bce29e8a4bcb2eeae4 ] Use msg->msg_namelen as a place holder instead of a temporary variable, notably in inet[6]_recvmsg(). This removes stack canaries and allows tail-calls. $ scripts/bloat-o-meter -t vmlinux.old vmlinux add/remove: 0/0 grow/shrink: 2/19 up/down: 26/-532 (-506) Function old new delta rawv6_recvmsg 744 767 +23 vsock_dgram_recvmsg 55 58 +3 vsock_connectible_recvmsg 50 47 -3 unix_stream_recvmsg 161 158 -3 unix_seqpacket_recvmsg 62 59 -3 unix_dgram_recvmsg 42 39 -3 tcp_recvmsg 546 543 -3 mptcp_recvmsg 1568 1565 -3 ping_recvmsg 806 800 -6 tcp_bpf_recvmsg_parser 983 974 -9 ip_recv_error 588 576 -12 ipv6_recv_rxpmtu 442 428 -14 udp_recvmsg 1243 1224 -19 ipv6_recv_error 1046 1024 -22 udpv6_recvmsg 1487 1461 -26 raw_recvmsg 465 437 -28 udp_bpf_recvmsg 1027 984 -43 sock_common_recvmsg 103 27 -76 inet_recvmsg 257 175 -82 inet6_recvmsg 257 175 -82 tcp_bpf_recvmsg 663 568 -95 Total: Before=25143834, After=25143328, chg -0.00% Signed-off-by: Eric Dumazet Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260227151120.1346573-1-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: c010995b29c8 ("sockmap: Fix use-after-free in udp_bpf_recvmsg()") Signed-off-by: Sasha Levin --- .../chelsio/inline_crypto/chtls/chtls.h | 2 +- .../chelsio/inline_crypto/chtls/chtls_io.c | 8 ++++---- drivers/net/ovpn/tcp.c | 2 +- include/net/inet_common.h | 3 +-- include/net/ip.h | 2 +- include/net/ipv6.h | 6 ++---- include/net/ping.h | 5 ++--- include/net/sock.h | 2 +- include/net/tcp.h | 2 +- net/core/sock.c | 7 +------ net/ieee802154/socket.c | 6 +++--- net/ipv4/af_inet.c | 17 ++++++----------- net/ipv4/ip_sockglue.c | 4 ++-- net/ipv4/ping.c | 9 ++++----- net/ipv4/raw.c | 6 +++--- net/ipv4/tcp.c | 5 ++--- net/ipv4/tcp_bpf.c | 17 ++++++++--------- net/ipv4/udp.c | 9 ++++----- net/ipv4/udp_bpf.c | 16 ++++++++-------- net/ipv4/udp_impl.h | 3 +-- net/ipv6/af_inet6.c | 11 +++-------- net/ipv6/datagram.c | 9 ++++----- net/ipv6/ping.c | 3 +-- net/ipv6/raw.c | 8 ++++---- net/ipv6/udp.c | 10 +++++----- net/ipv6/udp_impl.h | 3 +-- net/l2tp/l2tp_ip.c | 4 ++-- net/l2tp/l2tp_ip6.c | 6 +++--- net/mptcp/protocol.c | 4 ++-- net/phonet/datagram.c | 4 ++-- net/phonet/pep.c | 2 +- net/sctp/socket.c | 12 ++++++------ net/tls/tls.h | 2 +- net/tls/tls_sw.c | 3 +-- net/unix/af_unix.c | 4 ++-- net/unix/unix_bpf.c | 2 +- net/vmw_vsock/af_vsock.c | 4 ++-- net/vmw_vsock/vsock_bpf.c | 2 +- net/xfrm/espintcp.c | 2 +- 39 files changed, 99 insertions(+), 127 deletions(-) diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h index 21e0dfeff1585..1de5744a49b07 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h @@ -567,7 +567,7 @@ void chtls_shutdown(struct sock *sk, int how); void chtls_destroy_sock(struct sock *sk); int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); int chtls_recvmsg(struct sock *sk, struct msghdr *msg, - size_t len, int flags, int *addr_len); + size_t len, int flags); void chtls_splice_eof(struct socket *sock); int send_tx_flowc_wr(struct sock *sk, int compl, u32 snd_nxt, u32 rcv_nxt); diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c index 4036db466e188..4ff9b99a1e9f2 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c @@ -1338,7 +1338,7 @@ static void chtls_cleanup_rbuf(struct sock *sk, int copied) } static int chtls_pt_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); struct chtls_hws *hws = &csk->tlshws; @@ -1662,7 +1662,7 @@ static int peekmsg(struct sock *sk, struct msghdr *msg, } int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct tcp_sock *tp = tcp_sk(sk); struct chtls_sock *csk; @@ -1676,7 +1676,7 @@ int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, buffers_freed = 0; if (unlikely(flags & MSG_OOB)) - return tcp_prot.recvmsg(sk, msg, len, flags, addr_len); + return tcp_prot.recvmsg(sk, msg, len, flags); if (unlikely(flags & MSG_PEEK)) return peekmsg(sk, msg, len, flags); @@ -1690,7 +1690,7 @@ int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, csk = rcu_dereference_sk_user_data(sk); if (is_tls_rx(csk)) - return chtls_pt_recvmsg(sk, msg, len, flags, addr_len); + return chtls_pt_recvmsg(sk, msg, len, flags); timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 505c2f214c9f1..433bd07a4f1be 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -158,7 +158,7 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb) } static int ovpn_tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { int err = 0, off, copied = 0, ret; struct ovpn_socket *sock; diff --git a/include/net/inet_common.h b/include/net/inet_common.h index c17a6585d0b0b..0f4626776b296 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h @@ -60,8 +60,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int inet_ctl_sock_create(struct sock **sk, unsigned short family, unsigned short type, unsigned char protocol, struct net *net); -int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, - int *addr_len); +int inet_recv_error(struct sock *sk, struct msghdr *msg, int len); struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb); int inet_gro_complete(struct sk_buff *skb, int nhoff); diff --git a/include/net/ip.h b/include/net/ip.h index 1ce79e62a76fb..e94c59cecef01 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -812,7 +812,7 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *)); -int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len); +int ip_recv_error(struct sock *sk, struct msghdr *msg, int len); void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, u32 info, u8 *payload); void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport, diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 7e984e75f3345..087aa7f502d56 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -1196,10 +1196,8 @@ int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *addr, int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr); void ip6_datagram_release_cb(struct sock *sk); -int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, - int *addr_len); -int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len, - int *addr_len); +int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); +int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len); void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, u32 info, u8 *payload); void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info); diff --git a/include/net/ping.h b/include/net/ping.h index 9634b8800814d..08503f7f89539 100644 --- a/include/net/ping.h +++ b/include/net/ping.h @@ -20,8 +20,7 @@ /* Compatibility glue so we can support IPv6 when it's compiled as a module */ struct pingv6_ops { - int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len, - int *addr_len); + int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len); void (*ip6_datagram_recv_common_ctl)(struct sock *sk, struct msghdr *msg, struct sk_buff *skb); @@ -64,7 +63,7 @@ int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd, struct sk_buff *); int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len); + int flags); int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, void *user_icmph, size_t icmph_len); int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); diff --git a/include/net/sock.h b/include/net/sock.h index 5a26a3834ac68..9637ee4f55ee1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1289,7 +1289,7 @@ struct proto { int (*sendmsg)(struct sock *sk, struct msghdr *msg, size_t len); int (*recvmsg)(struct sock *sk, struct msghdr *msg, - size_t len, int flags, int *addr_len); + size_t len, int flags); void (*splice_eof)(struct socket *sock); int (*bind)(struct sock *sk, struct sockaddr *addr, int addr_len); diff --git a/include/net/tcp.h b/include/net/tcp.h index 5999786c42e1e..31ac90230b12d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -485,7 +485,7 @@ void tcp_reset_keepalive_timer(struct sock *sk, unsigned long timeout); void tcp_set_keepalive(struct sock *sk, int val); void tcp_syn_ack_timeout(const struct request_sock *req); int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len); + int flags); int tcp_set_rcvlowat(struct sock *sk, int val); int tcp_set_window_clamp(struct sock *sk, int val); void tcp_update_recv_tstamps(struct sk_buff *skb, diff --git a/net/core/sock.c b/net/core/sock.c index 04fa0c18adc3e..c52cec0bf942c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -3900,13 +3900,8 @@ int sock_common_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; - int addr_len = 0; - int err; - err = sk->sk_prot->recvmsg(sk, msg, size, flags, &addr_len); - if (err >= 0) - msg->msg_namelen = addr_len; - return err; + return sk->sk_prot->recvmsg(sk, msg, size, flags); } EXPORT_SYMBOL(sock_common_recvmsg); diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c index 18d267921bb53..dc0de5ae6afbd 100644 --- a/net/ieee802154/socket.c +++ b/net/ieee802154/socket.c @@ -313,7 +313,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) } static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { size_t copied = 0; int err = -EOPNOTSUPP; @@ -703,7 +703,7 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) } static int dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { size_t copied = 0; int err = -EOPNOTSUPP; @@ -737,7 +737,7 @@ static int dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, saddr->family = AF_IEEE802154; ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source); - *addr_len = sizeof(*saddr); + msg->msg_namelen = sizeof(*saddr); } if (ro->want_lqi) { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 3109c5ec38f39..4f105a0d0cfc5 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -871,22 +871,17 @@ void inet_splice_eof(struct socket *sock) EXPORT_SYMBOL_GPL(inet_splice_eof); INDIRECT_CALLABLE_DECLARE(int udp_recvmsg(struct sock *, struct msghdr *, - size_t, int, int *)); + size_t, int)); int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; - int addr_len = 0; - int err; if (likely(!(flags & MSG_ERRQUEUE))) sock_rps_record_flow(sk); - err = INDIRECT_CALL_2(sk->sk_prot->recvmsg, tcp_recvmsg, udp_recvmsg, - sk, msg, size, flags, &addr_len); - if (err >= 0) - msg->msg_namelen = addr_len; - return err; + return INDIRECT_CALL_2(sk->sk_prot->recvmsg, tcp_recvmsg, udp_recvmsg, + sk, msg, size, flags); } EXPORT_SYMBOL(inet_recvmsg); @@ -1571,15 +1566,15 @@ __be32 inet_current_timestamp(void) } EXPORT_SYMBOL(inet_current_timestamp); -int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) +int inet_recv_error(struct sock *sk, struct msghdr *msg, int len) { unsigned int family = READ_ONCE(sk->sk_family); if (family == AF_INET) - return ip_recv_error(sk, msg, len, addr_len); + return ip_recv_error(sk, msg, len); #if IS_ENABLED(CONFIG_IPV6) if (family == AF_INET6) - return pingv6_ops.ipv6_recv_error(sk, msg, len, addr_len); + return pingv6_ops.ipv6_recv_error(sk, msg, len); #endif return -EINVAL; } diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 6d9c5c20b1c4f..7d4f1face2e79 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -520,7 +520,7 @@ static bool ipv4_datagram_support_cmsg(const struct sock *sk, /* * Handle MSG_ERRQUEUE */ -int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) +int ip_recv_error(struct sock *sk, struct msghdr *msg, int len) { struct sock_exterr_skb *serr; struct sk_buff *skb; @@ -557,7 +557,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) serr->addr_offset); sin->sin_port = serr->port; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); - *addr_len = sizeof(*sin); + msg->msg_namelen = sizeof(*sin); } memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 690f486173e01..7f32753a16f41 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -847,8 +847,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) goto out; } -int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, - int *addr_len) +int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags) { struct inet_sock *isk = inet_sk(sk); int family = sk->sk_family; @@ -863,7 +862,7 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, goto out; if (flags & MSG_ERRQUEUE) - return inet_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len); skb = skb_recv_datagram(sk, flags, &err); if (!skb) @@ -891,7 +890,7 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, sin->sin_port = 0 /* skb->h.uh->source */; sin->sin_addr.s_addr = ip_hdr(skb)->saddr; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); - *addr_len = sizeof(*sin); + msg->msg_namelen = sizeof(*sin); } if (inet_cmsg_flags(isk)) @@ -912,7 +911,7 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr, inet6_iif(skb)); - *addr_len = sizeof(*sin6); + msg->msg_namelen = sizeof(*sin6); } if (inet6_sk(sk)->rxopt.all) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index f79f4c29a0433..26468a2ec63db 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -737,7 +737,7 @@ static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) */ static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct inet_sock *inet = inet_sk(sk); size_t copied = 0; @@ -749,7 +749,7 @@ static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto out; if (flags & MSG_ERRQUEUE) { - err = ip_recv_error(sk, msg, len, addr_len); + err = ip_recv_error(sk, msg, len); goto out; } @@ -775,7 +775,7 @@ static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, sin->sin_addr.s_addr = ip_hdr(skb)->saddr; sin->sin_port = 0; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); - *addr_len = sizeof(*sin); + msg->msg_namelen = sizeof(*sin); } if (inet_cmsg_flags(inet)) ip_cmsg_recv(msg, skb); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6fc00b38695ba..db80b81a74cbb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2909,14 +2909,13 @@ static int tcp_recvmsg_locked(struct sock *sk, struct msghdr *msg, size_t len, goto out; } -int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, - int *addr_len) +int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags) { int cmsg_flags = 0, ret; struct scm_timestamping_internal tss; if (unlikely(flags & MSG_ERRQUEUE)) - return inet_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len); if (sk_can_busy_loop(sk) && skb_queue_empty_lockless(&sk->sk_receive_queue) && diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index d3d6a47af5270..7a3f0b244c8fc 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -221,8 +221,7 @@ static bool is_next_msg_fin(struct sk_psock *psock) static int tcp_bpf_recvmsg_parser(struct sock *sk, struct msghdr *msg, size_t len, - int flags, - int *addr_len) + int flags) { int peek = flags & MSG_PEEK; struct sk_psock *psock; @@ -232,14 +231,14 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, u32 seq; if (unlikely(flags & MSG_ERRQUEUE)) - return inet_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len); if (!len) return 0; psock = sk_psock_get(sk); if (unlikely(!psock)) - return tcp_recvmsg(sk, msg, len, flags, addr_len); + return tcp_recvmsg(sk, msg, len, flags); lock_sock(sk); tcp = tcp_sk(sk); @@ -352,24 +351,24 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg) } static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct sk_psock *psock; int copied, ret; if (unlikely(flags & MSG_ERRQUEUE)) - return inet_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len); if (!len) return 0; psock = sk_psock_get(sk); if (unlikely(!psock)) - return tcp_recvmsg(sk, msg, len, flags, addr_len); + return tcp_recvmsg(sk, msg, len, flags); if (!skb_queue_empty(&sk->sk_receive_queue) && sk_psock_queue_empty(psock)) { sk_psock_put(sk, psock); - return tcp_recvmsg(sk, msg, len, flags, addr_len); + return tcp_recvmsg(sk, msg, len, flags); } lock_sock(sk); msg_bytes_ready: @@ -389,7 +388,7 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto msg_bytes_ready; release_sock(sk); sk_psock_put(sk, psock); - return tcp_recvmsg(sk, msg, len, flags, addr_len); + return tcp_recvmsg(sk, msg, len, flags); } copied = -EAGAIN; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 4ac4a120fca8c..2f5c91ba0ce92 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2059,8 +2059,7 @@ EXPORT_IPV6_MOD(udp_read_skb); * return it, otherwise we block. */ -int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, - int *addr_len) +int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags) { struct inet_sock *inet = inet_sk(sk); DECLARE_SOCKADDR(struct sockaddr_in *, sin, msg->msg_name); @@ -2071,7 +2070,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, bool checksum_valid = false; if (flags & MSG_ERRQUEUE) - return ip_recv_error(sk, msg, len, addr_len); + return ip_recv_error(sk, msg, len); try_again: off = sk_peek_offset(sk, flags); @@ -2134,11 +2133,11 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, sin->sin_port = udp_hdr(skb)->source; sin->sin_addr.s_addr = ip_hdr(skb)->saddr; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); - *addr_len = sizeof(*sin); + msg->msg_namelen = sizeof(*sin); BPF_CGROUP_RUN_PROG_UDP4_RECVMSG_LOCK(sk, (struct sockaddr *)sin, - addr_len); + &msg->msg_namelen); } if (udp_test_bit(GRO_ENABLED, sk)) diff --git a/net/ipv4/udp_bpf.c b/net/ipv4/udp_bpf.c index 779a3a03762f1..d328a3078ae04 100644 --- a/net/ipv4/udp_bpf.c +++ b/net/ipv4/udp_bpf.c @@ -12,13 +12,13 @@ static struct proto *udpv6_prot_saved __read_mostly; static int sk_udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == AF_INET6) - return udpv6_prot_saved->recvmsg(sk, msg, len, flags, addr_len); + return udpv6_prot_saved->recvmsg(sk, msg, len, flags); #endif - return udp_prot.recvmsg(sk, msg, len, flags, addr_len); + return udp_prot.recvmsg(sk, msg, len, flags); } static bool udp_sk_has_data(struct sock *sk) @@ -61,23 +61,23 @@ static int udp_msg_wait_data(struct sock *sk, struct sk_psock *psock, } static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct sk_psock *psock; int copied, ret; if (unlikely(flags & MSG_ERRQUEUE)) - return inet_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len); if (!len) return 0; psock = sk_psock_get(sk); if (unlikely(!psock)) - return sk_udp_recvmsg(sk, msg, len, flags, addr_len); + return sk_udp_recvmsg(sk, msg, len, flags); if (!psock_has_data(psock)) { - ret = sk_udp_recvmsg(sk, msg, len, flags, addr_len); + ret = sk_udp_recvmsg(sk, msg, len, flags); goto out; } @@ -92,7 +92,7 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, if (data) { if (psock_has_data(psock)) goto msg_bytes_ready; - ret = sk_udp_recvmsg(sk, msg, len, flags, addr_len); + ret = sk_udp_recvmsg(sk, msg, len, flags); goto out; } copied = -EAGAIN; diff --git a/net/ipv4/udp_impl.h b/net/ipv4/udp_impl.h index c7142213fc211..17a6fa8b14093 100644 --- a/net/ipv4/udp_impl.h +++ b/net/ipv4/udp_impl.h @@ -18,8 +18,7 @@ int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, int udp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); -int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, - int *addr_len); +int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags); void udp_destroy_sock(struct sock *sk); #ifdef CONFIG_PROC_FS diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3709f213d33de..f8e1dc4f3227f 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -661,25 +661,20 @@ int inet6_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) } INDIRECT_CALLABLE_DECLARE(int udpv6_recvmsg(struct sock *, struct msghdr *, - size_t, int, int *)); + size_t, int)); int inet6_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; const struct proto *prot; - int addr_len = 0; - int err; if (likely(!(flags & MSG_ERRQUEUE))) sock_rps_record_flow(sk); /* IPV6_ADDRFORM can change sk->sk_prot under us. */ prot = READ_ONCE(sk->sk_prot); - err = INDIRECT_CALL_2(prot->recvmsg, tcp_recvmsg, udpv6_recvmsg, - sk, msg, size, flags, &addr_len); - if (err >= 0) - msg->msg_namelen = addr_len; - return err; + return INDIRECT_CALL_2(prot->recvmsg, tcp_recvmsg, udpv6_recvmsg, + sk, msg, size, flags); } const struct proto_ops inet6_stream_ops = { diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index a62c20e8329f6..97c655c260568 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -451,7 +451,7 @@ static bool ip6_datagram_support_cmsg(struct sk_buff *skb, /* * Handle MSG_ERRQUEUE */ -int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) +int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) { struct ipv6_pinfo *np = inet6_sk(sk); struct sock_exterr_skb *serr; @@ -502,7 +502,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) &sin->sin6_addr); sin->sin6_scope_id = 0; } - *addr_len = sizeof(*sin); + msg->msg_namelen = sizeof(*sin); } memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); @@ -544,8 +544,7 @@ EXPORT_SYMBOL_GPL(ipv6_recv_error); /* * Handle IPV6_RECVPATHMTU */ -int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len, - int *addr_len) +int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len) { struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff *skb; @@ -578,7 +577,7 @@ int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len, sin->sin6_port = 0; sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id; sin->sin6_addr = mtu_info.ip6m_addr.sin6_addr; - *addr_len = sizeof(*sin); + msg->msg_namelen = sizeof(*sin); } put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info); diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index d7a2cdaa26312..b74606334c346 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -24,8 +24,7 @@ #include /* Compatibility glue so we can support IPv6 when it's compiled as a module */ -static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, - int *addr_len) +static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) { return -EAFNOSUPPORT; } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index e369f54844dd9..c534848933ee6 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -431,7 +431,7 @@ int rawv6_rcv(struct sock *sk, struct sk_buff *skb) */ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct ipv6_pinfo *np = inet6_sk(sk); DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); @@ -443,10 +443,10 @@ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, return -EOPNOTSUPP; if (flags & MSG_ERRQUEUE) - return ipv6_recv_error(sk, msg, len, addr_len); + return ipv6_recv_error(sk, msg, len); if (np->rxopt.bits.rxpmtu && READ_ONCE(np->rxpmtu)) - return ipv6_recv_rxpmtu(sk, msg, len, addr_len); + return ipv6_recv_rxpmtu(sk, msg, len); skb = skb_recv_datagram(sk, flags, &err); if (!skb) @@ -480,7 +480,7 @@ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr, inet6_iif(skb)); - *addr_len = sizeof(*sin6); + msg->msg_namelen = sizeof(*sin6); } sock_recv_cmsgs(msg, sk, skb); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index d47b4f5ac8705..7ae825cb5413b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -466,7 +466,7 @@ static int udp6_skb_len(struct sk_buff *skb) */ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct ipv6_pinfo *np = inet6_sk(sk); struct inet_sock *inet = inet_sk(sk); @@ -479,10 +479,10 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int is_udp4; if (flags & MSG_ERRQUEUE) - return ipv6_recv_error(sk, msg, len, addr_len); + return ipv6_recv_error(sk, msg, len); if (np->rxopt.bits.rxpmtu && READ_ONCE(np->rxpmtu)) - return ipv6_recv_rxpmtu(sk, msg, len, addr_len); + return ipv6_recv_rxpmtu(sk, msg, len); try_again: off = sk_peek_offset(sk, flags); @@ -554,11 +554,11 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, ipv6_iface_scope_id(&sin6->sin6_addr, inet6_iif(skb)); } - *addr_len = sizeof(*sin6); + msg->msg_namelen = sizeof(*sin6); BPF_CGROUP_RUN_PROG_UDP6_RECVMSG_LOCK(sk, (struct sockaddr *)sin6, - addr_len); + &msg->msg_namelen); } if (udp_test_bit(GRO_ENABLED, sk)) diff --git a/net/ipv6/udp_impl.h b/net/ipv6/udp_impl.h index 8a406be25a3a6..1bd4a573e1bb2 100644 --- a/net/ipv6/udp_impl.h +++ b/net/ipv6/udp_impl.h @@ -22,8 +22,7 @@ int udpv6_getsockopt(struct sock *sk, int level, int optname, int udpv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen); int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); -int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, - int *addr_len); +int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags); void udpv6_destroy_sock(struct sock *sk); #ifdef CONFIG_PROC_FS diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 29795d2839e8b..e9565bbdc4f62 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -535,7 +535,7 @@ static int l2tp_ip_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } static int l2tp_ip_recvmsg(struct sock *sk, struct msghdr *msg, - size_t len, int flags, int *addr_len) + size_t len, int flags) { struct inet_sock *inet = inet_sk(sk); size_t copied = 0; @@ -568,7 +568,7 @@ static int l2tp_ip_recvmsg(struct sock *sk, struct msghdr *msg, sin->sin_addr.s_addr = ip_hdr(skb)->saddr; sin->sin_port = 0; memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); - *addr_len = sizeof(*sin); + msg->msg_namelen = sizeof(*sin); } if (inet_cmsg_flags(inet)) ip_cmsg_recv(msg, skb); diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index ea232f338dcb6..2d26d10b66125 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -678,7 +678,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } static int l2tp_ip6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct ipv6_pinfo *np = inet6_sk(sk); DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name); @@ -690,7 +690,7 @@ static int l2tp_ip6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto out; if (flags & MSG_ERRQUEUE) - return ipv6_recv_error(sk, msg, len, addr_len); + return ipv6_recv_error(sk, msg, len); skb = skb_recv_datagram(sk, flags, &err); if (!skb) @@ -718,7 +718,7 @@ static int l2tp_ip6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, lsa->l2tp_conn_id = 0; if (ipv6_addr_type(&lsa->l2tp_addr) & IPV6_ADDR_LINKLOCAL) lsa->l2tp_scope_id = inet6_iif(skb); - *addr_len = sizeof(*lsa); + msg->msg_namelen = sizeof(*lsa); } if (np->rxopt.all) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index b82b2a4094874..72a909bc0fca4 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2206,7 +2206,7 @@ static unsigned int mptcp_inq_hint(const struct sock *sk) } static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct mptcp_sock *msk = mptcp_sk(sk); struct scm_timestamping_internal tss; @@ -2216,7 +2216,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, /* MSG_ERRQUEUE is really a no-op till we support IP_RECVERR */ if (unlikely(flags & MSG_ERRQUEUE)) - return inet_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len); lock_sock(sk); if (unlikely(sk->sk_state == TCP_LISTEN)) { diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index 976fe250b5095..22cf23f068328 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c @@ -109,7 +109,7 @@ static int pn_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } static int pn_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct sk_buff *skb = NULL; struct sockaddr_pn sa; @@ -143,7 +143,7 @@ static int pn_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, if (msg->msg_name != NULL) { __sockaddr_check_size(sizeof(sa)); memcpy(msg->msg_name, &sa, sizeof(sa)); - *addr_len = sizeof(sa); + msg->msg_namelen = sizeof(sa); } out: diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 058a16c423aef..7c695f3afc285 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -1276,7 +1276,7 @@ struct sk_buff *pep_read(struct sock *sk) } static int pep_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct sk_buff *skb; int err; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c763eb3296b3e..8a2622da56d23 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -2095,7 +2095,7 @@ static int sctp_skb_pull(struct sk_buff *skb, int len) * 5 for complete description of the flags. */ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct sctp_ulpevent *event = NULL; struct sctp_sock *sp = sctp_sk(sk); @@ -2104,11 +2104,11 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int err = 0; int skb_len; - pr_debug("%s: sk:%p, msghdr:%p, len:%zd, flags:0x%x, addr_len:%p)\n", - __func__, sk, msg, len, flags, addr_len); + pr_debug("%s: sk:%p, msghdr:%p, len:%zd, flags:0x%x)\n", + __func__, sk, msg, len, flags); if (unlikely(flags & MSG_ERRQUEUE)) - return inet_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len); if (sk_can_busy_loop(sk) && skb_queue_empty_lockless(&sk->sk_receive_queue)) @@ -2149,9 +2149,9 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, sock_recv_cmsgs(msg, sk, head_skb); if (sctp_ulpevent_is_notification(event)) { msg->msg_flags |= MSG_NOTIFICATION; - sp->pf->event_msgname(event, msg->msg_name, addr_len); + sp->pf->event_msgname(event, msg->msg_name, &msg->msg_namelen); } else { - sp->pf->skb_msgname(head_skb, msg->msg_name, addr_len); + sp->pf->skb_msgname(head_skb, msg->msg_name, &msg->msg_namelen); } /* Check if we allow SCTP_NXTINFO. */ diff --git a/net/tls/tls.h b/net/tls/tls.h index a1d8467bece33..12f44cb649c96 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -161,7 +161,7 @@ void tls_sw_free_resources_rx(struct sock *sk); void tls_sw_release_resources_rx(struct sock *sk); void tls_sw_free_ctx_rx(struct tls_context *tls_ctx); int tls_sw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len); + int flags); bool tls_sw_sock_is_readable(struct sock *sk); ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, struct pipe_inode_info *pipe, diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 9949ae0270811..c80ae566ad4e6 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2064,8 +2064,7 @@ static void tls_rx_reader_unlock(struct sock *sk, struct tls_sw_context_rx *ctx) int tls_sw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, - int *addr_len) + int flags) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 12c075a07e76b..b65479a738f4c 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2702,7 +2702,7 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, size_t si const struct proto *prot = READ_ONCE(sk->sk_prot); if (prot != &unix_dgram_proto) - return prot->recvmsg(sk, msg, size, flags, NULL); + return prot->recvmsg(sk, msg, size, flags); #endif return __unix_dgram_recvmsg(sk, msg, size, flags); } @@ -3179,7 +3179,7 @@ static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, const struct proto *prot = READ_ONCE(sk->sk_prot); if (prot != &unix_stream_proto) - return prot->recvmsg(sk, msg, size, flags, NULL); + return prot->recvmsg(sk, msg, size, flags); #endif return unix_stream_read_generic(&state, true); } diff --git a/net/unix/unix_bpf.c b/net/unix/unix_bpf.c index 57f3124c9d8db..f86ff19e9764d 100644 --- a/net/unix/unix_bpf.c +++ b/net/unix/unix_bpf.c @@ -49,7 +49,7 @@ static int __unix_recvmsg(struct sock *sk, struct msghdr *msg, } static int unix_bpf_recvmsg(struct sock *sk, struct msghdr *msg, - size_t len, int flags, int *addr_len) + size_t len, int flags) { struct unix_sock *u = unix_sk(sk); struct sk_psock *psock; diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 2db48b53e47c7..d007ce89d19b8 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -1412,7 +1412,7 @@ int vsock_dgram_recvmsg(struct socket *sock, struct msghdr *msg, prot = READ_ONCE(sk->sk_prot); if (prot != &vsock_proto) - return prot->recvmsg(sk, msg, len, flags, NULL); + return prot->recvmsg(sk, msg, len, flags); #endif return __vsock_dgram_recvmsg(sock, msg, len, flags); @@ -2485,7 +2485,7 @@ vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, prot = READ_ONCE(sk->sk_prot); if (prot != &vsock_proto) - return prot->recvmsg(sk, msg, len, flags, NULL); + return prot->recvmsg(sk, msg, len, flags); #endif return __vsock_connectible_recvmsg(sock, msg, len, flags); diff --git a/net/vmw_vsock/vsock_bpf.c b/net/vmw_vsock/vsock_bpf.c index 07b96d56f3a57..9049d26486460 100644 --- a/net/vmw_vsock/vsock_bpf.c +++ b/net/vmw_vsock/vsock_bpf.c @@ -74,7 +74,7 @@ static int __vsock_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int } static int vsock_bpf_recvmsg(struct sock *sk, struct msghdr *msg, - size_t len, int flags, int *addr_len) + size_t len, int flags) { struct sk_psock *psock; struct vsock_sock *vsk; diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c index b951700dffc4d..542098283783d 100644 --- a/net/xfrm/espintcp.c +++ b/net/xfrm/espintcp.c @@ -133,7 +133,7 @@ static int espintcp_parse(struct strparser *strp, struct sk_buff *skb) } static int espintcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags, int *addr_len) + int flags) { struct espintcp_ctx *ctx = espintcp_getctx(sk); struct sk_buff *skb; From 81567d2b3f4dc4fc32f8b61433738bce2cafd4a1 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 15 Jun 2026 10:19:57 +0800 Subject: [PATCH 1165/3297] sockmap: Fix use-after-free in udp_bpf_recvmsg() [ Upstream commit c010995b29c8939c6aa69e3cb26f8dbee163d156 ] syzbot reported use-after-free of struct sk_msg in sk_msg_recvmsg(). [0] sk_msg_recvmsg() peeks sk_msg from psock->ingress_msg under a lock, but its processing is lockless. Thus, sk_msg_recvmsg() must be serialised by callers, otherwise multiple threads could touch the same sk_msg. For example, TCP uses lock_sock(), and AF_UNIX uses unix_sk(sk)->iolock. Initially, udp_bpf_recvmsg() had used lock_sock(), but the cited commit removed it. Let's serialise sk_msg_recvmsg() with lock_sock() in udp_bpf_recvmsg(). Note that holding spin_lock_bh(&sk->sk_receive_queue.lock) is not an option due to copy_page_to_iter() in sk_msg_recvmsg(). [0]: BUG: KASAN: slab-use-after-free in sk_msg_recvmsg+0xb54/0xc30 net/core/skmsg.c:428 Read of size 4 at addr ffff88814cdcf000 by task syz.0.24/6020 CPU: 1 UID: 0 PID: 6020 Comm: syz.0.24 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 01/13/2026 Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xba/0x230 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 sk_msg_recvmsg+0xb54/0xc30 net/core/skmsg.c:428 udp_bpf_recvmsg+0x4bd/0xe00 net/ipv4/udp_bpf.c:84 inet_recvmsg+0x260/0x270 net/ipv4/af_inet.c:891 sock_recvmsg_nosec net/socket.c:1078 [inline] sock_recvmsg+0x1a8/0x270 net/socket.c:1100 ____sys_recvmsg+0x1e6/0x4a0 net/socket.c:2812 ___sys_recvmsg+0x215/0x590 net/socket.c:2854 do_recvmmsg+0x334/0x800 net/socket.c:2949 __sys_recvmmsg net/socket.c:3023 [inline] __do_sys_recvmmsg net/socket.c:3046 [inline] __se_sys_recvmmsg net/socket.c:3039 [inline] __x64_sys_recvmmsg+0x198/0x250 net/socket.c:3039 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xe2/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fb319f9aeb9 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 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 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fb31ad97028 EFLAGS: 00000246 ORIG_RAX: 000000000000012b RAX: ffffffffffffffda RBX: 00007fb31a216090 RCX: 00007fb319f9aeb9 RDX: 0000000000000001 RSI: 0000200000000400 RDI: 0000000000000004 RBP: 00007fb31a008c1f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000040000021 R11: 0000000000000246 R12: 0000000000000000 R13: 00007fb31a216128 R14: 00007fb31a216090 R15: 00007ffe21dd0a98 Allocated by task 6019: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x3d1/0x6e0 mm/slub.c:5780 kmalloc_noprof include/linux/slab.h:957 [inline] kzalloc_noprof include/linux/slab.h:1094 [inline] alloc_sk_msg net/core/skmsg.c:510 [inline] sk_psock_skb_ingress_self+0x60/0x350 net/core/skmsg.c:612 sk_psock_verdict_apply net/core/skmsg.c:1038 [inline] sk_psock_verdict_recv+0x7d9/0x8d0 net/core/skmsg.c:1236 udp_read_skb+0x73e/0x7e0 net/ipv4/udp.c:2045 sk_psock_verdict_data_ready+0x12d/0x550 net/core/skmsg.c:1257 __udp_enqueue_schedule_skb+0xc54/0x10b0 net/ipv4/udp.c:1789 __udp_queue_rcv_skb net/ipv4/udp.c:2346 [inline] udp_queue_rcv_one_skb+0xac5/0x19c0 net/ipv4/udp.c:2475 __udp4_lib_mcast_deliver+0xc06/0xcf0 net/ipv4/udp.c:2585 __udp4_lib_rcv+0x10f6/0x2620 net/ipv4/udp.c:2724 ip_protocol_deliver_rcu+0x282/0x440 net/ipv4/ip_input.c:207 ip_local_deliver_finish+0x3bb/0x6f0 net/ipv4/ip_input.c:241 NF_HOOK+0x336/0x3c0 include/linux/netfilter.h:318 dst_input include/net/dst.h:474 [inline] ip_sublist_rcv_finish+0x221/0x2a0 net/ipv4/ip_input.c:584 ip_list_rcv_finish net/ipv4/ip_input.c:628 [inline] ip_sublist_rcv+0x5c6/0xa70 net/ipv4/ip_input.c:644 ip_list_rcv+0x3f1/0x450 net/ipv4/ip_input.c:678 __netif_receive_skb_list_ptype net/core/dev.c:6195 [inline] __netif_receive_skb_list_core+0x7e5/0x810 net/core/dev.c:6242 __netif_receive_skb_list net/core/dev.c:6294 [inline] netif_receive_skb_list_internal+0x995/0xcf0 net/core/dev.c:6385 netif_receive_skb_list+0x54/0x410 net/core/dev.c:6437 xdp_recv_frames net/bpf/test_run.c:269 [inline] xdp_test_run_batch net/bpf/test_run.c:350 [inline] bpf_test_run_xdp_live+0x1946/0x1cf0 net/bpf/test_run.c:379 bpf_prog_test_run_xdp+0x81c/0x1160 net/bpf/test_run.c:1396 bpf_prog_test_run+0x2c7/0x340 kernel/bpf/syscall.c:4703 __sys_bpf+0x5cb/0x920 kernel/bpf/syscall.c:6182 __do_sys_bpf kernel/bpf/syscall.c:6274 [inline] __se_sys_bpf kernel/bpf/syscall.c:6272 [inline] __x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:6272 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xe2/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 6021: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2540 [inline] slab_free mm/slub.c:6674 [inline] kfree+0x1be/0x650 mm/slub.c:6882 kfree_sk_msg include/linux/skmsg.h:385 [inline] sk_msg_recvmsg+0xaa8/0xc30 net/core/skmsg.c:483 udp_bpf_recvmsg+0x4bd/0xe00 net/ipv4/udp_bpf.c:84 inet_recvmsg+0x260/0x270 net/ipv4/af_inet.c:891 sock_recvmsg_nosec net/socket.c:1078 [inline] sock_recvmsg+0x1a8/0x270 net/socket.c:1100 ____sys_recvmsg+0x1e6/0x4a0 net/socket.c:2812 ___sys_recvmsg+0x215/0x590 net/socket.c:2854 do_recvmmsg+0x334/0x800 net/socket.c:2949 __sys_recvmmsg net/socket.c:3023 [inline] __do_sys_recvmmsg net/socket.c:3046 [inline] __se_sys_recvmmsg net/socket.c:3039 [inline] __x64_sys_recvmmsg+0x198/0x250 net/socket.c:3039 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xe2/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 9f2470fbc4cb ("skmsg: Improve udp_bpf_recvmsg() accuracy") Reported-by: syzbot+9307c991a6d07ce6e6d8@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/69922ac9.a70a0220.2c38d7.00e0.GAE@google.com/ Reviewed-by: Jiayuan Chen Reviewed-by: Jakub Sitnicki Reviewed-by: Emil Tsalapatis Signed-off-by: Kuniyuki Iwashima Signed-off-by: Jiayuan Chen Link: https://lore.kernel.org/r/20260615021959.140010-5-jiayuan.chen@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/ipv4/udp_bpf.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/ipv4/udp_bpf.c b/net/ipv4/udp_bpf.c index d328a3078ae04..6987b8cf59eb4 100644 --- a/net/ipv4/udp_bpf.c +++ b/net/ipv4/udp_bpf.c @@ -52,7 +52,9 @@ static int udp_msg_wait_data(struct sock *sk, struct sk_psock *psock, sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); ret = udp_msg_has_data(sk, psock); if (!ret) { + release_sock(sk); wait_woken(&wait, TASK_INTERRUPTIBLE, timeo); + lock_sock(sk); ret = udp_msg_has_data(sk, psock); } sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); @@ -81,6 +83,7 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto out; } + lock_sock(sk); msg_bytes_ready: copied = sk_msg_recvmsg(sk, psock, msg, len, flags); if (!copied) { @@ -92,11 +95,17 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, if (data) { if (psock_has_data(psock)) goto msg_bytes_ready; + + release_sock(sk); + ret = sk_udp_recvmsg(sk, msg, len, flags); goto out; } copied = -EAGAIN; } + + release_sock(sk); + ret = copied; out: sk_psock_put(sk, psock); From fe09dd288722f1c749b7506c0b3e7841a7d85027 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Mon, 15 Jun 2026 10:19:58 +0800 Subject: [PATCH 1166/3297] bpf, sockmap: fix integer overflow in bpf_msg_pop_data() bounds check [ Upstream commit a48802fb2cd2d1e23651989f8ff4d15e9d5dad54 ] start and len are u32, so u64 last = start + len; evaluates start + len in 32-bit and wraps before storing it in last. The bounds check if (start >= offset + l || last > msg->sg.size) return -EINVAL; can then be passed with an out-of-range start/len, after which the pop loop runs off the end of the scatterlist and sk_msg_shift_left() calls put_page() on the empty msg->sg.end slot: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:sk_msg_shift_left net/core/filter.c:2957 [inline] RIP: 0010:____bpf_msg_pop_data net/core/filter.c:3103 [inline] RIP: 0010:bpf_msg_pop_data+0x753/0x1a10 net/core/filter.c:2984 Call Trace: bpf_prog_4cc92c278f4d5d56+0x1b1/0x1e8 bpf_prog_run_pin_on_cpu+0x107/0x320 include/linux/filter.h:746 sk_psock_msg_verdict+0x357/0x7f0 net/core/skmsg.c:934 tcp_bpf_send_verdict net/ipv4/tcp_bpf.c:420 [inline] tcp_bpf_sendmsg+0x766/0x1ae0 net/ipv4/tcp_bpf.c:583 __sock_sendmsg+0x153/0x1c0 net/socket.c:802 __sys_sendto+0x326/0x430 net/socket.c:2265 __x64_sys_sendto+0xe3/0x100 net/socket.c:2268 do_syscall_64+0x14c/0x480 entry_SYSCALL_64_after_hwframe+0x77/0x7f Widen the addition with a (u64) cast so the bound is evaluated in 64-bit and a len near U32_MAX no longer wraps below msg->sg.size. While here, change pop from int to u32. It counts bytes against the unsigned scatterlist lengths and can never be negative, so the signed type only invites sign-confusion in the pop loop. Fixes: 7246d8ed4dcc ("bpf: helper to pop data from messages") Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: Kuniyuki Iwashima Signed-off-by: Sechang Lim Signed-off-by: Jiayuan Chen Link: https://lore.kernel.org/r/20260615021959.140010-6-jiayuan.chen@linux.dev Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index 5643f8f34bde6..e91659caea0f8 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2999,8 +2999,8 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, u32, len, u64, flags) { u32 i = 0, l = 0, space, offset = 0; - u64 last = start + len; - int pop; + u64 last = (u64)start + len; + u32 pop; if (unlikely(flags)) return -EINVAL; From 92f38fe85198ace9b211bc7712b7951acfb94560 Mon Sep 17 00:00:00 2001 From: Yadan Fan Date: Mon, 25 May 2026 12:04:36 +0800 Subject: [PATCH 1167/3297] MIPS: mm: Fix out-of-bounds write in maar_res_walk() [ Upstream commit 1b001b16bc88f3f7817e228acfd91ee01bdcfcce ] maar_res_walk() uses wi->num_cfg as the index into the fixed-size wi->cfg array, but checks whether the array is full only after it has filled the selected entry. If walk_system_ram_range() reports more than 16 memory ranges, the overflow call writes one struct maar_config past the end of the array before WARN_ON() prevents num_cfg from advancing. Move the full-array check before taking the array slot and return non-zero when the scratch array is full, so walk_system_ram_range() terminates the walk instead of invoking the callback for further ranges. Fixes: a5718fe8f70f ("MIPS: mm: Drop boot_mem_map") Signed-off-by: Yadan Fan Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/mm/init.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 8986048f9b110..decd201504043 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -275,9 +275,15 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages, void *data) { struct maar_walk_info *wi = data; - struct maar_config *cfg = &wi->cfg[wi->num_cfg]; + struct maar_config *cfg; unsigned int maar_align; + /* Ensure we don't overflow the cfg array */ + if (WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg))) + return -1; + + cfg = &wi->cfg[wi->num_cfg]; + /* MAAR registers hold physical addresses right shifted by 4 bits */ maar_align = BIT(MIPS_MAAR_ADDR_SHIFT + 4); @@ -286,9 +292,7 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages, cfg->upper = ALIGN_DOWN(PFN_PHYS(start_pfn + nr_pages), maar_align) - 1; cfg->attrs = MIPS_MAAR_S; - /* Ensure we don't overflow the cfg array */ - if (!WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg))) - wi->num_cfg++; + wi->num_cfg++; return 0; } From 73711688479df333acc134e3730fff1acba1843e Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:10 +0530 Subject: [PATCH 1168/3297] powerpc/perf: fix preempt count underflow in fsl_emb_pmu_del [ Upstream commit 81e3a86030462824a67d697739cf3f387f4ba350 ] fsl_emb_pmu_del() unconditionally calls put_cpu_var(cpu_hw_events) at the 'out:' label, but only calls the matching get_cpu_var() after the 'i < 0' early-return check. When event->hw.idx is negative the function jumps to 'out:' without having taken get_cpu_var(), and the trailing put_cpu_var() then issues an unmatched preempt_enable(), underflowing preempt_count. On a CONFIG_PREEMPT=y kernel preempt_count would underflow and eventually present as a 'scheduling while atomic' BUG. Move put_cpu_var() to pair with get_cpu_var() so the percpu access is correctly bracketed and the 'out:' label only handles perf_pmu_enable. Fixes: a11106544f33 ("powerpc/perf: e500 support") Reviewed-by: Shrikanth Hegde Signed-off-by: Aboorva Devarajan Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-2-aboorvad@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/perf/core-fsl-emb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/perf/core-fsl-emb.c b/arch/powerpc/perf/core-fsl-emb.c index 7120ab20cbfec..02b5dd74c187a 100644 --- a/arch/powerpc/perf/core-fsl-emb.c +++ b/arch/powerpc/perf/core-fsl-emb.c @@ -366,9 +366,10 @@ static void fsl_emb_pmu_del(struct perf_event *event, int flags) cpuhw->n_events--; + put_cpu_var(cpu_hw_events); + out: perf_pmu_enable(event->pmu); - put_cpu_var(cpu_hw_events); } static void fsl_emb_pmu_start(struct perf_event *event, int ef_flags) From 527cd14a416f23d6e681ea6bac176cf8533770f4 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:11 +0530 Subject: [PATCH 1169/3297] powerpc/powernv: fix preempt count leak in pnv_kexec_wait_secondaries_down [ Upstream commit 0ecd26e93e698c8327521910fc6296f5b84a4b92 ] pnv_kexec_wait_secondaries_down() calls get_cpu() to obtain the current CPU id but never calls the matching put_cpu(), leaking one preempt_disable() nesting level on every invocation. In practice the imbalance does not trigger a visible splat because the kexec teardown path is a one-way trip: IRQs are already disabled, no schedule() occurs after the leak, and default_machine_kexec() overwrites preempt_count with HARDIRQ_OFFSET before jumping into kexec_sequence() which never returns. However the bookkeeping is still wrong. The function only needs the current CPU id, and this path runs with interrupts disabled and the CPU pinned, so the preempt_disable() side-effect of get_cpu() is unnecessary. Replace it with raw_smp_processor_id(). Fixes: 298b34d7d578 ("powerpc/powernv: Fix kexec races going back to OPAL") Signed-off-by: Aboorva Devarajan Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-3-aboorvad@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/powernv/setup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 4dbb47ddbdcc4..06ed5e2aa2658 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -396,7 +396,8 @@ static void pnv_kexec_wait_secondaries_down(void) { int my_cpu, i, notified = -1; - my_cpu = get_cpu(); + /* Called with interrupts disabled, so the CPU is pinned. */ + my_cpu = raw_smp_processor_id(); for_each_online_cpu(i) { uint8_t status; From 66919a6d72b9e32efb4dc7c517f95e4d6ba245c5 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Fri, 5 Jun 2026 13:59:12 +0530 Subject: [PATCH 1170/3297] powerpc/kexec: fix double get_cpu() imbalance in kexec_prepare_cpus [ Upstream commit 5c86f1c1f972761a04bf22f4c0618d1aa714185b ] kexec_prepare_cpus_wait() calls get_cpu() internally to obtain the current CPU id. kexec_prepare_cpus() calls kexec_prepare_cpus_wait() twice -- once for KEXEC_STATE_IRQS_OFF and once for KEXEC_STATE_REAL_MODE -- but only issues a single put_cpu() at the end, leaving preempt_count elevated by one extra nesting level. In practice the imbalance does not trigger a 'scheduling while atomic' splat because the kexec path is a one-way trip: IRQs are already disabled, no schedule() occurs after the leak, and default_machine_kexec() overwrites preempt_count with HARDIRQ_OFFSET before jumping into kexec_sequence() which never returns. However the bookkeeping is still wrong. kexec_prepare_cpus() calls local_irq_disable()/hard_irq_disable() before invoking kexec_prepare_cpus_wait(), so the CPU is already pinned and the get_cpu()/put_cpu() preempt_disable() bracketing is unnecessary. Only the current CPU id is needed, so replace get_cpu() with raw_smp_processor_id() and drop the now-unneeded put_cpu(). Fixes: 1fc711f7ffb0 ("powerpc/kexec: Fix race in kexec shutdown") Signed-off-by: Aboorva Devarajan Reviewed-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605082912.305100-4-aboorvad@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/kexec/core_64.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/powerpc/kexec/core_64.c b/arch/powerpc/kexec/core_64.c index 825ab8a88f18e..58c13a59b93b5 100644 --- a/arch/powerpc/kexec/core_64.c +++ b/arch/powerpc/kexec/core_64.c @@ -169,7 +169,7 @@ static void kexec_prepare_cpus_wait(int wait_state) int my_cpu, i, notified=-1; hw_breakpoint_disable(); - my_cpu = get_cpu(); + my_cpu = raw_smp_processor_id(); /* Make sure each CPU has at least made it to the state we need. * * FIXME: There is a (slim) chance of a problem if not all of the CPUs @@ -267,8 +267,6 @@ static void kexec_prepare_cpus(void) /* after we tell the others to go down */ if (ppc_md.kexec_cpu_down) ppc_md.kexec_cpu_down(0, 0); - - put_cpu(); } #else /* ! SMP */ From db1616263a2c53bd8b2248d0b08ea05f7af2d714 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 29 May 2026 11:34:06 +0800 Subject: [PATCH 1171/3297] KEYS: Use acquire when reading state in keyring search [ Upstream commit c1201b37f666f6466ab1fd3a381c2b7a4b7e9fee ] The negative-key race fix added release/acquire ordering for key use. Publish payload before state; read state before payload. keyring_search_iterator() still uses READ_ONCE() before match callbacks. An asymmetric match callback calls asymmetric_key_ids(), which reads key->payload.data[asym_key_ids]. Use key_read_state() there to complete that ordering. Fixes: 363b02dab09b ("KEYS: Fix race between updating and finding a negative key") Signed-off-by: Gui-Dong Han Reviewed-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260529033406.20673-1-hanguidong02@gmail.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- security/keys/keyring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index df2580072cfe1..e105349794f23 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -576,7 +576,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) struct keyring_search_context *ctx = iterator_data; const struct key *key = keyring_ptr_to_key(object); unsigned long kflags = READ_ONCE(key->flags); - short state = READ_ONCE(key->state); + short state = key_read_state(key); kenter("{%d}", key->serial); From 0d8a12d7143126afdf9fbe2e3d438650dd6603ed Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Jun 2026 13:59:49 +0000 Subject: [PATCH 1172/3297] tipc: fix UAF in tipc_l2_send_msg() [ Upstream commit f4c3d89fc986b0da196ddfc6cfe0ea5d5d08bec6 ] Syzbot reported a slab-use-after-free in ipvlan_hard_header() when called from tipc_l2_send_msg(). The root cause is that tipc_disable_l2_media() calls synchronize_net() while b->media_ptr is still valid. This allows concurrent RCU readers to obtain the device pointer after synchronize_net() has finished. The pointer is cleared later in bearer_disable(), but without any subsequent synchronization, allowing the device to be freed while still in use by readers. Fix this by clearing b->media_ptr in tipc_disable_l2_media() before calling synchronize_net(). This is safe to do now because the call order in bearer_disable() was reversed in 0d051bf93c06 ("tipc: make bearer packet filtering generic") to call tipc_node_delete_links() (which needs the pointer) before disable_media(). Fixes: 282b3a056225 ("tipc: send out RESET immediately when link goes down") https: //lore.kernel.org/netdev/6a2c1007.428ffe26.258b27.015d.GAE@google.com/T/#u Reported-by: syzbot+64ec81389cbad56a8c35@syzkaller.appspotmail.com Signed-off-by: Eric Dumazet Cc: Jon Maloy Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260612135949.4010482-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/bearer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index ae1ddbf718539..8866a3d713132 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -482,6 +482,7 @@ void tipc_disable_l2_media(struct tipc_bearer *b) dev = (struct net_device *)rtnl_dereference(b->media_ptr); dev_remove_pack(&b->pt); RCU_INIT_POINTER(dev->tipc_ptr, NULL); + RCU_INIT_POINTER(b->media_ptr, NULL); synchronize_net(); dev_put(dev); } From d774cdbda6634a78d0f2baf201ee5a8c57f3bc0e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Jun 2026 16:25:17 +0000 Subject: [PATCH 1173/3297] tcp: ipv6: clamp default adverting MSS to avoid GSO_BY_FRAGS (0xFFFF) [ Upstream commit 2bf43d0e2e6a27d52a7d624e2d6b9116972e8a22 ] When MTU is large, ip6_default_advmss() can return IPV6_MAXPLEN (65535). This is interpreted by TCP as mss_clamp, allowing the MSS to reach 65535. However, 0xFFFF is also used as a magic value GSO_BY_FRAGS in the kernel. If a TCP packet with gso_size=0xFFFF is passed to skb_segment(), it will be mistakenly treated as GSO_BY_FRAGS, leading to a NULL pointer dereference because local TCP packets do not use frag_list. Fix this by returning min(IPV6_MAXPLEN, GSO_BY_FRAGS - 1) (65534) from ip6_default_advmss() when MTU is large. Also update the stale comment in ip6_default_advmss() which suggested that IPV6_MAXPLEN is returned to mean "any MSS". Fixes: 3953c46c3ac7 ("sk_buff: allow segmenting based on frag sizes") Reported-by: syzbot+ebdb22d461c904fc3cb2@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2c3193.8812e0fc.3c3fa4.0001.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612162517.83394-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/route.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index f89220929c4e2..b34f6a3e70519 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3268,11 +3268,11 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst) /* * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. - * IPV6_MAXPLEN is also valid and means: "any MSS, - * rely only on pmtu discovery" + * Limit the default MSS to GSO_BY_FRAGS - 1 to avoid + * collision with the GSO_BY_FRAGS magic value (0xFFFF). */ if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) - mtu = IPV6_MAXPLEN; + mtu = min_t(unsigned int, IPV6_MAXPLEN, GSO_BY_FRAGS - 1); return mtu; } From 93e133b9193cb415b8cd0e81839fbbafb2a3359c Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Sat, 13 Jun 2026 08:23:12 +0800 Subject: [PATCH 1174/3297] net: airoha: Fix always-true condition in PPE1 queue reservation loop [ Upstream commit c66f8511a8109fa50767941b26d3623e316fde02 ] In airoha_fe_pse_ports_init(), the inner condition for PPE1 queue reservation is identical to the for-loop bound, making it always true and the else branch dead code: for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) /* always true */ set RSV_PAGES; else set 0; /* unreachable */ } The intended behavior is to reserve pages only for the first half of the queues, matching the PPE2 implementation on line 334 which correctly uses the /2 divisor. Fix the PPE1 condition accordingly. Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") Signed-off-by: Wayen.Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/6a2ca3de.ad59c0a6.147df9.2ac1@mx.google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 7e7256d92fdb0..5e7939c4f3e6f 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -311,7 +311,7 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth) PSE_QUEUE_RSV_PAGES); /* PPE1 */ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { - if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) + if (q < pse_port_num_queues[FE_PSE_PORT_PPE1] / 2) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, PSE_QUEUE_RSV_PAGES); else From 4c55003566c0b730876809cca2d79539b6823ee4 Mon Sep 17 00:00:00 2001 From: Selvamani Rajagopal Date: Thu, 11 Jun 2026 14:55:40 -0700 Subject: [PATCH 1175/3297] net: ethernet: oa_tc6: Remove FCS size in RX frame [ Upstream commit a5a1d11dd3729146abe7420b874bab15870a42b4 ] OA TC6 MAC-PHY appends FCS to the incoming frame. It must be removed from the frame before being passed to the stack. With FCS in the frame, many applications, like ping or any application that uses IP layer may work as they may carry the packet size information in the protocol. Application like ptp4l, particularly if it uses layer 2 for its communication, it will fail with "bad message" due to the extra 4 bytes added by the presence of FCS. Fixes: d70a0d8f2f2d ("net: ethernet: oa_tc6: implement receive path to receive rx ethernet frames") Signed-off-by: Selvamani Rajagopal Link: https://patch.msgid.link/20260611-level-trigger-v5-3-4533a9e85ce2@onsemi.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/oa_tc6.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 91a906a7918a2..cfe1f6e3cd12c 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -752,6 +752,17 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) { + /* MAC-PHY delivers each frame with its Ethernet FCS attached. + * Strip it before handing over to the stack, unless the user + * has asked to keep it via NETIF_F_RXFCS. Keeping the FCS + * in the frame is harmless for IP traffic, but is parsed as + * a (malformed) suffix TLV by PTP, which makes ptp4l reject + * every message with "bad message" error. + */ + if (!(tc6->netdev->features & NETIF_F_RXFCS) && + tc6->rx_skb->len > ETH_FCS_LEN) + skb_trim(tc6->rx_skb, tc6->rx_skb->len - ETH_FCS_LEN); + tc6->rx_skb->protocol = eth_type_trans(tc6->rx_skb, tc6->netdev); tc6->netdev->stats.rx_packets++; tc6->netdev->stats.rx_bytes += tc6->rx_skb->len; From 5a48dd5150d7c6b9fef64157a6ad0f3e9df80d45 Mon Sep 17 00:00:00 2001 From: Brett Creeley Date: Sun, 14 Jun 2026 13:52:59 -0700 Subject: [PATCH 1176/3297] ionic: Fix check in ionic_get_link_ext_stats [ Upstream commit 7678e69079c10b2fb10977f28f44ddb22971ea5b ] The current check will fail if SR-IOV is not initialized for the physical function; this is because is_physfn is 0 if sriov_init() isn't run or fails. Change the check that prevents getting the link down count to use is_virtfn instead so that VFs don't get this functionality, which was the original intent. Fixes: 132b4ebfa090 ("ionic: add support for ethtool extended stat link_down_count") Signed-off-by: Brett Creeley Signed-off-by: Eric Joyner Link: https://patch.msgid.link/20260614205303.48088-2-eric.joyner@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index 347b0aff100b9..62b739ca26d26 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -116,8 +116,15 @@ static void ionic_get_link_ext_stats(struct net_device *netdev, { struct ionic_lif *lif = netdev_priv(netdev); - if (lif->ionic->pdev->is_physfn) - stats->link_down_events = lif->link_down_count; + if (lif->ionic->pdev->is_virtfn) + return; + + if (!lif->ionic->idev.port_info) { + netdev_err_once(netdev, "port_info not initialized\n"); + return; + } + + stats->link_down_events = lif->link_down_count; } static int ionic_get_link_ksettings(struct net_device *netdev, From 104a7ff382a58a83cae018925e3574c784e8274c Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:38 -0700 Subject: [PATCH 1177/3297] RDMA/bnxt_re: Free SRQ toggle page after firmware teardown [ Upstream commit 131e2918b9b0529687e67e2e58047304027f095a ] Free the toggle page only after firmware teardown completes so that an NQ interrupt arriving during bnxt_qplib_destroy_srq() won't write the toggle values to an already-freed page. Move free_page() after bnxt_qplib_destroy_srq(). Fixes: 181028a0d84c ("RDMA/bnxt_re: Share a page to expose per SRQ info with userspace") Link: https://patch.msgid.link/r/20260615224751.232802-3-selvin.xavier@broadcom.com Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index c1645de495a72..36b64e1cc0b0c 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -1826,11 +1826,11 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) struct bnxt_re_dev *rdev = srq->rdev; struct bnxt_qplib_srq *qplib_srq = &srq->qplib_srq; - if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { - free_page((unsigned long)srq->uctx_srq_page); + if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) hash_del(&srq->hash_entry); - } bnxt_qplib_destroy_srq(&rdev->qplib_res, qplib_srq); + if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) + free_page((unsigned long)srq->uctx_srq_page); ib_umem_release(srq->umem); atomic_dec(&rdev->stats.res.srq_count); return 0; From 95d46a8d3ba9fdbe356fe7ed0117bc78dac9d557 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:44 -0700 Subject: [PATCH 1178/3297] RDMA/bnxt_re: Avoid displaying the kernel pointer [ Upstream commit 7d70c704a06f620d5d421ab76bac5e225bfb4308 ] While dumping the info on MR using the rdma tool, we dump the mr_hwq which is a kernel pointer. There is no need to expose this value for end user. So avoid it. Fixes: 7363eb76b7f3 ("RDMA/bnxt_re: Support driver specific data collection using rdma tool") Link: https://patch.msgid.link/r/20260615224751.232802-9-selvin.xavier@broadcom.com Reviewed-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index b13810572c2e8..4ca2eee6fbc22 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -1093,8 +1093,6 @@ static int bnxt_re_fill_res_mr_entry(struct sk_buff *msg, struct ib_mr *ib_mr) goto err; if (rdma_nl_put_driver_u32(msg, "element_size", mr_hwq->element_size)) goto err; - if (rdma_nl_put_driver_u64_hex(msg, "hwq", (unsigned long)mr_hwq)) - goto err; if (rdma_nl_put_driver_u64_hex(msg, "va", mr->qplib_mr.va)) goto err; From 0fe155aa844e44157b82b7390f15b2a49a59765c Mon Sep 17 00:00:00 2001 From: Kalesh AP Date: Mon, 2 Mar 2026 16:30:31 +0530 Subject: [PATCH 1179/3297] RDMA/bnxt_re: Move the UAPI methods to a dedicated file [ Upstream commit eee6268421a2ccc6d47d8e175a1f4bfcd78a83ca ] This is in preparation for upcoming patches in the series. Driver has to support additional UAPIs for some applications. Moving current UAPI implementation to a new file, uapi.c. Link: https://patch.msgid.link/r/20260302110036.36387-2-sriharsha.basavapatna@broadcom.com Signed-off-by: Kalesh AP Reviewed-by: Selvin Xavier Signed-off-by: Sriharsha Basavapatna Signed-off-by: Jason Gunthorpe Stable-dep-of: a57592c6392a ("RDMA/bnxt_re: Fail DBR related page allocation UAPIs if the feature is disabled") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/Makefile | 2 +- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 305 +------------------- drivers/infiniband/hw/bnxt_re/ib_verbs.h | 3 + drivers/infiniband/hw/bnxt_re/uapi.c | 339 +++++++++++++++++++++++ 4 files changed, 344 insertions(+), 305 deletions(-) create mode 100644 drivers/infiniband/hw/bnxt_re/uapi.c diff --git a/drivers/infiniband/hw/bnxt_re/Makefile b/drivers/infiniband/hw/bnxt_re/Makefile index f63417d2ccc64..1533c079c9da1 100644 --- a/drivers/infiniband/hw/bnxt_re/Makefile +++ b/drivers/infiniband/hw/bnxt_re/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o bnxt_re-y := main.o ib_verbs.o \ qplib_res.o qplib_rcfw.o \ qplib_sp.o qplib_fp.o hw_counters.o \ - debugfs.o + debugfs.o uapi.o diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 36b64e1cc0b0c..8f380e9e41a8c 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -627,7 +627,7 @@ static int bnxt_re_create_fence_mr(struct bnxt_re_pd *pd) return rc; } -static struct bnxt_re_user_mmap_entry* +struct bnxt_re_user_mmap_entry* bnxt_re_mmap_entry_insert(struct bnxt_re_ucontext *uctx, u64 mem_offset, enum bnxt_re_mmap_flag mmap_flag, u64 *offset) { @@ -4526,32 +4526,6 @@ int bnxt_re_destroy_flow(struct ib_flow *flow_id) return rc; } -static struct bnxt_re_cq *bnxt_re_search_for_cq(struct bnxt_re_dev *rdev, u32 cq_id) -{ - struct bnxt_re_cq *cq = NULL, *tmp_cq; - - hash_for_each_possible(rdev->cq_hash, tmp_cq, hash_entry, cq_id) { - if (tmp_cq->qplib_cq.id == cq_id) { - cq = tmp_cq; - break; - } - } - return cq; -} - -static struct bnxt_re_srq *bnxt_re_search_for_srq(struct bnxt_re_dev *rdev, u32 srq_id) -{ - struct bnxt_re_srq *srq = NULL, *tmp_srq; - - hash_for_each_possible(rdev->srq_hash, tmp_srq, hash_entry, srq_id) { - if (tmp_srq->qplib_srq.id == srq_id) { - srq = tmp_srq; - break; - } - } - return srq; -} - /* Helper function to mmap the virtual memory from user app */ int bnxt_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma) { @@ -4654,280 +4628,3 @@ int bnxt_re_process_mad(struct ib_device *ibdev, int mad_flags, ret |= IB_MAD_RESULT_REPLY; return ret; } - -static int UVERBS_HANDLER(BNXT_RE_METHOD_NOTIFY_DRV)(struct uverbs_attr_bundle *attrs) -{ - struct bnxt_re_ucontext *uctx; - - uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); - bnxt_re_pacing_alert(uctx->rdev); - return 0; -} - -static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle *attrs) -{ - struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); - enum bnxt_re_alloc_page_type alloc_type; - struct bnxt_re_user_mmap_entry *entry; - enum bnxt_re_mmap_flag mmap_flag; - struct bnxt_qplib_chip_ctx *cctx; - struct bnxt_re_ucontext *uctx; - struct bnxt_re_dev *rdev; - u64 mmap_offset; - u32 length; - u32 dpi; - u64 addr; - int err; - - uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); - if (IS_ERR(uctx)) - return PTR_ERR(uctx); - - err = uverbs_get_const(&alloc_type, attrs, BNXT_RE_ALLOC_PAGE_TYPE); - if (err) - return err; - - rdev = uctx->rdev; - cctx = rdev->chip_ctx; - - switch (alloc_type) { - case BNXT_RE_ALLOC_WC_PAGE: - if (cctx->modes.db_push) { - if (bnxt_qplib_alloc_dpi(&rdev->qplib_res, &uctx->wcdpi, - uctx, BNXT_QPLIB_DPI_TYPE_WC)) - return -ENOMEM; - length = PAGE_SIZE; - dpi = uctx->wcdpi.dpi; - addr = (u64)uctx->wcdpi.umdbr; - mmap_flag = BNXT_RE_MMAP_WC_DB; - } else { - return -EINVAL; - } - - break; - case BNXT_RE_ALLOC_DBR_BAR_PAGE: - length = PAGE_SIZE; - addr = (u64)rdev->pacing.dbr_bar_addr; - mmap_flag = BNXT_RE_MMAP_DBR_BAR; - break; - - case BNXT_RE_ALLOC_DBR_PAGE: - length = PAGE_SIZE; - addr = (u64)rdev->pacing.dbr_page; - mmap_flag = BNXT_RE_MMAP_DBR_PAGE; - break; - - default: - return -EOPNOTSUPP; - } - - entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); - if (!entry) - return -ENOMEM; - - uobj->object = entry; - uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); - err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, - &mmap_offset, sizeof(mmap_offset)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, - &length, sizeof(length)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_DPI, - &dpi, sizeof(dpi)); - if (err) - return err; - - return 0; -} - -static int alloc_page_obj_cleanup(struct ib_uobject *uobject, - enum rdma_remove_reason why, - struct uverbs_attr_bundle *attrs) -{ - struct bnxt_re_user_mmap_entry *entry = uobject->object; - struct bnxt_re_ucontext *uctx = entry->uctx; - - switch (entry->mmap_flag) { - case BNXT_RE_MMAP_WC_DB: - if (uctx && uctx->wcdpi.dbr) { - struct bnxt_re_dev *rdev = uctx->rdev; - - bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); - uctx->wcdpi.dbr = NULL; - } - break; - case BNXT_RE_MMAP_DBR_BAR: - case BNXT_RE_MMAP_DBR_PAGE: - break; - default: - goto exit; - } - rdma_user_mmap_entry_remove(&entry->rdma_entry); -exit: - return 0; -} - -DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_ALLOC_PAGE, - UVERBS_ATTR_IDR(BNXT_RE_ALLOC_PAGE_HANDLE, - BNXT_RE_OBJECT_ALLOC_PAGE, - UVERBS_ACCESS_NEW, - UA_MANDATORY), - UVERBS_ATTR_CONST_IN(BNXT_RE_ALLOC_PAGE_TYPE, - enum bnxt_re_alloc_page_type, - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, - UVERBS_ATTR_TYPE(u64), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_DPI, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_DESTROY_PAGE, - UVERBS_ATTR_IDR(BNXT_RE_DESTROY_PAGE_HANDLE, - BNXT_RE_OBJECT_ALLOC_PAGE, - UVERBS_ACCESS_DESTROY, - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_ALLOC_PAGE, - UVERBS_TYPE_ALLOC_IDR(alloc_page_obj_cleanup), - &UVERBS_METHOD(BNXT_RE_METHOD_ALLOC_PAGE), - &UVERBS_METHOD(BNXT_RE_METHOD_DESTROY_PAGE)); - -DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_NOTIFY_DRV); - -DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_NOTIFY_DRV, - &UVERBS_METHOD(BNXT_RE_METHOD_NOTIFY_DRV)); - -/* Toggle MEM */ -static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bundle *attrs) -{ - struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); - enum bnxt_re_mmap_flag mmap_flag = BNXT_RE_MMAP_TOGGLE_PAGE; - enum bnxt_re_get_toggle_mem_type res_type; - struct bnxt_re_user_mmap_entry *entry; - struct bnxt_re_ucontext *uctx; - struct ib_ucontext *ib_uctx; - struct bnxt_re_dev *rdev; - struct bnxt_re_srq *srq; - u32 length = PAGE_SIZE; - struct bnxt_re_cq *cq; - u64 mem_offset; - u32 offset = 0; - u64 addr = 0; - u32 res_id; - int err; - - ib_uctx = ib_uverbs_get_ucontext(attrs); - if (IS_ERR(ib_uctx)) - return PTR_ERR(ib_uctx); - - err = uverbs_get_const(&res_type, attrs, BNXT_RE_TOGGLE_MEM_TYPE); - if (err) - return err; - - uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); - rdev = uctx->rdev; - err = uverbs_copy_from(&res_id, attrs, BNXT_RE_TOGGLE_MEM_RES_ID); - if (err) - return err; - - switch (res_type) { - case BNXT_RE_CQ_TOGGLE_MEM: - cq = bnxt_re_search_for_cq(rdev, res_id); - if (!cq) - return -EINVAL; - - addr = (u64)cq->uctx_cq_page; - break; - case BNXT_RE_SRQ_TOGGLE_MEM: - srq = bnxt_re_search_for_srq(rdev, res_id); - if (!srq) - return -EINVAL; - - addr = (u64)srq->uctx_srq_page; - break; - - default: - return -EOPNOTSUPP; - } - - entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mem_offset); - if (!entry) - return -ENOMEM; - - uobj->object = entry; - uverbs_finalize_uobj_create(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); - err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_PAGE, - &mem_offset, sizeof(mem_offset)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, - &length, sizeof(length)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, - &offset, sizeof(offset)); - if (err) - return err; - - return 0; -} - -static int get_toggle_mem_obj_cleanup(struct ib_uobject *uobject, - enum rdma_remove_reason why, - struct uverbs_attr_bundle *attrs) -{ - struct bnxt_re_user_mmap_entry *entry = uobject->object; - - rdma_user_mmap_entry_remove(&entry->rdma_entry); - return 0; -} - -DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM, - UVERBS_ATTR_IDR(BNXT_RE_TOGGLE_MEM_HANDLE, - BNXT_RE_OBJECT_GET_TOGGLE_MEM, - UVERBS_ACCESS_NEW, - UA_MANDATORY), - UVERBS_ATTR_CONST_IN(BNXT_RE_TOGGLE_MEM_TYPE, - enum bnxt_re_get_toggle_mem_type, - UA_MANDATORY), - UVERBS_ATTR_PTR_IN(BNXT_RE_TOGGLE_MEM_RES_ID, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_PAGE, - UVERBS_ATTR_TYPE(u64), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM, - UVERBS_ATTR_IDR(BNXT_RE_RELEASE_TOGGLE_MEM_HANDLE, - BNXT_RE_OBJECT_GET_TOGGLE_MEM, - UVERBS_ACCESS_DESTROY, - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_GET_TOGGLE_MEM, - UVERBS_TYPE_ALLOC_IDR(get_toggle_mem_obj_cleanup), - &UVERBS_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM), - &UVERBS_METHOD(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM)); - -const struct uapi_definition bnxt_re_uapi_defs[] = { - UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_ALLOC_PAGE), - UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_NOTIFY_DRV), - UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_GET_TOGGLE_MEM), - {} -}; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 76ba9ab04d5ce..a11f56730a31c 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -293,4 +293,7 @@ static inline u32 __to_ib_port_num(u16 port_id) unsigned long bnxt_re_lock_cqs(struct bnxt_re_qp *qp); void bnxt_re_unlock_cqs(struct bnxt_re_qp *qp, unsigned long flags); +struct bnxt_re_user_mmap_entry* +bnxt_re_mmap_entry_insert(struct bnxt_re_ucontext *uctx, u64 mem_offset, + enum bnxt_re_mmap_flag mmap_flag, u64 *offset); #endif /* __BNXT_RE_IB_VERBS_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c new file mode 100644 index 0000000000000..0145882e49f6e --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Copyright (c) 2025, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * Description: uapi interpreter + */ + +#include +#include +#include +#include +#define UVERBS_MODULE_NAME bnxt_re +#include +#include + +#include "roce_hsi.h" +#include "qplib_res.h" +#include "qplib_sp.h" +#include "qplib_fp.h" +#include "qplib_rcfw.h" +#include "bnxt_re.h" +#include "ib_verbs.h" + +static struct bnxt_re_cq *bnxt_re_search_for_cq(struct bnxt_re_dev *rdev, u32 cq_id) +{ + struct bnxt_re_cq *cq = NULL, *tmp_cq; + + hash_for_each_possible(rdev->cq_hash, tmp_cq, hash_entry, cq_id) { + if (tmp_cq->qplib_cq.id == cq_id) { + cq = tmp_cq; + break; + } + } + return cq; +} + +static struct bnxt_re_srq *bnxt_re_search_for_srq(struct bnxt_re_dev *rdev, u32 srq_id) +{ + struct bnxt_re_srq *srq = NULL, *tmp_srq; + + hash_for_each_possible(rdev->srq_hash, tmp_srq, hash_entry, srq_id) { + if (tmp_srq->qplib_srq.id == srq_id) { + srq = tmp_srq; + break; + } + } + return srq; +} + +static int UVERBS_HANDLER(BNXT_RE_METHOD_NOTIFY_DRV)(struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_ucontext *uctx; + struct ib_ucontext *ib_uctx; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + if (IS_ERR(uctx)) + return PTR_ERR(uctx); + + bnxt_re_pacing_alert(uctx->rdev); + return 0; +} + +static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); + enum bnxt_re_alloc_page_type alloc_type; + struct bnxt_re_user_mmap_entry *entry; + enum bnxt_re_mmap_flag mmap_flag; + struct bnxt_qplib_chip_ctx *cctx; + struct bnxt_re_ucontext *uctx; + struct ib_ucontext *ib_uctx; + struct bnxt_re_dev *rdev; + u64 mmap_offset; + u32 length; + u32 dpi; + u64 addr; + int err; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + if (IS_ERR(uctx)) + return PTR_ERR(uctx); + + err = uverbs_get_const(&alloc_type, attrs, BNXT_RE_ALLOC_PAGE_TYPE); + if (err) + return err; + + rdev = uctx->rdev; + cctx = rdev->chip_ctx; + + switch (alloc_type) { + case BNXT_RE_ALLOC_WC_PAGE: + if (cctx->modes.db_push) { + if (bnxt_qplib_alloc_dpi(&rdev->qplib_res, &uctx->wcdpi, + uctx, BNXT_QPLIB_DPI_TYPE_WC)) + return -ENOMEM; + length = PAGE_SIZE; + dpi = uctx->wcdpi.dpi; + addr = (u64)uctx->wcdpi.umdbr; + mmap_flag = BNXT_RE_MMAP_WC_DB; + } else { + return -EINVAL; + } + + break; + case BNXT_RE_ALLOC_DBR_BAR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_bar_addr; + mmap_flag = BNXT_RE_MMAP_DBR_BAR; + break; + + case BNXT_RE_ALLOC_DBR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_page; + mmap_flag = BNXT_RE_MMAP_DBR_PAGE; + break; + + default: + return -EOPNOTSUPP; + } + + entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); + if (!entry) + return -ENOMEM; + + uobj->object = entry; + uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); + err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, + &mmap_offset, sizeof(mmap_offset)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, + &length, sizeof(length)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_DPI, + &dpi, sizeof(dpi)); + if (err) + return err; + + return 0; +} + +static int alloc_page_obj_cleanup(struct ib_uobject *uobject, + enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_user_mmap_entry *entry = uobject->object; + struct bnxt_re_ucontext *uctx = entry->uctx; + + switch (entry->mmap_flag) { + case BNXT_RE_MMAP_WC_DB: + if (uctx && uctx->wcdpi.dbr) { + struct bnxt_re_dev *rdev = uctx->rdev; + + bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); + uctx->wcdpi.dbr = NULL; + } + break; + case BNXT_RE_MMAP_DBR_BAR: + case BNXT_RE_MMAP_DBR_PAGE: + break; + default: + goto exit; + } + rdma_user_mmap_entry_remove(&entry->rdma_entry); +exit: + return 0; +} + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_ALLOC_PAGE, + UVERBS_ATTR_IDR(BNXT_RE_ALLOC_PAGE_HANDLE, + BNXT_RE_OBJECT_ALLOC_PAGE, + UVERBS_ACCESS_NEW, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(BNXT_RE_ALLOC_PAGE_TYPE, + enum bnxt_re_alloc_page_type, + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_DPI, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_DESTROY_PAGE, + UVERBS_ATTR_IDR(BNXT_RE_DESTROY_PAGE_HANDLE, + BNXT_RE_OBJECT_ALLOC_PAGE, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_ALLOC_PAGE, + UVERBS_TYPE_ALLOC_IDR(alloc_page_obj_cleanup), + &UVERBS_METHOD(BNXT_RE_METHOD_ALLOC_PAGE), + &UVERBS_METHOD(BNXT_RE_METHOD_DESTROY_PAGE)); + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_NOTIFY_DRV); + +DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_NOTIFY_DRV, + &UVERBS_METHOD(BNXT_RE_METHOD_NOTIFY_DRV)); + +/* Toggle MEM */ +static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); + enum bnxt_re_mmap_flag mmap_flag = BNXT_RE_MMAP_TOGGLE_PAGE; + enum bnxt_re_get_toggle_mem_type res_type; + struct bnxt_re_user_mmap_entry *entry; + struct bnxt_re_ucontext *uctx; + struct ib_ucontext *ib_uctx; + struct bnxt_re_dev *rdev; + struct bnxt_re_srq *srq; + u32 length = PAGE_SIZE; + struct bnxt_re_cq *cq; + u64 mem_offset; + u32 offset = 0; + u64 addr = 0; + u32 res_id; + int err; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + err = uverbs_get_const(&res_type, attrs, BNXT_RE_TOGGLE_MEM_TYPE); + if (err) + return err; + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + rdev = uctx->rdev; + err = uverbs_copy_from(&res_id, attrs, BNXT_RE_TOGGLE_MEM_RES_ID); + if (err) + return err; + + switch (res_type) { + case BNXT_RE_CQ_TOGGLE_MEM: + cq = bnxt_re_search_for_cq(rdev, res_id); + if (!cq) + return -EINVAL; + + addr = (u64)cq->uctx_cq_page; + break; + case BNXT_RE_SRQ_TOGGLE_MEM: + srq = bnxt_re_search_for_srq(rdev, res_id); + if (!srq) + return -EINVAL; + + addr = (u64)srq->uctx_srq_page; + break; + + default: + return -EOPNOTSUPP; + } + + entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mem_offset); + if (!entry) + return -ENOMEM; + + uobj->object = entry; + uverbs_finalize_uobj_create(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); + err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_PAGE, + &mem_offset, sizeof(mem_offset)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, + &length, sizeof(length)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, + &offset, sizeof(offset)); + if (err) + return err; + + return 0; +} + +static int get_toggle_mem_obj_cleanup(struct ib_uobject *uobject, + enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_user_mmap_entry *entry = uobject->object; + + rdma_user_mmap_entry_remove(&entry->rdma_entry); + return 0; +} + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM, + UVERBS_ATTR_IDR(BNXT_RE_TOGGLE_MEM_HANDLE, + BNXT_RE_OBJECT_GET_TOGGLE_MEM, + UVERBS_ACCESS_NEW, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(BNXT_RE_TOGGLE_MEM_TYPE, + enum bnxt_re_get_toggle_mem_type, + UA_MANDATORY), + UVERBS_ATTR_PTR_IN(BNXT_RE_TOGGLE_MEM_RES_ID, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_PAGE, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM, + UVERBS_ATTR_IDR(BNXT_RE_RELEASE_TOGGLE_MEM_HANDLE, + BNXT_RE_OBJECT_GET_TOGGLE_MEM, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_GET_TOGGLE_MEM, + UVERBS_TYPE_ALLOC_IDR(get_toggle_mem_obj_cleanup), + &UVERBS_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM), + &UVERBS_METHOD(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM)); + +const struct uapi_definition bnxt_re_uapi_defs[] = { + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_ALLOC_PAGE), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_NOTIFY_DRV), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_GET_TOGGLE_MEM), + {} +}; From aa0c43c13c0bf50c2eea1d590addc9ac7100a1e7 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:50 -0700 Subject: [PATCH 1180/3297] RDMA/bnxt_re: Fail DBR related page allocation UAPIs if the feature is disabled [ Upstream commit a57592c6392a8e333c9c2731701297a5a279313a ] No need to support the DBR related page allocations if the pacing feature is disabled. Fail the request if pacing is disabled. Fixes: ea2224857882 ("RDMA/bnxt_re: Update alloc_page uapi for pacing") Link: https://patch.msgid.link/r/20260615224751.232802-15-selvin.xavier@broadcom.com Reviewed-by: Sriharsha Basavapatna Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/bnxt_re/uapi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index 0145882e49f6e..d5e29d95a6385 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -112,12 +112,16 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * break; case BNXT_RE_ALLOC_DBR_BAR_PAGE: + if (!rdev->pacing.dbr_pacing) + return -EOPNOTSUPP; length = PAGE_SIZE; addr = (u64)rdev->pacing.dbr_bar_addr; mmap_flag = BNXT_RE_MMAP_DBR_BAR; break; case BNXT_RE_ALLOC_DBR_PAGE: + if (!rdev->pacing.dbr_pacing) + return -EOPNOTSUPP; length = PAGE_SIZE; addr = (u64)rdev->pacing.dbr_page; mmap_flag = BNXT_RE_MMAP_DBR_PAGE; From 35d3d6ff2bc1e7aaecb15d5377ebbd6227acae0d Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 5 Jun 2026 12:30:16 +0800 Subject: [PATCH 1181/3297] ksmbd: fix use-after-free in same_client_has_lease() [ Upstream commit 65b655f65c3ca1ab5d598d3832bb0ff531725858 ] same_client_has_lease() returns an opinfo pointer from ci->m_op_list after dropping ci->m_lock without taking a reference. smb_grant_oplock() then dereferences that pointer in copy_lease() and when checking breaking_cnt. A concurrent close can remove the old lease from ci->m_op_list and drop the last reference before the caller uses the returned pointer, leading to a use-after-free. Take a reference when same_client_has_lease() selects an existing lease, drop any previous match while scanning, and release the returned reference in smb_grant_oplock() after copying the lease state. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Signed-off-by: Guangshuo Li Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/oplock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 6454c7a4baa45..fc2ee00cfd810 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -531,7 +531,12 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); if (ret) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + if (m_opinfo) + opinfo_put(m_opinfo); m_opinfo = opinfo; + /* skip upgrading lease about breaking lease */ if (atomic_read(&opinfo->breaking_cnt)) continue; @@ -1249,6 +1254,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, if (atomic_read(&m_opinfo->breaking_cnt)) opinfo->o_lease->flags = SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + opinfo_put(m_opinfo); goto out; } } From 706fe1ce4f3a5185ded7eb669ea071713b50969d Mon Sep 17 00:00:00 2001 From: Matthew Bystrin Date: Wed, 29 Apr 2026 10:20:46 +0300 Subject: [PATCH 1182/3297] mfd: rsmu: Fix page register setup [ Upstream commit 6bc38f26ed07197b11a2b588edf1f43bfbc81d76 ] Fix writes to page register in 8A3400x family (Clock Matrix). All calls to rsmu_write_page_register() (both in i2c and spi) have resulted in early return, because all addresses in include/linux/mfd/idt8a340_reg.h are less than RSMU_CM_SCSR_BASE. There were 2 separate patch series which have to be merged in one time: mfd and ptp. The latter have been merged, the former[1] have not. Link: https://lore.kernel.org/netdev/LV3P220MB1202F8E2FCCFBA2519B4966EA0192@LV3P220MB1202.NAMP220.PROD.OUTLOOK.COM/ Fixes: 67d6c76fc815 ("mfd: rsmu: Support 32-bit address space") Signed-off-by: Matthew Bystrin Link: https://patch.msgid.link/20260429072047.1111427-2-dev.mbstr@gmail.com Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/rsmu_i2c.c | 6 +----- drivers/mfd/rsmu_spi.c | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/rsmu_i2c.c b/drivers/mfd/rsmu_i2c.c index cba64f107a2fd..9e5fc8259eec2 100644 --- a/drivers/mfd/rsmu_i2c.c +++ b/drivers/mfd/rsmu_i2c.c @@ -134,14 +134,10 @@ static int rsmu_i2c_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 by static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg, rsmu_rw_device rsmu_write_device) { - u32 page = reg & RSMU_CM_PAGE_MASK; + u32 page = (reg | RSMU_CM_SCSR_BASE) & RSMU_CM_PAGE_MASK; u8 buf[4]; int err; - /* Do not modify offset register for none-scsr registers */ - if (reg < RSMU_CM_SCSR_BASE) - return 0; - /* Simply return if we are on the same page */ if (rsmu->page == page) return 0; diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c index 39d9be1e141fb..c931d8cea0a1a 100644 --- a/drivers/mfd/rsmu_spi.c +++ b/drivers/mfd/rsmu_spi.c @@ -101,11 +101,8 @@ static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg) switch (rsmu->type) { case RSMU_CM: - /* Do not modify page register for none-scsr registers */ - if (reg < RSMU_CM_SCSR_BASE) - return 0; page_reg = RSMU_CM_PAGE_ADDR; - page = reg & RSMU_PAGE_MASK; + page = (reg | RSMU_CM_SCSR_BASE) & RSMU_PAGE_MASK; buf[0] = (u8)(page & 0xFF); buf[1] = (u8)((page >> 8) & 0xFF); buf[2] = (u8)((page >> 16) & 0xFF); From d35e4032f16d7621468c8b56816e7935ebf590a7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 8 May 2026 14:48:04 +0100 Subject: [PATCH 1183/3297] mfd: cs42l43: Sanity check firmware size [ Upstream commit b6ef1a74b3ec254f87a6a3c554fe8f8083ebd37c ] Currently the code checks if a firmware was received, however it does not verify that the firmware size is larger than the firmware header. As the firmware pointer is dereferenced as a pointer to the header structure this could lead to an out of bounds memory access. Add the missing check. Fixes: ace6d1448138 ("mfd: cs42l43: Add support for cs42l43 core driver") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260508134804.1787461-1-ckeepax@opensource.cirrus.com Signed-off-by: Lee Jones Signed-off-by: Sasha Levin --- drivers/mfd/cs42l43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 107cfb983fec4..13d446755fe20 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -676,7 +676,7 @@ static void cs42l43_mcu_load_firmware(const struct firmware *firmware, void *con unsigned int loadaddr, val; int ret; - if (!firmware) { + if (!firmware || firmware->size < sizeof(*hdr)) { dev_err(cs42l43->dev, "Failed to load firmware\n"); cs42l43->firmware_error = -ENODEV; goto err; From ae1f3460833d3e427420ab260278ec0e45d68c86 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 12 Jun 2026 11:50:20 +0000 Subject: [PATCH 1184/3297] ocfs2: fix circular locking dependency in ocfs2_dio_end_io_write [ Upstream commit ff6f26c58421614b02694ac9d219ac61d924bc68 ] A circular locking dependency involves INODE_ALLOC_SYSTEM_INODE, EXTENT_ALLOC_SYSTEM_INODE, and ORPHAN_DIR_SYSTEM_INODE. 1. ocfs2_mknod() acquires INODE_ALLOC then EXTENT_ALLOC. 2. ocfs2_dio_end_io_write() acquires EXTENT_ALLOC for unwritten extents, then ORPHAN_DIR via ocfs2_del_inode_from_orphan() while still holding EXTENT_ALLOC. 3. ocfs2_wipe_inode() acquires ORPHAN_DIR then INODE_ALLOC via ocfs2_remove_inode. Break the cycle in ocfs2_dio_end_io_write() by freeing the allocation contexts (releasing EXTENT_ALLOC) before acquiring ORPHAN_DIR. WARNING: possible circular locking dependency detected ------------------------------------------------------ is trying to acquire lock: ffff8881e78b33a0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 but task is already holding lock: ffff8881e78b4fa0 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0xe97/0x43b0 fs/ocfs2/inode.c:1299 the existing dependency chain (in reverse order) is: -> #2 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_del_inode_from_orphan+0x12e/0x7a0 fs/ocfs2/namei.c:2728 ocfs2_dio_end_io+0xf9c/0x1370 fs/ocfs2/aops.c:2418 dio_complete+0x25b/0x790 fs/direct-io.c:281 -> #1 (&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_reserve_suballoc_bits+0x16d/0x4840 fs/ocfs2/suballoc.c:882 ocfs2_reserve_new_metadata_blocks+0x415/0x9a0 fs/ocfs2/suballoc.c:1078 ocfs2_mknod+0x10f3/0x2260 fs/ocfs2/namei.c:351 -> #0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: __lock_acquire+0x15a5/0x2cf0 kernel/locking/lockdep.c:5237 lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868 down_write+0x96/0x200 kernel/locking/rwsem.c:1625 inode_lock include/linux/fs.h:1029 [inline] ocfs2_remove_inode fs/ocfs2/inode.c:733 [inline] ocfs2_wipe_inode fs/ocfs2/inode.c:896 [inline] ocfs2_delete_inode fs/ocfs2/inode.c:1157 [inline] ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 Chain exists of: &ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE] Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]); *** DEADLOCK *** Link: https://lore.kernel.org/97c902a6-3bcf-43ea-9b70-f1f136a6c3f2@mail.kernel.org Fixes: d647c5b2fbf8 ("ocfs2: split transactions in dio completion to avoid credit exhaustion") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+b225d4dfce6219600c42@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b225d4dfce6219600c42 Link: https://syzkaller.appspot.com/ai_job?id=0b53ce1e-2972-4192-aa85-8097a702762c Signed-off-by: Aleksandr Nogikh Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Joseph Qi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- fs/ocfs2/aops.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 4ff9f2b64fe95..53f99d49037b5 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -2373,6 +2373,15 @@ static int ocfs2_dio_end_io_write(struct inode *inode, unlock: up_write(&oi->ip_alloc_sem); + if (data_ac) { + ocfs2_free_alloc_context(data_ac); + data_ac = NULL; + } + if (meta_ac) { + ocfs2_free_alloc_context(meta_ac); + meta_ac = NULL; + } + /* everything looks good, let's start the cleanup */ if (!ret && dwc->dw_orphaned) { BUG_ON(dwc->dw_writer_pid != task_pid_nr(current)); @@ -2384,10 +2393,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode, ocfs2_inode_unlock(inode, 1); brelse(di_bh); out: - if (data_ac) - ocfs2_free_alloc_context(data_ac); - if (meta_ac) - ocfs2_free_alloc_context(meta_ac); ocfs2_run_deallocs(osb, &dealloc); ocfs2_dio_free_write_ctx(inode, dwc); From 9c1c120471a679592d0321bb35e8e571fe6e2b78 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 20 May 2026 10:26:50 +0800 Subject: [PATCH 1185/3297] 9p: avoid returning ERR_PTR(0) from mkdir operations [ Upstream commit 314b58c01a9047567fd19446ca5fd46c473b89ff ] When mkdir succeeds, v9fs_vfs_mkdir_dotl() and v9fs_vfs_mkdir() return ERR_PTR(0) which is incorrect. They should return NULL instead for success and ERR_PTR() only with negative error codes for failure. Return NULL instead of passing to ERR_PTR while err is zero Fixes smatch warnings: fs/9p/vfs_inode_dotl.c:420 v9fs_vfs_mkdir_dotl() warn: passing zero to 'ERR_PTR' fs/9p/vfs_inode.c:695 v9fs_vfs_mkdir() warn: passing zero to 'ERR_PTR' The v9fs_vfs_mkdir() code was further simplified because v9fs_create() can never return NULL, so we do not need to check for fid being set separately, and the error path can be a simple return immediately after v9fs_create() failure. There is no intended functional change. Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Suggested-by: David Laight Acked-by: Christian Schoenebeck Signed-off-by: Hongling Zeng Message-ID: <20260520022650.14217-1-zenghongling@kylinos.cn> Signed-off-by: Dominique Martinet Signed-off-by: Sasha Levin --- fs/9p/vfs_inode.c | 19 ++++++------------- fs/9p/vfs_inode_dotl.c | 4 ++-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 0f3189a0a516a..e2225bfa55035 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -672,27 +672,20 @@ v9fs_vfs_create(struct mnt_idmap *idmap, struct inode *dir, static struct dentry *v9fs_vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - int err; u32 perm; struct p9_fid *fid; struct v9fs_session_info *v9ses; p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); - err = 0; v9ses = v9fs_inode2v9ses(dir); perm = unixmode2p9mode(v9ses, mode | S_IFDIR); fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); - if (IS_ERR(fid)) { - err = PTR_ERR(fid); - fid = NULL; - } else { - inc_nlink(dir); - v9fs_invalidate_inode_attr(dir); - } - - if (fid) - p9_fid_put(fid); - return ERR_PTR(err); + if (IS_ERR(fid)) + return ERR_CAST(fid); + inc_nlink(dir); + v9fs_invalidate_inode_attr(dir); + p9_fid_put(fid); + return NULL; } /** diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 6312b3590f743..cbc72a17b01a8 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -349,7 +349,7 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t omode) { - int err; + int err = 0; struct v9fs_session_info *v9ses; struct p9_fid *fid = NULL, *dfid = NULL; kgid_t gid; @@ -412,7 +412,7 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, p9_fid_put(fid); v9fs_put_acl(dacl, pacl); p9_fid_put(dfid); - return ERR_PTR(err); + return err ? ERR_PTR(err) : NULL; } static int From 13bf9879b778b2f4b260b45bed18f31806120d1e Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Fri, 29 May 2026 15:39:31 +0800 Subject: [PATCH 1186/3297] net/9p: fix race condition on rdma->state in trans_rdma.c [ Upstream commit 7d54894a1ee265a72d70f7cae1da6cc774cccc71 ] The rdma->state field is modified without holding req_lock in both recv_done() and p9_cm_event_handler(), while rdma_request() accesses the same field under the req_lock spinlock. This inconsistent locking creates a race condition: - recv_done() running in softirq completion context sets rdma->state = P9_RDMA_FLUSHING without acquiring req_lock - p9_cm_event_handler() modifies rdma->state at multiple points (ADDR_RESOLVED, ROUTE_RESOLVED, ESTABLISHED, CLOSED) without req_lock - rdma_request() uses spin_lock_irqsave(&rdma->req_lock, flags) to protect the read-modify-write of rdma->state The race can cause lost state transitions: recv_done() or the CM event handler could set state to FLUSHING/CLOSED while rdma_request() is concurrently checking or modifying state under the lock, leading to the FLUSHING transition being silently overwritten by CLOSING. This corrupts the connection state machine and can cause use-after-free on RDMA request objects during teardown. Fix by adding req_lock protection to all rdma->state modifications in recv_done() and p9_cm_event_handler(), matching the pattern already used in rdma_request(). Use spin_lock_irqsave/spin_unlock_irqrestore in the CM event handler since it can race with recv_done() which runs in softirq context. Tested with a kernel module that races two threads (simulating rdma_request and recv_done/CM handler) on rdma->state with proper locking: 5.5M+ FLUSHING writes over 27M iterations with 0 lost transitions. Fixes: 473c7dd1d7b5 ("9p/rdma: remove useless check in cm_event_handler") Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: GLM:GLM-5.1 Signed-off-by: Yizhou Zhao Message-ID: <20260529073933.77315-1-zhaoyz24@mails.tsinghua.edu.cn> Signed-off-by: Dominique Martinet Signed-off-by: Sasha Levin --- net/9p/trans_rdma.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index b84748baf9cbe..d9f53f1da2c21 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -240,25 +240,36 @@ p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) { struct p9_client *c = id->context; struct p9_trans_rdma *rdma = c->trans; + unsigned long flags; + switch (event->event) { case RDMA_CM_EVENT_ADDR_RESOLVED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_INIT); rdma->state = P9_RDMA_ADDR_RESOLVED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_ROUTE_RESOLVED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED); rdma->state = P9_RDMA_ROUTE_RESOLVED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_ESTABLISHED: + spin_lock_irqsave(&rdma->req_lock, flags); BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED); rdma->state = P9_RDMA_CONNECTED; + spin_unlock_irqrestore(&rdma->req_lock, flags); break; case RDMA_CM_EVENT_DISCONNECTED: - if (rdma) + if (rdma) { + spin_lock_irqsave(&rdma->req_lock, flags); rdma->state = P9_RDMA_CLOSED; + spin_unlock_irqrestore(&rdma->req_lock, flags); + } c->status = Disconnected; break; @@ -296,6 +307,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc) struct p9_req_t *req; int err = 0; int16_t tag; + unsigned long flags; req = NULL; ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize, @@ -332,7 +344,10 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc) err_out: p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, wc->status); - rdma->state = P9_RDMA_FLUSHING; + spin_lock_irqsave(&rdma->req_lock, flags); + if (rdma->state < P9_RDMA_FLUSHING) + rdma->state = P9_RDMA_FLUSHING; + spin_unlock_irqrestore(&rdma->req_lock, flags); client->status = Disconnected; goto out; } From f04166c8677aac1c33c060e1b15e9ddcc5e9f3ce Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 23 Apr 2026 11:56:07 +0200 Subject: [PATCH 1187/3297] eventpoll: rename ep_remove_safe() back to ep_remove() [ Upstream commit 0bade234723e40e4937be912e105785d6a51464e ] The current name is just confusing and doesn't clarify anything. Link: https://patch.msgid.link/20260423-work-epoll-uaf-v1-4-2470f9eec0f5@kernel.org Signed-off-by: Christian Brauner (Amutable) Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index e9e6938f7184a..399e9487eabbe 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -910,7 +910,7 @@ static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi) /* * ep_remove variant for callers owing an additional reference to the ep */ -static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) +static void ep_remove(struct eventpoll *ep, struct epitem *epi) { struct file *file __free(fput) = NULL; @@ -961,7 +961,7 @@ static void ep_clear_and_put(struct eventpoll *ep) /* * Walks through the whole tree and try to free each "struct epitem". - * Note that ep_remove_safe() will not remove the epitem in case of a + * Note that ep_remove() will not remove the epitem in case of a * racing eventpoll_release_file(); the latter will do the removal. * At this point we are sure no poll callbacks will be lingering around. * Since we still own a reference to the eventpoll struct, the loop can't @@ -970,7 +970,7 @@ static void ep_clear_and_put(struct eventpoll *ep) for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = next) { next = rb_next(rbp); epi = rb_entry(rbp, struct epitem, rbn); - ep_remove_safe(ep, epi); + ep_remove(ep, epi); cond_resched(); } @@ -1635,21 +1635,21 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, mutex_unlock(&tep->mtx); /* - * ep_remove_safe() calls in the later error paths can't lead to + * ep_remove() calls in the later error paths can't lead to * ep_free() as the ep file itself still holds an ep reference. */ ep_get(ep); /* now check if we've created too many backpaths */ if (unlikely(full_check && reverse_path_check())) { - ep_remove_safe(ep, epi); + ep_remove(ep, epi); return -EINVAL; } if (epi->event.events & EPOLLWAKEUP) { error = ep_create_wakeup_source(epi); if (error) { - ep_remove_safe(ep, epi); + ep_remove(ep, epi); return error; } } @@ -1673,7 +1673,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, * high memory pressure. */ if (unlikely(!epq.epi)) { - ep_remove_safe(ep, epi); + ep_remove(ep, epi); return -ENOMEM; } @@ -2380,7 +2380,7 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds, * The eventpoll itself is still alive: the refcount * can't go to zero here. */ - ep_remove_safe(ep, epi); + ep_remove(ep, epi); error = 0; } else { error = -ENOENT; From baebd892f8a2c03f27c63ef0b4dcf19306ea2bcf Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:32 +0200 Subject: [PATCH 1188/3297] eventpoll: expand top-of-file overview / locking doc [ Upstream commit 7c25a0bd4bf7139944c5893ff61211f4b5a3455e ] The existing ~40-line "LOCKING:" banner covered the three-level lock hierarchy (epnested_mutex > ep->mtx > ep->lock) but nothing else. Lifetime rules, the ready-list state machine, the three removal paths, and the POLLFREE contract are implicit in the code. The recent UAF series (a6dc643c6931, 07712db80857, 8c2e52ebbe88, f2e467a48287) rode on invariants that were only implicit. Codify them at the top of the file: the subsystem overview, the lock hierarchy and its mutex_lock_nested() subclass convention (reworded from the old banner), a field-protection table for struct eventpoll and struct epitem that names the two faces of the rbn/rcu union (rbn under ep->mtx while linked into ep->rbr; rcu touched only by kfree_rcu(epi) on the free path), the ovflist sentinel encoding and scan-flip invariants, the three removal paths (A ep_remove, B ep_clear_and_put, C eventpoll_release_file) and the epi_fget() pin that orchestrates A vs C, and the POLLFREE store-release / load-acquire handshake. No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-1-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 199 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 162 insertions(+), 37 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 399e9487eabbe..bcad90e1f48a0 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -41,45 +41,170 @@ #include /* - * LOCKING: - * There are three level of locking required by epoll : + * fs/eventpoll.c - Efficient event polling ("epoll") kernel implementation. * - * 1) epnested_mutex (mutex) - * 2) ep->mtx (mutex) - * 3) ep->lock (spinlock) * - * The acquire order is the one listed above, from 1 to 3. - * We need a spinlock (ep->lock) because we manipulate objects - * from inside the poll callback, that might be triggered from - * a wake_up() that in turn might be called from IRQ context. - * So we can't sleep inside the poll callback and hence we need - * a spinlock. During the event transfer loop (from kernel to - * user space) we could end up sleeping due a copy_to_user(), so - * we need a lock that will allow us to sleep. This lock is a - * mutex (ep->mtx). It is acquired during the event transfer loop, - * during epoll_ctl(EPOLL_CTL_DEL) and during eventpoll_release_file(). - * The epnested_mutex is acquired when inserting an epoll fd onto another - * epoll fd. We do this so that we walk the epoll tree and ensure that this - * insertion does not create a cycle of epoll file descriptors, which - * could lead to deadlock. We need a global mutex to prevent two - * simultaneous inserts (A into B and B into A) from racing and - * constructing a cycle without either insert observing that it is - * going to. - * It is necessary to acquire multiple "ep->mtx"es at once in the - * case when one epoll fd is added to another. In this case, we - * always acquire the locks in the order of nesting (i.e. after - * epoll_ctl(e1, EPOLL_CTL_ADD, e2), e1->mtx will always be acquired - * before e2->mtx). Since we disallow cycles of epoll file - * descriptors, this ensures that the mutexes are well-ordered. In - * order to communicate this nesting to lockdep, when walking a tree - * of epoll file descriptors, we use the current recursion depth as - * the lockdep subkey. - * It is possible to drop the "ep->mtx" and to use the global - * mutex "epnested_mutex" (together with "ep->lock") to have it working, - * but having "ep->mtx" will make the interface more scalable. - * Events that require holding "epnested_mutex" are very rare, while for - * normal operations the epoll private "ep->mtx" will guarantee - * a better scalability. + * Overview + * -------- + * + * Each epoll_create(2) returns an anonymous [eventpoll] file whose + * ->private_data is a struct eventpoll. Each EPOLL_CTL_ADD installs + * a struct epitem linking one (watched file, fd) pair back to that + * eventpoll via the watched file's f_op->poll() wait queue(s). When + * the watched file signals readiness, ep_poll_callback() fires and + * marks the epitem ready. epoll_wait(2) drains the ready list under + * ep->mtx, re-queueing items in level-triggered mode. + * + * epoll instances can watch other epoll instances up to EP_MAX_NESTS + * deep; cycles are forbidden and detected at EPOLL_CTL_ADD time. + * + * + * Locking + * ------- + * + * Three levels, acquired from outer to inner: + * + * epnested_mutex (global; rare; taken only for EPOLL_CTL_ADD + * loop / path checks) + * > ep->mtx (per-eventpoll; sleepable; serializes most ops) + * > ep->lock (per-eventpoll; IRQ-safe spinlock) + * + * file->f_lock (per-file; NOT IRQ-safe; guards f_ep hlist ops; + * nested inside ep->mtx, outside ep->lock) + * + * Rationale: + * - ep->lock is a spinlock because ep_poll_callback() is called from + * wake_up() which may run in hard-IRQ context. All ep->lock + * critical sections use spin_lock_irqsave(). + * - ep->mtx is a sleepable mutex because the event delivery loop + * calls copy_to_user(), and ep_insert() may sleep in + * kmem_cache_alloc() and f_op->poll(). + * - epnested_mutex is global because cycle detection needs a global + * view of the epoll topology; a per-object scheme would let two + * concurrent inserts (A into B, B into A) construct a cycle + * without either observer seeing it. + * - Per-ep ep->mtx is preferred for scalability elsewhere. Events + * that require epnested_mutex are rare. + * + * When EPOLL_CTL_ADD nests one eventpoll inside another we acquire + * ep->mtx on both: outer first, target second. Since cycles are + * forbidden the set of live ep->mtx holds is always a strict chain, + * communicated to lockdep via mutex_lock_nested() subclasses derived + * from the current recursion depth. + * + * + * Field protection + * ---------------- + * + * struct eventpoll: + * mtx - self + * rbr - ep->mtx + * ovflist, rdllist - ep->lock (IRQ-safe) + * wq - ep->lock for queue mutation + * poll_wait - internal waitqueue spinlock + * refs - file->f_lock for adds; ep->mtx for removes; + * RCU for readers (hlist_del_rcu + kfree_rcu(ep)) + * ws - ep->mtx + * gen, loop_check_depth - epnested_mutex + * file, user - immutable after setup + * refcount - atomic (refcount_t) + * napi_* - READ_ONCE / WRITE_ONCE + * + * struct epitem: + * rbn / rcu union - rbn: ep->mtx (while epi is linked in ep->rbr). + * rcu: written only by kfree_rcu(epi) on the free + * path; otherwise untouched by epoll code. + * rdllink, next - ep->lock + * ffd, ep - immutable after ep_insert() + * pwqlist - ep->mtx for writes; POLLFREE clears pwq->whead + * via smp_store_release(), see below + * fllink - file->f_lock for mutation; hlist_del_rcu + + * kfree_rcu(epi) for safe RCU readers + * ws - RCU (rcu_assign_pointer / + * rcu_dereference_check(mtx)) + * event - ep->mtx for writes; lockless read in + * ep_poll_callback pairs with smp_mb() in + * ep_modify() + * + * + * Ready-list state machine + * ------------------------ + * + * Readiness is tracked in two lists under ep->lock: + * + * rdllist - doubly-linked FIFO; the "current" ready list. + * ovflist - singly-linked LIFO; used during a scan to catch + * events that arrive while rdllist is being iterated + * without ep->lock. + * + * Encoded in ep->ovflist: + * EP_UNACTIVE_PTR - no scan active; callback appends to rdllist. + * NULL - scan active, no spill yet. + * pointer to epi - scan active with spilled items (LIFO). + * + * Encoded in epi->next: + * EP_UNACTIVE_PTR - epi is not on ovflist. + * otherwise - next epi on ovflist (NULL at tail). + * + * ep_start_scan() flips "not scanning" to "scanning" and splices + * rdllist into a caller-local txlist. ep_done_scan() drains ovflist + * back to rdllist (list_add head-insert reverses LIFO to FIFO), + * flips back to "not scanning", and re-splices any items the caller + * left in txlist (e.g., level-triggered re-queues). + * + * + * Removal paths + * ------------- + * + * Three paths dispose of epitems and/or eventpolls: + * + * A. ep_remove() - EPOLL_CTL_DEL and ep_insert() + * rollback. Caller holds ep->mtx. + * B. ep_clear_and_put() - close of the epoll fd itself + * (ep_eventpoll_release). + * C. eventpoll_release_file() - close of a watched file, invoked + * from __fput(). + * + * Coordination: + * A and C exclude each other via the watched file's refcount. + * A pins the file with epi_fget() before touching file->f_ep or + * file->f_lock; if the pin fails, __fput() is in flight and C + * will clean this epi up. See the epi_fget() block comment. + * A and B both hold ep->mtx serially. B walks the rbtree with + * rb_next() captured before ep_remove() erases the current node. + * B and C both take ep->mtx; the loser sees fewer entries or an + * empty file->f_ep. + * + * Within every path the internal order is strict: + * ep_unregister_pollwait() - drain pwqlist; synchronizes with any + * in-flight ep_poll_callback via the + * watched wait-queue head's lock. + * ep_remove_file() - hlist_del_rcu of epi->fllink and, + * if last watcher, clear file->f_ep, + * under file->f_lock. + * ep_remove_epi() - rb_erase, rdllist unlink (ep->lock), + * wakeup_source_unregister, + * kfree_rcu(epi). + * + * kfree_rcu(epi) defers the free past RCU readers in + * reverse_path_check_proc(); kfree_rcu(ep) defers past readers in + * ep_get_upwards_depth_proc(). + * + * + * POLLFREE handshake + * ------------------ + * + * When a subsystem tears down a wait-queue head that an epitem is + * registered on (binder, signalfd, ...), it wakes the callback with + * POLLFREE and must RCU-defer the head's free. The store/load pair: + * + * ep_poll_callback() POLLFREE branch: + * smp_store_release(&pwq->whead, NULL) + * + * ep_remove_wait_queue(): + * smp_load_acquire(&pwq->whead) + * + * See those sites for the full argument. */ /* Epoll private bits inside the event mask */ From 25e85dc040a6c93e90000fdb558101bc88982d6c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:39 +0200 Subject: [PATCH 1189/3297] eventpoll: rename attach_epitem() to ep_attach_file() [ Upstream commit 6a3f1a494bc91d7976cf0d2b200bb3f1a22eef64 ] ep_remove_file() tears down the f_ep linkage that attach_epitem() establishes, so the pair should look like one. Rename to ep_attach_file() for the "ep_*" + subject symmetry and to match the naming used elsewhere in the file (ep_insert, ep_modify, ep_remove, ep_remove_file, ep_remove_epi, ep_unregister_pollwait). Pure rename; no functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-8-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index bcad90e1f48a0..c326577669ae4 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1669,7 +1669,7 @@ static noinline void ep_destroy_wakeup_source(struct epitem *epi) wakeup_source_unregister(ws); } -static int attach_epitem(struct file *file, struct epitem *epi) +static int ep_attach_file(struct file *file, struct epitem *epi) { struct epitems_head *to_free = NULL; struct hlist_head *head = NULL; @@ -1740,7 +1740,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, if (tep) mutex_lock_nested(&tep->mtx, 1); /* Add the current item to the list of active epoll hook for this file */ - if (unlikely(attach_epitem(tfile, epi) < 0)) { + if (unlikely(ep_attach_file(tfile, epi) < 0)) { if (tep) mutex_unlock(&tep->mtx); kmem_cache_free(epi_cache, epi); From 4fd51f413d7b566c3578d391d1d2a037dda0b1cb Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:41 +0200 Subject: [PATCH 1190/3297] eventpoll: split ep_insert() into alloc + register stages [ Upstream commit e0e35f4cb983a55a36e79e9b2a20ca0e0688fae6 ] ep_insert() was 130 lines and mixed four concerns in one body: user quota charge and epitem allocation, attach-into-file-hlist plus rbtree insert plus target-ep locking, reverse-path + EPOLLWAKEUP + poll-queue install with rollback, and ready-list publication. Factor the first two concerns into named helpers so the body reduces to orchestration. ep_alloc_epitem() charges the user's epoll_watches quota, allocates a fresh epitem, and initializes its fields. On failure it returns ERR_PTR(-ENOSPC) or ERR_PTR(-ENOMEM); on success the epi is not yet linked into anything. ep_register_epitem() installs @epi into @tfile's f_ep hlist and @ep's rbtree, optionally chains @tfile onto tfile_check_list for the path check, takes the tep->mtx nested lock for the epoll-watches- epoll case, and finally takes the ep_get() reference that pairs with ep_remove()'s ep_put() in ep_insert()'s error paths. On failure it frees the epi and decrements epoll_watches to match ep_alloc_epitem(). ep_insert()'s remaining body is the rollback-via-ep_remove() chain (reverse_path_check, EPOLLWAKEUP source creation, ep_ptable_queue_proc allocation) and the ready-list / wake publication. Remove a few stale comments that duplicated function-level documentation or described obvious code. No functional change; rollback boundaries unchanged -- every error path after ep_register_epitem() still calls ep_remove(), preserving the ep->refcount invariant that keeps ep_remove()'s WARN_ON_ONCE safe. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-10-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 111 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index c326577669ae4..d63d40d4e2606 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1704,68 +1704,112 @@ static int ep_attach_file(struct file *file, struct epitem *epi) } /* - * Must be called with "mtx" held. + * Charge the user's epoll_watches quota, allocate a fresh epitem for + * @tfile/@fd, and initialize its fields. The returned item is not yet + * linked into any data structure; the caller must install it via + * ep_register_epitem() (which takes over on success) or kmem_cache_free() + * it and decrement epoll_watches on its own. + * + * Returns ERR_PTR(-ENOSPC) if the quota is exceeded, ERR_PTR(-ENOMEM) + * if the slab allocation fails. */ -static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, - struct file *tfile, int fd, int full_check) +static struct epitem *ep_alloc_epitem(struct eventpoll *ep, + const struct epoll_event *event, + struct file *tfile, int fd) { - int error, pwake = 0; - __poll_t revents; struct epitem *epi; - struct ep_pqueue epq; - struct eventpoll *tep = NULL; - - if (is_file_epoll(tfile)) - tep = tfile->private_data; - - lockdep_assert_irqs_enabled(); if (unlikely(percpu_counter_compare(&ep->user->epoll_watches, max_user_watches) >= 0)) - return -ENOSPC; + return ERR_PTR(-ENOSPC); percpu_counter_inc(&ep->user->epoll_watches); - if (!(epi = kmem_cache_zalloc(epi_cache, GFP_KERNEL))) { + epi = kmem_cache_zalloc(epi_cache, GFP_KERNEL); + if (unlikely(!epi)) { percpu_counter_dec(&ep->user->epoll_watches); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } - /* Item initialization follow here ... */ INIT_LIST_HEAD(&epi->rdllink); epi->ep = ep; ep_set_ffd(&epi->ffd, tfile, fd); epi->event = *event; epi->next = EP_UNACTIVE_PTR; + return epi; +} + +/* + * Install @epi into its target file's f_ep hlist and into @ep's rbtree, + * taking one additional reference on @ep for the lifetime of the item. + * + * If @tep is non-NULL, the target file is itself an eventpoll; we hold + * tep->mtx at subclass 1 across the attach + rbtree insert to serialize + * with the target side. RB tree ops are protected by @ep->mtx, which + * the caller already holds. + * + * On failure the epi is freed and the epoll_watches counter decremented, + * matching ep_alloc_epitem()'s allocation. After this returns + * successfully, ep_insert()'s later error paths use ep_remove() for + * unwind; that cannot drop @ep's refcount to zero because the ep file + * itself still holds the original reference. + */ +static int ep_register_epitem(struct eventpoll *ep, struct epitem *epi, + struct eventpoll *tep, int full_check) +{ + struct file *tfile = epi->ffd.file; + int error; + if (tep) mutex_lock_nested(&tep->mtx, 1); - /* Add the current item to the list of active epoll hook for this file */ - if (unlikely(ep_attach_file(tfile, epi) < 0)) { + + error = ep_attach_file(tfile, epi); + if (unlikely(error)) { if (tep) mutex_unlock(&tep->mtx); kmem_cache_free(epi_cache, epi); percpu_counter_dec(&ep->user->epoll_watches); - return -ENOMEM; + return error; } if (full_check && !tep) list_file(tfile); - /* - * Add the current item to the RB tree. All RB tree operations are - * protected by "mtx", and ep_insert() is called with "mtx" held. - */ ep_rbtree_insert(ep, epi); + if (tep) mutex_unlock(&tep->mtx); - /* - * ep_remove() calls in the later error paths can't lead to - * ep_free() as the ep file itself still holds an ep reference. - */ ep_get(ep); + return 0; +} + +/* + * Must be called with "mtx" held. + */ +static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, + struct file *tfile, int fd, int full_check) +{ + int error, pwake = 0; + __poll_t revents; + struct epitem *epi; + struct ep_pqueue epq; + struct eventpoll *tep = NULL; + + if (is_file_epoll(tfile)) + tep = tfile->private_data; + + lockdep_assert_irqs_enabled(); + + epi = ep_alloc_epitem(ep, event, tfile, fd); + if (IS_ERR(epi)) + return PTR_ERR(epi); - /* now check if we've created too many backpaths */ + error = ep_register_epitem(ep, epi, tep, full_check); + if (error) + return error; + + /* Reject the insert if the new link would create too many back-paths. */ if (unlikely(full_check && reverse_path_check())) { ep_remove(ep, epi); return -EINVAL; @@ -1792,28 +1836,21 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, */ revents = ep_item_poll(epi, &epq.pt, 1); - /* - * We have to check if something went wrong during the poll wait queue - * install process. Namely an allocation for a wait queue failed due - * high memory pressure. - */ + /* ep_ptable_queue_proc() signals allocation failure by clearing epq.epi. */ if (unlikely(!epq.epi)) { ep_remove(ep, epi); return -ENOMEM; } - /* We have to drop the new item inside our item list to keep track of it */ + /* Drop the new item onto the ready list if it is already ready. */ spin_lock_irq(&ep->lock); - /* record NAPI ID of new item if present */ ep_set_busy_poll_napi_id(epi); - /* If the file is already "ready" we drop it inside the ready list */ if (revents && !ep_is_linked(epi)) { list_add_tail(&epi->rdllink, &ep->rdllist); ep_pm_stay_awake(epi); - /* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq)) wake_up(&ep->wq); if (waitqueue_active(&ep->poll_wait)) From d8f88803152f035b9f1f7b9ebeadf0a55b34302a Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:43 +0200 Subject: [PATCH 1191/3297] eventpoll: extract ep_deliver_event() from ep_send_events() [ Upstream commit 499a5e7f4a57fa08a297c627d007a55069acac9a ] ep_send_events()'s body covered two concerns: per-item work (PM wakeup-source bookkeeping, re-poll, copy_to_user, level-trigger re-queue, EPOLLONESHOT mask clear) and the scan-level accumulator (maxevents cap, EFAULT preservation, txlist/rdllist splice). Extract the per-item work as ep_deliver_event(), which returns a tri-state int: 1 one event was delivered; caller advances the counter, 0 re-poll produced no caller-requested events (item drops out of the ready list; a future callback will re-queue), -EFAULT copy_to_user() faulted; item is already re-inserted at the head of the txlist so ep_done_scan() splices it back to rdllist. The per-item comments (PM ordering, the "sole writer to rdllist" invariant for the LT re-queue, the EFAULT semantics) move into ep_deliver_event(). ep_send_events() reduces to the fatal-signal short-circuit, scan bracket, and a short txlist walk that accumulates the deliveries and preserves the "first error wins" EFAULT contract (res = delivered only if no event was previously delivered; otherwise the success count is returned and -EFAULT is reported on the next call). No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-12-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 138 ++++++++++++++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index d63d40d4e2606..ef580dbdecf8e 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1942,6 +1942,82 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, return 0; } +/* + * Attempt to deliver one event for @epi into @*uevents. + * + * Returns 1 if an event was delivered (with *uevents advanced to the + * next slot), 0 if the re-poll reported no caller-requested events + * (@epi drops out of the ready list; a future callback will re-add + * it), or -EFAULT if copy_to_user() faulted (in which case @epi is + * re-inserted at the head of @txlist so ep_done_scan() merges it + * back to rdllist for the next attempt). + * + * PM bookkeeping and level-triggered re-queue are handled here. + * Caller holds ep->mtx and the scan is active. + */ +static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi, + poll_table *pt, + struct epoll_event __user **uevents, + struct list_head *txlist) +{ + struct epoll_event __user *next; + struct wakeup_source *ws; + __poll_t revents; + + /* + * Activate ep->ws before deactivating epi->ws to prevent + * triggering auto-suspend here (in case we reactivate epi->ws + * below). Rearranging to delay the deactivation would let + * epi->ws drift out of sync with ep_is_linked(). + */ + ws = ep_wakeup_source(epi); + if (ws) { + if (ws->active) + __pm_stay_awake(ep->ws); + __pm_relax(ws); + } + + list_del_init(&epi->rdllink); + + /* + * Re-poll under ep->mtx so userspace cannot change the item + * out from under us. If no caller-requested events remain, + * @epi stays off the ready list; the poll callback will + * re-queue it when events next appear. + */ + revents = ep_item_poll(epi, pt, 1); + if (!revents) + return 0; + + next = epoll_put_uevent(revents, epi->event.data, *uevents); + if (!next) { + /* + * copy_to_user() faulted: put the item back so + * ep_done_scan() splices it onto rdllist for the next + * attempt. + */ + list_add(&epi->rdllink, txlist); + ep_pm_stay_awake(epi); + return -EFAULT; + } + *uevents = next; + + if (epi->event.events & EPOLLONESHOT) { + epi->event.events &= EP_PRIVATE_BITS; + } else if (!(epi->event.events & EPOLLET)) { + /* + * Level-triggered: re-queue so the next epoll_wait() + * rechecks availability. We are the sole writer to + * rdllist here -- epoll_ctl() callers are locked out + * by ep->mtx, and the poll callback queues to ovflist + * during scans. + */ + list_add_tail(&epi->rdllink, &ep->rdllist); + ep_pm_stay_awake(epi); + } + return 1; +} + static int ep_send_events(struct eventpoll *ep, struct epoll_event __user *events, int maxevents) { @@ -1964,70 +2040,24 @@ static int ep_send_events(struct eventpoll *ep, ep_start_scan(ep, &txlist); /* - * We can loop without lock because we are passed a task private list. - * Items cannot vanish during the loop we are holding ep->mtx. + * We can loop without lock because we are passed a task-private + * txlist; items cannot vanish while we hold ep->mtx. */ list_for_each_entry_safe(epi, tmp, &txlist, rdllink) { - struct wakeup_source *ws; - __poll_t revents; + int delivered; if (res >= maxevents) break; - /* - * Activate ep->ws before deactivating epi->ws to prevent - * triggering auto-suspend here (in case we reactive epi->ws - * below). - * - * This could be rearranged to delay the deactivation of epi->ws - * instead, but then epi->ws would temporarily be out of sync - * with ep_is_linked(). - */ - ws = ep_wakeup_source(epi); - if (ws) { - if (ws->active) - __pm_stay_awake(ep->ws); - __pm_relax(ws); - } - - list_del_init(&epi->rdllink); - - /* - * If the event mask intersect the caller-requested one, - * deliver the event to userspace. Again, we are holding ep->mtx, - * so no operations coming from userspace can change the item. - */ - revents = ep_item_poll(epi, &pt, 1); - if (!revents) - continue; - - events = epoll_put_uevent(revents, epi->event.data, events); - if (!events) { - list_add(&epi->rdllink, &txlist); - ep_pm_stay_awake(epi); + delivered = ep_deliver_event(ep, epi, &pt, &events, &txlist); + if (delivered < 0) { if (!res) - res = -EFAULT; + res = delivered; break; } - res++; - if (epi->event.events & EPOLLONESHOT) - epi->event.events &= EP_PRIVATE_BITS; - else if (!(epi->event.events & EPOLLET)) { - /* - * If this file has been added with Level - * Trigger mode, we need to insert back inside - * the ready list, so that the next call to - * epoll_wait() will check again the events - * availability. At this point, no one can insert - * into ep->rdllist besides us. The epoll_ctl() - * callers are locked out by - * ep_send_events() holding "mtx" and the - * poll callback will queue them in ep->ovflist. - */ - list_add_tail(&epi->rdllink, &ep->rdllist); - ep_pm_stay_awake(epi); - } + res += delivered; } + ep_done_scan(ep, &txlist); mutex_unlock(&ep->mtx); From 430dac191905be20a60f6e7a2eae0479e4d358f8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:45 +0200 Subject: [PATCH 1192/3297] eventpoll: wrap EP_UNACTIVE_PTR in typed sentinel helpers [ Upstream commit 0dcb726466a556f273beaadfb76f12d1b0087dd7 ] ep->ovflist and epi->next both use EP_UNACTIVE_PTR (a cast to (void *)-1) as a sentinel, with distinct meanings at each site: ep->ovflist == EP_UNACTIVE_PTR no scan in progress epi->next == EP_UNACTIVE_PTR epi not on ovflist Call sites had to know the sentinel's value and, by convention, what it meant in each context. Hide both behind inline helpers: ep_is_scanning(ep) predicate for "scan in progress" ep_enter_scan(ep) WRITE_ONCE flip to NULL (scan start) ep_exit_scan(ep) WRITE_ONCE flip to sentinel (scan end) epi_on_ovflist(epi) predicate for "epi is on ovflist" epi_clear_ovflist(epi) clear epi's ovflist link slot Convert ep_events_available(), ep_start_scan(), ep_done_scan(), ep_poll_callback(), and ep_alloc_epitem() to use the wrappers. The ovflist state-machine transitions are now named, not encoded in sentinel comparisons, and the top-of-file "Ready-list state machine" section is the single place that spells out the sentinel's meaning. ep_alloc() keeps the raw "ep->ovflist = EP_UNACTIVE_PTR" init (no concurrent access at that point) with an inline "not scanning" comment, and the tfile_check_list sentinel is left alone -- it will disappear entirely when the loop-check globals move into a stack-allocated ep_ctl_ctx in a later commit. Also rework ep_done_scan()'s for-loop: the combined initializer + update clause that advanced nepi AND cleared epi->next in one step was clever but hard to read; splitting the update into two statements inside the body makes the epi_clear_ovflist() call visible. No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-14-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 73 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index ef580dbdecf8e..f2a09086f67f2 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -507,6 +507,43 @@ static inline struct epitem *ep_item_from_wait(wait_queue_entry_t *p) return container_of(p, struct eppoll_entry, wait)->base; } +/* + * Ready-list / ovflist state (see "Ready-list state machine" in the + * top-of-file banner for the full state machine). EP_UNACTIVE_PTR is + * the sentinel; these wrappers name each transition and each test so + * call sites do not need to know the sentinel's value. + */ + +/* True iff @ep is between ep_enter_scan() and ep_exit_scan(). */ +static inline bool ep_is_scanning(struct eventpoll *ep) +{ + return READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR; +} + +/* Called by ep_start_scan(): divert ep_poll_callback() to ovflist. */ +static inline void ep_enter_scan(struct eventpoll *ep) +{ + WRITE_ONCE(ep->ovflist, NULL); +} + +/* Called by ep_done_scan(): redirect ep_poll_callback() back to rdllist. */ +static inline void ep_exit_scan(struct eventpoll *ep) +{ + WRITE_ONCE(ep->ovflist, EP_UNACTIVE_PTR); +} + +/* True iff @epi is currently linked on its ep's ovflist. */ +static inline bool epi_on_ovflist(const struct epitem *epi) +{ + return epi->next != EP_UNACTIVE_PTR; +} + +/* Mark @epi as not on any ovflist (init and post-drain). */ +static inline void epi_clear_ovflist(struct epitem *epi) +{ + epi->next = EP_UNACTIVE_PTR; +} + /** * ep_events_available - Checks if ready events might be available. * @@ -517,8 +554,7 @@ static inline struct epitem *ep_item_from_wait(wait_queue_entry_t *p) */ static inline int ep_events_available(struct eventpoll *ep) { - return !list_empty_careful(&ep->rdllist) || - READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR; + return !list_empty_careful(&ep->rdllist) || ep_is_scanning(ep); } #ifdef CONFIG_NET_RX_BUSY_POLL @@ -871,7 +907,7 @@ static void ep_start_scan(struct eventpoll *ep, struct list_head *txlist) lockdep_assert_irqs_enabled(); spin_lock_irq(&ep->lock); list_splice_init(&ep->rdllist, txlist); - WRITE_ONCE(ep->ovflist, NULL); + ep_enter_scan(ep); spin_unlock_irq(&ep->lock); } @@ -886,29 +922,24 @@ static void ep_done_scan(struct eventpoll *ep, * other events might have been queued by the poll callback. * We re-insert them inside the main ready-list here. */ - for (nepi = READ_ONCE(ep->ovflist); (epi = nepi) != NULL; - nepi = epi->next, epi->next = EP_UNACTIVE_PTR) { + for (nepi = READ_ONCE(ep->ovflist); (epi = nepi) != NULL; ) { + nepi = epi->next; + epi_clear_ovflist(epi); /* - * We need to check if the item is already in the list. - * During the "sproc" callback execution time, items are - * queued into ->ovflist but the "txlist" might already - * contain them, and the list_splice() below takes care of them. + * Skip items that the caller already returned via @txlist + * -- the list_splice() below takes care of those. */ if (!ep_is_linked(epi)) { /* - * ->ovflist is LIFO, so we have to reverse it in order - * to keep in FIFO. + * ovflist is LIFO; list_add() head-insert here + * reverses the iteration order into FIFO. */ list_add(&epi->rdllink, &ep->rdllist); ep_pm_stay_awake(epi); } } - /* - * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after - * releasing the lock, events will be queued in the normal way inside - * ep->rdllist. - */ - WRITE_ONCE(ep->ovflist, EP_UNACTIVE_PTR); + /* Back out of scan mode; callbacks target ep->rdllist again. */ + ep_exit_scan(ep); /* * Quickly re-inject items left on "txlist". @@ -1302,7 +1333,7 @@ static int ep_alloc(struct eventpoll **pep) init_waitqueue_head(&ep->poll_wait); INIT_LIST_HEAD(&ep->rdllist); ep->rbr = RB_ROOT_CACHED; - ep->ovflist = EP_UNACTIVE_PTR; + ep->ovflist = EP_UNACTIVE_PTR; /* not scanning */ ep->user = get_current_user(); refcount_set(&ep->refcount, 1); @@ -1426,8 +1457,8 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v * semantics). All the events that happen during that period of time are * chained in ep->ovflist and requeued later on. */ - if (READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR) { - if (epi->next == EP_UNACTIVE_PTR) { + if (ep_is_scanning(ep)) { + if (!epi_on_ovflist(epi)) { epi->next = READ_ONCE(ep->ovflist); WRITE_ONCE(ep->ovflist, epi); ep_pm_stay_awake_rcu(epi); @@ -1734,7 +1765,7 @@ static struct epitem *ep_alloc_epitem(struct eventpoll *ep, epi->ep = ep; ep_set_ffd(&epi->ffd, tfile, fd); epi->event = *event; - epi->next = EP_UNACTIVE_PTR; + epi_clear_ovflist(epi); return epi; } From f938bc8fde518758928f2ca97a7aab413c9615f7 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 24 Apr 2026 15:46:46 +0200 Subject: [PATCH 1193/3297] eventpoll: rename epi->next and txlist for clarity [ Upstream commit f38567bb63ae029e3b63fcb99b6a2dcc6f421e69 ] Two list-related names were confusing in isolation: struct epitem::next A singly-linked link slot used only when an epi is queued on ep->ovflist during an ep_start_scan/ep_done_scan window. The bare name "next" suggests a generic list link and doesn't say which list it belongs to. txlist The caller-local list_head used by ep_send_events() and __ep_eventpoll_poll() to hold the batch of items stolen from ep->rdllist for the current scan. "txlist" ("transmission list") is abbreviated and overloaded: it doesn't distinguish itself from ep->rdllist or ep->ovflist at a glance. Rename for what each actually is: struct epitem::next -> struct epitem::ovflist_next local txlist -> scan_batch With these in place: - epi->ovflist_next reads as "this is the ep->ovflist link slot", matching the rdllink pattern above it. - scan_batch reads as "the batch currently being scanned", clearly distinct from rdllist (canonical ready list) and ovflist (scan-window overflow). ep->rdllist and ep->ovflist struct field names are preserved -- they are long-standing interface-facing identifiers, and the new inline helpers (ep_is_scanning, epi_on_ovflist, ...) already hide the sentinel semantics at call sites. No functional change. Signed-off-by: Christian Brauner (Amutable) Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-15-249ed00a20f3@kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 0c4aefe3c2d0 ("eventpoll: Fix epoll_wait() report false negative") Signed-off-by: Sasha Levin --- fs/eventpoll.c | 62 ++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index f2a09086f67f2..43a89ae84f881 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -142,15 +142,15 @@ * NULL - scan active, no spill yet. * pointer to epi - scan active with spilled items (LIFO). * - * Encoded in epi->next: + * Encoded in epi->ovflist_next: * EP_UNACTIVE_PTR - epi is not on ovflist. * otherwise - next epi on ovflist (NULL at tail). * * ep_start_scan() flips "not scanning" to "scanning" and splices - * rdllist into a caller-local txlist. ep_done_scan() drains ovflist + * rdllist into a caller-local scan_batch. ep_done_scan() drains ovflist * back to rdllist (list_add head-insert reverses LIFO to FIFO), * flips back to "not scanning", and re-splices any items the caller - * left in txlist (e.g., level-triggered re-queues). + * left in scan_batch (e.g., level-triggered re-queues). * * * Removal paths @@ -261,14 +261,16 @@ struct epitem { struct rcu_head rcu; }; - /* List header used to link this structure to the eventpoll ready list */ + /* Link on the owning eventpoll's ready list (ep->rdllist). */ struct list_head rdllink; /* - * Works together "struct eventpoll"->ovflist in keeping the - * single linked chain of items. + * Link on the owning eventpoll's scan-overflow list (ep->ovflist), + * EP_UNACTIVE_PTR when not linked. See epi_on_ovflist() / + * epi_clear_ovflist() and the "Ready-list state machine" section + * in the top-of-file banner. */ - struct epitem *next; + struct epitem *ovflist_next; /* The file descriptor information this item refers to */ struct epoll_filefd ffd; @@ -535,13 +537,13 @@ static inline void ep_exit_scan(struct eventpoll *ep) /* True iff @epi is currently linked on its ep's ovflist. */ static inline bool epi_on_ovflist(const struct epitem *epi) { - return epi->next != EP_UNACTIVE_PTR; + return epi->ovflist_next != EP_UNACTIVE_PTR; } /* Mark @epi as not on any ovflist (init and post-drain). */ static inline void epi_clear_ovflist(struct epitem *epi) { - epi->next = EP_UNACTIVE_PTR; + epi->ovflist_next = EP_UNACTIVE_PTR; } /** @@ -894,7 +896,7 @@ static inline void ep_pm_stay_awake_rcu(struct epitem *epi) * ep->mutex needs to be held because we could be hit by * eventpoll_release_file() and epoll_ctl(). */ -static void ep_start_scan(struct eventpoll *ep, struct list_head *txlist) +static void ep_start_scan(struct eventpoll *ep, struct list_head *scan_batch) { /* * Steal the ready list, and re-init the original one to the @@ -906,13 +908,13 @@ static void ep_start_scan(struct eventpoll *ep, struct list_head *txlist) */ lockdep_assert_irqs_enabled(); spin_lock_irq(&ep->lock); - list_splice_init(&ep->rdllist, txlist); + list_splice_init(&ep->rdllist, scan_batch); ep_enter_scan(ep); spin_unlock_irq(&ep->lock); } static void ep_done_scan(struct eventpoll *ep, - struct list_head *txlist) + struct list_head *scan_batch) { struct epitem *epi, *nepi; @@ -923,10 +925,10 @@ static void ep_done_scan(struct eventpoll *ep, * We re-insert them inside the main ready-list here. */ for (nepi = READ_ONCE(ep->ovflist); (epi = nepi) != NULL; ) { - nepi = epi->next; + nepi = epi->ovflist_next; epi_clear_ovflist(epi); /* - * Skip items that the caller already returned via @txlist + * Skip items that the caller already returned via @scan_batch * -- the list_splice() below takes care of those. */ if (!ep_is_linked(epi)) { @@ -942,9 +944,9 @@ static void ep_done_scan(struct eventpoll *ep, ep_exit_scan(ep); /* - * Quickly re-inject items left on "txlist". + * Quickly re-inject items left on "scan_batch". */ - list_splice(txlist, &ep->rdllist); + list_splice(scan_batch, &ep->rdllist); __pm_relax(ep->ws); if (!list_empty(&ep->rdllist)) { @@ -1171,7 +1173,7 @@ static __poll_t ep_item_poll(const struct epitem *epi, poll_table *pt, int depth static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int depth) { struct eventpoll *ep = file->private_data; - LIST_HEAD(txlist); + LIST_HEAD(scan_batch); struct epitem *epi, *tmp; poll_table pt; __poll_t res = 0; @@ -1186,8 +1188,8 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep * the ready list. */ mutex_lock_nested(&ep->mtx, depth); - ep_start_scan(ep, &txlist); - list_for_each_entry_safe(epi, tmp, &txlist, rdllink) { + ep_start_scan(ep, &scan_batch); + list_for_each_entry_safe(epi, tmp, &scan_batch, rdllink) { if (ep_item_poll(epi, &pt, depth + 1)) { res = EPOLLIN | EPOLLRDNORM; break; @@ -1201,7 +1203,7 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep list_del_init(&epi->rdllink); } } - ep_done_scan(ep, &txlist); + ep_done_scan(ep, &scan_batch); mutex_unlock(&ep->mtx); return res; } @@ -1459,7 +1461,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v */ if (ep_is_scanning(ep)) { if (!epi_on_ovflist(epi)) { - epi->next = READ_ONCE(ep->ovflist); + epi->ovflist_next = READ_ONCE(ep->ovflist); WRITE_ONCE(ep->ovflist, epi); ep_pm_stay_awake_rcu(epi); } @@ -1980,7 +1982,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, * next slot), 0 if the re-poll reported no caller-requested events * (@epi drops out of the ready list; a future callback will re-add * it), or -EFAULT if copy_to_user() faulted (in which case @epi is - * re-inserted at the head of @txlist so ep_done_scan() merges it + * re-inserted at the head of @scan_batch so ep_done_scan() merges it * back to rdllist for the next attempt). * * PM bookkeeping and level-triggered re-queue are handled here. @@ -1989,7 +1991,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi, poll_table *pt, struct epoll_event __user **uevents, - struct list_head *txlist) + struct list_head *scan_batch) { struct epoll_event __user *next; struct wakeup_source *ws; @@ -2027,7 +2029,7 @@ static int ep_deliver_event(struct eventpoll *ep, struct epitem *epi, * ep_done_scan() splices it onto rdllist for the next * attempt. */ - list_add(&epi->rdllink, txlist); + list_add(&epi->rdllink, scan_batch); ep_pm_stay_awake(epi); return -EFAULT; } @@ -2053,7 +2055,7 @@ static int ep_send_events(struct eventpoll *ep, struct epoll_event __user *events, int maxevents) { struct epitem *epi, *tmp; - LIST_HEAD(txlist); + LIST_HEAD(scan_batch); poll_table pt; int res = 0; @@ -2068,19 +2070,19 @@ static int ep_send_events(struct eventpoll *ep, init_poll_funcptr(&pt, NULL); mutex_lock(&ep->mtx); - ep_start_scan(ep, &txlist); + ep_start_scan(ep, &scan_batch); /* * We can loop without lock because we are passed a task-private - * txlist; items cannot vanish while we hold ep->mtx. + * scan_batch; items cannot vanish while we hold ep->mtx. */ - list_for_each_entry_safe(epi, tmp, &txlist, rdllink) { + list_for_each_entry_safe(epi, tmp, &scan_batch, rdllink) { int delivered; if (res >= maxevents) break; - delivered = ep_deliver_event(ep, epi, &pt, &events, &txlist); + delivered = ep_deliver_event(ep, epi, &pt, &events, &scan_batch); if (delivered < 0) { if (!res) res = delivered; @@ -2089,7 +2091,7 @@ static int ep_send_events(struct eventpoll *ep, res += delivered; } - ep_done_scan(ep, &txlist); + ep_done_scan(ep, &scan_batch); mutex_unlock(&ep->mtx); return res; From 02e2dadd62eae41a14698bfbaafe2a9ab35eee9b Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Tue, 2 Jun 2026 19:51:46 +0200 Subject: [PATCH 1194/3297] eventpoll: Fix epoll_wait() report false negative [ Upstream commit 0c4aefe3c2d0f272a2ad73699a12d4446ffdbe7b ] ep_events_available() checks for available events by looking at ep->rdllist and ep_is_scanning(). However, this is done without a lock and can report false negative if ep_start_scan() or ep_done_scan() are executed by another task concurrently. For example: _________________________________________________________________________ |ep_start_scan() | list_splice_init(&ep->rdllist, ...) ep_events_available() | !list_empty_careful(&ep->rdllist)| || ep_is_scanning(ep) | | ep_enter_scan(ep) ___________________________________|_____________________________________ Another example: _________________________________________________________________________ ep_events_available() | |ep_start_scan() | list_splice_init(&ep->rdllist, ...) | ep_enter_scan(ep) !list_empty_careful(&ep->rdllist)| |ep_done_scan() | ep_exit_scan(ep) | list_splice(..., &ep->rdllist) || ep_is_scanning(ep) | ___________________________________|_____________________________________ In the above examples, ep_events_available() sees no event despite events being available. In case epoll_wait() is called with timeout=0, epoll_wait() will wrongly return "no event" to user. Introduce a sequence lock to resolve this issue. Measuring the time consumption of 10 million loop iterations doing epoll_wait(), the following performance drop is observed: timeout #event before after diff 0ms 0 3727ms 3974ms +6.6% 0ms 1 8099ms 9134ms +13% 1ms 1 13525ms 13586ms +0.45% Considering the use case of epoll_wait() (wait for events, do something with the events, repeat), it should only contribute to a small portion of user's CPU consumption. Therefore this performance drop is not alarming. Fixes: c5a282e9635e ("fs/epoll: reduce the scope of wq lock in epoll_wait()") Suggested-by: Mateusz Guzik Signed-off-by: Nam Cao Link: https://patch.msgid.link/4363cd8e34a21d4f0d257be1b33e84dc25030fdf.1780422138.git.namcao@linutronix.de Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/eventpoll.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 43a89ae84f881..1c31ee84dbe59 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -38,6 +38,7 @@ #include #include #include +#include #include /* @@ -324,6 +325,9 @@ struct eventpoll { /* Lock which protects rdllist and ovflist */ spinlock_t lock; + /* Protect switching between rdllist and ovflist */ + seqcount_spinlock_t seq; + /* RB tree root used to store monitored fd structs */ struct rb_root_cached rbr; @@ -556,7 +560,10 @@ static inline void epi_clear_ovflist(struct epitem *epi) */ static inline int ep_events_available(struct eventpoll *ep) { - return !list_empty_careful(&ep->rdllist) || ep_is_scanning(ep); + unsigned int seq = read_seqcount_begin(&ep->seq); + + return !list_empty_careful(&ep->rdllist) || ep_is_scanning(ep) || + read_seqcount_retry(&ep->seq, seq); } #ifdef CONFIG_NET_RX_BUSY_POLL @@ -908,8 +915,12 @@ static void ep_start_scan(struct eventpoll *ep, struct list_head *scan_batch) */ lockdep_assert_irqs_enabled(); spin_lock_irq(&ep->lock); + write_seqcount_begin(&ep->seq); + list_splice_init(&ep->rdllist, scan_batch); ep_enter_scan(ep); + + write_seqcount_end(&ep->seq); spin_unlock_irq(&ep->lock); } @@ -940,6 +951,9 @@ static void ep_done_scan(struct eventpoll *ep, ep_pm_stay_awake(epi); } } + + write_seqcount_begin(&ep->seq); + /* Back out of scan mode; callbacks target ep->rdllist again. */ ep_exit_scan(ep); @@ -947,6 +961,9 @@ static void ep_done_scan(struct eventpoll *ep, * Quickly re-inject items left on "scan_batch". */ list_splice(scan_batch, &ep->rdllist); + + write_seqcount_end(&ep->seq); + __pm_relax(ep->ws); if (!list_empty(&ep->rdllist)) { @@ -1331,6 +1348,7 @@ static int ep_alloc(struct eventpoll **pep) mutex_init(&ep->mtx); spin_lock_init(&ep->lock); + seqcount_spinlock_init(&ep->seq, &ep->lock); init_waitqueue_head(&ep->wq); init_waitqueue_head(&ep->poll_wait); INIT_LIST_HEAD(&ep->rdllist); From db2d8b6525bddd04d1e1a076172efdbea473cf78 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 28 Apr 2026 21:52:39 -0500 Subject: [PATCH 1195/3297] gpiolib: acpi: Only trigger ActiveBoth interrupts on boot [ Upstream commit 3bb62e3f99a557d257e5f5a803200051b7de3afa ] Commit ca876c7483b6 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") introduced logic to trigger edge-based GPIO interrupts during initialization to ensure proper initial state setup when firmware doesn't initialize it. However, according to the Microsoft GPIO documentation, triggering GPIO interrupts during initialization should only happen for interrupts marked as ActiveBoth (both IRQF_TRIGGER_RISING and IRQF_TRIGGER_FALLING) and only when the associated GPIO line is already asserted (logic level low). The current implementation incorrectly triggers: 1. Any edge-triggered interrupt (RISING-only or FALLING-only) 2. RISING interrupts when value is high and FALLING when value is low This causes problems at bootup for single-edge interrupts that don't follow the ActiveBoth pattern. Fix this by: - Only triggering when BOTH rising and falling edges are configured - Only triggering when the GPIO line is asserted (value == 0) Reported-by: Francesco Lauritano Closes: https://lore.kernel.org/all/6iFCwGH2vssb7NRUTWGpkubGMNbgIlBHSz40z8ZsezjxngXpoiiRiJaijviNvhiDAGIr43bfUmdxLmxYoHDjyft4DgwFc3Pnu5hzPguTa0s=@protonmail.com/ Tested-by: Marco Scardovi Fixes: ca876c7483b69 ("gpiolib-acpi: make sure we trigger edge events at least once on boot") Link: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- Suggested-by: Armin Wolf Signed-off-by: Mario Limonciello Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede Signed-off-by: Andy Shevchenko Signed-off-by: Sasha Levin --- drivers/gpio/gpiolib-acpi-core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c index 8110690ea69d0..8851e48669756 100644 --- a/drivers/gpio/gpiolib-acpi-core.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -233,12 +233,23 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, event->irq_requested = true; - /* Make sure we trigger the initial state of edge-triggered IRQs */ + /* + * Make sure we trigger the initial state of ActiveBoth IRQs. + * + * According to the Microsoft GPIO documentation, triggering GPIO + * interrupts marked as ActiveBoth during initialization is correct + * as long as the associated GPIO line is already "asserted" + * (logic level low). We should not trigger edge-based GPIO + * interrupts not marked as ActiveBoth. + * + * See: https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/general-purpose-i-o--gpio- + * Section: "GPIO controllers and ActiveBoth interrupts" + */ if (acpi_gpio_need_run_edge_events_on_boot() && - (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { + ((event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) == + (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { value = gpiod_get_raw_value_cansleep(event->desc); - if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || - ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) + if (value == 0) event->handler(event->irq, event); } } From 466c7f87de52d429b76a56064ab6959e4a312b6f Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Mon, 13 Apr 2026 08:50:39 +0800 Subject: [PATCH 1196/3297] i3c: master: svc: Fix missed IBI after false SLVSTART on NPCM845 [ Upstream commit fa1d4fa118f4229168e9ca88cea260c5e5a94652 ] The NPCM845 I3C controller may raise a false SLVSTART interrupt. The handler first latches MSTATUS and then clears SLVSTART. If a real IBI request arrives after the handler latches MSTATUS but before it clears the SLVSTART interrupt status, HW sets the SLVREQ state. However, the handler still relies on the stale MSTATUS snapshot, returns early, and misses the real IBI. No further interrupt is generated for this pending IBI. Re-read MSTATUS to obtain the latest state and avoid missing a real IBI due to this race condition. Fixes: 4dd12e944f07 ("i3c: master: svc: Fix npcm845 invalid slvstart event") Signed-off-by: Stanley Chu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260413005040.1211107-2-yschu@nuvoton.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master/svc-i3c-master.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 93531cb216d16..3d9f905d56039 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -635,10 +635,18 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id) /* Clear the interrupt status */ writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS); - /* Ignore the false event */ - if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART) && - !SVC_I3C_MSTATUS_STATE_SLVREQ(active)) - return IRQ_HANDLED; + if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART)) { + /* + * Re-read MSTATUS to obtain the latest state and avoid + * missing an IBI that arrives after MSTATUS is latched + * but before SLVSTART is cleared. + */ + active = readl(master->regs + SVC_I3C_MSTATUS); + + /* Ignore the false event */ + if (!SVC_I3C_MSTATUS_STATE_SLVREQ(active)) + return IRQ_HANDLED; + } /* * The SDA line remains low until the request is processed. From bb3d592c7d6c4ec8ac6640c690ca13298e7e8e90 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 27 Apr 2026 10:17:12 +0200 Subject: [PATCH 1197/3297] staging: nvec: fix use-after-free in nvec_rx_completed() [ Upstream commit 26813881181deb3a32fbb59eadb2599cbe8423f6 ] In nvec_rx_completed(), when an incomplete RX transfer is detected, nvec_msg_free() is called to return the message back to the pool by clearing its 'used' atomic flag. Immediately after this, the code accesses nvec->rx->data[0] to check the message type. Since nvec_msg_free() marks the pool slot as available via atomic_set(), any concurrent or subsequent call to nvec_msg_alloc() could claim that same slot and overwrite its data[] array. Reading nvec->rx->data[0] after freeing the message is therefore a use-after-free. Fix this by saving the message type byte before calling nvec_msg_free(), then using the saved value for the battery quirk check. Fixes: d6bdcf2e1019 ("staging: nvec: Add battery quirk to ignore incomplete responses") Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260427081713.3401874-2-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/nvec/nvec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 263774e6a78ca..1cdaf7dd02e9f 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -494,6 +494,8 @@ static void nvec_tx_completed(struct nvec_chip *nvec) static void nvec_rx_completed(struct nvec_chip *nvec) { if (nvec->rx->pos != nvec_msg_size(nvec->rx)) { + unsigned char msg_type = nvec->rx->data[0]; + dev_err(nvec->dev, "RX incomplete: Expected %u bytes, got %u\n", (uint)nvec_msg_size(nvec->rx), (uint)nvec->rx->pos); @@ -502,7 +504,7 @@ static void nvec_rx_completed(struct nvec_chip *nvec) nvec->state = 0; /* Battery quirk - Often incomplete, and likes to crash */ - if (nvec->rx->data[0] == NVEC_BAT) + if (msg_type == NVEC_BAT) complete(&nvec->ec_transfer); return; From fb940466fd4d3cdc7e084cd8777c838b2217ee45 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 4 May 2026 01:12:27 -0700 Subject: [PATCH 1198/3297] perf debuginfo: Fix libdw API contract violations [ Upstream commit 31088ccf0312b1a547046f1f69890ede07834a30 ] Check return value of `dwfl_report_end` during offline initialization. Validate `dwfl_module_relocation_info` result before passing to `strcmp` to avoid potential segmentation faults. Additionally: - Fix a file descriptor leak in `debuginfo__init_offline_dwarf()` when `dwfl_report_offline()` or subsequent setup calls fail. Fixes: 6f1b6291cf73cb32 ("perf tools: Add util/debuginfo.[ch] files") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zecheng Li Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/debuginfo.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index bb9ebd84ec2d2..07099b50f2192 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, { GElf_Addr dummy; int fd; + bool fd_consumed = false; fd = open(path, O_RDONLY); if (fd < 0) @@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; + fd_consumed = true; dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); if (!dbg->dbg) @@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - dwfl_report_end(dbg->dwfl, NULL, NULL); + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) + goto error; return 0; error: if (dbg->dwfl) dwfl_end(dbg->dwfl); - else + if (!fd_consumed) close(fd); memset(dbg, 0, sizeof(*dbg)); @@ -168,7 +171,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, /* Search the relocation related .text section */ for (i = 0; i < n; i++) { p = dwfl_module_relocation_info(dbg->mod, i, &shndx); - if (strcmp(p, ".text") == 0) { + if (p && strcmp(p, ".text") == 0) { /* OK, get the section header */ scn = elf_getscn(elf, shndx); if (!scn) From c3ca7c6741af3520ae66f2cebd1a9ee0b985dc1b Mon Sep 17 00:00:00 2001 From: Yingchao Deng Date: Sun, 26 Apr 2026 17:59:34 +0800 Subject: [PATCH 1199/3297] coresight: cti: Fix DT filter signals silently ignored [ Upstream commit 551bb2fd5e4ed63d33aa11f07102cce5179b7595 ] In cti_plat_process_filter_sigs(), after allocating a temporary cti_trig_grp struct via kzalloc_obj(), the code never assigns tg->nr_sigs = nr_filter_sigs. Since kzalloc zero-initialises the struct, tg->nr_sigs remains 0. cti_plat_read_trig_group() guards with: if (!tgrp->nr_sigs) return 0; so it returns immediately without reading any signal indices from DT. Fix by assigning tg->nr_sigs before calling cti_plat_read_trig_group(). Fixes: a5614770ab97 ("coresight: cti: Add device tree support for custom CTI") Signed-off-by: Yingchao Deng Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260426-nr_sigs-v1-1-3b9df99dab97@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-cti-platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index d0ae10bf61281..0ed52a693c35b 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -329,6 +329,7 @@ static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata, if (!tg) return -ENOMEM; + tg->nr_sigs = nr_filter_sigs; err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS); if (!err) drvdata->config.trig_out_filter |= tg->used_mask; From d3896c944338c3fdb0d61a919d57e6705113c2d6 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 28 Apr 2026 16:46:12 +0800 Subject: [PATCH 1200/3297] soundwire: don't program SDW_SCP_BUSCLOCK_SCALE on a unattached Peripheral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c368dd5cbd61ffab2b6f8a89b0d5775e2e16cde6 ] The SDW_SCP_BUSCLOCK_SCALE register will be programmed when the Peripheral is attached. We can and should skip programming the SDW_SCP_BUSCLOCK_SCALE register when the Peripheral is unattached. Fixes: 645291cfe5e5 ("Soundwire: stream: program BUSCLOCK_SCALE") Signed-off-by: Bard Liao Reviewed-by: Simon Trimmer Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20260428084612.322701-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/stream.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 38c9dbd356065..ecd0c2889bb42 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -691,6 +691,13 @@ static int sdw_program_params(struct sdw_bus *bus, bool prepare) if (scale_index < 0) return scale_index; + /* Skip the unattached Peripherals */ + if (!completion_done(&slave->enumeration_complete)) { + dev_warn(&slave->dev, + "Not enumerated, skip programming BUSCLOCK_SCALE\n"); + continue; + } + ret = sdw_write_no_pm(slave, addr1, scale_index); if (ret < 0) { dev_err(&slave->dev, "SDW_SCP_BUSCLOCK_SCALE register write failed\n"); From a454f61747c97e2eadaa7a35ffc1f4b1645c6a53 Mon Sep 17 00:00:00 2001 From: "Baoli.Zhang" Date: Wed, 6 May 2026 13:50:35 +0800 Subject: [PATCH 1201/3297] soundwire: fix bug in sdw_add_element_group_count found by syzkaller [ Upstream commit f772ff5a0e6758fd412803c09e03ba3bca5f5878 ] The original implementation caused an out-of-bounds memory access in the sdw_add_element_group_count for-loop when i == num. for (i = 0; i <= num; i++) { if (rate == group->rates[i] && lane == group->lanes[i]) ... To fix this error, the function now checks for existing rate/lane entries in the group(a function parameter) using a for-loop before adding them. No functional changes apart from this fix. Fixes: 9026118f20e2 ("soundwire: Add generic bandwidth allocation algorithm") Reviewed-by: Bard Liao Reviewed-by: Andy Shevchenko Signed-off-by: Baoli.Zhang Link: https://patch.msgid.link/20260506055039.3751028-2-baoli.zhang@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../soundwire/generic_bandwidth_allocation.c | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index c18f0c16f9297..e52c536d663c5 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -296,39 +296,36 @@ static int sdw_add_element_group_count(struct sdw_group *group, int num = group->count; int i; - for (i = 0; i <= num; i++) { + for (i = 0; i < num; i++) { if (rate == group->rates[i] && lane == group->lanes[i]) - break; - - if (i != num) - continue; - - if (group->count >= group->max_size) { - unsigned int *rates; - unsigned int *lanes; + return 0; + } - group->max_size += 1; - rates = krealloc(group->rates, - (sizeof(int) * group->max_size), - GFP_KERNEL); - if (!rates) - return -ENOMEM; + if (group->count >= group->max_size) { + unsigned int *rates; + unsigned int *lanes; - group->rates = rates; + group->max_size += 1; + rates = krealloc(group->rates, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!rates) + return -ENOMEM; - lanes = krealloc(group->lanes, - (sizeof(int) * group->max_size), - GFP_KERNEL); - if (!lanes) - return -ENOMEM; + group->rates = rates; - group->lanes = lanes; - } + lanes = krealloc(group->lanes, + (sizeof(int) * group->max_size), + GFP_KERNEL); + if (!lanes) + return -ENOMEM; - group->rates[group->count] = rate; - group->lanes[group->count++] = lane; + group->lanes = lanes; } + group->rates[group->count] = rate; + group->lanes[group->count++] = lane; + return 0; } From 1ac8f4c112aa99828fdda5b1ba5ca50e676ee1c8 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 11 Nov 2025 18:58:42 +0000 Subject: [PATCH 1202/3297] coresight: etm4x: Remove the state_needs_restore flag [ Upstream commit 9e9182cab5ebc3ee7544e60ef08ba19fdf216920 ] When the restore flow is invoked, it means no error occurred during the save phase. Otherwise, if any errors happened while saving the context, the function would return an error and abort the suspend sequence. Therefore, the state_needs_restore flag is unnecessary. The save and restore functions are changed to check two conditions: 1) The global flag pm_save_enable is SELF_HOSTED mode; 2) The device is in active mode (non DISABLED). Reviewed-by: Yeoreum Yun Reviewed-by: Mike Leach Tested-by: James Clark Signed-off-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20251111-arm_coresight_power_management_fix-v6-8-f55553b6c8b3@arm.com Stable-dep-of: 2ab4645fe420 ("coresight: ete: Always save state on power down") Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 14 ++++++++------ drivers/hwtracing/coresight/coresight-etm4x.h | 2 -- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index fdda924a2c711..36bf1eecb2a4b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1953,8 +1953,6 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) goto out; } - drvdata->state_needs_restore = true; - /* * Power can be removed from the trace unit now. We do this to * potentially save power on systems that respect the TRCPDCR_PU @@ -1972,11 +1970,14 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) { int ret = 0; + if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + return 0; + /* * Save and restore the ETM Trace registers only if * the ETM is active. */ - if (coresight_get_mode(drvdata->csdev) && drvdata->save_state) + if (coresight_get_mode(drvdata->csdev)) ret = __etm4_cpu_save(drvdata); return ret; } @@ -2066,8 +2067,6 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) if (!drvdata->skip_power_up) etm4x_relaxed_write32(csa, state->trcpdcr, TRCPDCR); - drvdata->state_needs_restore = false; - /* * As recommended by section 4.3.7 ("Synchronization when using the * memory-mapped interface") of ARM IHI 0064D @@ -2086,7 +2085,10 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) { - if (drvdata->state_needs_restore) + if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + return; + + if (coresight_get_mode(drvdata->csdev)) __etm4_cpu_restore(drvdata); } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index b8796b4271025..012c52fd19338 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -980,7 +980,6 @@ struct etmv4_save_state { * in EL2. Otherwise, 0. * @config: structure holding configuration parameters. * @save_state: State to be preserved across power loss - * @state_needs_restore: True when there is context to restore after PM exit * @skip_power_up: Indicates if an implementation can skip powering up * the trace unit. * @paused: Indicates if the trace unit is paused. @@ -1036,7 +1035,6 @@ struct etmv4_drvdata { u64 trfcr; struct etmv4_config config; struct etmv4_save_state *save_state; - bool state_needs_restore; bool skip_power_up; bool paused; DECLARE_BITMAP(arch_features, ETM4_IMPDEF_FEATURE_MAX); From 65d87f28daec31903fc39fee0744f7a0059c19f2 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 5 May 2026 17:51:25 +0100 Subject: [PATCH 1203/3297] coresight: ete: Always save state on power down [ Upstream commit 2ab4645fe4206c142a5f1491e191c906279686cf ] System register ETMs and ETE are unlikely to be preserved on CPU power down. The ETE DT binding also never documented "arm,coresight-loses-context-with-cpu" so nobody would have legitimately been able to use that binding to fix it and ACPI has no such binding at all. Fix it by hard coding the setting for sysreg ETMs (ETE is always sysreg) or ACPI boots. Use a local variable when setting up save_state so that it's immune to concurrent probing when devices have different configurations which is an issue with modifying the global. This fixes the following error when using Coresight with ACPI on the FVP which supports CPU PM: coresight ete0: External agent took claim tag WARNING: drivers/hwtracing/coresight/coresight-core.c:248 at coresight_disclaim_device_unlocked+0xe0/0xe8, CPU#0: perf/117 Fixes: 35e1c9163e02 ("coresight: ete: Add support for ETE tracing") Signed-off-by: James Clark Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260505-james-cs-ete-pm_save_enable-v3-1-485d21dd79b8@linaro.org Signed-off-by: Sasha Levin --- .../coresight/coresight-etm4x-core.c | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 36bf1eecb2a4b..97fab4b3ca284 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -55,10 +55,14 @@ MODULE_PARM_DESC(boot_enable, "Enable tracing on boot"); #define PARAM_PM_SAVE_NEVER 1 /* never save any state */ #define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */ +/* + * Save option for ETM4. ETE, sysreg ETM4s and ACPI boots ignore this option and + * will always save. + */ static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE; module_param(pm_save_enable, int, 0444); MODULE_PARM_DESC(pm_save_enable, - "Save/restore state on power down: 1 = never, 2 = self-hosted"); + "Save/restore state on power down: 1 = never, 2 = self-hosted. MMIO and DT only."); static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; static void etm4_set_default_config(struct etmv4_config *config); @@ -1970,7 +1974,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) { int ret = 0; - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + if (!drvdata->save_state) return 0; /* @@ -2085,7 +2089,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) { - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + if (!drvdata->save_state) return; if (coresight_get_mode(drvdata->csdev)) @@ -2170,6 +2174,17 @@ static void etm4_pm_clear(void) } } +static bool etm4x_always_pm_save(struct device *dev, struct csdev_access *csa) +{ + /* + * Only IO mem ETM devices will benefit from skipping PM save and only + * DT has the option to control it, not ACPI. Otherwise system register + * based ETMs and ETEs will always lose context on CPU power down, so + * always save. + */ + return !csa->io_mem || is_acpi_device_node(dev_fwnode(dev)); +} + static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) { int ret; @@ -2179,6 +2194,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) struct coresight_desc desc = { 0 }; u8 major, minor; char *type_name; + bool pm_save; if (!drvdata) return -EINVAL; @@ -2206,6 +2222,21 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) etm4_set_default(&drvdata->config); + if (etm4x_always_pm_save(dev, init_arg->csa)) + pm_save = true; + else if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE) + pm_save = coresight_loses_context_with_cpu(dev); + else + pm_save = pm_save_enable != PARAM_PM_SAVE_NEVER; + + if (pm_save) { + drvdata->save_state = devm_kmalloc(dev, + sizeof(struct etmv4_save_state), + GFP_KERNEL); + if (!drvdata->save_state) + return -ENOMEM; + } + pdata = coresight_get_platform_data(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); @@ -2263,17 +2294,6 @@ static int etm4_probe(struct device *dev) if (ret) return ret; - if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE) - pm_save_enable = coresight_loses_context_with_cpu(dev) ? - PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER; - - if (pm_save_enable != PARAM_PM_SAVE_NEVER) { - drvdata->save_state = devm_kmalloc(dev, - sizeof(struct etmv4_save_state), GFP_KERNEL); - if (!drvdata->save_state) - return -ENOMEM; - } - raw_spin_lock_init(&drvdata->spinlock); drvdata->cpu = coresight_get_cpu(dev); From 48dde5c56426ce95cc966ed9bffcdc4c9a0c43ea Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Wed, 8 Apr 2026 13:31:43 +0100 Subject: [PATCH 1204/3297] coresight: etm4x: Correct TRCVMIDCCTLR1 save and restore [ Upstream commit 0ec0a8785d21f63db520bd9d2a67c55e855d36a8 ] It is a typo to use trcvmidcctlr0 to save and restore TRCVMIDCCTLR1. Use trcvmidcctlr1 instead. Fixes: f5bd523690d2 ("coresight: etm4x: Convert all register accesses") Signed-off-by: Leo Yan Reviewed-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260408-arm_cs_fix_trcvmidcctlr1_typo-v1-1-6a5695363b46@arm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 97fab4b3ca284..2271f5ea81214 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1941,7 +1941,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR0); if (drvdata->numvmidc > 4) - state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR1); + state->trcvmidcctlr1 = etm4x_read32(csa, TRCVMIDCCTLR1); state->trcclaimset = etm4x_read32(csa, TRCCLAIMCLR); @@ -2064,7 +2064,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR0); if (drvdata->numvmidc > 4) - etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR1); + etm4x_relaxed_write32(csa, state->trcvmidcctlr1, TRCVMIDCCTLR1); etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET); From 10021c2d33061b7704129bc4cdfcacf9cc396349 Mon Sep 17 00:00:00 2001 From: Carlos Bilbao Date: Mon, 27 Apr 2026 21:01:04 -0700 Subject: [PATCH 1205/3297] PCI/ASPM: Don't reconfigure ASPM entering low-power state [ Upstream commit c855c9921da72e535c24737c748f603a52d03f7e ] Reconfiguring ASPM when a device transitions to low-power state can enable L1.1/L1.2 substates on the PCIe link at a time when the device is sleeping and may be unable to exit them. ASPM should be reconfigured on D0 entry (resume), not on the way down. pci_set_low_power_state() calls pcie_aspm_pm_state_change() after writing D3hot to PCI_PM_CTRL. pcie_aspm_pm_state_change() resets link->aspm_capable to link->aspm_support and then calls pcie_config_aspm_path(), which can enable ASPM L1.1/L1.2 substates on the PCIe link. If the device cannot recover the link from L1.2 while in D3hot, subsequent config space reads return 0xFFFF ("device inaccessible") and pci_power_up() fails with messages like: vfio-pci 0000:5d:00.0: Unable to change power state from D3hot to D0, device inaccessible This was observed on NVIDIA H100 SXM5 GPUs bound to vfio-pci when Linux runtime PM suspends them to D3hot: the GPU becomes permanently inaccessible and disappears from the PCIe bus. The call to pcie_aspm_pm_state_change() in pci_set_low_power_state() was restored by f93e71aea6c6 ("Revert "PCI/ASPM: Remove pcie_aspm_pm_state_change()""), which reverted 08d0cc5f3426 ("PCI/ASPM: Remove pcie_aspm_pm_state_change()"). The revert was necessary because the removal broke suspend/resume on certain platforms that required ASPM to be reconfigured on D0 entry. However, the revert restored the call in both pci_set_full_power_state() (D0 entry) and pci_set_low_power_state() (low-power entry). Only the D0-entry call is needed to fix the suspend/resume regression. The low-power-entry call is harmful: reconfiguring ASPM immediately after putting a device into D3hot can enable link substates that the device or platform cannot exit while the device is sleeping. Remove the pcie_aspm_pm_state_change() call from pci_set_low_power_state(). ASPM will still be reconfigured correctly when the device returns to D0 via pci_set_full_power_state(). Fixes: f93e71aea6c6 ("Revert "PCI/ASPM: Remove pcie_aspm_pm_state_change()"") Signed-off-by: Carlos Bilbao (Lambda) Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260428040104.78524-1-carlos.bilbao@kernel.org Signed-off-by: Sasha Levin --- drivers/pci/pci.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0b6a23405f167..6420b371aaf07 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1519,9 +1519,6 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool pci_power_name(dev->current_state), pci_power_name(state)); - if (dev->bus->self) - pcie_aspm_pm_state_change(dev->bus->self, locked); - return 0; } From 78f264c0cb2ac966f0e1efd53f4cd7cb41a30b76 Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Fri, 8 May 2026 16:21:27 +0800 Subject: [PATCH 1206/3297] PCI: Introduce named defines for PCI ROM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 113e86bc58a918f85d250723436a4d541a873358 ] Convert the magic numbers associated with PCI ROM into named definitions. Some of these definitions will be used in the second fix patch. Signed-off-by: Guixin Liu Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Reviewed-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260508082128.3344255-2-kanie@linux.alibaba.com Stable-dep-of: 538796b807fc ("PCI: Check ROM header and data structure addr before accessing") Signed-off-by: Sasha Levin --- drivers/pci/rom.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index e18d3a4383ba6..d4a141bd148b4 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -5,13 +5,28 @@ * (C) Copyright 2004 Jon Smirl * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes */ + +#include #include #include #include +#include #include #include "pci.h" +#define PCI_ROM_HEADER_SIZE 0x1A +#define PCI_ROM_POINTER_TO_DATA_STRUCT 0x18 +#define PCI_ROM_LAST_IMAGE_INDICATOR 0x15 +#define PCI_ROM_LAST_IMAGE_INDICATOR_BIT BIT(7) +#define PCI_ROM_IMAGE_LEN 0x10 +#define PCI_ROM_IMAGE_SECTOR_SIZE SZ_512 +#define PCI_ROM_IMAGE_SIGNATURE 0xAA55 + +/* Data structure signature is "PCIR" in ASCII representation */ +#define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350 +#define PCI_ROM_DATA_STRUCT_LEN 0x0A + /** * pci_enable_rom - enable ROM decoding for a PCI device * @pdev: PCI device to enable @@ -91,26 +106,27 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, do { void __iomem *pds; /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readw(image) != 0xAA55) { - pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n", - readw(image)); + if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", + PCI_ROM_IMAGE_SIGNATURE, readw(image)); break; } - /* get the PCI data structure and check its "PCIR" signature */ - pds = image + readw(image + 24); - if (readl(pds) != 0x52494350) { - pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n", - readl(pds)); + /* Get the PCI data structure and check its "PCIR" signature */ + pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT); + if (readl(pds) != PCI_ROM_DATA_STRUCT_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", + PCI_ROM_DATA_STRUCT_SIGNATURE, readl(pds)); break; } - last_image = readb(pds + 21) & 0x80; - length = readw(pds + 16); - image += length * 512; + last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) & + PCI_ROM_LAST_IMAGE_INDICATOR_BIT; + length = readw(pds + PCI_ROM_IMAGE_LEN); + image += length * PCI_ROM_IMAGE_SECTOR_SIZE; /* Avoid iterating through memory outside the resource window */ if (image >= rom + size) break; if (!last_image) { - if (readw(image) != 0xAA55) { + if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { pci_info(pdev, "No more image in the PCI ROM\n"); break; } From 1d495446ec7ace5b61da366ffb161ee8319dd9a2 Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Fri, 8 May 2026 16:21:28 +0800 Subject: [PATCH 1207/3297] PCI: Check ROM header and data structure addr before accessing [ Upstream commit 538796b807fcfb81b2ce40cc97a614fd8588feb5 ] We meet a crash when running stress-ng on x86_64 machine: BUG: unable to handle page fault for address: ffa0000007f40000 RIP: 0010:pci_get_rom_size+0x52/0x220 Call Trace: pci_map_rom+0x80/0x130 pci_read_rom+0x4b/0xe0 kernfs_file_read_iter+0x96/0x180 vfs_read+0x1b1/0x300 Our analysis reveals that the ROM space's start address is 0xffa0000007f30000, and size is 0x10000. Because of broken ROM space, before calling readl(pds), the pds's value is 0xffa0000007f3ffff, which is already pointed to the ROM space end, invoking readl() would read 4 bytes therefore cause an out-of-bounds access and trigger a crash. Fix this by adding image header and data structure checking. We also found another crash on arm64 machine: Unable to handle kernel paging request at virtual address ffff8000dd1393ff Mem abort info: ESR = 0x0000000096000021 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x21: alignment fault The call trace is the same with x86_64, but the crash reason is that the data structure addr is not aligned with 4, and arm64 machine report "alignment fault". Fix this by adding alignment checking. Fixes: 47b975d234ea ("PCI: Avoid iterating through memory outside the resource window") Suggested-by: Guanghui Feng Signed-off-by: Guixin Liu [bhelgaas: shorten function names, wrap comments] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260508082128.3344255-3-kanie@linux.alibaba.com Signed-off-by: Sasha Levin --- drivers/pci/rom.c | 123 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 18 deletions(-) diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index d4a141bd148b4..f2105c6ceef52 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -6,9 +6,12 @@ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes */ +#include #include #include #include +#include +#include #include #include #include @@ -27,6 +30,15 @@ #define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350 #define PCI_ROM_DATA_STRUCT_LEN 0x0A +/* + * Per PCI Firmware r3.3, sec 5.1.3, a conformant PCI Data Structure is at + * least 24 bytes (0x18), large enough to cover every fixed field this + * driver reads (up to the Indicator byte at offset 0x15). Reject smaller + * device-claimed lengths so the follow-up readers in pci_get_rom_size() + * cannot escape the mapped ROM window. + */ +#define PCI_ROM_DATA_STRUCT_MIN_LEN 0x18 + /** * pci_enable_rom - enable ROM decoding for a PCI device * @pdev: PCI device to enable @@ -84,6 +96,91 @@ void pci_disable_rom(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_rom); +static bool pci_rom_header_valid(struct pci_dev *pdev, void __iomem *image, + void __iomem *rom, size_t size, + bool expect_valid) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long header_end; + u16 signature; + + /* + * Per PCI Firmware r3.3, sec 5.1, each image must start on a + * 512-byte boundary and must contain the PCI Expansion ROM header. + * Because @rom is page-aligned (returned by ioremap()), checking + * 512-byte alignment of @image is equivalent to enforcing the + * spec's sector-aligned layout within the ROM. This also + * satisfies the natural-alignment requirement of readw() on archs + * such as arm64 that disallow unaligned IOMEM access. + */ + if (!IS_ALIGNED((unsigned long)image, PCI_ROM_IMAGE_SECTOR_SIZE)) + return false; + + if (check_add_overflow((unsigned long)image, PCI_ROM_HEADER_SIZE - 1, + &header_end)) + return false; + + if (image < rom || header_end > rom_end) + return false; + + /* Standard PCI ROMs start out with these bytes 55 AA */ + signature = readw(image); + if (signature != PCI_ROM_IMAGE_SIGNATURE) { + if (expect_valid) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", + PCI_ROM_IMAGE_SIGNATURE, signature); + } else { + pci_info(pdev, "No more images in PCI ROM\n"); + } + return false; + } + + return true; +} + +static bool pci_rom_data_struct_valid(struct pci_dev *pdev, void __iomem *pds, + void __iomem *rom, size_t size) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long end; + u32 signature; + u16 data_len; + + /* + * Some CPU architectures require IOMEM access addresses to be + * aligned, for example arm64, so since we're about to call + * readl(), check here for 4-byte alignment. + */ + if (!IS_ALIGNED((unsigned long)pds, 4)) + return false; + + if (check_add_overflow((unsigned long)pds, PCI_ROM_DATA_STRUCT_LEN + 1, + &end)) + return false; + + if (pds < rom || end > rom_end) + return false; + + signature = readl(pds); + if (signature != PCI_ROM_DATA_STRUCT_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", + PCI_ROM_DATA_STRUCT_SIGNATURE, signature); + return false; + } + + data_len = readw(pds + PCI_ROM_DATA_STRUCT_LEN); + if (data_len < PCI_ROM_DATA_STRUCT_MIN_LEN || data_len == U16_MAX) + return false; + + if (check_add_overflow((unsigned long)pds, data_len - 1, &end)) + return false; + + if (end > rom_end) + return false; + + return true; +} + /** * pci_get_rom_size - obtain the actual size of the ROM image * @pdev: target PCI device @@ -99,38 +196,28 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) { void __iomem *image; - int last_image; unsigned int length; + bool last_image; image = rom; do { void __iomem *pds; - /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { - pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", - PCI_ROM_IMAGE_SIGNATURE, readw(image)); + if (!pci_rom_header_valid(pdev, image, rom, size, true)) break; - } + /* Get the PCI data structure and check its "PCIR" signature */ pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT); - if (readl(pds) != PCI_ROM_DATA_STRUCT_SIGNATURE) { - pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", - PCI_ROM_DATA_STRUCT_SIGNATURE, readl(pds)); + if (!pci_rom_data_struct_valid(pdev, pds, rom, size)) break; - } + last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) & PCI_ROM_LAST_IMAGE_INDICATOR_BIT; length = readw(pds + PCI_ROM_IMAGE_LEN); image += length * PCI_ROM_IMAGE_SECTOR_SIZE; - /* Avoid iterating through memory outside the resource window */ - if (image >= rom + size) + + if (!last_image && + !pci_rom_header_valid(pdev, image, rom, size, false)) break; - if (!last_image) { - if (readw(image) != PCI_ROM_IMAGE_SIGNATURE) { - pci_info(pdev, "No more image in the PCI ROM\n"); - break; - } - } } while (length && !last_image); /* never return a size larger than the PCI resource window */ From 2ce4d93768d2c65a33c49e66b050d637a918e875 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 20:00:27 +0200 Subject: [PATCH 1208/3297] x86/platform/olpc: xo15: Drop wakeup source on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit cc966553e6ff0849978b5754531b768b0ff54985 ] Prevent leaking a wakeup source object after removing the driver by adding appropriate cleanup code to its remove callback function. Fixes: a0f30f592d2d ("x86, olpc: Add XO-1.5 SCI driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2069931.usQuhbGJ8B@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- arch/x86/platform/olpc/olpc-xo15-sci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 68244a3422d1d..683935da493d2 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -185,6 +185,7 @@ static int xo15_sci_add(struct acpi_device *device) static void xo15_sci_remove(struct acpi_device *device) { + device_init_wakeup(&device->dev, false); acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); From c788955b4a1459b55c2a63e341df41f4f1d768a8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:40:31 +0200 Subject: [PATCH 1209/3297] platform/x86: xo15-ebook: Fix wakeup source and GPE handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b2fc2c6ebbd2d49935c8960755d8170faead2159 ] The device_set_wakeup_enable() call in ebook_switch_add() doesn't actually do anything because power.can_wakeup is not set for ACPI device objects. Moreover, had it done anything, it would have registered a wakeup source object that wouldn't have been used going forward and that wakeup source would have been leaked after driver removal because ebook_switch_remove() doesn't clean it up. Accordingly, remove that call from ebook_switch_add(). Also prevent leaking an enabled ACPI GPE after removing the driver by adding appropriate cleanup code to ebook_switch_remove(). Fixes: 89ca11771a4b ("OLPC XO-1.5 ebook switch driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1966125.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/xo15-ebook.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index cb02222c978c0..ced2b44fd7f77 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -38,6 +38,7 @@ MODULE_DEVICE_TABLE(acpi, ebook_device_ids); struct ebook_switch { struct input_dev *input; char phys[32]; /* for input device */ + bool gpe_enabled; }; static int ebook_send_state(struct acpi_device *device) @@ -128,7 +129,7 @@ static int ebook_switch_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device_set_wakeup_enable(&device->dev, true); + button->gpe_enabled = true; } return 0; @@ -144,6 +145,10 @@ static void ebook_switch_remove(struct acpi_device *device) { struct ebook_switch *button = acpi_driver_data(device); + if (button->gpe_enabled) + acpi_disable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number); + input_unregister_device(button->input); kfree(button); } From e1b79f77336d1b8fd4797f557315ef8c74f71719 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 May 2026 17:45:42 -0700 Subject: [PATCH 1210/3297] perf sched: Add missing mmap2 handler in timehist [ Upstream commit 91182741369b261c441e63e6678893032a6d7e4c ] perf_sched__timehist() registers event handlers for options using the sched->tool struct. It registers handlers for MMAP, COMM, EXIT, FORK, etc. but completely omits registering a handler for MMAP2 events. Failing to register both MMAP and MMAP2 handlers causes modern systems (which primarily output MMAP2 records) to silently drop VMA map mappings. This results in uninitialized machine/thread mapping structures, making it impossible to resolve shared library instruction pointers (IPs) to dynamic symbols/DSOs during timehist callchain analysis. Fix this by correctly registering perf_event__process_mmap2 in sched->tool inside perf_sched__timehist(). Fixes: 49394a2a24c78ce0 ("perf sched timehist: Introduce timehist command") Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: David Ahern Cc: Gabriel Marin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index eca3b1c58c4bb..ce0858f309e16 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3292,6 +3292,7 @@ static int perf_sched__timehist(struct perf_sched *sched) */ sched->tool.sample = perf_timehist__process_sample; sched->tool.mmap = perf_event__process_mmap; + sched->tool.mmap2 = perf_event__process_mmap2; sched->tool.comm = perf_event__process_comm; sched->tool.exit = perf_event__process_exit; sched->tool.fork = perf_event__process_fork; From b773c7161cea7b82b006341dc756988cad3d5b08 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Fri, 1 May 2026 02:45:23 +0800 Subject: [PATCH 1211/3297] PCI: loongson: Do not ignore downstream devices on external bridges [ Upstream commit 1389ab9bf9f627d4daed86f492091b00f110aa86 ] Loongson PCI host controllers have a hardware quirk that requires software to ignore downstream devices with device number > 0 on the internal bridges. The current implementation applies the workaround to all non-root buses, which breaks external bridges (e.g., PCIe switches) with multiple downstream devices. Fix it by only applying the workaround to internal bridges. Tested on Loongson-LS3A4000-7A1000-NUC-SE, using AMD Promontory 21 chipset add-in card [1]. $ lspci -tnnnvvv -[0000:00]-+-00.0 Loongson Technology LLC 7A1000 Chipset Hyper Transport Bridge Controller [0014:7a00] +-00.1 Loongson Technology LLC 7A2000 Chipset Hyper Transport Bridge Controller [0014:7a10] +-03.0 Loongson Technology LLC 2K1000/2000 / 7A1000 Chipset Gigabit Ethernet Controller [0014:7a03] +-04.0 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB OHCI Controller [0014:7a24] +-04.1 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB EHCI Controller [0014:7a14] +-05.0 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB OHCI Controller [0014:7a24] +-05.1 Loongson Technology LLC 2K1000 / 7A1000/2000 Chipset USB EHCI Controller [0014:7a14] +-06.0 Loongson Technology LLC 7A1000 Chipset Vivante GC1000 GPU [0014:7a15] +-06.1 Loongson Technology LLC 2K1000 / 7A1000 Chipset Display Controller [0014:7a06] +-07.0 Loongson Technology LLC 2K1000/2000/3000 / 3B6000M / 7A1000/2000 Chipset HD Audio Controller [0014:7a07] +-08.0 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-08.1 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-08.2 Loongson Technology LLC 2K1000 / 7A1000 Chipset 3Gb/s SATA AHCI Controller [0014:7a08] +-09.0-[01]----00.0 Qualcomm Technologies, Inc QCNFA765 Wireless Network Adapter [17cb:1103] +-0a.0-[02]----00.0 Etron Technology, Inc. EJ188/EJ198 USB 3.0 Host Controller [1b6f:7052] +-0f.0-[03-08]----00.0-[04-08]--+-00.0-[05]----00.0 Shenzhen Longsys Electronics Co., Ltd. FORESEE XP1000 / Lexar Professional CFexpress Type B Gold series, NM620 PCIe NVME SSD (DRAM-less) [1d97:5216] | +-08.0-[06]----00.0 MAXIO Technology (Hangzhou) Ltd. NVMe SSD Controller MAP1202 (DRAM-less) [1e4b:1202] | +-0c.0-[07]----00.0 Advanced Micro Devices, Inc. [AMD] 600 Series Chipset USB 3.2 Controller [1022:43f7] | \-0d.0-[08]----00.0 Advanced Micro Devices, Inc. [AMD] 600 Series Chipset SATA Controller [1022:43f6] \-16.0 Loongson Technology LLC 7A1000 Chipset SPI Controller [0014:7a0b] Fixes: 2410e3301fcc ("PCI: loongson: Don't access non-existent devices") Co-developed-by: Jiaxun Yang Signed-off-by: Jiaxun Yang Co-developed-by: Lain "Fearyncess" Yang Signed-off-by: Lain "Fearyncess" Yang Signed-off-by: Rong Zhang Signed-off-by: Manivannan Sadhasivam Link: https://oshwhub.com/wesd/b650 [1] Link: https://patch.msgid.link/20260501-ls7a-bridge-fixes-v2-1-69fa93683805@rong.moe Signed-off-by: Sasha Levin --- drivers/pci/controller/pci-loongson.c | 31 ++++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 9609e6f50b981..d0c643996476f 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -80,6 +80,18 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_LPC, system_bus_quirk); +static const struct pci_device_id loongson_internal_bridge_devids[] = { + { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, + { 0, }, +}; + /* * Some Loongson PCIe ports have hardware limitations on their Maximum Read * Request Size. They can't handle anything larger than this. Sane @@ -92,24 +104,13 @@ static void loongson_set_min_mrrs_quirk(struct pci_dev *pdev) { struct pci_bus *bus = pdev->bus; struct pci_dev *bridge; - static const struct pci_device_id bridge_devids[] = { - { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, - { 0, }, - }; /* look for the matching bridge */ while (!pci_is_root_bus(bus)) { bridge = bus->self; bus = bus->parent; - if (pci_match_id(bridge_devids, bridge)) { + if (pci_match_id(loongson_internal_bridge_devids, bridge)) { if (pcie_get_readrq(pdev) > 256) { pci_info(pdev, "limiting MRRS to 256\n"); pcie_set_readrq(pdev, 256); @@ -266,11 +267,11 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, struct loongson_pci *priv = pci_bus_to_loongson_pci(bus); /* - * Do not read more than one device on the bus other than - * the host bus. + * Do not read more than one device on the internal bridges. */ if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) { - if (!pci_is_root_bus(bus) && (device > 0)) + if (!pci_is_root_bus(bus) && (device > 0) && + pci_match_id(loongson_internal_bridge_devids, bus->self)) return NULL; } From 601a9b2e3b2fbbf708384861186f5c4cc4281d67 Mon Sep 17 00:00:00 2001 From: Hsiu Che Yu Date: Mon, 27 Apr 2026 22:15:49 +0800 Subject: [PATCH 1212/3297] rust: alloc: fix assert in `Vec::reserve` doc test [ Upstream commit 75619f2df7a5da6ffb61eedc2a73fdf70c65471c ] The assert in the doctest used `>= 10`, which only checks that the capacity can hold `additional` elements, ignoring the existing length of `v`. The correct check should ensure there is room for `additional` *extra* elements on top of what is already in the vector. Fix the assert to use `>= v.len() + 10` so the example accurately reflects the actual semantics of the function. Reported-by: Miguel Ojeda Closes: https://lore.kernel.org/rust-for-linux/CANiq72nkXWhjK9iFRrhGtkMZGsvNE_zVsu4JnxaFRfxWL7RRdg@mail.gmail.com/ Fixes: 2aac4cd7dae3d ("rust: alloc: implement kernel `Vec` type") Signed-off-by: Hsiu Che Yu Reviewed-by: Alice Ryhl Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260427-doctest-kvec-reserve-v1-1-0623abcd9c2e@gmail.com Signed-off-by: Danilo Krummrich Signed-off-by: Sasha Levin --- rust/kernel/alloc/kvec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index b6a97b8fb5121..223c5ae0f8315 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -611,7 +611,7 @@ where /// /// v.reserve(10, GFP_KERNEL)?; /// let cap = v.capacity(); - /// assert!(cap >= 10); + /// assert!(cap >= v.len() + 10); /// /// v.reserve(10, GFP_KERNEL)?; /// let new_cap = v.capacity(); From 5bb87456dcd66870a2529bc22f4535027697f890 Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Tue, 14 Apr 2026 11:59:40 +0530 Subject: [PATCH 1213/3297] bus: mhi: ep: Fix potential deadlock in mhi_ep_reset_worker() [ Upstream commit 9dece4435d396e9877e27483552b910ba8654169 ] There is a potential deadlock scenario in mhi_ep_reset_worker() where the state_lock mutex is acquired twice in the same call chain: mhi_ep_reset_worker() mutex_lock(&mhi_cntrl->state_lock) mhi_ep_power_up() mhi_ep_set_ready_state() mutex_lock(&mhi_cntrl->state_lock) <- Deadlock Fix this by releasing the state_lock before calling mhi_ep_power_up(). The lock is only needed to protect current MHI state read operation. The lock can be safely released before proceeding with the power up sequence. Fixes: 7a97b6b47353 ("bus: mhi: ep: Add support for handling MHI_RESET") Signed-off-by: Sumit Kumar Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260414-reset_worker_deadlock-v2-1-42fd682b45db@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/bus/mhi/ep/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index cdea24e929195..4bb007d193331 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1087,11 +1087,12 @@ static void mhi_ep_reset_worker(struct work_struct *work) mhi_ep_power_down(mhi_cntrl); - mutex_lock(&mhi_cntrl->state_lock); - /* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */ mhi_ep_mmio_reset(mhi_cntrl); + + mutex_lock(&mhi_cntrl->state_lock); cur_state = mhi_cntrl->mhi_state; + mutex_unlock(&mhi_cntrl->state_lock); /* * Only proceed further if the reset is due to SYS_ERR. The host will @@ -1100,8 +1101,6 @@ static void mhi_ep_reset_worker(struct work_struct *work) */ if (cur_state == MHI_STATE_SYS_ERR) mhi_ep_power_up(mhi_cntrl); - - mutex_unlock(&mhi_cntrl->state_lock); } /* From 6abdf27fbcfb3e419efa85e60ece8782223780c1 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Tue, 12 May 2026 09:56:07 +0800 Subject: [PATCH 1214/3297] coresight: fix missing error code when trace ID is invalid [ Upstream commit f4526ffee6ff9f5845b430957417149eded74bf3 ] When coresight_path_assign_trace_id() cannot assign a valid trace ID, coresight_enable_sysfs() takes the err_path goto with ret still 0, returning success to the caller despite no trace session being started. Change coresight_path_assign_trace_id() to return int, moving the IS_VALID_CS_TRACE_ID() check inside it so it returns -EINVAL on failure and 0 on success. Update both callers to propagate this return value directly instead of inspecting path->trace_id after the call. Fixes: d87d76d823d1 ("Coresight: Allocate trace ID after building the path") Reviewed-by: James Clark Reviewed-by: Richard Cheng Signed-off-by: Jie Gan Reviewed-by: Leo Yan Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260512-fix-trace-id-error-v4-1-eb3de789767a@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-core.c | 23 +++++++++++-------- .../hwtracing/coresight/coresight-etm-perf.c | 5 ++-- drivers/hwtracing/coresight/coresight-priv.h | 2 +- drivers/hwtracing/coresight/coresight-sysfs.c | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 3267192f0c1c6..911a7b6d4867b 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -732,8 +732,8 @@ static int coresight_get_trace_id(struct coresight_device *csdev, * Call this after creating the path and before enabling it. This leaves * the trace ID set on the path, or it remains 0 if it couldn't be assigned. */ -void coresight_path_assign_trace_id(struct coresight_path *path, - enum cs_mode mode) +int coresight_path_assign_trace_id(struct coresight_path *path, + enum cs_mode mode) { struct coresight_device *sink = coresight_get_sink(path); struct coresight_node *nd; @@ -743,15 +743,18 @@ void coresight_path_assign_trace_id(struct coresight_path *path, /* Assign a trace ID to the path for the first device that wants to do it */ trace_id = coresight_get_trace_id(nd->csdev, mode, sink); - /* - * 0 in this context is that it didn't want to assign so keep searching. - * Non 0 is either success or fail. - */ - if (trace_id != 0) { - path->trace_id = trace_id; - return; - } + /* 0 means the device has no ID assignment, so keep searching */ + if (trace_id == 0) + continue; + + if (!IS_VALID_CS_TRACE_ID(trace_id)) + return -EINVAL; + + path->trace_id = trace_id; + return 0; } + + return -EINVAL; } /** diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 5c256af6e54af..accf101779de8 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -321,6 +321,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, struct coresight_device *sink = NULL; struct coresight_device *user_sink = NULL, *last_sink = NULL; struct etm_event_data *event_data = NULL; + int ret; event_data = alloc_event_data(cpu); if (!event_data) @@ -418,8 +419,8 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, } /* ensure we can allocate a trace ID for this CPU */ - coresight_path_assign_trace_id(path, CS_MODE_PERF); - if (!IS_VALID_CS_TRACE_ID(path->trace_id)) { + ret = coresight_path_assign_trace_id(path, CS_MODE_PERF); + if (ret) { cpumask_clear_cpu(cpu, mask); coresight_release_path(path); continue; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 33e22b1ba0432..bcc5db0d9c3c2 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -154,7 +154,7 @@ int coresight_make_links(struct coresight_device *orig, void coresight_remove_links(struct coresight_device *orig, struct coresight_connection *conn); u32 coresight_get_sink_id(struct coresight_device *csdev); -void coresight_path_assign_trace_id(struct coresight_path *path, +int coresight_path_assign_trace_id(struct coresight_path *path, enum cs_mode mode); #if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 5e52324aa9ac7..c5f3fc2b5135f 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -211,8 +211,8 @@ int coresight_enable_sysfs(struct coresight_device *csdev) goto out; } - coresight_path_assign_trace_id(path, CS_MODE_SYSFS); - if (!IS_VALID_CS_TRACE_ID(path->trace_id)) + ret = coresight_path_assign_trace_id(path, CS_MODE_SYSFS); + if (ret) goto err_path; ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); From 226feccaac81833f227e7ad4b7702ff5c918211f Mon Sep 17 00:00:00 2001 From: Luo Jie Date: Tue, 6 Jan 2026 21:35:10 -0800 Subject: [PATCH 1215/3297] clk: qcom: cmnpll: Account for reference clock divider [ Upstream commit 88c543fff756450bcd04ec4560c4440be36c9e75 ] The clk_cmn_pll_recalc_rate() function must account for the reference clock divider programmed in CMN_PLL_REFCLK_CONFIG. Without this fix, platforms with a reference divider other than 1 calculate incorrect CMN PLL rates. For example, on IPQ5332 where the reference divider is 2, the computed rate becomes twice the actual output. Read CMN_PLL_REFCLK_DIV and divide the parent rate by this value before applying the 2 * FACTOR scaling. This yields the correct rate calculation: rate = (parent_rate / ref_div) * 2 * factor. Maintain backward compatibility with earlier platforms (e.g. IPQ9574, IPQ5424, IPQ5018) that use ref_div = 1. Fixes: f81715a4c87c ("clk: qcom: Add CMN PLL clock controller driver for IPQ SoC") Signed-off-by: Luo Jie Reviewed-by: Konrad Dybcio Tested-by: George Moussalem Link: https://lore.kernel.org/r/20260106-qcom_ipq5332_cmnpll-v2-1-f9f7e4efbd79@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/clk/qcom/ipq-cmn-pll.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index dafbf57320480..369798d1ce427 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -185,7 +185,7 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); - u32 val, factor; + u32 val, factor, ref_div; /* * The value of CMN_PLL_DIVIDER_CTRL_FACTOR is automatically adjusted @@ -193,8 +193,15 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, */ regmap_read(cmn_pll->regmap, CMN_PLL_DIVIDER_CTRL, &val); factor = FIELD_GET(CMN_PLL_DIVIDER_CTRL_FACTOR, val); + if (WARN_ON(factor == 0)) + factor = 1; - return parent_rate * 2 * factor; + regmap_read(cmn_pll->regmap, CMN_PLL_REFCLK_CONFIG, &val); + ref_div = FIELD_GET(CMN_PLL_REFCLK_DIV, val); + if (WARN_ON(ref_div == 0)) + ref_div = 1; + + return div_u64((u64)parent_rate * 2 * factor, ref_div); } static int clk_cmn_pll_determine_rate(struct clk_hw *hw, From 210c202c0576b8e86502f04beb5c5318bcf5e731 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 May 2026 00:01:26 +0200 Subject: [PATCH 1216/3297] phy: phy-can-transceiver: Check driver match and driver data against NULL [ Upstream commit ebee9004cc0200b2b708ebf7ac625d35c71c049f ] 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 driver data need to verify its presence. Accordingly, add requisite match and driver data checks against NULL to the driver where they are missing. Fixes: a4a86d273ff1 ("phy: phy-can-transceiver: Add support for generic CAN transceiver driver") Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260513220336.369628-2-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/phy-can-transceiver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index f59caff4b3d4c..e09ce1a86f5a4 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -122,6 +122,9 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) return -ENOMEM; match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); + if (!match || !match->data) + return -ENODEV; + drvdata = match->data; mux_state = devm_mux_state_get_optional(dev, NULL); From f28906e7e32fd6d8737668ea9795f2ce8c90c660 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 10 Apr 2026 12:13:44 +0100 Subject: [PATCH 1217/3297] perf pmu: Skip test on Arm64 when #slots is zero [ Upstream commit 2e2ba7d1ea554ee6e9e751a53eebf3e9270b0670 ] Some Arm64 PMUs expose 'caps/slots' as 0 when the slot count is not implemented, tool_pmu__read_event() currently returns false for this, so metrics that reference #slots are reported as syntax error. Since the commit 3a61fd866ef9 ("perf expr: Return -EINVAL for syntax error in expr__find_ids()"), these syntax errors are populated as failures and make the PMU metric test fail: 9.3: Parsing of PMU event table metrics: --- start --- ... Found metric 'backend_bound' metric expr 100 * (stall_slot_backend / (#slots * cpu_cycles)) for backend_bound parsing metric: 100 * (stall_slot_backend / (#slots * cpu_cycles)) Failure to read '#slots' literal: #slots = nan syntax error Fail to parse metric or group `backend_bound' ... ---- end(-1) ---- 9.3: Parsing of PMU event table metrics : FAILED! This commit introduces a new function is_expected_broken_metric() to identify broken metrics, and treats metrics containing "#slots" as expected broken when #slots == 0 on Arm64 platforms. Fixes: 3a61fd866ef9aaa1 ("perf expr: Return -EINVAL for syntax error in expr__find_ids()") Reviewed-by: Ian Rogers Reviewed-by: James Clark Signed-off-by: Leo Yan Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/tests/pmu-events.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 95fd9f671a22b..5adac4f7d94cc 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -15,6 +15,7 @@ #include "util/expr.h" #include "util/hashmap.h" #include "util/parse-events.h" +#include "util/tool_pmu.h" #include "metricgroup.h" #include "stat.h" @@ -837,6 +838,26 @@ struct metric { struct metric_ref metric_ref; }; +static bool is_expected_broken_metric(const struct pmu_metric *pm) +{ + if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || + !strcmp(pm->metric_name, "M3")) + return true; + +#if defined(__aarch64__) + /* + * Arm64 platforms may return "#slots == 0", which is treated as a + * syntax error by the parser. Don't test these metrics when running + * on such platforms. + */ + if (strstr(pm->metric_expr, "#slots") && + !tool_pmu__cpu_slots_per_cycle()) + return true; +#endif + + return false; +} + static int test__parsing_callback(const struct pmu_metric *pm, const struct pmu_metrics_table *table, void *data) @@ -872,8 +893,7 @@ static int test__parsing_callback(const struct pmu_metric *pm, err = metricgroup__parse_groups_test(evlist, table, pm->metric_name); if (err) { - if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || - !strcmp(pm->metric_name, "M3")) { + if (is_expected_broken_metric(pm)) { (*failures)--; pr_debug("Expected broken metric %s skipping\n", pm->metric_name); err = 0; From 5732869c70d42006725d7c726412737530c25b6a Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Mar 2026 09:53:28 +0200 Subject: [PATCH 1218/3297] clk: at91: sam9x7: Fix gmac_gclk clock definition [ Upstream commit b6f6ebb0fb57ae6da622fb8fd4ebdc9ba1ae5756 ] According to the datasheet (see link section), table 12.1, instance ID 24 is used for the GMAC generic clock, while instance ID 67 is reserved. Add the correct gmac_gclk entry at ID 24, aligned with the SoC clock layout, and remove the old misplaced entry at ID 67. Link: https://ww1.microchip.com/downloads/aemDocuments/documents/MPU32/ProductDocuments/DataSheets/SAM9X75-SIP-Series-Data-Sheet-DS60001827.pdf Fixes: 33013b43e271 ("clk: at91: sam9x7: add sam9x7 pmc driver") Signed-off-by: Mihai Sain Link: https://lore.kernel.org/r/20260309075329.1528-4-mihai.sain@microchip.com [claudiu.beznea: massaged the patch description] Signed-off-by: Claudiu Beznea Signed-off-by: Sasha Levin --- drivers/clk/at91/sam9x7.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c index 89868a0aeaba9..6b330c3e6bca8 100644 --- a/drivers/clk/at91/sam9x7.c +++ b/drivers/clk/at91/sam9x7.c @@ -569,6 +569,15 @@ static const struct { .pp_chg_id = INT_MIN, }, + { + .n = "gmac_gclk", + .id = 24, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + { .n = "lcd_gclk", .id = 25, @@ -702,15 +711,6 @@ static const struct { .pp_count = 1, .pp_chg_id = INT_MIN, }, - - { - .n = "gmac_gclk", - .id = 67, - .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, - .pp_mux_table = { 6, 8, }, - .pp_count = 2, - .pp_chg_id = INT_MIN, - }, }; static void __init sam9x7_pmc_setup(struct device_node *np) From 67d0475e78b398918a00429a942216624a533ca4 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 14 May 2026 22:16:25 +0800 Subject: [PATCH 1219/3297] soundwire: intel_ace2x: release bpt_stream when close it [ Upstream commit 8a7fe10eec64bfb7cf4091bca540de4c55d56bfa ] The BPT stream was allocated in intel_ace2x_bpt_open_stream(), we need to free it in intel_ace2x_bpt_close_stream(). Fixes: 4c1ce9f37d8a8 ("soundwire: intel_ace2x: add BPT send_async/wait callbacks") Signed-off-by: Bard Liao Reviewed-by: Simon Trimmer Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260514141625.1834216-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/soundwire/intel_ace2x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 5b73bbb73be6e..63aef98361467 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -248,6 +248,7 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave dev_err(cdns->dev, "%s: remove slave failed: %d\n", __func__, ret); + sdw_release_stream(cdns->bus.bpt_stream); cdns->bus.bpt_stream = NULL; } From e6bc4e127707d338b776b4d5e44f91177ee2090a Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Fri, 15 May 2026 21:08:08 +0100 Subject: [PATCH 1220/3297] coresight: Fix source not disabled on idr_alloc_u32 failure [ Upstream commit ea2c2b9e2a66e2b4aa0455b2d70058e2f0ea4d23 ] In coresight_enable_sysfs(), for non-CPU sources (SOFTWARE, TPDM, OTHERS), the source device is enabled via coresight_enable_source_sysfs() before idr_alloc_u32() maps the path. If idr_alloc_u32() fails, the original code jumped directly to err_source, which only calls coresight_disable_path() and coresight_release_path(). The source device was left enabled with an incremented refcnt but no path tracked for it, leaving the device in an inconsistent state. Disable the source before jumping to err_source so the enable and path operations are fully unwound. Fixes: 5c0016d7b343 ("coresight: core: Use IDR for non-cpu bound sources' paths.") Signed-off-by: Jie Gan Reviewed-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-1-f88c4a3ecfe9@arm.com Signed-off-by: Sasha Levin --- drivers/hwtracing/coresight/coresight-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index c5f3fc2b5135f..958c0fe8837d5 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -244,8 +244,10 @@ int coresight_enable_sysfs(struct coresight_device *csdev) */ hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); - if (ret) + if (ret) { + coresight_disable_source_sysfs(csdev, NULL); goto err_source; + } break; default: /* We can't be here */ From 91353d63bbf61870959cdeace7c8ffa2815091f7 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 14 May 2026 17:39:51 +0100 Subject: [PATCH 1221/3297] mailbox: mpfs: fix check for syscon presence in mpfs_mbox_inbox_isr() [ Upstream commit e30d8b2730a33e5e8789371e947c3529789a6070 ] mpfs_mbox_inbox_isr() writes to the sysreg scb syscon, not the control scb syscon, but checks for the presence of the latter. Ultimately this makes little difference because if one syscon is present, both will be. Fixes: a4123ffab9ece ("mailbox: mpfs: support new, syscon based, devicetree configuration") Signed-off-by: Conor Dooley Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/mailbox-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index d5d9effece979..ef40fe2be30d6 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -201,7 +201,7 @@ static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data) struct mbox_chan *chan = data; struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; - if (mbox->control_scb) + if (mbox->sysreg_scb) regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0); else writel_relaxed(0, mbox->int_reg); From 7d881615fb6373f71fc628b3f00186aeca87a3d5 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 28 Apr 2026 11:55:44 +0900 Subject: [PATCH 1222/3297] mailbox: mtk-adsp: fix UAF during device teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b57d1a40bc43258372fa1f4d39305e093947a262 ] When the SOF audio driver fails to initialize (e.g. firmware boot timeout), its devres unwind frees the snd_sof_dev object that the mailbox client (mtk-adsp-ipc) reaches via chan->cl->rx_callback. The mtk-adsp-mailbox shutdown clears the mailbox command registers but leaves the IRQ line unmasked, so a late interrupt can still queue a threaded handler after mbox_free_channel() had cleared chan->cl, and mbox_chan_received_data() would then trigger UAF: BUG: KASAN: slab-use-after-free in sof_ipc3_validate_fw_version sof_ipc3_validate_fw_version sof_ipc3_do_rx_work sof_ipc3_rx_msg mt8196_dsp_handle_request mtk_adsp_ipc_recv mbox_chan_received_data mtk_adsp_mbox_isr irq_thread_fn Freed by task ...: kfree devres_release_all really_probe ... (sof-audio-of-mt8196 probe failure) The crash was observed roughly three seconds after the failed probe. disable_irq() in shutdown and enable_irq() in startup. disable_irq() also waits for any in-flight interrupts, so by the time mbox_free_channel() proceeds to clear chan->cl no rx_callback can run. In addition, request the IRQ with IRQF_NO_AUTOEN so it stays masked between probe and the first client bind — otherwise an early interrupt can crash on chan->cl == NULL in mbox_chan_received_data(). Fixes: af2dfa96c52d ("mailbox: mediatek: add support for adsp mailbox controller") Signed-off-by: Sergey Senozhatsky Reviewed-by: Tzung-Bi Shih Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/mtk-adsp-mailbox.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/mtk-adsp-mailbox.c b/drivers/mailbox/mtk-adsp-mailbox.c index 91487aa4d7da0..8bcecddee0eb5 100644 --- a/drivers/mailbox/mtk-adsp-mailbox.c +++ b/drivers/mailbox/mtk-adsp-mailbox.c @@ -19,6 +19,7 @@ struct mtk_adsp_mbox_priv { struct mbox_controller mbox; void __iomem *va_mboxreg; const struct mtk_adsp_mbox_cfg *cfg; + int irq; }; struct mtk_adsp_mbox_cfg { @@ -67,6 +68,8 @@ static int mtk_adsp_mbox_startup(struct mbox_chan *chan) writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); + enable_irq(priv->irq); + return 0; } @@ -74,6 +77,8 @@ static void mtk_adsp_mbox_shutdown(struct mbox_chan *chan) { struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox); + disable_irq(priv->irq); + /* Clear ADSP mbox command */ writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); @@ -139,8 +144,10 @@ static int mtk_adsp_mbox_probe(struct platform_device *pdev) if (irq < 0) return irq; + priv->irq = irq; ret = devm_request_threaded_irq(dev, irq, mtk_adsp_mbox_irq, - mtk_adsp_mbox_isr, IRQF_TRIGGER_NONE, + mtk_adsp_mbox_isr, + IRQF_TRIGGER_NONE | IRQF_NO_AUTOEN, dev_name(dev), mbox->chans); if (ret < 0) return ret; From fc5ce5606db5733e81ff6d99cb86856df2241136 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 May 2026 13:17:55 +0300 Subject: [PATCH 1223/3297] PCI: dwc: Fix signedness bug in fault injection test code [ Upstream commit 94ac934d2c054fba4a22d8dc84749094c5fa0ec0 ] The kstrtou32() function returns negative error code or zero on success. However, in this case "val" is a u32 and the function returns signed long, so negative error codes from kstrtou32() are returned as high positive values. Store the error code in an int instead. Fixes: d20ee8e2dbd6 ("PCI: dwc: Add debugfs based Error Injection support for DWC") Signed-off-by: Dan Carpenter Signed-off-by: Manivannan Sadhasivam Reviewed-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/agL-Uwfn26SI4Gb0@stanley.mountain Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-designware-debugfs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index afcc08efe2531..bc741d2261fbb 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -256,6 +256,7 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, u32 val, counter, vc_num, err_group, type_mask; int val_diff = 0; char *kern_buf; + int ret; err_group = err_inj_list[pdata->idx].err_inj_group; type_mask = err_inj_type_mask[err_group]; @@ -277,10 +278,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, return -EINVAL; } } else { - val = kstrtou32(kern_buf, 0, &counter); - if (val) { + ret = kstrtou32(kern_buf, 0, &counter); + if (ret) { kfree(kern_buf); - return val; + return ret; } } From 28ebd287a7fade131aa985d80023beb8b3a5952d Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Wed, 20 May 2026 00:38:55 +0200 Subject: [PATCH 1224/3297] perf build-id: Fix off-by-one bug when printing kernel/module build-id [ Upstream commit 017bca78e4d72b1ff027d368c20a1b2c654edaf7 ] When changing sprintf functions to snprintf, one byte got lost. Since snprintf ones do not handle the '\0' terminating character, the number of printed characters is 40, while sizeof(sbuild_id) is 41, including the terminating '\0' character. This makes the later check fail so that nothing is printed. Fix that. Before: [Michael@Carbon ~]$ perf buildid-list -k [Michael@Carbon ~]$ After: [Michael@Carbon ~]$ perf buildid-list -k a527806324d543c4bc3ff2f9c9519d494fed5f68 [Michael@Carbon ~]$ Fixes: fccaaf6fbbc59910 ("perf build-id: Change sprintf functions to snprintf") Signed-off-by: Michael Petlan Tested-by: Ian Rogers Cc: Ian Rogers Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-buildid-list.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index a91bbb34ac946..e0881b0ac38ff 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -61,7 +61,7 @@ static int sysfs__fprintf_build_id(FILE *fp) int ret; ret = sysfs__snprintf_build_id("/", sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); @@ -73,7 +73,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) int ret; ret = filename__snprintf_build_id(name, sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); From 45652323ce74b2df957837483257e409d25eafa6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 13 Jan 2026 15:29:02 -0800 Subject: [PATCH 1225/3297] perf inject: Add --convert-callchain option [ Upstream commit 92ea788d2af4e65ad7a144ccfff50667e9a0d227 ] There are applications not built with frame pointers, so DWARF is needed to get the stack traces. `perf record --call-graph dwarf` saves the stack and register data for each sample to get the stacktrace offline. But sometimes this data may have sensitive information and we don't want to keep them in the file. This new 'perf inject --convert-callchain' option creates the callchains and discards the stack and register after that. This saves storage space and processing time for the new data file. Of course, users should remove the original data file to not keep sensitive data around. :) The down side is that it cannot handle inlined callchain entries as they all have the same IPs. Maybe we can add an option to 'perf report' to look up inlined functions using DWARF - IIUC it doesn't require stack and register data. This is an example. $ perf record --call-graph dwarf -- perf test -w noploop $ perf report --stdio --no-children --percent-limit=0 > output-prev $ perf inject -i perf.data --convert-callchain -o perf.data.out $ perf report --stdio --no-children --percent-limit=0 -i perf.data.out > output-next $ diff -u output-prev output-next ... 0.23% perf ld-linux-x86-64.so.2 [.] _dl_relocate_object_no_relro | - ---elf_dynamic_do_Rela (inlined) - _dl_relocate_object_no_relro + ---_dl_relocate_object_no_relro _dl_relocate_object dl_main _dl_sysdep_start - _dl_start_final (inlined) _dl_start _start Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Stable-dep-of: 059e9100d82a ("perf event: Fix size of synthesized sample with branch stacks") Signed-off-by: Sasha Levin --- tools/perf/Documentation/perf-inject.txt | 5 + tools/perf/builtin-inject.c | 152 +++++++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index c972032f4ca0d..95dfdf39666ef 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt @@ -109,6 +109,11 @@ include::itrace.txt[] should be used, and also --buildid-all and --switch-events may be useful. +--convert-callchain:: + Parse DWARF callchains and convert them to usual callchains. This also + discards stack and register data from the samples. This will lose + inlined callchain entries. + :GMEXAMPLECMD: inject :GMEXAMPLESUBCMD: include::guestmount.txt[] diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a114b3fa1bea1..9606b4bf10979 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -122,6 +122,7 @@ struct perf_inject { bool in_place_update; bool in_place_update_dry_run; bool copy_kcore_dir; + bool convert_callchain; const char *input_name; struct perf_data output; u64 bytes_written; @@ -133,6 +134,7 @@ struct perf_inject { struct guest_session guest_session; struct strlist *known_build_ids; const struct evsel *mmap_evsel; + struct ip_callchain *raw_callchain; }; struct event_entry { @@ -396,6 +398,90 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } +static int perf_event__convert_sample_callchain(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine) +{ + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct callchain_cursor *cursor = get_tls_callchain_cursor(); + union perf_event *event_copy = (void *)inject->event_copy; + struct callchain_cursor_node *node; + struct thread *thread; + u64 sample_type = evsel->core.attr.sample_type; + u32 sample_size = event->header.size; + u64 i, k; + int ret; + + if (event_copy == NULL) { + inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE); + if (!inject->event_copy) + return -ENOMEM; + + event_copy = (void *)inject->event_copy; + } + + if (cursor == NULL) + return -ENOMEM; + + callchain_cursor_reset(cursor); + + thread = machine__find_thread(machine, sample->tid, sample->pid); + if (thread == NULL) + goto out; + + /* this will parse DWARF using stack and register data */ + ret = thread__resolve_callchain(thread, cursor, evsel, sample, + /*parent=*/NULL, /*root_al=*/NULL, + PERF_MAX_STACK_DEPTH); + thread__put(thread); + if (ret != 0) + goto out; + + /* copy kernel callchain and context entries */ + for (i = 0; i < sample->callchain->nr; i++) { + inject->raw_callchain->ips[i] = sample->callchain->ips[i]; + if (sample->callchain->ips[i] == PERF_CONTEXT_USER) { + i++; + break; + } + } + if (i == 0 || inject->raw_callchain->ips[i - 1] != PERF_CONTEXT_USER) + inject->raw_callchain->ips[i++] = PERF_CONTEXT_USER; + + node = cursor->first; + for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { + if (machine__kernel_ip(machine, node->ip)) + /* kernel IPs were added already */; + else if (node->ms.sym && node->ms.sym->inlined) + /* we can't handle inlined callchains */; + else + inject->raw_callchain->ips[i++] = node->ip; + + node = node->next; + } + + inject->raw_callchain->nr = i; + sample->callchain = inject->raw_callchain; + +out: + memcpy(event_copy, event, sizeof(event->header)); + + /* adjust sample size for stack and regs */ + sample_size -= sample->user_stack.size; + sample_size -= (hweight64(evsel->core.attr.sample_regs_user) + 1) * sizeof(u64); + sample_size += (sample->callchain->nr + 1) * sizeof(u64); + event_copy->header.size = sample_size; + + /* remove sample_type {STACK,REGS}_USER for synthesize */ + sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); + + perf_event__synthesize_sample(event_copy, sample_type, + evsel->core.attr.read_format, sample); + return perf_event__repipe_synth(tool, event_copy); +} + static struct dso *findnew_dso(int pid, int tid, const char *filename, const struct dso_id *id, struct machine *machine) { @@ -2280,6 +2366,15 @@ static int __cmd_inject(struct perf_inject *inject) /* Allow space in the header for guest attributes */ output_data_offset += gs->session->header.data_offset; output_data_offset = roundup(output_data_offset, 4096); + } else if (inject->convert_callchain) { + inject->tool.sample = perf_event__convert_sample_callchain; + inject->tool.fork = perf_event__repipe_fork; + inject->tool.comm = perf_event__repipe_comm; + inject->tool.exit = perf_event__repipe_exit; + inject->tool.mmap = perf_event__repipe_mmap; + inject->tool.mmap2 = perf_event__repipe_mmap2; + inject->tool.ordered_events = true; + inject->tool.ordering_requires_timestamps = true; } if (!inject->itrace_synth_opts.set) @@ -2332,6 +2427,23 @@ static int __cmd_inject(struct perf_inject *inject) perf_header__set_feat(&session->header, HEADER_BRANCH_STACK); } + + /* + * The converted data file won't have stack and registers. + * Update the perf_event_attr to remove them before writing. + */ + if (inject->convert_callchain) { + struct evsel *evsel; + + evlist__for_each_entry(session->evlist, evsel) { + evsel__reset_sample_bit(evsel, REGS_USER); + evsel__reset_sample_bit(evsel, STACK_USER); + evsel->core.attr.sample_regs_user = 0; + evsel->core.attr.sample_stack_user = 0; + evsel->core.attr.exclude_callchain_user = 0; + } + } + session->header.data_offset = output_data_offset; session->header.data_size = inject->bytes_written; perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc, @@ -2356,6 +2468,18 @@ static int __cmd_inject(struct perf_inject *inject) return ret; } +static bool evsel__has_dwarf_callchain(struct evsel *evsel) +{ + struct perf_event_attr *attr = &evsel->core.attr; + const u64 dwarf_callchain_flags = + PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_CALLCHAIN; + + if (!attr->exclude_callchain_user) + return false; + + return (attr->sample_type & dwarf_callchain_flags) == dwarf_callchain_flags; +} + int cmd_inject(int argc, const char **argv) { struct perf_inject inject = { @@ -2424,6 +2548,8 @@ int cmd_inject(int argc, const char **argv) OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", "guest mount directory under which every guest os" " instance has a subdir"), + OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, + "Generate callchains using DWARF and drop register/stack data"), OPT_END() }; const char * const inject_usage[] = { @@ -2439,6 +2565,9 @@ int cmd_inject(int argc, const char **argv) #ifndef HAVE_JITDUMP set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true); +#endif +#ifndef HAVE_LIBDW_SUPPORT + set_option_nobuild(options, 0, "convert-callchain", "NO_LIBDW=1", true); #endif argc = parse_options(argc, argv, options, inject_usage, 0); @@ -2597,6 +2726,28 @@ int cmd_inject(int argc, const char **argv) } } + if (inject.convert_callchain) { + struct evsel *evsel; + + if (inject.output.is_pipe || inject.session->data->is_pipe) { + pr_err("--convert-callchain cannot work with pipe\n"); + goto out_delete; + } + + evlist__for_each_entry(inject.session->evlist, evsel) { + if (!evsel__has_dwarf_callchain(evsel)) { + pr_err("--convert-callchain requires DWARF call graph.\n"); + goto out_delete; + } + } + + inject.raw_callchain = calloc(PERF_MAX_STACK_DEPTH, sizeof(u64)); + if (inject.raw_callchain == NULL) { + pr_err("callchain allocation failed\n"); + goto out_delete; + } + } + #ifdef HAVE_JITDUMP if (inject.jit_mode) { inject.tool.mmap2 = perf_event__repipe_mmap2; @@ -2627,5 +2778,6 @@ int cmd_inject(int argc, const char **argv) free(inject.itrace_synth_opts.vm_tm_corr_args); free(inject.event_copy); free(inject.guest_session.ev.event_buf); + free(inject.raw_callchain); return ret; } From 43078449ad6236d839e4d707954d2e36b10a6706 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sun, 17 May 2026 19:12:18 +0800 Subject: [PATCH 1226/3297] staging: most: video: avoid double free on video register failure [ Upstream commit 7cb1c5b32a2bfde961fff8d5204526b609bcb30a ] comp_register_videodev() allocates a video_device with video_device_alloc() and releases it if video_register_device() fails. This can double free the video_device when __video_register_device() reaches device_register() and that call fails: video_register_device() -> __video_register_device() -> device_register() fails -> put_device(&vdev->dev) -> v4l2_device_release() -> vdev->release(vdev) -> video_device_release(vdev) comp_register_videodev() -> video_device_release(mdev->vdev) Use video_device_release_empty() while registering the device so that registration failure paths do not free mdev->vdev through vdev->release(). comp_register_videodev() then releases mdev->vdev exactly once on failure. Restore video_device_release() after successful registration so the registered device keeps its normal lifetime handling. This issue was found by a static analysis tool I am developing. Fixes: eab231c0398a ("staging: most: v4l2-aim: remove unnecessary label err_vbi_dev") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260517111218.945796-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/most/video/video.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 32f71d9a9cf78..221295d8cac28 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -418,6 +418,7 @@ static int comp_register_videodev(struct most_video_dev *mdev) /* Fill the video capture device struct */ *mdev->vdev = comp_videodev_template; + mdev->vdev->release = video_device_release_empty; mdev->vdev->v4l2_dev = &mdev->v4l2_dev; mdev->vdev->lock = &mdev->lock; snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s", @@ -430,9 +431,13 @@ static int comp_register_videodev(struct most_video_dev *mdev) v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", ret); video_device_release(mdev->vdev); + return ret; } - return ret; + mdev->vdev->release = video_device_release; + + return 0; + } static void comp_unregister_videodev(struct most_video_dev *mdev) From 02d03c61e8a7b016956acb48e8a2512d16d87517 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 18:49:00 -0400 Subject: [PATCH 1227/3297] usb: host: max3421: Fix shift-out-of-bounds in max3421_hub_control() [ Upstream commit cff06b03b530ae1fe8a13e93a7848f2130e00fb4 ] The `max3421_hub_control()` function handles USB hub class requests to the virtual root hub. In the `default` branches of both the `ClearPortFeature` and `SetPortFeature` switch statements, it modifies `max3421_hcd->port_status` by left shifting 1 by the request's `value` parameter. However, it does not validate whether this shift will exceed the width of `port_status`. So if a malicious userspace task with access to the root hub via /dev/bus/usb/.../001 issues a USBDEVFS_CONTROL ioctl with `wValue` greater than or equal to 32, the left shift operation invokes shift-out-of-bounds undefined behavior. This results in arbitrary bit corruption of `port_status`, including the normally-immutable change bits, which can bypass internal state checks and confuse the hub status. Fix this by rejecting requests whose `value` exceeds the shift width before performing the shift. This issue was found using a KLEE-based symbolic execution tool for kernel drivers that I'm currently developing. Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.") Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20260518224901.1887013-1-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/host/max3421-hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 4b5f03f683f77..823dc61d31e86 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1694,6 +1694,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, !pdata->vbus_active_level); fallthrough; default: + if (value >= 32) + goto error; max3421_hcd->port_status &= ~(1 << value); } break; @@ -1747,6 +1749,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, max3421_reset_port(hcd); fallthrough; default: + if (value >= 32) + goto error; if ((max3421_hcd->port_status & USB_PORT_STAT_POWER) != 0) max3421_hcd->port_status |= (1 << value); From 2b2b1613b734bec8097a6701e98c6496630c16e8 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 18:49:02 -0400 Subject: [PATCH 1228/3297] usb: host: max3421: Reject hub port requests for non-existent ports [ Upstream commit 11b5c101e2fd206104b05bc92554d356989423a8 ] The `max3421_hub_control()` function handles USB hub class requests to the virtual root hub. The `GetPortStatus` case correctly rejects requests with `index != 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 `index != 1` rejection to both cases, matching the existing behavior of `GetPortStatus`. Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.") Suggested-by: Alan Stern Reviewed-by: Alan Stern Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20260518224901.1887013-3-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/host/max3421-hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 823dc61d31e86..8fbbf9e195b0a 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1685,6 +1685,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, case ClearHubFeature: break; case ClearPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_SUSPEND: break; @@ -1728,6 +1730,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, break; case SetPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_LINK_STATE: case USB_PORT_FEAT_U1_TIMEOUT: From 92f8b1d83383454a8bbf2f7bf3bfd0a856c8ce46 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Fri, 8 May 2026 05:59:57 +0000 Subject: [PATCH 1229/3297] perf test amd ibs: Fix incorrect kernel version check [ Upstream commit 0b97e92393a178765ee1ea01fe5087efece2c425 ] "AMD IBS sample period" unit test is getting skipped on kernel v7.x. Fix the kernel version >= v6.15 check. Fixes: 21fb366b2f457611 ("perf test amd: Skip amd-ibs-period test on kernel < v6.15") Signed-off-by: Ravi Bangoria Acked-by: Namhyung Kim Cc: Ananth Narayan Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Manali Shukla Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Cc: Santosh Shukla Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/arch/x86/tests/amd-ibs-period.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c index 223e059e04deb..986ac34d218fb 100644 --- a/tools/perf/arch/x86/tests/amd-ibs-period.c +++ b/tools/perf/arch/x86/tests/amd-ibs-period.c @@ -933,7 +933,7 @@ static bool kernel_v6_15_or_newer(void) endptr++; minor = strtol(endptr, NULL, 10); - return major >= 6 && minor >= 15; + return major > 6 || (major == 6 && minor >= 15); } int test__amd_ibs_period(struct test_suite *test __maybe_unused, From d72ece584c44829c4ebcecf54d22e1dec10bf026 Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Sat, 11 Apr 2026 12:20:25 +0200 Subject: [PATCH 1230/3297] gpib: Fix inappropriate ioctl error return [ Upstream commit 70ea440324e8c1a10837f721352f5bd469c85007 ] The driver was returning -ENOTTY in the case the ioctl command was not recognised. Change it to -EBADRQC. Fixes: 9dde4559e939 ("staging: gpib: Add GPIB common core driver") Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260411102025.2000-3-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/gpib/common/gpib_os.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/gpib/common/gpib_os.c b/drivers/staging/gpib/common/gpib_os.c index baa2fea5ebf7e..6bae025434e5c 100644 --- a/drivers/staging/gpib/common/gpib_os.c +++ b/drivers/staging/gpib/common/gpib_os.c @@ -613,7 +613,7 @@ long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) unsigned int minor = iminor(file_inode(filep)); struct gpib_board *board; struct gpib_file_private *file_priv = filep->private_data; - long retval = -ENOTTY; + long retval = -EBADRQC; if (minor >= GPIB_MAX_NUM_BOARDS) { pr_err("gpib: invalid minor number of device file\n"); @@ -806,7 +806,6 @@ long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) mutex_unlock(&board->big_gpib_mutex); return write_ioctl(file_priv, board, arg); default: - retval = -ENOTTY; goto done; } From 3d5e4cc0d9dce79b0429da3134ac7b072ab9009f Mon Sep 17 00:00:00 2001 From: James Kim Date: Sun, 3 May 2026 19:11:31 +0900 Subject: [PATCH 1231/3297] char: tlclk: fix use-after-free in tlclk_cleanup() [ Upstream commit bbf003b7794d6ad6f939fdd29f1f1bde8ac554c1 ] This patch improves the module cleanup process in the tlclk driver to prevent potential use-after-free and race conditions. Currently, the file_operations structure does not specify the .owner field, which could allow the module to be unloaded while user-space processes are still interacting with the device. Additionally, the tlclk_cleanup() function frees the alarm_events memory before ensuring that blocked processes in the waitqueue are fully awakened and that the switchover_timer has completed. To address these cases, this patch: - Sets '.owner = THIS_MODULE' in tlclk_fops to safely defer module unloading while the device is in use. - Updates tlclk_cleanup() to explicitly wake up all blocked readers (wake_up_all), properly release hardware I/O regions, and safely delete the timer (timer_delete_sync) prior to freeing memory. Fixes: 1a80ba882730 ("[PATCH] Telecom Clock Driver for MPCBL0010 ATCA computer blade") Signed-off-by: James Kim Link: https://patch.msgid.link/20260503101131.64219-1-james010kim@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/char/tlclk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index b381ea7e85d2f..d08948cdb2b89 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -264,6 +264,7 @@ static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count, } static const struct file_operations tlclk_fops = { + .owner = THIS_MODULE, .read = tlclk_read, .open = tlclk_open, .release = tlclk_release, @@ -837,6 +838,9 @@ static void __exit tlclk_cleanup(void) misc_deregister(&tlclk_miscdev); unregister_chrdev(tlclk_major, "telco_clock"); + got_event = 1; + wake_up_all(&wq); + release_region(TLCLK_BASE, 8); timer_delete_sync(&switchover_timer); kfree(alarm_events); From fdee9f207a48ce204ec6cfceaa1459d2473600a5 Mon Sep 17 00:00:00 2001 From: Adam Crosser Date: Fri, 24 Apr 2026 19:37:47 +0700 Subject: [PATCH 1232/3297] gpib: fix double decrement of descriptor_busy in command_ioctl() [ Upstream commit c4faab452b3c1ada003d49c477609dd80523b9bf ] commit d1857f8296dc ("gpib: fix use-after-free in IO ioctl handlers") introduced a descriptor_busy reference counter to pin struct gpib_descriptor across IO ioctl operations. In command_ioctl(), the error path inside the loop decrements descriptor_busy and breaks, but execution then falls through to the unconditional decrement after the loop, underflowing the counter to -1. This re-enables the use-after-free that the original fix was meant to prevent: a concurrent close_dev_ioctl() sees descriptor_busy == 0 on an actively-used descriptor and frees it. Remove the early decrement from the error path. The post-loop decrement already handles all exit paths, matching the correct pattern used in read_ioctl() and write_ioctl(). Fixes: d1857f8296dc ("gpib: fix use-after-free in IO ioctl handlers") Reported-by: Ruikai Peng Signed-off-by: Adam Crosser Link: https://patch.msgid.link/20260424123750.855863-1-adam.r.crosser@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/staging/gpib/common/gpib_os.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/gpib/common/gpib_os.c b/drivers/staging/gpib/common/gpib_os.c index 6bae025434e5c..a2bed6bd757a0 100644 --- a/drivers/staging/gpib/common/gpib_os.c +++ b/drivers/staging/gpib/common/gpib_os.c @@ -1017,7 +1017,6 @@ static int command_ioctl(struct gpib_file_private *file_priv, userbuf += bytes_written; if (retval < 0) { atomic_set(&desc->io_in_progress, 0); - atomic_dec(&desc->descriptor_busy); wake_up_interruptible(&board->wait); break; From 96c8f732cadf7885ca05129f18b9ae3024c7ff92 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 20 May 2026 10:29:47 +0100 Subject: [PATCH 1233/3297] clk: renesas: rzg2l: Rename iterator in for_each_mod_clock() to avoid shadowing [ Upstream commit 1f10c4509649e7c5f6d5d3acccf3ef6fbb5cdd46 ] Rename the internal loop iterator variable in the for_each_mod_clock() macro from 'i' to '__i'. The current naming conflicts with local loop variables named 'i' inside code blocks that utilize the macro, triggering compiler warnings due to variable shadowing: drivers/clk/renesas/rzg2l-cpg.c:1494:36: warning: declaration of `i` shadows a previous local [-Wshadow] 1494 | for (unsigned int i = 0; i < clk->num_shared_mstop_clks; i++) Using a unique identifier for the macro-internal iterator resolves the shadowing warnings globally across all macro expansions. Fixes: 3fd4a8bb4b63 ("clk: renesas: rzg2l: Add macro to loop through module clocks") Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260520092947.70596-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- drivers/clk/renesas/rzg2l-cpg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index edad47ca33ec5..769aae1a9fd35 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -1228,10 +1228,10 @@ struct mod_clock { #define to_mod_clock(_hw) container_of(_hw, struct mod_clock, hw) #define for_each_mod_clock(mod_clock, hw, priv) \ - for (unsigned int i = 0; (priv) && i < (priv)->num_mod_clks; i++) \ - if ((priv)->clks[(priv)->num_core_clks + i] == ERR_PTR(-ENOENT)) \ + for (unsigned int __i = 0; (priv) && __i < (priv)->num_mod_clks; __i++) \ + if ((priv)->clks[(priv)->num_core_clks + __i] == ERR_PTR(-ENOENT)) \ continue; \ - else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + i])) && \ + else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + __i])) && \ ((mod_clock) = to_mod_clock(hw))) /* Need to be called with a lock held to avoid concurrent access to mstop->usecnt. */ From de93ef83f99e8d6d477da107703e76ddc079a1b1 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Mon, 4 May 2026 20:43:20 +0530 Subject: [PATCH 1234/3297] powerpc tools perf: Initialize error code in auxtrace_record_init function [ Upstream commit 789d22d77879eabb042627f6627cdb62787bc142 ] perf trace record fails some cases in powerpc # perf test "perf trace record and replay" 128: perf trace record and replay : FAILED! # perf trace record sleep 1 # echo $? 32 This is happening because of non-zero err value from auxtrace_record__init() function. static int record__auxtrace_init(struct record *rec) { int err; if ((rec->opts.auxtrace_snapshot_opts || rec->opts.auxtrace_sample_opts) && record__threads_enabled(rec)) { pr_err("AUX area tracing options are not available in parallel streaming mode.\n"); return -EINVAL; } if (!rec->itr) { rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) return err; } Here "int err" is not initialised. The code expects "err" to be set from auxtrace_record__init() function. Update auxtrace_record__init() in arch/powerpc/util/auxtrace.c to clear err value in the beginning. - Clear err value in beginning of function. Any fail later will set appropriate return code to err. - Even if we haven't found any event for auxtrace, perf record should continue for other events. NULL return will indicate that there is no auxtrace record initialized. - Not having "err" set here will affect monitoring of other events also because perf record will fail seeing random value in err. Set err to -EINVAL before invoking auxtrace_record__init() in builtin-record.c With the fix, # perf trace record sleep 1 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.033 MB perf.data (228 samples) ] Fixes: 1dbfaf94cf66ec4b ("perf powerpc: Add basic CONFIG_AUXTRACE support for VPA pmu on powerpc") Reviewed-by: Adrian Hunter Signed-off-by: Athira Rajeev Acked-by: Namhyung Kim Cc: Athira Rajeev Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: linuxppc-dev@lists.ozlabs.org Cc: Madhavan Srinivasan Cc: Michael Petlan Cc: Shivani Nittor Cc: Tanushree Shah Cc: Tejas Manhas Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/arch/powerpc/util/auxtrace.c | 6 ++++++ tools/perf/builtin-record.c | 1 + 2 files changed, 7 insertions(+) diff --git a/tools/perf/arch/powerpc/util/auxtrace.c b/tools/perf/arch/powerpc/util/auxtrace.c index 62c6f67f1bbe6..57f2910c0b019 100644 --- a/tools/perf/arch/powerpc/util/auxtrace.c +++ b/tools/perf/arch/powerpc/util/auxtrace.c @@ -70,6 +70,12 @@ struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, struct evsel *pos; int found = 0; + /* + * Set err value to zero here. Any fail later + * will set appropriate return code to err. + */ + *err = 0; + evlist__for_each_entry(evlist, pos) { if (strstarts(pos->name, "vpa_dtl")) { found = 1; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 18e620113247d..c82b9720c9296 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -867,6 +867,7 @@ static int record__auxtrace_init(struct record *rec) } if (!rec->itr) { + err = -EINVAL; rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) return err; From be62602fe0797f650af9c49d1b445e1453d12f6c Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Apr 2026 17:39:34 +0800 Subject: [PATCH 1235/3297] PCI: qcom: Disable ASPM L0s for SA8775P [ Upstream commit 29f692985819f4089f02a86e151a72f6d4cdd90d ] Due to a hardware issue, L0s is not properly supported by the PCIe controller on the SA8775p SoC. If enabled, the L0s to L0 transition triggers below correctable AER errors and may also affect link stability: pcieport 0000:00:00.0: PME: Signaling with IRQ 332 pcieport 0000:00:00.0: AER: enabled with IRQ 332 pcieport 0000:00:00.0: AER: Correctable error message received from 0000:01:00.0 pci 0000:01:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) pci 0000:01:00.0: device [17cb:1103] error status/mask=00001000/0000e000 pci 0000:01:00.0: [12] Timeout pcieport 0000:00:00.0: AER: Multiple Correctable error message received from 0000:01:00.0 pcieport 0000:00:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) pcieport 0000:00:00.0: device [17cb:0115] error status/mask=00001000/0000e000 pcieport 0000:00:00.0: [12] Timeout Hence, disable L0s for the SA8775p SoC to allow it to properly function by sacrificing a little bit of power saving. Fixes: 58d0d3e032b3 ("PCI: qcom-ep: Add support for SA8775P SOC") Assisted-by: Claude:claude-4-6-sonnet Signed-off-by: Shawn Guo [mani: commit log, corrected fixes tag] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260419093934.1223027-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-qcom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index a70911a956753..fbdb5a9264785 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1438,6 +1438,7 @@ static const struct qcom_pcie_cfg cfg_1_9_0 = { static const struct qcom_pcie_cfg cfg_1_34_0 = { .ops = &ops_1_9_0, .override_no_snoop = true, + .no_l0s = true, }; static const struct qcom_pcie_cfg cfg_2_1_0 = { From fd6b65ade11908b46d3d972c95bc68e8022dc809 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 14:37:39 -0300 Subject: [PATCH 1236/3297] perf header: Sanity check HEADER_EVENT_DESC attr.size before swap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 944f65c8b8231d26d4db6be67bcb641142603cb4 ] read_event_desc() reads nre (event count), sz (attr size), and nr (IDs per event) from the file and uses them to control allocations and loops without validating them against the section size. A crafted perf.data could trigger large allocations or many loop iterations before __do_read() eventually rejects the reads. Add bounds checks in read_event_desc(): - Reject sz smaller than PERF_ATTR_SIZE_VER0. - Require at least one event (nre > 0). - Check that nre events fit in the remaining section, using the minimum per-event footprint of sz + sizeof(u32). - Pre-swap attr->size to native byte order, then reject values below PERF_ATTR_SIZE_VER0 or above sz before calling perf_event__attr_swap() to prevent heap out-of-bounds access. - Handle ABI0 (attr.size == 0): substitute PERF_ATTR_SIZE_VER0, and on native-endian files write the value back so free_event_desc() does not treat the zero as its end-of-array sentinel (it iterates while attr.size != 0). The swap path skips the write-back — perf_event__attr_swap() has its own ABI0 fallback that sets VER0 after swapping. - Check that nr IDs fit in the remaining section before allocating. Fixes: b30b61729246 ("perf tools: Fix a problem when opening old perf.data with different byte order") Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/header.c | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4e12be579140a..131fdbe9cefda 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1919,9 +1919,28 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (do_read_u32(ff, &nre)) goto error; + /* Size of each of the nre attributes. */ if (do_read_u32(ff, &sz)) goto error; + /* + * Require at least one event with an attr no smaller than the + * first published struct, and reject sz values where + * sz + sizeof(u32) would overflow size_t (possible on 32-bit) + * or nre == UINT32_MAX where nre + 1 wraps to 0 in the calloc. + * + * The minimum section footprint per event is sz bytes for the + * attr plus a u32 for the id count, check that nre events fit. + */ + if (!nre || sz < PERF_ATTR_SIZE_VER0 || + sz > ff->size || (size_t)sz > SIZE_MAX - sizeof(u32) || + nre == UINT32_MAX || + nre > (ff->size - ff->offset) / (sz + sizeof(u32))) { + pr_err("Invalid HEADER_EVENT_DESC: nre=%u sz=%u (min %d)\n", + nre, sz, PERF_ATTR_SIZE_VER0); + goto error; + } + /* buffer to hold on file attr struct */ buf = malloc(sz); if (!buf) @@ -1937,6 +1956,9 @@ static struct evsel *read_event_desc(struct feat_fd *ff) msz = sz; for (i = 0, evsel = events; i < nre; evsel++, i++) { + struct perf_event_attr *attr = buf; + u32 attr_size; + evsel->core.idx = i; /* @@ -1946,6 +1968,32 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (__do_read(ff, buf, sz)) goto error; + /* Reject before attr_swap to prevent OOB via bswap_safe() */ + attr_size = ff->ph->needs_swap ? bswap_32(attr->size) : attr->size; + /* ABI0: size == 0 means the producer didn't set it */ + if (!attr_size) { + attr_size = PERF_ATTR_SIZE_VER0; + /* + * Write back so free_event_desc() doesn't + * treat this event as the end-of-array sentinel + * (it iterates while attr.size != 0). + * + * Only for native — the swap path must NOT + * write native-endian VER0 here because + * perf_event__attr_swap() would re-swap it + * to 0x40000000, defeating bswap_safe() bounds. + * perf_event__attr_swap() has its own ABI0 + * fallback that sets VER0 after swapping. + */ + if (!ff->ph->needs_swap) + attr->size = attr_size; + } + if (attr_size < PERF_ATTR_SIZE_VER0 || attr_size > sz) { + pr_err("Event %d attr.size (%u) invalid (min: %d, max: %u)\n", + i, attr_size, PERF_ATTR_SIZE_VER0, sz); + goto error; + } + if (ff->ph->needs_swap) perf_event__attr_swap(buf); @@ -1967,6 +2015,12 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (!nr) continue; + /* Prevent oversized allocation from crafted nr */ + if (nr > (ff->size - ff->offset) / sizeof(*id)) { + pr_err("Event %d: id count %u exceeds remaining section\n", i, nr); + goto error; + } + id = calloc(nr, sizeof(*id)); if (!id) goto error; From f835b69fbeaeb7011c0729df8194bf9ee3283040 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:26 +0200 Subject: [PATCH 1237/3297] iio: light: si1133: reset counter to prevent race condition [ Upstream commit 0a5f45ed2342aabae1e32c72558d15be28940a95 ] Sashiko reported a potential race condition happening when the driver returns an errno after a timeout in the si1133_command() function. The premature exit causes the hardware and software counters to become out of sync by not updating data->rsp_seq, therefore the internal hardware counter keeps incrementing. Fix this by adding a call to si1133_cmd_reset_counter() before returning from timeout. Fixes: e01e7eaf37d8 ("iio: light: introduce si1133") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/message/20260428-si1133-checkup-v2-5-70ad14bfefe2%40gmail.com Assisted-by: gemini:gemini-3.1-pro-preview Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/light/si1133.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index 44fa152dbd24c..c88c79202be2e 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -427,6 +427,11 @@ static int si1133_command(struct si1133_data *data, u8 cmd) dev_warn(dev, "Failed to read command 0x%02x, ret=%d\n", cmd, err); + /* + * Reset counter on err to prevent software and hardware + * counters being out of sync. + */ + si1133_cmd_reset_counter(data); goto out; } } From 6ec473b3603426af39f9e6a5ecbd3cc6f13214eb Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Tue, 5 May 2026 09:31:27 +0200 Subject: [PATCH 1238/3297] iio: light: si1133: prevent race condition on timeout [ Upstream commit 8c50a95ceb230d17801758a9e41ffbbbe46f8b4d ] Sashiko reported a bug where the si1133_command exits on timeout without halting the sensor or masking the interrupt. If the sensor completes the command later, any subsequent command to the sensor will cause the IRQ handler to complete immediately, returning stale data to the driver all while the command hasn't finished yet, shifting all potential reads in the future. Fix this by masking the IRQ if wait_for_completion_timeout() fails. When initiating a new command, do a dummy read of the IRQ_STATUS register and turn the IRQ back on. Fixes: e01e7eaf37d8 ("iio: light: introduce si1133") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/message/20260428-si1133-checkup-v2-5-70ad14bfefe2%40gmail.com Assisted-by: gemini:gemini-3.1-pro-preview Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/light/si1133.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index c88c79202be2e..bf7bf0f1631d4 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -395,8 +395,14 @@ static int si1133_command(struct si1133_data *data, u8 cmd) expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR; - if (cmd == SI1133_CMD_FORCE) + if (cmd == SI1133_CMD_FORCE) { + /* Flush pending IRQs from a previous timeout. */ + regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &resp); + regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, + SI1133_IRQ_CHANNEL_ENABLE); + reinit_completion(&data->completion); + } err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd); if (err) { @@ -409,6 +415,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) /* wait for irq */ if (!wait_for_completion_timeout(&data->completion, msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) { + regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, 0); err = -ETIMEDOUT; goto out; } From 3d57672119525c59ed73ea21accb001ccbcd6cfd Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Fri, 15 May 2026 12:28:23 +0200 Subject: [PATCH 1239/3297] iio: magnetometer: ak8975: fix potential kernel stack memory leak [ Upstream commit a9a00d727b7bbc5e913a919530a9dd468935bf95 ] Currently in the AK8975 driver there are four instances where potential uninitialized kernel stack memory leaks can occur. If i2c_smbus_read_i2c_block_data_or_emulated() returns a value less than the size of the buffer, uninitialized bytes are retained in the buffer and later the buffer is passed on to IIO buffers, potentially leaking memory to userspace. Fix this by adding checks whether the return value of the function is equal to the size of the buffer and subsequently if the value is lesser than zero to distinguish from a returned error code. Fixes: bc11ca4a0b84 ("iio:magnetometer:ak8975: triggered buffer support") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260513-ak8975-fix-v1-1-104ea605dd54%40gmail.com Signed-off-by: Joshua Crofts Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/magnetometer/ak8975.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index cc1fb1e633339..0e9bb3ead61a7 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -495,6 +495,10 @@ static int ak8975_who_i_am(struct i2c_client *client, dev_err(&client->dev, "Error reading WIA\n"); return ret; } + if (ret != sizeof(wia_val)) { + dev_err(&client->dev, "Error reading WIA\n"); + return -EIO; + } if (wia_val[0] != AK8975_DEVICE_ID) return -ENODEV; @@ -619,6 +623,10 @@ static int ak8975_setup(struct i2c_client *client) dev_err(&client->dev, "Not able to read asa data\n"); return ret; } + if (ret != sizeof(data->asa)) { + dev_err(&client->dev, "Error reading asa data\n"); + return -EIO; + } /* After reading fuse ROM data set power-down mode */ ret = ak8975_set_mode(data, POWER_DOWN); @@ -758,6 +766,10 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) sizeof(rval), (u8*)&rval); if (ret < 0) goto exit; + if (ret != sizeof(rval)) { + ret = -EIO; + goto exit; + } /* Read out ST2 for release lock on measurment data. */ ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST2]); @@ -874,6 +886,8 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) (u8 *)fval); if (ret < 0) goto unlock; + if (ret != sizeof(fval)) + goto unlock; mutex_unlock(&data->lock); From 9ac3675bf875792dced45efbf47116719a7c097b Mon Sep 17 00:00:00 2001 From: Guilherme Ivo Bozi Date: Tue, 12 May 2026 21:31:50 -0300 Subject: [PATCH 1240/3297] iio: adc: xilinx-ams: fix out-of-bounds channel lookup in event handling [ Upstream commit 947eb6f0a274f8b15a0248051a65b069effd5057 ] ams_event_to_channel() may return a pointer past the end of dev->channels when no matching scan_index is found. This can lead to invalid memory access in ams_handle_event(). Add a bounds check in ams_event_to_channel() and return NULL when no channel is found. Also guard the caller to safely handle this case. Fixes: d5c70627a794 ("iio: adc: Add Xilinx AMS driver") Signed-off-by: Guilherme Ivo Bozi Reviewed-by: Salih Erim Tested-by: Salih Erim Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/adc/xilinx-ams.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 124470c925297..6191cd1b29a51 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -871,6 +871,9 @@ static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, if (dev->channels[i].scan_index == scan_index) break; + if (i == dev->num_channels) + return NULL; + return &dev->channels[i]; } @@ -1012,6 +1015,8 @@ static void ams_handle_event(struct iio_dev *indio_dev, u32 event) const struct iio_chan_spec *chan; chan = ams_event_to_channel(indio_dev, event); + if (!chan) + return; if (chan->type == IIO_TEMP) { /* From 1cddef80a180af74c33d2f26c962ce92b60fded4 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Tue, 5 May 2026 23:16:31 +0530 Subject: [PATCH 1241/3297] iio: accel: mma8452: handle I2C read error(s) in mma8452_read() [ Upstream commit 5bdff291d20c31b365d9ddfe9c426fbfb41da5bb ] Currently, If i2c_smbus_read_i2c_block_data() fails but mma8452_set_runtime_pm_state() succeeds, mma8452_read() returns 0. As a result, the caller mma8452_read_raw() assumes the read was successful and proceeds to use a buffer containing uninitialized stack memory. Add proper checking of the I2C read return value and propagate errors to the caller. Fixes: 96c0cb2bbfe0 ("iio: mma8452: add support for runtime power management") Signed-off-by: Sanjay Chitroda Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/accel/mma8452.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 15172ba2972c5..cefc7cf4bd835 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -252,6 +252,8 @@ static int mma8452_read(struct mma8452_data *data, __be16 buf[3]) ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *)buf); + if (ret < 0) + return ret; ret = mma8452_set_runtime_pm_state(data->client, false); From 0147c544cbc6e14cc2d5624ae90a416c85353be3 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Fri, 22 May 2026 14:34:12 +0200 Subject: [PATCH 1242/3297] iio: tcs3472: power down chip on probe failure [ Upstream commit b39f3bb9f5580d19bc0c10dea9224d75fed45f1d ] If tcs3472_probe() fails after enabling the chip (by writing PON | AEN to the ENABLE register), the error paths return without powering down the device. Add an 'error_powerdown' label at the end of the cleanup chain that calls tcs3472_powerdown() to power down the chip. The existing label cascade is rerouted to fall through to the new label. Move tcs3472_powerdown() above tcs3472_probe() so the probe can call it without a forward declaration. Found by code inspection while reviewing the probe error paths in preparation for the devm_ conversion. Fixes: eb869ade30a6 ("iio: Add tcs3472 color light sensor driver") Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin --- drivers/iio/light/tcs3472.c | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 12429a3261b38..849ca7885d719 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -440,6 +440,23 @@ static const struct iio_info tcs3472_info = { .attrs = &tcs3472_attribute_group, }; +static int tcs3472_powerdown(struct tcs3472_data *data) +{ + int ret; + u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, + data->enable & ~enable_mask); + if (!ret) + data->enable &= ~enable_mask; + + mutex_unlock(&data->lock); + + return ret; +} + static int tcs3472_probe(struct i2c_client *client) { struct tcs3472_data *data; @@ -513,7 +530,7 @@ static int tcs3472_probe(struct i2c_client *client) ret = iio_triggered_buffer_setup(indio_dev, NULL, tcs3472_trigger_handler, NULL); if (ret < 0) - return ret; + goto error_powerdown; if (client->irq) { ret = request_threaded_irq(client->irq, NULL, @@ -536,23 +553,8 @@ static int tcs3472_probe(struct i2c_client *client) free_irq(client->irq, indio_dev); buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); - return ret; -} - -static int tcs3472_powerdown(struct tcs3472_data *data) -{ - int ret; - u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON; - - mutex_lock(&data->lock); - - ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, - data->enable & ~enable_mask); - if (!ret) - data->enable &= ~enable_mask; - - mutex_unlock(&data->lock); - +error_powerdown: + tcs3472_powerdown(data); return ret; } From 26fa946925a094f9cca28c621a82eaa1faa495c2 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Fri, 29 May 2026 00:20:51 -0400 Subject: [PATCH 1243/3297] clk: at91: keep securam node alive while mapping it [ Upstream commit 22fa1c39ba6fe726b547c877c924379b7fee260a ] pmc_register_ops() gets an owned reference to the "atmel,sama5d2-securam" node with of_find_compatible_node(). The success path dropped that reference before passing the node to of_iomap(), leaving of_iomap() to consume a node pointer after the caller had released its reference. Move of_node_put() after of_iomap() so the node remains referenced for the mapping operation. The unavailable-node error path already releases the reference. Fixes: 4d21be864092 ("clk: at91: pmc: execute suspend/resume only for backup mode") Signed-off-by: Yuho Choi Reviewed-by: Alexandre Belloni Link: https://patch.msgid.link/20260529042051.1626978-1-dbgh9129@gmail.com Signed-off-by: Claudiu Beznea Signed-off-by: Sasha Levin --- drivers/clk/at91/pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index acf780a815890..4ef4f7b9d0bd1 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -177,9 +177,9 @@ static int __init pmc_register_ops(void) of_node_put(np); return -ENODEV; } - of_node_put(np); at91_pmc_backup_suspend = of_iomap(np, 0); + of_node_put(np); if (!at91_pmc_backup_suspend) { pr_warn("%s(): unable to map securam\n", __func__); return -ENOMEM; From 097fcf945d93a8126d807ba64710443da9db20f6 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 17:01:50 -0700 Subject: [PATCH 1244/3297] HID: logitech-hidpp: remove excess kernel-doc member in hidpp_scroll_counter [ Upstream commit f22a5db8a7d38152556f230d6d68e59dbc27971b ] The @dev member described in the kernel-doc does not exist in the struct. Remove the stale entry. Fixes: 0610430e3dea ("HID: logitech-hidpp: add input_device ptr to struct hidpp_device") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/hid-logitech-hidpp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 2eb67d0caebb2..4ba652ae54765 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -164,7 +164,6 @@ struct hidpp_battery { /** * struct hidpp_scroll_counter - Utility class for processing high-resolution * scroll events. - * @dev: the input device for which events should be reported. * @wheel_multiplier: the scalar multiplier to be applied to each wheel event * @remainder: counts the number of high-resolution units moved since the last * low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should From a31893206588374d7d16fad387189d8165c7efd3 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Thu, 30 Apr 2026 14:30:13 +0200 Subject: [PATCH 1245/3297] fs/ntfs3: add bounds check to run_get_highest_vcn() [ Upstream commit bb11485a87fbb2254b62cfed630b699d50e57da8 ] run_get_highest_vcn() parses a packed NTFS mapping-pairs buffer without any length bound, relying solely on a 0x00 terminator to stop. A crafted $LogFile UpdateMappingPairs record whose embedded attribute contains mapping-pairs runs without a terminator causes the function to read past the slab allocation, triggering a KASAN slab-out-of-bounds read on mount. The sibling function run_unpack() received an analogous bounds-check in commit b62567bca474 ("ntfs3: add buffer boundary checks to run_unpack()"), but run_get_highest_vcn() was missed. Take a run_buf_size parameter and reject any run header whose payload would extend past the buffer end, mirroring the pattern used by run_unpack(). The caller in fslog.c passes the remaining attribute bytes after the mapping-pairs offset. KASAN report (on mainline v7.1 merge window HEAD): BUG: KASAN: slab-out-of-bounds in run_get_highest_vcn+0x3c0/0x410 Read of size 1 at addr ffff88800e2d5400 by task mount/72 Call Trace: run_get_highest_vcn+0x3c0/0x410 do_action.isra.0+0x3ba8/0x7b50 log_replay+0x9ddd/0x10200 ntfs_loadlog_and_replay+0x4ad/0x610 ntfs_fill_super+0x214a/0x4540 Fixes: b62567bca474 ("ntfs3: add buffer boundary checks to run_unpack()") Signed-off-by: Jaeyeong Lee Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/fslog.c | 5 ++++- fs/ntfs3/ntfs_fs.h | 3 ++- fs/ntfs3/run.c | 9 +++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 5d9a2e1159af9..5aa3ac25aaeb7 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3362,7 +3362,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, memmove(Add2Ptr(attr, aoff), data, dlen); if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), - attr_run(attr), &t64)) { + attr_run(attr), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), + &t64)) { goto dirty_vol; } diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 7fb08bff02549..7e3d51b8d2ddf 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -821,7 +821,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, #else #define run_unpack_ex run_unpack #endif -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn); +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn); int run_clone(const struct runs_tree *run, struct runs_tree *new_run); /* Globals from super.c */ diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index de9081be462e5..66baaada02e9a 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1160,18 +1160,23 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, * Return the highest vcn from a mapping pairs array * it used while replaying log file. */ -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn) +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn) { + const u8 *run_last = run_buf + run_buf_size; u64 vcn64 = vcn; u8 size_size; - while ((size_size = *run_buf & 0xF)) { + while (run_buf < run_last && (size_size = *run_buf & 0xF)) { u8 offset_size = *run_buf++ >> 4; u64 len; if (size_size > 8 || offset_size > 8) return -EINVAL; + if (run_buf + size_size + offset_size > run_last) + return -EINVAL; + len = run_unpack_s64(run_buf, size_size, 0); if (!len) return -EINVAL; From c5388a957cf1da3c60d696358ec5c746bd35f5b7 Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Tue, 19 May 2026 12:42:20 -0700 Subject: [PATCH 1246/3297] fs/ntfs3: fix mount failure on 64K page-size kernels [ Upstream commit b7a9125cac8645245d2473c6c0a50e338280ad23 ] On 64K page-size kernels, mounting NTFS volumes smaller than ~650 MB fails with EINVAL. The issue is in log_replay(): the initial log page size probe uses PAGE_SIZE (65536) instead of DefaultLogPageSize (4096) when PAGE_SIZE exceeds DefaultLogPageSize * 2. This makes norm_file_page() require the $LogFile to be at least 50 * 65536 = 3.2 MB, but mkfs.ntfs creates a $LogFile of only ~1.5 MB for a typical 300 MB volume. norm_file_page() returns 0 and the mount is rejected with EINVAL. On 4K kernels the #if guard evaluates to true, so use_default=true is passed and DefaultLogPageSize (4096) is used, requiring only ~200 KB. This path works fine. Fix this by always passing use_default=true, which forces the initial probe to use DefaultLogPageSize regardless of the kernel's PAGE_SIZE. This is safe because, after reading the on-disk restart area, log_replay() already re-adjusts log->page_size to match the volume's actual sys_page_size. Also fix read_log_page() to pass log->page_size instead of PAGE_SIZE to ntfs_fix_post_read(), matching the actual buffer size. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Tested-by: Matthew R. Ochs Signed-off-by: Jamie Nguyen Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/fslog.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 5aa3ac25aaeb7..124198539b950 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -1170,7 +1170,7 @@ static int read_log_page(struct ntfs_log *log, u32 vbo, goto out; if (page_buf->rhdr.sign != NTFS_FFFF_SIGNATURE) - ntfs_fix_post_read(&page_buf->rhdr, PAGE_SIZE, false); + ntfs_fix_post_read(&page_buf->rhdr, log->page_size, false); if (page_buf != *buffer) memcpy(*buffer, Add2Ptr(page_buf, page_off), bytes); @@ -3798,11 +3798,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) log->l_size = log->orig_file_size = ni->vfs_inode.i_size; /* Get the size of page. NOTE: To replay we can use default page. */ -#if PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <= DefaultLogPageSize * 2 log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, true); -#else - log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, false); -#endif if (!log->page_size) { err = -EINVAL; goto out; From 038a0f01dda595dceda8f0c9c5172b1297281929 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Mon, 1 Jun 2026 19:42:15 +0530 Subject: [PATCH 1247/3297] drm/amd/display: Add missing kdoc for ALLM parameters [ Upstream commit d4c6ec729fb7a8bf8a27b19bd70a1b945ad93dac ] Add descriptions for the missing parameters for ALLMEnabled and ALLMValue to keep the function documentation synchronized with the function prototype mod_build_hf_vsif_infopacket(). Fixes the below with gcc W=1: ../display/modules/info_packet/info_packet.c:507 function parameter 'ALLMEnabled' not described in 'mod_build_hf_vsif_infopacket' ../display/modules/info_packet/info_packet.c:507 function parameter 'ALLMValue' not described in 'mod_build_hf_vsif_infopacket' Fixes: 3c2381b92cba ("drm/amd/display: add support for VSIP info packet") Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Aurabindo Pillai Cc: Wayne Lin Cc: Nicholas Kazlauskas Cc: Bhawanpreet Lakha Signed-off-by: Srinivasan Shanmugam Reviewed-by: Alex Hung Signed-off-by: Alex Deucher Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index b3d55cac35694..ae858a43c35f4 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -447,6 +447,8 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, * * @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.) * @info_packet: output structure where to store VSIF + * @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated + * @ALLMValue: ALLM bit value to advertise in HF-VSIF */ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet) From da40583823153f830e089b2fde08498cc25a38db Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Wed, 3 Jun 2026 17:59:57 +0800 Subject: [PATCH 1248/3297] thunderbolt: debugfs: Fix margining error counter buffer leak [ Upstream commit 503c5ae1e72aa9ed91925dafa3d82ee2e992747f ] When USB4 lane margining debugfs write support is enabled, margining_error_counter_write() copies the user input with validate_and_copy_from_user(). This allocates a temporary page that is only needed while parsing the requested error counter mode. The function currently returns without freeing that page. This leaks one page per write to the error_counter debugfs file, including successful writes and writes that later fail while taking the domain lock or because software margining is not enabled. Free the temporary page once parsing has completed, and also before returning from the invalid-input path. Fixes: 10904df3f20c ("thunderbolt: Improve software receiver lane margining") Signed-off-by: Xu Rao Signed-off-by: Mika Westerberg Signed-off-by: Sasha Levin --- drivers/thunderbolt/debugfs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 46a2a3550be71..71c9833563c70 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -956,7 +956,9 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, else if (!strcmp(buf, "stop")) error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP; else - return -EINVAL; + goto err_free; + + free_page((unsigned long)buf); scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { if (!margining->software) @@ -966,6 +968,10 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, } return count; + +err_free: + free_page((unsigned long)buf); + return -EINVAL; } static int margining_error_counter_show(struct seq_file *s, void *not_used) From a498063f95bddff3c17ad048fdbcf884b0eeb9e8 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 7 Apr 2026 11:27:55 +0800 Subject: [PATCH 1249/3297] dmaengine: imx-sdma: Refine spba bus searching in probe [ Upstream commit d52d42e2e5d9f13166e81ac837ebb023d1306e61 ] There are multi spba-busses for i.MX8M* platforms, if only search for the first spba-bus in DT, the found spba-bus may not the real bus of audio devices, which cause issue for sdma p2p case, as the sdma p2p script presently does not deal with the transactions involving two devices connected to the AIPS bus. Search the SDMA parent node first, which should be the AIPS bus, then search the child node whose compatible string is spba-bus under that AIPS bus for the above multi spba-busses case. Fixes: 8391ecf465ec ("dmaengine: imx-sdma: Add device to device support") Signed-off-by: Shengjiu Wang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260407032755.2758049-1-shengjiu.wang@nxp.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/imx-sdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index ed9e56de5a9b9..b04d3218ddc90 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -2376,7 +2376,9 @@ static int sdma_probe(struct platform_device *pdev) goto err_register; } - spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus"); + struct device_node *sdma_parent_np __free(device_node) = of_get_parent(np); + + spba_bus = of_get_compatible_child(sdma_parent_np, "fsl,spba-bus"); ret = of_address_to_resource(spba_bus, 0, &spba_res); if (!ret) { sdma->spba_start_addr = spba_res.start; From d78b16d078149327010f2df604c079d757300b9f Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:54 +0530 Subject: [PATCH 1250/3297] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional [ Upstream commit cc6049bd3fa8501ee27042df469a19ed69cf406d ] On Tegra264, GPCDMA reset control is not exposed to Linux and is handled by the boot firmware. Although reset was not exposed in Tegra234 as well, the firmware supported a dummy reset which just returns success on reset without doing an actual reset. This is also not supported in Tegra264 BPMP. Therefore mark 'reset' and 'reset-names' properties as required only for devices prior to Tegra264. This also necessitates that the Tegra264 compatible be standalone and cannot have the fallback compatible of Tegra186. Since there is no functional impact, we keep reset as required for Tegra234 to avoid breaking the ABI. Fixes: bb8c97571db5 ("dt-bindings: dma: Add Tegra264 compatible string") Signed-off-by: Akhil R Acked-by: Rob Herring (Arm) Acked-by: Thierry Reding Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260331102303.33181-2-akhilrajeev@nvidia.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml index 0dabe9bbb219b..64f1e9d9896df 100644 --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml @@ -15,16 +15,14 @@ maintainers: - Jon Hunter - Rajesh Gumasta -allOf: - - $ref: dma-controller.yaml# - properties: compatible: oneOf: - - const: nvidia,tegra186-gpcdma + - enum: + - nvidia,tegra264-gpcdma + - nvidia,tegra186-gpcdma - items: - enum: - - nvidia,tegra264-gpcdma - nvidia,tegra234-gpcdma - nvidia,tegra194-gpcdma - const: nvidia,tegra186-gpcdma @@ -60,12 +58,23 @@ required: - compatible - reg - interrupts - - resets - - reset-names - "#dma-cells" - iommus - dma-channel-mask +allOf: + - $ref: dma-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - nvidia,tegra186-gpcdma + then: + required: + - resets + - reset-names + additionalProperties: false examples: From 93f3e84fc74e660cdbab507fb63faf449d860c25 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Thu, 28 May 2026 14:23:55 +0800 Subject: [PATCH 1251/3297] perf: Fix off-by-one stack buffer overflow in kallsyms__parse() [ Upstream commit 68018df3f55eba96a20dd703f5f276a6518f4963 ] In kallsyms__parse(), the loop reading symbol names iterates with i < sizeof(symbol_name), which allows i to reach sizeof(symbol_name) upon loop exit. The subsequent symbol_name[i] = '\0' then writes one byte past the end of the stack-allocated symbol_name[] array. Fix this by changing the loop bound to KSYM_NAME_LEN, so the null terminator always lands within the array. The overflow is triggerable by a kallsyms entry with a symbol name of KSYM_NAME_LEN+1 or more characters (e.g., long Rust mangled names or a malicious /proc/kallsyms). Fixes: 53df2b9344128984 ("libsymbols kallsyms: Parse using io api") Signed-off-by: Rui Qi Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/symbol/kallsyms.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c index e335ac2b9e197..d64bd9cc82a90 100644 --- a/tools/lib/symbol/kallsyms.c +++ b/tools/lib/symbol/kallsyms.c @@ -60,7 +60,7 @@ int kallsyms__parse(const char *filename, void *arg, read_to_eol(&io); continue; } - for (i = 0; i < sizeof(symbol_name); i++) { + for (i = 0; i < KSYM_NAME_LEN; i++) { ch = io__get_char(&io); if (ch < 0 || ch == '\n') break; @@ -68,6 +68,9 @@ int kallsyms__parse(const char *filename, void *arg, } symbol_name[i] = '\0'; + if (i == KSYM_NAME_LEN) + read_to_eol(&io); + err = process_symbol(arg, symbol_name, symbol_type, start); if (err) break; From 18961e0f8966e41642b537bbba8d3933388c8511 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 20 Apr 2026 12:52:17 +0100 Subject: [PATCH 1252/3297] perf annotate: Fix crashes on empty annotate windows [ Upstream commit 74802634e4a7e556429417963a8cbda27dd8b4b3 ] Annotate can open with an empty window if the disassembly tool fails. After the linked change, the TUI started assuming there was a current annotation line and could assert or segfault in the seek, refresh, and source-toggle paths. Handle empty annotate windows explicitly: set the asm entry count before resetting the browser, return early when refreshing an empty list, and ignore source line toggle when there is no current annotation line. Fixes the following when opening an annotation: perf: ui/browser.c:125: ui_browser__list_head_seek: Assertion `pos != NULL' failed. Aborted Fixes: e201757f7a0a901e ("perf annotate: Fix source code annotate with objdump") Assisted-by: GitHub Copilot:GPT-5.4 Signed-off-by: James Clark Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/ui/browser.c | 3 +++ tools/perf/ui/browsers/annotate.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index dc88427b4ae5a..321187b204d38 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -513,6 +513,9 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) struct list_head *head = browser->entries; int row = 0; + if (browser->nr_entries == 0) + return 0; + if (browser->top == NULL || browser->top == browser->entries) browser->top = ui_browser__list_head_filter_entries(browser, head->next); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 8fe699f985423..97f92512f136d 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -449,6 +449,9 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser, struct annotation_line *al; off_t offset = browser->b.index - browser->b.top_idx; + if (browser->b.nr_entries == 0) + return false; + browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); @@ -542,8 +545,8 @@ static void annotate_browser__show_full_location(struct ui_browser *browser) static void ui_browser__init_asm_mode(struct ui_browser *browser) { struct annotation *notes = browser__annotation(browser); - ui_browser__reset_index(browser); browser->nr_entries = notes->src->nr_asm_entries; + ui_browser__reset_index(browser); } static int sym_title(struct symbol *sym, struct map *map, char *title, From ea486d61b1663b345ce1bfdb2ec04903f61dd82f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:55:06 -0300 Subject: [PATCH 1253/3297] perf tools: Guard test_bit from out-of-bounds sample CPU [ Upstream commit a5498ccf8079fc91c938f122ff9697b0c526b2fd ] When PERF_SAMPLE_CPU is absent from a perf.data file, sample->cpu is initialized to (u32)-1 by evsel__parse_sample(). Five call sites pass this value directly to test_bit(sample->cpu, cpu_bitmap), reading massively out of bounds past the DECLARE_BITMAP(..., MAX_NR_CPUS) allocation of 4096 bits. Add a sample->cpu >= MAX_NR_CPUS guard before each test_bit() call, matching the existing safe pattern in builtin-kwork.c. This catches both the (u32)-1 sentinel and any corrupted CPU value exceeding the bitmap size. Fixes: 5d67be97f890 ("perf report/annotate/script: Add option to specify a CPU range") Cc: Anton Blanchard Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-annotate.c | 3 ++- tools/perf/builtin-diff.c | 3 ++- tools/perf/builtin-report.c | 3 ++- tools/perf/builtin-sched.c | 6 ++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 646f43b0f7c4c..0485bddb02464 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -299,7 +299,8 @@ static int process_sample_event(const struct perf_tool *tool, goto out_put; } - if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) + if (ann->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, ann->cpu_bitmap))) goto out_put; if (!al.filtered && diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 59bf1f72d12e2..98fcfe924f5d6 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -416,7 +416,8 @@ static int diff__process_sample_event(const struct perf_tool *tool, goto out; } - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) { ret = 0; goto out; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 35df04dad2fd0..fd5bd3d976862 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -303,7 +303,8 @@ static int process_sample_event(const struct perf_tool *tool, if (symbol_conf.hide_unresolved && al.sym == NULL) goto out_put; - if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) + if (rep->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, rep->cpu_bitmap))) goto out_put; if (sort__mode == SORT_MODE__BRANCH) { diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index ce0858f309e16..8310aa23cbb3c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2168,7 +2168,8 @@ static void timehist_print_sample(struct perf_sched *sched, char nstr[30]; u64 wait_time; - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) return; timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); @@ -2850,7 +2851,8 @@ static int timehist_sched_change_event(const struct perf_tool *tool, } if (!sched->idle_hist || thread__tid(thread) == 0) { - if (!cpu_list || test_bit(sample->cpu, cpu_bitmap)) + if (!cpu_list || (sample->cpu < MAX_NR_CPUS && + test_bit(sample->cpu, cpu_bitmap))) timehist_update_runtime_stats(tr, t, tprev); if (sched->idle_hist) { From 89489a31f44437edb5c799abb8005b1ea2a16a6d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 12:55:29 -0300 Subject: [PATCH 1254/3297] perf sched: Fix thread reference leak in latency_switch_event [ Upstream commit 66ea9de60396a4dea5276bc87025884691876c36 ] In latency_switch_event(), after acquiring thread references for sched_out and sched_in via machine__findnew_thread(), the first add_sched_out_event() failure path does 'return -1', bypassing the out_put label that calls thread__put() on both references. The second and third add_sched_out_event() failures correctly use 'goto out_put'. Fix the first one to match. Fixes: b91fc39f4ad7 ("perf machine: Protect the machine->threads with a rwlock") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 8310aa23cbb3c..723a4ca059cc3 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1170,7 +1170,7 @@ static int latency_switch_event(struct perf_sched *sched, } } if (add_sched_out_event(out_events, prev_state, timestamp)) - return -1; + goto out_put; in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid); if (!in_events) { From 1d25a8418c8904d5ad4cd49a1d9b64ad98ec7d41 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:14:23 -0300 Subject: [PATCH 1255/3297] perf tools: Add bounds check to cpu__get_node() [ Upstream commit 1e7921d7227de5da0dfc167943092c823ec7e49b ] cpu__get_node() accesses cpunode_map[cpu.cpu] without checking against max_cpu_num, the allocation size of cpunode_map. Callers such as builtin-kmem.c:evsel__process_alloc_event() pass sample->cpu from perf.data events, which may exceed the host's CPU count when analyzing cross-machine recordings. Add a bounds check against max_cpu_num before indexing, returning -1 for out-of-range values. This is a central fix that protects all callers. Fixes: 86895b480a2f ("perf stat: Add --per-node agregation support") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 89570397a4b35..0b419783c9dd0 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -548,6 +548,10 @@ int cpu__get_node(struct perf_cpu cpu) return -1; } + /* cpunode_map allocated for max_cpu_num entries; input may be untrusted */ + if (cpu.cpu < 0 || cpu.cpu >= max_cpu_num.cpu) + return -1; + return cpunode_map[cpu.cpu]; } From 068e9b6a07bc0998b63c5a2561535ba394c48764 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:23:35 -0300 Subject: [PATCH 1256/3297] perf sched: Cap max_cpu at MAX_CPUS in timehist sample processing [ Upstream commit 06e7994427ab56e32699a5e45d048ff0826f3d53 ] perf_timehist__process_sample() updates sched->max_cpu from the sample CPU without bounds checking. Later code uses max_cpu + 1 as an iteration count over arrays allocated with MAX_CPUS entries (curr_thread, cpu_last_switched). A recording with CPU IDs >= MAX_CPUS causes out-of-bounds array accesses. Also cap the env->nr_cpus_online initialization of max_cpu in perf_sched__timehist(), which could exceed MAX_CPUS on very large systems. Add bounds checks before both max_cpu updates, matching the pattern already used in map_switch_event(). Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reviewed-by: David Ahern Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 723a4ca059cc3..c9ed723beda30 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3188,7 +3188,9 @@ static int perf_timehist__process_sample(const struct perf_tool *tool, .cpu = sample->cpu, }; - if (this_cpu.cpu > sched->max_cpu.cpu) + /* max_cpu indexes arrays allocated with MAX_CPUS entries */ + if (this_cpu.cpu >= 0 && this_cpu.cpu < MAX_CPUS && + this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; if (evsel->handler != NULL) { @@ -3358,8 +3360,8 @@ static int perf_sched__timehist(struct perf_sched *sched) perf_session__set_tracepoints_handlers(session, migrate_handlers)) goto out; - /* pre-allocate struct for per-CPU idle stats */ - sched->max_cpu.cpu = env->nr_cpus_online; + /* pre-allocate struct for per-CPU idle stats; cap to array bounds */ + sched->max_cpu.cpu = min(env->nr_cpus_online, MAX_CPUS); if (sched->max_cpu.cpu == 0) sched->max_cpu.cpu = 4; if (init_idle_threads(sched->max_cpu.cpu)) From 652cea73b7b7b7c622a2be670e44e3c499c6d49f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 4 Jun 2026 18:25:57 -0300 Subject: [PATCH 1257/3297] perf sched: Fix register_pid() overflow, strcpy, and BUG_ON [ Upstream commit 5949d339f5ec98752d56dcd4e36f619a59d513a5 ] register_pid() has several issues when processing untrusted perf.data: 1. Integer overflow: (pid + 1) * sizeof(struct task_desc *) can wrap to a small value on 32-bit systems when pid is large (e.g. 0x40000000), causing realloc to return a tiny buffer followed by out-of-bounds writes in the initialization loop. 2. Heap buffer overflow: strcpy(task->comm, comm) copies the untrusted comm string into a fixed 20-byte COMM_LEN buffer with no length check. 3. BUG_ON on allocation failure: perf.data is untrusted input, so allocation failures should be handled gracefully rather than killing the process. 4. Realloc of sched->tasks assigned directly back, leaking the old pointer on failure; nr_tasks incremented before the realloc, leaving corrupted state on failure. Cap pid at PID_MAX_LIMIT (4194304, matching the kernel's maximum on 64-bit), replace strcpy with strlcpy, guard against NULL comm, replace BUG_ON with NULL returns using safe realloc patterns, and add NULL checks in callers that dereference the result. Fixes: ec156764d424 ("perf sched: Import schedbench.c") Reported-by: sashiko-bot Cc: Ingo Molnar Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c9ed723beda30..ad92f01a38b02 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -52,6 +52,7 @@ #define COMM_LEN 20 #define SYM_LEN 129 #define MAX_PID 1024000 +#define PID_MAX_LIMIT 4194304 /* kernel limit on 64-bit */ #define MAX_PRIO 140 static const char *cpu_list; @@ -441,17 +442,28 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta static struct task_desc *register_pid(struct perf_sched *sched, unsigned long pid, const char *comm) { - struct task_desc *task; + struct task_desc *task, **tasks_p; static int pid_max; + /* perf.data is untrusted — cap pid to prevent overflow in size calculations */ + if (pid >= PID_MAX_LIMIT) { + pr_err("pid %lu exceeds limit %d, skipping\n", pid, PID_MAX_LIMIT); + return NULL; + } + if (sched->pid_to_task == NULL) { if (sysctl__read_int("kernel/pid_max", &pid_max) < 0) pid_max = MAX_PID; - BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL); + sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *)); + if (sched->pid_to_task == NULL) + return NULL; } if (pid >= (unsigned long)pid_max) { - BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) * - sizeof(struct task_desc *))) == NULL); + void *p = realloc(sched->pid_to_task, (pid + 1) * sizeof(struct task_desc *)); + + if (p == NULL) + return NULL; + sched->pid_to_task = p; while (pid >= (unsigned long)pid_max) sched->pid_to_task[pid_max++] = NULL; } @@ -462,9 +474,11 @@ static struct task_desc *register_pid(struct perf_sched *sched, return task; task = zalloc(sizeof(*task)); + if (task == NULL) + return NULL; task->pid = pid; - task->nr = sched->nr_tasks; - strcpy(task->comm, comm); + if (comm) + strlcpy(task->comm, comm, sizeof(task->comm)); /* * every task starts in sleeping state - this gets ignored * if there's no wakeup pointing to this sleep state: @@ -472,10 +486,12 @@ static struct task_desc *register_pid(struct perf_sched *sched, add_sched_event_sleep(sched, task, 0); sched->pid_to_task[pid] = task; - sched->nr_tasks++; - sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *)); - BUG_ON(!sched->tasks); - sched->tasks[task->nr] = task; + tasks_p = realloc(sched->tasks, (sched->nr_tasks + 1) * sizeof(struct task_desc *)); + if (!tasks_p) + return NULL; + sched->tasks = tasks_p; + sched->tasks[sched->nr_tasks] = task; + task->nr = sched->nr_tasks++; if (verbose > 0) printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm); @@ -834,6 +850,8 @@ replay_wakeup_event(struct perf_sched *sched, waker = register_pid(sched, sample->tid, ""); wakee = register_pid(sched, pid, comm); + if (waker == NULL || wakee == NULL) + return -1; add_sched_event_wakeup(sched, waker, sample->time, wakee); return 0; @@ -875,6 +893,8 @@ static int replay_switch_event(struct perf_sched *sched, prev = register_pid(sched, prev_pid, prev_comm); next = register_pid(sched, next_pid, next_comm); + if (prev == NULL || next == NULL) + return -1; sched->cpu_last_switched[cpu] = timestamp; From 21a9b87ada08db49e487f1b41f3bc39bf700a669 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 10:56:33 -0300 Subject: [PATCH 1258/3297] perf mmap: Guard cpu__get_node() return in aio_bind() [ Upstream commit f32dc302a090f48893477ef297a888db109ec0bd ] perf_mmap__aio_bind() passes the cpu__get_node() return value directly to an unsigned long variable (node_index). When cpu__get_node() returns -1 for an unknown CPU, the implicit int-to-unsigned-long conversion sign-extends it to ULONG_MAX. This causes bitmap_zalloc(ULONG_MAX + 1) which wraps to bitmap_zalloc(0), returning a zero-sized allocation. The subsequent __set_bit(ULONG_MAX, node_mask) then writes massively out of bounds. Check the return value in a signed temporary before assigning to node_index, and skip the NUMA binding when the node is unknown. Fixes: c44a8b44ca9f ("perf record: Bind the AIO user space buffers to nodes") Reported-by: sashiko-bot Cc: Alexey Budankov Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/mmap.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index a34726219af3d..ca89799918972 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -103,9 +103,15 @@ static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, i int err = 0; if (affinity != PERF_AFFINITY_SYS && cpu__max_node() > 1) { + int node; + data = map->aio.data[idx]; mmap_len = mmap__mmap_len(map); - node_index = cpu__get_node(cpu); + node = cpu__get_node(cpu); + /* -1 sign-extends to ULONG_MAX, wrapping bitmap_zalloc(0) and OOB __set_bit */ + if (node < 0) + return 0; + node_index = node; node_mask = bitmap_zalloc(node_index + 1); if (!node_mask) { pr_err("Failed to allocate node mask for mbind: error %m\n"); From 278e30717c356e5e3078e5a2a6bcc6401fba9801 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 10:59:11 -0300 Subject: [PATCH 1259/3297] perf stat: Bounds-check CPU index in topology aggregation callbacks [ Upstream commit 52e69b1c5b606b513d403dd4addc784c27a0c8e2 ] Six perf_env__get_*_aggr_by_cpu() functions access env->cpu[cpu.cpu] after only checking cpu.cpu != -1. env->cpu[] is allocated with env->nr_cpus_avail entries, so a CPU index from an untrusted perf.data file that exceeds that count causes an out-of-bounds heap read. Replace the != -1 guard with >= 0 && < env->nr_cpus_avail in all six functions. The >= 0 check also catches -1 and any other negative values that could bypass the old check. Affected functions: - perf_env__get_socket_aggr_by_cpu() - perf_env__get_die_aggr_by_cpu() - perf_env__get_cache_aggr_by_cpu() - perf_env__get_cluster_aggr_by_cpu() - perf_env__get_core_aggr_by_cpu() - perf_env__get_cpu_aggr_by_cpu() Fixes: 68d702f7a120 ("perf stat report: Add support to initialize aggr_map from file") Reported-by: sashiko-bot Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-stat.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index e8a75cb198b19..7437c1073211b 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1564,7 +1564,8 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) + /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) id.socket = env->cpu[cpu.cpu].socket_id; return id; @@ -1575,7 +1576,7 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * die_id is relative to socket, so start * with the socket ID and then add die to @@ -1631,7 +1632,7 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; id.socket = env->cpu[cpu.cpu].socket_id; @@ -1648,7 +1649,7 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { id.socket = env->cpu[cpu.cpu].socket_id; id.die = env->cpu[cpu.cpu].die_id; id.cluster = env->cpu[cpu.cpu].cluster_id; @@ -1662,7 +1663,7 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * core_id is relative to socket, die and cluster, we need a * global id. So we set socket, die id, cluster id and core id. @@ -1681,7 +1682,7 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu.cpu != -1) { + if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { /* * core_id is relative to socket and die, * we need a global id. So we set From 2f9f7224e769196744f8fc863dc08404ff1e240e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:05:13 -0300 Subject: [PATCH 1260/3297] perf c2c: Bounds-check CPU and node IDs before bitmap and array access [ Upstream commit 65117c3da50f749f4c22eb7a7effc53453dff57f ] c2c_he__set_cpu() passes sample->cpu directly to __set_bit(cpu, cpuset) after only checking for the (u32)-1 sentinel. The cpuset bitmap is allocated with c2c.cpus_cnt bits (from env->nr_cpus_avail), so a crafted perf.data with CPU IDs exceeding that count causes out-of-bounds heap writes. c2c_he__set_node() similarly passes the node ID from mem2node__node() to __set_bit(node, nodeset) after only checking for negative values. The nodeset bitmap is sized to c2c.nodes_cnt (from env->nr_numa_nodes), so a node ID exceeding that causes OOB writes. process_sample_event() indexes c2c.cpu2node[cpu] and c2c_he->node_stats[node] without bounds checking. Both arrays are sized to c2c.cpus_cnt and c2c.nodes_cnt respectively. Add bounds checks in all three paths: - c2c_he__set_cpu(): return if sample->cpu >= c2c.cpus_cnt - c2c_he__set_node(): return if node >= c2c.nodes_cnt - process_sample_event(): clamp cpu to 0 if >= cpus_cnt, guard node_stats access with bounds check Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") Reported-by: sashiko-bot Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 9e9ff471ddd16..f67645aa8c1e4 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -232,6 +232,10 @@ static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, "WARNING: no sample cpu value")) return; + /* cpuset bitmap has c2c.cpus_cnt bits from env->nr_cpus_avail */ + if (sample->cpu >= (unsigned int)c2c.cpus_cnt) + return; + __set_bit(sample->cpu, c2c_he->cpuset); } @@ -249,6 +253,10 @@ static void c2c_he__set_node(struct c2c_hist_entry *c2c_he, if (WARN_ONCE(node < 0, "WARNING: failed to find node\n")) return; + /* nodeset bitmap has c2c.nodes_cnt bits from env->nr_numa_nodes */ + if (node >= c2c.nodes_cnt) + return; + __set_bit(node, c2c_he->nodeset); if (c2c_he->paddr != sample->phys_addr) { @@ -348,7 +356,12 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, * Doing node stats only for single callchain data. */ int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu; - int node = c2c.cpu2node[cpu]; + int node; + + /* cpu2node[] has c2c.cpus_cnt entries; large u32 wraps signed negative */ + if (cpu < 0 || cpu >= c2c.cpus_cnt) + cpu = 0; + node = c2c.cpu2node[cpu]; mi = mi_dup; @@ -365,7 +378,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, c2c_he = container_of(he, struct c2c_hist_entry, he); c2c_add_stats(&c2c_he->stats, &stats); c2c_add_stats(&c2c_hists->stats, &stats); - c2c_add_stats(&c2c_he->node_stats[node], &stats); + /* node_stats[] has c2c.nodes_cnt entries */ + if (node >= 0 && node < c2c.nodes_cnt) + c2c_add_stats(&c2c_he->node_stats[node], &stats); compute_stats(c2c_he, &stats, sample->weight); From d6b586bb8f4892ffd772d04c29023c6561fecde8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:06:30 -0300 Subject: [PATCH 1261/3297] perf c2c: Bounds-check CPU IDs in setup_nodes() topology loop [ Upstream commit 5fb2e6ad8c5d6b3b380f94c7456595511f3731be ] setup_nodes() iterates CPU maps from the perf.data topology header and uses cpu.cpu directly as an array index into cpu2node[] (allocated with c2c.cpus_cnt = env->nr_cpus_avail entries) and __set_bit(cpu.cpu, set) (bitmap also sized to c2c.cpus_cnt). A crafted perf.data with topology CPU IDs exceeding nr_cpus_avail causes out-of-bounds heap writes into both the cpu2node array and the per-node bitmap. Add a bounds check to skip CPU IDs that fall outside the valid range. Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") Reported-by: sashiko-bot Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index f67645aa8c1e4..7efba10299643 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -2327,6 +2327,10 @@ static int setup_nodes(struct perf_session *session) nodes[node] = set; perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) { + /* topology CPU IDs from perf.data may exceed nr_cpus_avail */ + if (cpu.cpu < 0 || cpu.cpu >= c2c.cpus_cnt) + continue; + __set_bit(cpu.cpu, set); if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug")) From 1517402d0a81cf8c37745c97c2e8dfc77ff75c3a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:12:08 -0300 Subject: [PATCH 1262/3297] perf sched: Clean up idle_threads entry on init failure [ Upstream commit cda5a94ad9181cd60cbf04be11d524201bf489a2 ] get_idle_thread() allocates a thread via thread__new() and stores it in idle_threads[cpu], then calls init_idle_thread() to set up the private data. If init_idle_thread() fails (e.g. OOM for the idle_thread_runtime struct), the function returns NULL but leaves the partially initialized thread in idle_threads[cpu]. On subsequent calls for the same CPU, get_idle_thread() finds a non-NULL idle_threads[cpu], skips allocation, and returns thread__get() on a thread that has no priv data. Callers then get a thread whose thread__priv() returns NULL, leading to unexpected behavior. Release the thread and reset the slot to NULL on init failure so the entry doesn't persist in a corrupted state. Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index ad92f01a38b02..728accfb223c8 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2489,8 +2489,11 @@ static struct thread *get_idle_thread(int cpu) idle_threads[cpu] = thread__new(0, 0); if (idle_threads[cpu]) { - if (init_idle_thread(idle_threads[cpu]) < 0) + if (init_idle_thread(idle_threads[cpu]) < 0) { + /* clean up so next call doesn't find a half-initialized thread */ + thread__zput(idle_threads[cpu]); return NULL; + } } } From b1f76836327124db88056d6ed37311844dd48578 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:17:03 -0300 Subject: [PATCH 1263/3297] perf sched: Use thread__put() in free_idle_threads() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a99d6394cd48fed75b1d24733d5afe6837a61a3f ] free_idle_threads() calls thread__delete() directly instead of thread__put(), bypassing the reference counting lifecycle. Under REFCNT_CHECKING builds, this leaks the pointer handle since thread__delete() frees the object without going through the refcount wrapper. The idle threads are created via thread__new() (refcount=1) in get_idle_thread(). Callers get additional references via thread__get() which they release with thread__put(). free_idle_threads() drops the base reference — thread__put() is the correct call, matching the thread__new() acquisition. Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") Reported-by: sashiko-bot Cc: David Ahern Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 728accfb223c8..9790838f3bf9e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2456,7 +2456,7 @@ static void free_idle_threads(void) if (itr) thread__put(itr->last_thread); - thread__delete(idle); + thread__put(idle); } } From c4406dbe5d8f4b3c322e483261bdbc7cf3872d2f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 5 Jun 2026 11:26:28 -0300 Subject: [PATCH 1264/3297] perf sched: Replace BUG_ON and add NULL checks in replay event helpers [ Upstream commit 75eafe4a3a93a0143a20c0cc286bfb9008ac1478 ] get_new_event() has three issues: 1. The zalloc() result is dereferenced without a NULL check, crashing on allocation failure. 2. BUG_ON(!task->atoms) kills the process when realloc() fails. Since perf.data is untrusted input, this should be a graceful error. 3. The realloc pattern assigns directly to task->atoms, losing the old pointer on failure. task->nr_events is also incremented before the realloc, leaving corrupted state on failure. Fix get_new_event() to: - Check the zalloc() result before dereferencing - Use a temporary for realloc() to avoid losing the old pointer - Increment nr_events only after successful realloc - Return NULL instead of calling BUG_ON on failure Also fix add_sched_event_wakeup() where zalloc() for wait_sem is passed to sem_init() without a NULL check. Update all callers (add_sched_event_run, add_sched_event_wakeup, add_sched_event_sleep) to handle NULL returns by returning early. The replay may produce incomplete output on OOM but will not crash. Fixes: ec156764d424 ("perf sched: Import schedbench.c") Reported-by: sashiko-bot Cc: Ingo Molnar Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 9790838f3bf9e..e027626022e19 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -358,14 +358,25 @@ get_new_event(struct task_desc *task, u64 timestamp) struct sched_atom *event = zalloc(sizeof(*event)); unsigned long idx = task->nr_events; size_t size; + struct sched_atom **atoms_p; + + if (event == NULL) { + pr_err("ERROR: sched: failed to allocate event\n"); + return NULL; + } event->timestamp = timestamp; event->nr = idx; + size = sizeof(struct sched_atom *) * (task->nr_events + 1); + atoms_p = realloc(task->atoms, size); + if (!atoms_p) { + pr_err("ERROR: sched: failed to grow atoms array\n"); + free(event); + return NULL; + } + task->atoms = atoms_p; task->nr_events++; - size = sizeof(struct sched_atom *) * task->nr_events; - task->atoms = realloc(task->atoms, size); - BUG_ON(!task->atoms); task->atoms[idx] = event; @@ -396,6 +407,8 @@ static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task } event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_RUN; event->duration = duration; @@ -409,6 +422,8 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t struct sched_atom *event, *wakee_event; event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_WAKEUP; event->wakee = wakee; @@ -423,6 +438,10 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t } wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); + if (!wakee_event->wait_sem) { + pr_err("ERROR: sched: failed to allocate semaphore\n"); + return; + } sem_init(wakee_event->wait_sem, 0, 0); event->wait_sem = wakee_event->wait_sem; @@ -434,6 +453,9 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta { struct sched_atom *event = get_new_event(task, timestamp); + if (event == NULL) + return; + event->type = SCHED_EVENT_SLEEP; sched->nr_sleep_events++; From c05ba5b57505aa8958e50a7a6158336e225e0ff7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:03:29 -0300 Subject: [PATCH 1265/3297] perf mmap: Fix NULL deref in aio cleanup on alloc failure [ Upstream commit 25627346b10e6a564610ea2c49dc6dd54812226d ] perf_mmap__aio_mmap() sets map->aio.nr_cblocks before allocating the data array. If calloc() for aiocb or cblocks fails before the data array is allocated, the return -1 path leads to perf_mmap__aio_munmap() which loops nr_cblocks times calling perf_mmap__aio_free(). Both versions of perf_mmap__aio_free() (NUMA and non-NUMA) dereference map->aio.data[idx] without checking if data is NULL, causing a NULL pointer dereference. Add NULL checks for map->aio.data at the top of both perf_mmap__aio_free() variants so the cleanup path is safe when allocation fails partway through perf_mmap__aio_mmap(). Fixes: d3d1af6f011a553a ("perf record: Enable asynchronous trace writing") Reported-by: sashiko-bot Cc: Alexey Budankov Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/mmap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index ca89799918972..fcc0939846830 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -88,10 +88,10 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { - if (map->aio.data[idx]) { - munmap(map->aio.data[idx], mmap__mmap_len(map)); - map->aio.data[idx] = NULL; - } + if (!map->aio.data || !map->aio.data[idx]) + return; + munmap(map->aio.data[idx], mmap__mmap_len(map)); + map->aio.data[idx] = NULL; } static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, int affinity) @@ -140,6 +140,8 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { + if (!map->aio.data) + return; zfree(&(map->aio.data[idx])); } From 01564c1a260f6ddcf5357348075539eeeb8c79d4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:17:45 -0300 Subject: [PATCH 1266/3297] perf stat: Introduce perf_env__get_cpu_topology() to guard NULL env->cpu [ Upstream commit afa4363a91a19dff65dceb7fbce7bba689bbc854 ] process_cpu_topology() in header.c frees env->cpu on old-format perf.data files that predate topology information, but leaves nr_cpus_avail set. The six perf_env__get_*_aggr_by_cpu() functions in builtin-stat.c pass the bounds check but dereference a NULL env->cpu pointer, crashing on old recordings. Introduce perf_env__get_cpu_topology() as a safe accessor that validates env->cpu, cpu.cpu >= 0, and cpu.cpu < nr_cpus_avail in one place, returning a struct cpu_topology_map pointer or NULL. Convert all six topology aggregation callbacks to use it. Fixes: 88031a0de7d68d13 ("perf stat: Switch to cpu version of cpu_map__get()") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-stat.c | 51 +++++++++++++++++++++------------------ tools/perf/util/env.h | 14 +++++++++++ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 7437c1073211b..1202ceb425f07 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1563,10 +1563,10 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) - id.socket = env->cpu[cpu.cpu].socket_id; + if (topo) + id.socket = topo->socket_id; return id; } @@ -1575,15 +1575,16 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * die_id is relative to socket, so start * with the socket ID and then add die to * make a unique ID. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; } return id; @@ -1631,12 +1632,13 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; perf_env__get_cache_id_for_cpu(cpu, env, cache_level, &id); } @@ -1648,11 +1650,12 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; + if (topo) { + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; } return id; @@ -1662,16 +1665,17 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * core_id is relative to socket, die and cluster, we need a * global id. So we set socket, die id, cluster id and core id. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; - id.core = env->cpu[cpu.cpu].core_id; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; + id.core = topo->core_id; } return id; @@ -1681,18 +1685,19 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { + if (topo) { /* * core_id is relative to socket and die, * we need a global id. So we set * socket, die id and core id */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.core = env->cpu[cpu.cpu].core_id; - id.cpu = cpu; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.core = topo->core_id; } + id.cpu = cpu; return id; } diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 9977b85523a8c..6ca0e223ea405 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -164,6 +164,20 @@ const char *perf_env__pmu_mappings(struct perf_env *env); int perf_env__read_cpu_topology_map(struct perf_env *env); +/* + * Safe accessor for env->cpu[] topology array. env->cpu can be NULL when + * reading old-format perf.data that predates topology information — + * process_cpu_topology() in header.c frees it while nr_cpus_avail remains + * set, so callers must not index env->cpu[] without this check. + */ +static inline struct cpu_topology_map * +perf_env__get_cpu_topology(struct perf_env *env, struct perf_cpu cpu) +{ + if (env->cpu && cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) + return &env->cpu[cpu.cpu]; + return NULL; +} + void cpu_cache_level__free(struct cpu_cache_level *cache); const char *perf_env__arch(struct perf_env *env); From 330219fe8523f4d1212217283278b7f4ad2f815f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:19:10 -0300 Subject: [PATCH 1267/3297] perf c2c: Fix use-after-free in he__get_c2c_hists() error path [ Upstream commit 5e5e6196d737c5be03d20647428316b36621608d ] he__get_c2c_hists() assigns c2c_he->hists before calling c2c_hists__init(). If init fails, the error path calls free(hists) but leaves c2c_he->hists pointing to freed memory. On teardown, c2c_he_free() finds the non-NULL pointer and calls hists__delete_entries() on it, causing a use-after-free. Set c2c_he->hists to NULL before freeing so teardown skips the already-freed allocation. Fixes: b2252ae67b687d2b ("perf c2c report: Decode c2c_stats for hist entries") Reported-by: sashiko-bot Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-c2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 7efba10299643..0849092ef5fc1 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -218,6 +218,7 @@ he__get_c2c_hists(struct hist_entry *he, ret = c2c_hists__init(hists, sort, nr_header_lines, env); if (ret) { + c2c_he->hists = NULL; free(hists); return NULL; } From e16012f8f63d33a4e0d5e1c6d992c63f60b7e9a7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:21:32 -0300 Subject: [PATCH 1268/3297] perf timechart: Fix cpu2y() OOB read on untrusted CPU index [ Upstream commit e2496db45bfd8dfb6154ec415798fee330f1cc0a ] cpu2y() indexes topology_map[cpu] without bounds checking. The array is allocated with nr_cpus entries (from env->nr_cpus_online), but callers pass sample CPU values from perf.data which can exceed that size with cross-machine recordings. Track the topology_map allocation size and bounds-check the CPU argument in cpu2y() before indexing. Out-of-bounds CPUs fall back to the identity mapping (cpu2slot(cpu)), which is the same behavior as when no topology is available. Fixes: c507999790438cde ("perf timechart: Add support for topology") Reported-by: sashiko-bot Cc: Stanislav Fomichev Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/svghelper.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index b1d259f590e98..7c862c33321ec 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -47,13 +47,13 @@ static double cpu2slot(int cpu) } static int *topology_map; +static int topology_map_size; static double cpu2y(int cpu) { - if (topology_map) + if (topology_map && cpu >= 0 && cpu < topology_map_size) return cpu2slot(topology_map[cpu]) * SLOT_MULT; - else - return cpu2slot(cpu) * SLOT_MULT; + return cpu2slot(cpu) * SLOT_MULT; } static double time2pixels(u64 __time) @@ -735,7 +735,8 @@ static int str_to_bitmap(char *s, cpumask_t *b, int nr_cpus) return -1; perf_cpu_map__for_each_cpu(cpu, idx, map) { - if (cpu.cpu >= nr_cpus) { + /* perf_cpu_map__new("") returns cpu.cpu == -1 */ + if (cpu.cpu < 0 || cpu.cpu >= nr_cpus) { ret = -1; break; } @@ -793,6 +794,7 @@ int svg_build_topology_map(struct perf_env *env) fprintf(stderr, "topology: no memory\n"); goto exit; } + topology_map_size = nr_cpus; for (i = 0; i < nr_cpus; i++) topology_map[i] = -1; From c7c8bab87d0df275333ca3483ffb9b8ec86bc643 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 11:25:40 -0300 Subject: [PATCH 1269/3297] perf tools: Fix int16_t truncation of max_cpu_num in set_max_cpu_num() [ Upstream commit 33fa2bf5608fc36bc25231592145f4738f14f11b ] set_max_cpu_num() assigns the sysfs "possible" CPU count to max_cpu_num.cpu which is int16_t (struct perf_cpu). On systems with >32767 possible CPUs the value silently truncates, potentially wrapping negative. This causes cpunode_map to be underallocated and subsequent cpu__get_node() calls to read out of bounds. The matching check for max_present_cpu_num was added by commit c760174401f6 ("perf cpumap: Reduce cpu size from int to int16_t") but max_cpu_num was missed. Add the same INT16_MAX guard. Fixes: c760174401f605cf ("perf cpumap: Reduce cpu size from int to int16_t") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 0b419783c9dd0..d0516797a0088 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -466,6 +466,16 @@ static void set_max_cpu_num(void) if (ret) goto out; + /* + * struct perf_cpu.cpu is int16_t (libperf ABI) — clamp to avoid + * truncation to negative. See tools/lib/perf/TODO for the ABI + * widening plan. + */ + if (max > INT16_MAX) { + pr_warning("WARNING: max possible cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; + } max_cpu_num.cpu = max; /* get the highest present cpu number for a sparse allocation */ @@ -478,11 +488,12 @@ static void set_max_cpu_num(void) ret = get_max_num(path, &max); if (!ret && max > INT16_MAX) { - pr_err("Read out of bounds max cpus of %d\n", max); - ret = -1; + pr_warning("WARNING: max present cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; } if (!ret) - max_present_cpu_num.cpu = (int16_t)max; + max_present_cpu_num.cpu = max; out: if (ret) pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num.cpu); @@ -619,7 +630,9 @@ int cpu__setup_cpunode_map(void) while ((dent2 = readdir(dir2)) != NULL) { if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) continue; - cpunode_map[cpu] = mem; + /* cpunode_map allocated for max_cpu_num entries */ + if (cpu < (unsigned int)max_cpu_num.cpu) + cpunode_map[cpu] = mem; } closedir(dir2); } From 032692e4525a00392a4de8bb500f2e45bcd3487f Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:27 +0530 Subject: [PATCH 1270/3297] dt-bindings: clock: qcom: Add X1P42100 camera clock controller [ Upstream commit 97a5e120be5d3d7cf7d221b8703921046b73f0d2 ] Add X1P42100 camera clock controller support and clock bindings for camera QDSS debug clocks which are applicable for both X1E80100 and X1P42100 platforms. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jagadeesh Kona Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-2-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson Stable-dep-of: 1e6ae74ac6f2 ("clk: qcom: camcc-x1e80100: Add support for camera QDSS debug clocks") Signed-off-by: Sasha Levin --- .../devicetree/bindings/clock/qcom,x1e80100-camcc.yaml | 1 + include/dt-bindings/clock/qcom,x1e80100-camcc.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml index 938a2f1ff3fca..b28614186cc09 100644 --- a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml @@ -23,6 +23,7 @@ properties: compatible: enum: - qcom,x1e80100-camcc + - qcom,x1p42100-camcc reg: maxItems: 1 diff --git a/include/dt-bindings/clock/qcom,x1e80100-camcc.h b/include/dt-bindings/clock/qcom,x1e80100-camcc.h index d72fdfb06a7c7..06c316022fb0d 100644 --- a/include/dt-bindings/clock/qcom,x1e80100-camcc.h +++ b/include/dt-bindings/clock/qcom,x1e80100-camcc.h @@ -115,6 +115,9 @@ #define CAM_CC_SLEEP_CLK_SRC 105 #define CAM_CC_SLOW_AHB_CLK_SRC 106 #define CAM_CC_XO_CLK_SRC 107 +#define CAM_CC_QDSS_DEBUG_CLK 108 +#define CAM_CC_QDSS_DEBUG_CLK_SRC 109 +#define CAM_CC_QDSS_DEBUG_XO_CLK 110 /* CAM_CC power domains */ #define CAM_CC_BPS_GDSC 0 From eaf937b501fd0cfaba68f89654f4de2b223f397f Mon Sep 17 00:00:00 2001 From: Jagadeesh Kona Date: Thu, 7 May 2026 11:08:29 +0530 Subject: [PATCH 1271/3297] clk: qcom: camcc-x1e80100: Add support for camera QDSS debug clocks [ Upstream commit 1e6ae74ac6f28ace7a0eb84897c6e17bb044e5de ] Add support for camera QDSS debug clocks on X1E80100 platform which are required to be voted for camera icp and cpas usecases. This change aligns the camcc driver to the new ABI exposed from X1E80100 camcc bindings that supports these camcc QDSS debug clocks. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Signed-off-by: Jagadeesh Kona Fixes: 76126a5129b5 ("clk: qcom: Add camcc clock driver for x1e80100") Link: https://lore.kernel.org/r/20260507-purwa-videocc-camcc-v5-4-fc3af4130282@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/clk/qcom/camcc-x1e80100.c | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/clk/qcom/camcc-x1e80100.c b/drivers/clk/qcom/camcc-x1e80100.c index cbcc1c9fcb341..7e3fc7aee854e 100644 --- a/drivers/clk/qcom/camcc-x1e80100.c +++ b/drivers/clk/qcom/camcc-x1e80100.c @@ -1052,6 +1052,31 @@ static struct clk_rcg2 cam_cc_mclk7_clk_src = { }, }; +static const struct freq_tbl ftbl_cam_cc_qdss_debug_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(60000000, P_CAM_CC_PLL8_OUT_EVEN, 8, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { + .cmd_rcgr = 0x13938, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + static const struct freq_tbl ftbl_cam_cc_sfe_0_clk_src[] = { F(345600000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), F(432000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), @@ -2182,6 +2207,42 @@ static struct clk_branch cam_cc_mclk7_clk = { }, }; +static struct clk_branch cam_cc_qdss_debug_clk = { + .halt_reg = 0x13a64, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a64, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_qdss_debug_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_xo_clk = { + .halt_reg = 0x13a68, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a68, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch cam_cc_sfe_0_clk = { .halt_reg = 0x133c0, .halt_check = BRANCH_HALT, @@ -2398,6 +2459,9 @@ static struct clk_regmap *cam_cc_x1e80100_clocks[] = { [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, [CAM_CC_PLL8] = &cam_cc_pll8.clkr, [CAM_CC_PLL8_OUT_EVEN] = &cam_cc_pll8_out_even.clkr, + [CAM_CC_QDSS_DEBUG_CLK] = &cam_cc_qdss_debug_clk.clkr, + [CAM_CC_QDSS_DEBUG_CLK_SRC] = &cam_cc_qdss_debug_clk_src.clkr, + [CAM_CC_QDSS_DEBUG_XO_CLK] = &cam_cc_qdss_debug_xo_clk.clkr, [CAM_CC_SFE_0_CLK] = &cam_cc_sfe_0_clk.clkr, [CAM_CC_SFE_0_CLK_SRC] = &cam_cc_sfe_0_clk_src.clkr, [CAM_CC_SFE_0_FAST_AHB_CLK] = &cam_cc_sfe_0_fast_ahb_clk.clkr, From 8e2c460a8f0e44d1c3f6ee338aabbd7aec814de0 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 16 Apr 2026 22:18:05 +0800 Subject: [PATCH 1272/3297] mshv: add bounds check on vp_index in mshv_intercept_isr() [ Upstream commit a4ffc59238be84dd1c26bf1c001543e832674fc6 ] mshv_intercept_isr() extracts vp_index from the hypervisor message payload and uses it directly to index into pt_vp_array without validation. handle_bitset_message() and handle_pair_message() already validate vp_index against MSHV_MAX_VPS before array access. Add the same MSHV_MAX_VPS bounds check for consistency with the other message handlers. Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Reported-by: Yuhao Jiang Signed-off-by: Junrui Luo Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/mshv_synic.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index e6b6381b7c369..1a822beae2390 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -375,6 +375,11 @@ mshv_intercept_isr(struct hv_message *msg) */ vp_index = ((struct hv_opaque_intercept_message *)msg->u.payload)->vp_index; + /* This shouldn't happen, but just in case. */ + if (unlikely(vp_index >= MSHV_MAX_VPS)) { + pr_debug("VP index %u out of bounds\n", vp_index); + goto unlock_out; + } vp = partition->pt_vp_array[vp_index]; if (unlikely(!vp)) { pr_debug("failed to find VP %u\n", vp_index); From 7d49f0ddaf5a4834ecf7085c13f4f8eac6a431b8 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 2 Jun 2026 15:03:44 +0800 Subject: [PATCH 1273/3297] dmaengine: qcom: gpi: set DMA_PRIVATE capability [ Upstream commit 4e351f408743354d54ee1af5193fc78234f2044e ] The GPI DMA controller is only responsible for QUP peripherals, and cannot work as a general-purpose DMA accelerator. Set DMA_PRIVATE capability for it. This fixes error messages about GPI being shown when an async-tx consumer is loaded. Fixes: 5d0c3533a19f ("dmaengine: qcom: Add GPI dma driver") Signed-off-by: Icenowy Zheng Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260602070344.3707256-1-zhengxingda@iscas.ac.cn Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/qcom/gpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index 8908b7c719007..51d52a140c1b3 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -2253,6 +2253,7 @@ static int gpi_probe(struct platform_device *pdev) /* clear and Set capabilities */ dma_cap_zero(gpi_dev->dma_device.cap_mask); dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask); + dma_cap_set(DMA_PRIVATE, gpi_dev->dma_device.cap_mask); /* configure dmaengine apis */ gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); From 329ec20a86091d2a6e5b4fe507545e7d678a22a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:14 +0100 Subject: [PATCH 1274/3297] dmaengine: Fix possible use after free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 92f853f0645aebf1d05d333e97ab7c342ace1892 ] In dma_release_channel(), check chan->device->privatecnt after call dma_chan_put(). However, dma_chan_put() call dma_device_put() which could release the last reference of the device if the DMA provider is already gone and hence free it. Fixes it by moving dma_chan_put() after the check. Fixes: 0f571515c332 ("dmaengine: Add privatecnt to revert DMA_PRIVATE property") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-1-90f43412fdc0@analog.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/dmaengine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index ca13cd39330ba..e6d9d338a60a8 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -905,11 +905,12 @@ void dma_release_channel(struct dma_chan *chan) mutex_lock(&dma_list_mutex); WARN_ONCE(chan->client_count != 1, "chan reference count %d != 1\n", chan->client_count); - dma_chan_put(chan); /* drop PRIVATE cap enabled by __dma_request_channel() */ if (--chan->device->privatecnt == 0) dma_cap_clear(DMA_PRIVATE, chan->device->cap_mask); + dma_chan_put(chan); + if (chan->slave) { sysfs_remove_link(&chan->dev->device.kobj, DMA_SLAVE_NAME); sysfs_remove_link(&chan->slave->kobj, chan->name); From 9f1ef67c041ef592c13603957ba08b6f21010004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:15 +0100 Subject: [PATCH 1275/3297] dmaengine: dma-axi-dmac: Properly free struct axi_dmac_desc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4910ce1b3b35687bb2a5e742c4bfbea3c647c980 ] Use axi_dmac_free_desc() to free fully the descriptor at fail path when call axi_dmac_alloc_desc() in axi_dmac_prep_peripheral_dma_vec(). Fixes: 74609e568670 ("dmaengine: dma-axi-dmac: Implement device_prep_peripheral_dma_vec") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-2-90f43412fdc0@analog.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/dma-axi-dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 0f25f6d8ae71f..a04b09b8cd6f8 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -643,7 +643,7 @@ axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs, for (i = 0; i < nb; i++) { if (!axi_dmac_check_addr(chan, vecs[i].addr) || !axi_dmac_check_len(chan, vecs[i].len)) { - kfree(desc); + axi_dmac_free_desc(desc); return NULL; } From c0e6bb2b0408fcac6382158ee2bd9fdc45eceee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Fri, 24 Apr 2026 18:40:17 +0100 Subject: [PATCH 1276/3297] dmaengine: dma-axi-dmac: use DMA pool to manange DMA descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9e942c8579130e62734c14338e9f451780669164 ] For architectures like Microblaze or arm64 (where this IP is used), DMA_DIRECT_REMAP is set which means that dma_alloc_coherent() might remap (and hence vmalloc()) some memory. This became visible in a design where dma_direct_use_pool() is not possible. With the above, when calling dma_free_coherent(), vunmap() would be called from softirq context and thus leading to a BUG(). To fix it, use a dma pool that is allocated in .device_alloc_chan_resources() and allocate blocks from it. The key point is that now dma_pool_free() is used in axi_dmac_free_desc() to free the blocks and that just frees the blocks from the pool in the sense they can be used again. In other words, no actual call to dma_free_coherent() happens. That only happens when destroying the pool in axi_dmac_free_chan_resources() which does not happen in any interrupt context. Fixes: 3f8fd25936ee ("dmaengine: axi-dmac: Allocate hardware descriptors") Signed-off-by: Nuno Sá Reviewed-by: Frank Li Link: https://patch.msgid.link/20260424-dma-dmac-handle-vunmap-v4-4-90f43412fdc0@analog.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/dma-axi-dmac.c | 66 +++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index a04b09b8cd6f8..66fc1afaf5b0c 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +144,7 @@ struct axi_dmac_chan { struct virt_dma_chan vchan; struct axi_dmac_desc *next_desc; + void *pool; struct list_head active_descs; enum dma_transfer_direction direction; @@ -524,11 +526,17 @@ static void axi_dmac_issue_pending(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } +static void axi_dmac_free_desc(struct axi_dmac_desc *desc) +{ + for (unsigned int i = 0; i < desc->num_sgs; i++) + dma_pool_free(desc->chan->pool, desc->sg[i].hw, desc->sg[i].hw_phys); + + kfree(desc); +} + static struct axi_dmac_desc * axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) { - struct axi_dmac *dmac = chan_to_axi_dmac(chan); - struct device *dev = dmac->dma_dev.dev; struct axi_dmac_hw_desc *hws; struct axi_dmac_desc *desc; dma_addr_t hw_phys; @@ -540,22 +548,22 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) desc->num_sgs = num_sgs; desc->chan = chan; - hws = dma_alloc_coherent(dev, PAGE_ALIGN(num_sgs * sizeof(*hws)), - &hw_phys, GFP_ATOMIC); - if (!hws) { - kfree(desc); - return NULL; - } - for (i = 0; i < num_sgs; i++) { - desc->sg[i].hw = &hws[i]; - desc->sg[i].hw_phys = hw_phys + i * sizeof(*hws); + hws = dma_pool_zalloc(chan->pool, GFP_NOWAIT, &hw_phys); + if (!hws) { + desc->num_sgs = i; + axi_dmac_free_desc(desc); + return NULL; + } + + desc->sg[i].hw = hws; + desc->sg[i].hw_phys = hw_phys; - hws[i].id = AXI_DMAC_SG_UNUSED; - hws[i].flags = 0; + hws->id = AXI_DMAC_SG_UNUSED; /* Link hardware descriptors */ - hws[i].next_sg_addr = hw_phys + (i + 1) * sizeof(*hws); + if (i) + desc->sg[i - 1].hw->next_sg_addr = hw_phys; } /* The last hardware descriptor will trigger an interrupt */ @@ -564,18 +572,6 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) return desc; } -static void axi_dmac_free_desc(struct axi_dmac_desc *desc) -{ - struct axi_dmac *dmac = chan_to_axi_dmac(desc->chan); - struct device *dev = dmac->dma_dev.dev; - struct axi_dmac_hw_desc *hw = desc->sg[0].hw; - dma_addr_t hw_phys = desc->sg[0].hw_phys; - - dma_free_coherent(dev, PAGE_ALIGN(desc->num_sgs * sizeof(*hw)), - hw, hw_phys); - kfree(desc); -} - static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan, enum dma_transfer_direction direction, dma_addr_t addr, unsigned int num_periods, unsigned int period_len, @@ -807,9 +803,26 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved( return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } +static int axi_dmac_alloc_chan_resources(struct dma_chan *c) +{ + struct axi_dmac_chan *chan = to_axi_dmac_chan(c); + struct device *dev = c->device->dev; + + chan->pool = dma_pool_create(dev_name(dev), dev, + sizeof(struct axi_dmac_hw_desc), + __alignof__(struct axi_dmac_hw_desc), 0); + if (!chan->pool) + return -ENOMEM; + + return 0; +} + static void axi_dmac_free_chan_resources(struct dma_chan *c) { + struct axi_dmac_chan *chan = to_axi_dmac_chan(c); + vchan_free_chan_resources(to_virt_chan(c)); + dma_pool_destroy(chan->pool); } static void axi_dmac_desc_free(struct virt_dma_desc *vdesc) @@ -1093,6 +1106,7 @@ static int axi_dmac_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask); dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask); + dma_dev->device_alloc_chan_resources = axi_dmac_alloc_chan_resources; dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources; dma_dev->device_tx_status = dma_cookie_status; dma_dev->device_issue_pending = axi_dmac_issue_pending; From e8dc126e8039584fe1ed5cb26e4fa1cd83b0889e Mon Sep 17 00:00:00 2001 From: Phillip Varney Date: Fri, 5 Jun 2026 00:55:45 +0000 Subject: [PATCH 1277/3297] clk: qcom: a53: Corrected frequency multiplier for 1152MHz [ Upstream commit bb56147ea9fce98ebde1d367335ba006cba61fbd ] The 1152MHz frequency entry for the a53 currently selects a multiplier of 62, giving 1190MHz. This changes the mulitiplier to 60 giving the intended 1152MHz. Signed-off-by: Phillip Varney Reviewed-by: Konrad Dybcio Fixes: 0c6ab1b8f894 ("clk: qcom: Add A53 PLL support") Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260605005502.313928-1-pbvarney@protonmail.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin --- drivers/clk/qcom/a53-pll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/a53-pll.c b/drivers/clk/qcom/a53-pll.c index 724a642311e50..0549b214fcfc8 100644 --- a/drivers/clk/qcom/a53-pll.c +++ b/drivers/clk/qcom/a53-pll.c @@ -20,7 +20,7 @@ static const struct pll_freq_tbl a53pll_freq[] = { { 998400000, 52, 0x0, 0x1, 0 }, { 1094400000, 57, 0x0, 0x1, 0 }, - { 1152000000, 62, 0x0, 0x1, 0 }, + { 1152000000, 60, 0x0, 0x1, 0 }, { 1209600000, 63, 0x0, 0x1, 0 }, { 1248000000, 65, 0x0, 0x1, 0 }, { 1363200000, 71, 0x0, 0x1, 0 }, From f3f21b94cf98001abc6bef5c4844d8241e67371e Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 13 May 2026 17:28:59 +0800 Subject: [PATCH 1278/3297] sunrpc: Fix error handling in rpc_sysfs_xprt_switch_add_xprt_store() [ Upstream commit 37957478be021b92981aa4c99b69f308d3b784d0 ] xprt_create_transport() never returns NULL, only valid pointers or error pointers. Using IS_ERR_OR_NULL() is incorrect, and PTR_ERR(NULL) would return 0, which indicates EOF in a sysfs store function. Fix this by using IS_ERR() instead of IS_ERR_OR_NULL(). Fixes: df210d9b0951 ("sunrpc: Add a sysfs file for adding a new xprt") Signed-off-by: Hongling Zeng Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index 8b01b7ae2690c..fa9892eec1b44 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -347,7 +347,7 @@ static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj, xprt_create_args.reconnect_timeout = xprt->max_reconnect_timeout; new = xprt_create_transport(&xprt_create_args); - if (IS_ERR_OR_NULL(new)) { + if (IS_ERR(new)) { count = PTR_ERR(new); goto out_put_xprt; } From 44b73b4b7eff734d852abda30f9fefe8161039b9 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Wed, 13 May 2026 09:58:24 +0300 Subject: [PATCH 1279/3297] pNFS/filelayout: fix cheking if a layout is striped [ Upstream commit 91668417d4e925c98cae4a55b1b9860380ddbf16 ] A layout can still be striped with num_fh = 1 as it is perfectly possible that both MDS and DSs can handle the same filehandle. Hence check according to stripe_count > 1, which is the correct check to begin with. We should not be called with flseg->dsaddr = NULL, but if for some reason we do, return our best guess with is flseg->num_fh > 1. Fixes: a6b9d2fa0024 ("pNFS/filelayout: Fix coalescing test for single DS") Signed-off-by: Sagi Grimberg Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/filelayout/filelayout.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index 5c4551117c58c..c933e3b695bc3 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -778,6 +778,8 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid, static bool filelayout_lseg_is_striped(const struct nfs4_filelayout_segment *flseg) { + if (flseg->dsaddr) + return flseg->dsaddr->stripe_count > 1; return flseg->num_fh > 1; } From b7bc8e7f09ae4b76019373c66919c68730c14f3c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 6 Mar 2026 16:56:23 -0500 Subject: [PATCH 1280/3297] xprtrdma: Avoid 250 ms delay on backlog wakeup [ Upstream commit 100142093e22b3f7741ac88e94878bb3694e306f ] Commit a721035477fb ("SUNRPC/xprt: async tasks mustn't block waiting for memory") changed xprt_rdma_alloc_slot() to set tk_status to -ENOMEM so that call_reserveresult() would sleep HZ/4 before retrying. That rationale applies to xprt_dynamic_alloc_slot(), where an immediate retry under memory pressure wastes CPU, but not to the RDMA backlog path: a task woken from the backlog has a slot waiting for it, so the 250 ms rpc_delay adds latency without benefit. This also aligns the code with the existing kernel-doc for xprt_rdma_alloc_slot(), which already documented %-EAGAIN. Fixes: a721035477fb ("SUNRPC/xprt: async tasks mustn't block waiting for memory") Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust Stable-dep-of: e786233d2e0b ("xprtrdma: Decouple req recycling from RPC completion") Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 9a8ce5df83cad..ca079439f9cce 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -510,7 +510,7 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) return; out_sleep: - task->tk_status = -ENOMEM; + task->tk_status = -EAGAIN; xprt_add_backlog(xprt, task); } From f043dd58fbd79b8e560fb0c7079b3b6fe4ed63c7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 6 Mar 2026 16:56:24 -0500 Subject: [PATCH 1281/3297] xprtrdma: Close lost-wakeup race in xprt_rdma_alloc_slot [ Upstream commit 765bde47fe7f197dabeb12da76831f40d0b20377 ] xprt_rdma_alloc_slot() and xprt_rdma_free_slot() lack serialization between the buffer pool and the backlog queue. A buffer freed after rpcrdma_buffer_get() finds the pool empty but before rpc_sleep_on() places the task on the backlog is returned to the pool with no waiter to wake, leaving the task stuck on the backlog indefinitely. After joining the backlog, re-check the pool and route any recovered buffer through xprt_wake_up_backlog(), whose queue lock serializes with concurrent wakeups and avoids double-assignment of slots. Because xprt_rdma_free_slot() does not hold reserve_lock, the XPRT_CONGESTED double-check in xprt_throttle_congested() is ineffective: a task can join the backlog through that path after free_slot has already found it empty and cleared the bit. Avoid this by using xprt_add_backlog_noncongested(), which queues the task without setting XPRT_CONGESTED, so every allocation reaches xprt_rdma_alloc_slot() and its post-sleep re-check. Fixes: edb41e61a54e ("xprtrdma: Make rpc_rqst part of rpcrdma_req") Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust Stable-dep-of: e786233d2e0b ("xprtrdma: Decouple req recycling from RPC completion") Signed-off-by: Sasha Levin --- include/linux/sunrpc/xprt.h | 2 ++ net/sunrpc/xprt.c | 16 ++++++++++++++++ net/sunrpc/xprtrdma/transport.c | 15 ++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index f46d1fb8f71ae..a82045804d34e 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -404,6 +404,8 @@ struct rpc_xprt * xprt_alloc(struct net *net, size_t size, unsigned int max_req); void xprt_free(struct rpc_xprt *); void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task); +void xprt_add_backlog_noncongested(struct rpc_xprt *xprt, + struct rpc_task *task); bool xprt_wake_up_backlog(struct rpc_xprt *xprt, struct rpc_rqst *req); void xprt_cleanup_ids(void); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 1023361845f9f..59b45aaadda21 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1663,6 +1663,22 @@ void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task) } EXPORT_SYMBOL_GPL(xprt_add_backlog); +/** + * xprt_add_backlog_noncongested - queue task on backlog + * @xprt: transport whose backlog queue receives the task + * @task: task to queue + * + * Like xprt_add_backlog, but does not set XPRT_CONGESTED. + * For transports whose free_slot path does not synchronize + * with xprt_throttle_congested via reserve_lock. + */ +void xprt_add_backlog_noncongested(struct rpc_xprt *xprt, + struct rpc_task *task) +{ + rpc_sleep_on(&xprt->backlog, task, xprt_complete_request_init); +} +EXPORT_SYMBOL_GPL(xprt_add_backlog_noncongested); + static bool __xprt_set_rq(struct rpc_task *task, void *data) { struct rpc_rqst *req = data; diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index ca079439f9cce..61706df5e485a 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -511,7 +511,20 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) out_sleep: task->tk_status = -EAGAIN; - xprt_add_backlog(xprt, task); + xprt_add_backlog_noncongested(xprt, task); + /* A buffer freed between buffer_get and rpc_sleep_on + * goes back to the pool with no waiter to wake. + * Re-check after joining the backlog to close that gap. + */ + req = rpcrdma_buffer_get(&r_xprt->rx_buf); + if (req) { + struct rpc_rqst *rqst = &req->rl_slot; + + if (!xprt_wake_up_backlog(xprt, rqst)) { + memset(rqst, 0, sizeof(*rqst)); + rpcrdma_buffer_put(&r_xprt->rx_buf, req); + } + } } /** From e7ae0883c8c89b901226de43862184bf37054338 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 6 Mar 2026 16:56:28 -0500 Subject: [PATCH 1282/3297] xprtrdma: Post receive buffers after RPC completion [ Upstream commit 704f3f640f72db4d44ec5ce3db8d4e150c974bc7 ] rpcrdma_post_recvs() runs in CQ poll context and its cost falls on the latency-critical path between polling a Receive completion and waking the RPC consumer. Every cycle spent refilling the Receive Queue delays delivery of the reply to the NFS layer. Move the rpcrdma_post_recvs() call in rpcrdma_reply_handler() to after the RPC has been decoded and completed. The larger batch size from the preceding patch provides sufficient Receive Queue headroom to absorb the brief delay before buffers are replenished. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust Stable-dep-of: e786233d2e0b ("xprtrdma: Decouple req recycling from RPC completion") Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 3aac1456e23e5..edd2ee0776e37 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1471,7 +1471,6 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) credits = 1; /* don't deadlock */ else if (credits > r_xprt->rx_ep->re_max_requests) credits = r_xprt->rx_ep->re_max_requests; - rpcrdma_post_recvs(r_xprt, credits + (buf->rb_bc_srv_max_requests << 1)); if (buf->rb_credits != credits) rpcrdma_update_cwnd(r_xprt, credits); @@ -1490,15 +1489,20 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) /* LocalInv completion will complete the RPC */ else kref_put(&req->rl_kref, rpcrdma_reply_done); - return; -out_badversion: - trace_xprtrdma_reply_vers_err(rep); - goto out; +out_post: + rpcrdma_post_recvs(r_xprt, + credits + (buf->rb_bc_srv_max_requests << 1)); + return; out_norqst: spin_unlock(&xprt->queue_lock); trace_xprtrdma_reply_rqst_err(rep); + rpcrdma_rep_put(buf, rep); + goto out_post; + +out_badversion: + trace_xprtrdma_reply_vers_err(rep); goto out; out_shortreply: From becc90a04780a1ac75678c1c726c780e72b645ba Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:01 -0400 Subject: [PATCH 1283/3297] xprtrdma: Use sendctx DMA state for Send signaling [ Upstream commit 2797ae7c929610fb2d2303a996a08173fa096730 ] Send signaling matters only when the prepared Send has page mappings to unmap. Today that test is expressed indirectly with rl_kref, because the Send-side reference is taken only for Sends with mapped SGEs. Split the SGE DMA unmap loop into its own helper and use sc_unmap_count directly for the signaling decision. This keeps the current behavior but removes one dependency on the old rl_kref semantics before the request lifetime rules are changed. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Stable-dep-of: e786233d2e0b ("xprtrdma: Decouple req recycling from RPC completion") Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/frwr_ops.c | 2 +- net/sunrpc/xprtrdma/rpc_rdma.c | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index 31434aeb8e29c..a82eefe5e6b58 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -404,7 +404,7 @@ int frwr_send(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req) ++num_wrs; } - if ((kref_read(&req->rl_kref) > 1) || num_wrs > ep->re_send_count) { + if (req->rl_sendctx->sc_unmap_count || num_wrs > ep->re_send_count) { send_wr->send_flags |= IB_SEND_SIGNALED; ep->re_send_count = min_t(unsigned int, ep->re_send_batch, num_wrs - ep->re_send_count); diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index edd2ee0776e37..5bd2cb6df9938 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -526,19 +526,11 @@ static void rpcrdma_sendctx_done(struct kref *kref) rep->rr_rxprt->rx_stats.reply_waits_for_send++; } -/** - * rpcrdma_sendctx_unmap - DMA-unmap Send buffer - * @sc: sendctx containing SGEs to unmap - * - */ -void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) +static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) { struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf; struct ib_sge *sge; - if (!sc->sc_unmap_count) - return; - /* The first two SGEs contain the transport header and * the inline buffer. These are always left mapped so * they can be cheaply re-used. @@ -547,7 +539,19 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) ++sge, --sc->sc_unmap_count) ib_dma_unmap_page(rdmab_device(rb), sge->addr, sge->length, DMA_TO_DEVICE); +} + +/** + * rpcrdma_sendctx_unmap - DMA-unmap Send buffer + * @sc: sendctx containing SGEs to unmap + * + */ +void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) +{ + if (!sc->sc_unmap_count) + return; + rpcrdma_sendctx_dma_unmap(sc); kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done); } From 53442c7d0c888e51b8bc3da196970a669cc6b294 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:02 -0400 Subject: [PATCH 1284/3297] xprtrdma: Decouple req recycling from RPC completion [ Upstream commit e786233d2e0bbff9a82e43f02ae3a46ab4b08ec3 ] rl_kref formerly served two distinct lifetimes through a single refcount: it gated when a Reply could wake its RPC task, and it gated when an rpcrdma_req could return to its free pool. The marshal path took the Send-side reference only when SGEs needed DMA-unmap (sc_unmap_count > 0), which made a Send carrying only pre-registered buffers an exception: the Reply handler dropped rl_kref from 1 to 0 and freed the req while the HCA might still be DMA-reading from its send buffer. Give rl_kref a narrower job. The RPC layer takes one reference when slot allocation hands a req out. rpcrdma_prepare_send_sges() takes a Send-side reference unconditionally after WR preparation succeeds. xprt_rdma_free_slot() and xprt_rdma_bc_free_rqst() drop the RPC-layer reference; rpcrdma_sendctx_unmap() drops the Send-side reference. The req returns to its free pool only after both owners have signed off. The existing kref_init(&req->rl_kref) call in rpcrdma_prepare_send_sges() is removed. Initialization moves to the slot-allocation paths (xprt_rdma_alloc_slot and rpcrdma_bc_rqst_get), and the release callback re-arms rl_kref before the req returns to a free pool. A re-init in the marshal path would discard the RPC-layer reference that already exists on entry. Three invariants follow: - Any rpcrdma_req held by an rpc_rqst has rl_kref >= 1. xprt_rdma_alloc_slot(), rpcrdma_bc_rqst_get(), and the backlog-wake branch in xprt_rdma_alloc_slot() each kref_init rl_kref before publishing the req. Without this invariant, an RPC task that aborts between slot allocation and marshal (gss_refresh failure or signal during call_connect, for example) would drive xprt_release() -> xprt_rdma_free_slot() -> kref_put against a refcount of zero, saturating refcount_t and stranding the slot. - The Send-side reference is taken only after WR prep succeeds. A mapping failure in rpcrdma_prepare_send_sges() runs rpcrdma_sendctx_cancel(), which DMA-unmaps the sendctx and clears sc_req without touching rl_kref. The sendctx ring walks in rpcrdma_sendctx_put_locked() and rpcrdma_sendctxs_destroy() skip entries with sc_req == NULL, so a burst of -EIO marshal failures cannot hold reqs off rb_send_bufs. - The release callback re-arms rl_kref so the next consumer enters with the invariant satisfied. Replies now complete the RPC directly. rpcrdma_reply_handler() calls rpcrdma_complete_rqst() in place of kref_put on the non-LocalInv branch. The LocalInv branch already completes the RPC from frwr_unmap_async() and is unaffected. Because Send-side references can now outlive RPC completion, connection teardown drains sendctx entries whose unsignaled Sends never had a later signaled completion to walk the ring. rpcrdma_sendctxs_destroy() walks the active range and runs rpcrdma_sendctx_unmap() on each entry with a non-NULL sc_req before the request buffers are reset, and is moved ahead of rpcrdma_reqs_reset() in rpcrdma_xprt_disconnect() so the reqs are still in their pre-reset state when the Send-side refs are released. The drain creates a teardown-ordering hazard on the backchannel path. With the new lifetime, releasing a bc_prealloc req from rpcrdma_req_release() re-adds it to bc_pa_list. The disconnect in xprt_rdma_destroy() runs after xprt_destroy_backchannel() has already emptied bc_pa_list, so the drained reqs would otherwise leak. xprt_rdma_destroy() now runs xprt_rdma_bc_destroy(xprt, 0) a second time after the disconnect to reclaim them. Fixes: 0ab115237025 ("xprtrdma: Wake RPCs directly in rpcrdma_wc_send path") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/backchannel.c | 5 ++- net/sunrpc/xprtrdma/rpc_rdma.c | 47 +++++++++++--------------- net/sunrpc/xprtrdma/transport.c | 55 ++++++++++++++++++++++++++++--- net/sunrpc/xprtrdma/verbs.c | 29 +++++++++++++--- net/sunrpc/xprtrdma/xprt_rdma.h | 2 +- 5 files changed, 97 insertions(+), 41 deletions(-) diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c index 8c817e755262d..02ffe7e1b5ed0 100644 --- a/net/sunrpc/xprtrdma/backchannel.c +++ b/net/sunrpc/xprtrdma/backchannel.c @@ -158,9 +158,7 @@ void xprt_rdma_bc_free_rqst(struct rpc_rqst *rqst) rpcrdma_rep_put(&r_xprt->rx_buf, rep); req->rl_reply = NULL; - spin_lock(&xprt->bc_pa_lock); - list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); - spin_unlock(&xprt->bc_pa_lock); + rpcrdma_req_put(req); xprt_put(xprt); } @@ -202,6 +200,7 @@ static struct rpc_rqst *rpcrdma_bc_rqst_get(struct rpcrdma_xprt *r_xprt) rqst->rq_xprt = xprt; __set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state); xdr_buf_init(&rqst->rq_snd_buf, rdmab_data(req->rl_sendbuf), size); + kref_init(&req->rl_kref); return rqst; } diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 5bd2cb6df9938..2f8d4f529be43 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -516,16 +516,6 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, return 0; } -static void rpcrdma_sendctx_done(struct kref *kref) -{ - struct rpcrdma_req *req = - container_of(kref, struct rpcrdma_req, rl_kref); - struct rpcrdma_rep *rep = req->rl_reply; - - rpcrdma_complete_rqst(rep); - rep->rr_rxprt->rx_stats.reply_waits_for_send++; -} - static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) { struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf; @@ -542,17 +532,26 @@ static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc) } /** - * rpcrdma_sendctx_unmap - DMA-unmap Send buffer + * rpcrdma_sendctx_unmap - DMA-unmap Send buffer and release Send owner * @sc: sendctx containing SGEs to unmap * */ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) { - if (!sc->sc_unmap_count) - return; + struct rpcrdma_req *req = sc->sc_req; rpcrdma_sendctx_dma_unmap(sc); - kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done); + sc->sc_req = NULL; + rpcrdma_req_put(req); +} + +/* No Send was posted. Release DMA mappings prepared for this + * sendctx, but leave the request reference count alone. + */ +static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc) +{ + rpcrdma_sendctx_dma_unmap(sc); + sc->sc_req = NULL; } /* Prepare an SGE for the RPC-over-RDMA transport header. @@ -744,8 +743,6 @@ static bool rpcrdma_prepare_noch_mapped(struct rpcrdma_xprt *r_xprt, tail->iov_len)) return false; - if (req->rl_sendctx->sc_unmap_count) - kref_get(&req->rl_kref); return true; } @@ -775,7 +772,6 @@ static bool rpcrdma_prepare_readch(struct rpcrdma_xprt *r_xprt, len -= len & 3; if (!rpcrdma_prepare_tail_iov(req, xdr, page_base, len)) return false; - kref_get(&req->rl_kref); } return true; @@ -804,7 +800,6 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, goto out_nosc; req->rl_sendctx->sc_unmap_count = 0; req->rl_sendctx->sc_req = req; - kref_init(&req->rl_kref); req->rl_wr.wr_cqe = &req->rl_sendctx->sc_cqe; req->rl_wr.sg_list = req->rl_sendctx->sc_sges; req->rl_wr.num_sge = 0; @@ -832,10 +827,14 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, goto out_unmap; } + /* The Send-side owner releases this reference when the + * Send has completed. + */ + kref_get(&req->rl_kref); return 0; out_unmap: - rpcrdma_sendctx_unmap(req->rl_sendctx); + rpcrdma_sendctx_cancel(req->rl_sendctx); out_nosc: trace_xprtrdma_prepsend_failed(&req->rl_slot, ret); return ret; @@ -1413,14 +1412,6 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) goto out; } -static void rpcrdma_reply_done(struct kref *kref) -{ - struct rpcrdma_req *req = - container_of(kref, struct rpcrdma_req, rl_kref); - - rpcrdma_complete_rqst(req->rl_reply); -} - /** * rpcrdma_reply_handler - Process received RPC/RDMA messages * @rep: Incoming rpcrdma_rep object to process @@ -1492,7 +1483,7 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) frwr_unmap_async(r_xprt, req); /* LocalInv completion will complete the RPC */ else - kref_put(&req->rl_kref, rpcrdma_reply_done); + rpcrdma_complete_rqst(rep); out_post: rpcrdma_post_recvs(r_xprt, diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 61706df5e485a..5569f17fdd9b5 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -279,6 +279,13 @@ xprt_rdma_destroy(struct rpc_xprt *xprt) cancel_delayed_work_sync(&r_xprt->rx_connect_worker); rpcrdma_xprt_disconnect(r_xprt); + + /* The disconnect's sendctx drain can return bc_prealloc reqs + * to bc_pa_list after xprt_destroy_backchannel() emptied it. + */ +#if defined(CONFIG_SUNRPC_BACKCHANNEL) + xprt_rdma_bc_destroy(xprt, 0); +#endif rpcrdma_buffer_destroy(&r_xprt->rx_buf); xprt_rdma_free_addresses(xprt); @@ -487,6 +494,45 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task) queue_delayed_work(system_long_wq, &r_xprt->rx_connect_worker, delay); } +/* rl_kref has two owners while a Send is outstanding: the rpc_rqst + * owner and the sendctx. Replies complete the RPC but do not drop + * either reference. The req returns to its free pool only after + * xprt_rdma_free_slot() or xprt_rdma_bc_free_rqst() has dropped the + * RPC-layer reference and rpcrdma_sendctx_unmap() has dropped the + * Send-side reference. + */ +static void rpcrdma_req_release(struct kref *kref) +{ + struct rpcrdma_req *req = + container_of(kref, struct rpcrdma_req, rl_kref); + struct rpc_rqst *rqst = &req->rl_slot; + struct rpc_xprt *xprt = rqst->rq_xprt; + struct rpcrdma_xprt *r_xprt; + + kref_init(&req->rl_kref); + +#if defined(CONFIG_SUNRPC_BACKCHANNEL) + if (bc_prealloc(rqst)) { + spin_lock(&xprt->bc_pa_lock); + list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); + spin_unlock(&xprt->bc_pa_lock); + return; + } +#endif + + if (xprt_wake_up_backlog(xprt, rqst)) + return; + + r_xprt = rpcx_to_rdmax(xprt); + memset(rqst, 0, sizeof(*rqst)); + rpcrdma_buffer_put(&r_xprt->rx_buf, req); +} + +void rpcrdma_req_put(struct rpcrdma_req *req) +{ + kref_put(&req->rl_kref, rpcrdma_req_release); +} + /** * xprt_rdma_alloc_slot - allocate an rpc_rqst * @xprt: controlling RPC transport @@ -505,6 +551,7 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) req = rpcrdma_buffer_get(&r_xprt->rx_buf); if (!req) goto out_sleep; + kref_init(&req->rl_kref); task->tk_rqstp = &req->rl_slot; task->tk_status = 0; return; @@ -520,6 +567,7 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) if (req) { struct rpc_rqst *rqst = &req->rl_slot; + kref_init(&req->rl_kref); if (!xprt_wake_up_backlog(xprt, rqst)) { memset(rqst, 0, sizeof(*rqst)); rpcrdma_buffer_put(&r_xprt->rx_buf, req); @@ -540,10 +588,7 @@ xprt_rdma_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *rqst) container_of(xprt, struct rpcrdma_xprt, rx_xprt); rpcrdma_reply_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst)); - if (!xprt_wake_up_backlog(xprt, rqst)) { - memset(rqst, 0, sizeof(*rqst)); - rpcrdma_buffer_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst)); - } + rpcrdma_req_put(rpcr_to_rdmar(rqst)); } static bool rpcrdma_check_regbuf(struct rpcrdma_xprt *r_xprt, @@ -716,7 +761,7 @@ void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) r_xprt->rx_stats.mrs_allocated, r_xprt->rx_stats.local_inv_needed, r_xprt->rx_stats.empty_sendctx_q, - r_xprt->rx_stats.reply_waits_for_send); + 0LU); /* was reply_waits_for_send; column preserved */ } static int diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 8abbd9c4045a4..59fc1aebe8c31 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -65,6 +65,8 @@ static int rpcrdma_sendctxs_create(struct rpcrdma_xprt *r_xprt); static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt); +static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf, + unsigned long item); static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, struct rpcrdma_sendctx *sc); static int rpcrdma_reqs_setup(struct rpcrdma_xprt *r_xprt); @@ -571,9 +573,9 @@ void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt) rpcrdma_xprt_drain(r_xprt); rpcrdma_reps_unmap(r_xprt); + rpcrdma_sendctxs_destroy(r_xprt); rpcrdma_reqs_reset(r_xprt); rpcrdma_mrs_destroy(r_xprt); - rpcrdma_sendctxs_destroy(r_xprt); if (rpcrdma_ep_put(ep)) rdma_destroy_id(id); @@ -605,6 +607,20 @@ static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt) if (!buf->rb_sc_ctxs) return; + + /* The QP is drained, but the final unsignaled Sends might not + * have been walked by a signaled Send completion. Release those + * Send owners before request buffers are reset. + */ + for (i = rpcrdma_sendctx_next(buf, buf->rb_sc_tail); + i != rpcrdma_sendctx_next(buf, buf->rb_sc_head); + i = rpcrdma_sendctx_next(buf, i)) { + struct rpcrdma_sendctx *sc = buf->rb_sc_ctxs[i]; + + if (sc && sc->sc_req) + rpcrdma_sendctx_unmap(sc); + } + for (i = 0; i <= buf->rb_sc_last; i++) kfree(buf->rb_sc_ctxs[i]); kfree(buf->rb_sc_ctxs); @@ -727,15 +743,20 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, struct rpcrdma_buffer *buf = &r_xprt->rx_buf; unsigned long next_tail; - /* Unmap SGEs of previously completed but unsignaled - * Sends by walking up the queue until @sc is found. + /* Release previously completed but unsignaled Sends by walking + * up the queue until @sc is found. Entries left behind by a + * failed rpcrdma_prepare_send_sges() have sc_req cleared. */ next_tail = buf->rb_sc_tail; do { + struct rpcrdma_sendctx *cur; + next_tail = rpcrdma_sendctx_next(buf, next_tail); /* ORDER: item must be accessed _before_ tail is updated */ - rpcrdma_sendctx_unmap(buf->rb_sc_ctxs[next_tail]); + cur = buf->rb_sc_ctxs[next_tail]; + if (cur->sc_req) + rpcrdma_sendctx_unmap(cur); } while (buf->rb_sc_ctxs[next_tail] != sc); diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 8147d2b414940..40a9aa0cda67f 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -410,7 +410,6 @@ struct rpcrdma_stats { /* accessed when receiving a reply */ unsigned long long total_rdma_reply; unsigned long long fixup_copy_count; - unsigned long reply_waits_for_send; unsigned long local_inv_needed; unsigned long nomsg_call_count; unsigned long bcall_count; @@ -488,6 +487,7 @@ void rpcrdma_buffer_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req); void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep); void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req); +void rpcrdma_req_put(struct rpcrdma_req *req); bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags); From f84949dd17847b5a6ada28ff6224144c7544a71f Mon Sep 17 00:00:00 2001 From: Dai Ngo Date: Tue, 26 May 2026 16:29:53 -0700 Subject: [PATCH 1285/3297] NFSv4/pnfs: defer return_range callbacks until after inode unlock [ Upstream commit 77b160b2d863d37f36b6c38e80a7d259ce939e69 ] Sometimes unmounting an NFS filesystem mounted with pNFS SCSI layouts triggers the following warning: BUG: scheduling while atomic: umount.nfs4/... __schedule_bug+0xbd/0x100 schedule_debug.constprop.0+0x19f/0x220 __schedule+0x10d/0x10a0 schedule+0x74/0x190 schedule_timeout+0xf5/0x220 io_schedule_timeout+0xd5/0x160 __wait_for_common+0x186/0x4b0 blk_execute_rq+0x2ef/0x3a0 scsi_execute_cmd+0x1ff/0x700 sd_pr_out_command.isra.0+0x242/0x380 [sd_mod] bl_unregister_scsi.constprop.0+0x109/0x3c0 [blocklayoutdriver] bl_unregister_dev+0x175/0x1c0 [blocklayoutdriver] bl_free_device+0x1f/0x1b0 [blocklayoutdriver] bl_free_deviceid_node+0x12/0x30 [blocklayoutdriver] nfs4_put_deviceid_node+0x171/0x360 [nfsv4] ext_tree_remove+0x11c/0x1d0 [blocklayoutdriver] _pnfs_return_layout+0x416/0x900 [nfsv4] nfs4_evict_inode+0x108/0x130 [nfsv4] evict+0x316/0x750 dispose_list+0xf1/0x1a0 evict_inodes+0x33f/0x440 generic_shutdown_super+0xc9/0x4e0 kill_anon_super+0x3a/0x90 nfs_kill_super+0x44/0x60 [nfs] deactivate_locked_super+0xb8/0x1b0 cleanup_mnt+0x25a/0x380 task_work_run+0x13e/0x210 exit_to_user_mode_loop+0x169/0x400 do_syscall_64+0x467/0x1550 entry_SYSCALL_64_after_hwframe+0x76/0x7e The warning occurs because the block layout driver unregisters the SCSI device while the inode lock is still held. Device unregistration issues a SCSI PR command, which may sleep, resulting in a "scheduling while atomic" warning. During layout return, ext_tree_remove() invokes the layout driver's return_range callback while holding the inode lock. For block layouts, this callback eventually calls bl_unregister_scsi(), which may block in scsi_execute_cmd() while issuing PR commands to the device. Fix this by deferring the return_range callbacks until after the inode lock has been released. The layout header reference count is incremented before invoking return_range(), ensuring that the layout header remains valid while the layout driver removes extents from the extent tree. Fixes: c88953d87f5c8 ("pnfs: add return_range method") Signed-off-by: Dai Ngo Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/callback_proc.c | 9 +++++---- fs/nfs/pnfs.c | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 8397c43358bd1..4f41986b875e8 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -254,6 +254,7 @@ static u32 initiate_file_draining(struct nfs_client *clp, struct pnfs_layout_hdr *lo; u32 rv = NFS4ERR_NOMATCHING_LAYOUT; LIST_HEAD(free_me_list); + bool return_range = false; ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid); if (IS_ERR(ino)) { @@ -298,13 +299,13 @@ static u32 initiate_file_draining(struct nfs_client *clp, /* Embrace your forgetfulness! */ rv = NFS4ERR_NOMATCHING_LAYOUT; - if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) { - NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, - &args->cbl_range); - } + return_range = true; } unlock: spin_unlock(&ino->i_lock); + if (return_range && NFS_SERVER(ino)->pnfs_curr_ld->return_range) + NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, + &args->cbl_range); pnfs_free_lseg_list(&free_me_list); /* Free all lsegs that are attached to commit buckets */ nfs_commit_inode(ino, 0); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index d3c1f49f39242..f84086437f531 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1463,8 +1463,6 @@ _pnfs_return_layout(struct inode *ino) pnfs_clear_layoutcommit(ino, &tmp_list); pnfs_mark_matching_lsegs_return(lo, &tmp_list, &range, 0); - if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) - NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range); /* Don't send a LAYOUTRETURN if list was initially empty */ if (!test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) || @@ -1476,6 +1474,8 @@ _pnfs_return_layout(struct inode *ino) send = pnfs_prepare_layoutreturn(lo, &stateid, &cred, NULL); spin_unlock(&ino->i_lock); + if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) + NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range); if (send) status = pnfs_send_layoutreturn(lo, &stateid, &cred, IOMODE_ANY, 0); From b694c7de94bc5267627cde2dce0335a96a5253ae Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Tue, 2 Jun 2026 19:04:38 +0800 Subject: [PATCH 1286/3297] nfs: keep PG_UPTODATE clear after read errors in page groups [ Upstream commit 3ff72e1cdf5c337b6acfcf3fcef748c5b9a5316b ] When a read request is split into multiple subrequests, earlier completions may advance PG_UPTODATE state for the page group once their bytes fall within hdr->good_bytes. If a later subrequest in the same group then completes with NFS_IOHDR_ERROR, the read path needs to clear any accumulated PG_UPTODATE state and keep later completions from rebuilding it. Otherwise, a subsequent successful subrequest can re-enter nfs_page_group_set_uptodate(), restore the page-group sync state, and leave stale PG_UPTODATE behind for nfs_page_group_destroy() to trip over in nfs_free_request(). Add a sticky page-group read-failed flag. Once any subrequest in the group is known to be bad, mark the group failed, clear any accumulated PG_UPTODATE state, and refuse further PG_UPTODATE synchronization for the rest of the completion walk. Fixes: 67d0338edd71 ("nfs: page group syncing in read path") Signed-off-by: Clark Wang Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/read.c | 25 ++++++++++++++++++++++++- include/linux/nfs_page.h | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 3c1fa320b3f1b..fe41e42729944 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -132,10 +132,32 @@ static void nfs_readpage_release(struct nfs_page *req, int error) static void nfs_page_group_set_uptodate(struct nfs_page *req) { - if (nfs_page_group_sync_on_bit(req, PG_UPTODATE)) + bool uptodate = false; + + nfs_page_group_lock(req); + if (!test_bit(PG_READ_FAILED, &req->wb_head->wb_flags) && + nfs_page_group_sync_on_bit_locked(req, PG_UPTODATE)) + uptodate = true; + nfs_page_group_unlock(req); + + if (uptodate) folio_mark_uptodate(nfs_page_to_folio(req)); } +static void nfs_page_group_mark_read_failed(struct nfs_page *req) +{ + struct nfs_page *tmp; + + nfs_page_group_lock(req); + set_bit(PG_READ_FAILED, &req->wb_head->wb_flags); + tmp = req; + do { + clear_bit(PG_UPTODATE, &tmp->wb_flags); + tmp = tmp->wb_this_page; + } while (tmp != req); + nfs_page_group_unlock(req); +} + static void nfs_read_completion(struct nfs_pgio_header *hdr) { unsigned long bytes = 0; @@ -172,6 +194,7 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr) if (bytes <= hdr->good_bytes) nfs_page_group_set_uptodate(req); else { + nfs_page_group_mark_read_failed(req); error = hdr->error; xchg(&nfs_req_openctx(req)->error, error); } diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index afe1d8f09d89f..4b9a35dbc062d 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -33,6 +33,7 @@ enum { PG_TEARDOWN, /* page group sync for destroy */ PG_UNLOCKPAGE, /* page group sync bit in read path */ PG_UPTODATE, /* page group sync bit in read path */ + PG_READ_FAILED, /* page group saw a read error */ PG_WB_END, /* page group sync bit in write path */ PG_REMOVE, /* page group sync bit in write path */ PG_CONTENDED1, /* Is someone waiting for a lock? */ From a70375f0b793ed438bb624ebfb1547a4e73a1877 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 4 Jun 2026 16:24:02 -0400 Subject: [PATCH 1287/3297] NFSv4/flexfiles: honor FF_FLAGS_NO_IO_THRU_MDS on fatal DS connect errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7a375cafc14ed151508f908ea5681caf0a9cc1d6 ] Commit f06bedfa62d5 ("pNFS/flexfiles: don't attempt pnfs on fatal DS errors") teaches ff_layout_{read,write}_pagelist() to return PNFS_NOT_ATTEMPTED when nfs4_ff_layout_prepare_ds() fails with a nfs_error_is_fatal() errno (e.g. -ETIMEDOUT from a SOFTCONN connect deadline, -ENOMEM, -ERESTARTSYS), so that the client gives up instead of spinning. pnfs_do_{read,write}() then dispatches the I/O through pnfs_{read,write}_through_mds() → nfs_pageio_reset_{read,write}_mds(). That fallback is unconditional and silently violates FF_FLAGS_NO_IO_THRU_MDS: when the layout segment carries the flag (typically single-mirror appliance layouts where MDS I/O is explicitly forbidden), the out_failed: path's \`&& !ds_fatal_error\` clause overrides the flag's short-circuit through ff_layout_avoid_mds_available_ds() and routes the I/O to the MDS file handle anyway. This is reachable in practice during a data-server restart: SOFTCONN exhaustion produces -ETIMEDOUT, which is fatal per nfs_error_is_fatal(), which triggers PNFS_NOT_ATTEMPTED, which silently goes to MDS. Preserve the upstream "don't spin on fatal errors" intent for layouts that permit MDS fallback. For layouts with FF_FLAGS_NO_IO_THRU_MDS set, mark the layout for return and request PNFS_TRY_AGAIN instead; if the server cannot supply a usable layout the failure now surfaces cleanly via pnfs_update_layout(), rather than via silent MDS I/O that contradicts the flag. Fixes: f06bedfa62d5 ("pNFS/flexfiles: don't attempt pnfs on fatal DS errors") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/flexfilelayout/flexfilelayout.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index bb3f6c18630cb..b602118d80216 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -2206,6 +2206,14 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr) out_failed: if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error) return PNFS_TRY_AGAIN; + if (ff_layout_no_fallback_to_mds(lseg)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET, + * never fall through to MDS I/O. + */ + pnfs_error_mark_layout_for_return(hdr->inode, lseg); + return PNFS_TRY_AGAIN; + } trace_pnfs_mds_fallback_read_pagelist(hdr->inode, hdr->args.offset, hdr->args.count, IOMODE_READ, NFS_I(hdr->inode)->layout, lseg); @@ -2291,6 +2299,14 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync) out_failed: if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error) return PNFS_TRY_AGAIN; + if (ff_layout_no_fallback_to_mds(lseg)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET, + * never fall through to MDS I/O. + */ + pnfs_error_mark_layout_for_return(hdr->inode, lseg); + return PNFS_TRY_AGAIN; + } trace_pnfs_mds_fallback_write_pagelist(hdr->inode, hdr->args.offset, hdr->args.count, IOMODE_RW, NFS_I(hdr->inode)->layout, lseg); From a6f147b23e36154a84f8c5f1dca3974925d56190 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 4 Jun 2026 16:24:03 -0400 Subject: [PATCH 1288/3297] NFSv4/flexfiles: honor FF_FLAGS_NO_IO_THRU_MDS in pg_get_mirror_count_write [ Upstream commit 1d62e659c0bf11649cf48e002c2a55d148f2610a ] The FF_FLAGS_NO_IO_THRU_MDS flag lives on each lseg, so any fallback decision made when there is no current lseg (e.g. between LAYOUTRETURN and the next LAYOUTGET) cannot run the per-lseg check. Introduce a sticky hdr-level ditto for FF_FLAGS_NO_IO_THRU_MDS in struct nfs4_flexfile_layout::flags (NFS4_FF_HDR_NO_IO_THRU_MDS bit), set whenever ff_layout_alloc_lseg() parses an lseg with the flag. The bit is never cleared for the lifetime of the layout hdr; the server is assumed to be consistent in its no-fallback policy per file. kzalloc() in ff_layout_alloc_layout_hdr() zero-initializes the field. Use the new ff_layout_hdr_no_fallback_to_mds() helper to gate ff_layout_pg_get_mirror_count_write(): when pnfs_update_layout() returns NULL (e.g. NFS_LAYOUT_BULK_RECALL, pnfs_layout_io_test_failed, pnfs_layoutgets_blocked) the existing code unconditionally calls nfs_pageio_reset_write_mds(). This is a source of unwanted WRITE to MDS. Fix it by checking NFS4_FF_HDR_NO_IO_THRU_MDS bit, and if set surface -EAGAIN instead; the writepage-side caller (nfs_do_writepage() for buffered, nfs_direct_write_reschedule() for O_DIRECT) then redirties the request so writeback retries via pNFS. Fixes: 260074cd8413 ("pNFS/flexfiles: Add support for FF_FLAGS_NO_IO_THRU_MDS") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Snitzer Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/flexfilelayout/flexfilelayout.c | 13 +++++++++++++ fs/nfs/flexfilelayout/flexfilelayout.h | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index b602118d80216..4604f10882722 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -638,6 +638,9 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, if (!p) goto out_sort_mirrors; fls->flags = be32_to_cpup(p); + if (fls->flags & FF_FLAGS_NO_IO_THRU_MDS) + set_bit(NFS4_FF_HDR_NO_IO_THRU_MDS, + &FF_LAYOUT_FROM_HDR(lh)->flags); p = xdr_inline_decode(&stream, 4); if (!p) @@ -1187,6 +1190,16 @@ ff_layout_pg_get_mirror_count_write(struct nfs_pageio_descriptor *pgio, 0, NFS4_MAX_UINT64, IOMODE_RW, NFS_I(pgio->pg_inode)->layout, pgio->pg_lseg); + if (NFS_I(pgio->pg_inode)->layout && + ff_layout_hdr_no_fallback_to_mds(NFS_I(pgio->pg_inode)->layout)) { + /* + * FF_FLAGS_NO_IO_THRU_MDS: no current lseg but the server's + * policy forbids MDS fallback. Surface -EAGAIN so writeback + * retries rather than silently issuing the WRITE via MDS. + */ + pgio->pg_error = -EAGAIN; + goto out; + } /* no lseg means that pnfs is not in use, so no mirroring here */ nfs_pageio_reset_write_mds(pgio); out: diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h index 17a008c8e97ce..a5bd00f69e824 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.h +++ b/fs/nfs/flexfilelayout/flexfilelayout.h @@ -112,12 +112,16 @@ struct nfs4_ff_layout_segment { struct nfs4_ff_layout_mirror *mirror_array[] __counted_by(mirror_array_cnt); }; +/* nfs4_flexfile_layout::flags bit indices */ +#define NFS4_FF_HDR_NO_IO_THRU_MDS 0 /* any lseg has had FF_FLAGS_NO_IO_THRU_MDS */ + struct nfs4_flexfile_layout { struct pnfs_layout_hdr generic_hdr; struct pnfs_ds_commit_info commit_info; struct list_head mirrors; struct list_head error_list; /* nfs4_ff_layout_ds_err */ ktime_t last_report_time; /* Layoutstat report times */ + unsigned long flags; }; struct nfs4_flexfile_layoutreturn_args { @@ -184,6 +188,18 @@ ff_layout_no_fallback_to_mds(struct pnfs_layout_segment *lseg) return FF_LAYOUT_LSEG(lseg)->flags & FF_FLAGS_NO_IO_THRU_MDS; } +/* + * Sticky hdr-level mirror of FF_FLAGS_NO_IO_THRU_MDS so callers that have + * no current lseg (e.g. between LAYOUTRETURN and the next LAYOUTGET) can + * still honor the no-MDS-fallback policy. + */ +static inline bool +ff_layout_hdr_no_fallback_to_mds(struct pnfs_layout_hdr *lo) +{ + return test_bit(NFS4_FF_HDR_NO_IO_THRU_MDS, + &FF_LAYOUT_FROM_HDR(lo)->flags); +} + static inline bool ff_layout_no_read_on_rw(struct pnfs_layout_segment *lseg) { From f161ef7b0dd2f51fdb002ba4a4e9bf0ee7409218 Mon Sep 17 00:00:00 2001 From: Yang Erkun Date: Thu, 26 Feb 2026 09:22:03 +0800 Subject: [PATCH 1289/3297] nfs: use nfsi->rwsem to protect traversal of the file lock list [ Upstream commit 4837fb36219e6c08b666bc31a86841bad8526358 ] Lingfeng identified a bug and suggested two solutions, but both appear to have issues. Generally, we cannot release flc_lock while iterating over the file lock list to avoid use-after-free (UAF) problems with file locks. However, functions like nfs_delegation_claim_locks and nfs4_reclaim_locks cannot adhere to this rule because recover_lock or nfs4_lock_delegation_recall may take a long time. To resolve this, NFS switches to using nfsi->rwsem for the same protection, and nfs_reclaim_locks follows this approach. Although nfs_delegation_claim_locks uses so_delegreturn_mutex instead, this is inadequate since a single inode can have multiple nfs4_state instances. Therefore, the fix is to also use nfsi->rwsem in this case. Furthermore, after commit c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update"), the functions nfs4_locku_done and nfs4_lock_done also break this rule because they call locks_lock_inode_wait without holding nfsi->rwsem. Simply adding this protection could cause many deadlocks, so instead, the call to locks_lock_inode_wait is moved into _nfs4_proc_setlk. Regarding the bug fixed by commit c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update"), it has been resolved after commit 0460253913e5 ("NFSv4: nfs4_do_open() is incorrectly triggering state recovery") because all slots are drained before calling nfs4_do_reclaim, which prevents concurrent stateid changes along this path. Also, nfs_delegation_claim_locks does not cause this concurrency either since when _nfs4_proc_setlk is called with NFS_DELEGATED_STATE, no RPC is sent, so nfs4_lock_done is not called. Therefore, nfs4_lock_delegation_recall from nfs_delegation_claim_locks is the first time the stateid is set. Reported-by: Li Lingfeng Closes: https://lore.kernel.org/all/20250419085709.1452492-1-lilingfeng3@huawei.com/ Closes: https://lore.kernel.org/all/20250715030559.2906634-1-lilingfeng3@huawei.com/ Fixes: c69899a17ca4 ("NFSv4: Update of VFS byte range lock must be atomic with the stateid update") Signed-off-by: Yang Erkun Reviewed-by: Jeff Layton Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- fs/nfs/delegation.c | 9 ++++++++- fs/nfs/nfs4proc.c | 22 +++++++++++----------- include/linux/nfs_xdr.h | 1 - 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 9d3a5f29f17fd..f95707fc0edff 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -158,6 +158,7 @@ int nfs4_check_delegation(struct inode *inode, fmode_t type) static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) { struct inode *inode = state->inode; + struct nfs_inode *nfsi = NFS_I(inode); struct file_lock *fl; struct file_lock_context *flctx = locks_inode_context(inode); struct list_head *list; @@ -167,6 +168,9 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state goto out; list = &flctx->flc_posix; + + /* Guard against reclaim and new lock/unlock calls */ + down_write(&nfsi->rwsem); spin_lock(&flctx->flc_lock); restart: for_each_file_lock(fl, list) { @@ -174,8 +178,10 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state continue; spin_unlock(&flctx->flc_lock); status = nfs4_lock_delegation_recall(fl, state, stateid); - if (status < 0) + if (status < 0) { + up_write(&nfsi->rwsem); goto out; + } spin_lock(&flctx->flc_lock); } if (list == &flctx->flc_posix) { @@ -183,6 +189,7 @@ static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_state goto restart; } spin_unlock(&flctx->flc_lock); + up_write(&nfsi->rwsem); out: return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index fc858b5c3f66d..403f7f8dfea2b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -7178,7 +7178,6 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) switch (task->tk_status) { case 0: renew_lease(calldata->server, calldata->timestamp); - locks_lock_inode_wait(calldata->lsp->ls_state->inode, &calldata->fl); if (nfs4_update_lock_stateid(calldata->lsp, &calldata->res.stateid)) break; @@ -7445,11 +7444,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) case 0: renew_lease(NFS_SERVER(d_inode(data->ctx->dentry)), data->timestamp); - if (data->arg.new_lock && !data->cancelled) { - data->fl.c.flc_flags &= ~(FL_SLEEP | FL_ACCESS); - if (locks_lock_inode_wait(lsp->ls_state->inode, &data->fl) < 0) - goto out_restart; - } if (data->arg.new_lock_owner != 0) { nfs_confirm_seqid(&lsp->ls_seqid, 0); nfs4_stateid_copy(&lsp->ls_stateid, &data->res.stateid); @@ -7559,11 +7553,10 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f msg.rpc_argp = &data->arg; msg.rpc_resp = &data->res; task_setup_data.callback_data = data; - if (recovery_type > NFS_LOCK_NEW) { - if (recovery_type == NFS_LOCK_RECLAIM) - data->arg.reclaim = NFS_LOCK_RECLAIM; - } else - data->arg.new_lock = 1; + + if (recovery_type == NFS_LOCK_RECLAIM) + data->arg.reclaim = NFS_LOCK_RECLAIM; + task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -7675,6 +7668,13 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock up_read(&nfsi->rwsem); mutex_unlock(&sp->so_delegreturn_mutex); status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW); + if (status) + goto out; + + down_read(&nfsi->rwsem); + request->c.flc_flags &= ~(FL_SLEEP | FL_ACCESS); + status = locks_lock_inode_wait(state->inode, request); + up_read(&nfsi->rwsem); out: request->c.flc_flags = flags; return status; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 31463286402fd..972059a016c9a 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -579,7 +579,6 @@ struct nfs_lock_args { struct nfs_lowner lock_owner; unsigned char block : 1; unsigned char reclaim : 1; - unsigned char new_lock : 1; unsigned char new_lock_owner : 1; }; From a57692ad365f321729a2d244c915492be77e728f Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 14 May 2026 20:55:52 -0400 Subject: [PATCH 1290/3297] PCI: mediatek: Fix operator precedence in PCIE_FTS_NUM_L0 macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 282305d7e9c0e27fd8b4df34b7cd5506a1eccdd6 ] The original PCIE_FTS_NUM_L0(x) macro was buggy due to improper operator precedence, where ((x) & 0xff << 8) was evaluated as ((x) & 0xff00). Instead of just fixing the parentheses, use the standard FIELD_PREP() macro. This makes the code more robust by automatically handling masks and shifts, while also adding compile-time type and range checking to ensure the value fits within PCIE_FTS_NUM_MASK. Fixes: 637cfacae96f ("PCI: mediatek: Add MediaTek PCIe host controller support") Signed-off-by: Li RongQing [mani: added the bitfield header include spotted by Sashiko] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260515005552.2343-1-lirongqing@baidu.com Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-mediatek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index d779baf1ae6d8..83880840b8764 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -7,6 +7,7 @@ * Honghui Zhang */ +#include #include #include #include @@ -61,7 +62,7 @@ /* MediaTek specific configuration registers */ #define PCIE_FTS_NUM 0x70c #define PCIE_FTS_NUM_MASK GENMASK(15, 8) -#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) +#define PCIE_FTS_NUM_L0(x) FIELD_PREP(PCIE_FTS_NUM_MASK, x) #define PCIE_FC_CREDIT 0x73c #define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) From f0aaa198e068b6546224f0dceaa64c5fec0aa811 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 4 Jun 2026 10:56:34 +0100 Subject: [PATCH 1291/3297] pwm: rzg2l-gpt: Add missing newlines to dev_err_probe() messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 898ab0f30e008e411ce93ddf81c4099abd9d4e46 ] dev_err_probe() internally calls dev_err() which uses pr_fmt() and printk(). Kernel log messages should end with a newline character to ensure proper log formatting. Add missing '\n' at the end of the error strings in rzg2l_gpt_probe(). Signed-off-by: Biju Das Link: https://patch.msgid.link/20260604095647.108654-5-biju.das.jz@bp.renesas.com Fixes: 061f087f5d0b ("pwm: Add support for RZ/G2L GPT") Signed-off-by: Uwe Kleine-König Signed-off-by: Sasha Levin --- drivers/pwm/pwm-rzg2l-gpt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index 4856af080e8e9..8534a2a9cf750 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -408,14 +408,14 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) rate = clk_get_rate(clk); if (!rate) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0\n"); /* * Refuse clk rates > 1 GHz to prevent overflow later for computing * period and duty cycle. */ if (rate > NSEC_PER_SEC) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz\n"); /* * Rate is in MHz and is always integer for peripheral clk @@ -424,7 +424,7 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) */ rzg2l_gpt->rate_khz = rate / KILO; if (rzg2l_gpt->rate_khz * KILO != rate) - return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000"); + return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000\n"); mutex_init(&rzg2l_gpt->lock); From a5c0ba31eef9ec11557004a05d4e7c5bd14c6be3 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 13:49:40 +0545 Subject: [PATCH 1292/3297] PCI: meson: Propagate devm_add_action_or_reset() failure [ Upstream commit b12341b98d5ac52f48ca1390e1e371aed81346c8 ] meson_pcie_probe_clock() enables a clock and then registers a devres action to disable it during teardown. If devm_add_action_or_reset() fails, it runs the action immediately, disabling the clock. The return value is currently ignored, so on that failure path, meson_pcie_probe_clock() returns the disabled clock and probe continues. Return the error so the existing probe error path unwinds normally. Fixes: 9c0ef6d34fdbf ("PCI: amlogic: Add the Amlogic Meson PCIe controller driver") Signed-off-by: Shuvam Pandey Signed-off-by: Manivannan Sadhasivam Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/177909148011.9588.6639767953842842291@gmail.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pci-meson.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 0694084f612b7..8d495bcc3a41a 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -204,7 +204,9 @@ static inline struct clk *meson_pcie_probe_clock(struct device *dev, return ERR_PTR(ret); } - devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + ret = devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + if (ret) + return ERR_PTR(ret); return clk; } From d3491b23bc207dd282d244488b910cbaf1e49356 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 22:44:18 +0545 Subject: [PATCH 1293/3297] PCI: meson: Add missing remove callback [ Upstream commit 4b0dc84b293984f75598881809fb2d3daf54a2a8 ] meson_pcie_probe() powers on the PHY and registers the DesignWare host bridge with dw_pcie_host_init(), but the driver has no remove callback. On driver unbind or module unload, the driver core therefore proceeds to devres cleanup without first unregistering the host bridge or powering off the PHY. Add a remove callback that deinitializes the DesignWare host bridge and powers off the PHY while device-managed resources are still valid. Fixes: 9c0ef6d34fdb ("PCI: amlogic: Add the Amlogic Meson PCIe controller driver") Signed-off-by: Shuvam Pandey Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/1a0c86ab264cdc1c79c917e984b90991af51d827.1779123847.git.shuvampandey1@gmail.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pci-meson.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 8d495bcc3a41a..225d887cd0a3e 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -453,6 +453,14 @@ static int meson_pcie_probe(struct platform_device *pdev) return ret; } +static void meson_pcie_remove(struct platform_device *pdev) +{ + struct meson_pcie *mp = platform_get_drvdata(pdev); + + dw_pcie_host_deinit(&mp->pci.pp); + meson_pcie_power_off(mp); +} + static const struct of_device_id meson_pcie_of_match[] = { { .compatible = "amlogic,axg-pcie", @@ -466,6 +474,7 @@ MODULE_DEVICE_TABLE(of, meson_pcie_of_match); static struct platform_driver meson_pcie_driver = { .probe = meson_pcie_probe, + .remove = meson_pcie_remove, .driver = { .name = "meson-pcie", .of_match_table = meson_pcie_of_match, From 4f129fc6f756f8541e5bff45b1804cc11b1ec712 Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Thu, 4 Jun 2026 21:19:30 -0700 Subject: [PATCH 1294/3297] fs/ntfs3: resize log->one_page_buf when adopting on-disk page size [ Upstream commit 5a35454179fe1041d9cd286f5d320ce0d448c12a ] log_replay() allocates log->one_page_buf using the page size that was chosen from the host PAGE_SIZE: log->one_page_buf = kmalloc(log->page_size, GFP_NOFS); Later, when a restart area is found, the log page size recorded on disk is adopted: t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size); if (log->page_size != t32) { log->l_size = log->orig_file_size; log->page_size = norm_file_page(t32, &log->l_size, t32 == DefaultLogPageSize); } If the on-disk page size is larger than the size used for the initial allocation, log->page_size grows but one_page_buf is left at its original, smaller size. A subsequent unaligned read_log_page() then reads log->page_size bytes into the undersized scratch buffer: page_buf = page_off ? log->one_page_buf : *buffer; err = ntfs_read_run_nb_ra(ni->mi.sbi, &ni->file.run, page_vbo, page_buf, log->page_size, NULL, &log->read_ahead); overflowing the allocation. This is reachable when mounting a dirty NTFS volume whose log was formatted with a page size larger than the buffer initially allocated on the mounting host (for example a 64K-log volume mounted on a host that allocated a 4K scratch buffer). Grow one_page_buf when the adopted on-disk page size exceeds the size used for the initial allocation. On krealloc() failure the original buffer is left intact and freed by the existing error path. Fixes: b46acd6a6a627 ("fs/ntfs3: Add NTFS journal") Reported-by: Carol L Soto Signed-off-by: Jamie Nguyen Signed-off-by: Konstantin Komarov Signed-off-by: Sasha Levin --- fs/ntfs3/fslog.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 124198539b950..8395ca003af03 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3936,9 +3936,28 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) */ t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size); if (log->page_size != t32) { + u32 old_page_size = log->page_size; + log->l_size = log->orig_file_size; log->page_size = norm_file_page(t32, &log->l_size, t32 == DefaultLogPageSize); + + /* + * If the adopted on-disk page size is larger than the size used + * to allocate one_page_buf above, grow the scratch buffer so a + * later read_log_page() cannot overflow it. + */ + if (log->page_size > old_page_size) { + void *buf; + + buf = krealloc(log->one_page_buf, log->page_size, + GFP_NOFS); + if (!buf) { + err = -ENOMEM; + goto out; + } + log->one_page_buf = buf; + } } if (log->page_size != t32 || From 3e86797c0699d9b4312e5df491699983352d74a5 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Tue, 14 Oct 2025 14:45:30 -0700 Subject: [PATCH 1295/3297] platform/x86:intel/pmc: Add support for multiple DMU GUIDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3b603955f2423cf668ebd5ba670019a5b4960cc5 ] Enable support for multiple DMU GUIDs to accommodate Arrow Lake H/U platforms. Arrow Lake U/H may have several GUIDs pointing to a single telemetry region providing die C6 value Add support to search for available GUIDs. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20251014214548.629023-3-xi.pardee@linux.intel.com [ij: add include & reverse logic in a loop] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Stable-dep-of: 375bbbbd112a ("platform/x86/intel/vsec: Restore BAR fallback for header walk") Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/pmc/arl.c | 6 ++++-- drivers/platform/x86/intel/pmc/core.c | 22 ++++++++++++++++++---- drivers/platform/x86/intel/pmc/core.h | 6 +++--- drivers/platform/x86/intel/pmc/mtl.c | 3 ++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index 17ad87b392abe..cc05a168c3721 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -720,9 +720,10 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_ return generic_core_init(pmcdev, pmc_dev_info); } +static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_pmc_dev = { .pci_func = 0, - .dmu_guid = ARL_PMT_DMU_GUID, + .dmu_guids = ARL_PMT_DMU_GUIDS, .regmap_list = arl_pmc_info_list, .map = &arl_socs_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, @@ -732,9 +733,10 @@ struct pmc_dev_info arl_pmc_dev = { .sub_req = pmc_core_pmt_get_lpm_req, }; +static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_h_pmc_dev = { .pci_func = 2, - .dmu_guid = ARL_PMT_DMU_GUID, + .dmu_guids = ARL_H_PMT_DMU_GUIDS, .regmap_list = arl_pmc_info_list, .map = &mtl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index ac3d19ae8c56d..ca126a253f9d0 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -20,6 +20,7 @@ enum header_type { #include #include #include +#include #include #include #include @@ -1281,7 +1282,20 @@ int get_primary_reg_base(struct pmc *pmc) return 0; } -void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) +static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, u32 *guids) +{ + struct telem_endpoint *ep; + unsigned int i; + + for (i = 0; guids[i]; i++) { + ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0); + if (!IS_ERR(ep)) + return ep; + } + return ERR_PTR(-ENODEV); +} + +void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) { struct telem_endpoint *ep; struct pci_dev *pcidev; @@ -1292,7 +1306,7 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) return; } - ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); + ep = pmc_core_register_endpoint(pcidev, guids); pci_dev_put(pcidev); if (IS_ERR(ep)) { dev_err(&pmcdev->pdev->dev, @@ -1689,8 +1703,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) } pmc_core_get_low_power_modes(pmcdev); - if (pmc_dev_info->dmu_guid) - pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid); + if (pmc_dev_info->dmu_guids) + pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids); if (ssram) { ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info); diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index d6818bd34768e..83d6e2e833785 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -481,7 +481,7 @@ enum pmc_index { /** * struct pmc_dev_info - Structure to keep PMC device info * @pci_func: Function number of the primary PMC - * @dmu_guid: Die Management Unit GUID + * @dmu_guids: List of Die Management Unit GUID * @regmap_list: Pointer to a list of pmc_info structure that could be * available for the platform. When set, this field implies * SSRAM support. @@ -495,7 +495,7 @@ enum pmc_index { */ struct pmc_dev_info { u8 pci_func; - u32 dmu_guid; + u32 *dmu_guids; struct pmc_info *regmap_list; const struct pmc_reg_map *map; const struct file_operations *sub_req_show; @@ -532,7 +532,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); int pmc_core_resume_common(struct pmc_dev *pmcdev); int get_primary_reg_base(struct pmc *pmc); void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); -void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid); +void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids); void pmc_core_set_device_d3(unsigned int device); int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 0b87e10f864e6..19470ca311cf7 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -992,9 +992,10 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in return generic_core_init(pmcdev, pmc_dev_info); } +static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info mtl_pmc_dev = { .pci_func = 2, - .dmu_guid = MTL_PMT_DMU_GUID, + .dmu_guids = MTL_PMT_DMU_GUIDS, .regmap_list = mtl_pmc_info_list, .map = &mtl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, From b95e1facc5b7f11a3f1362386cb696ac7043a06f Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Tue, 14 Oct 2025 14:45:32 -0700 Subject: [PATCH 1296/3297] platform/x86:intel/pmc: Rename PMC index variable to pmc_idx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7848154c3a11fb3ffbffd150f2185f97b5a6595a ] Rename all PMC index variables to pmc_idx in core.c. This improves code readability and consistency. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20251014214548.629023-5-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Stable-dep-of: 375bbbbd112a ("platform/x86/intel/vsec: Restore BAR fallback for header walk") Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/pmc/core.c | 108 +++++++++++++------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index ca126a253f9d0..5f58dfa989ad5 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -312,20 +312,20 @@ static inline u8 pmc_core_reg_read_byte(struct pmc *pmc, int offset) } static void pmc_core_display_map(struct seq_file *s, int index, int idx, int ip, - int pmc_index, u8 pf_reg, const struct pmc_bit_map **pf_map) + int pmc_idx, u8 pf_reg, const struct pmc_bit_map **pf_map) { seq_printf(s, "PMC%d:PCH IP: %-2d - %-32s\tState: %s\n", - pmc_index, ip, pf_map[idx][index].name, + pmc_idx, ip, pf_map[idx][index].name, pf_map[idx][index].bit_mask & pf_reg ? "Off" : "On"); } static int pmc_core_ppfear_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - unsigned int i; + unsigned int pmc_idx; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { - struct pmc *pmc = pmcdev->pmcs[i]; + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; const struct pmc_bit_map **maps; u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES]; unsigned int index, iter, idx, ip = 0; @@ -343,7 +343,7 @@ static int pmc_core_ppfear_show(struct seq_file *s, void *unused) for (idx = 0; maps[idx]; idx++) { for (index = 0; maps[idx][index].name && index < pmc->map->ppfear_buckets * 8; ip++, index++) - pmc_core_display_map(s, index, idx, ip, i, + pmc_core_display_map(s, index, idx, ip, pmc_idx, pf_regs[index / 8], maps); } } @@ -472,7 +472,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) struct pmc *pmc; const struct pmc_reg_map *map; u32 reg; - unsigned int pmc_index; + unsigned int pmc_idx; int ltr_index; ltr_index = value; @@ -480,8 +480,8 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) * is based on the contiguous indexes from ltr_show output. * pmc index and ltr index needs to be calculated from it. */ - for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_index++) { - pmc = pmcdev->pmcs[pmc_index]; + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_idx++) { + pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) continue; @@ -498,10 +498,10 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) ltr_index = ltr_index - (map->ltr_ignore_max + 2) - 1; } - if (pmc_index >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) + if (pmc_idx >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) return -EINVAL; - pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_index, ltr_index); + pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_idx, ltr_index); guard(mutex)(&pmcdev->lock); @@ -636,14 +636,14 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) u64 decoded_snoop_ltr, decoded_non_snoop_ltr, val; u32 ltr_raw_data, scale; u16 snoop_ltr, nonsnoop_ltr; - unsigned int i, index, ltr_index = 0; + unsigned int pmc_idx, index, ltr_index = 0; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { struct pmc *pmc; const struct pmc_bit_map *map; u32 ltr_ign_reg; - pmc = pmcdev->pmcs[i]; + pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) continue; @@ -677,7 +677,7 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) } seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\tLTR_IGNORE: %d\n", - ltr_index, i, map[index].name, ltr_raw_data, + ltr_index, pmc_idx, map[index].name, ltr_raw_data, decoded_non_snoop_ltr, decoded_snoop_ltr, ltr_ign_data); ltr_index++; @@ -690,15 +690,15 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr); static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - unsigned int pmcidx; + unsigned int pmc_idx; - for (pmcidx = 0; pmcidx < ARRAY_SIZE(pmcdev->pmcs); pmcidx++) { + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { const struct pmc_bit_map **maps; unsigned int arr_size, r_idx; u32 offset, counter; struct pmc *pmc; - pmc = pmcdev->pmcs[pmcidx]; + pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) continue; maps = pmc->map->s0ix_blocker_maps; @@ -712,7 +712,7 @@ static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) if (!map->blk) continue; counter = pmc_core_reg_read(pmc, offset); - seq_printf(s, "PMC%d:%-30s %-30d\n", pmcidx, + seq_printf(s, "PMC%d:%-30s %-30d\n", pmc_idx, map->name, counter); offset += map->blk * S0IX_BLK_SIZE; } @@ -724,13 +724,13 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker); static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) { - unsigned int i; + unsigned int pmc_idx; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { struct pmc *pmc; u32 ltr_ign; - pmc = pmcdev->pmcs[i]; + pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) continue; @@ -751,12 +751,12 @@ static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) static void pmc_core_ltr_restore_all(struct pmc_dev *pmcdev) { - unsigned int i; + unsigned int pmc_idx; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { struct pmc *pmc; - pmc = pmcdev->pmcs[i]; + pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) continue; @@ -795,10 +795,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_res); static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - unsigned int i; + unsigned int pmc_idx; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { - struct pmc *pmc = pmcdev->pmcs[i]; + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; const struct pmc_bit_map **maps; u32 offset; @@ -806,7 +806,7 @@ static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) continue; maps = pmc->map->lpm_sts; offset = pmc->map->lpm_status_offset; - pmc_core_lpm_display(pmc, NULL, s, offset, i, "STATUS", maps); + pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "STATUS", maps); } return 0; @@ -816,10 +816,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_sts_regs); static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - unsigned int i; + unsigned int pmc_idx; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { - struct pmc *pmc = pmcdev->pmcs[i]; + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; const struct pmc_bit_map **maps; u32 offset; @@ -827,7 +827,7 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) continue; maps = pmc->map->lpm_sts; offset = pmc->map->lpm_live_status_offset; - pmc_core_lpm_display(pmc, NULL, s, offset, i, "LIVE_STATUS", maps); + pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "LIVE_STATUS", maps); } return 0; @@ -920,11 +920,11 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) u32 sts_offset; u32 sts_offset_live; u32 *lpm_req_regs; - unsigned int mp, pmc_index; + unsigned int mp, pmc_idx; int num_maps; - for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) { - struct pmc *pmc = pmcdev->pmcs[pmc_index]; + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; const struct pmc_bit_map **maps; if (!pmc) @@ -945,7 +945,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) continue; /* Display the header */ - pmc_core_substate_req_header_show(s, pmc_index, HEADER_STATUS); + pmc_core_substate_req_header_show(s, pmc_idx, HEADER_STATUS); /* Loop over maps */ for (mp = 0; mp < num_maps; mp++) { @@ -983,7 +983,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) } /* Display the element name in the first column */ - seq_printf(s, "pmc%d: %34s |", pmc_index, map[i].name); + seq_printf(s, "pmc%d: %34s |", pmc_idx, map[i].name); /* Loop over the enabled states and display if required */ pmc_for_each_mode(mode, pmcdev) { @@ -1567,7 +1567,7 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * { struct pci_dev *pcidev __free(pci_dev_put) = NULL; struct telem_endpoint *ep; - unsigned int i; + unsigned int pmc_idx; u32 guid; int ret; @@ -1575,10 +1575,10 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * if (!pcidev) return -ENODEV; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { struct pmc *pmc; - pmc = pmcdev->pmcs[i]; + pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) continue; @@ -1610,7 +1610,7 @@ static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 return NULL; } -static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) +static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) { struct pmc_ssram_telemetry pmc_ssram_telemetry; @@ -1618,7 +1618,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) struct pmc *pmc; int ret; - ret = pmc_ssram_telemetry_get_pmc_info(pmc_index, &pmc_ssram_telemetry); + ret = pmc_ssram_telemetry_get_pmc_info(pmc_idx, &pmc_ssram_telemetry); if (ret) return ret; @@ -1626,7 +1626,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) if (!map) return -ENODEV; - pmc = pmcdev->pmcs[pmc_index]; + pmc = pmcdev->pmcs[pmc_idx]; /* Memory for primary PMC has been allocated */ if (!pmc) { pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL); @@ -1643,7 +1643,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) return -ENOMEM; } - pmcdev->pmcs[pmc_index] = pmc; + pmcdev->pmcs[pmc_idx] = pmc; return 0; } @@ -1715,8 +1715,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) return 0; unmap_regbase: - for (unsigned int i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { - struct pmc *pmc = pmcdev->pmcs[i]; + for (unsigned int pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; if (pmc && pmc->regbase) iounmap(pmc->regbase); @@ -1809,10 +1809,10 @@ static void pmc_core_do_dmi_quirks(struct pmc *pmc) static void pmc_core_clean_structure(struct platform_device *pdev) { struct pmc_dev *pmcdev = platform_get_drvdata(pdev); - unsigned int i; + unsigned int pmc_idx; - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { - struct pmc *pmc = pmcdev->pmcs[i]; + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; if (pmc && pmc->regbase) iounmap(pmc->regbase); @@ -1972,7 +1972,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; const struct pmc_bit_map **maps = pmc->map->lpm_sts; int offset = pmc->map->lpm_status_offset; - unsigned int i; + unsigned int pmc_idx, i; /* Check if the syspend used S0ix */ if (pm_suspend_via_firmware()) @@ -2010,13 +2010,13 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) if (pmc->map->slps0_dbg_maps) pmc_core_slps0_display(pmc, dev, NULL); - for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { - struct pmc *pmc = pmcdev->pmcs[i]; + for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) continue; if (pmc->map->lpm_sts) - pmc_core_lpm_display(pmc, dev, NULL, offset, i, "STATUS", maps); + pmc_core_lpm_display(pmc, dev, NULL, offset, pmc_idx, "STATUS", maps); } return 0; From c0d97519c9dfb25d43b68e720decc5f672f57b4f Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Tue, 14 Oct 2025 14:45:33 -0700 Subject: [PATCH 1297/3297] platform/x86:intel/pmc: Relocate lpm_req_guid to pmc_reg_map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c2bc11f1f204ef916ec96e45cf329e42873b37d6 ] Relocate the lpm_req_guid field from pmc_info to pmc_reg_map. The previous implementation stored lpm_req_guid in pmc_info and relied on pmc_core_find_guid() to retrieve the correct GUID, which was unnecessary. Since lpm_req_guid is specific to PMC, pmc_reg_map is a more appropriate location for this information. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20251014214548.629023-6-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Stable-dep-of: 375bbbbd112a ("platform/x86/intel/vsec: Restore BAR fallback for header walk") Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/pmc/arl.c | 6 ++---- drivers/platform/x86/intel/pmc/core.c | 15 ++------------- drivers/platform/x86/intel/pmc/core.h | 4 +++- drivers/platform/x86/intel/pmc/lnl.c | 2 +- drivers/platform/x86/intel/pmc/mtl.c | 6 +++--- drivers/platform/x86/intel/pmc/ptl.c | 3 +-- 6 files changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index cc05a168c3721..7a802b799e0ae 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -281,6 +281,7 @@ static const struct pmc_reg_map arl_socs_reg_map = { .etr3_offset = ETR3_OFFSET, .pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET, .pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP, + .lpm_req_guid = SOCS_LPM_REQ_GUID, }; static const struct pmc_bit_map arl_pchs_ltr_show_map[] = { @@ -648,26 +649,23 @@ static const struct pmc_reg_map arl_pchs_reg_map = { .lpm_num_maps = ADL_LPM_NUM_MAPS, .lpm_reg_index = ARL_LPM_REG_INDEX, .etr3_offset = ETR3_OFFSET, + .lpm_req_guid = PCHS_LPM_REQ_GUID, }; static struct pmc_info arl_pmc_info_list[] = { { - .guid = IOEP_LPM_REQ_GUID, .devid = PMC_DEVID_ARL_IOEP, .map = &mtl_ioep_reg_map, }, { - .guid = SOCS_LPM_REQ_GUID, .devid = PMC_DEVID_ARL_SOCS, .map = &arl_socs_reg_map, }, { - .guid = PCHS_LPM_REQ_GUID, .devid = PMC_DEVID_ARL_PCHS, .map = &arl_pchs_reg_map, }, { - .guid = SOCM_LPM_REQ_GUID, .devid = PMC_DEVID_ARL_SOCM, .map = &mtl_socm_reg_map, }, diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 5f58dfa989ad5..e272c971d73a5 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1444,15 +1444,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info } } -static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) -{ - for (; list->map; ++list) - if (list->map == map) - return list->guid; - - return 0; -} - /* * This function retrieves low power mode requirement data from PMC Low * Power Mode (LPM) table. @@ -1568,7 +1559,6 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * struct pci_dev *pcidev __free(pci_dev_put) = NULL; struct telem_endpoint *ep; unsigned int pmc_idx; - u32 guid; int ret; pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func)); @@ -1582,11 +1572,10 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * if (!pmc) continue; - guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); - if (!guid) + if (!pmc->map->lpm_req_guid) return -ENXIO; - ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); + ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0); if (IS_ERR(ep)) { dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); return -EPROBE_DEFER; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 83d6e2e833785..7d2ad88324b84 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -355,6 +355,7 @@ struct pmc_bit_map { * @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter * @num_s0ix_blocker: Number of S0ix blockers * @blocker_req_offset: Telemetry offset to S0ix blocker low power mode substate requirement table + * @lpm_req_guid: Telemetry GUID to read low power mode substate requirement table * * Each PCH has unique set of register offsets and bit indexes. This structure * captures them to have a common implementation. @@ -396,6 +397,8 @@ struct pmc_reg_map { const u8 *lpm_reg_index; const u32 pson_residency_offset; const u32 pson_residency_counter_step; + /* GUID for telemetry regions */ + const u32 lpm_req_guid; }; /** @@ -405,7 +408,6 @@ struct pmc_reg_map { * specific attributes */ struct pmc_info { - u32 guid; u16 devid; const struct pmc_reg_map *map; }; diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index 6fa027e7071f4..1cd81ee54dcf8 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -533,11 +533,11 @@ static const struct pmc_reg_map lnl_socm_reg_map = { .s0ix_blocker_maps = lnl_blk_maps, .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, .lpm_reg_index = LNL_LPM_REG_INDEX, + .lpm_req_guid = SOCM_LPM_REQ_GUID, }; static struct pmc_info lnl_pmc_info_list[] = { { - .guid = SOCM_LPM_REQ_GUID, .devid = PMC_DEVID_LNL_SOCM, .map = &lnl_socm_reg_map, }, diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 19470ca311cf7..57508cbf9cd42 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -473,6 +473,7 @@ const struct pmc_reg_map mtl_socm_reg_map = { .lpm_status_offset = MTL_LPM_STATUS_OFFSET, .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, .lpm_reg_index = MTL_LPM_REG_INDEX, + .lpm_req_guid = SOCP_LPM_REQ_GUID, }; static const struct pmc_bit_map mtl_ioep_pfear_map[] = { @@ -797,6 +798,7 @@ const struct pmc_reg_map mtl_ioep_reg_map = { .lpm_en_offset = MTL_LPM_EN_OFFSET, .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, .lpm_reg_index = MTL_LPM_REG_INDEX, + .lpm_req_guid = IOEP_LPM_REQ_GUID, }; static const struct pmc_bit_map mtl_ioem_pfear_map[] = { @@ -944,21 +946,19 @@ static const struct pmc_reg_map mtl_ioem_reg_map = { .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, .lpm_reg_index = MTL_LPM_REG_INDEX, + .lpm_req_guid = IOEM_LPM_REQ_GUID, }; static struct pmc_info mtl_pmc_info_list[] = { { - .guid = SOCP_LPM_REQ_GUID, .devid = PMC_DEVID_MTL_SOCM, .map = &mtl_socm_reg_map, }, { - .guid = IOEP_LPM_REQ_GUID, .devid = PMC_DEVID_MTL_IOEP, .map = &mtl_ioep_reg_map, }, { - .guid = IOEM_LPM_REQ_GUID, .devid = PMC_DEVID_MTL_IOEM, .map = &mtl_ioem_reg_map }, diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c index 1b35b84e06fa2..1f48e2bbc699f 100644 --- a/drivers/platform/x86/intel/pmc/ptl.c +++ b/drivers/platform/x86/intel/pmc/ptl.c @@ -528,16 +528,15 @@ static const struct pmc_reg_map ptl_pcdp_reg_map = { .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, .num_s0ix_blocker = PTL_NUM_S0IX_BLOCKER, .blocker_req_offset = PTL_BLK_REQ_OFFSET, + .lpm_req_guid = PCDP_LPM_REQ_GUID, }; static struct pmc_info ptl_pmc_info_list[] = { { - .guid = PCDP_LPM_REQ_GUID, .devid = PMC_DEVID_PTL_PCDH, .map = &ptl_pcdp_reg_map, }, { - .guid = PCDP_LPM_REQ_GUID, .devid = PMC_DEVID_PTL_PCDP, .map = &ptl_pcdp_reg_map, }, From e6523bcafeb61b61d2ffd993a5edc19e7bdaa147 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 15 Dec 2025 22:38:00 -0800 Subject: [PATCH 1298/3297] platform/x86/intel/vsec: correct kernel-doc comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 20e20b147cf7cb6780a5b95da2a0e37c52cd1015 ] Fix kernel-doc warnings in intel_vsec.h to eliminate all kernel-doc warnings: Warning: include/linux/intel_vsec.h:92 struct member 'read_telem' not described in 'pmt_callbacks' Warning: include/linux/intel_vsec.h:146 expecting prototype for struct intel_sec_device. Prototype was for struct intel_vsec_device instead Warning: include/linux/intel_vsec.h:146 struct member 'priv_data_size' not described in 'intel_vsec_device' In struct pmt_callbacks, correct the kernel-doc for @read_telem. kernel-doc doesn't support documenting callback function parameters, so drop the '@' signs on those and use "* *" to make them somewhat readable in the produced documentation output. Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251216063801.2896495-1-rdunlap@infradead.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Stable-dep-of: 375bbbbd112a ("platform/x86/intel/vsec: Restore BAR fallback for header walk") Signed-off-by: Sasha Levin --- include/linux/intel_vsec.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index d7f1f531660a8..d551174b00491 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -80,13 +80,13 @@ enum intel_vsec_quirks { /** * struct pmt_callbacks - Callback infrastructure for PMT devices - * ->read_telem() when specified, called by client driver to access PMT data (instead - * of direct copy). - * @pdev: PCI device reference for the callback's use - * @guid: ID of data to acccss - * @data: buffer for the data to be copied - * @off: offset into the requested buffer - * @count: size of buffer + * @read_telem: when specified, called by client driver to access PMT + * data (instead of direct copy). + * * pdev: PCI device reference for the callback's use + * * guid: ID of data to acccss + * * data: buffer for the data to be copied + * * off: offset into the requested buffer + * * count: size of buffer */ struct pmt_callbacks { int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count); @@ -120,7 +120,7 @@ struct intel_vsec_platform_info { }; /** - * struct intel_sec_device - Auxbus specific device information + * struct intel_vsec_device - Auxbus specific device information * @auxdev: auxbus device struct for auxbus access * @pcidev: pci device associated with the device * @resource: any resources shared by the parent @@ -128,6 +128,7 @@ struct intel_vsec_platform_info { * @num_resources: number of resources * @id: xarray id * @priv_data: any private data needed + * @priv_data_size: size of private data area * @quirks: specified quirks * @base_addr: base address of entries (if specified) * @cap_id: the enumerated id of the vsec feature From 817ab332d37ceecafb4ca67437ec11817422acd4 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:42 -0700 Subject: [PATCH 1299/3297] platform/x86/intel/vsec: Decouple add/link helpers from PCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c62fd96a04e4a7b847448f97ecfe9f3fe706e7b3 ] This refactor prepares for adding ACPI-enumerated PMT endpoints. While intel_vsec is bound to PCI today, some helpers are used by code that will also register PMT endpoints from non-PCI (ACPI) paths. Clean up PCI-specific plumbing where it isn’t strictly required and rely on generic struct device where possible. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260313015202.3660072-4-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen Stable-dep-of: 375bbbbd112a ("platform/x86/intel/vsec: Restore BAR fallback for header walk") Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/vsec.c | 13 +++++++++---- drivers/platform/x86/intel/vsec_tpmi.c | 2 +- include/linux/intel_vsec.h | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 96fc27d88fbf3..cf1fc63ab6df2 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -158,18 +158,23 @@ static bool vsec_driver_present(int cap_id) */ static const struct pci_device_id intel_vsec_pci_ids[]; -static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, +static int intel_vsec_link_devices(struct device *parent, struct device *dev, int consumer_id) { const struct vsec_feature_dependency *deps; enum vsec_device_state *state; struct device **suppliers; struct vsec_priv *priv; + struct pci_dev *pdev; int supplier_id; if (!consumer_id) return 0; + if (!dev_is_pci(parent)) + return 0; + + pdev = to_pci_dev(parent); if (!pci_match_id(intel_vsec_pci_ids, pdev)) return 0; @@ -204,7 +209,7 @@ static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, return 0; } -int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, +int intel_vsec_add_aux(struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name) { @@ -252,7 +257,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, if (ret) goto cleanup_aux; - ret = intel_vsec_link_devices(pdev, &auxdev->dev, intel_vsec_dev->cap_id); + ret = intel_vsec_link_devices(parent, &auxdev->dev, intel_vsec_dev->cap_id); if (ret) goto cleanup_aux; @@ -343,7 +348,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he * Pass the ownership of intel_vsec_dev and resource within it to * intel_vsec_add_aux() */ - return intel_vsec_add_aux(pdev, parent, no_free_ptr(intel_vsec_dev), + return intel_vsec_add_aux(parent, no_free_ptr(intel_vsec_dev), intel_vsec_name(header->id)); } diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index a8ccdd801ddd4..7eb0f86469cd0 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -655,7 +655,7 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, * feature_vsec_dev and res memory are also freed as part of * device deletion. */ - return intel_vsec_add_aux(vsec_dev->pcidev, &vsec_dev->auxdev.dev, + return intel_vsec_add_aux(&vsec_dev->auxdev.dev, feature_vsec_dev, feature_id_name); } diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index d551174b00491..49a746ec01289 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -184,7 +184,7 @@ struct pmt_feature_group { struct telemetry_region regions[]; }; -int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, +int intel_vsec_add_aux(struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name); From 4df30a4dc0e975e6ec1aa9bfd5d42aa8d7273138 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:43 -0700 Subject: [PATCH 1300/3297] platform/x86/intel/vsec: Switch exported helpers from pci_dev to device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 353042d54d82f6c46449f0ee38c244b5a13c1fe4 ] Preparatory refactor for ACPI-enumerated PMT endpoints. Several exported PMT/VSEC interfaces and structs carried struct pci_dev * even though callers only need a generic struct device. Move those to struct device * so the same APIs work for PCI and ACPI parents. Acked-by: Rodrigo Vivi Signed-off-by: David E. Box Link: https://patch.msgid.link/20260313015202.3660072-5-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Stable-dep-of: 375bbbbd112a ("platform/x86/intel/vsec: Restore BAR fallback for header walk") Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_debugfs.c | 2 +- drivers/gpu/drm/xe/xe_hwmon.c | 2 +- drivers/gpu/drm/xe/xe_vsec.c | 7 ++- drivers/gpu/drm/xe/xe_vsec.h | 4 +- drivers/platform/x86/intel/pmc/core.c | 4 +- .../platform/x86/intel/pmc/ssram_telemetry.c | 2 +- drivers/platform/x86/intel/pmt/class.c | 8 ++-- drivers/platform/x86/intel/pmt/class.h | 5 ++- drivers/platform/x86/intel/pmt/discovery.c | 4 +- drivers/platform/x86/intel/pmt/telemetry.c | 13 +++--- drivers/platform/x86/intel/pmt/telemetry.h | 12 ++--- drivers/platform/x86/intel/sdsi.c | 5 ++- drivers/platform/x86/intel/vsec.c | 44 +++++++++++-------- drivers/platform/x86/intel/vsec_tpmi.c | 6 +-- include/linux/intel_vsec.h | 13 +++--- 15 files changed, 71 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c index 7b48bf90cab8f..bbd7c5938a04d 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.c +++ b/drivers/gpu/drm/xe/xe_debugfs.c @@ -45,7 +45,7 @@ static void read_residency_counter(struct xe_device *xe, struct xe_mmio *mmio, u64 residency = 0; int ret; - ret = xe_pmt_telem_read(to_pci_dev(xe->drm.dev), + ret = xe_pmt_telem_read(xe->drm.dev, xe_mmio_read32(mmio, PUNIT_TELEMETRY_GUID), &residency, offset, sizeof(residency)); if (ret != sizeof(residency)) { diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c index b6790589e6237..65621775878b6 100644 --- a/drivers/gpu/drm/xe/xe_hwmon.c +++ b/drivers/gpu/drm/xe/xe_hwmon.c @@ -464,7 +464,7 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy) if (hwmon->xe->info.platform == XE_BATTLEMAGE) { u64 pmt_val; - ret = xe_pmt_telem_read(to_pci_dev(hwmon->xe->drm.dev), + ret = xe_pmt_telem_read(hwmon->xe->drm.dev, xe_mmio_read32(mmio, PUNIT_TELEMETRY_GUID), &pmt_val, BMG_ENERGY_STATUS_PMT_OFFSET, sizeof(pmt_val)); if (ret != sizeof(pmt_val)) { diff --git a/drivers/gpu/drm/xe/xe_vsec.c b/drivers/gpu/drm/xe/xe_vsec.c index 8f23a27871b60..997fe0c8858e3 100644 --- a/drivers/gpu/drm/xe/xe_vsec.c +++ b/drivers/gpu/drm/xe/xe_vsec.c @@ -141,10 +141,10 @@ static int xe_guid_decode(u32 guid, int *index, u32 *offset) return 0; } -int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset, +int xe_pmt_telem_read(struct device *dev, u32 guid, u64 *data, loff_t user_offset, u32 count) { - struct xe_device *xe = pdev_to_xe_device(pdev); + struct xe_device *xe = kdev_to_xe_device(dev); void __iomem *telem_addr = xe->mmio.regs + BMG_TELEMETRY_OFFSET; u32 mem_region; u32 offset; @@ -197,7 +197,6 @@ void xe_vsec_init(struct xe_device *xe) { struct intel_vsec_platform_info *info; struct device *dev = xe->drm.dev; - struct pci_dev *pdev = to_pci_dev(dev); enum xe_vsec platform; platform = get_platform_info(xe); @@ -220,6 +219,6 @@ void xe_vsec_init(struct xe_device *xe) * Register a VSEC. Cleanup is handled using device managed * resources. */ - intel_vsec_register(pdev, info); + intel_vsec_register(dev, info); } MODULE_IMPORT_NS("INTEL_VSEC"); diff --git a/drivers/gpu/drm/xe/xe_vsec.h b/drivers/gpu/drm/xe/xe_vsec.h index dabfb4e02d707..a25b4e6e681b5 100644 --- a/drivers/gpu/drm/xe/xe_vsec.h +++ b/drivers/gpu/drm/xe/xe_vsec.h @@ -6,10 +6,10 @@ #include -struct pci_dev; +struct device; struct xe_device; void xe_vsec_init(struct xe_device *xe); -int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset, u32 count); +int xe_pmt_telem_read(struct device *dev, u32 guid, u64 *data, loff_t user_offset, u32 count); #endif diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index e272c971d73a5..ef712f3bc7ae8 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1288,7 +1288,7 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, unsigned int i; for (i = 0; guids[i]; i++) { - ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0); + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, guids[i], 0); if (!IS_ERR(ep)) return ep; } @@ -1575,7 +1575,7 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * if (!pmc->map->lpm_req_guid) return -ENXIO; - ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0); + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc->map->lpm_req_guid, 0); if (IS_ERR(ep)) { dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); return -EPROBE_DEFER; diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 03fad9331fc0c..6f6e83e70fc5a 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -60,7 +60,7 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem info.base_addr = ssram_base; info.parent = &pcidev->dev; - return intel_vsec_register(pcidev, &info); + return intel_vsec_register(&pcidev->dev, &info); } static inline u64 get_base(void __iomem *addr, u32 offset) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 7c3023d5d91de..8077274726b5d 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -60,11 +60,11 @@ pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) return count; } -int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, +int pmt_telem_read_mmio(struct device *dev, struct pmt_callbacks *cb, u32 guid, void *buf, void __iomem *addr, loff_t off, u32 count) { if (cb && cb->read_telem) - return cb->read_telem(pdev, guid, buf, off, count); + return cb->read_telem(dev, guid, buf, off, count); addr += off; @@ -99,7 +99,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, if (count > entry->size - off) count = entry->size - off; - count = pmt_telem_read_mmio(entry->pcidev, entry->cb, entry->header.guid, buf, + count = pmt_telem_read_mmio(entry->ep->dev, entry->cb, entry->header.guid, buf, entry->base, off, count); return count; @@ -208,7 +208,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, struct intel_vsec_device *ivdev, struct resource *disc_res) { - struct pci_dev *pci_dev = ivdev->pcidev; + struct pci_dev *pci_dev = to_pci_dev(ivdev->dev); struct device *dev = &ivdev->auxdev.dev; struct intel_pmt_header *header = &entry->header; u8 bir; diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 3c5ad5f52bca6..1ae56a5baad23 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -19,11 +19,12 @@ #define GET_BIR(v) ((v) & GENMASK(2, 0)) #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) +struct device; struct pci_dev; extern struct class intel_pmt_class; struct telem_endpoint { - struct pci_dev *pcidev; + struct device *dev; struct telem_header header; struct pmt_callbacks *cb; void __iomem *base; @@ -65,7 +66,7 @@ struct intel_pmt_namespace { struct intel_pmt_entry *entry); }; -int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, +int pmt_telem_read_mmio(struct device *dev, struct pmt_callbacks *cb, u32 guid, void *buf, void __iomem *addr, loff_t off, u32 count); bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, diff --git a/drivers/platform/x86/intel/pmt/discovery.c b/drivers/platform/x86/intel/pmt/discovery.c index e500aa327d237..c482368bfaaef 100644 --- a/drivers/platform/x86/intel/pmt/discovery.c +++ b/drivers/platform/x86/intel/pmt/discovery.c @@ -542,7 +542,7 @@ static int pmt_features_probe(struct auxiliary_device *auxdev, const struct auxi if (!priv) return -ENOMEM; - priv->parent = &ivdev->pcidev->dev; + priv->parent = ivdev->dev; auxiliary_set_drvdata(auxdev, priv); priv->dev = device_create(&intel_pmt_class, &auxdev->dev, MKDEV(0, 0), priv, @@ -609,7 +609,7 @@ void intel_pmt_get_features(struct intel_pmt_entry *entry) mutex_lock(&feature_list_lock); list_for_each_entry(feature, &pmt_feature_list, list) { - if (feature->priv->parent != &entry->ep->pcidev->dev) + if (feature->priv->parent != entry->ep->dev) continue; pmt_get_features(entry, feature); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index a4dfca6cac199..e11f7bde41b20 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -112,7 +112,7 @@ static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev, return -ENOMEM; ep = entry->ep; - ep->pcidev = ivdev->pcidev; + ep->dev = ivdev->dev; ep->header.access_type = entry->header.access_type; ep->header.guid = entry->header.guid; ep->header.base_offset = entry->header.base_offset; @@ -204,7 +204,7 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info) goto unlock; } - info->pdev = entry->ep->pcidev; + info->dev = entry->ep->dev; info->header = entry->ep->header; unlock: @@ -218,9 +218,10 @@ static int pmt_copy_region(struct telemetry_region *region, struct intel_pmt_entry *entry) { + struct pci_dev *pdev = to_pci_dev(entry->ep->dev); struct oobmsm_plat_info *plat_info; - plat_info = intel_vsec_get_mapping(entry->ep->pcidev); + plat_info = intel_vsec_get_mapping(pdev); if (IS_ERR(plat_info)) return PTR_ERR(plat_info); @@ -308,7 +309,7 @@ int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) if (offset + NUM_BYTES_QWORD(count) > size) return -EINVAL; - pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base, offset, + pmt_telem_read_mmio(ep->dev, ep->cb, ep->header.guid, data, ep->base, offset, NUM_BYTES_QWORD(count)); return ep->present ? 0 : -EPIPE; @@ -335,7 +336,7 @@ int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count) EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, "INTEL_PMT_TELEMETRY"); struct telem_endpoint * -pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) +pmt_telem_find_and_register_endpoint(struct device *dev, u32 guid, u16 pos) { int devid = 0; int inst = 0; @@ -348,7 +349,7 @@ pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) if (err) return ERR_PTR(err); - if (ep_info.header.guid == guid && ep_info.pdev == pcidev) { + if (ep_info.header.guid == guid && ep_info.dev == dev) { if (inst == pos) return pmt_telem_register_endpoint(devid); ++inst; diff --git a/drivers/platform/x86/intel/pmt/telemetry.h b/drivers/platform/x86/intel/pmt/telemetry.h index d45af5512b4e2..0f88c5e7d90e9 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.h +++ b/drivers/platform/x86/intel/pmt/telemetry.h @@ -6,8 +6,8 @@ #define PMT_TELEM_TELEMETRY 0 #define PMT_TELEM_CRASHLOG 1 +struct device; struct telem_endpoint; -struct pci_dev; struct telem_header { u8 access_type; @@ -17,7 +17,7 @@ struct telem_header { }; struct telem_endpoint_info { - struct pci_dev *pdev; + struct device *dev; struct telem_header header; }; @@ -71,8 +71,8 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); /** * pmt_telem_find_and_register_endpoint() - Get a telemetry endpoint from - * pci_dev device, guid and pos - * @pdev: PCI device inside the Intel vsec + * device, guid and pos + * @dev: device inside the Intel vsec * @guid: GUID of the telemetry space * @pos: Instance of the guid * @@ -80,8 +80,8 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); * * endpoint - On success returns pointer to the telemetry endpoint * * -ENXIO - telemetry endpoint not found */ -struct telem_endpoint *pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, - u32 guid, u16 pos); +struct telem_endpoint * +pmt_telem_find_and_register_endpoint(struct device *dev, u32 guid, u16 pos); /** * pmt_telem_read() - Read qwords from counter sram using sample id diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index da75f53d0bcc0..d7e37d4ace238 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -599,13 +599,14 @@ static int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table) return 0; } -static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent, +static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct device *dev, struct disc_table *disc_table, struct resource *disc_res) { u32 access_type = FIELD_GET(DT_ACCESS_TYPE, disc_table->access_info); u32 size = FIELD_GET(DT_SIZE, disc_table->access_info); u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset); u32 offset = DT_OFFSET(disc_table->offset); + struct pci_dev *parent = to_pci_dev(dev); struct resource res = {}; /* Starting location of SDSi MMIO region based on access type */ @@ -681,7 +682,7 @@ static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_de return ret; /* Map the SDSi mailbox registers */ - ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res); + ret = sdsi_map_mbox_registers(priv, intel_cap_dev->dev, &disc_table, disc_res); if (ret) return ret; diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index cf1fc63ab6df2..cb065de62f8f0 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -274,7 +274,7 @@ int intel_vsec_add_aux(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, +static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *header, const struct intel_vsec_platform_info *info, unsigned long cap_id, u64 base_addr) { @@ -288,18 +288,18 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he if (info->parent) parent = info->parent; else - parent = &pdev->dev; + parent = dev; if (!intel_vsec_supported(header->id, info->caps)) return -EINVAL; if (!header->num_entries) { - dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); + dev_dbg(dev, "Invalid 0 entry count for header id %d\n", header->id); return -EINVAL; } if (!header->entry_size) { - dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); + dev_dbg(dev, "Invalid 0 entry size for header id %d\n", header->id); return -EINVAL; } @@ -331,7 +331,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he release_mem_region(tmp->start, resource_size(tmp)); } - intel_vsec_dev->pcidev = pdev; + intel_vsec_dev->dev = dev; intel_vsec_dev->resource = no_free_ptr(res); intel_vsec_dev->num_resources = header->num_entries; intel_vsec_dev->quirks = info->quirks; @@ -409,13 +409,14 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) return 0; } -static int intel_vsec_register_device(struct pci_dev *pdev, +static int intel_vsec_register_device(struct device *dev, struct intel_vsec_header *header, const struct intel_vsec_platform_info *info, u64 base_addr) { const struct vsec_feature_dependency *consumer_deps; struct vsec_priv *priv; + struct pci_dev *pdev; unsigned long cap_id; int ret; @@ -427,8 +428,12 @@ static int intel_vsec_register_device(struct pci_dev *pdev, * Only track dependencies for devices probed by the VSEC driver. * For others using the exported APIs, add the device directly. */ + if (!dev_is_pci(dev)) + return intel_vsec_add_dev(dev, header, info, cap_id, base_addr); + + pdev = to_pci_dev(dev); if (!pci_match_id(intel_vsec_pci_ids, pdev)) - return intel_vsec_add_dev(pdev, header, info, cap_id, base_addr); + return intel_vsec_add_dev(dev, header, info, cap_id, base_addr); priv = pci_get_drvdata(pdev); if (priv->state[cap_id] == STATE_REGISTERED || @@ -444,7 +449,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, base_addr); + ret = intel_vsec_add_dev(dev, header, info, cap_id, base_addr); if (ret) priv->state[cap_id] = STATE_SKIP; else @@ -456,7 +461,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, return -EAGAIN; } -static bool intel_vsec_walk_header(struct pci_dev *pdev, +static bool intel_vsec_walk_header(struct device *dev, const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; @@ -464,7 +469,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(pdev, *header, info, info->base_addr); + ret = intel_vsec_register_device(dev, *header, info, info->base_addr); if (!ret) have_devices = true; } @@ -512,7 +517,7 @@ 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->dev, &header, info, pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -558,7 +563,7 @@ 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->dev, &header, info, pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -569,13 +574,13 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, return have_devices; } -int intel_vsec_register(struct pci_dev *pdev, +int intel_vsec_register(struct device *dev, const struct intel_vsec_platform_info *info) { - if (!pdev || !info || !info->headers) + if (!dev || !info || !info->headers) return -EINVAL; - if (!intel_vsec_walk_header(pdev, info)) + if (!intel_vsec_walk_header(dev, info)) return -ENODEV; else return 0; @@ -601,7 +606,7 @@ static bool intel_vsec_get_features(struct pci_dev *pdev, found = true; if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && - intel_vsec_walk_header(pdev, info)) + intel_vsec_walk_header(&pdev->dev, info)) found = true; return found; @@ -682,7 +687,10 @@ int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, { struct vsec_priv *priv; - priv = pci_get_drvdata(vsec_dev->pcidev); + if (!dev_is_pci(vsec_dev->dev)) + return -ENODEV; + + priv = pci_get_drvdata(to_pci_dev(vsec_dev->dev)); if (!priv) return -EINVAL; @@ -825,7 +833,7 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) xa_for_each(&auxdev_array, index, intel_vsec_dev) { /* check if pdev doesn't match */ - if (pdev != intel_vsec_dev->pcidev) + if (&pdev->dev != intel_vsec_dev->dev) continue; devm_release_action(&pdev->dev, intel_vsec_remove_aux, &intel_vsec_dev->auxdev); diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 7eb0f86469cd0..e5f79c4a1d270 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -530,7 +530,7 @@ static const struct file_operations mem_write_ops = { .release = single_release, }; -#define tpmi_to_dev(info) (&info->vsec_dev->pcidev->dev) +#define tpmi_to_dev(info) ((info)->vsec_dev->dev) static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info) { @@ -642,7 +642,7 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, tmp->flags = IORESOURCE_MEM; } - feature_vsec_dev->pcidev = vsec_dev->pcidev; + feature_vsec_dev->dev = vsec_dev->dev; feature_vsec_dev->resource = res; feature_vsec_dev->num_resources = pfs->pfs_header.num_entries; feature_vsec_dev->priv_data = &tpmi_info->plat_info; @@ -742,7 +742,7 @@ static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, i static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) { struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev); - struct pci_dev *pci_dev = vsec_dev->pcidev; + struct pci_dev *pci_dev = to_pci_dev(vsec_dev->dev); struct intel_tpmi_info *tpmi_info; u64 pfs_start = 0; int ret, i; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 49a746ec01289..4eecb2a6bac4f 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -29,6 +29,7 @@ #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) #define TABLE_OFFSET_SHIFT 3 +struct device; struct pci_dev; struct resource; @@ -82,14 +83,14 @@ enum intel_vsec_quirks { * struct pmt_callbacks - Callback infrastructure for PMT devices * @read_telem: when specified, called by client driver to access PMT * data (instead of direct copy). - * * pdev: PCI device reference for the callback's use + * * dev: device reference for the callback's use * * guid: ID of data to acccss * * data: buffer for the data to be copied * * off: offset into the requested buffer * * count: size of buffer */ struct pmt_callbacks { - int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count); + int (*read_telem)(struct device *dev, u32 guid, u64 *data, loff_t off, u32 count); }; struct vsec_feature_dependency { @@ -122,7 +123,7 @@ struct intel_vsec_platform_info { /** * struct intel_vsec_device - Auxbus specific device information * @auxdev: auxbus device struct for auxbus access - * @pcidev: pci device associated with the device + * @dev: struct device associated with the device * @resource: any resources shared by the parent * @ida: id reference * @num_resources: number of resources @@ -135,7 +136,7 @@ struct intel_vsec_platform_info { */ struct intel_vsec_device { struct auxiliary_device auxdev; - struct pci_dev *pcidev; + struct device *dev; struct resource *resource; struct ida *ida; int num_resources; @@ -199,13 +200,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, +int intel_vsec_register(struct device *dev, 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, +static inline int intel_vsec_register(struct device *dev, const struct intel_vsec_platform_info *info) { return -ENODEV; From d6565e08166c8bea4029f2eaddd8f7e58f71fd06 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:44 -0700 Subject: [PATCH 1301/3297] platform/x86/intel/vsec: Return real error codes from registration path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a6ce8bf3c993d8c2e8a6aeb2596429c101fe4462 ] Stop collapsing registration results into booleans. Make intel_vsec_walk_header() return int and propagate the first non-zero error from intel_vsec_register_device(). intel_vsec_register() now returns that error directly and 0 on success. This preserves success behavior while surfacing meaningful errors instead of hiding them behind a bool/-ENODEV, which makes debugging and probe ordering issues clearer. Reviewed-by: Ilpo Järvinen Signed-off-by: David E. Box Link: https://patch.msgid.link/20260313015202.3660072-6-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen Stable-dep-of: 375bbbbd112a ("platform/x86/intel/vsec: Restore BAR fallback for header walk") Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/vsec.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index cb065de62f8f0..4e2e9f6a586b1 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -461,20 +461,19 @@ static int intel_vsec_register_device(struct device *dev, return -EAGAIN; } -static bool intel_vsec_walk_header(struct device *dev, - const struct intel_vsec_platform_info *info) +static int intel_vsec_walk_header(struct device *dev, + const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; - bool have_devices = false; int ret; for ( ; *header; header++) { ret = intel_vsec_register_device(dev, *header, info, info->base_addr); - if (!ret) - have_devices = true; + if (ret) + return ret; } - return have_devices; + return 0; } static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, @@ -580,10 +579,7 @@ int intel_vsec_register(struct device *dev, if (!dev || !info || !info->headers) return -EINVAL; - if (!intel_vsec_walk_header(dev, info)) - return -ENODEV; - else - return 0; + return intel_vsec_walk_header(dev, info); } EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); From 3f4476a089a6d217e2687e0a8faa4d83abffb9cd Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 29 May 2026 11:31:49 -0700 Subject: [PATCH 1302/3297] platform/x86/intel/vsec: Restore BAR fallback for header walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 375bbbbd112af028ee0b45d833a6233c23d19bbf ] The base_addr refactor changed intel_vsec_walk_header() to pass info->base_addr as the discovery-table base address. For the PCI VSEC driver this info comes from driver_data, but exported callers may provide their own static headers and leave base_addr unset. For xe, this made the discovery-table base address zero instead of the BAR selected by header->tbir, preventing PMT endpoints from being created. Restore the previous behavior for the header-walk path by falling back to pci_resource_start(pdev, header->tbir) when base_addr is not specified. Keep explicit base_addr override behavior unchanged. This preserves the refactor structure while fixing the functional regression in manual-header users. Fixes: 904b333fc51c ("platform/x86/intel/vsec: Refactor base_addr handling") Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260529183150.129744-1-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/vsec.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 4e2e9f6a586b1..1e292eaeb209d 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -465,10 +465,25 @@ static int intel_vsec_walk_header(struct device *dev, const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; + u64 base_addr; int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(dev, *header, info, info->base_addr); + if (info->base_addr) { + base_addr = info->base_addr; + } else { + struct pci_dev *pdev; + + if (!dev_is_pci(dev)) { + dev_err(dev, "non-PCI device without a base address\n"); + return -EINVAL; + } + + pdev = to_pci_dev(dev); + base_addr = pci_resource_start(pdev, (*header)->tbir); + } + + ret = intel_vsec_register_device(dev, *header, info, base_addr); if (ret) return ret; } From 2a8244988316a4e1a2e0ad5a2d65295add2ae09d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:37:52 -0300 Subject: [PATCH 1303/3297] perf tools: Fix get_max_num() size_t underflow on empty sysfs file [ Upstream commit 0a012113bb3a44482c163f16f4db03ccaa37a339 ] get_max_num() reads a sysfs file (cpu/possible, cpu/present, or node/possible) and scans backward from the end to find the last number. If the file is empty, filename__read_str() returns num == 0. The loop `while (--num)` decrements the size_t from 0 to SIZE_MAX, reading backward across the heap until a comma or hyphen is found or unmapped memory is hit. Add an early return for empty files before the backward scan. Fixes: 7780c25bae59fd04 ("perf tools: Allow ability to map cpus to nodes easily") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Don Zickus Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index d0516797a0088..bbf959b443ab1 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -420,6 +420,12 @@ static int get_max_num(char *path, int *max) buf[num] = '\0'; + /* empty file — nothing to parse */ + if (num == 0) { + err = -1; + goto out; + } + /* start on the right, to find highest node num */ while (--num) { if ((buf[num] == ',') || (buf[num] == '-')) { From 504028f561b19e9c3121ab24e4bce5ea9db20802 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:43:52 -0300 Subject: [PATCH 1304/3297] perf tools: Use scnprintf() in cpu_map__snprint() to prevent overflow [ Upstream commit 7953a3a9b8e02e98c6e6958f291d0ae22393e46a ] cpu_map__snprint() accumulates snprintf() return values in ret. snprintf() returns the number of characters that *would have been written* on truncation, not the actual count. When a fragmented CPU list exceeds the buffer, ret grows past size, causing `size - ret` to underflow (both are size_t), and subsequent snprintf() calls write past the end of the caller's stack buffer. Switch to scnprintf() which returns the actual number of characters written, making ret accumulation safe by construction. Fixes: a24020e6b7cf6eb8 ("perf tools: Change cpu_map__fprintf output") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cpumap.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index bbf959b443ab1..d243eee115427 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -664,21 +664,21 @@ size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) if (start == -1) { start = i; if (last) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, i).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, i).cpu); } } else if (((i - start) != (cpu.cpu - perf_cpu_map__cpu(map, start).cpu)) || last) { int end = i - 1; if (start == end) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, start).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, start).cpu); } else { - ret += snprintf(buf + ret, size - ret, - "%s%d-%d", COMMA, - perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d-%d", COMMA, + perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); } first = false; start = i; From b9e8406651dcc1c19238aad11861a758683525b4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 20:57:49 -0300 Subject: [PATCH 1305/3297] perf tools: Use perf_env__get_cpu_topology() in machine__resolve() [ Upstream commit 5484b43a0ec8231c36fba6ead654cb72dbba8b8f ] machine__resolve() accesses env->cpu[al->cpu].socket_id after checking al->cpu >= 0 and env->cpu != NULL, but without validating al->cpu against env->nr_cpus_avail. Since al->cpu comes from the untrusted perf.data sample, a crafted file with a large CPU index causes an out-of-bounds heap read. Use perf_env__get_cpu_topology() which validates both NULL and bounds. Also bounds-check al->cpu before the cast to struct perf_cpu (int16_t): without this, values like 65536 silently truncate to 0, bypassing the accessor's internal check and returning CPU 0's topology. Fixes: 0c4c4debb0adda4c ("perf tools: Add processor socket info to hist_entry and addr_location") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Kan Liang Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/event.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index fcf44149feb20..f3c331c702472 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -15,6 +15,7 @@ #include #include "cpumap.h" #include "dso.h" +#include "env.h" #include "event.h" #include "debug.h" #include "hist.h" @@ -786,8 +787,18 @@ int machine__resolve(struct machine *machine, struct addr_location *al, if (al->cpu >= 0) { struct perf_env *env = machine->env; - if (env && env->cpu) - al->socket = env->cpu[al->cpu].socket_id; + /* + * Bounds-check al->cpu (s32) before casting to struct perf_cpu + * (int16_t): without this, e.g. 65536 truncates to 0 and silently + * returns CPU 0's topology. Can go once perf_cpu.cpu is widened. + */ + if (env && al->cpu < env->nr_cpus_avail) { + struct cpu_topology_map *topo; + + topo = perf_env__get_cpu_topology(env, (struct perf_cpu){ al->cpu }); + if (topo) + al->socket = topo->socket_id; + } } /* Account for possible out-of-order switch events. */ From 2e0dd50e5a4dabe8079632f18004691126c09183 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 21 May 2026 10:12:56 +0100 Subject: [PATCH 1306/3297] PCI: rcar-host: Remove unused LIST_HEAD(res) [ Upstream commit 6ba90ce2069ae923b0ec787aebdf2d786e5d2a58 ] Remove the unused LIST_HEAD(res) declaration from rcar_pcie_hw_enable(). The macro instantiation defines an unused 'struct list_head res' variable, which conflicts with a valid resource loop-local 'struct resource *res' declaration further down in the function, triggering a compiler variable shadowing warning: drivers/pci/controller/pcie-rcar-host.c:357:34: warning: declaration of 'res' shadows a previous local [-Wshadow] 357 | struct resource *res = win->res; Fixes: ce351636c67f75a9 ("PCI: rcar: Add suspend/resume") Signed-off-by: Lad Prabhakar Signed-off-by: Manivannan Sadhasivam Reviewed-by: Geert Uytterhoeven Reviewed-by: Marek Vasut Link: https://patch.msgid.link/20260521091256.15737-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-rcar-host.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 213028052aa58..cd9171eebc289 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -346,7 +346,6 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host) struct rcar_pcie *pcie = &host->pcie; struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct resource_entry *win; - LIST_HEAD(res); int i = 0; /* Try setting 5 GT/s link speed */ From 77051ef66e4ad282c373384707d94f287fa1ed32 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:48:13 -0300 Subject: [PATCH 1307/3297] perf sched: Bounds-check prio before test_bit() in timehist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4477dc01fcfc7f404772a67e0c1e056541ceb61d ] timehist_skip_sample() reads prio from untrusted tracepoint data via perf_sample__intval(sample, "prev_prio") without bounds validation. A crafted perf.data with prev_prio >= MAX_PRIO (140) causes test_bit() to read past the end of the prio_bitmap, which is only MAX_PRIO bits. Add a prio >= 0 guard before the test_bit() call and skip out-of-range values (>= MAX_PRIO) that can never match the user's filter set. The original prio != -1 already let all negatives other than -1 through (after an undefined-behavior bitmap read); the new prio >= 0 guard preserves that pass-through behavior — negative means "no priority info", so the event is shown unfiltered — while fixing the OOB. Values >= MAX_PRIO are skipped because they cannot be represented in the filter bitmap. Fixes: 9b3a48bbe20d9692 ("perf sched timehist: Add --prio option") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Yang Jihong Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index e027626022e19..d38b8d9dee4ed 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2613,7 +2613,9 @@ static bool timehist_skip_sample(struct perf_sched *sched, else if (evsel__name_is(evsel, "sched:sched_switch")) prio = evsel__intval(evsel, sample, "prev_prio"); - if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) { + /* negative prio means no info; out-of-range prio can't match the filter */ + if (prio >= 0 && + (prio >= MAX_PRIO || !test_bit(prio, sched->prio_bitmap))) { rc = true; sched->skipped_samples++; } From c32fe40b0c745983191e5b89110ea8c062db7d79 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:49:16 -0300 Subject: [PATCH 1308/3297] perf sched: Fix idle-hist callchain display using wrong rb_first variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d9b99dc8148e0c1f5da3942131b47e0d21187a32 ] timehist_print_idlehist_callchain() calls rb_first_cached() on sorted_root, but the sort function (callchain_param.sort) populates it via rb_insert_color() on the plain rb_root member — not the cached variant. This means rb_leftmost is never set, so rb_first_cached() always returns NULL and the entire callchain summary is silently dropped from --idle-hist output. The original code in ba957ebb54893aca ("perf sched timehist: Show callchains for idle stat") was correct — it used struct rb_root and rb_first(). The bug was introduced when sorted_root was converted to rb_root_cached without converting the sort insertion path to use rb_insert_color_cached(). Use rb_first(&root->rb_root) to match how the tree was populated. Fixes: cb4c13a5137766c3 ("perf sched: Use cached rbtrees") Reported-by: sashiko-bot Cc: Davidlohr Bueso Cc: Namhyung Kim Acked-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d38b8d9dee4ed..f5bef8f98756f 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3093,7 +3093,8 @@ static size_t timehist_print_idlehist_callchain(struct rb_root_cached *root) size_t ret = 0; FILE *fp = stdout; struct callchain_node *chain; - struct rb_node *rb_node = rb_first_cached(root); + /* sort() uses rb_insert_color() on rb_root, not rb_root_cached */ + struct rb_node *rb_node = rb_first(&root->rb_root); printf(" %16s %8s %s\n", "Idle time (msec)", "Count", "Callchains"); printf(" %.16s %.8s %.50s\n", graph_dotted_line, graph_dotted_line, From 56ad33189ed5f7c1c4121bf6e9611b6f394bfdff Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:23:15 -0300 Subject: [PATCH 1309/3297] perf bpf: Use scnprintf() in snprintf_hex() and synthesize_bpf_prog_name() [ Upstream commit cab3a9331ed0b3f884dd61c8a25b3cf123705982 ] Both functions accumulate formatted output via ret += snprintf(buf + ret, size - ret, ...). If the buffer is too small and snprintf() returns more than the remaining space, ret exceeds size and the next 'size - ret' underflows, causing snprintf() to write past the buffer end. Switch to scnprintf() which returns the actual number of bytes written, making the accumulation safe. Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 67e7786bb878b..878bbcd03cd5b 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -36,7 +36,7 @@ static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) size_t i; for (i = 0; i < len; i++) - ret += snprintf(buf + ret, size - ret, "%02x", data[i]); + ret += scnprintf(buf + ret, size - ret, "%02x", data[i]); return ret; } @@ -140,7 +140,7 @@ static int synthesize_bpf_prog_name(char *buf, int size, const struct btf_type *t; int name_len; - name_len = snprintf(buf, size, "bpf_prog_"); + name_len = scnprintf(buf, size, "bpf_prog_"); name_len += snprintf_hex(buf + name_len, size - name_len, prog_tags[sub_id], BPF_TAG_SIZE); if (btf) { @@ -153,9 +153,10 @@ static int synthesize_bpf_prog_name(char *buf, int size, short_name = info->name; } else short_name = "F"; - if (short_name) - name_len += snprintf(buf + name_len, size - name_len, - "_%s", short_name); + if (short_name) { + name_len += scnprintf(buf + name_len, size - name_len, + "_%s", short_name); + } return name_len; } From 6d52921f47020cf79496358f24b55b1e2a7c3e74 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 14:35:28 -0300 Subject: [PATCH 1310/3297] perf hists: Fix snprintf() in hists__scnprintf_title() UID filter path [ Upstream commit 227a8748742f0263f1fe3131449b44563b77a209 ] hists__scnprintf_title() accumulates formatted output into a buffer using scnprintf() for all filter clauses except the UID filter, which uses snprintf(). If the buffer fills up and snprintf() returns more than the remaining space, printed exceeds size and the next 'size - printed' underflows, causing later scnprintf() calls to write past the buffer. Switch the UID filter clause to scnprintf() to match the rest of the function. Fixes: 25c312dbf88ca402 ("perf hists: Move hists__scnprintf_title() away from the TUI code") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Arnaldo Carvalho de Melo Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hist.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index ef4b569f7df46..bd894054021b6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2960,9 +2960,10 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events); - if (hists->uid_filter_str) - printed += snprintf(bf + printed, size - printed, - ", UID: %s", hists->uid_filter_str); + if (hists->uid_filter_str) { + printed += scnprintf(bf + printed, size - printed, + ", UID: %s", hists->uid_filter_str); + } if (thread) { if (hists__has(hists, thread)) { printed += scnprintf(bf + printed, size - printed, From d0479c2b12974aa188b10d221a5770126b118b6d Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:33 -0400 Subject: [PATCH 1311/3297] xprtrdma: Fix ep kref imbalance on ADDR_CHANGE [ Upstream commit af9b65b29af341932625c4283dc7a23cdb62688a ] rpcrdma_cm_event_handler() falls through to the disconnected: label on RDMA_CM_EVENT_ADDR_CHANGE and calls rpcrdma_ep_put() with no matching get when the event arrives before RDMA_CM_EVENT_ESTABLISHED. The kref then underflows during connect teardown and rpcrdma_xprt_disconnect() operates on a freed ep. Reference counts across a normal connection lifecycle: rpcrdma_ep_create() kref_init ->1 rpcrdma_xprt_connect() ep_get ->2 (before post_recvs) RDMA_CM_EVENT_ESTABLISHED ep_get ->3 RDMA_CM_EVENT_DISCONNECTED ep_put ->2 rpcrdma_xprt_drain() ep_put ->1 rpcrdma_xprt_disconnect() tail ep_put ->0 (ep_destroy) The connect-time get in rpcrdma_xprt_connect(), taken just before rpcrdma_post_recvs() "while there are outstanding Receives," is balanced by rpcrdma_xprt_drain. ADDR_CHANGE before ESTABLISHED has no get to consume, so its put drops the count to 1 and the drain put then frees the ep while rpcrdma_xprt_disconnect() still holds a pointer to it. Fix by dispatching on the prior re_connect_status via xchg(): for prev == 0 (pre-ESTABLISHED) wake the connect waiter and return with no put; for prev == 1 call rpcrdma_force_disconnect() and return. The case-1 arm relies on the subsequent RDMA_CM_EVENT_DISCONNECTED event -- reliably delivered when rdma_disconnect() is called on a still-connected cm_id -- to balance the ESTABLISHED get; rpcrdma_xprt_drain() continues to balance only that connect-time get. Any other prior value means teardown is already in flight. Fixes: 2acc5cae2923 ("xprtrdma: Prevent dereferencing r_xprt->rx_ep after it is freed") Assisted-by: kres:claude-opus-4-7 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 59fc1aebe8c31..ab7c6178a2b9b 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -245,8 +245,17 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) complete(&ep->re_done); return 0; case RDMA_CM_EVENT_ADDR_CHANGE: - ep->re_connect_status = -ENODEV; - goto disconnected; + switch (xchg(&ep->re_connect_status, -ENODEV)) { + case 0: + goto wake_connect_worker; + case 1: + /* The later DISCONNECTED event balances the + * ESTABLISHED get; do not put here. + */ + rpcrdma_force_disconnect(ep); + return 0; + } + return 0; case RDMA_CM_EVENT_ESTABLISHED: rpcrdma_ep_get(ep); ep->re_connect_status = 1; @@ -269,7 +278,6 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event) return 0; case RDMA_CM_EVENT_DISCONNECTED: ep->re_connect_status = -ECONNABORTED; -disconnected: rpcrdma_force_disconnect(ep); return rpcrdma_ep_put(ep); default: From 28743571c17b58c21a7216fc9faaf8028df5869b Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:34 -0400 Subject: [PATCH 1312/3297] xprtrdma: Initialize re_id before removal registration [ Upstream commit bb7caa63e1db22fd03e8dc591b12169e99169dff ] rpcrdma_create_id() registers ep->re_rn with the rpcrdma ib_client before returning the new rdma_cm_id to rpcrdma_ep_create(). However rpcrdma_ep_create() currently stores that pointer in ep->re_id only after rpcrdma_create_id() returns. A local administrator can race an NFS/RDMA mount against RDMA device removal. If rpcrdma_remove_one() observes the just-registered notification before rpcrdma_ep_create() assigns ep->re_id, rpcrdma_ep_removal_done() calls trace_xprtrdma_device_removal(NULL). The tracepoint dereferences id->device->name and copies id->route.addr.dst_addr, so the callback can crash the kernel with a NULL pointer dereference. Store the rdma_cm_id in ep->re_id immediately before publishing ep->re_rn. The existing error path still destroys the id directly if registration fails; ep is then freed by the caller without using ep->re_id. Remove the later duplicate assignment in rpcrdma_ep_create(). Fixes: 3f4eb9ff9234 ("xprtrdma: Handle device removal outside of the CM event handler") Assisted-by: kres:openai-gpt-5 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index ab7c6178a2b9b..dc8629bf294dc 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -334,6 +334,7 @@ static struct rdma_cm_id *rpcrdma_create_id(struct rpcrdma_xprt *r_xprt, if (rc) goto out; + ep->re_id = id; rc = rpcrdma_rn_register(id->device, &ep->re_rn, rpcrdma_ep_removal_done); if (rc) goto out; @@ -406,7 +407,6 @@ static int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt) } __module_get(THIS_MODULE); device = id->device; - ep->re_id = id; reinit_completion(&ep->re_done); ep->re_max_requests = r_xprt->rx_xprt.max_reqs; From 7471e66373a4444a57ef2192f8c4081202c54f45 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:35 -0400 Subject: [PATCH 1313/3297] xprtrdma: Check frwr_wp_create() during connect [ Upstream commit 0f13fc7c7d2e0427517e63c739277a4cd338b0c5 ] frwr_wp_create() creates the singleton Memory Region used to encode padding for Write chunks whose payload length is not XDR-aligned. Its failure paths return a negative errno and leave ep->re_write_pad_mr set to NULL. rpcrdma_xprt_connect() currently ignores that return value. If frwr_wp_create() fails after the rest of the connection setup succeeds, xprt_rdma_connect_worker() treats the connection attempt as successful and sets XPRT_CONNECTED. A later NFS/RDMA read with a non-4-byte-aligned receive page length reaches rpcrdma_encode_write_list(), passes the NULL write-pad MR to encode_rdma_segment(), and dereferences it. This is locally triggerable on an NFS/RDMA client after a connect or reconnect hits a local MR allocation, DMA-map, MR-map, or post-send failure; a remote peer alone cannot force the local MR setup failure. Check the return value and fail the connect as -ENOTCONN, matching the adjacent setup failures. This keeps XPRT_CONNECTED clear and lets the normal reconnect path retry. Fixes: 21037b8c2258 ("xprtrdma: Provide a buffer to pad Write chunks of unaligned length") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index dc8629bf294dc..09fa25a83ce12 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -549,7 +549,17 @@ int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt) goto out; } rpcrdma_mrs_create(r_xprt); - frwr_wp_create(r_xprt); + + /* + * rpcrdma_encode_write_list() dereferences the write-pad + * MR with no NULL check, so fail the connect rather than + * publish a transport whose write-pad MR is NULL. + */ + rc = frwr_wp_create(r_xprt); + if (rc) { + rc = -ENOTCONN; + goto out; + } out: trace_xprtrdma_connect(r_xprt, rc); From 47b3dc59e09e9a70fa8f5777ea506dcf0fd64c0c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:05 -0400 Subject: [PATCH 1314/3297] xprtrdma: Document and assert reply-handler invariants [ Upstream commit 797943e8bd1ffcc63bfe79d24faad9a77054ec40 ] The xprtrdma reply path has been the subject of recurring LLM-driven review claims that 'an RPC can complete while receive buffers are still DMA-mapped' or that 'the req can be freed while the HCA still owns the send buffer.' No runtime reproducer has surfaced, but the absence of a written-down invariant set lets each pass of automated review reach the same hypothetical conclusion. Subsequent fixes against ce2f9a4d9ccc ('xprtrdma: Decouple req recycling from RPC completion') closed the underlying races but did not document the closure where future readers will look for it. State the invariants explicitly in a comment above rpcrdma_reply_handler() and back four of them with WARN_ON_ONCE() probes positioned where each invariant is locally checkable on the previous patch's cleaned-up ownership state: - I1 (Receive WR ownership): WARN at rpcrdma_post_recvs() that a rep pulled from rb_free_reps carries rr_rqst == NULL. - I2 (rep attachment): WARN at rpcrdma_reply_put() that req->rl_reply was NULLed before the matching rep_put. - I3 (Registered-MR fence): WARN at rpcrdma_complete_rqst() that req->rl_registered is empty. Strong send-queue ordering of the LocalInv WR chain makes the last completion observe the ib_dma_unmap_sg() of every earlier MR, so 'list empty' implies 'all MRs unmapped'. - I4 (Send-buffer release): WARN at rpcrdma_req_release() that req->rl_sendctx is NULL. Reaching the kref release callback requires both the RPC-layer and Send-side references to have dropped; the Send-side drop runs in rpcrdma_sendctx_unmap(), which clears rl_sendctx (previous patch). A non-NULL rl_sendctx here would mean the Send-side owner had not run -- a contradiction. The XXX comment in xprt_rdma_free() about signal-driven release racing the Send completion described the pre-decouple state. Replace it with a one-line note pointing at the invariant set, since the kref scheme now holds the req across the in-flight Send regardless of which path released the rpc_task. I5 (req lifecycle) is stated in the comment but not probed: making it locally assertible would require moving kref_init out of rpcrdma_req_release(), which in turn requires adding kref_init to the bc_pa_list and backlog-wake reuse paths. That restructuring is deferred -- the invariant is unchanged either way. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Stable-dep-of: 234c0ff695ef ("xprtrdma: Resize reply buffers before reposting receives") Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 69 +++++++++++++++++++++++++++++++++ net/sunrpc/xprtrdma/transport.c | 13 +++++-- net/sunrpc/xprtrdma/verbs.c | 6 +++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 2f8d4f529be43..fb65afb44d900 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1381,6 +1381,11 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) struct rpc_rqst *rqst = rep->rr_rqst; int status; + /* I3: every registered MR has been invalidated and + * ib_dma_unmap_sg()'d before complete_rqst runs. + */ + WARN_ON_ONCE(!list_empty(&rpcr_to_rdmar(rqst)->rl_registered)); + switch (rep->rr_proc) { case rdma_msg: status = rpcrdma_decode_msg(r_xprt, rep, rqst); @@ -1412,6 +1417,70 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep) goto out; } +/* Reply-side ownership invariants + * + * I1 (Receive WR ownership). A struct rpcrdma_rep is owned by the + * HCA between ib_post_recv() and the matching Receive completion. + * After ib_dma_sync_single_for_cpu() in rpcrdma_wc_receive() it is + * owned by the CPU until rpcrdma_rep_put() returns it to + * rb_free_reps; a rep on rb_free_reps is not re-posted until + * rpcrdma_post_recvs() pulls it off. Asserted: rpcrdma_post_recvs() + * WARNs that a pulled rep has rr_rqst == NULL. + * + * I2 (rep attachment). While req->rl_reply == rep, the rep cannot be + * re-posted. rpcrdma_reply_put() NULLs req->rl_reply before handing + * the rep to rpcrdma_rep_put(). Asserted: rpcrdma_reply_put() WARNs + * that rl_reply is NULL after the put. + * + * I3 (Registered-MR fence). On entry to rpcrdma_complete_rqst() every + * MR that was on req->rl_registered has had its rkey invalidated + * (remotely via IB_WC_WITH_INVALIDATE or locally via IB_WR_LOCAL_INV) + * and its pages ib_dma_unmap_sg()'d. The LocalInv chain is posted + * on a single QP; strong send-queue ordering makes the last + * completion (frwr_wc_localinv_done) observe the + * ib_dma_unmap_sg() that ran from each earlier completion's + * frwr_mr_put() before complete_rqst is called. The inline + * frwr_reminv() path unmaps its one MR synchronously before + * rpcrdma_reply_handler() reaches complete_rqst. Asserted: + * rpcrdma_complete_rqst() WARNs that rl_registered is empty. + * + * I4 (Send-buffer release). req->rl_kref carries two unconditional + * owners while a Send is outstanding: the RPC-layer reference (set + * at xprt_rdma_alloc_slot / xprt_rdma_bc_rqst_get / rpcrdma_req_release + * pool-entry) and the Send-side reference (kref_get() in + * rpcrdma_prepare_send_sges()). rpcrdma_req_release() runs only + * after both have dropped, so the req does not return to its free + * pool until rpcrdma_sendctx_unmap() has fired -- the HCA has + * released the send buffer before the req can be reused. Asserted: + * rpcrdma_req_release() WARNs that rl_sendctx is NULL. + * + * I5 (req lifecycle). A req is owned by the RPC layer between slot + * acquisition and the matching xprt_rdma_free_slot() (or, for the + * backchannel, xprt_rdma_bc_free_rqst()). While owned, rl_kref >= 1. + * The pools (rb_send_bufs, bc_pa_list, backlog wake target) never + * contain a req with outstanding Send-side or Reply-side work. + * + * Non-hazards. The following claims have been raised by adversarial + * review and are each closed by the invariants above: + * + * * "Reply completes the RPC while the HCA still holds the send + * buffer" -- excluded by I4. The Send-side kref reference is held + * until rpcrdma_sendctx_unmap() runs from Send completion. + * + * * "Signal-driven release races the in-flight Send" -- same + * resolution. xprt_rdma_free() does not touch rl_kref; the + * Send-side reference keeps the req out of its pool until Send + * completion fires. + * + * * "Receive completion races rep reuse" -- excluded by I1. A rep + * is on rb_free_reps only after rpcrdma_rep_put() has been called + * and rpcrdma_post_recvs() owns the next transition back to the HCA. + * + * * "Pages still DMA-mapped when call_decode reads them" -- excluded + * by I3. The matching ib_dma_unmap_sg() for every MR has run on + * the same CPU thread that calls rpcrdma_complete_rqst(). + */ + /** * rpcrdma_reply_handler - Process received RPC/RDMA messages * @rep: Incoming rpcrdma_rep object to process diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 5569f17fdd9b5..5ff8e5126a6c2 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -509,6 +509,11 @@ static void rpcrdma_req_release(struct kref *kref) struct rpc_xprt *xprt = rqst->rq_xprt; struct rpcrdma_xprt *r_xprt; + /* I4: both the RPC-layer and Send-side owners have dropped, + * so rpcrdma_sendctx_unmap() has cleared rl_sendctx. + */ + WARN_ON_ONCE(req->rl_sendctx); + kref_init(&req->rl_kref); #if defined(CONFIG_SUNRPC_BACKCHANNEL) @@ -652,10 +657,10 @@ xprt_rdma_free(struct rpc_task *task) frwr_unmap_sync(rpcx_to_rdmax(rqst->rq_xprt), req); } - /* XXX: If the RPC is completing because of a signal and - * not because a reply was received, we ought to ensure - * that the Send completion has fired, so that memory - * involved with the Send is not still visible to the NIC. + /* The Send-side rl_kref owner keeps req out of its free pool + * until rpcrdma_sendctx_unmap() has fired -- see I4 above + * rpcrdma_reply_handler() -- so signal-driven release here + * does not let the HCA touch a recycled send buffer. */ } diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 09fa25a83ce12..6f3e967b3ad09 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1235,6 +1235,10 @@ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) rpcrdma_rep_put(buffers, req->rl_reply); req->rl_reply = NULL; } + /* I2: rl_reply NULL after the put closes the + * 'rep on rb_free_reps still referenced by req' window. + */ + WARN_ON_ONCE(req->rl_reply); } /** @@ -1411,6 +1415,8 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed) rep = rpcrdma_rep_create(r_xprt); if (!rep) break; + /* I1: a rep on rb_free_reps must carry no rqst pointer. */ + WARN_ON_ONCE(rep->rr_rqst); if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) { rpcrdma_rep_put(buf, rep); break; From 345652531400f2407a4010538092a0be6e3bd88f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:36 -0400 Subject: [PATCH 1315/3297] xprtrdma: Resize reply buffers before reposting receives [ Upstream commit 234c0ff695ef3ffb656931000e6b823d0c2f30fd ] Commit 0e13dd9ea8be ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") made rpcrdma_rep objects survive disconnects. That is normally fine, but it also means their receive regbufs keep the size they had when they were first allocated. Each rep's receive buffer is sized to ep->re_inline_recv when the rep is created. rpcrdma_ep_create() resets that threshold to the rdma_max_inline_read ceiling for every new endpoint, and the connect handshake then shrinks it to the peer's advertised inline send size. A rep allocated under a smaller negotiated threshold keeps that size: on disconnect, rpcrdma_xprt_disconnect() drains and DMA-unmaps the surviving reps but does not free or resize them. The threshold can come back larger on the next connection. The first peer may supply no RPC-over-RDMA CM private data, defaulting its send size to 1024, while the reconnect target is an ordinary server offering 4096; or, with rdma_max_inline_read raised above its default, the reconnect target may advertise a larger svcrdma_max_req_size than the first. rpcrdma_post_recvs() then reposts a surviving rep whose SGE length is still the old, smaller value, and a larger inline Reply hits a receive length error and forces another disconnect. The undersized rep returns to the free list when its failed Receive flushes, so the following reconnect reposts the same rep and fails the same way. The transport flaps without making forward progress for as long as the peer keeps advertising the larger inline size. This is local/admin-triggerable rather than remote-triggerable: a local administrator must create and maintain the NFS/RDMA mount, while the server or reconnect target has to advertise a larger inline send size and return a reply that uses it. Fix this by checking each rep before it is reposted. If the receive regbuf is smaller than the current endpoint's inline receive size, reallocate it on the current RDMA device's NUMA node and reinitialize the rep's xdr_buf before DMA-mapping and posting the Receive WR. Fixes: 0e13dd9ea8be ("xprtrdma: Remove temp allocation of rpcrdma_rep objects") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/verbs.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 6f3e967b3ad09..24bf170720fc8 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -81,6 +81,8 @@ rpcrdma_regbuf_alloc_node(size_t size, enum dma_data_direction direction, int node); static struct rpcrdma_regbuf * rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction); +static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb, + size_t size, gfp_t flags, int node); static void rpcrdma_regbuf_dma_unmap(struct rpcrdma_regbuf *rb); static void rpcrdma_regbuf_free(struct rpcrdma_regbuf *rb); @@ -1319,10 +1321,16 @@ rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction) * returned, @rb is left untouched. */ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags) +{ + return rpcrdma_regbuf_realloc_node(rb, size, flags, NUMA_NO_NODE); +} + +static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb, + size_t size, gfp_t flags, int node) { void *buf; - buf = kmalloc(size, flags); + buf = kmalloc_node(size, flags, node); if (!buf) return false; @@ -1334,6 +1342,23 @@ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags) return true; } +static bool rpcrdma_rep_resize(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_rep *rep) +{ + struct rpcrdma_regbuf *rb = rep->rr_rdmabuf; + struct rpcrdma_ep *ep = r_xprt->rx_ep; + size_t size = ep->re_inline_recv; + + if (likely(rdmab_length(rb) >= size)) + return true; + if (!rpcrdma_regbuf_realloc_node(rb, size, XPRTRDMA_GFP_FLAGS, + ibdev_to_node(ep->re_id->device))) + return false; + + xdr_buf_init(&rep->rr_hdrbuf, rdmab_data(rb), rdmab_length(rb)); + return true; +} + /** * __rpcrdma_regbuf_dma_map - DMA-map a regbuf * @r_xprt: controlling transport instance @@ -1417,6 +1442,10 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed) break; /* I1: a rep on rb_free_reps must carry no rqst pointer. */ WARN_ON_ONCE(rep->rr_rqst); + if (!rpcrdma_rep_resize(r_xprt, rep)) { + rpcrdma_rep_put(buf, rep); + break; + } if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) { rpcrdma_rep_put(buf, rep); break; From d7a2870dde3bb09d51d6b9c877642996ad6b92dd Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 4 Jun 2026 13:06:37 -0400 Subject: [PATCH 1316/3297] xprtrdma: Fix bcall rep leak and unbounded peek [ Upstream commit c7653d5cebc8492c77ec0415b5e9c0fb3e644bc6 ] rpcrdma_is_bcall() decodes a reply's first words to decide whether the frame is a backchannel call. Two issues in that decode path let a short or malformed reply leak the receive buffer and drain the Receive queue. First, the speculative peek p = xdr_inline_decode(xdr, 0); /* five p++ reads follow */ asks xdr_inline_decode() for zero bytes, which returns xdr->p without consulting xdr->end. The five subsequent __be32 reads can then walk up to 20 bytes past the wire payload into stale regbuf contents and misclassify the reply as a backchannel call. Second, after the post-peek p = xdr_inline_decode(xdr, 3 * sizeof(*p)); if (unlikely(!p)) return true; the short-header arm returns true without calling rpcrdma_bc_receive_call(). The contract with the caller is that a true return transfers ownership of rep to the backchannel path: rpcrdma_reply_handler() if (rpcrdma_is_bcall(r_xprt, rep)) return; /* bare return, skips out_post */ ... out_post: rpcrdma_post_recvs(r_xprt, credits + ...); Because rpcrdma_bc_receive_call() never ran, no one took rep, but rpcrdma_reply_handler still bare-returns past rpcrdma_rep_put() and rpcrdma_post_recvs(). The rep, with its persistently DMA-mapped receive buffer, is orphaned on rb_all_reps and freed only at transport teardown. This completion reposts nothing, so its slot is reclaimed only when a later forward-channel reply reaches out_post and rpcrdma_post_recvs() allocates a fresh rep to backfill; absent that traffic the Receive queue drains and the peer's Sends draw RNR NAKs. Fix by consulting xdr->end after the zero-length peek so the five __be32 reads cannot run unless 20 bytes of wire payload remain. A byte-precise comparison against xdr->end is required because a non-4-aligned receive rounds the stream's word count up past the true payload. Also return false from the short-header arm so the reply falls through the normal out_norqst cleanup chain (rpcrdma_rep_put() plus rpcrdma_post_recvs()). Fixes: 41c8f70f5a3d ("xprtrdma: Harden backchannel call decoding") Assisted-by: kres:claude-opus-4-7 Signed-off-by: Chris Mason Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index fb65afb44d900..d19e371d63cf1 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1133,6 +1133,8 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep) /* Peek at stream contents without advancing. */ p = xdr_inline_decode(xdr, 0); + if ((char *)xdr->end - (char *)p < 5 * XDR_UNIT) + return false; /* Chunk lists */ if (xdr_item_is_present(p++)) @@ -1157,7 +1159,7 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep) */ p = xdr_inline_decode(xdr, 3 * sizeof(*p)); if (unlikely(!p)) - return true; + return false; rpcrdma_bc_receive_call(r_xprt, rep); return true; From 469b22376ee73369711ecf2761bd122ef4195963 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:38 -0400 Subject: [PATCH 1317/3297] xprtrdma: Sanitize the reply credit grant after parsing [ Upstream commit c3a628aab2dc8f5fd7bff86ceaeae64de590e60a ] The out_norqst exit in rpcrdma_reply_handler() branches away before the credit clamp, so a reply that matches no pending request reaches out_post carrying the raw credit value parsed from the wire. rpcrdma_post_recvs() does not bound its @needed argument: the refill loop allocates and chains Receive WRs until the count is satisfied or allocation fails. A peer that sends a well-formed reply carrying an unknown XID and an inflated credit grant therefore drives rep allocation and Receive posting past re_max_requests on every such reply. Move the clamp to immediately after the credit field is parsed, ahead of the first branch that can reach out_post, so every later consumer sees a sanitized value. The cwnd update stays on the matched-request path. Fixes: 704f3f640f72 ("xprtrdma: Post receive buffers after RPC completion") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index d19e371d63cf1..3002e625f2b3f 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1517,6 +1517,14 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) credits = be32_to_cpu(*p++); rep->rr_proc = *p++; + /* The credit grant from the wire is not trustworthy; + * sanitize it before any code path consumes it. + */ + if (credits == 0) + credits = 1; /* don't deadlock */ + else if (credits > r_xprt->rx_ep->re_max_requests) + credits = r_xprt->rx_ep->re_max_requests; + if (rep->rr_vers != rpcrdma_version) goto out_badversion; @@ -1533,10 +1541,6 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) xprt_pin_rqst(rqst); spin_unlock(&xprt->queue_lock); - if (credits == 0) - credits = 1; /* don't deadlock */ - else if (credits > r_xprt->rx_ep->re_max_requests) - credits = r_xprt->rx_ep->re_max_requests; if (buf->rb_credits != credits) rpcrdma_update_cwnd(r_xprt, credits); From 007b4da2f38dcc16a13265416f4ca9f179bab610 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:39 -0400 Subject: [PATCH 1318/3297] xprtrdma: Repost Receive buffers for malformed replies [ Upstream commit abc011ddaf1617e3e82d8a1e87daa7ddbfb9bac5 ] rpcrdma_wc_receive() decrements the transport's Receive count for every completion before it dispatches a successful Receive to rpcrdma_reply_handler(). The handler must post a replacement Receive WR before returning unless ownership of the rep has moved elsewhere, as on the backchannel path. Commit 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") moved the Receive refill out of rpcrdma_wc_receive(), where it had run ahead of every reply, into rpcrdma_reply_handler() so that the responder's credit grant could be parsed before reposting. The bad-version and short-reply exits never reach that refill: they recycle the rep and return without calling rpcrdma_post_recvs(). A remote peer can therefore drain the client's posted Receive queue by sending a sustained stream of replies that are shorter than the fixed transport header or that carry an unrecognized RPC/RDMA version. Each such reply consumes one posted Receive without replacing it. Once the queue empties, the peer's next Send finds no posted Receive and the transport stalls until reconnect. Route both malformed-reply exits through the shared repost tail after recycling the rep, refilling against buf->rb_credits, the most recent accepted credit grant. Neither exit updates the congestion window, so RPCs admitted under the previous grant remain in flight awaiting replies. A smaller refill target would let a stream of malformed replies ratchet the posted Receive count down to the batch floor while the congestion window still admits rb_credits RPCs; a burst of valid replies to those RPCs could then overrun the posted Receives, and because the client connects with rnr_retry_count of zero, a single RNR NAK terminates the connection. Refilling against rb_credits also restores the target that applied to malformed replies before commit 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") when rpcrdma_post_recvs() computed it from rb_credits internally. rb_credits is at least one from connection establishment onward, so the repost path always keeps Receives posted. Fixes: 2ae50ad68cd7 ("xprtrdma: Close window between waking RPC senders and posting Receives") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 3002e625f2b3f..425dbaec66fdf 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1573,11 +1573,13 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) out_badversion: trace_xprtrdma_reply_vers_err(rep); - goto out; + rpcrdma_rep_put(buf, rep); + credits = buf->rb_credits; + goto out_post; out_shortreply: trace_xprtrdma_reply_short_err(rep); - -out: rpcrdma_rep_put(buf, rep); + credits = buf->rb_credits; + goto out_post; } From b5a0a4a564d21c3b905d2c341e9bdc5409094d2d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 4 Jun 2026 13:06:40 -0400 Subject: [PATCH 1319/3297] xprtrdma: Return sendctx slot after Send preparation failure [ Upstream commit 60e7870052f417d83965db144f70ae21fcfcf37f ] rpcrdma_prepare_send_sges() gets a sendctx before it maps the SGEs for the Send WR. If one of the mapping helpers fails, no Send WR is posted, so no Send completion is guaranteed to advance rb_sc_tail. Current cleanup clears sc_req so a later completion can sweep over that slot, but a consecutive run of preparation failures can still advance rb_sc_head until the ring appears full. At that point rpcrdma_sendctx_get_locked() returns NULL and no Send can be posted to produce the completion needed to recover the ring. The trigger requires CONFIG_SUNRPC_XPRT_RDMA and an NFS/RDMA mount. Mount setup and reliable DMA-map fault injection require local admin authority. Unprivileged I/O on an existing mount can exercise the send path, but a remote peer alone cannot force this local DMA-map failure. Add rpcrdma_sendctx_unget_locked() for the single-consumer send path to rewind rb_sc_head when the just-acquired sendctx is canceled before ib_post_send(). Wake waiters after making the slot available again. After the rewind, every slot the completion sweep visits belongs to a posted Send, so rpcrdma_sendctx_put_locked() no longer needs to test sc_req before unmapping. Fixes: ae72950abf99 ("xprtrdma: Add data structure to manage RDMA Send arguments") Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 5 ++++- net/sunrpc/xprtrdma/verbs.c | 40 +++++++++++++++++++++++++++++---- net/sunrpc/xprtrdma/xprt_rdma.h | 2 ++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 425dbaec66fdf..5d1f96bffd749 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -792,6 +792,7 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, struct xdr_buf *xdr, enum rpcrdma_chunktype rtype) { + struct rpcrdma_sendctx *sc; int ret; ret = -EAGAIN; @@ -834,7 +835,9 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt, return 0; out_unmap: - rpcrdma_sendctx_cancel(req->rl_sendctx); + sc = req->rl_sendctx; + rpcrdma_sendctx_cancel(sc); + rpcrdma_sendctx_unget_locked(r_xprt, sc); out_nosc: trace_xprtrdma_prepsend_failed(&req->rl_slot, ret); return ret; diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 24bf170720fc8..efd91ed249fab 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -631,6 +631,11 @@ static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt) /* The QP is drained, but the final unsignaled Sends might not * have been walked by a signaled Send completion. Release those * Send owners before request buffers are reset. + * + * Unlike the completion sweep, this walk can visit slots with + * no Send posted: after a partial rpcrdma_sendctxs_create() + * failure on reconnect, rb_sc_head and rb_sc_tail are stale, + * and slots between them can be NULL or have sc_req clear. */ for (i = rpcrdma_sendctx_next(buf, buf->rb_sc_tail); i != rpcrdma_sendctx_next(buf, buf->rb_sc_head); @@ -703,6 +708,12 @@ static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf, return likely(item < buf->rb_sc_last) ? item + 1 : 0; } +static unsigned long rpcrdma_sendctx_prev(struct rpcrdma_buffer *buf, + unsigned long item) +{ + return item > 0 ? item - 1 : buf->rb_sc_last; +} + /** * rpcrdma_sendctx_get_locked - Acquire a send context * @r_xprt: controlling transport instance @@ -747,6 +758,29 @@ struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt) return NULL; } +/** + * rpcrdma_sendctx_unget_locked - Release an unposted send context + * @r_xprt: controlling transport instance + * @sc: send context to release + * + * Usage: Called when no Send is posted for the sendctx most + * recently returned by rpcrdma_sendctx_get_locked(). + * + * The caller serializes calls to this function and to + * rpcrdma_sendctx_get_locked() (per transport). + */ +void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_sendctx *sc) +{ + struct rpcrdma_buffer *buf = &r_xprt->rx_buf; + + if (WARN_ON_ONCE(buf->rb_sc_ctxs[buf->rb_sc_head] != sc)) + return; + + buf->rb_sc_head = rpcrdma_sendctx_prev(buf, buf->rb_sc_head); + xprt_write_space(&r_xprt->rx_xprt); +} + /** * rpcrdma_sendctx_put_locked - Release a send context * @r_xprt: controlling transport instance @@ -764,8 +798,7 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, unsigned long next_tail; /* Release previously completed but unsignaled Sends by walking - * up the queue until @sc is found. Entries left behind by a - * failed rpcrdma_prepare_send_sges() have sc_req cleared. + * up the queue until @sc is found. */ next_tail = buf->rb_sc_tail; do { @@ -775,8 +808,7 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, /* ORDER: item must be accessed _before_ tail is updated */ cur = buf->rb_sc_ctxs[next_tail]; - if (cur->sc_req) - rpcrdma_sendctx_unmap(cur); + rpcrdma_sendctx_unmap(cur); } while (buf->rb_sc_ctxs[next_tail] != sc); diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 40a9aa0cda67f..ae14f6a526e6e 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -478,6 +478,8 @@ void rpcrdma_req_destroy(struct rpcrdma_req *req); int rpcrdma_buffer_create(struct rpcrdma_xprt *); void rpcrdma_buffer_destroy(struct rpcrdma_buffer *); struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt); +void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_sendctx *sc); struct rpcrdma_mr *rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt); void rpcrdma_mrs_refresh(struct rpcrdma_xprt *r_xprt); From fa9eb50ddfea3871d5c9ee240d3d4d86bb89ee0e Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Wed, 10 Jun 2026 13:24:51 +0200 Subject: [PATCH 1320/3297] perf s390: Fix TEXTREL in Python extension by compiling as PIC [ Upstream commit 49f5f6ae67dec54014584bb3126a5a94f14e2a5c ] On s390 the Python extension build fails as follows when using a linker that is configured to treat text relocations (TEXTREL) in shared libraries as error by default: GEN python/perf.cpython-314-s390x-linux-gnu.so /usr/bin/ld.bfd: error: read-only segment has dynamic relocations This occurrs because util/llvm-c-helpers.o is erroneously built from util/llvm-c-helpers.cpp without compiler option -fPIC but linked into the shared library (via libperf-util.a(perf-util-in.o)). On s390, object files must be compiled as position-indepedent code (PIC) in order to be linked into shared libraries. Commit a9a3f1d18a6c ("perf s390: Always build with -fPIC") added compiler option -fPIC to CFLAGS for s390, which is used in C compiles. Add -fPIC to CXXFLAGS for s390 as well, so that it is also used in C++ compiles. Fixes: a9a3f1d18a6c9ccf ("perf s390: Always build with -fPIC") Reported-by: Thomas Richter Reviewed-by: Ian Rogers Reviewed-by: James Clark Signed-off-by: Jens Remus Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Bill Wendling Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Ingo Molnar Cc: Jan Polensky Cc: Jiri Olsa Cc: Justin Stitt Cc: Mark Rutland Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/Makefile.config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 2dd5f5a60568d..8ae13809f68b2 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -110,6 +110,7 @@ endif ifeq ($(ARCH),s390) CFLAGS += -fPIC + CXXFLAGS += -fPIC endif ifeq ($(ARCH),mips) From e274dfa05904f21de2333faed1de805a031599d9 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 9 Jun 2026 15:40:06 +0100 Subject: [PATCH 1321/3297] perf cs-etm: Queue context packets for frontend [ Upstream commit 68ca50bc0fa64841cd73b8a1538df1d7f7eb4108 ] PE_CONTEXT elements update the context ID and exception level, but the decoder may still have prior packets cached for frontend processing. Updating the context immediately in the decoder backend can make those cached packets get consumed with the wrong thread or EL state. Add a CS_ETM_CONTEXT packet carrying the TID and EL to the frontend, this keeps context changes ordered with the rest of the packet stream and avoids mismatches when synthesizing samples from cached packets. Separate the memory access function into one for the frontend and one for decoding. The frontend also needs memory access to attach the instruction to samples. Because the frontend does memory access for both previous and current packets, change all the frontend memory access function signatures to take both a tidq and packet. But backend always uses the current backend EL and thread from the tidq. Treat context packets as a boundary for branch sample generation and remove tidq->prev_packet_thread because it's not possible to branch to a different thread, so only tracking the current thread is required for sample generation. Fixes: e573e978fb12e160 ("perf cs-etm: Inject capabilitity for CoreSight traces") Reported-by: Amir Ayupov Closes: https://lore.kernel.org/linux-perf-users/20260515021135.1729028-1-aaupov@meta.com/ Co-authored-by: James Clark Signed-off-by: Leo Yan Cc: Ian Rogers Cc: Jiri Olsa Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Cc: Mike Leach Cc: Namhyung Kim Cc: Paschalis Mpeis Cc: Robert Walker Cc: Shuah Khan Cc: Suzuki Poulouse Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- .../perf/util/cs-etm-decoder/cs-etm-decoder.c | 21 +- tools/perf/util/cs-etm.c | 236 +++++++++++------- tools/perf/util/cs-etm.h | 8 +- 3 files changed, 163 insertions(+), 102 deletions(-) diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index 43ac711a4e2ae..4f66ff9fe7f61 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -399,6 +399,8 @@ cs_etm_decoder__buffer_packet(struct cs_etm_queue *etmq, packet_queue->packet_buffer[et].flags = 0; packet_queue->packet_buffer[et].exception_number = UINT32_MAX; packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id; + packet_queue->packet_buffer[et].el = ocsd_EL_unknown; + packet_queue->packet_buffer[et].tid = -1; if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1) return OCSD_RESP_WAIT; @@ -446,6 +448,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq, packet->last_instr_type = elem->last_i_type; packet->last_instr_subtype = elem->last_i_subtype; packet->last_instr_cond = elem->last_instr_cond; + packet->el = elem->context.exception_level; if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT) packet->last_instr_taken_branch = elem->last_instr_exec; @@ -522,7 +525,9 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, const ocsd_generic_trace_elem *elem, const uint8_t trace_chan_id) { + struct cs_etm_packet *packet; pid_t tid = -1; + int ret; /* * Process the PE_CONTEXT packets if we have a valid contextID or VMID. @@ -543,12 +548,18 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, break; } - if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id, - elem->context.exception_level)) + if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id, + elem->context.exception_level, tid)) return OCSD_RESP_FATAL_SYS_ERR; - if (tid == -1) - return OCSD_RESP_CONT; + ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id, + CS_ETM_CONTEXT); + if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT) + return ret; + + packet = &packet_queue->packet_buffer[packet_queue->tail]; + packet->tid = tid; + packet->el = elem->context.exception_level; /* * A timestamp is generated after a PE_CONTEXT element so make sure @@ -556,7 +567,7 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, */ cs_etm_decoder__reset_timestamp(packet_queue); - return OCSD_RESP_CONT; + return ret; } static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer( diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 06eb1a56430cb..ea5f7b3545c10 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -86,15 +86,22 @@ struct cs_etm_traceid_queue { u64 period_instructions; size_t last_branch_pos; union perf_event *event_buf; - struct thread *thread; - struct thread *prev_packet_thread; - ocsd_ex_level prev_packet_el; - ocsd_ex_level el; struct branch_stack *last_branch; struct branch_stack *last_branch_rb; struct cs_etm_packet *prev_packet; struct cs_etm_packet *packet; struct cs_etm_packet_queue packet_queue; + + struct thread *decode_thread; + ocsd_ex_level decode_el; + + /* + * The frontend accesses the EL from '[prev_]packet' because it needs + * previous EL for branch and current EL for instruction samples. It's + * not possible to change thread in a single branch sample so no need to + * store or access the thread through the packet. + */ + struct thread *frontend_thread; }; enum cs_etm_format { @@ -615,10 +622,11 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq, queue = &etmq->etm->queues.queue_array[etmq->queue_nr]; tidq->trace_chan_id = trace_chan_id; - tidq->el = tidq->prev_packet_el = ocsd_EL_unknown; - tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1, + tidq->decode_el = ocsd_EL_unknown; + tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1, + queue->tid); + tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1, queue->tid); - tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host); tidq->packet = zalloc(sizeof(struct cs_etm_packet)); if (!tidq->packet) @@ -751,21 +759,10 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm, /* * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for * the next incoming packet. - * - * Threads and exception levels are also tracked for both the - * previous and current packets. This is because the previous - * packet is used for the 'from' IP for branch samples, so the - * thread at that time must also be assigned to that sample. - * Across discontinuity packets the thread can change, so by - * tracking the thread for the previous packet the branch sample - * will have the correct info. */ tmp = tidq->packet; tidq->packet = tidq->prev_packet; tidq->prev_packet = tmp; - tidq->prev_packet_el = tidq->el; - thread__put(tidq->prev_packet_thread); - tidq->prev_packet_thread = thread__get(tidq->thread); } } @@ -938,8 +935,8 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq) /* Free this traceid_queue from the array */ tidq = etmq->traceid_queues[idx]; - thread__zput(tidq->thread); - thread__zput(tidq->prev_packet_thread); + thread__zput(tidq->frontend_thread); + thread__zput(tidq->decode_thread); zfree(&tidq->event_buf); zfree(&tidq->last_branch); zfree(&tidq->last_branch_rb); @@ -1084,47 +1081,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address, } } -static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, - u64 address, size_t size, u8 *buffer, - const ocsd_mem_space_acc_t mem_space) +static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space, + ocsd_ex_level el, struct thread *thread) { u8 cpumode; u64 offset; int len; struct addr_location al; struct dso *dso; - struct cs_etm_traceid_queue *tidq; int ret = 0; if (!etmq) return 0; addr_location__init(&al); - tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); - if (!tidq) - goto out; /* - * We've already tracked EL along side the PID in cs_etm__set_thread() - * so double check that it matches what OpenCSD thinks as well. It - * doesn't distinguish between EL0 and EL1 for this mem access callback - * so we had to do the extra tracking. Skip validation if it's any of - * the 'any' values. + * We track EL for the frontend and the backend when receiving context + * and range packets. OpenCSD doesn't distinguish between EL0 and EL1 + * for this mem access callback so we had to do the extra tracking. Skip + * validation if it's any of the 'any' values. */ if (!(mem_space == OCSD_MEM_SPACE_ANY || mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) { if (mem_space & OCSD_MEM_SPACE_EL1N) { /* Includes both non secure EL1 and EL0 */ - assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0); + assert(el == ocsd_EL1 || el == ocsd_EL0); } else if (mem_space & OCSD_MEM_SPACE_EL2) - assert(tidq->el == ocsd_EL2); + assert(el == ocsd_EL2); else if (mem_space & OCSD_MEM_SPACE_EL3) - assert(tidq->el == ocsd_EL3); + assert(el == ocsd_EL3); } - cpumode = cs_etm__cpu_mode(etmq, address, tidq->el); + cpumode = cs_etm__cpu_mode(etmq, address, el); - if (!thread__find_map(tidq->thread, cpumode, address, &al)) + if (!thread__find_map(thread, cpumode, address, &al)) goto out; dso = map__dso(al.map); @@ -1139,7 +1132,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, map__load(al.map); - len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)), + len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)), offset, buffer, size); if (len <= 0) { @@ -1159,6 +1152,30 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, return ret; } +static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, + u64 address, size_t size, u8 *buffer) +{ + return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el, + tidq->frontend_thread); +} + +static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space) +{ + struct cs_etm_traceid_queue *tidq; + + tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); + if (!tidq) + return 0; + + return __cs_etm__mem_access(etmq, address, size, buffer, + mem_space, tidq->decode_el, + tidq->decode_thread); +} + static struct cs_etm_queue *cs_etm__alloc_queue(void) { struct cs_etm_queue *etmq = zalloc(sizeof(*etmq)); @@ -1334,12 +1351,13 @@ void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq) } static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq, - u8 trace_chan_id, u64 addr) + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr) { u8 instrBytes[2]; - cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes), - instrBytes, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + ARRAY_SIZE(instrBytes), instrBytes); /* * T32 instruction size is indicated by bits[15:11] of the first * 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111 @@ -1372,16 +1390,16 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet) } static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 offset) { if (packet->isa == CS_ETM_ISA_T32) { u64 addr = packet->start_addr; while (offset) { - addr += cs_etm__t32_instr_size(etmq, - trace_chan_id, addr); + addr += cs_etm__t32_instr_size(etmq, tidq, packet, + addr); offset--; } return addr; @@ -1473,34 +1491,51 @@ cs_etm__get_trace(struct cs_etm_queue *etmq) return etmq->buf_len; } -static void cs_etm__set_thread(struct cs_etm_queue *etmq, - struct cs_etm_traceid_queue *tidq, pid_t tid, - ocsd_ex_level el) +/* + * Convert a raw thread number to a thread struct and assign it to **thread. + */ +static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq, + ocsd_ex_level el, pid_t tid, + struct thread **thread) { struct machine *machine = cs_etm__get_machine(etmq, el); + if (!machine || !*thread) + return -EINVAL; + if (tid != -1) { - thread__zput(tidq->thread); - tidq->thread = machine__find_thread(machine, -1, tid); + thread__zput(*thread); + *thread = machine__find_thread(machine, -1, tid); } /* Couldn't find a known thread */ - if (!tidq->thread) - tidq->thread = machine__idle_thread(machine); + if (!*thread) + *thread = machine__idle_thread(machine); - tidq->el = el; + return 0; } -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el) +/* + * Set the thread and EL of the decode context which is ahead in time of the + * frontend context. + */ +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, + ocsd_ex_level el, pid_t tid) { struct cs_etm_traceid_queue *tidq; + int ret; tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); if (!tidq) return -EINVAL; - cs_etm__set_thread(etmq, tidq, tid, el); + ret = cs_etm__etmq_update_thread(etmq, el, tid, + &tidq->decode_thread); + if (ret) + return ret; + + tidq->decode_el = el; return 0; } @@ -1510,8 +1545,8 @@ bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq) } static void cs_etm__copy_insn(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, struct perf_sample *sample) { /* @@ -1528,14 +1563,14 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq, * cs_etm__t32_instr_size(). */ if (packet->isa == CS_ETM_ISA_T32) - sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id, + sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet, sample->ip); /* Otherwise, A64 and A32 instruction size are always 32-bit. */ else sample->insn_len = 4; - cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len, - (void *)sample->insn, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip, + sample->insn_len, (void *)sample->insn); } u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp) @@ -1562,6 +1597,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq, static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr, u64 period) { int ret = 0; @@ -1571,23 +1607,23 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, perf_sample__init(&sample, /*all=*/true); event->sample.header.type = PERF_RECORD_SAMPLE; - event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el); + event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = addr; - sample.pid = thread__pid(tidq->thread); - sample.tid = thread__tid(tidq->thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.id = etmq->etm->instructions_id; sample.stream_id = etmq->etm->instructions_id; sample.period = period; - sample.cpu = tidq->packet->cpu; + sample.cpu = packet->cpu; sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample); + cs_etm__copy_insn(etmq, tidq, packet, &sample); if (etm->synth_opts.last_branch) sample.branch_stack = tidq->last_branch; @@ -1632,15 +1668,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, event->sample.header.type = PERF_RECORD_SAMPLE; event->sample.header.misc = cs_etm__cpu_mode(etmq, ip, - tidq->prev_packet_el); + tidq->prev_packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = ip; - sample.pid = thread__pid(tidq->prev_packet_thread); - sample.tid = thread__tid(tidq->prev_packet_thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.addr = cs_etm__first_executed_instr(tidq->packet); sample.id = etmq->etm->branches_id; sample.stream_id = etmq->etm->branches_id; @@ -1649,8 +1685,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet, - &sample); + cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample); /* * perf report cannot handle events without a branch stack @@ -1774,7 +1809,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, { struct cs_etm_auxtrace *etm = etmq->etm; int ret; - u8 trace_chan_id = tidq->trace_chan_id; u64 instrs_prev; /* Get instructions remainder from previous packet */ @@ -1860,10 +1894,10 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, * been executed, but PC has not advanced to next * instruction) */ - addr = cs_etm__instr_addr(etmq, trace_chan_id, - tidq->packet, offset - 1); + addr = cs_etm__instr_addr(etmq, tidq, tidq->packet, + offset - 1); ret = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->packet, addr, etm->instructions_sample_period); if (ret) return ret; @@ -1945,7 +1979,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2000,7 +2034,7 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2037,9 +2071,9 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq) return etmq->buf_len; } -static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, - struct cs_etm_packet *packet, - u64 end_addr) +static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 end_addr) { /* Initialise to keep compiler happy */ u16 instr16 = 0; @@ -2061,8 +2095,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * so below only read 2 bytes as instruction size for T32. */ addr = end_addr - 2; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16), - (u8 *)&instr16, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr16), (u8 *)&instr16); if ((instr16 & 0xFF00) == 0xDF00) return true; @@ -2077,8 +2111,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +---------+---------+-------------------------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0x0F000000) == 0x0F000000 && (instr32 & 0xF0000000) != 0xF0000000) return true; @@ -2094,8 +2128,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +-----------------------+---------+-----------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0xFFE0001F) == 0xd4000001) return true; @@ -2111,7 +2145,6 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2126,7 +2159,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, */ if (magic == __perf_cs_etmv4_magic) { if (packet->exception_number == CS_ETMV4_EXC_CALL && - cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; } @@ -2164,7 +2197,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2190,7 +2222,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, * (SMC, HVC) are taken as sync exceptions. */ if (packet->exception_number == CS_ETMV4_EXC_CALL && - !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + !cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; @@ -2214,7 +2246,6 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, { struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; - u8 trace_chan_id = tidq->trace_chan_id; u64 magic; int ret; @@ -2295,11 +2326,11 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, if (prev_packet->flags == (PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT) && - cs_etm__is_svc_instr(etmq, trace_chan_id, - packet, packet->start_addr)) + cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) { prev_packet->flags = PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET; + } break; case CS_ETM_DISCONTINUITY: /* @@ -2380,6 +2411,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT; break; + case CS_ETM_CONTEXT: case CS_ETM_EMPTY: default: break; @@ -2455,6 +2487,19 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, */ cs_etm__sample(etmq, tidq); break; + case CS_ETM_CONTEXT: + /* + * Update context but don't swap packet. Keep the + * previous one for branch source address info, if + * tracing the kernel the context packet will be emitted + * between two ranges. + */ + ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el, + tidq->packet->tid, + &tidq->frontend_thread); + if (ret) + goto out; + break; case CS_ETM_EXCEPTION: case CS_ETM_EXCEPTION_RET: /* @@ -2483,6 +2528,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, } } +out: return ret; } @@ -2606,7 +2652,7 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm, if (!tidq) continue; - if (tid == -1 || thread__tid(tidq->thread) == tid) + if (tid == -1 || thread__tid(tidq->frontend_thread) == tid) cs_etm__run_per_thread_timeless_decoder(etmq); } else cs_etm__run_per_cpu_timeless_decoder(etmq); @@ -3322,7 +3368,7 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq) */ if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, 0x0L, ((u64) -1L), - cs_etm__mem_access)) + cs_etm__decoder_mem_access)) goto out_free_decoder; zfree(&t_params); diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h index a8caeea720aa1..dfc9aeacfd0b6 100644 --- a/tools/perf/util/cs-etm.h +++ b/tools/perf/util/cs-etm.h @@ -158,6 +158,7 @@ enum cs_etm_sample_type { CS_ETM_DISCONTINUITY, CS_ETM_EXCEPTION, CS_ETM_EXCEPTION_RET, + CS_ETM_CONTEXT, }; enum cs_etm_isa { @@ -184,6 +185,8 @@ struct cs_etm_packet { u8 last_instr_size; u8 trace_chan_id; int cpu; + int el; + pid_t tid; }; #define CS_ETM_PACKET_MAX_BUFFER 1024 @@ -244,8 +247,9 @@ enum cs_etm_pid_fmt { #include int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu); enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq); -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el); +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, ocsd_ex_level el, + pid_t tid); bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq); void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, u8 trace_chan_id); From a6eec54329b432d6e537be3356b45227c42e1db5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:01:43 -0300 Subject: [PATCH 1322/3297] perf pmu: Fix pmu_id() heap underwrite on empty identifier file [ Upstream commit 836455e6dbd34eb3d12eeab5e2d2b9a7f1512459 ] pmu_id() calls filename__read_str() then strips the trailing newline via str[len - 1] = 0. If the PMU identifier file is empty, filename__read_str() succeeds with len = 0. len - 1 underflows size_t to SIZE_MAX, writing a null byte before the heap allocation. Add a len == 0 check before the newline stripping. Fixes: 51d548471510843e ("perf pmu: Add pmu_id()") Reported-by: sashiko-bot Cc: John Garry Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/pmu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 3d1f975e8db9f..0e65025ef9c2b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -856,6 +856,12 @@ static char *pmu_id(const char *name) if (filename__read_str(path, &str, &len) < 0) return NULL; + /* empty identifier file — nothing useful */ + if (len == 0) { + free(str); + return NULL; + } + str[len - 1] = 0; /* remove line feed */ return str; From 09962b811ef140dd9c57d86186139878988ad2f1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:03:13 -0300 Subject: [PATCH 1323/3297] perf pmu: Fix perf_pmu__parse_scale/unit() OOB access on empty sysfs file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 33035f7dd4e49f3f117e70c5e36c8c1ae88d37f2 ] perf_pmu__parse_scale() reads a PMU scale file then accesses scale[sret - 1] to strip a trailing newline. Only sret < 0 is guarded, so an empty file (sret == 0) causes scale[-1] — a stack buffer underflow that reads and potentially writes out of bounds. perf_pmu__parse_unit() has the same pattern: alias->unit[sret - 1] with sret == 0 accesses the byte before the struct member, which may corrupt the adjacent pmu_name pointer field. Change both guards from sret < 0 to sret <= 0 so that empty files are treated as read errors. Fixes: 410136f5dd96b601 ("tools/perf/stat: Add event unit and scale support") Reported-by: sashiko-bot Cc: Stephane Eranian Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 0e65025ef9c2b..bcdbbaadcbb3a 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -328,7 +328,7 @@ static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *al goto error; sret = read(fd, scale, sizeof(scale)-1); - if (sret < 0) + if (sret <= 0) goto error; if (scale[sret - 1] == '\n') @@ -360,7 +360,7 @@ static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *ali return -1; sret = read(fd, alias->unit, UNIT_MAX_LEN); - if (sret < 0) + if (sret <= 0) goto error; close(fd); From 490473192ac2f71a5d44086142c9f328e8fe3f2a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:03:55 -0300 Subject: [PATCH 1324/3297] tools lib api: Fix missing null termination in filename__read_int/ull() [ Upstream commit 52b1f9678499b13b7aeb0186d9c6f486c043283f ] filename__read_int() passes a stack buffer to read() using the full sizeof(line) and then hands it to atoi() without null-terminating. If a sysfs file fills the 64-byte buffer exactly, atoi() reads past the array into uninitialized stack memory. filename__read_ull_base() has the same issue with strtoull(). Fix both by reading sizeof(line) - 1 bytes and explicitly null-terminating after a successful read. Fixes: 3a351127cbc682c3 ("tools lib fs: Adopt filename__read_int from tools/perf/") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/api/fs/fs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index edec23406dbc6..3cc302d4c47b1 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -294,11 +294,14 @@ int filename__read_int(const char *filename, int *value) { char line[64]; int fd = open(filename, O_RDONLY), err = -1; + ssize_t n; if (fd < 0) return -errno; - if (read(fd, line, sizeof(line)) > 0) { + n = read(fd, line, sizeof(line) - 1); + if (n > 0) { + line[n] = '\0'; *value = atoi(line); err = 0; } @@ -312,11 +315,14 @@ static int filename__read_ull_base(const char *filename, { char line[64]; int fd = open(filename, O_RDONLY), err = -1; + ssize_t n; if (fd < 0) return -errno; - if (read(fd, line, sizeof(line)) > 0) { + n = read(fd, line, sizeof(line) - 1); + if (n > 0) { + line[n] = '\0'; *value = strtoull(line, NULL, base); if (*value != ULLONG_MAX) err = 0; From 4f883ab5bc7b77256af2762398761cb573463f37 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:04:50 -0300 Subject: [PATCH 1325/3297] perf symbols: Fix signed overflow in sysfs__read_build_id() size check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6eaa8ee3e2abe5112e80e94c27196bb175689469 ] sysfs__read_build_id() reads ELF note headers from sysfs files. The note's namesz and descsz fields are used to compute the skip size: int n = namesz + descsz; if (n > (int)sizeof(bf)) Both namesz and descsz are size_t from NOTE_ALIGN() of 32-bit note header fields. Their sum can exceed INT_MAX, overflowing the signed int n to a negative value. The check n > sizeof(bf) then evaluates false (negative < positive in signed comparison), and read(fd, bf, n) reinterprets the negative n as a huge size_t count — the kernel writes up to MAX_RW_COUNT bytes into the 8192-byte stack buffer. In practice the overflow is bounded by the sysfs file's actual size, so a real sysfs notes file won't trigger it organically. But crafted input (e.g. via a mounted debugfs/sysfs image) could. Fix by validating namesz and descsz individually against the buffer size before summing, and change n to size_t to avoid the signed overflow entirely. Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 77a8b529b54f4..562268ddd5712 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -962,14 +962,17 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) } else if (read(fd, bf, descsz) != (ssize_t)descsz) break; } else { - int n = namesz + descsz; + size_t n; - if (n > (int)sizeof(bf)) { + /* int sum of namesz+descsz can overflow negative, bypassing size check */ + if (namesz > sizeof(bf) || descsz > sizeof(bf) - namesz) { n = sizeof(bf); pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n", __func__, filename, nhdr.n_namesz, nhdr.n_descsz); + } else { + n = namesz + descsz; } - if (read(fd, bf, n) != n) + if (read(fd, bf, n) != (ssize_t)n) break; } } From 45e7900e1555c7d9e2d9efc4cd57f22f797f9261 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:05:15 -0300 Subject: [PATCH 1326/3297] perf symbols: Bounds-check .gnu_debuglink section data [ Upstream commit 9c74f0aab398cb32ab250401f323c0fdc9a3a496 ] filename__read_debuglink() copies .gnu_debuglink section data into a caller-provided buffer via: strncpy(debuglink, data->d_buf, size); where size is PATH_MAX. If the ELF section is smaller than size and lacks a null terminator, strncpy reads past data->d_buf into adjacent memory. A malformed ELF file can trigger this, potentially causing a segfault or leaking heap data. Additionally, strncpy does not guarantee null termination when the source fills the buffer. Replace with an explicit memcpy bounded by both the output buffer size and the actual section data size (data->d_size), followed by explicit null termination. Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 562268ddd5712..5d4597d6cffd1 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1025,7 +1025,14 @@ int filename__read_debuglink(const char *filename, char *debuglink, goto out_elf_end; /* the start of this section is a zero-terminated string */ - strncpy(debuglink, data->d_buf, size); + if (data->d_size > 0) { + size_t len = min(size - 1, data->d_size); + + memcpy(debuglink, data->d_buf, len); + debuglink[len] = '\0'; + } else { + debuglink[0] = '\0'; + } err = 0; From f2e5262589d9487c8f8433c83d98609687e581b2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 21:06:54 -0300 Subject: [PATCH 1327/3297] perf intel-pt: Fix snprintf size tracking bug in insn decoder [ Upstream commit b6bb3b005dcdd960b8e0b7f9d6869132b3de08d5 ] dump_insn() tracks remaining buffer space with a 'left' variable, but the loop subtracts the cumulative offset 'n' each iteration instead of just the per-iteration delta: n += snprintf(x->out + n, left, "%02x ", inbuf[i]); left -= n; /* BUG: n is cumulative, not the delta */ After two iterations left goes massively negative, wrapping to a huge value when passed as size_t to snprintf(), disabling all bounds checking for the rest of the loop. Switch to scnprintf() accumulation using sizeof(x->out) - n as the remaining space, which is always correct and eliminates the separate 'left' variable entirely. Fixes: 48d02a1d5c137d36 ("perf script: Add 'brstackinsn' for branch stacks") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: Andi Kleen Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- .../util/intel-pt-decoder/intel-pt-insn-decoder.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index 72c7a4e15d617..f90fcbc4302df 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -220,7 +220,6 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, { struct insn insn; int n, i, ret; - int left; ret = insn_decode(&insn, inbuf, inlen, x->is64bit ? INSN_MODE_64 : INSN_MODE_32); @@ -229,13 +228,9 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, return ""; if (lenp) *lenp = insn.length; - left = sizeof(x->out); - n = snprintf(x->out, left, "insn: "); - left -= n; - for (i = 0; i < insn.length; i++) { - n += snprintf(x->out + n, left, "%02x ", inbuf[i]); - left -= n; - } + n = scnprintf(x->out, sizeof(x->out), "insn: "); + for (i = 0; i < insn.length; i++) + n += scnprintf(x->out + n, sizeof(x->out) - n, "%02x ", inbuf[i]); return x->out; } From 56b17c84394f1213d05ece38657bb4dc6d5ad47c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:37:55 -0300 Subject: [PATCH 1328/3297] perf tools: Fix thread__set_comm_from_proc() on empty comm file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 31d596054550f793508abe7dd593853ece47d428 ] thread__set_comm_from_proc() calls procfs__read_str() then strips the trailing newline via comm[sz - 1] = '\0'. procfs__read_str() allocates the buffer before reading, so on an empty /proc/pid/comm (reachable during late exit teardown) it returns success with sz = 0 and an unterminated heap buffer. The sz - 1 underflow was the original sashiko finding: it writes a null byte before the allocation. But even with a sz > 0 guard on the newline strip, the unterminated buffer would still be passed to thread__set_comm() which calls strlen() — an unbounded heap read. Fix by treating sz == 0 as failure: free the buffer and return -1. This is consistent with pmu.c's perf_pmu__parse_scale/unit which already treat len == 0 from filename__read_str as an error. Fixes: 2f3027ac28bf6bc3 ("perf thread: Introduce method to set comm from /proc/pid/self") Reported-by: sashiko-bot Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/thread.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index aa9c58bbf9d32..9409d32dce04c 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -294,6 +294,11 @@ int thread__set_comm_from_proc(struct thread *thread) if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", thread__pid(thread), thread__tid(thread)) >= (int)sizeof(path)) && procfs__read_str(path, &comm, &sz) == 0) { + /* sz==0: read got nothing, e.g. race during exit teardown */ + if (sz == 0) { + free(comm); + return -1; + } comm[sz - 1] = '\0'; err = thread__set_comm(thread, comm, 0); } From 76dfa13a0acb12146ebf3260449484a5ebc9b23a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:38:26 -0300 Subject: [PATCH 1329/3297] perf hwmon: Fix off-by-one null termination on sysfs reads [ Upstream commit 1847c5fae344a0fb9cc0f95be40b378359c6fc3b ] Three functions read sysfs files into fixed-size stack buffers using the full buffer size, then null-terminate at buf[read_len]. If the read fills the buffer exactly, read_len equals sizeof(buf) and the null byte writes one past the array, corrupting an adjacent stack variable. Fix all three by reading sizeof(buf) - 1 bytes, reserving space for the null terminator: - hwmon_pmu__read_events(): buf[128] - hwmon_pmu__describe_items(): buf[64] - evsel__hwmon_pmu_read(): buf[32] Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index 5f51cfd13ce32..24a13560b3847 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -289,7 +289,7 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) if (fd < 0) continue; - read_len = read(fd, buf, sizeof(buf)); + read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; @@ -432,7 +432,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si is_alarm ? "_alarm" : ""); fd = openat(dir, buf, O_RDONLY); if (fd > 0) { - ssize_t read_len = read(fd, buf, sizeof(buf)); + ssize_t read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; @@ -816,7 +816,7 @@ int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) count = perf_counts(evsel->counts, cpu_map_idx, thread); fd = FD(evsel, cpu_map_idx, thread); - len = pread(fd, buf, sizeof(buf), 0); + len = pread(fd, buf, sizeof(buf) - 1, 0); if (len <= 0) { count->lost++; return -EINVAL; From f830bb8d221f3ad4d7b22baa2645d42975d83526 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:38:48 -0300 Subject: [PATCH 1330/3297] perf hwmon: Use scnprintf() in hwmon_pmu__for_each_event() [ Upstream commit ae75956c166fe169b8c137bf375305fc97820a62 ] hwmon_pmu__for_each_event() formats description strings via: len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", ...); len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len, ...); If value->label is long enough to cause snprintf() to truncate, it returns the would-have-been-written count, making len exceed sizeof(desc_buf). The subsequent sizeof(desc_buf) - len underflows to a huge size_t value, disabling bounds checking in hwmon_pmu__describe_items(). The alias_buf snprintf has the same issue. Switch both to scnprintf() which returns actual bytes written. Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index 24a13560b3847..f111d9e9b6b5a 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -514,14 +514,14 @@ int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callb int ret; size_t len; - len = snprintf(alias_buf, sizeof(alias_buf), "%s%d", - hwmon_type_strs[key.type], key.num); + scnprintf(alias_buf, sizeof(alias_buf), "%s%d", + hwmon_type_strs[key.type], key.num); if (!info.name) { info.name = info.alias; info.alias = NULL; } - len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", + len = scnprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", hwmon_desc[key.type], pmu->name + 6, value->label ?: info.name); From bc2fc12ce6e4d7166fcab575be0e9e6e1e3694c9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:39:53 -0300 Subject: [PATCH 1331/3297] perf hwmon: Fix parse_hwmon_filename() strlcpy buffer overflow [ Upstream commit e1a2c9d70b312acc262f6be936dd5bbd9bbc6236 ] parse_hwmon_filename() strips the "_alarm" suffix from event names by copying into a 24-byte stack buffer: strlcpy(fn_type, fn_item, fn_item_len - 5); The third argument is the source length minus the suffix, not the destination buffer capacity. A long event name ending in "_alarm" can have fn_item_len - 5 > sizeof(fn_type), causing strlcpy() to write past the 24-byte fn_type[] array. The assert() only validates that the longest *valid* hwmon item fits, but does not protect against crafted input. Clamp the strlcpy size to min(fn_item_len - 5, sizeof(fn_type)). Fixes: 4810b761f812da3c ("perf hwmon_pmu: Add hwmon filename parser") Reported-by: sashiko-bot Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index f111d9e9b6b5a..520ae65258eaa 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -202,7 +202,8 @@ bool parse_hwmon_filename(const char *filename, fn_item_len = strlen(fn_item); if (fn_item_len > 6 && !strcmp(&fn_item[fn_item_len - 6], "_alarm")) { assert(strlen(LONGEST_HWMON_ITEM_STR) < sizeof(fn_type)); - strlcpy(fn_type, fn_item, fn_item_len - 5); + /* fn_item_len - 5 strips "_alarm"; clamp to buffer size */ + strlcpy(fn_type, fn_item, min_t(size_t, fn_item_len - 5, sizeof(fn_type))); fn_item = fn_type; *alarm = true; } From d34b42ee0c74d11e82aadc4b28608f8b359a5cea Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:40:20 -0300 Subject: [PATCH 1332/3297] perf symbols: Bounds-check descsz in sysfs__read_build_id() GNU fallback [ Upstream commit 1b4e9fbdeabc549965e70ac0cd8095d57ff6df06 ] When sysfs__read_build_id() matches NT_GNU_BUILD_ID with the right namesz but the name content is not "GNU", it falls back to reading descsz bytes into the stack buffer bf[BUFSIZ]: } else if (read(fd, bf, descsz) != (ssize_t)descsz) Unlike the else branch which validates namesz + descsz against sizeof(bf), this path passes descsz directly to read() without any bounds check. A crafted sysfs file with a large n_descsz overflows the 8192-byte stack buffer. Add a descsz > sizeof(bf) check before the read, breaking out of the loop on oversized values. Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 5d4597d6cffd1..917c63846aeff 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -959,8 +959,13 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) err = 0; break; } - } else if (read(fd, bf, descsz) != (ssize_t)descsz) - break; + } else { + /* descsz from untrusted file — clamp to buffer */ + if (descsz > sizeof(bf)) + break; + if (read(fd, bf, descsz) != (ssize_t)descsz) + break; + } } else { size_t n; From fbaf9bdfc0917c25c2d06426ba88692369c4a37a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:03:04 -0300 Subject: [PATCH 1333/3297] perf hwmon: Guard label read against empty or failed reads [ Upstream commit 34d3d93fac6d92237cb9d730ca04c37ed361c7a6 ] hwmon_pmu__read_events() reads label files with read() into a stack buffer, strips trailing newlines, then checks buf[0] == '\0'. When read() returns 0 (empty file) or -1 (error), the buffer is never written, so buf[0] reads uninitialized stack memory. If the garbage byte is non-zero, the code falls through to strdup(buf) which copies arbitrary stack data as the label string. Fix by checking read_len <= 0 before accessing buf contents, closing the fd and skipping the entry. Reported-by: sashiko-bot Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index 520ae65258eaa..a1133de5ed22e 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -295,8 +295,11 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; - if (read_len > 0) - buf[read_len] = '\0'; + if (read_len <= 0) { + close(fd); + continue; + } + buf[read_len] = '\0'; if (buf[0] == '\0') { pr_debug("hwmon_pmu: empty label file %s %s\n", From 41b3a92310450474821c8ea67e3916f317b22654 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:04:25 -0300 Subject: [PATCH 1334/3297] perf tools: Use snprintf() in dso__read_running_kernel_build_id() [ Upstream commit 2ea64782a428bed74f595961e651ceb8c4c5bf22 ] dso__read_running_kernel_build_id() uses sprintf() to format a sysfs path from machine->root_dir into a PATH_MAX buffer. If root_dir is close to PATH_MAX in length, appending "/sys/kernel/notes" (18 bytes) overflows the stack buffer. Switch to snprintf() with sizeof(path) to prevent the overflow. Reported-by: sashiko-bot Fixes: cdd059d731eeb466 ("perf tools: Move dso_* related functions into dso object") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 52d3f537a111d..2b7b5f94115f5 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1698,7 +1698,7 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) if (machine__is_default_guest(machine)) return; - sprintf(path, "%s/sys/kernel/notes", machine->root_dir); + snprintf(path, sizeof(path), "%s/sys/kernel/notes", machine->root_dir); sysfs__read_build_id(path, &bid); dso__set_build_id(dso, &bid); } From b0385203a09f0dd86415d8777b72d717ae0dc19b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 07:05:19 -0300 Subject: [PATCH 1335/3297] tools lib api: Fix filename__write_int() writing uninitialized stack data [ Upstream commit 438ece06185696e14c63c6113d5e2d34ec0a9680 ] filename__write_int() formats an integer into a 64-byte buffer with sprintf() then passes sizeof(buf) (64) as the write length. This writes all 64 bytes including uninitialized stack data past the formatted string. Most sysfs files reject the oversized write, making the function always return -1. Fix by capturing the sprintf() return value and using it as the write length. Reported-by: sashiko-bot Fixes: 3b00ea938653d136 ("tools lib api fs: Add sysfs__write_int function") Cc: Kan Liang Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/api/fs/fs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index 3cc302d4c47b1..d16911818d4d3 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -376,12 +376,13 @@ int filename__write_int(const char *filename, int value) { int fd = open(filename, O_WRONLY), err = -1; char buf[64]; + int len; if (fd < 0) return -errno; - sprintf(buf, "%d", value); - if (write(fd, buf, sizeof(buf)) == sizeof(buf)) + len = sprintf(buf, "%d", value); + if (write(fd, buf, len) == len) err = 0; close(fd); From fe4d8ad2e96f409c7492ee688c397cb885d40fe7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:10:18 -0300 Subject: [PATCH 1336/3297] tools lib api: Fix mount_overload() snprintf truncation and toupper range [ Upstream commit fd1f70776add263f8ef38a87ae593c75303f1dcd ] mount_overload() builds an environment variable name like "PERF_SYSFS_ENVIRONMENT" from fs->name. Two bugs: 1) snprintf() uses name_len as the buffer size instead of sizeof(upper_name). For fs->name = "sysfs" (len=5), the output is truncated to "PERF" (4 chars + null), so getenv() never finds the intended variable. 2) mem_toupper() only uppercases name_len bytes, converting just the "PERF" prefix rather than the full string including the filesystem name portion. Fix by using sizeof(upper_name) for snprintf and strlen(upper_name) for mem_toupper, so the full "PERF_SYSFS_ENVIRONMENT" string is correctly formatted and uppercased. Reported-by: sashiko-bot Fixes: 73ca85ad364769ff ("tools lib api fs: Add FSTYPE__mount() method") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/lib/api/fs/fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index d16911818d4d3..cbd8eab0d1df0 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -261,8 +261,8 @@ static const char *mount_overload(struct fs *fs) /* "PERF_" + name + "_ENVIRONMENT" + '\0' */ char upper_name[5 + name_len + 12 + 1]; - snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name); - mem_toupper(upper_name, name_len); + snprintf(upper_name, sizeof(upper_name), "PERF_%s_ENVIRONMENT", fs->name); + mem_toupper(upper_name, strlen(upper_name)); return getenv(upper_name) ?: *fs->mounts; } From bafb6bfb346feefd84a8d55bbbfc88ed37aecaf1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:10:43 -0300 Subject: [PATCH 1337/3297] perf bpf: Add NULL check for btf__type_by_id() in synthesize_bpf_prog_name() [ Upstream commit 903b0526dcf86d030c5970b4b0a67f9c227368e2 ] synthesize_bpf_prog_name() calls btf__type_by_id() and immediately dereferences the result via t->name_off without checking for NULL. btf__type_by_id() returns NULL when the type_id is invalid or out of range. When processing perf.data files, finfo->type_id comes from untrusted input, so an invalid ID causes a NULL pointer dereference. Fix by checking t for NULL before dereferencing. Reported-by: sashiko-bot Fixes: fc462ac75b36daaa ("perf bpf: Extract logic to create program names from perf_event__synthesize_one_bpf_prog()") Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 878bbcd03cd5b..401b242bd60ff 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -146,7 +146,8 @@ static int synthesize_bpf_prog_name(char *buf, int size, if (btf) { finfo = func_infos + sub_id * info->func_info_rec_size; t = btf__type_by_id(btf, finfo->type_id); - short_name = btf__name_by_offset(btf, t->name_off); + if (t) + short_name = btf__name_by_offset(btf, t->name_off); } else if (sub_id == 0 && sub_prog_cnt == 1) { /* no subprog */ if (info->name[0]) From f593775fecf5a2e0ac747a586a5fe8242c14e764 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:11:28 -0300 Subject: [PATCH 1338/3297] perf bpf: Fix map data leak in bpf_metadata_create() on alloc failure [ Upstream commit aece2b8966fc8de5be46ee9287d0f60d6690c300 ] bpf_metadata_create() calls bpf_metadata_read_map_data() which allocates map.btf and map.rodata. If the subsequent bpf_metadata_alloc() fails, the code does 'continue' which skips bpf_metadata_free_map_data(), permanently leaking both allocations. Fix by calling bpf_metadata_free_map_data() before continue. Reported-by: sashiko-bot Fixes: ab38e84ba9a80581 ("perf record: collect BPF metadata from existing BPF programs") Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 401b242bd60ff..8c82163ae441e 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -395,8 +395,10 @@ static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info) continue; metadata = bpf_metadata_alloc(info->nr_prog_tags, map.num_vars); - if (!metadata) + if (!metadata) { + bpf_metadata_free_map_data(&map); continue; + } bpf_metadata_fill_event(&map, &metadata->event->bpf_metadata); From a3e758e7412287bcbeeaf7d247f0e3b716ba9d9a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:12:04 -0300 Subject: [PATCH 1339/3297] perf bpf: Fix metadata leak in perf_env__add_bpf_info() on duplicate insert [ Upstream commit a0e4362a3e7b592f1d58949ffe3d6decad39a17c ] When perf_env__insert_bpf_prog_info() returns false (duplicate program), the error path frees info_linear and info_node but not info_node->metadata. If bpf_metadata_create() had succeeded, the metadata allocation is permanently leaked. Fix by calling bpf_metadata_free() on info_node->metadata before freeing info_node. bpf_metadata_free() handles NULL, so this is safe even when bpf_metadata_create() returned NULL. Reported-by: sashiko-bot Fixes: fdc3441f2d317b40 ("perf record: collect BPF metadata from new programs") Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 8c82163ae441e..895bab82da08f 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -873,6 +873,7 @@ static int perf_env__add_bpf_info(struct perf_env *env, u32 id) if (!perf_env__insert_bpf_prog_info(env, info_node)) { pr_debug("%s: duplicate add bpf info request for id %u\n", __func__, btf_id); + bpf_metadata_free(info_node->metadata); free(info_linear); free(info_node); goto out; From cc6cd3fe8b8b1b9ae58790e85d9f94579506d53f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 8 Jun 2026 08:12:34 -0300 Subject: [PATCH 1340/3297] perf symbols: Add bounds checks to elf_read_build_id() note iteration [ Upstream commit acc56d3941fc2997a5a21ea9233a8ac3d87c4f2f ] elf_read_build_id() iterates ELF notes using pointer arithmetic driven by n_namesz and n_descsz from the note headers. Neither the note header read nor the subsequent name/desc advances are checked against the section boundary. A malformed ELF file with oversized note sizes causes out-of-bounds reads past the section data buffer. Add two bounds checks: verify the note header fits within the remaining section data, and verify that namesz + descsz (after alignment) fits before advancing the pointer. Fixes: fd7a346ea292074e ("perf symbols: Filename__read_build_id should look at .notes section too") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 917c63846aeff..4cc9e91a2d997 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -836,10 +836,24 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) ptr = data->d_buf; while (ptr < (data->d_buf + data->d_size)) { GElf_Nhdr *nhdr = ptr; - size_t namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); + size_t namesz, descsz, remaining; const char *name; + /* ensure the note header fits within the section */ + if (ptr + sizeof(*nhdr) > data->d_buf + data->d_size) + break; + + namesz = NOTE_ALIGN(nhdr->n_namesz); + descsz = NOTE_ALIGN(nhdr->n_descsz); + + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = data->d_buf + data->d_size - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; From f1f5f8d334e914f93cef371236cc7b1131e73aab Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 16:09:45 -0300 Subject: [PATCH 1341/3297] perf symbols: Add bounds checks to read_build_id() note iteration in minimal build [ Upstream commit 52e582e316c48c53bb3082c29f7862ebc554087e ] symbol-minimal.c's read_build_id() iterates ELF notes with the same pattern as symbol-elf.c's elf_read_build_id(): pointer arithmetic driven by n_namesz and n_descsz from 32-bit note header fields, without validating that the name and desc fit within the note section data. A malformed ELF file with oversized note sizes causes out-of-bounds reads past the section data buffer. Add the same bounds check as the libelf path: validate namesz and descsz individually against remaining data before advancing the pointer, avoiding size_t overflow on 32-bit. Fixes: b691f64360ecec49 ("perf symbols: Implement poor man's ELF parser") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-minimal.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index aeb2532488953..6080d85047e33 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -1,3 +1,4 @@ +#include "debug.h" #include "dso.h" #include "symbol.h" #include "symsrc.h" @@ -44,7 +45,7 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, ptr = note_data; while ((ptr + sizeof(*nhdr)) < (note_data + note_len)) { const char *name; - size_t namesz, descsz; + size_t namesz, descsz, remaining; nhdr = ptr; if (need_swap) { @@ -56,6 +57,14 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, namesz = NOTE_ALIGN(nhdr->n_namesz); descsz = NOTE_ALIGN(nhdr->n_descsz); + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = note_data + note_len - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; From 9ca0a78a5d56133fe139eac216da941818c5628e Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 10 Jun 2026 11:45:12 +0200 Subject: [PATCH 1342/3297] dt-bindings: phy: sc8280xp-qmp-pcie: Disallow bifurcation register on Purwa [ Upstream commit b3ee497970c63cea37976aeaa84bac39611fe0eb ] Neither of the two Gen4x4 PHYs found on Purwa supports bifurcation. The PHY is however physically laid out as if it were to, since there are two separate ports (A/B). Split out a new if-then block to un-require the bifurcation register handle to squash this warning: purwa-iot-evk.dtb: phy@1bd4000 (qcom,x1p42100-qmp-gen4x4-pcie-phy): 'qcom,4ln-config-sel' is a required property Fixes: 2e1ffd4c1805 ("dt-bindings: phy: qcom,qmp-pcie: Add X1P42100 PCIe Gen4x4 PHY") Reported-by: Rob Herring Closes: https://lore.kernel.org/linux-arm-msm/176857775469.1631885.16133311938753588148.robh@kernel.org/ Signed-off-by: Konrad Dybcio Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260610-topic-purwa_phy_shutup_warning-v2-1-951c1fbfe9b2@oss.qualcomm.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 119b4ff36dbd6..19d7de3ce7a96 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -134,13 +134,22 @@ allOf: items: - description: port a - description: port b - required: - - qcom,4ln-config-sel else: properties: reg: maxItems: 1 + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-qmp-gen3x4-pcie-phy + - qcom,x1e80100-qmp-gen4x4-pcie-phy + then: + required: + - qcom,4ln-config-sel + - if: properties: compatible: From f77c490c45d46e125e9452bdf3376f8a7f6c8e00 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Mon, 2 Mar 2026 17:46:48 -0800 Subject: [PATCH 1343/3297] PCI: mediatek: Fix possible truncation in mtk_pcie_parse_port() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ab4a4043db1fcc4fd4c5745c5be8caf053502e29 ] As reported by the W=1 warning below, content of the 'name' variable might get truncated with the existing size of 10 bytes. Though it is not practically possible to exceed the 10 bytes size, increase it to 20 to silence the warning for a clean W=1 build: $ make W=1 drivers/pci/controller/pcie-mediatek.o CALL scripts/checksyscalls.sh DESCEND objtool INSTALL libsubcmd_headers CC drivers/pci/controller/pcie-mediatek.o drivers/pci/controller/pcie-mediatek.c: In function ‘mtk_pcie_parse_port’: drivers/pci/controller/pcie-mediatek.c:963:43: error: ‘%d’ directive output may be truncated writing between 1 and 10 bytes into a region of size 6 [-Werror=format-truncation=] 963 | snprintf(name, sizeof(name), "port%d", slot); | ^~ drivers/pci/controller/pcie-mediatek.c:963:38: note: directive argument in the range [0, 2147483647] 963 | snprintf(name, sizeof(name), "port%d", slot); | ^~~~~~~~ drivers/pci/controller/pcie-mediatek.c:963:9: note: ‘snprintf’ output between 6 and 15 bytes into a destination of size 10 963 | snprintf(name, sizeof(name), "port%d", slot); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ryder Lee [mani: commit log] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/b835e360b42c5e0994f9301a34dbdf140a8d3ef5.1772493898.git.ryder.lee@mediatek.com Stable-dep-of: ebc1d9906894 ("PCI: mediatek: Use actual physical address instead of virt_to_phys()") Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-mediatek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 83880840b8764..cd4be8ec0d03a 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -920,7 +920,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, struct mtk_pcie_port *port; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); - char name[10]; + char name[20]; int err; port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); From 872f9a63a108e2ccb94dac1209ce96dcc411f531 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 21 May 2026 17:19:49 +0000 Subject: [PATCH 1344/3297] PCI: mediatek: Use actual physical address instead of virt_to_phys() [ Upstream commit ebc1d9906894703286d12306a6f242d90cfb49e8 ] The driver previously used virt_to_phys() on the ioremapped register base (port->base) to compute the MSI message address. Using virt_to_phys() on an IO mapped address is incorrect because it expects a kernel virtual address. To fix it, store the physical start of the I/O register region in mtk_pcie_port->phys_base and use it to build the MSI address. This replaces the incorrect virt_to_phys() usage and ensures MSI addresses are generated correctly. Fixes: 43e6409db64d ("PCI: mediatek: Add MSI support for MT2712 and MT7622") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Tested-by: Caleb James DeLisle Link: https://patch.msgid.link/20260521171951.1495781-2-cjd@cjdns.fr Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-mediatek.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index cd4be8ec0d03a..6afce031523bd 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -166,6 +166,7 @@ struct mtk_pcie_soc { /** * struct mtk_pcie_port - PCIe port information * @base: IO mapped register base + * @phys_base: Physical address of the I/O register base region * @list: port list * @pcie: pointer to PCIe host info * @reset: pointer to port reset control @@ -187,6 +188,7 @@ struct mtk_pcie_soc { */ struct mtk_pcie_port { void __iomem *base; + phys_addr_t phys_base; struct list_head list; struct mtk_pcie *pcie; struct reset_control *reset; @@ -396,7 +398,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) phys_addr_t addr; /* MT2712/MT7622 only support 32-bit MSI addresses */ - addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + addr = port->phys_base + PCIE_MSI_VECTOR; msg->address_hi = 0; msg->address_lo = lower_32_bits(addr); @@ -511,7 +513,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) u32 val; phys_addr_t msg_addr; - msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + msg_addr = port->phys_base + PCIE_MSI_VECTOR; val = lower_32_bits(msg_addr); writel(val, port->base + PCIE_IMSI_ADDR); @@ -920,6 +922,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, struct mtk_pcie_port *port; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); + struct resource *res; char name[20]; int err; @@ -928,7 +931,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, return -ENOMEM; snprintf(name, sizeof(name), "port%d", slot); - port->base = devm_platform_ioremap_resource_byname(pdev, name); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) { + dev_err(dev, "failed to get port%d base\n", slot); + return -EINVAL; + } + + port->phys_base = res->start; + port->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(port->base)) { dev_err(dev, "failed to map port%d base\n", slot); return PTR_ERR(port->base); From 11016555d7510a384c884d505ed20e3180beb7d9 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 5 Jun 2026 19:57:20 +0800 Subject: [PATCH 1345/3297] phy: freescale: phy-fsl-imx8qm-lvds-phy: Fix missing pm_runtime_disable() on probe error path [ Upstream commit 799e7cf2f0b50b34660b5ffce0f7d8dec376a0d5 ] If mixel_lvds_phy_reset() fails in probe after pm_runtime_enable(), the function returns directly without calling pm_runtime_disable(), leaving runtime PM permanently enabled for the device. Fix this by using devm_pm_runtime_enable() so that cleanup is automatic on any probe failure or driver unbind. This also allows removing the manual err label and the .remove callback. Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support") Acked-by: Liu Ying Signed-off-by: Felix Gu Reviewed-by: Frank Li Link: https://patch.msgid.link/20260605-lvds-v2-1-3ce7539d1104@gmail.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- .../phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c index 7aef2f59e8eb4..c366d765d6508 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -347,7 +347,9 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, priv); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = mixel_lvds_phy_reset(dev); if (ret) { @@ -357,17 +359,15 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) for (i = 0; i < PHY_NUM; i++) { lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL); - if (!lvds_phy) { - ret = -ENOMEM; - goto err; - } + if (!lvds_phy) + return -ENOMEM; phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops); if (IS_ERR(phy)) { ret = PTR_ERR(phy); dev_err(dev, "failed to create PHY for channel%d: %d\n", i, ret); - goto err; + return ret; } lvds_phy->phy = phy; @@ -381,19 +381,10 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev) if (IS_ERR(phy_provider)) { ret = PTR_ERR(phy_provider); dev_err(dev, "failed to register PHY provider: %d\n", ret); - goto err; + return ret; } return 0; -err: - pm_runtime_disable(dev); - - return ret; -} - -static void mixel_lvds_phy_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); } static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev) @@ -434,7 +425,6 @@ MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match); static struct platform_driver mixel_lvds_phy_driver = { .probe = mixel_lvds_phy_probe, - .remove = mixel_lvds_phy_remove, .driver = { .pm = &mixel_lvds_phy_pm_ops, .name = "mixel-lvds-phy", From 514b84b1bf30f75f32c2fa66734dc1a177abb73e Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 18 May 2026 22:44:17 +0545 Subject: [PATCH 1346/3297] PCI: dwc: Avoid dwc_pcie_rasdes_debugfs_deinit() NULL dereference when no RAS DES capability [ Upstream commit 26b67fa10ef84ea667942491b50e6261a45f098d ] dwc_pcie_rasdes_debugfs_init() returns success when the controller has no RAS DES capability, leaving pci->debugfs->rasdes_info unset. The common debugfs teardown path still calls dwc_pcie_rasdes_debugfs_deinit(), which dereferences rasdes_info unconditionally. Return early when no RAS DES state was allocated. In that case no RAS DES mutex was initialized, so there is nothing to destroy. Fixes: 4fbfa17f9a07 ("PCI: dwc: Add debugfs based Silicon Debug support for DWC") Signed-off-by: Shuvam Pandey [mani: reworded subject] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/0f97352506d8d813f70f441de4d63fcd5b7d1c3e.1779123847.git.shuvampandey1@gmail.com Signed-off-by: Sasha Levin --- drivers/pci/controller/dwc/pcie-designware-debugfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index bc741d2261fbb..0c81058e32fc9 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -560,6 +560,9 @@ static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + if (!rinfo) + return; + mutex_destroy(&rinfo->reg_event_lock); } From f79519f636059b007d6abc91652f36dea4f54a20 Mon Sep 17 00:00:00 2001 From: Yuanhe Shu Date: Thu, 11 Jun 2026 10:59:01 +0800 Subject: [PATCH 1347/3297] Revert "PCI/MSI: Unmap MSI-X region on error" [ Upstream commit f64e03da0d83cb173743888bff4a7e61476a8fc2 ] This reverts commit 1a8d4c6ecb4c81261bcdf13556abd4a958eca202. Commit 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") added an iounmap(dev->msix_base) on the error path of msix_capability_init() to release the MSI-X region when msix_setup_interrupts() fails. When msix_setup_interrupts() fails, the call chain is: msix_setup_interrupts() -> __msix_setup_interrupts() struct pci_dev *dev __free(free_msi_irqs) = __dev; ... return ret; // __free cleanup fires on error The __free(free_msi_irqs) cleanup calls pci_free_msi_irqs(), which already handles the unmap: void pci_free_msi_irqs(struct pci_dev *dev) { pci_msi_teardown_msi_irqs(dev); if (dev->msix_base) { iounmap(dev->msix_base); // already unmapped here dev->msix_base = NULL; // and set to NULL } } So dev->msix_base is unmapped and set to NULL before msix_setup_interrupts() returns to msix_capability_init(). The "goto out_unmap" introduced by commit 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") then calls iounmap() a second time on a NULL pointer. This was reproduced on Intel Emerald Rapids (192 CPUs) while running tools/testing/selftests/kexec/test_kexec_jump.sh: WARNING: CPU#44 at iounmap+0x2a/0xe0 RIP: 0010:iounmap+0x2a/0xe0 RDI: 0000000000000000 Call Trace: msix_capability_init+0x317/0x3f0 __pci_enable_msix_range+0x21d/0x2c0 pci_alloc_irq_vectors_affinity+0xa9/0x130 nvme_setup_io_queues+0x2a8/0x420 [nvme] nvme_reset_work+0x151/0x340 [nvme] ... RDI=0 confirms iounmap() is called with NULL. Restore the original "goto out_disable" and leave the unmap to the existing __free(free_msi_irqs) cleanup. Fixes: 1a8d4c6ecb4c ("PCI/MSI: Unmap MSI-X region on error") Reported-by: Guenter Roeck Signed-off-by: Yuanhe Shu Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/all/20260610194406.GA380991@bhelgaas/ Link: https://patch.msgid.link/20260611025901.1105209-1-xiangzao@linux.alibaba.com Closes: https://lore.kernel.org/all/4fc6208d-513b-4f41-a13a-4a0829ab50ad@roeck-us.net/ Signed-off-by: Sasha Levin --- drivers/pci/msi/msi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index e010ecd9f90dd..34d664139f48f 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -737,7 +737,7 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, ret = msix_setup_interrupts(dev, entries, nvec, affd); if (ret) - goto out_unmap; + goto out_disable; /* Disable INTX */ pci_intx_for_msi(dev, 0); @@ -758,8 +758,6 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, pcibios_free_irq(dev); return 0; -out_unmap: - iounmap(dev->msix_base); out_disable: dev->msix_enabled = 0; pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE, 0); From ef488d7429d23d131eec3d2c1e6f8292d37e92b7 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Oct 2025 23:46:19 -0700 Subject: [PATCH 1348/3297] apparmor: fix shadowing of plabel that prevents cache from being updated [ Upstream commit 4483efe4f21510b30c24bc97d9fd0e8feab94125 ] Unfortunately the plabel was being shadowed by an unused local var. This didn't affect the mediation check but did cauase the cache to not correctly be updated resulting in extra mediation checks. Fixes: 88fec3526e841 ("apparmor: make sure unix socket labeling is correctly updated.") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/af_unix.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index ac0f4be791ece..7757829188d60 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -758,11 +758,10 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, unix_fs_perm(op, request, subj_cred, label, is_unix_fs(peer_sk) ? &peer_path : NULL)); } else if (!is_sk_fs) { - struct aa_label *plabel; struct aa_sk_ctx *pctx = aa_sock(peer_sk); rcu_read_lock(); - plabel = aa_get_label_rcu(&pctx->label); + plabel = aa_get_newest_label(pctx->label); rcu_read_unlock(); /* no fs check of aa_unix_peer_perm because conditions above * ensure they will never be done From d8ea44f6090c087fe255d9512fb808574b4d88e8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 24 Oct 2025 12:25:38 -0700 Subject: [PATCH 1349/3297] apparmor: fix race in unix socket mediation when peer_path is used [ Upstream commit b1aea2c1960771a276d7e68c7424168eccd0c3da ] The holding a reference to the peer_sk is not enough to ensure access to the peer sk path. Accessing the path outside of the state lock allows for a race with unix_release_sock(). Fix this by taking the state lock and getting a reference to the path under lock. Ideally for connected sockets we would cache this information so we don't have to take the lock here. But for now just fix the race. Fixes: bc6e5f6933b8e ("apparmor: Remove use of the double lock") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/af_unix.c | 64 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 7757829188d60..81cba79f7aa23 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -748,41 +748,47 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, if (!peer_sk) goto out; - peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); - - struct path peer_path; - - peer_path = unix_sk(peer_sk)->path; - if (!is_sk_fs && is_unix_fs(peer_sk)) { - last_error(error, - unix_fs_perm(op, request, subj_cred, label, - is_unix_fs(peer_sk) ? &peer_path : NULL)); - } else if (!is_sk_fs) { - struct aa_sk_ctx *pctx = aa_sock(peer_sk); - - rcu_read_lock(); - plabel = aa_get_newest_label(pctx->label); - rcu_read_unlock(); - /* no fs check of aa_unix_peer_perm because conditions above - * ensure they will never be done - */ - last_error(error, - xcheck(unix_peer_perm(subj_cred, label, op, + if (!is_sk_fs) { + bool is_peer_fs = is_unix_fs(peer_sk); + + peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen); + if (is_peer_fs) { + struct path peer_path; + + unix_state_lock(peer_sk); + peer_path = unix_sk(peer_sk)->path; + if (peer_path.dentry) + path_get(&peer_path); + unix_state_unlock(peer_sk); + + last_error(error, + unix_fs_perm(op, request, subj_cred, label, + &peer_path)); + if (peer_path.dentry) + path_put(&peer_path); + } else { + struct aa_sk_ctx *pctx = aa_sock(peer_sk); + + rcu_read_lock(); + plabel = aa_get_newest_label(pctx->label); + rcu_read_unlock(); + /* no fs check of aa_unix_peer_perm because conditions + * above ensure they will never be done + */ + last_error(error, + xcheck(unix_peer_perm(subj_cred, label, op, MAY_READ | MAY_WRITE, sock->sk, is_sk_fs ? &path : NULL, peer_addr, peer_addrlen, - is_unix_fs(peer_sk) ? - &peer_path : NULL, - plabel), - unix_peer_perm(file->f_cred, plabel, op, + NULL, plabel), + unix_peer_perm(file->f_cred, plabel, op, MAY_READ | MAY_WRITE, peer_sk, - is_unix_fs(peer_sk) ? - &peer_path : NULL, - addr, addrlen, + NULL, addr, addrlen, is_sk_fs ? &path : NULL, label))); - if (!error && !__aa_subj_label_is_cached(plabel, label)) - update_peer_ctx(peer_sk, pctx, label); + if (!error && !__aa_subj_label_is_cached(plabel, label)) + update_peer_ctx(peer_sk, pctx, label); + } } sock_put(peer_sk); From 045dbe89ac31709abd73390d0805d52fece7ef39 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 24 Oct 2025 12:59:51 -0700 Subject: [PATCH 1350/3297] apparmor: fix refcount leak when updating the sk_ctx [ Upstream commit 6d25e7b47616cb2db43351210929c8f19dc305a3 ] Currently update_sk_ctx() transfers the plabel reference, unfortunately it is also unconditionally put in the caller. Ideally we would make the caller conditionally put the reference based on whether it was transferred but for now just fix the bug by getting a reference. Fixes: 88fec3526e841 ("apparmor: make sure unix socket labeling is correctly updated.") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/af_unix.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c index 81cba79f7aa23..1e061345c0b16 100644 --- a/security/apparmor/af_unix.c +++ b/security/apparmor/af_unix.c @@ -674,9 +674,11 @@ static void update_sk_ctx(struct sock *sk, struct aa_label *label, old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock)); if (old == plabel) { - rcu_assign_pointer(ctx->peer_lastupdate, plabel); + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); } else if (aa_label_is_subset(plabel, old)) { - rcu_assign_pointer(ctx->peer_lastupdate, plabel); + rcu_assign_pointer(ctx->peer_lastupdate, + aa_get_label(plabel)); rcu_assign_pointer(ctx->peer, aa_get_label(plabel)); aa_put_label(old); } /* else race or a subset - don't update */ From ad965f36d2986741e5077efc83909f110970db8e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jun 2026 13:30:46 -0700 Subject: [PATCH 1351/3297] security/apparmor/apparmorfs.c: conditionally compile get_loaddata_common_ref() [ Upstream commit d62d9bfe050f44f772d05a32079dba3e3523ab2a ] Some config did this: security/apparmor/apparmorfs.c:177:28: warning: 'get_loaddata_common_ref' defined but not used [-Wunused-function] 177 | static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) get_loaddata_common_ref() is only used if CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y. (Or of course move the function into that block if maintainers perfer) Fixes: 8e135b8aee5a0 ("apparmor: fix race between freeing data and fs accessing it") Cc: John Johansen Cc: Paul Moore Cc: James Morris Cc: "Serge E. Hallyn" Signed-off-by: Andrew Morton Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ff301c7e84d90..5b326d0b170fa 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -174,6 +174,7 @@ static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref) return NULL; } +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) { if (ref) @@ -181,6 +182,7 @@ static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) count)); return NULL; } +#endif static void aa_put_common_ref(struct aa_common_ref *ref) { From d82160132345688a09cbaa648cfdd16bb32e8ea2 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Mon, 8 Jun 2026 14:36:31 +0800 Subject: [PATCH 1352/3297] apparmor: check label build before no_new_privs test [ Upstream commit a58cafd38b46fb1a2220e2fbbcfe291ea75fa147 ] aa_change_profile() builds a replacement label with fn_label_build_in_scope() before the no_new_privs subset check. The build helper can fail and return NULL or an ERR_PTR, but the result was passed to aa_label_is_unconfined_subset() before the existing IS_ERR_OR_NULL() check. Reuse the existing target-label build failure handling immediately after the build. This preserves the current audit handling while preventing the subset helper from dereferencing an invalid label. Fixes: e00b02bb6ac2a ("apparmor: move change_profile mediation to using labels") Signed-off-by: Ruoyu Wang Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/domain.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 267da82afb14a..cc54c1205efe8 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -1525,6 +1525,8 @@ int aa_change_profile(const char *fqname, int flags) new = fn_label_build_in_ns(label, profile, GFP_KERNEL, aa_get_label(target), aa_get_label(&profile->label)); + if (IS_ERR_OR_NULL(new)) + goto build_fail; /* * no new privs prevents domain transitions that would * reduce restrictions. @@ -1543,16 +1545,8 @@ int aa_change_profile(const char *fqname, int flags) /* only transition profiles in the current ns */ if (stack) new = aa_label_merge(label, target, GFP_KERNEL); - if (IS_ERR_OR_NULL(new)) { - info = "failed to build target label"; - if (!new) - error = -ENOMEM; - else - error = PTR_ERR(new); - new = NULL; - perms.allow = 0; - goto audit; - } + if (IS_ERR_OR_NULL(new)) + goto build_fail; error = aa_replace_current_label(new); } else { if (new) { @@ -1564,6 +1558,17 @@ int aa_change_profile(const char *fqname, int flags) aa_set_current_onexec(target, stack); } + goto audit; + +build_fail: + info = "failed to build target label"; + if (!new) + error = -ENOMEM; + else + error = PTR_ERR(new); + new = NULL; + perms.allow = 0; + audit: error = fn_for_each_in_ns(label, profile, aa_audit_file(subj_cred, From ae02e603c0b39b29f3ce6fe3efe01b286af1a2a4 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Sat, 2 May 2026 13:21:33 +0200 Subject: [PATCH 1353/3297] apparmor: aa_label_alloc use aa_label_free on alloc failure [ Upstream commit 654fe7505dc6889724d4094fa64f89991afabfc3 ] aa_label_alloc() allocates a secid before allocating or taking the label proxy. If the later proxy step fails, the error path only freed the label memory, leaking any resources initialized by aa_label_init(). Use aa_label_free() on the failure path so partially initialized labels release their secid and other label resources before the backing memory is freed. Fixes: f1bd904175e81 ("apparmor: add the base fns() for domain labels") Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 3bec1e33815e2..d382ac772ca1d 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -458,7 +458,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp) return new; fail: - kfree(new); + aa_label_free(new); return NULL; } From 9111f76e8dc8cfe76908969f3fbec5a006f6b34d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 1 Mar 2026 12:24:06 -0800 Subject: [PATCH 1354/3297] apparmor: fix rawdata_f_data implicit flex array [ Upstream commit ad213bbbc0e3e270ce7df2d9d80d4ce3826993d7 ] rawdata_f_data has a blob of data that is allocated at its end but not explicitly declared. Makes sure it is correctly declared as a flex_rray. Fixes: 63c16c3a76085 ("apparmor: Initial implementation of raw policy blob compression") Reviewed-by: Georgia Garcia Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 5b326d0b170fa..921daf31267ab 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -71,10 +71,10 @@ struct rawdata_f_data { struct aa_loaddata *loaddata; + DECLARE_FLEX_ARRAY(char, data); }; #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY -#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) static void rawdata_f_data_free(struct rawdata_f_data *private) { @@ -1442,7 +1442,7 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, struct rawdata_f_data *private = file->private_data; return simple_read_from_buffer(buf, size, ppos, - RAWDATA_F_DATA_BUF(private), + private->data, private->loaddata->size); } @@ -1475,8 +1475,7 @@ static int rawdata_open(struct inode *inode, struct file *file) private->loaddata = loaddata; error = decompress_zstd(loaddata->data, loaddata->compressed_size, - RAWDATA_F_DATA_BUF(private), - loaddata->size); + private->data, loaddata->size); if (error) goto fail_decompress; From b427061ca498309bf581bb6f8df4ffcfe4c772cd Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 13 Feb 2026 11:29:38 -0800 Subject: [PATCH 1355/3297] apparmor: grab ns lock and refresh when looking up changehat child profiles [ Upstream commit 32e92764d6f8d251c1bca62be33793287b453a81 ] There was a race condition involving change_hat and profile replacement in which replacement of the parent profile during a changehat operation could result in the list of children becoming empty and the changehat operation failing. To prevent this: - grab the namespace lock until we've built the hat transition, and - use aa_get_newest_profile to avoid using stale profile objects. Link: https://bugs.launchpad.net/bugs/2139664 Fixes: 89dbf1962aa63 ("apparmor: move change_hat mediation to using labels") Reviewed-by: Georgia Garcia Signed-off-by: Ryan Lee Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/domain.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index cc54c1205efe8..cf9feed1306de 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1107,6 +1108,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, int count, int flags) { struct aa_profile *profile, *root, *hat = NULL; + struct aa_ns *ns, *new_ns; struct aa_label *new; struct label_it it; bool sibling = false; @@ -1117,6 +1119,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred, AA_BUG(!hats); AA_BUG(count < 1); + /* + * Acquire the newest label and then hold the lock until we choose a + * hat, so that profile replacement doesn't atomically truncate the + * list of potential hats. Because we are getting the namespaces from + * the profiles and label, we can rely on the namespaces being live + * and avoid incrementing their refcounts while grabbing the lock. + */ + label = aa_get_label(label); + ns = labels_ns(label); + +retry: + mutex_lock_nested(&ns->lock, ns->level); + if (label_is_stale(label)) { + new = aa_get_newest_label(label); + new_ns = labels_ns(new); + if (new_ns != ns) { + aa_put_label(new); + mutex_unlock(&ns->lock); + ns = new_ns; + label = new; + goto retry; + } + aa_put_label(label); + label = new; + } + if (PROFILE_IS_HAT(labels_profile(label))) sibling = true; @@ -1125,7 +1153,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, name = hats[i]; label_for_each_in_ns(it, labels_ns(label), label, profile) { if (sibling && PROFILE_IS_HAT(profile)) { - root = aa_get_profile_rcu(&profile->parent); + root = aa_get_profile(profile->parent); } else if (!sibling && !PROFILE_IS_HAT(profile)) { root = aa_get_profile(profile); } else { /* conflicting change type */ @@ -1185,6 +1213,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, GLOBAL_ROOT_UID, info, error); } } + mutex_unlock(&ns->lock); return ERR_PTR(error); build: @@ -1197,7 +1226,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred, error = -ENOMEM; goto fail; } /* else if (IS_ERR) build_change_hat has logged error so return new */ - + mutex_unlock(&ns->lock); return new; } From 57b1bd4486d56254bb8af1a8f3d4445bbe505290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20B=C3=A9lair?= Date: Wed, 18 Feb 2026 10:27:34 +0100 Subject: [PATCH 1356/3297] apparmor: fix potential UAF in aa_replace_profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7b42f95813dc9ceb6bda35afcf914630909a19f9 ] The function aa_replace_profiles was accessing udata->size after calling aa_put_loaddata(udata), causing a potential UAF. Fixed this by saving the size to a local variable before dropping the reference. Fixes: 5ac8c355ae001 ("apparmor: allow introspecting the loaded policy pre internal transform") Reviewed-by: Georgia Garcia Signed-off-by: Maxime Bélair Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c474a55fed22c..6762124521aa0 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1358,13 +1358,15 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_unlock(&ns->lock); out: + ssize_t udata_sz = udata->size; + aa_put_ns(ns); aa_put_profile_loaddata(udata); kfree(ns_name); if (error) return error; - return udata->size; + return udata_sz; fail_lock: mutex_unlock(&ns->lock); From fbfdb5a94a48797bea232791e24c38f3aa3063c8 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Wed, 6 May 2026 16:02:11 -0300 Subject: [PATCH 1357/3297] apparmor: fix NULL pointer dereference in unpack_pdb [ Upstream commit 7681ca43d2b1c776e62fe77e3167835fb1ab8319 ] pdb->dfa could be NULL if unpack_dfa fails, causing a NULL pointer dereference. Fixes: 2e12c5f06017 ("apparmor: add additional flags to extended permission.") Signed-off-by: Georgia Garcia Signed-off-by: Manuel Diewald Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy_unpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index b0e18dd8d512b..3ae58f476c081 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -804,7 +804,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, } /* accept2 is in some cases being allocated, even with perms */ - if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { + if (pdb->dfa && pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { /* add dfa flags table missing in v2 */ u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen; u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags; From 566d1ef98a7115026ef3c96c9d71c390aca29def Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Thu, 29 Jan 2026 15:39:42 -0300 Subject: [PATCH 1358/3297] apparmor: remove or add symlinks to rawdata according to export_binary [ Upstream commit 59fe6fbc4cd45582bc8893de0a382a36562317b3 ] When the export_binary parameter is set, then rawdata is available and there should be a symbolic link for the rawdata in the profile directory in apparmorfs. If the parameter is unset, then the symlinks should not exist. The issue arises when changing the value of export_binary on runtime and replacing profiles. If export_binary was set when the profile was originally loaded, then changed to 0 and the profile was reloaded, then the symbolic links would still exist but would return ENOENT because the rawdata no longer exists. On the opposite side, if export_binary was unset when the profile was originally loaded, then changed to 1 and the profile was reloaded, then the symbolic links would not exist, even though the rawdata does. Fixes: d61c57fde8191 ("apparmor: make export of raw binary profile to userspace optional") Signed-off-by: Georgia Garcia Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 102 +++++++++++++++++++------ security/apparmor/include/apparmorfs.h | 12 +++ security/apparmor/policy.c | 15 ++++ 3 files changed, 104 insertions(+), 25 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 921daf31267ab..11a92680dcc1a 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1759,6 +1759,80 @@ static const struct inode_operations rawdata_link_abi_iops = { static const struct inode_operations rawdata_link_data_iops = { .get_link = rawdata_get_link_data, }; + +/* + * Requires: @profile->ns->lock held + */ +void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile) +{ + aafs_remove(profile->dents[AAFS_PROF_RAW_HASH]); + profile->dents[AAFS_PROF_RAW_HASH] = NULL; + aafs_remove(profile->dents[AAFS_PROF_RAW_ABI]); + profile->dents[AAFS_PROF_RAW_ABI] = NULL; + aafs_remove(profile->dents[AAFS_PROF_RAW_DATA]); + profile->dents[AAFS_PROF_RAW_DATA] = NULL; +} + +static inline int create_symlink_dent(struct aa_profile *profile, + const char *name, + enum aafs_prof_type type, + const struct inode_operations *iops) +{ + struct dentry *dent = NULL; + struct dentry *dir = prof_dir(profile); + + if (profile->dents[type]) + return 0; + + dent = aafs_create(name, S_IFLNK | 0444, dir, + &profile->label.proxy->count, NULL, NULL, iops); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + profile->dents[type] = dent; + return 0; +} + +/* + * Requires: @profile->ns->lock held + */ +int __aa_create_rawdata_symlink_dents(struct aa_profile *profile) +{ + int error; + + if (!profile || + (profile->dents[AAFS_PROF_RAW_HASH] && + profile->dents[AAFS_PROF_RAW_ABI] && + profile->dents[AAFS_PROF_RAW_DATA])) + return 0; + + if (!profile->rawdata) + return 0; + + if (aa_g_hash_policy) { + error = create_symlink_dent(profile, "raw_sha256", + AAFS_PROF_RAW_HASH, + &rawdata_link_sha256_iops); + if (error) + return error; + } + + error = create_symlink_dent(profile, "raw_abi", + AAFS_PROF_RAW_ABI, + &rawdata_link_abi_iops); + if (error) + return error; + + + error = create_symlink_dent(profile, "raw_data", + AAFS_PROF_RAW_DATA, + &rawdata_link_data_iops); + if (error) + return error; + + return 0; +} + #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ /* @@ -1834,31 +1908,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) profile->dents[AAFS_PROF_HASH] = dent; } -#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY - if (profile->rawdata) { - if (aa_g_hash_policy) { - dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, - NULL, &rawdata_link_sha256_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_HASH] = dent; - } - dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, NULL, - &rawdata_link_abi_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_ABI] = dent; - - dent = aafs_create("raw_data", S_IFLNK | 0444, dir, - &profile->label.proxy->count, NULL, NULL, - &rawdata_link_data_iops); - if (IS_ERR(dent)) - goto fail; - profile->dents[AAFS_PROF_RAW_DATA] = dent; - } -#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ + error = __aa_create_rawdata_symlink_dents(profile); + if (error) + goto fail2; list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aafs_profile_mkdir(child, prof_child_dir(profile)); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 1e94904f68d90..9224497736fdf 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -118,6 +118,8 @@ struct aa_loaddata; #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata); +void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile); +int __aa_create_rawdata_symlink_dents(struct aa_profile *profile); #else static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata) { @@ -129,6 +131,16 @@ static inline int __aa_fs_create_rawdata(struct aa_ns *ns, { return 0; } + +static inline void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile) +{ + /* empty stub */ +} + +static inline int __aa_create_rawdata_symlink_dents(struct aa_profile *profile) +{ + return 0; +} #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 6762124521aa0..f44cd6ac9b7b4 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1329,6 +1329,16 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, goto skip; } + if (!aa_g_export_binary) { + if (ent->old && ent->old->rawdata && + ent->old->dents[AAFS_LOADDATA_DIR]) { + /* remove rawdata symlinks because the symlink + * target will be removed + */ + __aa_remove_rawdata_symlink_dents(ent->old); + } + } + /* * TODO: finer dedup based on profile range in data. Load set * can differ but profile may remain unchanged @@ -1339,6 +1349,11 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, if (ent->old) { share_name(ent->old, ent->new); __replace_profile(ent->old, ent->new); + if (aa_g_export_binary) { + /* recreate rawdata symlinks */ + if (!ent->old->rawdata) + __aa_create_rawdata_symlink_dents(ent->new); + } } else { struct list_head *lh; From 3b918f6f5239087cf9f9224c356a1eb37dfcf46d Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Sun, 3 May 2026 12:12:43 +0800 Subject: [PATCH 1359/3297] apparmor: Fix return in ns_mkdir_op [ Upstream commit b7a2b49bba4e5994a476c49d662b796818079e5e ] Return NULL instead of passing to ERR_PTR while error is zero. Fixes smatch warning: - security/apparmor/apparmorfs.c:1846 ns_mkdir_op() warn: passing zero to 'ERR_PTR' Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Reviewed-by: Ryan Lee Signed-off-by: Hongling Zeng Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/apparmorfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 11a92680dcc1a..515cc80064c60 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1977,7 +1977,7 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir, mutex_unlock(&parent->lock); aa_put_ns(parent); - return ERR_PTR(error); + return error ? ERR_PTR(error) : NULL; } static int ns_rmdir_op(struct inode *dir, struct dentry *dentry) From 22dc9433d458cff1270f120685114a9753e0ad1b Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Mon, 4 May 2026 08:32:37 +0200 Subject: [PATCH 1360/3297] apparmor: fail policy unpack on accept2 allocation failure [ Upstream commit 45cf568241048e560a81aa2053f06a62069f5640 ] unpack_pdb() may need to allocate a missing ACCEPT2 table for older policy data. If that allocation failed, it set an error message but jumped to the success path, returning a policydb with the required table missing. Return -ENOMEM through the normal failure path when the ACCEPT2 allocation fails. Remove the now-unused out label. Fixes: 2e12c5f06017 ("apparmor: add additional flags to extended permission.") Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy_unpack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 3ae58f476c081..1e1792d3ff5a4 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -813,7 +813,8 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL); if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) { *info = "failed to alloc dfa flags table"; - goto out; + error = -ENOMEM; + goto fail; } pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents; pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags; @@ -837,7 +838,6 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, * - move free of unneeded trans table here, has to be done * after perm mapping. */ -out: *policy = pdb; return 0; From 66a6c61369d4120d6d988ec0eb07d0340f6a5acb Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Sat, 2 May 2026 13:37:14 +0200 Subject: [PATCH 1361/3297] apparmor: aa_getprocattr free procattr leak on format failure [ Upstream commit fea23bf73f0cae8ccb1d0684e4a3003874771f41 ] aa_getprocattr() allocates the output string before rendering the label into it. If the second aa_label_snxprint() call fails, the function returned without freeing that allocation. Free and clear the output pointer on the uncommon formatting failure path before dropping the namespace reference. Fixes: 76a1d263aba3 ("apparmor: switch getprocattr to using label_print fns()") Reviewed-by: Tyler Hicks Reviewed-by: Ryan Lee Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/procattr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index ce40f15d4952d..c07b6e8fd9c93 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -54,6 +54,8 @@ int aa_getprocattr(struct aa_label *label, char **string, bool newline) FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED); if (len < 0) { + kfree(*string); + *string = NULL; aa_put_ns(current_ns); return len; } From b58d240883dfef8734a9d36fa333b049e2766ac0 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Tue, 5 May 2026 05:40:53 +0200 Subject: [PATCH 1362/3297] apparmor: put secmark label after secid lookup [ Upstream commit 340372688bb87da45ff8d4e2f82ccfd1b64c65ff ] apparmor_secmark_init() parses a configured secmark label to obtain its secid. aa_label_strn_parse() returns a refcounted label, but the success path kept that reference after copying the secid. Fixes: ab9f2115081a ("apparmor: Allow filtering based on secmark policy") Signed-off-by: Zygmunt Krynicki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 1fc6145ccbb8d..cf590dd08540c 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -356,6 +356,7 @@ static int apparmor_secmark_init(struct aa_secmark *secmark) return PTR_ERR(label); secmark->secid = label->secid; + aa_put_label(label); return 0; } From fdf610a9a9e72e9f63a6ac7c9ea1d8e5a6002b57 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Thu, 28 May 2026 16:04:12 -0300 Subject: [PATCH 1363/3297] apparmor: don't audit files pointing to aa_null.dentry [ Upstream commit add2b70038bea194bcdef8a680f9153ee7f93ac0 ] In commit 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") there was a change to not audit files pointing to aa_null.dentry because they provide no value, but setting the error variable instead of returning -EACCES was still causing them to be audited. Fixes: 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") Acked-by: David Disseldorp Signed-off-by: Georgia Garcia Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 7de23e85cd5d0..80e4ad2fca9c4 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -156,7 +156,7 @@ static int path_name(const char *op, const struct cred *subj_cred, /* don't reaudit files closed during inheritance */ if (unlikely(path->dentry == aa_null.dentry)) - error = -EACCES; + return -EACCES; else error = aa_path_name(path, flags, buffer, name, &info, labels_profile(label)->disconnected); From 2791dd42d41e38805572b12116f721c97c4fcc0c Mon Sep 17 00:00:00 2001 From: Maciek Borzecki Date: Fri, 8 May 2026 10:30:16 +0200 Subject: [PATCH 1364/3297] apparmor: fix uninitialised pointer passed to audit_log_untrustedstring() [ Upstream commit bcd1b34c21748531a3febaf7440632b89d8deab7 ] Commit 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") intrdouced a small bug, where path_name() may pass a potentially uninitialized *name to aa_audit_file() if the path->dentry had been replaced with aa_null.dentry earlier on. This can lead to page fault like one observed on 7.0.2 openSUSE Tumbleweed kernel: [51692.242756] [ T24690] BUG: unable to handle page fault for address: 0000000f00000003 [51692.242762] [ T24690] #PF: supervisor read access in kernel mode [51692.242763] [ T24690] #PF: error_code(0x0000) - not-present page [51692.242765] [ T24690] PGD 0 P4D 0 [51692.242768] [ T24690] Oops: Oops: 0000 [#1] SMP NOPTI [51692.242772] [ T24690] CPU: 3 UID: 1020 PID: 24690 Comm: snap-confine Tainted: G O 7.0.2-1-default #1 PREEMPT(full) openSUSE Tumbleweed ab90b4c9940707f9cafa19bdad80b2cec52dbe51 [51692.242775] [ T24690] Tainted: [O]=OOT_MODULE [51692.242777] [ T24690] Hardware name: Framework Laptop 13 (AMD Ryzen 7040Series)/FRANMDCP05, BIOS 03.18 01/08/2026 [51692.242778] [ T24690] RIP: 0010:strlen+0x4/0x30 [51692.242783] [ T24690] Code: f7 75 ec 31 c0 e9 17 9f 00 ff 48 89 f8 e9 0f 9f 00 ff 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa <80> 3f 00 74 18 48 89 f8 0f 1f 40 00 48 83 c0 01 80 38 00 75 f7 48 [51692.242785] [ T24690] RSP: 0018:ffffd015eb1e3608 EFLAGS: 00010282 [51692.242787] [ T24690] RAX: 0000000000000000 RBX: ffff89796198a360 RCX: 0000000000000000 [51692.242788] [ T24690] RDX: 00000000000000d1 RSI: 0000000f00000003 RDI: 0000000f00000003 [51692.242790] [ T24690] RBP: ffffffffb7ede090 R08: 00000000000005f5 R09: 0000000000000000 [51692.242791] [ T24690] R10: 0000000000000000 R11: 0000000000000000 R12: ffffd015eb1e3700 [51692.242792] [ T24690] R13: ffff8977a22bc380 R14: ffffffffb7ec5190 R15: ffff8977a0c8aa80 [51692.242794] [ T24690] FS: 0000000000000000(0000) GS:ffff897f640d8000(0000) knlGS:0000000000000000 [51692.242796] [ T24690] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [51692.242797] [ T24690] CR2: 0000000f00000003 CR3: 00000006ad15f000 CR4: 0000000000f50ef0 [51692.242799] [ T24690] PKRU: 55555554 [51692.242800] [ T24690] Call Trace: [51692.242802] [ T24690] [51692.242804] [ T24690] audit_log_untrustedstring+0x1d/0x40 [51692.242811] [ T24690] common_lsm_audit+0x71/0x1d0 [51692.242816] [ T24690] aa_audit+0x5a/0x170 [51692.242819] [ T24690] aa_audit_file+0x18a/0x1b0 [51692.242825] [ T24690] path_name+0xd2/0x100 [51692.242829] [ T24690] profile_path_perm.part.0+0x58/0xb0 [51692.242832] [ T24690] aa_path_perm+0xef/0x150 [51692.242837] [ T24690] apparmor_file_open+0x153/0x2e0 [51692.242840] [ T24690] security_file_open+0x46/0xd0 [51692.242844] [ T24690] do_dentry_open+0xe9/0x4d0 [51692.242848] [ T24690] vfs_open+0x30/0x100 While here, initialise variables which are passed down to path_name(). Fixes: 4a134723f9f1 ("apparmor: move check for aa_null file to cover all cases") Signed-off-by: Maciek Borzecki Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/file.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 80e4ad2fca9c4..534a93520c569 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -157,9 +157,9 @@ static int path_name(const char *op, const struct cred *subj_cred, /* don't reaudit files closed during inheritance */ if (unlikely(path->dentry == aa_null.dentry)) return -EACCES; - else - error = aa_path_name(path, flags, buffer, name, &info, - labels_profile(label)->disconnected); + + error = aa_path_name(path, flags, buffer, name, &info, + labels_profile(label)->disconnected); if (error) { fn_for_each_confined(label, profile, aa_audit_file(subj_cred, @@ -249,7 +249,7 @@ static int profile_path_perm(const char *op, const struct cred *subj_cred, struct path_cond *cond, int flags, struct aa_perms *perms) { - const char *name; + const char *name = NULL; int error; if (profile_unconfined(profile)) @@ -327,7 +327,7 @@ static int profile_path_link(const struct cred *subj_cred, struct path_cond *cond) { struct aa_ruleset *rules = profile->label.rules[0]; - const char *lname, *tname = NULL; + const char *lname = NULL, *tname = NULL; struct aa_perms lperms = {}, perms; const char *info = NULL; u32 request = AA_MAY_LINK; From 5625b8767ce3e521f4d716d6d6f19224de8127ad Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Jan 2026 18:44:08 +0200 Subject: [PATCH 1365/3297] i3c: mipi-i3c-hci: Quieten initialization messages [ Upstream commit 581d5b7953b8f24d2f379c8c56ceaa7d163488ce ] The copious initialization messages are at most useful only for debugging. Change them from dev_info() or dev_notice() to dev_dbg(). Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260106164416.67074-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 093eb8e73c90 ("i3c: mipi-i3c-hci: Preserve RUN bit when aborting DMA ring") Signed-off-by: Sasha Levin --- drivers/i3c/master/mipi-i3c-hci/core.c | 18 +++---- drivers/i3c/master/mipi-i3c-hci/dma.c | 4 +- drivers/i3c/master/mipi-i3c-hci/ext_caps.c | 55 ++++++++++------------ drivers/i3c/master/mipi-i3c-hci/pio.c | 16 +++---- 4 files changed, 45 insertions(+), 48 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index c355f4a2baf57..6d8a00486ba62 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -599,8 +599,8 @@ static int i3c_hci_init(struct i3c_hci *hci) hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8; if (size_in_dwords) hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size; - dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n", - hci->DAT_entries, hci->DAT_entry_size, offset); + dev_dbg(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n", + hci->DAT_entries, hci->DAT_entry_size, offset); regval = reg_read(DCT_SECTION); offset = FIELD_GET(DCT_TABLE_OFFSET, regval); @@ -609,23 +609,23 @@ static int i3c_hci_init(struct i3c_hci *hci) hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16; if (size_in_dwords) hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size; - dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n", - hci->DCT_entries, hci->DCT_entry_size, offset); + dev_dbg(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n", + hci->DCT_entries, hci->DCT_entry_size, offset); regval = reg_read(RING_HEADERS_SECTION); offset = FIELD_GET(RING_HEADERS_OFFSET, regval); hci->RHS_regs = offset ? hci->base_regs + offset : NULL; - dev_info(&hci->master.dev, "Ring Headers at offset %#x\n", offset); + dev_dbg(&hci->master.dev, "Ring Headers at offset %#x\n", offset); regval = reg_read(PIO_SECTION); offset = FIELD_GET(PIO_REGS_OFFSET, regval); hci->PIO_regs = offset ? hci->base_regs + offset : NULL; - dev_info(&hci->master.dev, "PIO section at offset %#x\n", offset); + dev_dbg(&hci->master.dev, "PIO section at offset %#x\n", offset); regval = reg_read(EXT_CAPS_SECTION); offset = FIELD_GET(EXT_CAPS_OFFSET, regval); hci->EXTCAPS_regs = offset ? hci->base_regs + offset : NULL; - dev_info(&hci->master.dev, "Extended Caps at offset %#x\n", offset); + dev_dbg(&hci->master.dev, "Extended Caps at offset %#x\n", offset); ret = i3c_hci_parse_ext_caps(hci); if (ret) @@ -710,7 +710,7 @@ static int i3c_hci_init(struct i3c_hci *hci) ret = -EIO; } else { hci->io = &mipi_i3c_hci_dma; - dev_info(&hci->master.dev, "Using DMA\n"); + dev_dbg(&hci->master.dev, "Using DMA\n"); } } @@ -722,7 +722,7 @@ static int i3c_hci_init(struct i3c_hci *hci) ret = -EIO; } else { hci->io = &mipi_i3c_hci_pio; - dev_info(&hci->master.dev, "Using PIO\n"); + dev_dbg(&hci->master.dev, "Using PIO\n"); } } diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 42ee94767be3d..2364d9507d75f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -213,7 +213,7 @@ static int hci_dma_init(struct i3c_hci *hci) regval = rhs_reg_read(CONTROL); nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval); - dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings); + dev_dbg(&hci->master.dev, "%d DMA rings available\n", nr_rings); if (unlikely(nr_rings > 8)) { dev_err(&hci->master.dev, "number of rings should be <= 8\n"); nr_rings = 8; @@ -233,7 +233,7 @@ static int hci_dma_init(struct i3c_hci *hci) for (i = 0; i < rings->total; i++) { u32 offset = rhs_reg_read(RHn_OFFSET(i)); - dev_info(&hci->master.dev, "Ring %d at offset %#x\n", i, offset); + dev_dbg(&hci->master.dev, "Ring %d at offset %#x\n", i, offset); ret = -EINVAL; if (!offset) goto err_out; diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c index 533a495e14c86..5350b209aa3d0 100644 --- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c +++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c @@ -27,9 +27,9 @@ static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base) hci->vendor_version_id = readl(base + 0x08); hci->vendor_product_id = readl(base + 0x0c); - dev_info(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id); - dev_info(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id); - dev_info(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id); + dev_dbg(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id); + dev_dbg(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id); + dev_dbg(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id); /* ought to go in a table if this grows too much */ switch (hci->vendor_mipi_id) { @@ -49,7 +49,7 @@ static int hci_extcap_master_config(struct i3c_hci *hci, void __iomem *base) static const char * const functionality[] = { "(unknown)", "master only", "target only", "primary/secondary master" }; - dev_info(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]); + dev_dbg(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]); if (operation_mode & 0x1) return 0; dev_err(&hci->master.dev, "only master mode is currently supported\n"); @@ -61,7 +61,7 @@ static int hci_extcap_multi_bus(struct i3c_hci *hci, void __iomem *base) u32 bus_instance = readl(base + 0x04); unsigned int count = FIELD_GET(GENMASK(3, 0), bus_instance); - dev_info(&hci->master.dev, "%d bus instances\n", count); + dev_dbg(&hci->master.dev, "%d bus instances\n", count); return 0; } @@ -71,8 +71,7 @@ static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base) u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1; unsigned int index; - dev_info(&hci->master.dev, "transfer mode table has %d entries\n", - entries); + dev_dbg(&hci->master.dev, "transfer mode table has %d entries\n", entries); base += 4; /* skip header */ for (index = 0; index < entries; index++) { u32 mode_entry = readl(base); @@ -95,7 +94,7 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base) base += 4; /* skip header */ - dev_info(&hci->master.dev, "available data rates:\n"); + dev_dbg(&hci->master.dev, "available data rates:\n"); for (index = 0; index < entries; index++) { rate_entry = readl(base); dev_dbg(&hci->master.dev, "entry %d: 0x%08x", @@ -103,12 +102,12 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base) rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry); rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry); mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry); - dev_info(&hci->master.dev, "rate %d for %s = %d kHz\n", - rate_id, - mode_id == XFERRATE_MODE_I3C ? "I3C" : - mode_id == XFERRATE_MODE_I2C ? "I2C" : - "unknown mode", - rate); + dev_dbg(&hci->master.dev, "rate %d for %s = %d kHz\n", + rate_id, + mode_id == XFERRATE_MODE_I3C ? "I3C" : + mode_id == XFERRATE_MODE_I2C ? "I2C" : + "unknown mode", + rate); base += 4; } @@ -122,8 +121,8 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base) u32 autocmd_ext_config = readl(base + 0x08); unsigned int count = FIELD_GET(GENMASK(3, 0), autocmd_ext_config); - dev_info(&hci->master.dev, "%d/%d active auto-command entries\n", - count, max_count); + dev_dbg(&hci->master.dev, "%d/%d active auto-command entries\n", + count, max_count); /* remember auto-command register location for later use */ hci->AUTOCMD_regs = base; return 0; @@ -131,46 +130,46 @@ static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base) static int hci_extcap_debug(struct i3c_hci *hci, void __iomem *base) { - dev_info(&hci->master.dev, "debug registers present\n"); + dev_dbg(&hci->master.dev, "debug registers present\n"); hci->DEBUG_regs = base; return 0; } static int hci_extcap_scheduled_cmd(struct i3c_hci *hci, void __iomem *base) { - dev_info(&hci->master.dev, "scheduled commands available\n"); + dev_dbg(&hci->master.dev, "scheduled commands available\n"); /* hci->schedcmd_regs = base; */ return 0; } static int hci_extcap_non_curr_master(struct i3c_hci *hci, void __iomem *base) { - dev_info(&hci->master.dev, "Non-Current Master support available\n"); + dev_dbg(&hci->master.dev, "Non-Current Master support available\n"); /* hci->NCM_regs = base; */ return 0; } static int hci_extcap_ccc_resp_conf(struct i3c_hci *hci, void __iomem *base) { - dev_info(&hci->master.dev, "CCC Response Configuration available\n"); + dev_dbg(&hci->master.dev, "CCC Response Configuration available\n"); return 0; } static int hci_extcap_global_DAT(struct i3c_hci *hci, void __iomem *base) { - dev_info(&hci->master.dev, "Global DAT available\n"); + dev_dbg(&hci->master.dev, "Global DAT available\n"); return 0; } static int hci_extcap_multilane(struct i3c_hci *hci, void __iomem *base) { - dev_info(&hci->master.dev, "Master Multi-Lane support available\n"); + dev_dbg(&hci->master.dev, "Master Multi-Lane support available\n"); return 0; } static int hci_extcap_ncm_multilane(struct i3c_hci *hci, void __iomem *base) { - dev_info(&hci->master.dev, "NCM Multi-Lane support available\n"); + dev_dbg(&hci->master.dev, "NCM Multi-Lane support available\n"); return 0; } @@ -203,7 +202,7 @@ static const struct hci_ext_caps ext_capabilities[] = { static int hci_extcap_vendor_NXP(struct i3c_hci *hci, void __iomem *base) { hci->vendor_data = (__force void *)base; - dev_info(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1*4)); + dev_dbg(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1 * 4)); /* reset the FPGA */ writel(0xdeadbeef, base + 1*4); return 0; @@ -241,9 +240,8 @@ static int hci_extcap_vendor_specific(struct i3c_hci *hci, void __iomem *base, } if (!vendor_cap_entry) { - dev_notice(&hci->master.dev, - "unknown ext_cap 0x%02x for vendor 0x%02x\n", - cap_id, hci->vendor_mipi_id); + dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x for vendor 0x%02x\n", + cap_id, hci->vendor_mipi_id); return 0; } if (cap_length < vendor_cap_entry->min_length) { @@ -296,8 +294,7 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci) } } if (!cap_entry) { - dev_notice(&hci->master.dev, - "unknown ext_cap 0x%02x\n", cap_id); + dev_dbg(&hci->master.dev, "unknown ext_cap 0x%02x\n", cap_id); } else if (cap_length < cap_entry->min_length) { dev_err(&hci->master.dev, "ext_cap 0x%02x has size %d (expecting >= %d)\n", diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index 67dc34163d51b..bc8a011ef1682 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -147,14 +147,14 @@ static int hci_pio_init(struct i3c_hci *hci) hci->io_data = pio; size_val = pio_reg_read(QUEUE_SIZE); - dev_info(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n", - FIELD_GET(CR_QUEUE_SIZE, size_val)); - dev_info(&hci->master.dev, "IBI FIFO = %ld bytes\n", - 4 * FIELD_GET(IBI_STATUS_SIZE, size_val)); - dev_info(&hci->master.dev, "RX data FIFO = %d bytes\n", - 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val))); - dev_info(&hci->master.dev, "TX data FIFO = %d bytes\n", - 4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val))); + dev_dbg(&hci->master.dev, "CMD/RESP FIFO = %ld entries\n", + FIELD_GET(CR_QUEUE_SIZE, size_val)); + dev_dbg(&hci->master.dev, "IBI FIFO = %ld bytes\n", + 4 * FIELD_GET(IBI_STATUS_SIZE, size_val)); + dev_dbg(&hci->master.dev, "RX data FIFO = %d bytes\n", + 4 * (2 << FIELD_GET(RX_DATA_BUFFER_SIZE, size_val))); + dev_dbg(&hci->master.dev, "TX data FIFO = %d bytes\n", + 4 * (2 << FIELD_GET(TX_DATA_BUFFER_SIZE, size_val))); /* * Let's initialize data thresholds to half of the actual FIFO size. From 973fda38b124c35a7982c09d986be3fe969a4de2 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Jan 2026 18:44:12 +0200 Subject: [PATCH 1366/3297] i3c: mipi-i3c-hci: Allow for Multi-Bus Instances [ Upstream commit b8460480f62e16751876a1f367dc14fb62867463 ] Add support for MIPI I3C Host Controllers with the Multi-Bus Instance capability. These controllers can host multiple I3C buses (up to 15) within a single hardware function (e.g., PCIe B/D/F), providing one indepedent HCI register set and corresponding I3C bus controller logic per bus. A separate platform device will represent each instance, but it is necessary to allow for shared resources. Multi-bus instances share the same MMIO address space, but the ranges are not guaranteed to be contiguous. To avoid overlapping mappings, pass base_regs from the parent mapping to child devices. Allow the IRQ to be shared among instances. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260106164416.67074-8-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 093eb8e73c90 ("i3c: mipi-i3c-hci: Preserve RUN bit when aborting DMA ring") Signed-off-by: Sasha Levin --- drivers/i3c/master/mipi-i3c-hci/core.c | 21 +++++++++++++++++---- include/linux/platform_data/mipi-i3c-hci.h | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 include/linux/platform_data/mipi-i3c-hci.h diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 6d8a00486ba62..75e6610cfd812 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "hci.h" @@ -742,15 +743,27 @@ static int i3c_hci_init(struct i3c_hci *hci) static int i3c_hci_probe(struct platform_device *pdev) { + const struct mipi_i3c_hci_platform_data *pdata = pdev->dev.platform_data; struct i3c_hci *hci; int irq, ret; hci = devm_kzalloc(&pdev->dev, sizeof(*hci), GFP_KERNEL); if (!hci) return -ENOMEM; - hci->base_regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(hci->base_regs)) - return PTR_ERR(hci->base_regs); + + /* + * Multi-bus instances share the same MMIO address range, but not + * necessarily in separate contiguous sub-ranges. To avoid overlapping + * mappings, provide base_regs from the parent mapping. + */ + if (pdata) + hci->base_regs = pdata->base_regs; + + if (!hci->base_regs) { + hci->base_regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hci->base_regs)) + return PTR_ERR(hci->base_regs); + } platform_set_drvdata(pdev, hci); /* temporary for dev_printk's, to be replaced in i3c_master_register */ @@ -764,7 +777,7 @@ static int i3c_hci_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler, - 0, NULL, hci); + IRQF_SHARED, NULL, hci); if (ret) return ret; diff --git a/include/linux/platform_data/mipi-i3c-hci.h b/include/linux/platform_data/mipi-i3c-hci.h new file mode 100644 index 0000000000000..ab7395f455f94 --- /dev/null +++ b/include/linux/platform_data/mipi-i3c-hci.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H +#define INCLUDE_PLATFORM_DATA_MIPI_I3C_HCI_H + +#include + +/** + * struct mipi_i3c_hci_platform_data - Platform-dependent data for mipi_i3c_hci + * @base_regs: Register set base address (to support multi-bus instances) + */ +struct mipi_i3c_hci_platform_data { + void __iomem *base_regs; +}; + +#endif From d22ab94261c7b1214675ab6fa3d9300866d55bdd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 13 Jan 2026 09:26:47 +0200 Subject: [PATCH 1367/3297] i3c: mipi-i3c-hci: Switch PIO data allocation to devm_kzalloc() [ Upstream commit 11d17c2855bfc04550557017eae02e92f3eeab1c ] The driver already uses managed resources, so convert the PIO data structure allocation to devm_zalloc(). Remove the manual kfree(). Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260113072702.16268-7-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 093eb8e73c90 ("i3c: mipi-i3c-hci: Preserve RUN bit when aborting DMA ring") Signed-off-by: Sasha Levin --- drivers/i3c/master/mipi-i3c-hci/pio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index bc8a011ef1682..35e6acc958a8e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -140,7 +140,7 @@ static int hci_pio_init(struct i3c_hci *hci) struct hci_pio_data *pio; u32 val, size_val, rx_thresh, tx_thresh, ibi_val; - pio = kzalloc(sizeof(*pio), GFP_KERNEL); + pio = devm_kzalloc(hci->master.dev.parent, sizeof(*pio), GFP_KERNEL); if (!pio) return -ENOMEM; @@ -217,8 +217,6 @@ static void hci_pio_cleanup(struct i3c_hci *hci) BUG_ON(pio->curr_rx); BUG_ON(pio->curr_tx); BUG_ON(pio->curr_resp); - kfree(pio); - hci->io_data = NULL; } } From 45bbc1e1fe626e5c1ae0c20788c8b8759527bb62 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 3 Jun 2026 12:07:39 +0300 Subject: [PATCH 1368/3297] i3c: mipi-i3c-hci: Preserve RUN bit when aborting DMA ring [ Upstream commit 093eb8e73c90aa0c8cfb0421aa85bd70c23488be ] The MIPI I3C HCI specification does not require the DMA ring RUN bit (RUN_STOP) to be cleared when issuing an ABORT. That allows the DMA ring to continue to receive IBIs, although an IBI is anyway not lost because it can be received once the ring restarts if the I3C device has not given up. Note, currently ABORT is only used on a timeout error path so the change has very little effect in practice. In the more common case of a transfer error, the ring (bundle) operation is halted by the controller anyway. Adjust the RING_CONTROL handling to set ABORT without clearing RUN_STOP, bringing the driver into alignment with the specification. Fixes: b795e68bf3073 ("i3c: mipi-i3c-hci: Correct RING_CTRL_ABORT handling in DMA dequeue") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260603090754.16252-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master/mipi-i3c-hci/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 2364d9507d75f..34c21a56a25d9 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -493,7 +493,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, if (ring_status & RING_STATUS_RUNNING) { /* stop the ring */ reinit_completion(&rh->op_done); - rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_ABORT); + rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); wait_for_completion_timeout(&rh->op_done, HZ); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { From eb9db96a5deb324c39f3652eee8703712b646b6d Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 7 Nov 2025 14:29:49 +0100 Subject: [PATCH 1369/3297] i3c: master: add WQ_PERCPU to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit de53ad6ca49e5d73bba72d24b49ec5d40f33ee01 ] Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20251107132949.184944-1-marco.crivellari@suse.com Signed-off-by: Alexandre Belloni Stable-dep-of: 527756cb9ebb ("i3c: master: Make hot-join workqueue freezable to block hot-join during suspend") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 425e36b36009b..a0cb4b3d33b92 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2927,7 +2927,7 @@ int i3c_master_register(struct i3c_master_controller *master, if (ret) goto err_put_dev; - master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent)); + master->wq = alloc_workqueue("%s", WQ_PERCPU, 0, dev_name(parent)); if (!master->wq) { ret = -ENOMEM; goto err_put_dev; From 0d66830f302fd05f2d4e16a5a9e90b08c6f87a4d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:05 +0300 Subject: [PATCH 1370/3297] i3c: master: Make hot-join workqueue freezable to block hot-join during suspend [ Upstream commit 527756cb9ebb277dca12fff00af9fbb3b9ec8cc8 ] The I3C master workqueue (master->wq) is used to defer work that needs thread context and the bus maintenance lock, most notably Hot Join processing (which calls i3c_master_do_daa() to assign dynamic addresses to newly joined devices). Currently the workqueue keeps running across system suspend, which can race with the suspend path: - do_daa() may execute after the controller has been suspended, issuing bus transactions on a powered-down or otherwise unusable controller. - New I3C devices can be enumerated and added to the bus mid-suspend, registering driver model objects at a point where the I3C subsystem and its consumers are not prepared to handle them. Mark the workqueue WQ_FREEZABLE so its workers are frozen for the duration of system suspend/hibernate and resumed afterwards. This naturally defers any pending or newly queued Hot Join work until the system (and the controller) is fully resumed, closing both races without adding explicit suspend/resume synchronization in the master drivers. Update the kerneldoc for struct i3c_master_controller::wq to reflect that the workqueue is freezable. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 2 +- include/linux/i3c/master.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index a0cb4b3d33b92..776b0ec3abe2c 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2927,7 +2927,7 @@ int i3c_master_register(struct i3c_master_controller *master, if (ret) goto err_put_dev; - master->wq = alloc_workqueue("%s", WQ_PERCPU, 0, dev_name(parent)); + master->wq = alloc_workqueue("%s", WQ_PERCPU | WQ_FREEZABLE, 0, dev_name(parent)); if (!master->wq) { ret = -ENOMEM; goto err_put_dev; diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index c52a82dd79a63..cfe0285a5c381 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -509,7 +509,7 @@ struct i3c_master_controller_ops { * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus * @bus: I3C bus exposed by this master - * @wq: workqueue which can be used by master + * @wq: freezable workqueue which can be used by master * drivers if they need to postpone operations that need to take place * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only From de2106d99b87aaf47542fd5f140929fefb00248d Mon Sep 17 00:00:00 2001 From: Adrian Ng Ho Yin Date: Fri, 12 Dec 2025 17:02:55 +0800 Subject: [PATCH 1371/3297] i3c: add sysfs entry and attribute for Device NACK Retry count [ Upstream commit b58f47eb392680d4c6626c8b3b1fcf6412a0a02c ] Document sysfs attribute dev_nack_retry_cnt that controls the number of automatic retries performed by the I3C controller when a target device returns a NACK Add a `dev_nack_retry_count` sysfs attribute to allow reading and updating the device NACK retry count. A new `dev_nack_retry_count` field and an optional `set_dev_nack_retry()` callback are added to i3c_master_controller. The attribute is created only when the callback is implemented. Updates are applied under the I3C bus maintenance lock to ensure safe hardware reconfiguration. Signed-off-by: Adrian Ng Ho Yin Reviewed-by: Frank Li Link: https://patch.msgid.link/3c4b5082bde64024fc383c44bebeef89ad3c7ed3.1765529948.git.adrianhoyin.ng@altera.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- Documentation/ABI/testing/sysfs-bus-i3c | 11 +++++++ drivers/i3c/master.c | 39 +++++++++++++++++++++++++ include/linux/i3c/master.h | 6 ++++ 3 files changed, 56 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-i3c b/Documentation/ABI/testing/sysfs-bus-i3c index c812ab180ff40..c1e048957a010 100644 --- a/Documentation/ABI/testing/sysfs-bus-i3c +++ b/Documentation/ABI/testing/sysfs-bus-i3c @@ -161,3 +161,14 @@ Contact: linux-i3c@vger.kernel.org Description: These directories are just symbolic links to /sys/bus/i3c/devices/i3c-/-. + +What: /sys/bus/i3c/devices/i3c-/-/dev_nack_retry_count +KernelVersion: 6.18 +Contact: linux-i3c@vger.kernel.org +Description: + Expose the dev_nak_retry_count which controls the number of + automatic retries that will be performed by the controller when + the target device returns a NACK response. A value of 0 disables + the automatic retries. Exist only when I3C constroller supports + this retry on nack feature. + diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 776b0ec3abe2c..49b947f951247 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -686,6 +686,39 @@ static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, cha static DEVICE_ATTR_RW(hotjoin); +static ssize_t dev_nack_retry_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); +} + +static ssize_t dev_nack_retry_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i3c_bus *i3cbus = dev_to_i3cbus(dev); + struct i3c_master_controller *master = dev_to_i3cmaster(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; + + i3c_bus_maintenance_lock(i3cbus); + ret = master->ops->set_dev_nack_retry(master, val); + i3c_bus_maintenance_unlock(i3cbus); + + if (ret) + return ret; + + master->dev_nack_retry_count = val; + + return count; +} + +static DEVICE_ATTR_RW(dev_nack_retry_count); + static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_mode.attr, &dev_attr_current_master.attr, @@ -2964,6 +2997,9 @@ int i3c_master_register(struct i3c_master_controller *master, i3c_master_register_new_i3c_devs(master); i3c_bus_normaluse_unlock(&master->bus); + if (master->ops->set_dev_nack_retry) + device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); + return 0; err_del_dev: @@ -2989,6 +3025,9 @@ void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); + if (master->ops->set_dev_nack_retry) + device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); + i3c_master_i2c_adapter_cleanup(master); i3c_master_unregister_i3c_devs(master); i3c_master_bus_cleanup(master); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index cfe0285a5c381..abe0a134465e6 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -462,6 +462,8 @@ struct i3c_bus { * @enable_hotjoin: enable hot join event detect. * @disable_hotjoin: disable hot join event detect. * @set_speed: adjust I3C open drain mode timing. + * @set_dev_nack_retry: configure device NACK retry count for the master + * controller. */ struct i3c_master_controller_ops { int (*bus_init)(struct i3c_master_controller *master); @@ -491,6 +493,8 @@ struct i3c_master_controller_ops { int (*enable_hotjoin)(struct i3c_master_controller *master); int (*disable_hotjoin)(struct i3c_master_controller *master); int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed); + int (*set_dev_nack_retry)(struct i3c_master_controller *master, + unsigned long dev_nack_retry_cnt); }; /** @@ -514,6 +518,7 @@ struct i3c_master_controller_ops { * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only * be done from a sleep-able context + * @dev_nack_retry_count: retry count when slave device nack * * A &struct i3c_master_controller has to be registered to the I3C subsystem * through i3c_master_register(). None of &struct i3c_master_controller fields @@ -534,6 +539,7 @@ struct i3c_master_controller { } boardinfo; struct i3c_bus bus; struct workqueue_struct *wq; + unsigned int dev_nack_retry_count; }; /** From 882ee831366a15a7c4c9e1df6925011fb3a3d36c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 13 Jan 2026 09:26:45 +0200 Subject: [PATCH 1372/3297] i3c: master: Replace WARN_ON() with dev_err() in i3c_dev_free_ibi_locked() [ Upstream commit 471895799c2f46688792e175ced936ffeb6cdf01 ] IBI disable failures are not indicative of a software bug, so using WARN_ON() is not appropriate. Replace these warnings with dev_err(). Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260113072702.16268-5-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 49b947f951247..5d8afd147efa8 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -3157,8 +3157,11 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev) if (!dev->ibi) return; - if (WARN_ON(dev->ibi->enabled)) - WARN_ON(i3c_dev_disable_ibi_locked(dev)); + if (dev->ibi->enabled) { + dev_err(&master->dev, "Freeing IBI that is still enabled\n"); + if (i3c_dev_disable_ibi_locked(dev)) + dev_err(&master->dev, "Failed to disable IBI before freeing\n"); + } master->ops->free_ibi(dev); From b07a318afca1624fabe1713cec74a1bab7e45bd1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 13 Jan 2026 09:27:00 +0200 Subject: [PATCH 1373/3297] i3c: master: Introduce optional Runtime PM support [ Upstream commit 990c149c61ee45da4fb6372e6b2fdd9808414e7a ] Master drivers currently manage Runtime PM individually, but all require runtime resume for bus operations. This can be centralized in common code. Add optional Runtime PM support to ensure the parent device is runtime resumed before bus operations and auto-suspended afterward. Notably, do not call ->bus_cleanup() if runtime resume fails. Master drivers that opt-in to core runtime PM support must take that into account. Also provide an option to allow IBIs and hot-joins while runtime suspended. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260113072702.16268-20-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/device.c | 46 +++++++++++++++++-- drivers/i3c/internals.h | 4 ++ drivers/i3c/master.c | 93 +++++++++++++++++++++++++++++++++++--- include/linux/i3c/master.h | 4 ++ 4 files changed, 138 insertions(+), 9 deletions(-) diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c index 2396545763ff8..6382d70299e2b 100644 --- a/drivers/i3c/device.c +++ b/drivers/i3c/device.c @@ -47,10 +47,16 @@ int i3c_device_do_priv_xfers(struct i3c_device *dev, return -EINVAL; } + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + i3c_bus_normaluse_lock(dev->bus); ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers); i3c_bus_normaluse_unlock(dev->bus); + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_do_priv_xfers); @@ -67,10 +73,16 @@ int i3c_device_do_setdasa(struct i3c_device *dev) { int ret; + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + i3c_bus_normaluse_lock(dev->bus); ret = i3c_dev_setdasa_locked(dev->desc); i3c_bus_normaluse_unlock(dev->bus); + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_do_setdasa); @@ -107,16 +119,27 @@ EXPORT_SYMBOL_GPL(i3c_device_get_info); */ int i3c_device_disable_ibi(struct i3c_device *dev) { - int ret = -ENOENT; + int ret; + + if (i3c_bus_rpm_ibi_allowed(dev->bus)) { + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + } i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_disable_ibi_locked(dev->desc); mutex_unlock(&dev->desc->ibi_lock); + } else { + ret = -ENOENT; } i3c_bus_normaluse_unlock(dev->bus); + if (!ret || i3c_bus_rpm_ibi_allowed(dev->bus)) + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_disable_ibi); @@ -136,16 +159,25 @@ EXPORT_SYMBOL_GPL(i3c_device_disable_ibi); */ int i3c_device_enable_ibi(struct i3c_device *dev) { - int ret = -ENOENT; + int ret; + + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_enable_ibi_locked(dev->desc); mutex_unlock(&dev->desc->ibi_lock); + } else { + ret = -ENOENT; } i3c_bus_normaluse_unlock(dev->bus); + if (ret || i3c_bus_rpm_ibi_allowed(dev->bus)) + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_enable_ibi); @@ -164,19 +196,27 @@ EXPORT_SYMBOL_GPL(i3c_device_enable_ibi); int i3c_device_request_ibi(struct i3c_device *dev, const struct i3c_ibi_setup *req) { - int ret = -ENOENT; + int ret; if (!req->handler || !req->num_slots) return -EINVAL; + ret = i3c_bus_rpm_get(dev->bus); + if (ret) + return ret; + i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_request_ibi_locked(dev->desc, req); mutex_unlock(&dev->desc->ibi_lock); + } else { + ret = -ENOENT; } i3c_bus_normaluse_unlock(dev->bus); + i3c_bus_rpm_put(dev->bus); + return ret; } EXPORT_SYMBOL_GPL(i3c_device_request_ibi); diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h index 79ceaa5f5afd6..a0c76adf7ea33 100644 --- a/drivers/i3c/internals.h +++ b/drivers/i3c/internals.h @@ -11,6 +11,10 @@ #include #include +int __must_check i3c_bus_rpm_get(struct i3c_bus *bus); +void i3c_bus_rpm_put(struct i3c_bus *bus); +bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus); + void i3c_bus_normaluse_lock(struct i3c_bus *bus); void i3c_bus_normaluse_unlock(struct i3c_bus *bus); diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 5d8afd147efa8..f426c4bcb271e 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -106,6 +106,38 @@ static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev) return container_of(dev, struct i3c_master_controller, dev); } +static int __must_check i3c_master_rpm_get(struct i3c_master_controller *master) +{ + int ret = master->rpm_allowed ? pm_runtime_resume_and_get(master->dev.parent) : 0; + + if (ret < 0) { + dev_err(master->dev.parent, "runtime resume failed, error %d\n", ret); + return ret; + } + return 0; +} + +static void i3c_master_rpm_put(struct i3c_master_controller *master) +{ + if (master->rpm_allowed) + pm_runtime_put_autosuspend(master->dev.parent); +} + +int i3c_bus_rpm_get(struct i3c_bus *bus) +{ + return i3c_master_rpm_get(i3c_bus_to_i3c_master(bus)); +} + +void i3c_bus_rpm_put(struct i3c_bus *bus) +{ + i3c_master_rpm_put(i3c_bus_to_i3c_master(bus)); +} + +bool i3c_bus_rpm_ibi_allowed(struct i3c_bus *bus) +{ + return i3c_bus_to_i3c_master(bus)->rpm_ibi_allowed; +} + static const struct device_type i3c_device_type; static struct i3c_bus *dev_to_i3cbus(struct device *dev) @@ -613,6 +645,12 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin) return -EINVAL; + if (enable || master->rpm_ibi_allowed) { + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + } + i3c_bus_normaluse_lock(&master->bus); if (enable) @@ -625,6 +663,9 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) i3c_bus_normaluse_unlock(&master->bus); + if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed) + i3c_master_rpm_put(master); + return ret; } @@ -1747,18 +1788,23 @@ int i3c_master_do_daa(struct i3c_master_controller *master) { int ret; + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(&master->bus); ret = master->ops->do_daa(master); i3c_bus_maintenance_unlock(&master->bus); if (ret) - return ret; + goto out; i3c_bus_normaluse_lock(&master->bus); i3c_master_register_new_i3c_devs(master); i3c_bus_normaluse_unlock(&master->bus); - - return 0; +out: + i3c_master_rpm_put(master); + return ret; } EXPORT_SYMBOL_GPL(i3c_master_do_daa); @@ -2101,8 +2147,17 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) static void i3c_master_bus_cleanup(struct i3c_master_controller *master) { - if (master->ops->bus_cleanup) - master->ops->bus_cleanup(master); + if (master->ops->bus_cleanup) { + int ret = i3c_master_rpm_get(master); + + if (ret) { + dev_err(&master->dev, + "runtime resume error: master bus_cleanup() not done\n"); + } else { + master->ops->bus_cleanup(master); + i3c_master_rpm_put(master); + } + } i3c_master_detach_free_devs(master); } @@ -2457,6 +2512,10 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap, return -EOPNOTSUPP; } + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_normaluse_lock(&master->bus); dev = i3c_master_find_i2c_dev_by_addr(master, addr); if (!dev) @@ -2465,6 +2524,8 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap, ret = master->ops->i2c_xfers(dev, xfers, nxfers); i3c_bus_normaluse_unlock(&master->bus); + i3c_master_rpm_put(master); + return ret ? ret : nxfers; } @@ -2567,6 +2628,10 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action master = i2c_adapter_to_i3c_master(adap); + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(&master->bus); switch (action) { case BUS_NOTIFY_ADD_DEVICE: @@ -2580,6 +2645,8 @@ static int i3c_i2c_notifier_call(struct notifier_block *nb, unsigned long action } i3c_bus_maintenance_unlock(&master->bus); + i3c_master_rpm_put(master); + return ret; } @@ -2917,6 +2984,10 @@ int i3c_master_register(struct i3c_master_controller *master, INIT_LIST_HEAD(&master->boardinfo.i2c); INIT_LIST_HEAD(&master->boardinfo.i3c); + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + device_initialize(&master->dev); master->dev.dma_mask = parent->dma_mask; @@ -3000,6 +3071,8 @@ int i3c_master_register(struct i3c_master_controller *master, if (master->ops->set_dev_nack_retry) device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); + i3c_master_rpm_put(master); + return 0; err_del_dev: @@ -3009,6 +3082,7 @@ int i3c_master_register(struct i3c_master_controller *master, i3c_master_bus_cleanup(master); err_put_dev: + i3c_master_rpm_put(master); put_device(&master->dev); return ret; @@ -3158,8 +3232,15 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev) return; if (dev->ibi->enabled) { + int ret; + dev_err(&master->dev, "Freeing IBI that is still enabled\n"); - if (i3c_dev_disable_ibi_locked(dev)) + ret = i3c_master_rpm_get(master); + if (!ret) { + ret = i3c_dev_disable_ibi_locked(dev); + i3c_master_rpm_put(master); + } + if (ret) dev_err(&master->dev, "Failed to disable IBI before freeing\n"); } diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index abe0a134465e6..bff98590eb2a7 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -509,6 +509,8 @@ struct i3c_master_controller_ops { * @secondary: true if the master is a secondary master * @init_done: true when the bus initialization is done * @hotjoin: true if the master support hotjoin + * @rpm_allowed: true if Runtime PM allowed + * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended * @boardinfo.i3c: list of I3C boardinfo objects * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus @@ -533,6 +535,8 @@ struct i3c_master_controller { unsigned int secondary : 1; unsigned int init_done : 1; unsigned int hotjoin: 1; + unsigned int rpm_allowed: 1; + unsigned int rpm_ibi_allowed: 1; struct { struct list_head i3c; struct list_head i2c; From fd32e8d4a2933143b7bcf5d13f5bc49fc01afbbc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Jan 2026 08:33:23 +0200 Subject: [PATCH 1374/3297] i3c: master: Add i3c_master_do_daa_ext() for post-hibernation address recovery [ Upstream commit c481ef12e713fb7c292d04f53b3532ac0804ab3d ] After system hibernation, I3C Dynamic Addresses may be reassigned at boot and no longer match the values recorded before suspend. Introduce i3c_master_do_daa_ext() to handle this situation. The restore procedure is straightforward: issue a Reset Dynamic Address Assignment (RSTDAA), then run the standard DAA sequence. The existing DAA logic already supports detecting and updating devices whose dynamic addresses differ from previously known values. Refactor the DAA path by introducing a shared helper used by both the normal i3c_master_do_daa() path and the new extended restore function, and correct the kernel-doc in the process. Export i3c_master_do_daa_ext() so that master drivers can invoke it from their PM restore callbacks. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260123063325.8210-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 49 +++++++++++++++++++++++++++++--------- include/linux/i3c/master.h | 1 + 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index f426c4bcb271e..0430e5898dae4 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1770,22 +1770,24 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) } /** - * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment) - * @master: master doing the DAA + * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) + * @master: controller + * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA) * - * This function is instantiating an I3C device object and adding it to the - * I3C device list. All device information are automatically retrieved using - * standard CCC commands. - * - * The I3C device object is returned in case the master wants to attach - * private data to it using i3c_dev_set_master_data(). + * Perform Dynamic Address Assignment with optional support for System + * Hibernation (@rstdaa is true). * - * This function must be called with the bus lock held in write mode. + * After System Hibernation, Dynamic Addresses can have been reassigned at boot + * time to different values. A simple strategy is followed to handle that. + * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA + * procedure which has provision for reassigning addresses that differ from the + * previously recorded addresses. * * Return: a 0 in case of success, an negative error code otherwise. */ -int i3c_master_do_daa(struct i3c_master_controller *master) +int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) { + int rstret = 0; int ret; ret = i3c_master_rpm_get(master); @@ -1793,7 +1795,15 @@ int i3c_master_do_daa(struct i3c_master_controller *master) return ret; i3c_bus_maintenance_lock(&master->bus); + + if (rstdaa) { + rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); + if (rstret == I3C_ERROR_M2) + rstret = 0; + } + ret = master->ops->do_daa(master); + i3c_bus_maintenance_unlock(&master->bus); if (ret) @@ -1804,7 +1814,24 @@ int i3c_master_do_daa(struct i3c_master_controller *master) i3c_bus_normaluse_unlock(&master->bus); out: i3c_master_rpm_put(master); - return ret; + + return rstret ?: ret; +} +EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext); + +/** + * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment) + * @master: master doing the DAA + * + * This function instantiates I3C device objects and adds them to the + * I3C device list. All device information is automatically retrieved using + * standard CCC commands. + * + * Return: a 0 in case of success, an negative error code otherwise. + */ +int i3c_master_do_daa(struct i3c_master_controller *master) +{ + return i3c_master_do_daa_ext(master, false); } EXPORT_SYMBOL_GPL(i3c_master_do_daa); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index bff98590eb2a7..2e39a0d443a1e 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -605,6 +605,7 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master, int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr); int i3c_master_do_daa(struct i3c_master_controller *master); +int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa); struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr, size_t len, bool force_bounce, enum dma_data_direction dir); From 0bd450d40f8745b8ac1015b2dccfa6a6cc5eb41a Mon Sep 17 00:00:00 2001 From: Jorge Marques Date: Mon, 23 Mar 2026 17:11:29 +0100 Subject: [PATCH 1375/3297] i3c: master: Move rstdaa error suppression [ Upstream commit 19a1b61fa623748f37f467e7813c58a2a792b90c ] Prepare to fix improper Mx positive error propagation in later commits by handling Mx error codes where the i3c_ccc_cmd command is allocated. Two of the four i3c_master_rstdaa_locked() are error paths that already suppressed the return value, the remaining two are changed to handle the I3C_ERROR_M2 Mx error code inside i3c_master_rstdaa_locked(), checking cmd->err directly. Reviewed-by: Frank Li Reviewed-by: Adrian Hunter Signed-off-by: Jorge Marques Link: https://patch.msgid.link/20260323-ad4062-positive-error-fix-v3-1-30bdc68004be@analog.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 0430e5898dae4..6cbd0ab0520f5 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1018,6 +1018,10 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, ret = i3c_master_send_ccc_cmd_locked(master, &cmd); i3c_ccc_cmd_dest_cleanup(&dest); + /* No active devices on the bus. */ + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + return ret; } @@ -1796,11 +1800,8 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) i3c_bus_maintenance_lock(&master->bus); - if (rstdaa) { + if (rstdaa) rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - if (rstret == I3C_ERROR_M2) - rstret = 0; - } ret = master->ops->do_daa(master); @@ -2096,7 +2097,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) * (assigned by the bootloader for example). */ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - if (ret && ret != I3C_ERROR_M2) + if (ret) goto err_bus_cleanup; if (master->ops->set_speed) { From 57490b302b984e6c2b6dcc16e2d2af6f52a973ee Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:07 +0300 Subject: [PATCH 1376/3297] i3c: master: Consolidate Hot-Join DAA work in the core [ Upstream commit 828c6130235db8144f4810b329b61390dc82719b ] Three master drivers (dw-i3c-master, i3c-master-cdns, svc-i3c-master) each carry an essentially identical Hot-Join handler: a struct work_struct embedded in their private state, a work function that just calls i3c_master_do_daa() on the embedded i3c_master_controller, plus matching INIT_WORK()/cancel_work_sync() boilerplate in probe/remove (and shutdown for dw-i3c). The IBI/ISR paths then queue that work onto master->wq, which already lives in the core. Move this pattern into the I3C core: - Add struct work_struct hj_work to struct i3c_master_controller and initialise it in i3c_master_register() with a core-provided handler i3c_master_hj_work_fn() that performs i3c_master_do_daa(). - Cancel the work in i3c_master_unregister() so all controllers get correct teardown ordering against the workqueue for free. - Export i3c_master_queue_hotjoin() as the single entry point drivers call from their Hot-Join IBI handler. Convert the three existing users to the new API: drop their private hj_work fields, work functions, INIT_WORK() and cancel_work_sync() calls, and replace the queue_work(master->wq, &drv->hj_work) call sites with i3c_master_queue_hotjoin(&drv->base). The dw-i3c shutdown path still needs to flush pending Hot-Join work before tearing down the hardware, so it is updated to cancel master->base.hj_work directly. No functional change intended: the work is still queued on the same master->wq, runs the same i3c_master_do_daa(), and is cancelled at controller teardown. Future Hot-Join improvements now only need to be made in one place. Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 21 +++++++++++++++++++++ drivers/i3c/master/dw-i3c-master.c | 15 ++------------- drivers/i3c/master/dw-i3c-master.h | 2 -- drivers/i3c/master/i3c-master-cdns.c | 14 +------------- drivers/i3c/master/svc-i3c-master.c | 14 +------------- include/linux/i3c/master.h | 4 ++++ 6 files changed, 29 insertions(+), 41 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 6cbd0ab0520f5..4ca2d77fb552b 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -635,6 +635,13 @@ static ssize_t i2c_scl_frequency_show(struct device *dev, } static DEVICE_ATTR_RO(i2c_scl_frequency); +static void i3c_master_hj_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), hj_work); + + i3c_master_do_daa(master); +} + static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) { int ret; @@ -713,6 +720,18 @@ int i3c_master_disable_hotjoin(struct i3c_master_controller *master) } EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin); +/** + * i3c_master_queue_hotjoin - Queue DAA processing after a Hot-Join event + * @master: I3C master object + * + * Queue the hot-join worker on the master's workqueue. + */ +void i3c_master_queue_hotjoin(struct i3c_master_controller *master) +{ + queue_work(master->wq, &master->hj_work); +} +EXPORT_SYMBOL_GPL(i3c_master_queue_hotjoin); + static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *i3cbus = dev_to_i3cbus(dev); @@ -3064,6 +3083,7 @@ int i3c_master_register(struct i3c_master_controller *master, ret = -ENOMEM; goto err_put_dev; } + INIT_WORK(&master->hj_work, i3c_master_hj_work_fn); ret = i3c_master_bus_init(master); if (ret) @@ -3126,6 +3146,7 @@ EXPORT_SYMBOL_GPL(i3c_master_register); void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); + cancel_work_sync(&master->hj_work); if (master->ops->set_dev_nack_retry) device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 585f320119741..675c257ebe2c2 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1449,7 +1449,7 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master) if (IBI_TYPE_SIRQ(reg)) { dw_i3c_master_handle_ibi_sir(master, reg); } else if (IBI_TYPE_HJ(reg)) { - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); } else { len = IBI_QUEUE_STATUS_DATA_LEN(reg); dev_info(&master->base.dev, @@ -1523,14 +1523,6 @@ static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = { .set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop, }; -static void dw_i3c_hj_work(struct work_struct *work) -{ - struct dw_i3c_master *master = - container_of(work, typeof(*master), hj_work); - - i3c_master_do_daa(&master->base); -} - int dw_i3c_common_probe(struct dw_i3c_master *master, struct platform_device *pdev) { @@ -1592,8 +1584,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->quirks = (unsigned long)device_get_match_data(&pdev->dev); - INIT_WORK(&master->hj_work, dw_i3c_hj_work); - device_set_of_node_from_dev(&master->base.i2c.dev, &pdev->dev); ret = i3c_master_register(&master->base, &pdev->dev, &dw_mipi_i3c_ops, false); @@ -1613,7 +1603,6 @@ EXPORT_SYMBOL_GPL(dw_i3c_common_probe); void dw_i3c_common_remove(struct dw_i3c_master *master) { - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); pm_runtime_disable(master->dev); @@ -1749,7 +1738,7 @@ static void dw_i3c_shutdown(struct platform_device *pdev) return; } - cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->base.hj_work); /* Disable interrupts */ writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index c5cb695c16ab8..2f029bd362326 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -68,8 +68,6 @@ struct dw_i3c_master { /* platform-specific data */ const struct dw_i3c_platform_ops *platform_ops; - - struct work_struct hj_work; }; struct dw_i3c_platform_ops { diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 97b151564d3d3..46a24b8607dfa 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -398,7 +398,6 @@ struct cdns_i3c_data { }; struct cdns_i3c_master { - struct work_struct hj_work; struct i3c_master_controller base; u32 free_rr_slots; unsigned int maxdevs; @@ -1357,7 +1356,7 @@ static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master) case IBIR_TYPE_HJ: WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR)); - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case IBIR_TYPE_MR: @@ -1528,15 +1527,6 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = { .recycle_ibi_slot = cdns_i3c_master_recycle_ibi_slot, }; -static void cdns_i3c_master_hj(struct work_struct *work) -{ - struct cdns_i3c_master *master = container_of(work, - struct cdns_i3c_master, - hj_work); - - i3c_master_do_daa(&master->base); -} - static struct cdns_i3c_data cdns_i3c_devdata = { .thd_delay_ns = 10, }; @@ -1584,7 +1574,6 @@ static int cdns_i3c_master_probe(struct platform_device *pdev) spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); - INIT_WORK(&master->hj_work, cdns_i3c_master_hj); writel(0xffffffff, master->regs + MST_IDR); writel(0xffffffff, master->regs + SLV_IDR); ret = devm_request_irq(&pdev->dev, irq, cdns_i3c_master_interrupt, 0, @@ -1627,7 +1616,6 @@ static void cdns_i3c_master_remove(struct platform_device *pdev) { struct cdns_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); } diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 3d9f905d56039..d33f4c7654974 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -201,7 +201,6 @@ struct svc_i3c_drvdata { * @free_slots: Bit array of available slots * @addrs: Array containing the dynamic addresses of each attached device * @descs: Array of descriptors, one per attached device - * @hj_work: Hot-join work * @irq: Main interrupt * @num_clks: I3C clock number * @fclk: Fast clock (bus) @@ -228,7 +227,6 @@ struct svc_i3c_master { u32 free_slots; u8 addrs[SVC_I3C_MAX_DEVS]; struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS]; - struct work_struct hj_work; int irq; int num_clks; struct clk *fclk; @@ -359,14 +357,6 @@ to_svc_i3c_master(struct i3c_master_controller *master) return container_of(master, struct svc_i3c_master, base); } -static void svc_i3c_master_hj_work(struct work_struct *work) -{ - struct svc_i3c_master *master; - - master = container_of(work, struct svc_i3c_master, hj_work); - i3c_master_do_daa(&master->base); -} - static struct i3c_dev_desc * svc_i3c_master_dev_from_addr(struct svc_i3c_master *master, unsigned int ibiaddr) @@ -614,7 +604,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: svc_i3c_master_emit_stop(master); if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN)) - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: svc_i3c_master_emit_stop(master); @@ -1949,7 +1939,6 @@ static int svc_i3c_master_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "can't enable I3C clocks\n"); - INIT_WORK(&master->hj_work, svc_i3c_master_hj_work); mutex_init(&master->lock); ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler, @@ -2008,7 +1997,6 @@ static void svc_i3c_master_remove(struct platform_device *pdev) { struct svc_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); pm_runtime_dont_use_autosuspend(&pdev->dev); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 2e39a0d443a1e..fc146bbb304fa 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -520,6 +520,8 @@ struct i3c_master_controller_ops { * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only * be done from a sleep-able context + * @hj_work: work item used to run DAA after a Hot-Join event is detected. + * Queued to @wq by i3c_master_queue_hotjoin() * @dev_nack_retry_count: retry count when slave device nack * * A &struct i3c_master_controller has to be registered to the I3C subsystem @@ -543,6 +545,7 @@ struct i3c_master_controller { } boardinfo; struct i3c_bus bus; struct workqueue_struct *wq; + struct work_struct hj_work; unsigned int dev_nack_retry_count; }; @@ -623,6 +626,7 @@ int i3c_master_register(struct i3c_master_controller *master, void i3c_master_unregister(struct i3c_master_controller *master); int i3c_master_enable_hotjoin(struct i3c_master_controller *master); int i3c_master_disable_hotjoin(struct i3c_master_controller *master); +void i3c_master_queue_hotjoin(struct i3c_master_controller *master); /** * i3c_dev_get_master_data() - get master private data attached to an I3C From 3891c061341fb73b3dd562392e7fb022596f29e7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:08 +0300 Subject: [PATCH 1377/3297] i3c: master: Ensure Hot-Join operations are stopped on shutdown [ Upstream commit 8323e783dc3904839e64cb08cfcc7571ef9212c4 ] System shutdown invokes each device's bus shutdown callback to quiesce hardware, but the I3C bus type does not currently implement one. As a result, on shutdown the controller's Hot-Join work and any in-flight i3c_master_do_daa() can keep running (or be newly triggered) while the rest of the system is being torn down. A similar window exists at i3c_master_unregister() time: cancel_work_sync() on hj_work prevents queued work from completing, but does not stop a fresh Hot-Join IBI from re-queueing the worker, nor a concurrent sysfs writer from toggling Hot-Join via i3c_set_hotjoin(). Introduce a single "shutting down" gate in the I3C core, set under the bus maintenance lock so it is observed by any in-progress DAA path before pending work is cancelled. Install an i3c_bus_type shutdown callback that engages this gate for master devices during system shutdown, and use the same gate in i3c_master_unregister() so both paths get identical guarantees. Once the gate is engaged, the Hot-Join worker, i3c_master_do_daa_ext() and i3c_set_hotjoin() all bail out cleanly, so Hot-Join IBIs that race with shutdown become no-ops, direct DAA callers see -ENODEV, and sysfs writers can no longer re-enable Hot-Join through ops->enable_hotjoin() while the controller is going away. No functional change for the steady-state runtime path; the new checks only take effect once the controller has been marked as shutting down. Note, this patch depends on patch "i3c: master: Consolidate Hot-Join DAA work in the core". Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-5-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: 3f79dac3ea1c ("i3c: master: Defer new-device registration out of DAA caller context") Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 52 +++++++++++++++++++++++++++----------- include/linux/i3c/master.h | 2 ++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 4ca2d77fb552b..e537e89ffc2db 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -370,14 +370,6 @@ static void i3c_device_remove(struct device *dev) i3c_device_free_ibi(i3cdev); } -const struct bus_type i3c_bus_type = { - .name = "i3c", - .match = i3c_device_match, - .probe = i3c_device_probe, - .remove = i3c_device_remove, -}; -EXPORT_SYMBOL_GPL(i3c_bus_type); - static enum i3c_addr_slot_status i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask) { @@ -639,7 +631,8 @@ static void i3c_master_hj_work_fn(struct work_struct *work) { struct i3c_master_controller *master = container_of(work, typeof(*master), hj_work); - i3c_master_do_daa(master); + if (!master->shutting_down) + i3c_master_do_daa(master); } static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) @@ -660,7 +653,9 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) i3c_bus_normaluse_lock(&master->bus); - if (enable) + if (master->shutting_down) + ret = -ENODEV; + else if (enable) ret = master->ops->enable_hotjoin(master); else ret = master->ops->disable_hotjoin(master); @@ -812,6 +807,30 @@ static const struct device_type i3c_masterdev_type = { .groups = i3c_masterdev_groups, }; +static void i3c_master_shutdown(struct i3c_master_controller *master) +{ + i3c_bus_maintenance_lock(&master->bus); + master->shutting_down = true; + i3c_bus_maintenance_unlock(&master->bus); + + cancel_work_sync(&master->hj_work); +} + +static void i3c_device_shutdown(struct device *dev) +{ + if (dev->type == &i3c_masterdev_type) + i3c_master_shutdown(dev_to_i3cmaster(dev)); +} + +const struct bus_type i3c_bus_type = { + .name = "i3c", + .match = i3c_device_match, + .probe = i3c_device_probe, + .remove = i3c_device_remove, + .shutdown = i3c_device_shutdown, +}; +EXPORT_SYMBOL_GPL(i3c_bus_type); + static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate) { @@ -1819,10 +1838,13 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) i3c_bus_maintenance_lock(&master->bus); - if (rstdaa) - rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - - ret = master->ops->do_daa(master); + if (master->shutting_down) { + ret = -ENODEV; + } else { + if (rstdaa) + rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); + ret = master->ops->do_daa(master); + } i3c_bus_maintenance_unlock(&master->bus); @@ -3146,7 +3168,7 @@ EXPORT_SYMBOL_GPL(i3c_master_register); void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); - cancel_work_sync(&master->hj_work); + i3c_master_shutdown(master); if (master->ops->set_dev_nack_retry) device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index fc146bbb304fa..056f4f25ccfbc 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -511,6 +511,7 @@ struct i3c_master_controller_ops { * @hotjoin: true if the master support hotjoin * @rpm_allowed: true if Runtime PM allowed * @rpm_ibi_allowed: true if IBI and Hot-Join allowed while runtime suspended + * @shutting_down: set to true when master begins shutdown or unregister * @boardinfo.i3c: list of I3C boardinfo objects * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus @@ -539,6 +540,7 @@ struct i3c_master_controller { unsigned int hotjoin: 1; unsigned int rpm_allowed: 1; unsigned int rpm_ibi_allowed: 1; + bool shutting_down; struct { struct list_head i3c; struct list_head i2c; From c4f2afcdc547058806ecf3d3b0d9dca58026cad1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 8 Jun 2026 08:43:10 +0300 Subject: [PATCH 1378/3297] i3c: master: Defer new-device registration out of DAA caller context [ Upstream commit 3f79dac3ea1c30516fcc791770af034387c7f917 ] Master drivers may invoke i3c_master_do_daa_ext() during resume to re-run Dynamic Address Assignment. As well as assigning addresses to any newly arrived devices, this restores the dynamic address of devices that lost it across system suspend, so it has to run as part of the controller's resume path. A side effect of i3c_master_do_daa_ext() today is that it also registers any newly discovered I3C devices with the driver model inline, via i3c_master_register_new_i3c_devs(). Doing that from the resume path is problematic: a hot-join-capable device may join the bus during this same DAA, and registering it immediately would push driver model work (probing, sysfs, etc.) into the controller's resume context, where the rest of the system is not yet fully resumed and the controller driver is still partway through its own resume sequence. Decouple discovery from registration: add a reg_work work item to struct i3c_master_controller and have i3c_master_do_daa_ext() queue it on master->wq (the freezable workqueue) instead of calling i3c_master_register_new_i3c_devs() directly. The worker performs the registration only when the controller is not shutting_down, and is cancelled alongside hj_work in i3c_master_shutdown(). Because wq is freezable, any newly observed devices end up being registered after the system has finished resuming. i3c_master_register() also routes its initial post-bus-init registration through reg_work, using flush_work() to keep probe-time behavior synchronous. This keeps a single registration code path and ensures the worker is the only writer of desc->dev. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260608054312.10604-7-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 27 ++++++++++++++++++++------- include/linux/i3c/master.h | 6 ++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index e537e89ffc2db..9cb1376e74a95 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -814,6 +814,7 @@ static void i3c_master_shutdown(struct i3c_master_controller *master) i3c_bus_maintenance_unlock(&master->bus); cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->reg_work); } static void i3c_device_shutdown(struct device *dev) @@ -1811,6 +1812,16 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) } } +static void i3c_master_reg_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), reg_work); + + i3c_bus_normaluse_lock(&master->bus); + if (!master->shutting_down) + i3c_master_register_new_i3c_devs(master); + i3c_bus_normaluse_unlock(&master->bus); +} + /** * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) * @master: controller @@ -1851,9 +1862,7 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) if (ret) goto out; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); out: i3c_master_rpm_put(master); @@ -3106,6 +3115,7 @@ int i3c_master_register(struct i3c_master_controller *master, goto err_put_dev; } INIT_WORK(&master->hj_work, i3c_master_hj_work_fn); + INIT_WORK(&master->reg_work, i3c_master_reg_work_fn); ret = i3c_master_bus_init(master); if (ret) @@ -3131,12 +3141,15 @@ int i3c_master_register(struct i3c_master_controller *master, /* * We're done initializing the bus and the controller, we can now - * register I3C devices discovered during the initial DAA. + * register I3C devices discovered during the initial DAA. Device + * registration is done via reg_work because that keeps a single + * registration code path and ensures the worker is the only writer + * of desc->dev. Flush the work to preserve synchronous probe-time + * behavior. */ master->init_done = true; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); + flush_work(&master->reg_work); if (master->ops->set_dev_nack_retry) device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 056f4f25ccfbc..1aa1efd835b86 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -523,6 +523,11 @@ struct i3c_master_controller_ops { * be done from a sleep-able context * @hj_work: work item used to run DAA after a Hot-Join event is detected. * Queued to @wq by i3c_master_queue_hotjoin() + * @reg_work: work item used to register newly discovered I3C devices with + * the driver model. Queued to @wq by i3c_master_do_daa_ext() so + * that device registration is deferred out of the DAA caller's + * context (notably the resume path), and is skipped if the + * controller is shutting down * @dev_nack_retry_count: retry count when slave device nack * * A &struct i3c_master_controller has to be registered to the I3C subsystem @@ -548,6 +553,7 @@ struct i3c_master_controller { struct i3c_bus bus; struct workqueue_struct *wq; struct work_struct hj_work; + struct work_struct reg_work; unsigned int dev_nack_retry_count; }; From 38d3d33bf42c250053c4a06fc1b3a714f3ad0a74 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 12 Jun 2026 11:01:03 +0300 Subject: [PATCH 1379/3297] i3c: master: Prevent reuse of dynamic address on device add failure [ Upstream commit b3ba8383da4d0cff15810e32ea785eceb0a80813 ] i3c_master_add_i3c_dev_locked() is called after a device has already been assigned a dynamic address. If the function fails, the address remains marked as free and may be reallocated to another device, leading to address conflicts on the bus. Ensure the address is not marked as free on failure, by updating the address slot state to prevent the address from being re-used. Emit an error message to inform of the failure. Opportunistically remove the !master check because it is impossible. Note, directly resetting the device's dynamic address is no longer an option, since Direct RSTDAA was deprecated from I3C starting from version 1.1 and v1.1 (or later) target devices are meant to NACK it. Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260612080107.11606-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 9cb1376e74a95..f0ff305642bf0 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2295,12 +2295,11 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, bool enable_ibi = false; int ret; - if (!master) - return -EINVAL; - newdev = i3c_master_alloc_i3c_dev(master, &info); - if (IS_ERR(newdev)) - return PTR_ERR(newdev); + if (IS_ERR(newdev)) { + ret = PTR_ERR(newdev); + goto err_prevent_addr_reuse; + } ret = i3c_master_attach_i3c_dev(master, newdev); if (ret) @@ -2422,6 +2421,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, err_free_dev: i3c_master_free_i3c_dev(newdev); +err_prevent_addr_reuse: + /* + * Although the device has not been added, the address has been + * assigned. Prevent the address from being used again. + */ + if (i3c_bus_get_addr_slot_status(&master->bus, addr) == I3C_ADDR_SLOT_FREE) + i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV); + + dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret); + return ret; } EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); From 85873b1bd3664af5cba608a014cd2df930d77f3e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 14 Jun 2026 16:16:59 -0700 Subject: [PATCH 1380/3297] apparmor: fix label can not be immediately before a declaration [ Upstream commit 1ed40bd525c00d22af666016af9aef7167f8085f ] Fix error reported by kernel test robot security/apparmor/policy.c:1381:2: error: a label can only be part of a statement and a declaration is not a statement All errors (new ones prefixed by >>): security/apparmor/policy.c: In function 'aa_replace_profiles': >> security/apparmor/policy.c:1381:2: error: a label can only be part of a statement and a declaration is not a statement ssize_t udata_sz = udata->size; ^~~~~ Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606150525.npax8WiH-lkp@intel.com/ Fixes: 7b42f95813dc9 ("apparmor: fix potential UAF in aa_replace_profiles") Signed-off-by: John Johansen Signed-off-by: Sasha Levin --- security/apparmor/policy.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f44cd6ac9b7b4..b0203b30db606 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1373,9 +1373,10 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_unlock(&ns->lock); out: + aa_put_ns(ns); + ssize_t udata_sz = udata->size; - aa_put_ns(ns); aa_put_profile_loaddata(udata); kfree(ns_name); From 17955f1995bf9985c5b27901730a270239e38344 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 11 Jun 2026 07:51:40 +0200 Subject: [PATCH 1381/3297] accel/ivpu: fix HWS command queue leak on registration failure [ Upstream commit e7ab91e2bf01b024691d6ce488546533943e7a6b ] A command queue is considered valid and usable by the driver only when it has a doorbell ID assigned (db_id != 0), meaning both the FW cmdq creation and doorbell registration completed successfully. However, when either ivpu_register_db() or set_context_sched_properties() fails after ivpu_hws_cmdq_init() has already created the cmdq in FW, the command queue is left registered in FW while the driver treats it as uninitialized (db_id remains 0). On the next submission attempt the driver tries to register the same cmdq again, which fails because FW already has an entry for it. Fix by calling ivpu_jsm_hws_destroy_cmdq() on error paths to properly unwind FW state and allow subsequent registration attempts to succeed. Fixes: 465a3914b254 ("accel/ivpu: Add API for command queue create/destroy/submit") Reviewed-by: Andrzej Kacprowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260611055140.948684-1-karol.wachowski@linux.intel.com Signed-off-by: Sasha Levin --- drivers/accel/ivpu/ivpu_job.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 1e4caf5726474..dcae72a79e72c 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -195,9 +195,9 @@ static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->id, priority); if (ret) - return ret; + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) @@ -255,10 +255,10 @@ static int ivpu_cmdq_register(struct ivpu_file_priv *file_priv, struct ivpu_cmdq } ret = ivpu_register_db(file_priv, cmdq); - if (ret) - return ret; + if (ret && vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_cmdq_unregister(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) From f231387f3d2bbce38b99d49c54ec6a8128d0081b Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 13:58:50 +0800 Subject: [PATCH 1382/3297] sparc: led: avoid trimming a newline from empty writes [ Upstream commit 7eb475e8a738ee6fd1260aa59ddccb610fdd4300 ] led_proc_write() duplicates up to LED_MAX_LENGTH bytes with memdup_user_nul() and then unconditionally inspects buf[count - 1] to strip a trailing newline. A zero-length write therefore reads one byte before the duplicated buffer. The previous version rejected empty writes, but empty input already falls through to the existing default case and turns the LED off like any other unrecognized string. Preserve that behavior and only skip the newline trim when there is no input byte to inspect. Fixes: ee1858d3122d ("[SPARC]: Add sun4m LED driver.") Suggested-by: Andreas Larsson Signed-off-by: Pengpeng Hou Signed-off-by: Andreas Larsson Signed-off-by: Sasha Levin --- arch/sparc/kernel/led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c index f4fb82b019bb9..9b53ac1fe533d 100644 --- a/arch/sparc/kernel/led.c +++ b/arch/sparc/kernel/led.c @@ -78,7 +78,7 @@ static ssize_t led_proc_write(struct file *file, const char __user *buffer, return PTR_ERR(buf); /* work around \n when echo'ing into proc */ - if (buf[count - 1] == '\n') + if (count > 0 && buf[count - 1] == '\n') buf[count - 1] = '\0'; /* before we change anything we want to stop any running timers, From ca3393e258f632b92f4f81903a965af1788562e7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:28:43 -0300 Subject: [PATCH 1383/3297] perf symbols: Fix bswap copy-paste error for 32-bit ELF p_filesz [ Upstream commit 081b387c7397498c583b1ba7c2fdaf4c6da6b538 ] filename__read_build_id() byte-swaps 32-bit ELF program headers on cross-endian files, but line 178 passes p_offset to bswap_32() instead of p_filesz: hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); This clobbers p_filesz with the already-swapped p_offset value. The 64-bit path on line 182 is correct and swaps p_filesz from p_filesz. The consequence is that the PT_NOTE segment read uses the wrong size, which can cause either a short read (missing the build-id) or an oversized read (reading past the segment into adjacent data). Fix by swapping the correct field. Reported-by: sashiko-bot Fixes: fef8f648bb47726d ("perf symbol: Fix use-after-free in filename__read_build_id") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-minimal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 6080d85047e33..39b806651b925 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -168,7 +168,7 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo if (elf32) { hdrs.phdr32[i].p_type = bswap_32(hdrs.phdr32[i].p_type); hdrs.phdr32[i].p_offset = bswap_32(hdrs.phdr32[i].p_offset); - hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); + hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_filesz); } else { hdrs.phdr64[i].p_type = bswap_32(hdrs.phdr64[i].p_type); hdrs.phdr64[i].p_offset = bswap_64(hdrs.phdr64[i].p_offset); From 137eabe3c18ff5e12453e98c7b0fd0246288bfa2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:29:34 -0300 Subject: [PATCH 1384/3297] perf symbols: Validate p_filesz before use in filename__read_build_id() [ Upstream commit 2a3716544359d4312c81b0fa909a13301186da17 ] filename__read_build_id() stores ELF p_filesz in a ssize_t variable. A crafted 32-bit ELF with p_filesz = 0xFFFFFFFF produces ssize_t value -1. The comparison `p_filesz > buf_size` evaluates false because signed -1 is less than any non-negative buf_size, so the realloc is skipped and buf remains NULL. The subsequent read(fd, NULL, -1) returns -1, which equals p_filesz, passing the error check. read_build_id() then dereferences the NULL buffer. Add an explicit check for p_filesz <= 0 before using the value, catching both zero-length and sign-wrapped negative sizes from crafted ELF files. Reported-by: sashiko-bot Fixes: ba0b7081f7a521d7 ("perf symbol-minimal: Fix ehdr reading in filename__read_build_id") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-minimal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 39b806651b925..aa931a0015c10 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -179,6 +179,9 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo continue; p_filesz = elf32 ? hdrs.phdr32[i].p_filesz : hdrs.phdr64[i].p_filesz; + /* ssize_t can go negative with crafted ELF p_filesz values */ + if (p_filesz <= 0) + continue; if (p_filesz > buf_size) { void *tmp; From 2c19e40753ec1f915ce58f56b7347d30b6562567 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:32:22 -0300 Subject: [PATCH 1385/3297] perf symbols: Break infinite loop on zero-filled notes in sysfs__read_build_id() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 063c647b24f640657d6d9e2e90d620ea3ee19ae6 ] sysfs__read_build_id() iterates ELF note headers from sysfs files in a while(1) loop. If the file contains a zero-filled note header (both n_namesz and n_descsz are 0), the code computes n = namesz + descsz = 0 and calls read(fd, bf, 0). read() with count 0 returns 0, which matches the expected (ssize_t)n value, so the error check passes and the loop repeats — reading the same zero bytes and spinning forever. This can happen with corrupted or zero-padded sysfs pseudo-files. Add a check for n == 0 before the read, since no valid ELF note has both name and description of zero length. Reported-by: sashiko-bot Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") Reviewed-by: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/symbol-elf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 4cc9e91a2d997..220689e8fb9b3 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -991,6 +991,9 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) } else { n = namesz + descsz; } + /* no valid note has both namesz and descsz zero */ + if (n == 0) + break; if (read(fd, bf, n) != (ssize_t)n) break; } From 3ae7947101b977f9fb0b8aece1c5cdd8c0f75360 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 24 Nov 2025 10:59:08 +0000 Subject: [PATCH 1386/3297] perf tools: Don't read build-ids from non-regular files [ Upstream commit 834ebb5678d75d844f5d4f44ede78724d8c96630 ] Simplify the build ID reading code by removing the non-blocking option. Having to pass the correct option to this function was fragile and a mistake would result in a hang, see the linked fix. Furthermore, compressed files are always opened blocking anyway, ignoring the non-blocking option. We also don't expect to read build IDs from non-regular files. The only hits to this function that are non-regular are devices that won't be elf files with build IDs, for example "/dev/dri/renderD129". Now instead of opening these as non-blocking and failing to read, we skip them. Even if something like a pipe or character device did have a build ID, I don't think it would have worked because you need to call read() in a loop, check for -EAGAIN and handle timeouts to make non-blocking reads work. Link: https://lore.kernel.org/linux-perf-users/20251022-james-perf-fix-dso-block-v1-1-c4faab150546@linaro.org/ Signed-off-by: James Clark Signed-off-by: Namhyung Kim Stable-dep-of: f973e52a9977 ("perf dso: Fix heap overflow in dso__get_filename() on decompressed path") Signed-off-by: Sasha Levin --- tools/perf/bench/inject-buildid.c | 2 +- tools/perf/builtin-buildid-cache.c | 8 ++++---- tools/perf/builtin-inject.c | 4 ++-- tools/perf/tests/pe-file-parsing.c | 4 ++-- tools/perf/tests/sdt.c | 2 +- tools/perf/util/build-id.c | 4 ++-- tools/perf/util/debuginfo.c | 2 +- tools/perf/util/dsos.c | 4 ++-- tools/perf/util/libbfd.c | 9 +++++++-- tools/perf/util/libbfd.h | 5 ++--- tools/perf/util/symbol-elf.c | 13 +++++++------ tools/perf/util/symbol-minimal.c | 11 ++++++++--- tools/perf/util/symbol.c | 5 ++--- tools/perf/util/symbol.h | 2 +- tools/perf/util/synthetic-events.c | 2 +- 15 files changed, 43 insertions(+), 34 deletions(-) diff --git a/tools/perf/bench/inject-buildid.c b/tools/perf/bench/inject-buildid.c index 12387ea88b9a9..aad572a78d7fc 100644 --- a/tools/perf/bench/inject-buildid.c +++ b/tools/perf/bench/inject-buildid.c @@ -85,7 +85,7 @@ static int add_dso(const char *fpath, const struct stat *sb __maybe_unused, if (typeflag == FTW_D || typeflag == FTW_SL) return 0; - if (filename__read_build_id(fpath, &bid, /*block=*/true) < 0) + if (filename__read_build_id(fpath, &bid) < 0) return 0; dso->name = realpath(fpath, NULL); diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 2e0f2004696ae..c98104481c8a1 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -180,7 +180,7 @@ static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi) struct nscookie nsc; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); @@ -204,7 +204,7 @@ static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi) int err; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); @@ -280,7 +280,7 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) if (!dso__build_id_filename(dso, filename, sizeof(filename), false)) return true; - if (filename__read_build_id(filename, &bid, /*block=*/true) == -1) { + if (filename__read_build_id(filename, &bid) == -1) { if (errno == ENOENT) return false; @@ -309,7 +309,7 @@ static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi) int err; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 9606b4bf10979..5ea08e80cf1a3 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -766,12 +766,12 @@ static int dso__read_build_id(struct dso *dso) mutex_lock(dso__lock(dso)); nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - if (filename__read_build_id(dso__long_name(dso), &bid, /*block=*/true) > 0) + if (filename__read_build_id(dso__long_name(dso), &bid) > 0) dso__set_build_id(dso, &bid); else if (dso__nsinfo(dso)) { char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - if (new_name && filename__read_build_id(new_name, &bid, /*block=*/true) > 0) + if (new_name && filename__read_build_id(new_name, &bid) > 0) dso__set_build_id(dso, &bid); free(new_name); } diff --git a/tools/perf/tests/pe-file-parsing.c b/tools/perf/tests/pe-file-parsing.c index 8b31d1d05f905..30c7da79e109b 100644 --- a/tools/perf/tests/pe-file-parsing.c +++ b/tools/perf/tests/pe-file-parsing.c @@ -37,7 +37,7 @@ static int run_dir(const char *d) size_t idx; scnprintf(filename, PATH_MAX, "%s/pe-file.exe", d); - ret = filename__read_build_id(filename, &bid, /*block=*/true); + ret = filename__read_build_id(filename, &bid); TEST_ASSERT_VAL("Failed to read build_id", ret == sizeof(expect_build_id)); TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id, @@ -49,7 +49,7 @@ static int run_dir(const char *d) !strcmp(debuglink, expect_debuglink)); scnprintf(debugfile, PATH_MAX, "%s/%s", d, debuglink); - ret = filename__read_build_id(debugfile, &bid, /*block=*/true); + ret = filename__read_build_id(debugfile, &bid); TEST_ASSERT_VAL("Failed to read debug file build_id", ret == sizeof(expect_build_id)); TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id, diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c index 6132f1af3e22d..93baee2eae42a 100644 --- a/tools/perf/tests/sdt.c +++ b/tools/perf/tests/sdt.c @@ -31,7 +31,7 @@ static int build_id_cache__add_file(const char *filename) struct build_id bid = { .size = 0, }; int err; - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); if (err < 0) { pr_debug("Failed to read build id of %s\n", filename); return err; diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 35505a1ffd111..fdb35133fde43 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -122,7 +122,7 @@ int filename__snprintf_build_id(const char *pathname, char *sbuild_id, size_t sb struct build_id bid = { .size = 0, }; int ret; - ret = filename__read_build_id(pathname, &bid, /*block=*/true); + ret = filename__read_build_id(pathname, &bid); if (ret < 0) return ret; @@ -848,7 +848,7 @@ static int filename__read_build_id_ns(const char *filename, int ret; nsinfo__mountns_enter(nsi, &nsc); - ret = filename__read_build_id(filename, bid, /*block=*/true); + ret = filename__read_build_id(filename, bid); nsinfo__mountns_exit(&nsc); return ret; diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index 07099b50f2192..46fecb7b17e14 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -118,7 +118,7 @@ struct debuginfo *debuginfo__new(const char *path) * incase the path isn't for a regular file. */ assert(!dso__has_build_id(dso)); - if (filename__read_build_id(path, &bid, /*block=*/false) > 0) + if (filename__read_build_id(path, &bid) > 0) dso__set_build_id(dso, &bid); for (type = distro_dwarf_types; diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c index 64c1d65b01496..0a7645c7fae7d 100644 --- a/tools/perf/util/dsos.c +++ b/tools/perf/util/dsos.c @@ -81,13 +81,13 @@ static int dsos__read_build_ids_cb(struct dso *dso, void *data) return 0; } nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - if (filename__read_build_id(dso__long_name(dso), &bid, /*block=*/true) > 0) { + if (filename__read_build_id(dso__long_name(dso), &bid) > 0) { dso__set_build_id(dso, &bid); args->have_build_id = true; } else if (errno == ENOENT && dso__nsinfo(dso)) { char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - if (new_name && filename__read_build_id(new_name, &bid, /*block=*/true) > 0) { + if (new_name && filename__read_build_id(new_name, &bid) > 0) { dso__set_build_id(dso, &bid); args->have_build_id = true; } diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c index 2324f6846d510..d9effc3ada995 100644 --- a/tools/perf/util/libbfd.c +++ b/tools/perf/util/libbfd.c @@ -418,13 +418,18 @@ int dso__load_bfd_symbols(struct dso *dso, const char *debugfile) return err; } -int libbfd__read_build_id(const char *filename, struct build_id *bid, bool block) +int libbfd__read_build_id(const char *filename, struct build_id *bid) { size_t size = sizeof(bid->data); int err = -1, fd; bfd *abfd; - fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + if (!filename) + return -EFAULT; + if (!is_regular_file(filename)) + return -EWOULDBLOCK; + + fd = open(filename, O_RDONLY); if (fd < 0) return -1; diff --git a/tools/perf/util/libbfd.h b/tools/perf/util/libbfd.h index e300f171d1bd7..953886f3d62f3 100644 --- a/tools/perf/util/libbfd.h +++ b/tools/perf/util/libbfd.h @@ -25,7 +25,7 @@ void dso__free_a2l_libbfd(struct dso *dso); int symbol__disassemble_libbfd(const char *filename, struct symbol *sym, struct annotate_args *args); -int libbfd__read_build_id(const char *filename, struct build_id *bid, bool block); +int libbfd__read_build_id(const char *filename, struct build_id *bid); int libbfd_filename__read_debuglink(const char *filename, char *debuglink, size_t size); @@ -59,8 +59,7 @@ static inline int symbol__disassemble_libbfd(const char *filename __always_unuse } static inline int libbfd__read_build_id(const char *filename __always_unused, - struct build_id *bid __always_unused, - bool block __always_unused) + struct build_id *bid __always_unused) { return -1; } diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 220689e8fb9b3..3de977eea15d6 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -874,20 +874,20 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) return err; } -static int read_build_id(const char *filename, struct build_id *bid, bool block) +static int read_build_id(const char *filename, struct build_id *bid) { size_t size = sizeof(bid->data); int fd, err; Elf *elf; - err = libbfd__read_build_id(filename, bid, block); + err = libbfd__read_build_id(filename, bid); if (err >= 0) goto out; if (size < BUILD_ID_SIZE) goto out; - fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + fd = open(filename, O_RDONLY); if (fd < 0) goto out; @@ -908,7 +908,7 @@ static int read_build_id(const char *filename, struct build_id *bid, bool block) return err; } -int filename__read_build_id(const char *filename, struct build_id *bid, bool block) +int filename__read_build_id(const char *filename, struct build_id *bid) { struct kmod_path m = { .name = NULL, }; char path[PATH_MAX]; @@ -916,6 +916,8 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo if (!filename) return -EFAULT; + if (!is_regular_file(filename)) + return -EWOULDBLOCK; err = kmod_path__parse(&m, filename); if (err) @@ -932,10 +934,9 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo } close(fd); filename = path; - block = true; } - err = read_build_id(filename, bid, block); + err = read_build_id(filename, bid); if (m.comp) unlink(filename); diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index aa931a0015c10..2102ed36f2dc2 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -94,7 +94,7 @@ int filename__read_debuglink(const char *filename __maybe_unused, /* * Just try PT_NOTE header otherwise fails */ -int filename__read_build_id(const char *filename, struct build_id *bid, bool block) +int filename__read_build_id(const char *filename, struct build_id *bid) { int fd, ret = -1; bool need_swap = false, elf32; @@ -111,7 +111,12 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo void *phdr, *buf = NULL; ssize_t phdr_size, ehdr_size, buf_size = 0; - fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + if (!filename) + return -EFAULT; + if (!is_regular_file(filename)) + return -EWOULDBLOCK; + + fd = open(filename, O_RDONLY); if (fd < 0) return -1; @@ -335,7 +340,7 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, if (ret >= 0) RC_CHK_ACCESS(dso)->is_64_bit = ret; - if (filename__read_build_id(ss->name, &bid, /*block=*/true) > 0) + if (filename__read_build_id(ss->name, &bid) > 0) dso__set_build_id(dso, &bid); return 0; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index f6c268c588a56..6e0395e107356 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1748,14 +1748,13 @@ int dso__load(struct dso *dso, struct map *map) /* * Read the build id if possible. This is required for - * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work. Don't block in case path - * isn't for a regular file. + * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work. */ if (!dso__has_build_id(dso)) { struct build_id bid = { .size = 0, }; __symbol__join_symfs(name, PATH_MAX, dso__long_name(dso)); - if (filename__read_build_id(name, &bid, /*block=*/false) > 0) + if (filename__read_build_id(name, &bid) > 0) dso__set_build_id(dso, &bid); } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 3471062187992..3fb5d146d9b15 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -140,7 +140,7 @@ struct symbol *dso__next_symbol(struct symbol *sym); enum dso_type dso__type_fd(int fd); -int filename__read_build_id(const char *filename, struct build_id *id, bool block); +int filename__read_build_id(const char *filename, struct build_id *id); int sysfs__read_build_id(const char *filename, struct build_id *bid); int modules__parse(const char *filename, void *arg, int (*process_module)(void *arg, const char *name, diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index c85d219928d47..c5b0ba775098f 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -401,7 +401,7 @@ static void perf_record_mmap2__read_build_id(struct perf_record_mmap2 *event, nsi = nsinfo__new(event->pid); nsinfo__mountns_enter(nsi, &nc); - rc = filename__read_build_id(event->filename, &bid, /*block=*/false) > 0 ? 0 : -1; + rc = filename__read_build_id(event->filename, &bid) > 0 ? 0 : -1; nsinfo__mountns_exit(&nc); nsinfo__put(nsi); From fa870f951793d9135ebf9c79da95b6b27fde4c00 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 6 Jun 2026 21:51:42 -0300 Subject: [PATCH 1387/3297] perf tools: Add O_CLOEXEC to open() calls in DSO and ELF code [ Upstream commit b145137fec13dc8fc7fcb14193ce395a1164e3a1 ] open() calls in dso.c and symbol-elf.c omit O_CLOEXEC, which leaks file descriptors to child processes spawned during symbol resolution (e.g., addr2line, objdump). This can exhaust the fd limit during long profiling sessions or when processing many DSOs. Add O_CLOEXEC to all open() calls in both files (12 call sites). Fixes: cdd059d731eeb466 ("perf tools: Move dso_* related functions into dso object") Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") Reported-by: sashiko-bot Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Stable-dep-of: f973e52a9977 ("perf dso: Fix heap overflow in dso__get_filename() on decompressed path") Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 4 ++-- tools/perf/util/symbol-elf.c | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 2b7b5f94115f5..c1b26337e03bb 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -343,7 +343,7 @@ int filename__decompress(const char *name, char *pathname, * descriptor to the uncompressed file. */ if (!compressions[comp].is_compressed(name)) - return open(name, O_RDONLY); + return open(name, O_RDONLY | O_CLOEXEC); fd = mkstemp(tmpbuf); if (fd < 0) { @@ -1836,7 +1836,7 @@ static const u8 *__dso__read_symbol(struct dso *dso, const char *symfs_filename, int saved_errno; nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - fd = open(symfs_filename, O_RDONLY); + fd = open(symfs_filename, O_RDONLY | O_CLOEXEC); saved_errno = errno; nsinfo__mountns_exit(&nsc); if (fd < 0) { diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 3de977eea15d6..f46e9218194cd 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -217,7 +217,7 @@ bool filename__has_section(const char *filename, const char *sec) GElf_Shdr shdr; bool found = false; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) return false; @@ -887,7 +887,7 @@ static int read_build_id(const char *filename, struct build_id *bid) if (size < BUILD_ID_SIZE) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -948,7 +948,7 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) size_t size = sizeof(bid->data); int fd, err = -1; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -1019,7 +1019,7 @@ int filename__read_debuglink(const char *filename, char *debuglink, if (err >= 0) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -1184,7 +1184,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, type = dso__symtab_type(dso); } else { - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY | O_CLOEXEC); if (fd < 0) { *dso__load_errno(dso) = errno; return -1; @@ -1974,7 +1974,7 @@ static int kcore__open(struct kcore *kcore, const char *filename) { GElf_Ehdr *ehdr; - kcore->fd = open(filename, O_RDONLY); + kcore->fd = open(filename, O_RDONLY | O_CLOEXEC); if (kcore->fd == -1) return -1; @@ -2007,7 +2007,7 @@ static int kcore__init(struct kcore *kcore, char *filename, int elfclass, if (temp) kcore->fd = mkstemp(filename); else - kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); + kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0400); if (kcore->fd == -1) return -1; @@ -2483,11 +2483,11 @@ static int kcore_copy__compare_files(const char *from_filename, { int from, to, err = -1; - from = open(from_filename, O_RDONLY); + from = open(from_filename, O_RDONLY | O_CLOEXEC); if (from < 0) return -1; - to = open(to_filename, O_RDONLY); + to = open(to_filename, O_RDONLY | O_CLOEXEC); if (to < 0) goto out_close_from; @@ -2905,7 +2905,7 @@ int get_sdt_note_list(struct list_head *head, const char *target) Elf *elf; int fd, ret; - fd = open(target, O_RDONLY); + fd = open(target, O_RDONLY | O_CLOEXEC); if (fd < 0) return -EBADF; From 95bf4dbcd5022ffd924f570f63f357f9ec968852 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 7 Jun 2026 22:43:09 -0300 Subject: [PATCH 1388/3297] perf tools: Fix uninitialized pathname on uncompressed fallback in filename__decompress() [ Upstream commit 51cdb188edeaf389e4377859b9c483c19ce5a259 ] filename__decompress() has an early return path for files that are not actually compressed. This path returns the fd from open() directly but never writes to the pathname output parameter, leaving the caller with an uninitialized buffer despite a successful return. Callers like dso__decompress_kmodule_path() pass pathname to decompress_kmodule() which uses it to set the decompressed file path. If pathname is uninitialized, subsequent operations on the path produce undefined behavior. Fix by setting pathname to an empty string on the uncompressed path. Callers already check for an empty pathname to distinguish temporary decompressed files (which need unlink) from the original file. Reported-by: sashiko-bot Fixes: 7ac22b088afe26a4 ("perf tools: Add filename__decompress function") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Stable-dep-of: f973e52a9977 ("perf dso: Fix heap overflow in dso__get_filename() on decompressed path") Signed-off-by: Sasha Levin --- tools/perf/tests/code-reading.c | 7 +++++-- tools/perf/util/disasm.c | 7 +++++-- tools/perf/util/dso.c | 12 +++++++++--- tools/perf/util/symbol-elf.c | 6 ++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 4c9fbf6965c4a..4e759b8989806 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -465,8 +465,11 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, goto out; } - decomp = true; - objdump_name = decomp_name; + /* empty pathname means file wasn't actually compressed */ + if (decomp_name[0] != '\0') { + decomp = true; + objdump_name = decomp_name; + } } /* Read the object code using objdump */ diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index c513db41137fa..61a1863990354 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1684,8 +1684,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0) return -1; - decomp = true; - strcpy(symfs_filename, tmp); + /* empty pathname means file wasn't actually compressed */ + if (tmp[0] != '\0') { + decomp = true; + strcpy(symfs_filename, tmp); + } } /* diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index c1b26337e03bb..176260428965d 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -342,8 +342,11 @@ int filename__decompress(const char *name, char *pathname, * To keep this transparent, we detect this and return the file * descriptor to the uncompressed file. */ - if (!compressions[comp].is_compressed(name)) + if (!compressions[comp].is_compressed(name)) { + if (pathname && len > 0) + pathname[0] = '\0'; return open(name, O_RDONLY | O_CLOEXEC); + } fd = mkstemp(tmpbuf); if (fd < 0) { @@ -597,8 +600,11 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, goto out; } - *decomp = true; - strcpy(name, newpath); + /* empty pathname means file wasn't actually compressed */ + if (newpath[0] != '\0') { + *decomp = true; + strcpy(name, newpath); + } } return name; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index f46e9218194cd..bcaeb5ce56873 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -933,12 +933,14 @@ int filename__read_build_id(const char *filename, struct build_id *bid) return -1; } close(fd); - filename = path; + /* non-empty path means a temp file was created */ + if (path[0] != '\0') + filename = path; } err = read_build_id(filename, bid); - if (m.comp) + if (m.comp && filename == path) unlink(filename); return err; } From debfcd673a6d14e71e82b6390b13cd8331416506 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 19:33:48 -0300 Subject: [PATCH 1389/3297] perf dso: Fix heap overflow in dso__get_filename() on decompressed path [ Upstream commit f973e52a99776fcc473488984828d1fce56d5382 ] dso__get_filename() allocates name with malloc(PATH_MAX), but the dso__filename_with_chroot() path replaces name with an asprintf'd exact-size string (e.g. 8 bytes for "/a/b.ko"). When the DSO needs decompression, dso__decompress_kmodule_path() writes the temp path ("/tmp/perf-kmod-XXXXXX", 22 bytes) into newpath, and strcpy(name, newpath) overflows the smaller allocation. Replace the strcpy with strdup(newpath) + free(name) so the buffer is always correctly sized for its content. Reported-by: sashiko-bot Fixes: 1d6b3c9ba756a513 ("perf tools: Decompress kernel module when reading DSO data") Reviewed-by: Ian Rogers Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 176260428965d..3cd6e59fcb73f 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -602,8 +602,15 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, /* empty pathname means file wasn't actually compressed */ if (newpath[0] != '\0') { + char *tmp = strdup(newpath); + + if (!tmp) { + unlink(newpath); + goto out; + } + free(name); + name = tmp; *decomp = true; - strcpy(name, newpath); } } return name; From 5d080b7324f0753736c95c1eb538a1f6b8fe6e58 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:16:02 -0300 Subject: [PATCH 1390/3297] perf dso: Set error code when open() fails on uncompressed fallback path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d2c6069d68ee9d53b05fe38bc2049cc4286fbb16 ] filename__decompress() has an early return for files that are not actually compressed, where it calls open() directly. When open() fails, the function returns -1 but never sets *err. The caller chain (decompress_kmodule → dso__decompress_kmodule_path → dso__get_filename) then reads *dso__load_errno(dso) to set errno, but that field was never populated, so errno gets a stale or zero value. With errno=0, __open_dso() computes fd = -errno = 0, which is non- negative, so callers treat fd 0 (stdin) as a valid DSO file descriptor. Set *err = errno when open() fails on the uncompressed path, matching the error handling on the compressed path at line 354. Reported-by: sashiko-bot Fixes: 8b42b7e5e8b5692b ("perf tools: Add is_compressed callback to compressions array") Cc: Jiri Olsa Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 3cd6e59fcb73f..f5f2209f06f5e 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -343,9 +343,12 @@ int filename__decompress(const char *name, char *pathname, * descriptor to the uncompressed file. */ if (!compressions[comp].is_compressed(name)) { + fd = open(name, O_RDONLY | O_CLOEXEC); + if (fd < 0) + *err = errno; if (pathname && len > 0) pathname[0] = '\0'; - return open(name, O_RDONLY | O_CLOEXEC); + return fd; } fd = mkstemp(tmpbuf); From 661f60a8a5cf8155181d99ca3ad88961a541653b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:34:38 -0300 Subject: [PATCH 1391/3297] perf tools: Use snprintf() for root_dir path construction [ Upstream commit 7b0df6f4d498b1608afccfd6dffb264e6da91693 ] get_kernel_version() in machine.c and dso__load_guest_kernel_sym() in symbol.c use sprintf() to construct paths by prepending root_dir to "/proc/version" and "/proc/kallsyms" respectively. Both write into PATH_MAX stack buffers, but root_dir comes from --guestmount or KVM configuration and is not length-checked. A root_dir at or near PATH_MAX causes a stack buffer overflow. Switch to snprintf() with sizeof(path) to prevent overflow. Reported-by: sashiko-bot Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") Cc: Zhang Yanmin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/machine.c | 2 +- tools/perf/util/symbol.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index b5dd42588c916..ba541f12908ec 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1319,7 +1319,7 @@ static char *get_kernel_version(const char *root_dir) char *name, *tmp; const char *prefix = "Linux version "; - sprintf(version, "%s/proc/version", root_dir); + snprintf(version, sizeof(version), "%s/proc/version", root_dir); file = fopen(version, "r"); if (!file) return NULL; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 6e0395e107356..af0df841d640c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2158,7 +2158,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) if (!kallsyms_filename) return -1; } else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); + snprintf(path, sizeof(path), "%s/proc/kallsyms", machine->root_dir); kallsyms_filename = path; } From bc27041e8971e01fe8caa31d0e994931f3c9da3b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 20:40:34 -0300 Subject: [PATCH 1392/3297] perf hwmon: Fix fd check to accept fd 0 in hwmon_pmu__describe_items() [ Upstream commit cfafef390ca9c753b34c7e97b5abee4cab0ce270 ] hwmon_pmu__describe_items() checks 'if (fd > 0)' after openat(), which incorrectly rejects fd 0. While fd 0 is normally stdin, if stdin has been closed (common in daemon/service contexts), the kernel reuses fd 0 for the next open. With fd > 0, the sysfs file is not read and the fd is leaked. Change to 'if (fd >= 0)' to match the standard openat() error check. Reported-by: sashiko-bot Fixes: 53cc0b351ec99278 ("perf hwmon_pmu: Add a tool PMU exposing events from hwmon in sysfs") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/hwmon_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index a1133de5ed22e..b24cdc3299447 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -435,7 +435,7 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si hwmon_item_strs[bit], is_alarm ? "_alarm" : ""); fd = openat(dir, buf, O_RDONLY); - if (fd > 0) { + if (fd >= 0) { ssize_t read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') From aea30b437ebd02b9f1ba195581d46a8ed1c0049d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:00:11 -0300 Subject: [PATCH 1393/3297] perf sched: Replace (void*)1 sentinel with proper runtime allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 500f5dd0a8b6f7bd174102587c7dff5a7d2fecbf ] map__findnew_thread() marks color-pid threads by storing (void*)1 as the thread private data via thread__set_priv(). This sentinel value causes two problems: 1. thread__get_runtime() returns (void*)1 as a struct thread_runtime pointer. Any field access (e.g. tr->shortname) dereferences address 1, which is an unmapped page — immediate segfault. 2. cmd_sched() registers free() as the thread priv destructor, so thread cleanup calls free((void*)1) — undefined behavior that corrupts the heap on many allocators. Fix by adding a 'color' flag to struct thread_runtime and allocating a real runtime struct for color-pid threads. thread__has_color() now checks the flag instead of relying on priv being non-NULL. Reported-by: sashiko-bot Fixes: 58a606149c60d5da ("perf sched: Avoid union type punning undefined behavior") Reviewed-by: Ian Rogers Cc: Ian Rogers Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/builtin-sched.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index f5bef8f98756f..f2da112e49f5f 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -267,6 +267,7 @@ struct thread_runtime { u64 migrations; int prio; + bool color; }; /* per event run time data */ @@ -1576,22 +1577,32 @@ static int process_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unus static bool thread__has_color(struct thread *thread) { - return thread__priv(thread) != NULL; + struct thread_runtime *tr = thread__priv(thread); + + return tr != NULL && tr->color; } static struct thread* map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid, pid_t tid) { struct thread *thread = machine__findnew_thread(machine, pid, tid); - bool color = false; - if (!sched->map.color_pids || !thread || thread__priv(thread)) + if (!sched->map.color_pids || !thread) return thread; - if (thread_map__has(sched->map.color_pids, tid)) - color = true; + /* + * Always check the color-pids map, even if thread__priv() is + * already set. COMM events processed before the first sched_switch + * allocate a thread_runtime via thread__get_runtime(), so priv is + * non-NULL before we ever get here. Skipping the check on non-NULL + * priv would prevent those threads from being colored. + */ + if (thread_map__has(sched->map.color_pids, tid)) { + struct thread_runtime *tr = thread__get_runtime(thread); - thread__set_priv(thread, color ? ((void*)1) : NULL); + if (tr) + tr->color = true; + } return thread; } From 1935d213aeb235fca1f8931967724ab24e10716a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:01:15 -0300 Subject: [PATCH 1394/3297] perf bpf: Validate func_info_rec_size and sub_id in synthesize_bpf_prog_name() [ Upstream commit 10b3c3d63ecc17c6acb855bac5f40367f1115765 ] synthesize_bpf_prog_name() computes a pointer into the func_info array using sub_id * info->func_info_rec_size without validating either value. Both come from perf.data and are untrusted: - A func_info_rec_size smaller than sizeof(struct bpf_func_info) means the finfo pointer would reference a truncated entry, reading past it into adjacent data. - A sub_id >= nr_func_info computes an offset past the func_info buffer, causing an out-of-bounds read. Add bounds checks for both values before computing the pointer offset. When validation fails, fall through to the non-BTF name path instead of reading garbage. Reported-by: sashiko-bot Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 895bab82da08f..90a25ef1ee7a6 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -143,7 +143,9 @@ static int synthesize_bpf_prog_name(char *buf, int size, name_len = scnprintf(buf, size, "bpf_prog_"); name_len += snprintf_hex(buf + name_len, size - name_len, prog_tags[sub_id], BPF_TAG_SIZE); - if (btf) { + if (btf && + info->func_info_rec_size >= sizeof(*finfo) && + sub_id < info->nr_func_info) { finfo = func_infos + sub_id * info->func_info_rec_size; t = btf__type_by_id(btf, finfo->type_id); if (t) From cafd80d81f08befd4b6a5fd949dded598f0ab303 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:02:25 -0300 Subject: [PATCH 1395/3297] perf bpf: Reject oversized BPF metadata events that truncate header.size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2d6ea0875093da9033fcb62c09a9e2f1de49fe91 ] bpf_metadata_alloc() computes event_size from the number of BPF metadata variables and stores it in header.size, which is __u16. With 204 or more .rodata variables prefixed "bpf_metadata_", event_size exceeds 65535 and silently truncates. The truncated header.size causes synthesize_perf_record_bpf_metadata() to allocate a buffer sized by the truncated value, then memcpy the full event data into it — a heap buffer overflow. Add a check that event_size fits in __u16 before proceeding. BPF programs with that many metadata variables are exotic enough that silently dropping the metadata is acceptable. Reported-by: sashiko-bot Fixes: ab38e84ba9a80581 ("perf record: collect BPF metadata from existing BPF programs") Reviewed-by: Ian Rogers Cc: Blake Jones Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 90a25ef1ee7a6..24efc024c59e7 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -369,6 +369,15 @@ static struct bpf_metadata *bpf_metadata_alloc(__u32 nr_prog_tags, event_size = sizeof(metadata->event->bpf_metadata) + nr_variables * sizeof(metadata->event->bpf_metadata.entries[0]); + /* + * header.size is __u16. synthesize_perf_record_bpf_metadata() + * adds machine->id_hdr_size (up to ~64 bytes) after this, so + * leave headroom to prevent the final size from wrapping. + */ + if (event_size > UINT16_MAX - 256) { + bpf_metadata_free(metadata); + return NULL; + } metadata->event = zalloc(event_size); if (!metadata->event) { bpf_metadata_free(metadata); From f8898d2eb71ae8e6f51bdd24a92cdffcc6a1c8f8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 21:03:16 -0300 Subject: [PATCH 1396/3297] perf bpf: Bounds-check array offsets in bpil_offs_to_addr() [ Upstream commit 033e85edfbf271f92979d2a39aeaf40f8472a795 ] bpil_offs_to_addr() converts offsets stored in perf.data's bpf_prog_info_linear structure into heap pointers by adding the offset to the data allocation base. The offsets come from untrusted file input and are not validated against data_len. If an offset exceeds data_len, the computed address points outside the allocated data buffer. Callers like synthesize_bpf_prog_name() then dereference prog_tags[sub_id] or func_info pointers, reading arbitrary heap memory. Add a bounds check: when an offset exceeds data_len, zero the field and skip the conversion. This prevents out-of-bounds pointer construction from crafted perf.data files. Reported-by: sashiko-bot Fixes: 6ac22d036f86c4e2 ("perf bpf: Pull in bpf_program__get_prog_info_linear()") Cc: Dave Marchevsky Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-utils.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/util/bpf-utils.c b/tools/perf/util/bpf-utils.c index d6d2c9c190f7a..69148197b1ef9 100644 --- a/tools/perf/util/bpf-utils.c +++ b/tools/perf/util/bpf-utils.c @@ -264,12 +264,28 @@ void bpil_offs_to_addr(struct perf_bpil *info_linear) for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { const struct bpil_array_desc *desc = &bpil_array_desc[i]; __u64 addr, offs; + __u32 count, size; if ((info_linear->arrays & (1UL << i)) == 0) continue; offs = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); + count = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->count_offset); + size = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->size_offset); + /* offset and extent from perf.data are untrusted — keep within data[] */ + if (offs >= info_linear->data_len || + (u64)count * size > info_linear->data_len - offs) { + bpf_prog_info_set_offset_u64(&info_linear->info, + desc->array_offset, 0); + bpf_prog_info_set_offset_u32(&info_linear->info, + desc->count_offset, 0); + /* clear the bit so bpil_addr_to_offs() won't reverse a zeroed address */ + info_linear->arrays &= ~(1UL << i); + continue; + } addr = offs + ptr_to_u64(info_linear->data); bpf_prog_info_set_offset_u64(&info_linear->info, desc->array_offset, addr); From a56f29ad8aacf8dfa05716e11cc3dfb3126b7721 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 23 Jan 2026 13:32:04 +0000 Subject: [PATCH 1397/3297] perf: Remove redundant kernel.h include [ Upstream commit 7a0ba3891104da77cfd1a16d41699e0fdf45603a ] Now that the bitfield dependency is resolved, the explicit inclusion of kernel.h is no longer needed. Remove the redundant include. Signed-off-by: Leo Yan Cc: Ian Rogers Cc: James Clark Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo Stable-dep-of: 542e88a4c6f7 ("perf cs-etm: Reject CPU IDs that would overflow signed comparison") Signed-off-by: Sasha Levin --- tools/perf/arch/arm64/util/header.c | 1 - tools/perf/util/cs-etm.c | 1 - 2 files changed, 2 deletions(-) diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c index f445a2dd62934..cbc0ba101636b 100644 --- a/tools/perf/arch/arm64/util/header.c +++ b/tools/perf/arch/arm64/util/header.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index ea5f7b3545c10..7e4feccfba32f 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -6,7 +6,6 @@ * Author: Mathieu Poirier */ -#include #include #include #include From 7d3532a0b11a242c14f8446f0e13f5c5b4f877b9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 10 Jun 2026 22:45:08 -0300 Subject: [PATCH 1398/3297] perf cs-etm: Reject CPU IDs that would overflow signed comparison [ Upstream commit 542e88a4c6f7b6edd1326ce767d4cb3c2ea9d61d ] metadata[j][CS_ETM_CPU] is a u64 from perf.data, but the comparison with max_cpu casts it to (int). A crafted value like 0xFFFFFFFF becomes -1 after the cast, which compares less than max_cpu (0), so the queue array is never sized to accommodate it. When the value is later passed to cs_etm__get_queue(), it indexes queue_array with the original large value, causing an out-of-bounds access. Validate that CS_ETM_CPU fits in an int before using it in the signed comparison. Fixes: 57880a7966be510c ("perf: cs-etm: Allocate queues for all CPUs") Reported-by: sashiko-bot Cc: James Clark Cc: Adrian Hunter Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 7e4feccfba32f..0fb37fd4d10df 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -6,6 +6,7 @@ * Author: Mathieu Poirier */ +#include #include #include #include @@ -3461,7 +3462,13 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, goto err_free_metadata; } - if ((int) metadata[j][CS_ETM_CPU] > max_cpu) + /* CPU id comes from perf.data and must fit max_cpu + 1 without overflow */ + if (metadata[j][CS_ETM_CPU] >= INT_MAX) { + err = -EINVAL; + goto err_free_metadata; + } + + if ((int)metadata[j][CS_ETM_CPU] > max_cpu) max_cpu = metadata[j][CS_ETM_CPU]; } From 1348bf64c1978aa329c333995de46f92d2895ba8 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 17:19:18 +0800 Subject: [PATCH 1399/3297] gpio: mlxbf3: fail probe if gpiochip registration fails [ Upstream commit 0482862a90169f4daaba0ed31a85d8304bf51e04 ] mlxbf3_gpio_probe() logs a devm_gpiochip_add_data() failure but still returns success. That leaves the platform device bound even though the GPIO chip was not registered. Return the registration error so probe failure matches the missing gpiochip state. Fixes: cd33f216d241 ("gpio: mlxbf3: Add gpio driver support") Signed-off-by: Pengpeng Hou Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260615091918.43333-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mlxbf3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 4770578269bae..566326644a2c1 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -255,7 +255,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) ret = devm_gpiochip_add_data(dev, gc, gs); if (ret) - dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n"); + return dev_err_probe(dev, ret, + "Failed adding memory mapped gpiochip\n"); return 0; } From 2024940522ef451098c940ab0b82d647de5e5d9b Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:53:10 +0800 Subject: [PATCH 1400/3297] drm/i915: clear CRTC color blob pointers after dropping refs [ Upstream commit 31f077088e0faae6be8377741f356dea1b94ba46 ] intel_crtc_put_color_blobs() drops the CRTC color blob references, but leaves the corresponding pointers unchanged. This can matter in intel_crtc_prepare_cleared_state(), which frees the old CRTC hw state before calling intel_dp_tunnel_atomic_clear_stream_bw(). The latter can fail while looking up the DP tunnel group state, for example with -EDEADLK. If that happens, the function returns without completing the cleared state preparation. The failed atomic state will then be cleared by the atomic core and intel_crtc_free_hw_state() can be called again for the same state, dropping the same blob references again. Clear the blob pointers after dropping the references so repeated cleanup of the same CRTC hw state is safe. Fixes: 77fcf58df15e ("drm/i915/dp_tunnel: Fix error handling when clearing stream BW in atomic state") Suggested-by: Imre Deak Signed-off-by: Guangshuo Li Reviewed-by: Imre Deak Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260612035310.3013066-1-lgs201920130244@gmail.com (cherry picked from commit d5005addb5f68e8a0edce249506757bdc9e3d8c8) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_atomic.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 348b1655435e1..5b6e3605732e1 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -289,6 +289,12 @@ static void intel_crtc_put_color_blobs(struct intel_crtc_state *crtc_state) drm_property_blob_put(crtc_state->pre_csc_lut); drm_property_blob_put(crtc_state->post_csc_lut); + + crtc_state->hw.degamma_lut = NULL; + crtc_state->hw.gamma_lut = NULL; + crtc_state->hw.ctm = NULL; + crtc_state->pre_csc_lut = NULL; + crtc_state->post_csc_lut = NULL; } void intel_crtc_free_hw_state(struct intel_crtc_state *crtc_state) From 355e51eeffc6b19c6d54d5c2c3b52283d5f490f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Thu, 4 Jun 2026 09:45:00 +0200 Subject: [PATCH 1401/3297] drm/xe: Fix wa_oob codegen recipe for external module builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 92dc59ab2a09097cdf249e0288ff9b69261761c6 ] When building with 'make M=drivers/gpu/drm/xe modules', kbuild invokes scripts/Makefile.build with obj=., causing $(obj) to expand to '.'. Make normalizes './xe_gen_wa_oob' to 'xe_gen_wa_oob' when constructing the $^ automatic variable (target name normalization), so the recipe command becomes just 'xe_gen_wa_oob ...' without any path prefix, and the shell cannot find the tool. Fix by replacing $^ with explicit $(obj)/xe_gen_wa_oob and $(src)/ references in both wa_oob recipe commands. In recipe strings, make does not apply target name normalization, so $(obj)/xe_gen_wa_oob correctly expands to './xe_gen_wa_oob' and the shell can execute it. This matches the pattern already used by other DRM drivers (e.g. radeon's mkregtable). Fixes: f037e0b78e6d ("drm/xe: add xe_device_wa infrastructure") Cc: Matt Atwood Cc: Matthew Brost Cc: Rodrigo Vivi Cc: intel-xe@lists.freedesktop.org Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellström Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260604074501.172129-1-thomas.hellstrom@linux.intel.com (cherry picked from commit 3a11a63cc16660d514ff584e7551589655337e87) Signed-off-by: Matthew Brost Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index d9c6cf0f189ef..ee52b751623f0 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -16,14 +16,14 @@ subdir-ccflags-y += -I$(obj) -I$(src) hostprogs := xe_gen_wa_oob generated_oob := $(obj)/generated/xe_wa_oob.c $(obj)/generated/xe_wa_oob.h quiet_cmd_wa_oob = GEN $(notdir $(generated_oob)) - cmd_wa_oob = mkdir -p $(@D); $^ $(generated_oob) + cmd_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_wa_oob.rules $(generated_oob) $(obj)/generated/%_wa_oob.c $(obj)/generated/%_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_wa_oob.rules $(call cmd,wa_oob) generated_device_oob := $(obj)/generated/xe_device_wa_oob.c $(obj)/generated/xe_device_wa_oob.h quiet_cmd_device_wa_oob = GEN $(notdir $(generated_device_oob)) - cmd_device_wa_oob = mkdir -p $(@D); $^ $(generated_device_oob) + cmd_device_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_device_wa_oob.rules $(generated_device_oob) $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_device_wa_oob.rules $(call cmd,device_wa_oob) From 95028569589f49fd7ea82fbd95be91343717ee1c Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 12 Jun 2026 08:28:35 +0800 Subject: [PATCH 1402/3297] spi: dw: fix wrong BAUDR setting after resume [ Upstream commit 66b6605bcea7af7aca3d1d858b9c5f14903f9f9a ] After resuming from suspend to ram, spi transfer stops working. Further debugging shows that the BAUDR register isn't correctly set, this is due to dws->current_freq doesn't match the HW BAUDR setting, specifically, the dws->current_freq equals to speed_hz, but BAUDR is 0. so the dw_spi_set_clk() in below code won't be called: if (dws->current_freq != speed_hz) { dw_spi_set_clk(dws, clk_div); dws->current_freq = speed_hz; } The mismatch comes from dw_spi_shutdown_chip() when suspending. Fix this mismatch by setting dws->current_freq to 0 as well when clearing BAUDR reg in dw_spi_shutdown_chip(). Fixes: e24c74527207 ("spi: controller driver for Designware SPI core") Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260612002835.5240-1-jszhang@kernel.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-dw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index fc267c6437ae0..8a3c085c85e07 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -282,6 +282,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws) { dw_spi_enable_chip(dws, 0); dw_spi_set_clk(dws, 0); + dws->current_freq = 0; } extern void dw_spi_set_cs(struct spi_device *spi, bool enable); From 34cd92141a0243aac457baad2c2c164ce5441aea Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Jun 2026 14:37:50 +0300 Subject: [PATCH 1403/3297] i3c: master: Update dev_nack_retry_count under maintenance lock [ Upstream commit ab5f9c5cb527c03790a92142ad368881a9100aaf ] Protect master->dev_nack_retry_count against concurrent sysfs updates by updating it while holding the bus maintenance lock. Consequently, combine adjacent return statements into one. For consistency, read dev_nack_retry_count while holding the bus normaluse lock. Fixes: b58f47eb39268 ("i3c: add sysfs entry and attribute for Device NACK Retry count") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260616113752.196140-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index f0ff305642bf0..90743266cd85f 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -744,7 +744,14 @@ static DEVICE_ATTR_RW(hotjoin); static ssize_t dev_nack_retry_count_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + struct i3c_bus *i3cbus = dev_to_i3cbus(dev); + ssize_t ret; + + i3c_bus_normaluse_lock(i3cbus); + ret = sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + i3c_bus_normaluse_unlock(i3cbus); + + return ret; } static ssize_t dev_nack_retry_count_store(struct device *dev, @@ -762,14 +769,11 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, i3c_bus_maintenance_lock(i3cbus); ret = master->ops->set_dev_nack_retry(master, val); + if (!ret) + master->dev_nack_retry_count = val; i3c_bus_maintenance_unlock(i3cbus); - if (ret) - return ret; - - master->dev_nack_retry_count = val; - - return count; + return ret ?: count; } static DEVICE_ATTR_RW(dev_nack_retry_count); From 507a7b07f3fa3ba55e1274b6b28c7bd5f2a416a5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Jun 2026 14:37:51 +0300 Subject: [PATCH 1404/3297] i3c: master: Add missing runtime PM get in dev_nack_retry_count_store() [ Upstream commit 79ce29e100ab3de0cad66eb48d32a7de4043e2ae ] Ensure the device is runtime resumed while updating the retry configuration to avoid accessing the controller while suspended. Call i3c_master_rpm_get() before accessing the controller in dev_nack_retry_count_store() and release it with i3c_master_rpm_put() afterwards. Fixes: 990c149c61ee4 ("i3c: master: Introduce optional Runtime PM support") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260616113752.196140-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/i3c/master.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 90743266cd85f..588395e7a7706 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -767,12 +767,18 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, if (ret) return ret; + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(i3cbus); ret = master->ops->set_dev_nack_retry(master, val); if (!ret) master->dev_nack_retry_count = val; i3c_bus_maintenance_unlock(i3cbus); + i3c_master_rpm_put(master); + return ret ?: count; } From 1467ca02ddac401ac2013e9dc55be867b570422c Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 16 Jun 2026 19:59:16 +0800 Subject: [PATCH 1405/3297] ALSA: usb-audio: qcom: Free sideband sg_table objects [ Upstream commit 7d69804a35103a50852eae41bfe6a2e0061c68fd ] The Qualcomm USB audio offload driver obtains an endpoint transfer-ring table by calling xhci_sideband_get_endpoint_buffer(). This getter passes the endpoint ring to xhci_ring_to_sgtable(), which allocates the outer struct sg_table with kzalloc_obj(*sgt). The event-ring path is equivalent: xhci_sideband_get_event_buffer() also returns the result of xhci_ring_to_sgtable(). Inside xhci_ring_to_sgtable(), sg_alloc_table_from_pages() separately allocates the scatterlist storage referenced by sgt->sgl. The returned object therefore has two allocation layers: the outer struct sg_table and its internal scatterlist storage. The Qualcomm caller only invokes sg_free_table(sgt). sg_free_table() releases the scatterlist storage owned by the table, but it does not free the separately allocated outer struct sg_table. The local sgt pointer is then discarded, so every successful endpoint or event-ring query leaks the outer object. Call kfree(sgt) after sg_free_table(sgt) in both setup paths, after the required page and DMA addresses have been copied out. Fixes: 326bbc348298 ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Signed-off-by: Xu Rao Link: https://patch.msgid.link/90B353283AA150C4+20260616115916.1222915-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 962b5f27f0447..26b632baefadc 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1165,6 +1165,7 @@ uaudio_endpoint_setup(struct snd_usb_substream *subs, tr_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); /* data transfer ring */ iova = uaudio_iommu_map_pa(MEM_XFER_RING, dma_coherent, tr_pa, @@ -1234,6 +1235,7 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, er_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); iova = uaudio_iommu_map_pa(MEM_EVENT_RING, dma_coherent, er_pa, PAGE_SIZE); From a1a3360a0c44b8b5c134db2a9d0667b61c9cf523 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 12 Jun 2026 12:58:59 +0800 Subject: [PATCH 1406/3297] xfrm: Fix xfrm state cache insertion race [ Upstream commit ddd3d0132920319ac426e12456013eadbae67e15 ] The xfrm input state cache insertion code checks the validity of the state before acquiring the global xfrm_state_lock. Thus it's possible for someone else to kill the state after it passed the validity check, and then the insertion will add the dead state to the cache. Fix this by moving the validity check inside the lock. This entire function is called on the input path, where BH must be off (e.g., the caller of this function xfrm_input acquires its spinlocks without disabling BH). So there is no need to disable BH here or take the RCU read lock. Remove both and replace them with an assertion that trips if BH is accidentally enabled on some future calling path. Fixes: 81a331a0e72d ("xfrm: Add an inbound percpu state cache.") Reported-by: Zero Day Initiative Signed-off-by: Herbert Xu Reviewed-by: Simon Horman Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_state.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index efbe0135d2776..7fc6727e75c5a 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1207,9 +1207,11 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark, struct hlist_head *state_cache_input; struct xfrm_state *x = NULL; + /* BH is always disabled on the input path. */ + lockdep_assert_in_softirq(); + state_cache_input = raw_cpu_ptr(net->xfrm.state_cache_input); - rcu_read_lock(); hlist_for_each_entry_rcu(x, state_cache_input, state_cache_input) { if (x->props.family != family || x->id.spi != spi || @@ -1227,20 +1229,25 @@ struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark, xfrm_hash_ptrs_get(net, &state_ptrs); x = __xfrm_state_lookup(&state_ptrs, mark, daddr, spi, proto, family); - - if (x && x->km.state == XFRM_STATE_VALID) { - spin_lock_bh(&net->xfrm.xfrm_state_lock); - if (hlist_unhashed(&x->state_cache_input)) { + if (x) { + spin_lock(&net->xfrm.xfrm_state_lock); + if (x->km.state != XFRM_STATE_VALID) { + /* + * The state is about to be destroyed. + * + * Don't add it to the cache but still + * return it to the caller. + */ + } else if (hlist_unhashed(&x->state_cache_input)) { hlist_add_head_rcu(&x->state_cache_input, state_cache_input); } else { hlist_del_rcu(&x->state_cache_input); hlist_add_head_rcu(&x->state_cache_input, state_cache_input); } - spin_unlock_bh(&net->xfrm.xfrm_state_lock); + spin_unlock(&net->xfrm.xfrm_state_lock); } out: - rcu_read_unlock(); return x; } EXPORT_SYMBOL(xfrm_input_state_lookup); From 7248ae02a9453342dbc7d5237c25b28dce4af96d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Jun 2026 05:56:34 +0000 Subject: [PATCH 1407/3297] xfrm: annotate data-races around xfrm_policy_count[] and xfrm_policy_default[] [ Upstream commit 68de007d5ac9df0e3f4f187a179c5c842bb5a2be ] KCSAN reported a data race involving net->xfrm.policy_count access. Add missing READ_ONCE()/WRITE_ONCE() annotations on xfrm_policy_count and xfrm_policy_default. Fixes: 2518c7c2b3d7 ("[XFRM]: Hash policies when non-prefixed.") Reported-by: syzbot+d85ba1c732720b9a4097@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2b9e96.99669fcc.12a77b.0006.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- include/net/xfrm.h | 8 ++++---- net/xfrm/xfrm_policy.c | 24 ++++++++++++------------ net/xfrm/xfrm_user.c | 18 +++++++++--------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 7ad342e20750e..682bfc1de9cc5 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1250,8 +1250,8 @@ int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb, int dir) { - if (!net->xfrm.policy_count[dir] && !secpath_exists(skb)) - return net->xfrm.policy_default[dir] == XFRM_USERPOLICY_ACCEPT; + if (!READ_ONCE(net->xfrm.policy_count[dir]) && !secpath_exists(skb)) + return READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_ACCEPT; return false; } @@ -1351,8 +1351,8 @@ static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family) { struct net *net = dev_net(skb->dev); - if (!net->xfrm.policy_count[XFRM_POLICY_OUT] && - net->xfrm.policy_default[XFRM_POLICY_OUT] == XFRM_USERPOLICY_ACCEPT) + if (!READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]) && + READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]) == XFRM_USERPOLICY_ACCEPT) return true; return (skb_dst(skb)->flags & DST_NOXFRM) || diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c76625d511ec3..5f468f01daf7b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -685,7 +685,7 @@ static void xfrm_byidx_resize(struct net *net) static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total) { - unsigned int cnt = net->xfrm.policy_count[dir]; + unsigned int cnt = READ_ONCE(net->xfrm.policy_count[dir]); unsigned int hmask = net->xfrm.policy_bydst[dir].hmask; if (total) @@ -711,12 +711,12 @@ static inline int xfrm_byidx_should_resize(struct net *net, int total) void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si) { - si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN]; - si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT]; - si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD]; - si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]; - si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]; - si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]; + si->incnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN]); + si->outcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]); + si->fwdcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD]); + si->inscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]); + si->outscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]); + si->fwdscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]); si->spdhcnt = net->xfrm.policy_idx_hmask; si->spdhmcnt = xfrm_policy_hashmax; } @@ -2318,7 +2318,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir) } list_add(&pol->walk.all, &net->xfrm.policy_all); - net->xfrm.policy_count[dir]++; + WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] + 1); xfrm_pol_hold(pol); } @@ -2337,7 +2337,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, } list_del_init(&pol->walk.all); - net->xfrm.policy_count[dir]--; + WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] - 1); return pol; } @@ -3222,7 +3222,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net, /* To accelerate a bit... */ if (!if_id && ((dst_orig->flags & DST_NOXFRM) || - !net->xfrm.policy_count[XFRM_POLICY_OUT])) + !READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]))) goto nopol; xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id); @@ -3296,7 +3296,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net, nopol: if ((!dst_orig->dev || !(dst_orig->dev->flags & IFF_LOOPBACK)) && - net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { + READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) { err = -EPERM; goto error; } @@ -3750,7 +3750,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, const bool is_crypto_offload = sp && (xfrm_input_state(skb)->xso.type == XFRM_DEV_OFFLOAD_CRYPTO); - if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) { + if (READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS); return 0; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 2c11866be5788..817e40d057341 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2476,9 +2476,9 @@ static int xfrm_notify_userpolicy(struct net *net) } up = nlmsg_data(nlh); - up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; - up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; - up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; + up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]); + up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]); + up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]); nlmsg_end(skb, nlh); @@ -2502,13 +2502,13 @@ static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_userpolicy_default *up = nlmsg_data(nlh); if (xfrm_userpolicy_is_valid(up->in)) - net->xfrm.policy_default[XFRM_POLICY_IN] = up->in; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN], up->in); if (xfrm_userpolicy_is_valid(up->fwd)) - net->xfrm.policy_default[XFRM_POLICY_FWD] = up->fwd; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD], up->fwd); if (xfrm_userpolicy_is_valid(up->out)) - net->xfrm.policy_default[XFRM_POLICY_OUT] = up->out; + WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT], up->out); rt_genid_bump_all(net); @@ -2538,9 +2538,9 @@ static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh, } r_up = nlmsg_data(r_nlh); - r_up->in = net->xfrm.policy_default[XFRM_POLICY_IN]; - r_up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD]; - r_up->out = net->xfrm.policy_default[XFRM_POLICY_OUT]; + r_up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]); + r_up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]); + r_up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]); nlmsg_end(r_skb, r_nlh); return nlmsg_unicast(net->xfrm.nlsk, r_skb, portid); From 6d99379c58f7f1c6ab2cc7aba01a4f52d71adcfe Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 15 Jun 2026 09:02:37 +0000 Subject: [PATCH 1408/3297] xfrm: validate selector family and prefixlen during match [ Upstream commit 40f0b1047918539f0b0f795ac65e35336b4c2c78 ] syzbot reported a shift-out-of-bounds in xfrm_selector_match() due to AF_UNSPEC selector with large prefixlen (e.g. 128) matched against IPv4 flow (when XFRM_STATE_AF_UNSPEC is set). Fix this by: - Rejecting mismatched families in xfrm_selector_match. - Returning false in addr4_match if prefixlen > 32. - Returning false in addr_match if prefixlen > 128 (prevents overflow). Fixes: 3f0ab59e6537 ("xfrm: validate new SA's prefixlen using SA family when sel.family is unset") Reported-by: syzbot+9383b1ff0df4b29ca5e6@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a2fbe35.be3f099c.2836ae.0018.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- include/net/xfrm.h | 7 +++++++ net/xfrm/xfrm_policy.c | 3 +++ 2 files changed, 10 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 682bfc1de9cc5..af0869c14a253 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -943,6 +943,9 @@ static inline bool addr_match(const void *token1, const void *token2, unsigned int pdw; unsigned int pbi; + if (prefixlen > 128) + return false; + pdw = prefixlen >> 5; /* num of whole u32 in prefix */ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */ @@ -967,6 +970,10 @@ static inline bool addr4_match(__be32 a1, __be32 a2, u8 prefixlen) /* C99 6.5.7 (3): u32 << 32 is undefined behaviour */ if (sizeof(long) == 4 && prefixlen == 0) return true; + + if (prefixlen > 32) + return false; + return !((a1 ^ a2) & htonl(~0UL << (32 - prefixlen))); } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 5f468f01daf7b..4dad1f8172286 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -242,6 +242,9 @@ __xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) bool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, unsigned short family) { + if (family != sel->family && sel->family != AF_UNSPEC) + return false; + switch (family) { case AF_INET: return __xfrm4_selector_match(sel, fl); From 87d23f25b5e97ec922ed77a7a79555337b4a1737 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 13:59:39 -0300 Subject: [PATCH 1409/3297] perf machine: Use snprintf() for guestmount path construction [ Upstream commit fe63d3bca288c5bb983304efd5fc3a5ff3183403 ] machines__findnew() and machines__create_guest_kernel_maps() use sprintf() to build paths by prepending symbol_conf.guestmount. Both write into PATH_MAX stack buffers, but guestmount comes from user configuration and is not length-checked. A guestmount path at or near PATH_MAX causes a stack buffer overflow. Switch to snprintf() with sizeof() to prevent overflow. The subsequent access()/fopen() calls will fail on a truncated path. Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") Reported-by: sashiko-bot Cc: Zhang, Yanmin Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/machine.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ba541f12908ec..9331b4cc19f18 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -327,7 +327,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid) if ((pid != HOST_KERNEL_ID) && (pid != DEFAULT_GUEST_KERNEL_ID) && (symbol_conf.guestmount)) { - sprintf(path, "%s/%d", symbol_conf.guestmount, pid); + snprintf(path, sizeof(path), "%s/%d", symbol_conf.guestmount, pid); if (access(path, R_OK)) { static struct strlist *seen; @@ -1239,9 +1239,9 @@ int machines__create_guest_kernel_maps(struct machines *machines) namelist[i]->d_name); continue; } - sprintf(path, "%s/%s/proc/kallsyms", - symbol_conf.guestmount, - namelist[i]->d_name); + snprintf(path, sizeof(path), "%s/%s/proc/kallsyms", + symbol_conf.guestmount, + namelist[i]->d_name); ret = access(path, R_OK); if (ret) { pr_debug("Can't access file %s\n", path); From c13532ff67faed29340f4bd6d7a8c6bc219c02f8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:16:45 -0300 Subject: [PATCH 1410/3297] perf cs-etm: Validate num_cpu before metadata allocation [ Upstream commit 312d91329b8fc6989a916a3f9a12d0674167b7e4 ] cs_etm__process_auxtrace_info_full() reads num_cpu from untrusted perf.data and uses it to allocate the metadata pointer array: metadata = zalloc(sizeof(*metadata) * num_cpu); On 32-bit, sizeof(*metadata) is 4, so num_cpu = 0x40000000 overflows the multiplication to 0, causing zalloc(0) to return a valid zero-sized allocation followed by out-of-bounds writes in the population loop. Fix by computing priv_size early and using it to bound num_cpu: each CPU needs at least one u64 metadata entry, so num_cpu cannot exceed the total number of u64 entries in the event's private data area. Fixes: cd8bfd8c973eaff8 ("perf tools: Add processing of coresight metadata") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Cc: Tor Jeremiassen Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 0fb37fd4d10df..fe8bb299693a8 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -3424,6 +3424,18 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, /* First the global part */ ptr = (u64 *) auxtrace_info->priv; num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff; + + /* + * Bound num_cpu by the event size: the global header consumes + * CS_ETM_HEADER_SIZE bytes, and each CPU needs at least one u64 + * metadata entry after that. + */ + priv_size = total_size - event_header_size - INFO_HEADER_SIZE - + CS_ETM_HEADER_SIZE; + if (num_cpu <= 0 || priv_size <= 0 || + num_cpu > priv_size / (int)sizeof(u64)) + return -EINVAL; + metadata = zalloc(sizeof(*metadata) * num_cpu); if (!metadata) return -ENOMEM; From b389a5b215e35e5c4a006c7232a865657bd928cd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:40:36 -0300 Subject: [PATCH 1411/3297] perf cs-etm: Require full global header in auxtrace_info size check [ Upstream commit 78d8ba680126f3545e8d0fba667e12d79fd4353b ] cs_etm__process_auxtrace_info() checks that header.size covers event_header_size + INFO_HEADER_SIZE (16 bytes total), but then accesses ptr[CS_PMU_TYPE_CPUS] at offset 24 from the start of the event. A crafted 16-byte auxtrace_info event passes the size check but reads out-of-bounds. Include CS_ETM_HEADER_SIZE in the minimum size check so that the global header entries (version, pmu_type_cpus, snapshot) are guaranteed to fit within the event. Fixes: 55c1de9973d66516 ("perf cs-etm: Print auxtrace info even if OpenCSD isn't linked") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm-base.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/cs-etm-base.c b/tools/perf/util/cs-etm-base.c index 4abe416e3febd..aebef71d3a0a1 100644 --- a/tools/perf/util/cs-etm-base.c +++ b/tools/perf/util/cs-etm-base.c @@ -170,7 +170,9 @@ int cs_etm__process_auxtrace_info(union perf_event *event, u64 *ptr = NULL; u64 hdr_version; - if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE)) + /* Ensure priv[] is large enough for the global header entries */ + if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE + + CS_ETM_HEADER_SIZE)) return -EINVAL; /* First the global part */ From c8cb4a92eda6eff46a1b89e609d34709fe3d7e75 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 14:55:32 -0300 Subject: [PATCH 1412/3297] perf cs-etm: Bounds-check CPU in cs_etm__get_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9a989e60cc6e29d98aed2087425cba53bf4b392d ] cs_etm__get_queue() indexes etm->queues.queue_array[cpu] without validating that cpu is within nr_queues. When processing AUX_OUTPUT_HW_ID events, the cpu value comes from untrusted perf.data trace payload and flows through cs_etm__process_trace_id_v0_1() and cs_etm__queue_aux_fragment() without bounds checking, allowing an out-of-bounds read with a crafted file. Add a bounds check in cs_etm__get_queue() and NULL checks in all callers. Also add NULL checks for queue_array[i].priv in the queue iteration loops in cs_etm__map_trace_id_v0() and cs_etm__process_trace_id_v0_1() — after auxtrace_queues__grow() new entries are zero-initialized so .priv can be NULL. Add a get_cpu_data() NULL check in cs_etm__process_trace_id_v0_1(), matching the existing check in cs_etm__process_trace_id_v0(). Fixes: 77c123f53e97ad4b ("perf: cs-etm: Move traceid_list to each queue") Reported-by: sashiko-bot Cc: Adrian Hunter Cc: James Clark Cc: Leo Yan Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/cs-etm.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index fe8bb299693a8..520670348311e 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -292,8 +292,11 @@ static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int c { if (etm->per_thread_decoding) return etm->queues.queue_array[0].priv; - else - return etm->queues.queue_array[cpu].priv; + + if (cpu < 0 || cpu >= (int)etm->queues.nr_queues) + return NULL; + + return etm->queues.queue_array[cpu].priv; } static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id, @@ -306,6 +309,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id * queue associated with that CPU so only one decoder is made. */ etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]); + if (!etmq) + return -EINVAL; + if (etmq->format == UNFORMATTED) return cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); @@ -318,6 +324,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id int ret; etmq = etm->queues.queue_array[i].priv; + if (!etmq) + continue; + ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); if (ret) @@ -358,6 +367,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); + if (!etmq) + return -EINVAL; + /* * Check sink id hasn't changed in per-cpu mode. In per-thread mode, * let it pass for now until an actual overlapping trace ID is hit. In @@ -375,6 +387,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv; + if (!other_etmq) + continue; + /* Different sinks, skip */ if (other_etmq->sink_id != etmq->sink_id) continue; @@ -396,6 +411,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, } cpu_data = get_cpu_data(etm, cpu); + if (!cpu_data) + return -EINVAL; + ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); if (ret) return ret; @@ -3137,6 +3155,9 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { struct cs_etm_queue *etmq = cs_etm__get_queue(etm, auxtrace_event->cpu); + if (!etmq) + return -EINVAL; + /* * If this AUX event was inside this buffer somewhere, create a new auxtrace event * based on the sizes of the aux event, and queue that fragment. From 05b11debdffe03f2c4097b577d2a36c477bc7b0a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:25:11 -0300 Subject: [PATCH 1413/3297] perf bpf: Validate array presence before casting BPF prog info pointers [ Upstream commit 5ebf4137d23a4fd6c0cc6a6fb766ee60d2b09193 ] Several functions cast bpf_prog_info fields (jited_ksyms, jited_func_lens, jited_prog_insns) from u64 to pointers and dereference them. These fields are only valid pointers if bpil_offs_to_addr() converted their file offsets to addresses, which only happens when the corresponding PERF_BPIL_* bits are set in info_linear->arrays. A crafted perf.data can leave these bits unset while setting non-zero counts and offset values, causing the functions to dereference raw file offsets as pointers. Add array bitmask validation to all perf.data processing paths: - __bpf_event__print_bpf_prog_info(): check JITED_KSYMS and JITED_FUNC_LENS (changed to take struct perf_bpil *) - machine__process_bpf_event_load(): check JITED_KSYMS - bpf_read(): check JITED_INSNS before memcpy from jited_prog_insns - dso__disassemble_filename(): check JITED_INSNS before returning jited_prog_insns pointer Fixes: f8dfeae009effc0b ("perf bpf: Show more BPF program info in print_bpf_prog_info()") Reported-by: sashiko-bot Cc: Song Liu Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/bpf-event.c | 20 +++++++++++++++++--- tools/perf/util/bpf-event.h | 4 ++-- tools/perf/util/dso.c | 10 ++++++++++ tools/perf/util/header.c | 3 +-- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 24efc024c59e7..2294336f6e60b 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -59,6 +59,10 @@ static int machine__process_bpf_event_load(struct machine *machine, return 0; info_linear = info_node->info_linear; + /* jited_ksyms is only valid if bpil_offs_to_addr() converted it */ + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_KSYMS))) + return 0; + for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) { u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms); u64 addr = addrs[i]; @@ -959,12 +963,15 @@ int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env) return evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env); } -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp) { - __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); - __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + struct bpf_prog_info *info = &info_linear->info; + __u64 required_arrays = (1UL << PERF_BPIL_JITED_KSYMS) | + (1UL << PERF_BPIL_JITED_FUNC_LENS); + __u32 *prog_lens; + __u64 *prog_addrs; char name[KSYM_NAME_LEN]; struct btf *btf = NULL; u32 sub_prog_cnt, i; @@ -974,6 +981,13 @@ void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, sub_prog_cnt != info->nr_jited_func_lens) return; + /* Ensure the arrays were present and converted by bpil_offs_to_addr() */ + if ((info_linear->arrays & required_arrays) != required_arrays) + return; + + prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); + prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + if (info->btf_id) { struct btf_node *node; diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h index 60d2c6637af5d..da4eeb4a1a732 100644 --- a/tools/perf/util/bpf-event.h +++ b/tools/perf/util/bpf-event.h @@ -40,7 +40,7 @@ struct btf_node { int machine__process_bpf(struct machine *machine, union perf_event *event, struct perf_sample *sample); int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env); -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp); void bpf_metadata_free(struct bpf_metadata *metadata); @@ -58,7 +58,7 @@ static inline int evlist__add_bpf_sb_event(struct evlist *evlist __maybe_unused, return 0; } -static inline void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info __maybe_unused, +static inline void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear __maybe_unused, struct perf_env *env __maybe_unused, FILE *fp __maybe_unused) { diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index f5f2209f06f5e..d7b9b1f7f4f4e 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -879,6 +879,12 @@ static ssize_t bpf_read(struct dso *dso, u64 offset, char *data) return -1; } + /* jited_prog_insns is only valid if bpil_offs_to_addr() converted it */ + if (!(node->info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + dso__data(dso)->status = DSO_DATA_STATUS_ERROR; + return -1; + } + len = node->info_linear->info.jited_prog_len; buf = (u8 *)(uintptr_t)node->info_linear->info.jited_prog_insns; @@ -1920,6 +1926,10 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename, return NULL; } info_linear = info_node->info_linear; + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; + return NULL; + } assert(len <= info_linear->info.jited_prog_len); *out_buf_len = len; return (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 131fdbe9cefda..9cb1d54b2452d 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1825,8 +1825,7 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp) node = rb_entry(next, struct bpf_prog_info_node, rb_node); next = rb_next(&node->rb_node); - __bpf_event__print_bpf_prog_info(&node->info_linear->info, - env, fp); + __bpf_event__print_bpf_prog_info(node->info_linear, env, fp); } up_read(&env->bpf_progs.lock); From ca297485271c62cae7e87bf64b9ca41207b314f4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 13 Jun 2026 15:55:33 -0300 Subject: [PATCH 1414/3297] perf dso: Set standard errno on decompression failure [ Upstream commit 1a5f9334a45a6b0c1cd7341cc72a3b87adad1d27 ] dso__get_filename() sets errno to a negative custom DSO_LOAD_ERRNO value when kernel module decompression fails: errno = *dso__load_errno(dso); /* e.g. -9996 */ The caller __open_dso() then computes fd = -errno, producing a large positive value (9996) that looks like a valid file descriptor. This can cause close_data_fd() to close an unrelated fd used by another subsystem. Set errno to EIO instead. The detailed error code is already stored in dso__load_errno(dso) for diagnostic messages. Fixes: 1d6b3c9ba756a513 ("perf tools: Decompress kernel module when reading DSO data") Reported-by: sashiko-bot Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin --- tools/perf/util/dso.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index d7b9b1f7f4f4e..6fa104752de2a 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -599,7 +599,13 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, size_t len = sizeof(newpath); if (dso__decompress_kmodule_path(dso, name, newpath, len) < 0) { - errno = *dso__load_errno(dso); + /* + * Use a standard errno value, not the negative custom + * DSO_LOAD_ERRNO stored in dso__load_errno(dso): + * __open_dso() computes fd = -errno, so a negative + * errno produces a positive fd that looks valid. + */ + errno = EIO; goto out; } From 7bcd4ef375fa369407fc6c433084ad66d4492102 Mon Sep 17 00:00:00 2001 From: Sen Wang Date: Tue, 16 Jun 2026 18:33:22 -0500 Subject: [PATCH 1415/3297] ASoC: tlv320aic3x: restrict CLKDIV bypass Q values in dual-rate mode [ Upstream commit fdf043f5f3bae150b678feae3d7bb1beed87ec14 ] The datasheet documents that when the PLL is disabled and dual-rate mode is enabled, only Q values {4, 8, 9, 12, 16} are valid for the CLKDIV bypass path; all other Q values produce invalid bitclock output. The existing loop iterates Q from 2 to 17 without this restriction, causing silent audio failure when an out-of-spec Q is picked. Restrict the Q search to the allowed set in dual-rate mode. Fixes: 4f9c16ccfa26 ("[ALSA] soc - tlv320aic3x - revisit clock setup") Suggested-by: Mir Jeffres Signed-off-by: Sen Wang Link: https://patch.msgid.link/20260616233322.873081-1-sen@ti.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/tlv320aic3x.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index eea8ca285f8e0..0e5e8002cd012 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + static const u8 dual_rate_q[] = {4, 8, 9, 12, 16}; struct snd_soc_component *component = dai->component; struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u16 d, pll_d = 1; + bool dual_rate; int clk; int width = aic3x->slot_width; @@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, /* Fsref can be 44100 or 48000 */ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000; + dual_rate = params_rate(params) >= 64000; /* Try to find a value for Q which allows us to bypass the PLL and * generate CODEC_CLK directly. */ - for (pll_q = 2; pll_q < 18; pll_q++) - if (aic3x->sysclk / (128 * pll_q) == fsref) { - bypass_pll = 1; - break; + if (dual_rate) { + for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) { + pll_q = dual_rate_q[i]; + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } } + } else { + for (pll_q = 2; pll_q < 18; pll_q++) + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } + } if (bypass_pll) { pll_q &= 0xf; @@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, * right DAC to right channel input */ data = (LDAC2LCH | RDAC2RCH); data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000; - if (params_rate(params) >= 64000) + if (dual_rate) data |= DUAL_RATE_MODE; snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data); /* codec sample rate select */ data = (fsref * 20) / params_rate(params); - if (params_rate(params) < 64000) + if (!dual_rate) data /= 2; data /= 5; data -= 2; From 0e27d92f69b8e4c750a4b181cc05a86b9338c4f5 Mon Sep 17 00:00:00 2001 From: Yunxiang Li Date: Thu, 4 Jun 2026 12:59:11 -0400 Subject: [PATCH 1416/3297] drm/amdkfd: Avoid double-unpin of DOORBELL/MMIO BOs on free [ Upstream commit 3f0cc1735273a57c5116710cf0202e12152f59cc ] amdgpu_amdkfd_gpuvm_free_memory_of_gpu() unpinned DOORBELL and MMIO remap BOs (which are pinned at allocation time) before checking whether the BO is still mapped to the GPU. When the BO is still mapped, the function returns -EBUSY and leaves the BO alive, but it has already been unpinned. The BO is then unpinned again when it is finally freed during process teardown, triggering a ttm_bo_unpin() underflow warning: WARNING: CPU: 18 PID: 15066 at ttm/ttm_bo.c:650 amdttm_bo_unpin+0x6d/0x80 [amdttm] Workqueue: kfd_process_wq kfd_process_wq_release [amdgpu] RIP: 0010:amdttm_bo_unpin+0x6d/0x80 [amdttm] Call Trace: amdgpu_bo_unpin+0x1a/0x90 [amdgpu] amdgpu_amdkfd_gpuvm_unpin_bo+0x31/0xb0 [amdgpu] amdgpu_amdkfd_gpuvm_free_memory_of_gpu+0x3bf/0x460 [amdgpu] kfd_process_free_outstanding_kfd_bos+0xd4/0x170 [amdgpu] kfd_process_wq_release+0x109/0x1b0 [amdgpu] process_one_work+0x1e2/0x3b0 worker_thread+0x50/0x3a0 kthread+0xdd/0x100 ret_from_fork+0x29/0x50 Move the unpin after the mapped_to_gpu_memory check so it only happens once we are committed to freeing the BO. Fixes: d25e35bc26c3 ("drm/amdgpu: Pin MMIO/DOORBELL BO's in GTT domain") Signed-off-by: Yunxiang Li Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 927c5b2defb9b09856444d94bebfd056a002bd75) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 0ab85d0a6a43e..285c35545ebbf 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1896,13 +1896,6 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( mutex_lock(&mem->lock); - /* Unpin MMIO/DOORBELL BO's that were pinned during allocation */ - if (mem->alloc_flags & - (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | - KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { - amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); - } - mapped_to_gpu_memory = mem->mapped_to_gpu_memory; is_imported = mem->is_imported; mutex_unlock(&mem->lock); @@ -1916,6 +1909,15 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( return -EBUSY; } + /* At this point the BO is guaranteed to be freed, so unpin the + * MMIO/DOORBELL BOs that were pinned during allocation. + */ + if (mem->alloc_flags & + (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | + KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { + amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); + } + /* Make sure restore workers don't access the BO any more */ mutex_lock(&process_info->lock); list_del(&mem->validate_list); From 211bb9d8f17c4136e72843bb00f9ed6fb12618b8 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Thu, 11 Jun 2026 08:44:38 -0700 Subject: [PATCH 1417/3297] drm/amd/display: Fix mem_type change detection for async flips [ Upstream commit 8e792f018e10e68f488f279fbd4f38009a2e066d ] [Why] amdgpu_dm_crtc_mem_type_changed() fetches the "old" and "new" plane state with two drm_atomic_get_plane_state() calls, which both return the new state. It compares a state against itself, so it never detects a mem_type change and never rejects the async flip. On DCN 3.0.1, this shows up as intermittent corruption when a single DCC plane is scanned out with immediate flips under gamescope and its buffer moves between the VRAM carveout and GTT. [How] Use drm_atomic_get_old_plane_state() and drm_atomic_get_new_plane_state() to compare the actual old and new states. These return NULL rather than an error pointer for a plane that is not part of the commit, so the IS_ERR() check becomes a NULL check that skips those planes, such as an unmodified cursor still in the CRTC's plane_mask. Fixes: 4caacd1671b7 ("drm/amd/display: Do not elevate mem_type change to full update") Reviewed-by: Harry Wentland Reviewed-by: Melissa Wen Signed-off-by: Matthew Schwartz Signed-off-by: Alex Deucher (cherry picked from commit 13158e5dbd896281f3e9982b5437cffa5fd621b2) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index ae10e8365ae15..a2ca1de6b43cf 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -12177,13 +12177,11 @@ static bool amdgpu_dm_crtc_mem_type_changed(struct drm_device *dev, struct drm_plane_state *new_plane_state, *old_plane_state; drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) { - new_plane_state = drm_atomic_get_plane_state(state, plane); - old_plane_state = drm_atomic_get_plane_state(state, plane); + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + old_plane_state = drm_atomic_get_old_plane_state(state, plane); - if (IS_ERR(new_plane_state) || IS_ERR(old_plane_state)) { - drm_err(dev, "Failed to get plane state for plane %s\n", plane->name); - return false; - } + if (!old_plane_state || !new_plane_state) + continue; if (old_plane_state->fb && new_plane_state->fb && get_mem_type(old_plane_state->fb) != get_mem_type(new_plane_state->fb)) From 96ac562a9ea3020981f536384711190841c81aa8 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 Jun 2026 21:22:04 -0500 Subject: [PATCH 1418/3297] drm/amdkfd: fix list_del corruption in kfd_criu_resume_svm [ Upstream commit 8fa5655da368d0306c03e9dc9cda8ae2a7840926 ] The cleanup tail of kfd_criu_resume_svm() walks svms->criu_svm_metadata_list and kfree()s each struct criu_svm_metadata without removing it from the list. The list head is left pointing at freed kmalloc-96 objects. A second AMDKFD_IOC_CRIU_OP from the same process re-enters: list_empty() reads the dangling ->next (use-after-free), the loop walks freed entries, and each is kfree()'d again (double-free). This is reachable by an unprivileged render-group user via /dev/kfd with no capabilities required. Add list_del() before the kfree() so the list is properly emptied. The list_for_each_entry_safe() iterator already caches the next pointer, so unlinking during the walk is safe. Fixes: 2a909ae71871 ("drm/amdkfd: CRIU resume shared virtual memory ranges") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 6322d278a298e2c1430b9d2697743d3a04b788b1) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 04e4bf41ccf72..202ed1c456320 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -4063,6 +4063,7 @@ int kfd_criu_resume_svm(struct kfd_process *p) list_for_each_entry_safe(criu_svm_md, next, &svms->criu_svm_metadata_list, list) { pr_debug("freeing criu_svm_md[]\n\tstart: 0x%llx\n", criu_svm_md->data.start_addr); + list_del(&criu_svm_md->list); kfree(criu_svm_md); } From 290ad0a548915bc2f457e6d315e1537f00e16497 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 8 Jun 2026 16:22:35 -0300 Subject: [PATCH 1419/3297] drm/amdgpu: initialize irq.lock spinlock earlier [ Upstream commit a2b270c0ecf6d95bcd14ef4c20d0301a88143ff5 ] If there is an early failure during amdgpu probe, like missing firmware, it will end up calling amdgpu_irq_disable_all, which takes irq.lock spinlock without it being initialized. Initializing irq.lock earlier at amdgpu_device_init fixes the issue. [ 79.334079] INFO: trying to register non-static key. [ 79.334081] The code is fine but needs lockdep annotation, or maybe [ 79.334083] you didn't initialize this object before use? [ 79.334084] turning off the locking correctness validator. [ 79.334088] CPU: 2 UID: 0 PID: 1819 Comm: bash Not tainted 7.1.0-rc5-gfd06300b2348 #96 PREEMPT 8e8f461221633dae3c832d6689eaf0546c0ed4cd [ 79.334092] Hardware name: Valve Jupiter/Jupiter, BIOS F7A0133 08/05/2024 [ 79.334094] Call Trace: [ 79.334095] [ 79.334097] dump_stack_lvl+0x5d/0x80 [ 79.334103] register_lock_class+0x7af/0x7c0 [ 79.334109] __lock_acquire+0x416/0x2610 [ 79.334114] lock_acquire+0xcf/0x310 [ 79.334117] ? amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.334503] ? _raw_spin_lock_irqsave+0x53/0x60 [ 79.334508] _raw_spin_lock_irqsave+0x3f/0x60 [ 79.334510] ? amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.334881] amdgpu_irq_disable_all+0x3b/0xf0 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.335240] amdgpu_device_fini_hw+0x90/0x32c [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.335704] amdgpu_driver_load_kms.cold+0x22/0x44 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.336159] amdgpu_pci_probe+0x204/0x440 [amdgpu c88bab43d391d519ad0d5c8e5a099b4aceefa180] [ 79.336494] local_pci_probe+0x3c/0x80 [ 79.336500] pci_call_probe+0x55/0x2e0 [ 79.336505] ? _raw_spin_unlock+0x2d/0x50 [ 79.336508] ? pci_match_device+0x157/0x180 [ 79.336512] pci_device_probe+0x9b/0x170 [ 79.336516] really_probe+0xd5/0x370 [ 79.336521] __driver_probe_device+0x84/0x150 [ 79.336525] device_driver_attach+0x47/0xb0 [ 79.336528] bind_store+0x73/0xc0 [ 79.336531] kernfs_fop_write_iter+0x176/0x250 [ 79.336536] vfs_write+0x24d/0x560 [ 79.336542] ksys_write+0x71/0xe0 [ 79.336546] do_syscall_64+0x122/0x710 [ 79.336550] ? do_syscall_64+0xd1/0x710 [ 79.336553] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 79.336557] RIP: 0033:0x7f92fd675006 [ 79.336561] Code: 5d e8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 75 19 83 e2 39 83 fa 08 75 11 e8 26 ff ff ff 66 0f 1f 44 00 00 48 8b 45 10 0f 05 <48> 8b 5d f8 c9 c3 0f 1f 40 00 f3 0f 1e fa 55 48 89 e5 48 83 ec 08 [ 79.336562] RSP: 002b:00007ffe4fa867a0 EFLAGS: 00000202 ORIG_RAX: 0000000000000001 [ 79.336565] RAX: ffffffffffffffda RBX: 000000000000000d RCX: 00007f92fd675006 [ 79.336567] RDX: 000000000000000d RSI: 000055b2dfce59b0 RDI: 0000000000000001 [ 79.336568] RBP: 00007ffe4fa867c0 R08: 0000000000000000 R09: 0000000000000000 [ 79.336569] R10: 0000000000000000 R11: 0000000000000202 R12: 000000000000000d [ 79.336570] R13: 000055b2dfce59b0 R14: 00007f92fd7ca5c0 R15: 000055b2dfdbaf70 [ 79.336574] Fixes: 9950cda2a018 ("drm/amdgpu: drop the drm irq pre/post/un install callbacks") Reviewed-by: Tvrtko Ursulin Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Alex Deucher (cherry picked from commit 7dba3e10ecdeec85208e255853fcd3890880b10e) Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index c22aea46efcdf..c2608d91bcd24 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4504,6 +4504,8 @@ int amdgpu_device_init(struct amdgpu_device *adev, mutex_init(&adev->vcn.workload_profile_mutex); mutex_init(&adev->userq_mutex); + spin_lock_init(&adev->irq.lock); + amdgpu_device_init_apu_flags(adev); r = amdgpu_device_check_arguments(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 8112ffc85995e..8d7f97eed5a90 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -274,8 +274,6 @@ int amdgpu_irq_init(struct amdgpu_device *adev) unsigned int irq, flags; int r; - spin_lock_init(&adev->irq.lock); - /* Enable MSI if not disabled by module parameter */ adev->irq.msi_enabled = false; From 3d45d40b872aec0073d4cf08956a6c9b87c5e396 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Mon, 15 Jun 2026 08:37:04 +0530 Subject: [PATCH 1420/3297] octeontx2-pf: Fix leak of SQ timestamp buffer on teardown [ Upstream commit a056db30de92945ff8ee6033096678bfbae878e3 ] The send-queue timestamp ring is allocated with qmem_alloc() when timestamping is used, but otx2_free_sq_res() never freed sq->timestamps, leaking that memory across ifdown and device removal. Add the missing qmem_free() alongside the other SQ companion buffers. Fixes: c9c12d339d93 ("octeontx2-pf: Add support for PTP clock") Cc: Aleksey Makarov Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260615030704.504536-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index fa23d42d1318b..e0369466c9fd4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1578,6 +1578,7 @@ static void otx2_free_sq_res(struct otx2_nic *pf) qmem_free(pf->dev, sq->sqe_ring); qmem_free(pf->dev, sq->cpt_resp); qmem_free(pf->dev, sq->tso_hdrs); + qmem_free(pf->dev, sq->timestamps); kfree(sq->sg); kfree(sq->sqb_ptrs); } From a6cfb924ad74efce254e99c197d2e3863de70868 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 15 Jun 2026 17:30:46 -0700 Subject: [PATCH 1421/3297] net: psample: fix info leak in PSAMPLE_ATTR_DATA [ Upstream commit aedd02af1f8b0bceb7f42f5a21c41634ca9ed390 ] psample open codes nla_put() presumably to avoid wiping the data with 0s just to override it with packet data. This open coding is missing clearing the pad, however, each netlink attr is padded to 4B and data_len may not be divisible by 4B. Fixes: 6ae0a6286171 ("net: Introduce psample, a new genetlink channel for packet sampling") Reported-by: Weiming Shi Reviewed-by: Jiri Pirko Link: https://patch.msgid.link/20260616003046.1099490-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/psample/psample.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/psample/psample.c b/net/psample/psample.c index 25f92ba0840c6..2d3bda59fa0d2 100644 --- a/net/psample/psample.c +++ b/net/psample/psample.c @@ -476,15 +476,17 @@ void psample_sample_packet(struct psample_group *group, goto error; if (data_len) { - int nla_len = nla_total_size(data_len); + int nla_len = nla_attr_size(data_len); struct nlattr *nla; nla = skb_put(nl_skb, nla_len); nla->nla_type = PSAMPLE_ATTR_DATA; - nla->nla_len = nla_attr_size(data_len); + nla->nla_len = nla_len; if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) goto error; + + skb_put_zero(nl_skb, nla_padlen(data_len)); } #ifdef CONFIG_INET From 207bb4ce8fe7de961ae7bb33569ad2cd61f44954 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 15 Jun 2026 15:36:30 -0400 Subject: [PATCH 1422/3297] sctp: hold socket lock when dumping endpoints in sctp_diag [ Upstream commit 7d8297e26b4e20b5d1c3c3fe51fe81a1c7fbc823 ] SCTP_DIAG endpoint dumping was traversing endpoint address lists without holding lock_sock(), while those lists could change concurrently via socket operations (e.g., bindx changes). This creates a race where nla_reserve() counts addresses under RCU protection, but the subsequent copy may see fewer entries, potentially leaking uninitialized memory to userspace. Fix this by: - Taking a reference on each endpoint during hash traversal - Moving socket operations (lock_sock()) outside read_lock_bh() - Serializing address list access during dump - Reworking sctp_for_each_endpoint() to support restart-based traversal with (net, pos) tracking Also: - Add WARN_ON_ONCE() for inconsistent address counts - Fix idiag_states filtering for LISTEN vs association cases - Skip dumping endpoints being freed (ep->base.dead) - Move dump position tracking into iterator, removing cb->args[4] and its comment for sctp_ep_dump()., - Update the comment for cb->args[4] and remove the comment for unused cb->args[5] for sctp_sock_dump(). Note: traversal is restart-based and may re-scan buckets multiple times, but this is acceptable due to small bucket sizes and required to support sleeping-safe callbacks. This issue was reported by Nico Yip (@_cyeaa_) working with TrendAI Zero Day Initiative. Reported-by: Zero Day Initiative Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Signed-off-by: Xin Long Link: https://patch.msgid.link/4c1b49ab87e0f7d552ebd8172b364b1994e913c9.1781552190.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/sctp/sctp.h | 3 +- net/sctp/diag.c | 67 ++++++++++++++++++++--------------------- net/sctp/socket.c | 29 +++++++++++++----- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index e96d1bd087f62..bf1e63d84159f 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -112,7 +112,8 @@ int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, const union sctp_addr *paddr, void *p, int dif); int sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done, struct net *net, int *pos, void *p); -int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p); +int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), + struct net *net, int *pos, void *p); int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc, struct sctp_info *info); diff --git a/net/sctp/diag.c b/net/sctp/diag.c index d758f5c3e06e5..c2a0de2adf6fd 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -92,6 +92,7 @@ static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb, if (!--addrcnt) break; } + WARN_ON_ONCE(addrcnt); rcu_read_unlock(); return 0; @@ -373,42 +374,39 @@ static int sctp_ep_dump(struct sctp_endpoint *ep, void *p) struct sk_buff *skb = commp->skb; struct netlink_callback *cb = commp->cb; const struct inet_diag_req_v2 *r = commp->r; - struct net *net = sock_net(skb->sk); struct inet_sock *inet = inet_sk(sk); int err = 0; - if (!net_eq(sock_net(sk), net)) + lock_sock(sk); + if (ep->base.dead) goto out; - if (cb->args[4] < cb->args[1]) - goto next; - - if (!(r->idiag_states & TCPF_LISTEN) && !list_empty(&ep->asocs)) - goto next; + /* Skip eps with assocs if non-LISTEN states were requested, since + * they'll be dumped by sctp_sock_dump() during assoc traversal. + */ + if ((r->idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)) && + !list_empty(&ep->asocs)) + goto out; if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family) - goto next; + goto out; if (r->id.idiag_sport != inet->inet_sport && r->id.idiag_sport) - goto next; + goto out; if (r->id.idiag_dport != inet->inet_dport && r->id.idiag_dport) - goto next; - - if (inet_sctp_diag_fill(sk, NULL, skb, r, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - cb->nlh, commp->net_admin) < 0) { - err = 2; goto out; - } -next: - cb->args[4]++; + + err = inet_sctp_diag_fill(sk, NULL, skb, r, + sk_user_ns(NETLINK_CB(cb->skb).sk), + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + cb->nlh, commp->net_admin); out: + release_sock(sk); return err; } @@ -479,41 +477,40 @@ static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, .r = r, .net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN), }; - int pos = cb->args[2]; + int pos; /* eps hashtable dumps * args: * 0 : if it will traversal listen sock * 1 : to record the sock pos of this time's traversal - * 4 : to work as a temporary variable to traversal list */ if (cb->args[0] == 0) { - if (!(idiag_states & TCPF_LISTEN)) - goto skip; - if (sctp_for_each_endpoint(sctp_ep_dump, &commp)) - goto done; -skip: + if (idiag_states & TCPF_LISTEN) { + pos = cb->args[1]; + if (sctp_for_each_endpoint(sctp_ep_dump, net, &pos, + &commp)) { + cb->args[1] = pos; + return; + } + } cb->args[0] = 1; cb->args[1] = 0; - cb->args[4] = 0; } + if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) + return; + /* asocs by transport hashtable dump * args: * 1 : to record the assoc pos of this time's traversal * 2 : to record the transport pos of this time's traversal * 3 : to mark if we have dumped the ep info of the current asoc - * 4 : to work as a temporary variable to traversal list - * 5 : to save the sk we get from travelsing the tsp list. + * 4 : to track position within ep->asocs list in sctp_sock_dump() */ - if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) - goto done; - + pos = cb->args[2]; sctp_transport_traverse_process(sctp_sock_filter, sctp_sock_dump, net, &pos, &commp); cb->args[2] = pos; - -done: cb->args[1] = cb->args[4]; cb->args[4] = 0; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 8a2622da56d23..7a641cb847384 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5310,24 +5310,39 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net, } int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), - void *p) { - int err = 0; - int hash = 0; - struct sctp_endpoint *ep; + struct net *net, int *pos, void *p) { + int err, hash = 0, idx = 0, start; struct sctp_hashbucket *head; + struct sctp_endpoint *ep; for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize; hash++, head++) { + start = idx; +again: read_lock_bh(&head->lock); sctp_for_each_hentry(ep, &head->chain) { - err = cb(ep, p); - if (err) + if (sock_net(ep->base.sk) != net) + continue; + if (idx++ >= *pos) { + sctp_endpoint_hold(ep); break; + } } read_unlock_bh(&head->lock); + + if (ep) { + err = cb(ep, p); + sctp_endpoint_put(ep); + if (err) + return err; + (*pos)++; + + idx = start; + goto again; + } } - return err; + return 0; } EXPORT_SYMBOL_GPL(sctp_for_each_endpoint); From 25a867aa5e67a84333fa6e5c21292c5bcff86b90 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 22:51:25 -0400 Subject: [PATCH 1423/3297] ALSA: usb-audio: qcom: reject stream disable with no active interface [ Upstream commit bdb640be82e645e2828731648f485224d0c2587b ] handle_uaudio_stream_req() resolves an interface index with info_idx_from_ifnum(), which returns -EINVAL when no interface matches. The enable branch and the response: cleanup label both guard against a negative index, but the disable branch does not: it forms info = &uadev[pcm_card_num].info[info_idx] and dereferences it. uadev[].info is a pointer allocated only when a stream is first enabled, so a negative info_idx on the disable path is unsafe in two ways: - If the card was never enabled, .info is NULL and &info[-EINVAL] is a wild pointer; reading info->data_ep_pipe faults (kernel oops). - If the card was enabled at least once (.info allocated) and the disable names an interface that does not match, &info[-EINVAL] points before the allocation; info->data_ep_pipe / info->sync_ep_pipe are an out-of-bounds slab read and, when non-zero, an out-of-bounds 4-byte write (both pipe fields are cleared to 0). That is memory corruption, not just a NULL dereference. The request is reachable from unprivileged local userspace over AF_QIPCRTR. Reject a disable request with no resolved interface, matching the guard the enable path already has. Fixes: 326bbc348298a ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618025126.1862954-2-michael.bommarito@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 26b632baefadc..09a699649f8d7 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1648,6 +1648,11 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, subs->opened = 0; } } else { + if (info_idx < 0) { + ret = -EINVAL; + goto response; + } + info = &uadev[pcm_card_num].info[info_idx]; if (info->data_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, From 53c23d56b46b1ae51574d92110895086aead7ad7 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 22:51:26 -0400 Subject: [PATCH 1424/3297] ALSA: usb-audio: qcom: clear opened when stream enable fails [ Upstream commit 3c7af07943b2718087ae791cad450af5cf646d90 ] On enable, subs->opened is set before the service_interval is validated; an invalid interval jumps to the response label without clearing it, so the substream is wedged at -EBUSY until a disable or disconnect. Clear subs->opened on the enable error path. Fixes: 326bbc348298a ("ALSA: usb-audio: qcom: Introduce QC USB SND offloading support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618025126.1862954-3-michael.bommarito@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/qcom/qc_audio_offload.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 09a699649f8d7..da2759f2ee866 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1626,8 +1626,13 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, if (req_msg->service_interval_valid) { ret = get_data_interval_from_si(subs, req_msg->service_interval); - if (ret == -EINVAL) + if (ret == -EINVAL) { + if (req_msg->enable) { + guard(mutex)(&chip->mutex); + subs->opened = 0; + } goto response; + } datainterval = ret; } From f37f2f804796e1026d838af39aa8e8ec3d9a49ca Mon Sep 17 00:00:00 2001 From: Mark Tomlinson Date: Thu, 30 Apr 2026 14:16:28 +1200 Subject: [PATCH 1425/3297] PCI: iproc: Restore .map_irq() for the platform bus driver [ Upstream commit 3c2e6cc6affa8acdb99a580be1f8f297edf54204 ] Commit b64aa11eb2dd ("PCI: Set bridge map_irq and swizzle_irq to default functions") moved the assignment of default .map_irq() callback to devm_of_pci_bridge_init() and removed the initialization of 'iproc_pcie::map_irq' in platform bus driver. This led to the callback getting assigned the NULL pointer for platform bus driver, thereby breaking the INTx functionality, since 'iproc_pcie::map_irq' overrides the 'pci_host_bridge::map_irq' callback in iproc_pcie_setup(). This issue only affected the iproc platform bus driver as this driver relies on the default callback for non-PAXC controllers. iproc-brcm driver was already providing the custom mapping function, so it was unaffected. Restore the original (and intended) behaviour to use the default map_irq function by removing the local 'iproc_pcie::map_irq' pointer and directly assigning the 'pci_host_bridge::map_irq' callback in iproc-bcma driver. This ensures that the default 'map_irq' callback is used for platform bus driver and only iproc-brcm driver overrides it with a custom one. Fixes: b64aa11eb2dd ("PCI: Set bridge map_irq and swizzle_irq to default functions") Signed-off-by: Mark Tomlinson [mani: commit log] Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Ray Jui Link: https://patch.msgid.link/20260430021628.1343154-1-mark.tomlinson@alliedtelesis.co.nz Signed-off-by: Sasha Levin --- drivers/pci/controller/pcie-iproc-bcma.c | 2 +- drivers/pci/controller/pcie-iproc-platform.c | 2 +- drivers/pci/controller/pcie-iproc.c | 1 - drivers/pci/controller/pcie-iproc.h | 2 -- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/pcie-iproc-bcma.c b/drivers/pci/controller/pcie-iproc-bcma.c index 99a99900444de..593418c2bc3aa 100644 --- a/drivers/pci/controller/pcie-iproc-bcma.c +++ b/drivers/pci/controller/pcie-iproc-bcma.c @@ -64,7 +64,7 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev) if (ret) return ret; - pcie->map_irq = iproc_bcma_pcie_map_irq; + bridge->map_irq = iproc_bcma_pcie_map_irq; bcma_set_drvdata(bdev, pcie); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 0cb78c583c7ea..4c9a0c4bb923e 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -98,7 +98,7 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev) switch (pcie->type) { case IPROC_PCIE_PAXC: case IPROC_PCIE_PAXC_V2: - pcie->map_irq = NULL; + bridge->map_irq = NULL; break; default: break; diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 22134e95574bd..5aa677f81e4fa 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1514,7 +1514,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->ops = &iproc_pcie_ops; host->sysdata = pcie; - host->map_irq = pcie->map_irq; ret = pci_host_probe(host); if (ret < 0) { diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h index 969ded03b8c2d..c4443f236ca3b 100644 --- a/drivers/pci/controller/pcie-iproc.h +++ b/drivers/pci/controller/pcie-iproc.h @@ -61,7 +61,6 @@ struct iproc_msi; * @base_addr: PCIe host controller register base physical address * @mem: host bridge memory window resource * @phy: optional PHY device that controls the Serdes - * @map_irq: function callback to map interrupts * @ep_is_internal: indicates an internal emulated endpoint device is connected * @iproc_cfg_read: indicates the iProc config read function should be used * @rej_unconfig_pf: indicates the root complex needs to detect and reject @@ -91,7 +90,6 @@ struct iproc_pcie { phys_addr_t base_addr; struct resource mem; struct phy *phy; - int (*map_irq)(const struct pci_dev *, u8, u8); bool ep_is_internal; bool iproc_cfg_read; bool rej_unconfig_pf; From 0d95587d662a5535e163d7eeb3bf4c02ad6cc4fb Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 18 Jun 2026 09:19:30 +0100 Subject: [PATCH 1426/3297] spi: rpc-if: Use correct device for hardware reinitialization on resume [ Upstream commit 7b25dbafa2fce50b1a48c1d057adb35da3563f9b ] rpcif_spi_resume() currently passes the SPI controller device to rpcif_hw_init(), but the function should be called with the RPC interface device. Retrieve the rpcif private data from the SPI controller and pass rpc->dev instead. Also propagate the return value of rpcif_hw_init() so that a failure during resume is properly reported rather than silently ignored. Fixes: ad4728740bd6 ("spi: rpc-if: Add resume support for RZ/G3E") Signed-off-by: Quang Nguyen Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260618081932.172168-1-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-rpc-if.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index 6edc0c4db854d..c2f9c86c1d8d4 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -206,8 +206,12 @@ static int rpcif_spi_suspend(struct device *dev) static int rpcif_spi_resume(struct device *dev) { struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rpcif *rpc = spi_controller_get_devdata(ctlr); + int ret; - rpcif_hw_init(dev, false); + ret = rpcif_hw_init(rpc->dev, false); + if (ret) + return ret; return spi_controller_resume(ctlr); } From c7fc9adf4e006155f7f2aeda052fbcde25cdcc49 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 15 Jun 2026 21:28:37 -0700 Subject: [PATCH 1427/3297] virtio-net: fix len check in receive_big() [ Upstream commit 9e5ad06ea826322ce8c58b4a68442a96f600c3c4 ] receive_big() bounds the device-announced length by (big_packets_num_skbfrags + 1) * PAGE_SIZE. That is still too loose: add_recvbuf_big() sets sg[1] to start at offset sizeof(struct padded_vnet_hdr) into the first page, so the chain actually carries hdr_len + (PAGE_SIZE - sizeof(padded_vnet_hdr)) + big_packets_num_skbfrags * PAGE_SIZE bytes -- 20 bytes less than the check allows for the common hdr_len == 12 case. A malicious virtio backend can announce a len in that gap. page_to_skb() then walks one frag past the page chain, storing a NULL page->private into skb_shinfo()->frags[MAX_SKB_FRAGS], which is both an out-of-bounds write past the static frag array and a NULL frag handed up the rx path. Bound len by the size add_recvbuf_big() actually advertised. Fixes: 0c716703965f ("virtio-net: fix received length check in big packets") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Xuan Zhuo Acked-by: Michael S. Tsirkin Reviewed-by: Bui Quang Minh Link: https://patch.msgid.link/20260616042837.2249468-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 8f7872d65a616..dbdba0a42efe9 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2105,15 +2105,18 @@ static struct sk_buff *receive_big(struct net_device *dev, struct virtnet_rq_stats *stats) { struct page *page = buf; + unsigned long max_len; struct sk_buff *skb; + max_len = (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE - + sizeof(struct padded_vnet_hdr) + vi->hdr_len; + /* Make sure that len does not exceed the size allocated in * add_recvbuf_big. */ - if (unlikely(len > (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE)) { + if (unlikely(len > max_len)) { pr_debug("%s: rx error: len %u exceeds allocated size %lu\n", - dev->name, len, - (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE); + dev->name, len, max_len); goto err; } From 0dafdaaf8684bfb6c74d6580eaa8f5636382d33c Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 16 Jun 2026 13:54:30 +0300 Subject: [PATCH 1428/3297] dpaa2-switch: fix VLAN upper check not rejecting bridge join [ Upstream commit ed2294f94e34e97342850c40b320833d881c3819 ] The blamed commit refactored the prechangeupper event handling but failed to actually return an error in case dpaa2_switch_prevent_bridging_with_8021q_upper() detected a 802.1q upper on a port which tries to join a bridge. Fix this by returning err instead of 0. Fixes: 45035febc495 ("net: dpaa2-switch: refactor prechangeupper sanity checks") Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260616105430.3725910-1-ioana.ciornei@nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index e212a014c8d41..28ac76b8783c3 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2177,7 +2177,7 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, if (err) { NL_SET_ERR_MSG_MOD(extack, "Cannot join a bridge while VLAN uppers are present"); - return 0; + return err; } netdev_for_each_lower_dev(upper_dev, other_dev, iter) { From 02c884d9aaca33ad25d517660b021e00f0df0b48 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Tue, 16 Jun 2026 14:06:32 +0300 Subject: [PATCH 1429/3297] devlink: Fix parent ref leak in devl_rate_node_create() [ Upstream commit ba45106342bbdd905651cb9fcefb8c11871d4c25 ] In the original commit the function bails out on kstrdup failure, forgetting to decrement the refcnt of the parent. Fix that by moving the parent refcnt setting after kstrdup. Fixes: caba177d7f4d ("devlink: Enable creation of the devlink-rate nodes from the driver") Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616110633.1449432-2-cratiu@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/devlink/rate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/devlink/rate.c b/net/devlink/rate.c index d157a8419bcad..3eaf1af3b59cf 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -724,11 +724,6 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, if (!rate_node) return ERR_PTR(-ENOMEM); - if (parent) { - rate_node->parent = parent; - refcount_inc(&rate_node->parent->refcnt); - } - rate_node->type = DEVLINK_RATE_TYPE_NODE; rate_node->devlink = devlink; rate_node->priv = priv; @@ -739,6 +734,11 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name, return ERR_PTR(-ENOMEM); } + if (parent) { + rate_node->parent = parent; + refcount_inc(&rate_node->parent->refcnt); + } + refcount_set(&rate_node->refcnt, 1); list_add(&rate_node->list, &devlink->rate_list); devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW); From 5ffb2b4987cc909eb6c9e6a9487c25d5a919d99e Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Tue, 16 Jun 2026 14:06:33 +0300 Subject: [PATCH 1430/3297] devlink: Fix parent ref leak on tc-bw failure [ Upstream commit ba81a8b80f042038b9f73a4e5bb135de890b59bb ] When a node is created via rate-new with tc-bw and a parent node, devlink_nl_rate_set() executes the sequence of ops. It bails out on the first failure and doesn't rollback anything. For most things that is fine (setting some numbers), but the parent set can leak if there's another failure after that. That is precisely what happens when parent setting isn't the last block in the function. After the referenced "Fixes" commit, when tc-bw fails to be set the function bails out after having set the parent and incremented its refcount. There are two callers: - devlink_nl_rate_set_doit() is fine, it just reports the error. - but devlink_nl_rate_new_doit() frees the newly created node and leaks the parent refcnt. Fix that by reordering the blocks so parent setting is last and adding a comment explaining this so future modification preserve the ordering (hopefully). Fixes: 566e8f108fc7 ("devlink: Extend devlink rate API with traffic classes bandwidth management") Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260616110633.1449432-3-cratiu@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/devlink/rate.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/devlink/rate.c b/net/devlink/rate.c index 3eaf1af3b59cf..de13b9818dfcc 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -487,16 +487,19 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate, devlink_rate->tx_weight = weight; } - nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; - if (nla_parent) { - err = devlink_nl_rate_parent_node_set(devlink_rate, info, - nla_parent); + if (attrs[DEVLINK_ATTR_RATE_TC_BWS]) { + err = devlink_nl_rate_tc_bw_set(devlink_rate, info); if (err) return err; } - if (attrs[DEVLINK_ATTR_RATE_TC_BWS]) { - err = devlink_nl_rate_tc_bw_set(devlink_rate, info); + /* Keep parent setting last because it takes a reference. This function + * has no rollback, so failing after taking the ref would leak it. + */ + nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME]; + if (nla_parent) { + err = devlink_nl_rate_parent_node_set(devlink_rate, info, + nla_parent); if (err) return err; } From 112b5eff24e044561ee9599a0d34e0fcea1df4e0 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Tue, 16 Jun 2026 19:52:36 +0800 Subject: [PATCH 1431/3297] net: airoha: fix foe_check_time allocation size [ Upstream commit 5c121ee635680c93d7074becf14cfbaac140f80d ] foe_check_time is declared as u16 pointer but was allocated with only ppe_num_entries bytes instead of ppe_num_entries * sizeof(u16). When airoha_ppe_foe_verify_entry() is called with hash >= ppe_num_entries/2, it writes beyond the allocated buffer, causing heap buffer overflow and potential kernel crash. Fixes: 6d5b601d52a2 ("net: airoha: ppe: Dynamically allocate foe_check_time array in airoha_ppe struct") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178161119471.2163752.14373384830691569758@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_ppe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 005128717a45c..798839aa1010b 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1538,7 +1538,8 @@ int airoha_ppe_init(struct airoha_eth *eth) return -ENOMEM; } - ppe->foe_check_time = devm_kzalloc(eth->dev, ppe_num_entries, + ppe->foe_check_time = devm_kzalloc(eth->dev, + ppe_num_entries * sizeof(*ppe->foe_check_time), GFP_KERNEL); if (!ppe->foe_check_time) return -ENOMEM; From 68af74ad696cedad71f5e7dce70de658cd2b1232 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 16 Jun 2026 15:23:03 +0200 Subject: [PATCH 1432/3297] net: macb: add TX stall timeout callback to recover from lost TSTART write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e438ec3e9e95cd3f49a8120e5f63ae3f9606e6fa ] The MACB found in the Raspberry Pi RP1 suffers from sporadic stalls on the TX queue. While the exact root cause is not yet fully understood, it is likely related to a hardware issue where a TSTART write to the NCR register is missed, preventing the transmission from being kicked off. Implement a timeout callback to handle TX queue stalls, triggering the existing restart mechanism to recover. Link: https://lore.kernel.org/all/20260514215459.36109-1-lukasz@raczylo.com/ Fixes: dc110d1b23564 ("net: cadence: macb: Add support for Raspberry Pi RP1 ethernet controller") Signed-off-by: Lukasz Raczylo Co-developed-by: Steffen Jaeckel Signed-off-by: Steffen Jaeckel Co-developed-by: Andrea della Porta Signed-off-by: Andrea della Porta Reviewed-by: Nicolai Buchwitz Reviewed-by: Théo Lebrun Link: https://patch.msgid.link/468f480454a314303bac6a54780b153f689f2267.1781598350.git.andrea.porta@suse.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/cadence/macb_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 17d4a3e039458..000796ebb6e36 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4416,6 +4416,13 @@ static int macb_setup_tc(struct net_device *dev, enum tc_setup_type type, } } +static void macb_tx_timeout(struct net_device *dev, unsigned int q) +{ + struct macb *bp = netdev_priv(dev); + + macb_tx_restart(&bp->queues[q]); +} + static const struct net_device_ops macb_netdev_ops = { .ndo_open = macb_open, .ndo_stop = macb_close, @@ -4434,6 +4441,7 @@ static const struct net_device_ops macb_netdev_ops = { .ndo_hwtstamp_set = macb_hwtstamp_set, .ndo_hwtstamp_get = macb_hwtstamp_get, .ndo_setup_tc = macb_setup_tc, + .ndo_tx_timeout = macb_tx_timeout, }; /* Configure peripheral capabilities according to device tree From c6d3bcb0f934d4297ac5fa1c8656ae40694fb601 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Tue, 16 Jun 2026 20:30:57 +0800 Subject: [PATCH 1433/3297] flow_dissector: check device type before reading ETH_ADDRS [ Upstream commit bf6e8af2c8be77489bedeae9f8a9654cb710e500 ] __skb_flow_dissect() unconditionally reads 12 bytes from eth_hdr(skb) when FLOW_DISSECTOR_KEY_ETH_ADDRS is requested. This assumes the skb has a valid Ethernet header at mac_header, which is not always the case. The problem can be triggered by: 1. Creating a TUN device in L3 mode (IFF_TUN, hard_header_len=0) 2. Attaching a multiq qdisc with a flower filter matching on eth_src 3. Sending a packet through AF_PACKET Since TUN in L3 mode has no link-layer header, mac_header points to the L3 data area. The flow dissector reads 12 bytes of uninitialized skb memory, which then propagates through fl_set_masked_key() and is used as a rhashtable lookup key in __fl_lookup(), as reported by KMSAN. Rejecting the filter in the control path (at tc filter add time) is not feasible because TC filter blocks can be shared between arbitrary devices -- a filter installed on an Ethernet device may later classify packets on a headerless device through a shared block. The device association is not fixed at filter creation time. Fix this by gating the memcpy on dev->type == ARPHRD_ETHER, which ensures only true Ethernet-framed packets have their addresses read. This is more precise than the previous hard_header_len >= 12 check, which would incorrectly pass for non-Ethernet link types like IPoIB (ARPHRD_INFINIBAND, hard_header_len=24) and FDDI (hard_header_len=21) whose L2 headers are not in Ethernet format. Additionally check skb_mac_header_was_set() to guard against the pathological case where mac_header is the unset sentinel (~0U), which would cause eth_hdr() to return a wild pointer. For the act_mirred redirect case (Ethernet packet redirected to a non-Ethernet device sharing a TC block), zeroing the key is the correct behavior: the packet is now being classified on the target device, where Ethernet address matching is not semantically meaningful. Note: on non-Ethernet devices, the zeroed key will match a filter configured with all-zero MAC addresses. This is an improvement over the previous behavior where uninitialized memory could randomly match any filter. Reported-by: syzbot+fa2f5b1fb06147be5e16@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fa2f5b1fb06147be5e16 Fixes: 67a900cc0436 ("flow_dissector: introduce support for Ethernet addresses") Signed-off-by: Yun Zhou Link: https://patch.msgid.link/20260616123057.482154-1-yun.zhou@windriver.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/flow_dissector.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 2a98f5fa74eb0..8aa4f9b4df810 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -1173,13 +1173,21 @@ bool __skb_flow_dissect(const struct net *net, if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { - struct ethhdr *eth = eth_hdr(skb); struct flow_dissector_key_eth_addrs *key_eth_addrs; key_eth_addrs = skb_flow_dissector_target(flow_dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS, target_container); - memcpy(key_eth_addrs, eth, sizeof(*key_eth_addrs)); + /* TC filter blocks can be shared across devices with + * different link types, so we cannot validate this + * when the filter is installed -- check at dissect time. + */ + if (skb && skb->dev && + skb->dev->type == ARPHRD_ETHER && + skb_mac_header_was_set(skb)) + memcpy(key_eth_addrs, eth_hdr(skb), sizeof(*key_eth_addrs)); + else + memset(key_eth_addrs, 0, sizeof(*key_eth_addrs)); } if (dissector_uses_key(flow_dissector, From a762b9865f494aa2dcee1393a7edded6c6099a05 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 17 Jun 2026 13:43:23 +0300 Subject: [PATCH 1434/3297] selftests: vlan_bridge_binding: Fix flaky operational state check [ Upstream commit 4045f1c3d68ef4b589ae2587e6ff66ce8017daf2 ] check_operstate() busy waits for up to one second for the operational state to change to the expected state. This is not enough since carrier loss events can be delayed by the kernel for up to one second (see __linkwatch_run_queue()), leading to sporadic failures. Fix by increasing the busy wait period to two seconds. Fixes: dca12e9ab760 ("selftests: net: Add a VLAN bridge binding selftest") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20260616092733.3a31be4d@kernel.org/ Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260617104323.1069457-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/vlan_bridge_binding.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/vlan_bridge_binding.sh b/tools/testing/selftests/net/vlan_bridge_binding.sh index e8c02c64e03a4..d04caa14202d0 100755 --- a/tools/testing/selftests/net/vlan_bridge_binding.sh +++ b/tools/testing/selftests/net/vlan_bridge_binding.sh @@ -64,7 +64,7 @@ check_operstate() local expect=$1; shift local operstate - operstate=$(busywait 1000 \ + operstate=$(busywait 2000 \ operstate_is "$dev" "$expect") check_err $? "Got operstate of $operstate, expected $expect" } From 4c16176fc11a61b7545464cb47c98b0c8a055fcb Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Fri, 19 Jun 2026 01:00:10 +0800 Subject: [PATCH 1435/3297] ALSA: usb-audio: Kill MIDI 2.0 URBs before freeing endpoints [ Upstream commit f199c8a8bdd54296d3458777e70fe82a78bd9817 ] MIDI 2.0 input URBs are started during snd_usb_midi_v2_create(). A later setup failure can still jump to snd_usb_midi_v2_free(), which currently frees each endpoint and its coherent URB buffers without first stopping the submitted URBs. A completion can then dereference the embedded URB context and endpoint state after they have been freed, or try to resubmit from the stale endpoint. This was observed as a KASAN slab-use-after-free in input_urb_complete(). The buggy scenario involves two paths, with each column showing the order within that path: probe error path: USB completion path: 1. start_input_streams() submits 1. The HCD still owns a input URBs. submitted input URB. 2. A later setup helper returns 2. input_urb_complete() runs an error. with urb->context in ep. 3. snd_usb_midi_v2_free() frees 3. The completion reads ep endpoint storage and URB buffers. state and can requeue URBs. Make the endpoint destructor follow the same teardown ordering used for disconnect when the endpoint has not already been disconnected: publish ep->disconnected, kill the URBs synchronously, and drain the endpoint before freeing URB buffers and endpoint storage. The guard avoids repeating the stop sequence after the normal snd_usb_midi_v2_disconnect_all() path, while still synchronizing the direct MIDI 2.0 create-error free path. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in input_urb_complete+0x37/0x1b0 Workqueue: usb_hub_wq hub_event RIP: 0010:_raw_spin_unlock_irq+0x2e/0x50 Read of size 8 Call trace: dump_stack_lvl+0x77/0xb0 print_report+0xce/0x5f0 input_urb_complete+0x37/0x1b0 (sound/usb/midi2.c:186) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __usb_hcd_giveback_urb+0x112/0x1d0 dummy_timer+0xaaa/0x19a0 lock_is_held_type+0x9a/0x110 __lock_acquire+0x467/0x28b0 mark_held_locks+0x40/0x70 _raw_spin_unlock_irqrestore+0x44/0x60 lockdep_hardirqs_on_prepare+0xbb/0x1a0 __hrtimer_run_queues+0x101/0x520 hrtimer_run_softirq+0xd0/0x130 handle_softirqs+0x15b/0x670 __irq_exit_rcu+0xd0/0x170 irq_exit_rcu+0xe/0x20 sysvec_apic_timer_interrupt+0x6c/0x80 asm_sysvec_apic_timer_interrupt+0x1a/0x20 Fixes: d9c99876868c ("ALSA: usb-audio: Create UMP blocks from USB MIDI GTBs") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260618170010.191433-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/midi2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 32a3102df15c5..50b04e5a190b6 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -470,6 +470,11 @@ static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi, static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) { list_del(&ep->list); + if (!ep->disconnected) { + ep->disconnected = 1; + kill_midi_urbs(ep, false); + drain_urb_queue(ep); + } free_midi_urbs(ep); kfree(ep); } From 679fd0bf4f8ab18a290ce60ccbf1a643df41c471 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 9 Jun 2026 06:15:53 -0700 Subject: [PATCH 1436/3297] arm64/hw_breakpoint: reject unaligned watchpoints that would truncate BAS [ Upstream commit 4cc70f75853bebac022334b6a86b953348072f74 ] hw_breakpoint_arch_parse() positions the BAS bit pattern in hw->ctrl.len with offset = hw->address & alignment_mask; /* 0..7 */ hw->ctrl.len <<= offset; ctrl.len is an 8-bit bitfield (struct arch_hw_breakpoint_ctrl::len is u32 :8), so the shift silently drops any bits past bit 7. For non-compat AArch64 watchpoints the offset is unbounded relative to ctrl.len: a perf_event_open(PERF_TYPE_BREAKPOINT) caller asking for HW_BREAKPOINT_W with bp_addr=page+1 and bp_len=HW_BREAKPOINT_LEN_8 ends up with 0xff << 1 = 0x1fe, stored as 0xfe. The kernel programs WCR.BAS=0xfe and the hardware watches bytes [1..7] instead of the requested [1..8] -- the eighth byte is silently dropped. The syscall still returns success, leaving userspace to discover the gap by empirical probing. The same class affects HW_BREAKPOINT_LEN_{2,4} when offset pushes the high BAS bit past bit 7 (e.g. LEN_4 with offset=5 yields 0xe0 instead of 0x1e0). No memory-safety impact -- the value is masked into 8 bits before encoding -- but debuggers and perf users observe missed events on bytes they thought they were watching. The AArch32 branch immediately above already rejects unrepresentable (offset, len) combinations via an explicit switch. Mirror that for the non-compat branch by checking that the shifted pattern fits in the BAS field, returning -EINVAL when it does not. GDB and similar debuggers are unaffected by the stricter check. aarch64_linux_set_debug_regs() already treats EINVAL on NT_ARM_HW_WATCH as a downgrade signal: it clears kernel_supports_any_contiguous_range, calls aarch64_downgrade_regs() to round the BAS up to a legacy 0x01/03/0f/ff mask with an aligned base, and retries -- the same fallback path that PR-20207 introduced. The new -EINVAL is therefore reachable only from a raw perf_event_open() that pairs an unaligned base with an oversized bp_len, which is precisely the bug. Reproducer: struct perf_event_attr a = { .type = PERF_TYPE_BREAKPOINT, .size = sizeof(a), .bp_type = HW_BREAKPOINT_W, .bp_addr = (uintptr_t)(buf + 1), .bp_len = HW_BREAKPOINT_LEN_8, .exclude_kernel = 1, .exclude_hv = 1, }; int fd = perf_event_open(&a, 0, -1, -1, 0); /* before this fix: succeeds, watches 7 bytes (buf+1..buf+7) */ /* after this fix: fails with EINVAL */ Fixes: b08fb180bb88 ("arm64: Allow hw watchpoint at varied offset from base address") Signed-off-by: Breno Leitao Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/kernel/hw_breakpoint.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index ab76b36dce820..73cce8ac83681 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -559,6 +559,15 @@ int hw_breakpoint_arch_parse(struct perf_event *bp, else alignment_mask = 0x7; offset = hw->address & alignment_mask; + + /* + * BAS is an 8-bit field in WCR/BCR; the shift below would + * silently drop the high bits of ctrl.len when offset + len + * exceeds 8, programming hardware to watch fewer bytes than + * the user requested. + */ + if (((u32)hw->ctrl.len << offset) > ARM_BREAKPOINT_LEN_8) + return -EINVAL; } hw->address &= ~alignment_mask; From 7ae67f0e1c16b551a916cf6d6636ba6a32f49884 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Sat, 13 Jun 2026 15:17:47 -0700 Subject: [PATCH 1437/3297] thermal: intel: Fix dangling resources on thermal_throttle_online() failure [ Upstream commit b91d287fa7a1ba0727eed5823c6ee4924ee5fa31 ] The function thermal_throttle_add_dev() may fail and abort a CPU hotplug online operation. Since the failure occurs within the online callback, thermal_throttle_online(), the CPU hotplug framework does not invoke the corresponding offline callback. As a result, the hardware and software resources set up during the failed operation are not torn down. Since only thermal_throttle_add_dev() can fail, call it before setting up the rest of the resources. Fixes: f6656208f04e ("x86/mce/therm_throt: Optimize notifications of thermal throttle") Signed-off-by: Ricardo Neri Link: https://patch.msgid.link/20260613-rneri-directed-therm-intr-v3-1-3a26d1e47fc8@linux.intel.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/intel/therm_throt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index debc94e2dc169..7c21483c46b91 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -528,8 +528,13 @@ static int thermal_throttle_online(unsigned int cpu) { struct thermal_state *state = &per_cpu(thermal_state, cpu); struct device *dev = get_cpu_device(cpu); + int err; u32 l; + err = thermal_throttle_add_dev(dev, cpu); + if (err) + return err; + state->package_throttle.level = PACKAGE_LEVEL; state->core_throttle.level = CORE_LEVEL; @@ -547,7 +552,7 @@ static int thermal_throttle_online(unsigned int cpu) l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED); - return thermal_throttle_add_dev(dev, cpu); + return err; } static int thermal_throttle_offline(unsigned int cpu) From 45465b0e013541d5891395539fc6d3f144c5e234 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 17 Jun 2026 11:05:55 +0200 Subject: [PATCH 1438/3297] ACPI: resource: Amend kernel-doc style [ Upstream commit 78ad5c7722b7bed9d35ffc5b45eb0f12e2c22fee ] The functions are referred as func() in the kernel-doc. The % (percent) character makes the rendering for constants as described in the respective documentation. Amend all these. Fixes: 8e345c991c8c ("ACPI: Centralized processing of ACPI device resources") Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260617090555.2648709-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/resource.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index bc8050d8a6f51..56df4599d3604 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -871,7 +871,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); /** - * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). + * acpi_dev_free_resource_list - Free resource from acpi_dev_get_resources(). * @list: The head of the resource list to free. */ void acpi_dev_free_resource_list(struct list_head *list) @@ -991,7 +991,7 @@ static int __acpi_dev_get_resources(struct acpi_device *adev, * * The resultant struct resource objects are put on the list pointed to by * @list, that must be empty initially, as members of struct resource_entry - * objects. Callers of this routine should use %acpi_dev_free_resource_list() to + * objects. Callers of this routine should use acpi_dev_free_resource_list() to * free that list. * * The number of resources in the output list is returned on success, an error @@ -1032,7 +1032,7 @@ static int is_memory(struct acpi_resource *ares, void *not_used) * The resultant struct resource objects are put on the list pointed to * by @list, that must be empty initially, as members of struct * resource_entry objects. Callers of this routine should use - * %acpi_dev_free_resource_list() to free that list. + * acpi_dev_free_resource_list() to free that list. * * The number of resources in the output list is returned on success, * an error code reflecting the error condition is returned otherwise. From 3b40ebc19ad023d3d1ed8136d05969c4e6a427c0 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 16 Jun 2026 17:36:21 +0800 Subject: [PATCH 1439/3297] ACPI: IPMI: Fix inverted interface check in ipmi_bmc_gone() [ Upstream commit 71b57aca295d61276a60e131d8f62b0cc7cf1a35 ] Before commit a1a69b297e47 ("ACPI / IPMI: Fix race caused by the unprotected ACPI IPMI user"), ipmi_bmc_gone() skipped entries whose interface number did not match the SMI being removed, then killed the matching entry: if (ipmi_device->ipmi_ifnum != iface) continue; __ipmi_dev_kill(ipmi_device); That commit folded the removal block into the existing non-match test while converting the object lifetime handling, but left the comparison unchanged. The old != meant "continue past this entry"; after the refactor it meant "kill this entry". As a result, a single ACPI IPMI interface is never removed when its SMI disappears. If multiple interfaces are tracked, the first interface whose number differs from iface is removed instead, while the interface that actually disappeared remains on driver_data.ipmi_devices. The stale entry is not marked dead and can continue to be selected for ACPI IPMI transactions. It can also prevent the same ACPI handle from being registered again. Change the comparison to == so ipmi_bmc_gone() removes exactly the interface reported as gone by the SMI watcher. This restores the pre-a1a69b297e47 behavior and is the correct interface matching logic. Fixes: a1a69b297e47 ("ACPI / IPMI: Fix race caused by the unprotected ACPI IPMI user") Signed-off-by: Xu Rao Link: https://patch.msgid.link/B486593E06E6F6E0+20260616093621.1039943-1-raoxu@uniontech.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/acpi_ipmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index c672abc3a6dbb..cf0ee52d4126d 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -490,7 +490,7 @@ static void ipmi_bmc_gone(int iface) mutex_lock(&driver_data.ipmi_lock); list_for_each_entry_safe(iter, temp, &driver_data.ipmi_devices, head) { - if (iter->ipmi_ifnum != iface) { + if (iter->ipmi_ifnum == iface) { ipmi_device = iter; __ipmi_dev_kill(iter); break; From 5d17ebdf6c2368a362428799feb1e4032545a037 Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:32 +0300 Subject: [PATCH 1440/3297] ieee802154: Restore initial state on failed device_rename() in cfg802154_switch_netns() [ Upstream commit a2e06b4bef20b59446d5088e938c2be53cc4e6c6 ] Currently, the return value of device_rename() is not acted upon. To avoid an inconsistent state in case of failure, roll back the changes made before the device_rename() call. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-2-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/core.c | 45 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 89b671b12600f..84d514430e45a 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -233,31 +233,36 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, wpan_dev->netdev->netns_immutable = true; } - if (err) { - /* failed -- clean up to old netns */ - net = wpan_phy_net(&rdev->wpan_phy); - - list_for_each_entry_continue_reverse(wpan_dev, - &rdev->wpan_dev_list, - list) { - if (!wpan_dev->netdev) - continue; - wpan_dev->netdev->netns_immutable = false; - err = dev_change_net_namespace(wpan_dev->netdev, net, - "wpan%d"); - WARN_ON(err); - wpan_dev->netdev->netns_immutable = true; - } - - return err; - } - - wpan_phy_net_set(&rdev->wpan_phy, net); + if (err) + goto errout; err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev)); WARN_ON(err); + if (err) + goto errout; + + wpan_phy_net_set(&rdev->wpan_phy, net); + return 0; + +errout: + /* failed -- clean up to old netns */ + net = wpan_phy_net(&rdev->wpan_phy); + + list_for_each_entry_continue_reverse(wpan_dev, + &rdev->wpan_dev_list, + list) { + if (!wpan_dev->netdev) + continue; + wpan_dev->netdev->netns_immutable = false; + err = dev_change_net_namespace(wpan_dev->netdev, net, + "wpan%d"); + WARN_ON(err); + wpan_dev->netdev->netns_immutable = true; + } + + return err; } void cfg802154_dev_free(struct cfg802154_registered_device *rdev) From f4860dd988b1041d70595f2ed84de80f90cb7ebc Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:33 +0300 Subject: [PATCH 1441/3297] ieee802154: Avoid calling WARN_ON() on -ENOMEM in cfg802154_switch_netns() [ Upstream commit 0569f67ed6a7af838e2141da93c68e6b6013f483 ] It's pointless to call WARN_ON() in case of an allocation failure in dev_change_net_namespace() and device_rename(), since it only leads to useless splats caused by deliberate fault injections, so avoid it. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reported-by: syzbot+e0bd4e4815a910c0daa8@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/000000000000f4a1b7061f9421de@google.com/#t Reviewed-by: Kuniyuki Iwashima Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-3-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 84d514430e45a..987c633e2c549 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -228,8 +228,10 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, continue; wpan_dev->netdev->netns_immutable = false; err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d"); - if (err) + if (err) { + WARN_ON(err && err != -ENOMEM); break; + } wpan_dev->netdev->netns_immutable = true; } @@ -237,7 +239,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, goto errout; err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev)); - WARN_ON(err); + WARN_ON(err && err != -ENOMEM); if (err) goto errout; @@ -258,7 +260,7 @@ int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, wpan_dev->netdev->netns_immutable = false; err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d"); - WARN_ON(err); + WARN_ON(err && err != -ENOMEM); wpan_dev->netdev->netns_immutable = true; } From d22e278cd067968d79e4ebe543dd87dd7e7b61ac Mon Sep 17 00:00:00 2001 From: Ivan Abramov Date: Thu, 3 Apr 2025 13:19:34 +0300 Subject: [PATCH 1442/3297] ieee802154: Remove WARN_ON() in cfg802154_pernet_exit() [ Upstream commit e69ed6fc9fb3b386b5fcdb9f51623f122cee2ebd ] There's no need to call WARN_ON() in cfg802154_pernet_exit(), since every point of failure in cfg802154_switch_netns() is covered with WARN_ON(), so remove it. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 66e5c2672cd1 ("ieee802154: add netns support") Reviewed-by: Miquel Raynal Signed-off-by: Ivan Abramov Link: https://lore.kernel.org/20250403101935.991385-4-i.abramov@mt-integration.ru Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index 987c633e2c549..c0b8712018a16 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -358,7 +358,7 @@ static void __net_exit cfg802154_pernet_exit(struct net *net) rtnl_lock(); list_for_each_entry(rdev, &cfg802154_rdev_list, list) { if (net_eq(wpan_phy_net(&rdev->wpan_phy), net)) - WARN_ON(cfg802154_switch_netns(rdev, &init_net)); + cfg802154_switch_netns(rdev, &init_net); } rtnl_unlock(); } From de3bd9809af7555611334cb6a071806744430ef7 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 27 May 2026 20:18:18 +0000 Subject: [PATCH 1443/3297] ieee802154: fix kernel-infoleak in dgram_recvmsg() [ Upstream commit 4db86f8ab11b5a41bfc36680be837e6ac1375ec6 ] KMSAN reported a kernel-infoleak in move_addr_to_user(): BUG: KMSAN: kernel-infoleak in instrument_copy_to_user include/linux/instrumented.h:131 [inline] BUG: KMSAN: kernel-infoleak in _inline_copy_to_user include/linux/uaccess.h:205 [inline] BUG: KMSAN: kernel-infoleak in _copy_to_user+0xcc/0x120 lib/usercopy.c:26 instrument_copy_to_user include/linux/instrumented.h:131 [inline] _inline_copy_to_user include/linux/uaccess.h:205 [inline] _copy_to_user+0xcc/0x120 lib/usercopy.c:26 copy_to_user include/linux/uaccess.h:236 [inline] move_addr_to_user+0x2e7/0x440 net/socket.c:302 ____sys_recvmsg+0x232/0x610 net/socket.c:2925 ... Uninit was stored to memory at: ieee802154_addr_to_sa include/net/ieee802154_netdev.h:369 [inline] dgram_recvmsg+0xa09/0xbe0 net/ieee802154/socket.c:739 The issue occurs because the `pan_id` field of `struct ieee802154_addr` is left uninitialized when the address mode is `IEEE802154_ADDR_NONE`. The execution flow is as follows: 1. `__ieee802154_rx_handle_packet()` declares a local `struct ieee802154_hdr hdr` on the stack. 2. `ieee802154_hdr_pull()` calls `ieee802154_hdr_get_addr()` to parse the source and destination addresses into this structure. 3. If the address mode is `IEEE802154_ADDR_NONE`, `ieee802154_hdr_get_addr()` previously only set the `mode` field, leaving the `pan_id` field containing uninitialized stack memory. 4. This uninitialized `pan_id` is later copied into a `struct sockaddr_ieee802154` in `dgram_recvmsg()` via `ieee802154_addr_to_sa()`. 5. Finally, `move_addr_to_user()` copies the socket address structure to user space, leaking the uninitialized bytes. Fix this by using `memset` to zero out the address structure in `ieee802154_hdr_get_addr()` when the mode is `IEEE802154_ADDR_NONE`. Fixes: 94b4f6c21cf5 ("ieee802154: add header structs with endiannes and operations") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+346474e3bf0b26bd3090@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090 Link: https://syzkaller.appspot.com/ai_job?id=a507a109-d683-4a2c-bc03-93394f491b17 Signed-off-by: Aleksandr Nogikh Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/62795fd9-fc0c-48eb-bb82-05ffc5a57104@mail.kernel.org Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/ieee802154/header_ops.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index 41a556be10179..a9f0c8df5ae4a 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -173,10 +173,13 @@ ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan, { int pos = 0; - addr->mode = mode; - - if (mode == IEEE802154_ADDR_NONE) + if (mode == IEEE802154_ADDR_NONE) { + memset(addr, 0, sizeof(*addr)); + addr->mode = IEEE802154_ADDR_NONE; return 0; + } + + addr->mode = mode; if (!omit_pan) { memcpy(&addr->pan_id, buf + pos, 2); From 5464ee6442376a2790953ece3f0c7d2ae9824fb3 Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Tue, 2 Jun 2026 12:41:33 +0700 Subject: [PATCH 1444/3297] mac802154: Prevent overwrite return code in mac802154_perform_association() [ Upstream commit 649147cb3f8b3c0c9aeba5d89d69a6ef221c12c2 ] When assoc_status not equal to IEEE802154_ASSOCIATION_SUCCESSFUL, the return value assigned to either "-ERANGE" or "-EPERM" but this return value will be overwritten to 0 after exiting the conditional scope. So, jump to clear_assoc label to preserve the return value when assoc_status not equal to IEEE802154_ASSOCIATION_SUCCESSFUL. This is reported by Coverity Scan as "Unused value". Fixes: fefd19807fe9 ("mac802154: Handle associating") Signed-off-by: Robertus Diawan Chris Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/20260602054133.470293-1-robertusdchris@gmail.com Signed-off-by: Stefan Schmidt Signed-off-by: Sasha Levin --- net/mac802154/scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index a6dab3cc3ad85..0eef3b4f5b9b3 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -594,6 +594,7 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, "Negative ASSOC RESP received from %8phC: %s\n", &ceaddr, local->assoc_status == IEEE802154_PAN_AT_CAPACITY ? "PAN at capacity" : "access denied"); + goto clear_assoc; } ret = 0; From 9408c233a5bbeebb4aca9a3b9528d3fbab48edcf Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Thu, 15 Jan 2026 01:12:30 +0800 Subject: [PATCH 1445/3297] md: merge mddev has_superblock into mddev_flags [ Upstream commit fba4a980403d2f489bc680dbff7d7d2514e669f9 ] There is not need to use a separate field in struct mddev, there are no functional changes. Link: https://lore.kernel.org/linux-raid/20260114171241.3043364-3-yukuai@fnnas.com Signed-off-by: Yu Kuai Reviewed-by: Li Nan Stable-dep-of: a286cb88ddb2 ("md/raid1: honor REQ_NOWAIT when waiting for behind writes") Signed-off-by: Sasha Levin --- drivers/md/md.c | 6 +++--- drivers/md/md.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index b7d47c018a12f..a26dde7bdecdd 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6537,7 +6537,7 @@ int md_run(struct mddev *mddev) * the only valid external interface is through the md * device. */ - mddev->has_superblocks = false; + clear_bit(MD_HAS_SUPERBLOCK, &mddev->flags); rdev_for_each(rdev, mddev) { if (test_bit(Faulty, &rdev->flags)) continue; @@ -6550,7 +6550,7 @@ int md_run(struct mddev *mddev) } if (rdev->sb_page) - mddev->has_superblocks = true; + set_bit(MD_HAS_SUPERBLOCK, &mddev->flags); /* perform some consistency tests on the device. * We don't want the data to overlap the metadata, @@ -9173,7 +9173,7 @@ void md_write_start(struct mddev *mddev, struct bio *bi) rcu_read_unlock(); if (did_change) sysfs_notify_dirent_safe(mddev->sysfs_state); - if (!mddev->has_superblocks) + if (!test_bit(MD_HAS_SUPERBLOCK, &mddev->flags)) return; wait_event(mddev->sb_wait, !test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags)); diff --git a/drivers/md/md.h b/drivers/md/md.h index da312d4692858..ce639f2b1d0a6 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -340,6 +340,7 @@ struct md_cluster_operations; * array is ready yet. * @MD_BROKEN: This is used to stop writes and mark array as failed. * @MD_DELETED: This device is being deleted + * @MD_HAS_SUPERBLOCK: There is persistence sb in member disks. * * change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added */ @@ -356,6 +357,7 @@ enum mddev_flags { MD_BROKEN, MD_DO_DELETE, MD_DELETED, + MD_HAS_SUPERBLOCK, }; enum mddev_sb_flags { @@ -622,7 +624,6 @@ struct mddev { /* The sequence number for sync thread */ atomic_t sync_seq; - bool has_superblocks:1; bool fail_last_dev:1; bool serialize_policy:1; }; From 2e414af05a7cf154aa42946006b748886f6e1eee Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Thu, 15 Jan 2026 01:12:31 +0800 Subject: [PATCH 1446/3297] md: merge mddev faillast_dev into mddev_flags [ Upstream commit 4f6d2e648cbe963b328cb8815290676da3866434 ] There is not need to use a separate field in struct mddev, there are no functional changes. Link: https://lore.kernel.org/linux-raid/20260114171241.3043364-4-yukuai@fnnas.com Signed-off-by: Yu Kuai Reviewed-by: Li Nan Stable-dep-of: a286cb88ddb2 ("md/raid1: honor REQ_NOWAIT when waiting for behind writes") Signed-off-by: Sasha Levin --- drivers/md/md.c | 10 ++++++---- drivers/md/md.h | 3 ++- drivers/md/raid0.c | 3 ++- drivers/md/raid1.c | 4 ++-- drivers/md/raid10.c | 4 ++-- drivers/md/raid5.c | 5 ++++- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index a26dde7bdecdd..963ebe233022c 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5868,11 +5868,11 @@ __ATTR(consistency_policy, S_IRUGO | S_IWUSR, consistency_policy_show, static ssize_t fail_last_dev_show(struct mddev *mddev, char *page) { - return sprintf(page, "%d\n", mddev->fail_last_dev); + return sprintf(page, "%d\n", test_bit(MD_FAILLAST_DEV, &mddev->flags)); } /* - * Setting fail_last_dev to true to allow last device to be forcibly removed + * Setting MD_FAILLAST_DEV to allow last device to be forcibly removed * from RAID1/RAID10. */ static ssize_t @@ -5885,8 +5885,10 @@ fail_last_dev_store(struct mddev *mddev, const char *buf, size_t len) if (ret) return ret; - if (value != mddev->fail_last_dev) - mddev->fail_last_dev = value; + if (value) + set_bit(MD_FAILLAST_DEV, &mddev->flags); + else + clear_bit(MD_FAILLAST_DEV, &mddev->flags); return len; } diff --git a/drivers/md/md.h b/drivers/md/md.h index ce639f2b1d0a6..ef6e6924bf130 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -341,6 +341,7 @@ struct md_cluster_operations; * @MD_BROKEN: This is used to stop writes and mark array as failed. * @MD_DELETED: This device is being deleted * @MD_HAS_SUPERBLOCK: There is persistence sb in member disks. + * @MD_FAILLAST_DEV: Allow last rdev to be removed. * * change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added */ @@ -358,6 +359,7 @@ enum mddev_flags { MD_DO_DELETE, MD_DELETED, MD_HAS_SUPERBLOCK, + MD_FAILLAST_DEV, }; enum mddev_sb_flags { @@ -624,7 +626,6 @@ struct mddev { /* The sequence number for sync thread */ atomic_t sync_seq; - bool fail_last_dev:1; bool serialize_policy:1; }; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index e443e478645ad..00d9fe37e7941 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -27,7 +27,8 @@ module_param(default_layout, int, 0644); (1L << MD_JOURNAL_CLEAN) | \ (1L << MD_FAILFAST_SUPPORTED) |\ (1L << MD_HAS_PPL) | \ - (1L << MD_HAS_MULTIPLE_PPLS)) + (1L << MD_HAS_MULTIPLE_PPLS) | \ + (1L << MD_FAILLAST_DEV)) /* * inform the user of the raid configuration diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index df01070eb0c29..7ed9e07a7e5d2 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1748,7 +1748,7 @@ static void raid1_status(struct seq_file *seq, struct mddev *mddev) * - &mddev->degraded is bumped. * * @rdev is marked as &Faulty excluding case when array is failed and - * &mddev->fail_last_dev is off. + * MD_FAILLAST_DEV is not set. */ static void raid1_error(struct mddev *mddev, struct md_rdev *rdev) { @@ -1761,7 +1761,7 @@ static void raid1_error(struct mddev *mddev, struct md_rdev *rdev) (conf->raid_disks - mddev->degraded) == 1) { set_bit(MD_BROKEN, &mddev->flags); - if (!mddev->fail_last_dev) { + if (!test_bit(MD_FAILLAST_DEV, &mddev->flags)) { conf->recovery_disabled = mddev->recovery_disabled; spin_unlock_irqrestore(&conf->device_lock, flags); return; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 4a8814fd75cd0..d6b174454b1ae 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1991,7 +1991,7 @@ static int enough(struct r10conf *conf, int ignore) * - &mddev->degraded is bumped. * * @rdev is marked as &Faulty excluding case when array is failed and - * &mddev->fail_last_dev is off. + * MD_FAILLAST_DEV is not set. */ static void raid10_error(struct mddev *mddev, struct md_rdev *rdev) { @@ -2003,7 +2003,7 @@ static void raid10_error(struct mddev *mddev, struct md_rdev *rdev) if (test_bit(In_sync, &rdev->flags) && !enough(conf, rdev->raid_disk)) { set_bit(MD_BROKEN, &mddev->flags); - if (!mddev->fail_last_dev) { + if (!test_bit(MD_FAILLAST_DEV, &mddev->flags)) { spin_unlock_irqrestore(&conf->device_lock, flags); return; } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index b9f0d01ce01cb..2904e3661c21d 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -56,7 +56,10 @@ #include "md-bitmap.h" #include "raid5-log.h" -#define UNSUPPORTED_MDDEV_FLAGS (1L << MD_FAILFAST_SUPPORTED) +#define UNSUPPORTED_MDDEV_FLAGS \ + ((1L << MD_FAILFAST_SUPPORTED) | \ + (1L << MD_FAILLAST_DEV)) + #define cpu_to_group(cpu) cpu_to_node(cpu) #define ANY_GROUP NUMA_NO_NODE From 119903c3208304aba590ddcfb7cbc87fe0a321c0 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Thu, 15 Jan 2026 01:12:32 +0800 Subject: [PATCH 1447/3297] md: merge mddev serialize_policy into mddev_flags [ Upstream commit 10787568cc1f3f80afc510b2728751989dfa0ae6 ] There is not need to use a separate field in struct mddev, there are no functional changes. Link: https://lore.kernel.org/linux-raid/20260114171241.3043364-5-yukuai@fnnas.com Signed-off-by: Yu Kuai Reviewed-by: Li Nan Stable-dep-of: a286cb88ddb2 ("md/raid1: honor REQ_NOWAIT when waiting for behind writes") Signed-off-by: Sasha Levin --- drivers/md/md-bitmap.c | 4 ++-- drivers/md/md.c | 20 ++++++++++++-------- drivers/md/md.h | 4 ++-- drivers/md/raid0.c | 3 ++- drivers/md/raid1.c | 4 ++-- drivers/md/raid5.c | 3 ++- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 35943f5f68347..1edccebb2130d 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -2086,7 +2086,7 @@ static void bitmap_destroy(struct mddev *mddev) return; bitmap_wait_behind_writes(mddev); - if (!mddev->serialize_policy) + if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) mddev_destroy_serial_pool(mddev, NULL); mutex_lock(&mddev->bitmap_info.mutex); @@ -2864,7 +2864,7 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len) mddev->bitmap_info.max_write_behind = backlog; if (!backlog && mddev->serial_info_pool) { /* serial_info_pool is not needed if backlog is zero */ - if (!mddev->serialize_policy) + if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) mddev_destroy_serial_pool(mddev, NULL); } else if (backlog && !mddev->serial_info_pool) { /* serial_info_pool is needed since backlog is not zero */ diff --git a/drivers/md/md.c b/drivers/md/md.c index 963ebe233022c..7675246fa8e98 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -278,7 +278,8 @@ void mddev_destroy_serial_pool(struct mddev *mddev, struct md_rdev *rdev) rdev_for_each(temp, mddev) { if (!rdev) { - if (!mddev->serialize_policy || + if (!test_bit(MD_SERIALIZE_POLICY, + &mddev->flags) || !rdev_need_serial(temp)) rdev_uninit_serial(temp); else @@ -5901,11 +5902,12 @@ static ssize_t serialize_policy_show(struct mddev *mddev, char *page) if (mddev->pers == NULL || (mddev->pers->head.id != ID_RAID1)) return sprintf(page, "n/a\n"); else - return sprintf(page, "%d\n", mddev->serialize_policy); + return sprintf(page, "%d\n", + test_bit(MD_SERIALIZE_POLICY, &mddev->flags)); } /* - * Setting serialize_policy to true to enforce write IO is not reordered + * Setting MD_SERIALIZE_POLICY enforce write IO is not reordered * for raid1. */ static ssize_t @@ -5918,7 +5920,7 @@ serialize_policy_store(struct mddev *mddev, const char *buf, size_t len) if (err) return err; - if (value == mddev->serialize_policy) + if (value == test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) return len; err = mddev_suspend_and_lock(mddev); @@ -5930,11 +5932,13 @@ serialize_policy_store(struct mddev *mddev, const char *buf, size_t len) goto unlock; } - if (value) + if (value) { mddev_create_serial_pool(mddev, NULL); - else + set_bit(MD_SERIALIZE_POLICY, &mddev->flags); + } else { mddev_destroy_serial_pool(mddev, NULL); - mddev->serialize_policy = value; + clear_bit(MD_SERIALIZE_POLICY, &mddev->flags); + } unlock: mddev_unlock_and_resume(mddev); return err ?: len; @@ -6907,7 +6911,7 @@ static void __md_stop_writes(struct mddev *mddev) md_update_sb(mddev, 1); } /* disable policy to guarantee rdevs free resources for serialization */ - mddev->serialize_policy = 0; + clear_bit(MD_SERIALIZE_POLICY, &mddev->flags); mddev_destroy_serial_pool(mddev, NULL); } diff --git a/drivers/md/md.h b/drivers/md/md.h index ef6e6924bf130..2960a98747607 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -342,6 +342,7 @@ struct md_cluster_operations; * @MD_DELETED: This device is being deleted * @MD_HAS_SUPERBLOCK: There is persistence sb in member disks. * @MD_FAILLAST_DEV: Allow last rdev to be removed. + * @MD_SERIALIZE_POLICY: Enforce write IO is not reordered, just used by raid1. * * change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added */ @@ -360,6 +361,7 @@ enum mddev_flags { MD_DELETED, MD_HAS_SUPERBLOCK, MD_FAILLAST_DEV, + MD_SERIALIZE_POLICY, }; enum mddev_sb_flags { @@ -625,8 +627,6 @@ struct mddev { /* The sequence number for sync thread */ atomic_t sync_seq; - - bool serialize_policy:1; }; enum recovery_flags { diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 00d9fe37e7941..0bdba5944af94 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -28,7 +28,8 @@ module_param(default_layout, int, 0644); (1L << MD_FAILFAST_SUPPORTED) |\ (1L << MD_HAS_PPL) | \ (1L << MD_HAS_MULTIPLE_PPLS) | \ - (1L << MD_FAILLAST_DEV)) + (1L << MD_FAILLAST_DEV) | \ + (1L << MD_SERIALIZE_POLICY)) /* * inform the user of the raid configuration diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 7ed9e07a7e5d2..6f2f065252e8c 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -542,7 +542,7 @@ static void raid1_end_write_request(struct bio *bio) call_bio_endio(r1_bio); } } - } else if (rdev->mddev->serialize_policy) + } else if (test_bit(MD_SERIALIZE_POLICY, &rdev->mddev->flags)) remove_serial(rdev, lo, hi); if (r1_bio->bios[mirror] == NULL) rdev_dec_pending(rdev, conf->mddev); @@ -1646,7 +1646,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO, &mddev->bio_set); - if (mddev->serialize_policy) + if (test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) wait_for_serialization(rdev, r1_bio); } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 2904e3661c21d..381f56c4b5e27 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -58,7 +58,8 @@ #define UNSUPPORTED_MDDEV_FLAGS \ ((1L << MD_FAILFAST_SUPPORTED) | \ - (1L << MD_FAILLAST_DEV)) + (1L << MD_FAILLAST_DEV) | \ + (1L << MD_SERIALIZE_POLICY)) #define cpu_to_group(cpu) cpu_to_node(cpu) From 2c5384c40a4ceba5446fdabadca10bc25c53c6ec Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Thu, 11 Jun 2026 08:35:14 +0000 Subject: [PATCH 1448/3297] md/raid1: honor REQ_NOWAIT when waiting for behind writes [ Upstream commit a286cb88ddb26c5f4377859d8e77233d9181eb82 ] raid1 supports REQ_NOWAIT reads by avoiding waits in the barrier path through wait_read_barrier(). However, a read can still block on a WriteMostly device when the array uses a bitmap and there are outstanding behind writes. In that case raid1 unconditionally calls wait_behind_writes(), which may sleep until all behind writes complete. As a result, a REQ_NOWAIT read can block despite the caller explicitly requesting non-blocking behavior. This ensures that raid1 consistently honors REQ_NOWAIT reads across all paths that may otherwise wait for behind writes. Fixes: 5aa705039c4f ("md: raid1 add nowait support") Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260611083514.754922-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/md-bitmap.c | 9 +++++++-- drivers/md/md-bitmap.h | 2 +- drivers/md/md-llbitmap.c | 13 ++++++++----- drivers/md/md.c | 2 +- drivers/md/raid1.c | 10 +++++++--- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 1edccebb2130d..2a95840782927 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -2064,18 +2064,23 @@ static void bitmap_end_behind_write(struct mddev *mddev) bitmap->mddev->bitmap_info.max_write_behind); } -static void bitmap_wait_behind_writes(struct mddev *mddev) +static bool bitmap_wait_behind_writes(struct mddev *mddev, bool nowait) { struct bitmap *bitmap = mddev->bitmap; /* wait for behind writes to complete */ if (bitmap && atomic_read(&bitmap->behind_writes) > 0) { + if (nowait) + return false; + pr_debug("md:%s: behind writes in progress - waiting to stop.\n", mdname(mddev)); /* need to kick something here to make sure I/O goes? */ wait_event(bitmap->behind_wait, atomic_read(&bitmap->behind_writes) == 0); } + + return true; } static void bitmap_destroy(struct mddev *mddev) @@ -2085,7 +2090,7 @@ static void bitmap_destroy(struct mddev *mddev) if (!bitmap) /* there was no bitmap */ return; - bitmap_wait_behind_writes(mddev); + bitmap_wait_behind_writes(mddev, false); if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags)) mddev_destroy_serial_pool(mddev, NULL); diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h index 214f623c7e790..f46674bdfeb91 100644 --- a/drivers/md/md-bitmap.h +++ b/drivers/md/md-bitmap.h @@ -98,7 +98,7 @@ struct bitmap_operations { void (*start_behind_write)(struct mddev *mddev); void (*end_behind_write)(struct mddev *mddev); - void (*wait_behind_writes)(struct mddev *mddev); + bool (*wait_behind_writes)(struct mddev *mddev, bool nowait); md_bitmap_fn *start_write; md_bitmap_fn *end_write; diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 50eeddf7f539b..dc9b72494a81a 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -1432,16 +1432,19 @@ static void llbitmap_end_behind_write(struct mddev *mddev) wake_up(&llbitmap->behind_wait); } -static void llbitmap_wait_behind_writes(struct mddev *mddev) +static bool llbitmap_wait_behind_writes(struct mddev *mddev, bool nowait) { struct llbitmap *llbitmap = mddev->bitmap; - if (!llbitmap) - return; + if (llbitmap && atomic_read(&llbitmap->behind_writes) > 0) { + if (nowait) + return false; - wait_event(llbitmap->behind_wait, - atomic_read(&llbitmap->behind_writes) == 0); + wait_event(llbitmap->behind_wait, + atomic_read(&llbitmap->behind_writes) == 0); + } + return true; } static ssize_t bits_show(struct mddev *mddev, char *page) diff --git a/drivers/md/md.c b/drivers/md/md.c index 7675246fa8e98..0d60c211e7c08 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6928,7 +6928,7 @@ EXPORT_SYMBOL_GPL(md_stop_writes); static void mddev_detach(struct mddev *mddev) { if (md_bitmap_enabled(mddev, false)) - mddev->bitmap_ops->wait_behind_writes(mddev); + mddev->bitmap_ops->wait_behind_writes(mddev, false); if (mddev->pers && mddev->pers->quiesce && !is_md_suspended(mddev)) { mddev->pers->quiesce(mddev, 1); mddev->pers->quiesce(mddev, 0); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 6f2f065252e8c..cc057ef1829ac 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1319,6 +1319,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, int max_sectors; int rdisk; bool r1bio_existed = !!r1_bio; + bool nowait = bio->bi_opf & REQ_NOWAIT; /* * If r1_bio is set, we are blocking the raid1d thread @@ -1331,8 +1332,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, * Still need barrier for READ in case that whole * array is frozen. */ - if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, - bio->bi_opf & REQ_NOWAIT)) { + if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) { bio_wouldblock_error(bio); return; } @@ -1373,7 +1373,11 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, * over-take any writes that are 'behind' */ mddev_add_trace_msg(mddev, "raid1 wait behind writes"); - mddev->bitmap_ops->wait_behind_writes(mddev); + if (!mddev->bitmap_ops->wait_behind_writes(mddev, nowait)) { + bio_wouldblock_error(bio); + set_bit(R1BIO_Returned, &r1_bio->state); + goto err_handle; + } } if (max_sectors < bio_sectors(bio)) { From 6d92dbd73d19a0622f60352ce9d9379f7a760112 Mon Sep 17 00:00:00 2001 From: Abd-Alrhman Masalkhi Date: Thu, 11 Jun 2026 10:13:50 +0000 Subject: [PATCH 1449/3297] md/raid1: free r1_bio when REQ_NOWAIT is set and read would block on retry [ Upstream commit 69ad6ce47f9bf2b9fe0ed69b042db993d33bbf12 ] When a read is retried, raid1_read_request() may be called with a pre-allocated r1_bio. If wait_read_barrier() fails for a REQ_NOWAIT read, the bio is completed and the function returns immediately. In this case the existing r1_bio is leaked. This fixes a leak of pre-allocated r1_bio structures for retried reads. Fixes: 5aa705039c4f ("md: raid1 add nowait support") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260611083514.754922-1-abd.masalkhi@gmail.com?part=1 Signed-off-by: Abd-Alrhman Masalkhi Link: https://patch.msgid.link/20260611101350.759154-1-abd.masalkhi@gmail.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid1.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index cc057ef1829ac..16625b79788bd 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1334,6 +1334,12 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, */ if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) { bio_wouldblock_error(bio); + + if (r1bio_existed) { + set_bit(R1BIO_Returned, &r1_bio->state); + raid_end_bio_io(r1_bio); + } + return; } From 7228cc8ff6265332406a2353d5286dc9765caac0 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Fri, 8 May 2026 22:58:58 +0200 Subject: [PATCH 1450/3297] netfilter: ipset: Fix data race between add and dump in all hash types [ Upstream commit 2358f7427ccd6ec8867a48205d8fcec973683a3f ] When adding a new entry to the next position in the existing hash bucket, the position index was incremented too early and parallel dump could read it before the entry was populated with the value. Move the setting of the position index after populating the entry. v2: Position counting fixed, noticed by Florian Westphal. Fixes: 18f84d41d34f ("netfilter: ipset: Introduce RCU locking in hash:* types") Reported-by: syzbot+786c889f046e8b003ca6@syzkaller.appspotmail.com Reported-by: syzbot+1da17e4b41d795df059e@syzkaller.appspotmail.com Reported-by: syzbot+421c5f3ff8e9493084d9@syzkaller.appspotmail.com Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Stable-dep-of: e4b4984e28c1 ("netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types") Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_hash_gen.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 4e56269efef28..5f493124c1590 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -844,7 +844,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, const struct mtype_elem *d = value; struct mtype_elem *data; struct hbucket *n, *old = ERR_PTR(-ENOENT); - int i, j = -1, ret; + int i, j = -1, npos = 0, ret; bool flag_exist = flags & IPSET_FLAG_EXIST; bool deleted = false, forceadd = false, reuse = false; u32 r, key, multi = 0, elements, maxelem; @@ -889,6 +889,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, ext_size(AHASH_INIT_SIZE, set->dsize); goto copy_elem; } + npos = n->pos; for (i = 0; i < n->pos; i++) { if (!test_bit(i, n->used)) { /* Reuse first deleted entry */ @@ -962,7 +963,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, } copy_elem: - j = n->pos++; + j = npos; + npos = n->pos + 1; data = ahash_data(n, j, set->dsize); copy_data: t->hregion[r].elements++; @@ -985,6 +987,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (SET_WITH_TIMEOUT(set)) ip_set_timeout_set(ext_timeout(data, set), ext->timeout); smp_mb__before_atomic(); + n->pos = npos; set_bit(j, n->used); if (old != ERR_PTR(-ENOENT)) { rcu_assign_pointer(hbucket(t, key), n); From a0afd353c2f7e74c8d3632c00a2e85c02a9cafe7 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 14 May 2026 10:55:13 +0200 Subject: [PATCH 1451/3297] netfilter: ipset: annotate "pos" for concurrent readers/writers [ Upstream commit 7f7445840b7771338618930e45ee641104b38ed8 ] The "pos" structure member of struct hbucket stores the first free slot in the hash bucket of a hash type of set and there are concurrent readers/writers. Annotate accesses properly. Fixes: 18f84d41d34f ("netfilter: ipset: Introduce RCU locking in hash:* types") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Stable-dep-of: e4b4984e28c1 ("netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types") Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_hash_gen.h | 62 ++++++++++++++++----------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 5f493124c1590..16cddd0bd6aeb 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -386,8 +386,9 @@ static void mtype_ext_cleanup(struct ip_set *set, struct hbucket *n) { int i; + u8 pos = smp_load_acquire(&n->pos); - for (i = 0; i < n->pos; i++) + for (i = 0; i < pos; i++) if (test_bit(i, n->used)) ip_set_ext_destroy(set, ahash_data(n, i, set->dsize)); } @@ -490,7 +491,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) #ifdef IP_SET_HASH_WITH_NETS u8 k; #endif - u8 htable_bits = t->htable_bits; + u8 pos, htable_bits = t->htable_bits; spin_lock_bh(&t->hregion[r].lock); for (i = ahash_bucket_start(r, htable_bits); @@ -498,7 +499,8 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) n = __ipset_dereference(hbucket(t, i)); if (!n) continue; - for (j = 0, d = 0; j < n->pos; j++) { + pos = smp_load_acquire(&n->pos); + for (j = 0, d = 0; j < pos; j++) { if (!test_bit(j, n->used)) { d++; continue; @@ -534,7 +536,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) /* Still try to delete expired elements. */ continue; tmp->size = n->size - AHASH_INIT_SIZE; - for (j = 0, d = 0; j < n->pos; j++) { + for (j = 0, d = 0; j < pos; j++) { if (!test_bit(j, n->used)) continue; data = ahash_data(n, j, dsize); @@ -623,7 +625,7 @@ mtype_resize(struct ip_set *set, bool retried) { struct htype *h = set->data; struct htable *t, *orig; - u8 htable_bits; + u8 pos, htable_bits; size_t hsize, dsize = set->dsize; #ifdef IP_SET_HASH_WITH_NETS u8 flags; @@ -685,7 +687,8 @@ mtype_resize(struct ip_set *set, bool retried) n = __ipset_dereference(hbucket(orig, i)); if (!n) continue; - for (j = 0; j < n->pos; j++) { + pos = smp_load_acquire(&n->pos); + for (j = 0; j < pos; j++) { if (!test_bit(j, n->used)) continue; data = ahash_data(n, j, dsize); @@ -809,9 +812,10 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size) { struct htype *h = set->data; const struct htable *t; - u32 i, j, r; struct hbucket *n; struct mtype_elem *data; + u32 i, j, r; + u8 pos; t = rcu_dereference_bh(h->table); for (r = 0; r < ahash_numof_locks(t->htable_bits); r++) { @@ -820,7 +824,8 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size) n = rcu_dereference_bh(hbucket(t, i)); if (!n) continue; - for (j = 0; j < n->pos; j++) { + pos = smp_load_acquire(&n->pos); + for (j = 0; j < pos; j++) { if (!test_bit(j, n->used)) continue; data = ahash_data(n, j, set->dsize); @@ -844,10 +849,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, const struct mtype_elem *d = value; struct mtype_elem *data; struct hbucket *n, *old = ERR_PTR(-ENOENT); - int i, j = -1, npos = 0, ret; + int i, j = -1, ret; bool flag_exist = flags & IPSET_FLAG_EXIST; bool deleted = false, forceadd = false, reuse = false; u32 r, key, multi = 0, elements, maxelem; + u8 npos = 0; rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); @@ -889,8 +895,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, ext_size(AHASH_INIT_SIZE, set->dsize); goto copy_elem; } - npos = n->pos; - for (i = 0; i < n->pos; i++) { + npos = smp_load_acquire(&n->pos); + for (i = 0; i < npos; i++) { if (!test_bit(i, n->used)) { /* Reuse first deleted entry */ if (j == -1) { @@ -934,7 +940,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (elements >= maxelem) goto set_full; /* Create a new slot */ - if (n->pos >= n->size) { + if (npos >= n->size) { #ifdef IP_SET_HASH_WITH_MULTI if (h->bucketsize >= AHASH_MAX_TUNED) goto set_full; @@ -963,8 +969,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, } copy_elem: - j = npos; - npos = n->pos + 1; + j = npos++; data = ahash_data(n, j, set->dsize); copy_data: t->hregion[r].elements++; @@ -987,7 +992,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (SET_WITH_TIMEOUT(set)) ip_set_timeout_set(ext_timeout(data, set), ext->timeout); smp_mb__before_atomic(); - n->pos = npos; + /* Ensure all data writes are visible before updating position */ + smp_store_release(&n->pos, npos); set_bit(j, n->used); if (old != ERR_PTR(-ENOENT)) { rcu_assign_pointer(hbucket(t, key), n); @@ -1046,6 +1052,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, int i, j, k, r, ret = -IPSET_ERR_EXIST; u32 key, multi = 0; size_t dsize = set->dsize; + u8 pos; /* Userspace add and resize is excluded by the mutex. * Kernespace add does not trigger resize. @@ -1061,7 +1068,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, n = rcu_dereference_bh(hbucket(t, key)); if (!n) goto out; - for (i = 0, k = 0; i < n->pos; i++) { + pos = smp_load_acquire(&n->pos); + for (i = 0, k = 0; i < pos; i++) { if (!test_bit(i, n->used)) { k++; continue; @@ -1075,8 +1083,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, ret = 0; clear_bit(i, n->used); smp_mb__after_atomic(); - if (i + 1 == n->pos) - n->pos--; + if (i + 1 == pos) + smp_store_release(&n->pos, --pos); t->hregion[r].elements--; #ifdef IP_SET_HASH_WITH_NETS for (j = 0; j < IPSET_NET_COUNT; j++) @@ -1098,11 +1106,11 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, x->flags = flags; } } - for (; i < n->pos; i++) { + for (; i < pos; i++) { if (!test_bit(i, n->used)) k++; } - if (k == n->pos) { + if (k == pos) { t->hregion[r].ext_size -= ext_size(n->size, dsize); rcu_assign_pointer(hbucket(t, key), NULL); kfree_rcu(n, rcu); @@ -1113,7 +1121,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (!tmp) goto out; tmp->size = n->size - AHASH_INIT_SIZE; - for (j = 0, k = 0; j < n->pos; j++) { + for (j = 0, k = 0; j < pos; j++) { if (!test_bit(j, n->used)) continue; data = ahash_data(n, j, dsize); @@ -1174,6 +1182,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, int ret, i, j = 0; #endif u32 key, multi = 0; + u8 pos; pr_debug("test by nets\n"); for (; j < NLEN && h->nets[j].cidr[0] && !multi; j++) { @@ -1191,7 +1200,8 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, n = rcu_dereference_bh(hbucket(t, key)); if (!n) continue; - for (i = 0; i < n->pos; i++) { + pos = smp_load_acquire(&n->pos); + for (i = 0; i < pos; i++) { if (!test_bit(i, n->used)) continue; data = ahash_data(n, i, set->dsize); @@ -1225,6 +1235,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, struct mtype_elem *data; int i, ret = 0; u32 key, multi = 0; + u8 pos; rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); @@ -1247,7 +1258,8 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, ret = 0; goto out; } - for (i = 0; i < n->pos; i++) { + pos = smp_load_acquire(&n->pos); + for (i = 0; i < pos; i++) { if (!test_bit(i, n->used)) continue; data = ahash_data(n, i, set->dsize); @@ -1364,6 +1376,7 @@ mtype_list(const struct ip_set *set, /* We assume that one hash bucket fills into one page */ void *incomplete; int i, ret = 0; + u8 pos; atd = nla_nest_start(skb, IPSET_ATTR_ADT); if (!atd) @@ -1382,7 +1395,8 @@ mtype_list(const struct ip_set *set, cb->args[IPSET_CB_ARG0], t, n); if (!n) continue; - for (i = 0; i < n->pos; i++) { + pos = smp_load_acquire(&n->pos); + for (i = 0; i < pos; i++) { if (!test_bit(i, n->used)) continue; e = ahash_data(n, i, set->dsize); From c4d257734e91bfcdc71d41843392dd6400b5bb1b Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:22 +0200 Subject: [PATCH 1452/3297] netfilter: ipset: Don't use test_bit() in lockless RCU readers in hash types [ Upstream commit e4b4984e28c16406ecb318444dea4a8bf47def3e ] Sashiko pointed out that there are a few lockless RCU readers using test_bit() which is a relaxed atomic operation and provides no memory barrier guarantees. Use test_bit_acquire() instead where the operation may run parallel with add/del/gc, i.e. is not one from the next cases - protected by region lock - in a set destroy phase - in a new/temporary set creation phase Fixes: 18f84d41d34f ("netfilter: ipset: Introduce RCU locking in hash:* types") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_hash_gen.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 16cddd0bd6aeb..94f11c0a5f928 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -689,7 +689,7 @@ mtype_resize(struct ip_set *set, bool retried) continue; pos = smp_load_acquire(&n->pos); for (j = 0; j < pos; j++) { - if (!test_bit(j, n->used)) + if (!test_bit_acquire(j, n->used)) continue; data = ahash_data(n, j, dsize); if (SET_ELEM_EXPIRED(set, data)) @@ -826,7 +826,7 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size) continue; pos = smp_load_acquire(&n->pos); for (j = 0; j < pos; j++) { - if (!test_bit(j, n->used)) + if (!test_bit_acquire(j, n->used)) continue; data = ahash_data(n, j, set->dsize); if (!SET_ELEM_EXPIRED(set, data)) @@ -1202,7 +1202,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, continue; pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) @@ -1260,7 +1260,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, } pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) @@ -1397,7 +1397,7 @@ mtype_list(const struct ip_set *set, continue; pos = smp_load_acquire(&n->pos); for (i = 0; i < pos; i++) { - if (!test_bit(i, n->used)) + if (!test_bit_acquire(i, n->used)) continue; e = ahash_data(n, i, set->dsize); if (SET_ELEM_EXPIRED(set, e)) From 8087bb360a936a6314d22b567e4b861656943eb6 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:24 +0200 Subject: [PATCH 1453/3297] netfilter: ipset: fix order of kfree_rcu() and rcu_assign_pointer() [ Upstream commit 3ca9982a8882470aa0ac4e8bb9a552b181d1efcd ] Sashiko pointed out that kfree_rcu() was called before rcu_assign_pointer() in handling the comment extension. Fix the order so that rcu_assign_pointer() called first. Fixes: b57b2d1fa53f ("netfilter: ipset: Prepare the ipset core to use RCU at set level") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 94157104d85a9..f51a1af31513c 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -351,8 +351,8 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, if (unlikely(c)) { set->ext_size -= sizeof(*c) + strlen(c->str) + 1; - kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); + kfree_rcu(c, rcu); } if (!len) return; @@ -393,8 +393,8 @@ ip_set_comment_free(struct ip_set *set, void *ptr) if (unlikely(!c)) return; set->ext_size -= sizeof(*c) + strlen(c->str) + 1; - kfree_rcu(c, rcu); rcu_assign_pointer(comment->c, NULL); + kfree_rcu(c, rcu); } typedef void (*destroyer)(struct ip_set *, void *); From 12088da6add5b003657c8506c5b0fcef835083b8 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 17 Jun 2026 10:41:26 +0200 Subject: [PATCH 1454/3297] netfilter: ipset: make sure gc is properly stopped [ Upstream commit 4a597a87e2e2f608edb6be2c510dc826b4fdfb53 ] Sashiko noticed that when destroying a set, cancel_delayed_work_sync() was called while gc calls queue_delayed_work() unconditionally which can lead not to properly shutting down the gc. Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 94f11c0a5f928..0426d012185db 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -606,7 +606,7 @@ mtype_cancel_gc(struct ip_set *set) struct htype *h = set->data; if (SET_WITH_TIMEOUT(set)) - cancel_delayed_work_sync(&h->gc.dwork); + disable_delayed_work_sync(&h->gc.dwork); } static int From 5baa149abb41a5129e2932a562dcd5c90b9aa189 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 06:58:24 +0200 Subject: [PATCH 1455/3297] netfilter: nft_payload: reject offsets exceeding 65535 bytes [ Upstream commit 213be32f46a29ca15a314df06c3424ecffd6c90a ] Large offsets were rejected based on netlink policy, but blamed commit removed the policy without updating nft_payload_inner_init() to use the truncation-check helper. Silent truncation is not a problem, but not wanted either, so add a check. Fixes: 077dc4a27579 ("netfilter: nft_payload: extend offset to 65535 bytes") Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_payload.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index b0214418f75ac..da1c6bf0dc572 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -224,11 +224,17 @@ static int nft_payload_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_payload *priv = nft_expr_priv(expr); + u32 offset; + int err; priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); - priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + err = nft_parse_u32_check(tb[NFTA_PAYLOAD_OFFSET], U16_MAX, &offset); + if (err < 0) + return err; + priv->offset = offset; + return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, priv->len); @@ -648,7 +654,8 @@ static int nft_payload_inner_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_payload *priv = nft_expr_priv(expr); - u32 base; + u32 base, offset; + int err; if (!tb[NFTA_PAYLOAD_BASE] || !tb[NFTA_PAYLOAD_OFFSET] || !tb[NFTA_PAYLOAD_LEN] || !tb[NFTA_PAYLOAD_DREG]) @@ -666,8 +673,11 @@ static int nft_payload_inner_init(const struct nft_ctx *ctx, } priv->base = base; - priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + err = nft_parse_u32_check(tb[NFTA_PAYLOAD_OFFSET], U16_MAX, &offset); + if (err < 0) + return err; + priv->offset = offset; return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, From 13c6ba6e0f216018add9709603ec2f6309502bb6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 08:16:18 +0200 Subject: [PATCH 1456/3297] netfilter: nft_meta_bridge: add validate callback for get operations [ Upstream commit bff1c8b49a9cb5c04af20f4e7d43bf4af5863bc6 ] Blamed commit added NFT_META_BRI_IIFHWADDR to the set validate callback, yet this is a get operation. Add a get validate callback and move the NFT_META_BRI_IIFHWADDR key there. AFAICS this is harmless, NFT_META_BRI_IIFHWADDR can deal with a NULL input device and the set handler ignores a NFT_META_BRI_IIFHWADDR operation, but it allows to read 4 bytes off bridge skb->cb[]. Fixes: cbd2257dc96e ("netfilter: nft_meta_bridge: introduce NFT_META_BRI_IIFHWADDR support") Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nft_meta.h | 2 ++ net/bridge/netfilter/nft_meta_bridge.c | 19 ++++++++++++++++++- net/netfilter/nft_meta.c | 5 +++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h index d602263590fed..fab3e6658e91c 100644 --- a/include/net/netfilter/nft_meta.h +++ b/include/net/netfilter/nft_meta.h @@ -40,6 +40,8 @@ void nft_meta_set_eval(const struct nft_expr *expr, void nft_meta_set_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr); +int nft_meta_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr); int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr); diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 1bcef43b2a811..2e24696c33b3c 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -107,12 +107,30 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, NULL, NFT_DATA_VALUE, len); } +static int nft_meta_bridge_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_meta *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->key) { + case NFT_META_BRI_IIFHWADDR: + hooks = 1 << NF_BR_PRE_ROUTING; + break; + default: + return nft_meta_get_validate(ctx, expr); + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_meta_bridge_type; static const struct nft_expr_ops nft_meta_bridge_get_ops = { .type = &nft_meta_bridge_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .eval = nft_meta_bridge_get_eval, .init = nft_meta_bridge_get_init, + .validate = nft_meta_bridge_get_validate, .dump = nft_meta_get_dump, .reduce = nft_meta_get_reduce, }; @@ -187,7 +205,6 @@ static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx, switch (priv->key) { case NFT_META_BRI_BROUTE: - case NFT_META_BRI_IIFHWADDR: hooks = 1 << NF_BR_PRE_ROUTING; break; default: diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 05cd1e6e6a2f6..d3b0efec036aa 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -580,8 +580,8 @@ static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx) #endif } -static int nft_meta_get_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr) +int nft_meta_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -597,6 +597,7 @@ static int nft_meta_get_validate(const struct nft_ctx *ctx, return 0; } +EXPORT_SYMBOL_GPL(nft_meta_get_validate); int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) From 4c61d28634fbf09f0f497ff4545dda9d9c59b6be Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 10 Oct 2025 12:32:23 +0200 Subject: [PATCH 1457/3297] netfilter: flowtable: move path discovery infrastructure to its own file [ Upstream commit 93d7a7ed07342f5e3da2d250cfd67f899d0b5318 ] This file contains the path discovery that is run from the forward chain for the packet offloading the flow into the flowtable. This consists of a series of calls to dev_fill_forward_path() for each device stack. More topologies may be supported in the future, so move this code to its own file to separate it from the nftables flow_offload expression. Signed-off-by: Pablo Neira Ayuso Stable-dep-of: e409c23c2d06 ("netfilter: nft_flow_offload: zero device address for non-ether case") Signed-off-by: Sasha Levin --- include/net/netfilter/nf_flow_table.h | 6 + net/netfilter/Makefile | 1 + net/netfilter/nf_flow_table_path.c | 274 ++++++++++++++++++++++++++ net/netfilter/nft_flow_offload.c | 259 ------------------------ 4 files changed, 281 insertions(+), 259 deletions(-) create mode 100644 net/netfilter/nf_flow_table_path.c diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index c003cd194fa2a..e9f72d2558e90 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -222,6 +222,12 @@ struct nf_flow_route { struct flow_offload *flow_offload_alloc(struct nf_conn *ct); void flow_offload_free(struct flow_offload *flow); +struct nft_flowtable; +struct nft_pktinfo; +int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, + struct nf_flow_route *route, enum ip_conntrack_dir dir, + struct nft_flowtable *ft); + static inline int nf_flow_table_offload_add_cb(struct nf_flowtable *flow_table, flow_setup_cb_t *cb, void *cb_priv) diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index e43e20f529f87..6bfc250e474fe 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -141,6 +141,7 @@ obj-$(CONFIG_NFT_FWD_NETDEV) += nft_fwd_netdev.o # flow table infrastructure obj-$(CONFIG_NF_FLOW_TABLE) += nf_flow_table.o nf_flow_table-objs := nf_flow_table_core.o nf_flow_table_ip.o \ + nf_flow_table_path.o \ nf_flow_table_offload.o nf_flow_table_xdp.o nf_flow_table-$(CONFIG_NF_FLOW_TABLE_PROCFS) += nf_flow_table_procfs.o ifeq ($(CONFIG_NF_FLOW_TABLE),m) diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c new file mode 100644 index 0000000000000..e525e3745651d --- /dev/null +++ b/net/netfilter/nf_flow_table_path.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static enum flow_offload_xmit_type nft_xmit_type(struct dst_entry *dst) +{ + if (dst_xfrm(dst)) + return FLOW_OFFLOAD_XMIT_XFRM; + + return FLOW_OFFLOAD_XMIT_NEIGH; +} + +static void nft_default_forward_path(struct nf_flow_route *route, + struct dst_entry *dst_cache, + enum ip_conntrack_dir dir) +{ + route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex; + route->tuple[dir].dst = dst_cache; + route->tuple[dir].xmit_type = nft_xmit_type(dst_cache); +} + +static bool nft_is_valid_ether_device(const struct net_device *dev) +{ + if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || + dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr)) + return false; + + return true; +} + +static int nft_dev_fill_forward_path(const struct nf_flow_route *route, + const struct dst_entry *dst_cache, + const struct nf_conn *ct, + enum ip_conntrack_dir dir, u8 *ha, + struct net_device_path_stack *stack) +{ + const void *daddr = &ct->tuplehash[!dir].tuple.src.u3; + struct net_device *dev = dst_cache->dev; + struct neighbour *n; + u8 nud_state; + + if (!nft_is_valid_ether_device(dev)) + goto out; + + n = dst_neigh_lookup(dst_cache, daddr); + if (!n) + return -1; + + read_lock_bh(&n->lock); + nud_state = n->nud_state; + ether_addr_copy(ha, n->ha); + read_unlock_bh(&n->lock); + neigh_release(n); + + if (!(nud_state & NUD_VALID)) + return -1; + +out: + return dev_fill_forward_path(dev, ha, stack); +} + +struct nft_forward_info { + const struct net_device *indev; + const struct net_device *outdev; + const struct net_device *hw_outdev; + struct id { + __u16 id; + __be16 proto; + } encap[NF_FLOW_TABLE_ENCAP_MAX]; + u8 num_encaps; + u8 ingress_vlans; + u8 h_source[ETH_ALEN]; + u8 h_dest[ETH_ALEN]; + enum flow_offload_xmit_type xmit_type; +}; + +static void nft_dev_path_info(const struct net_device_path_stack *stack, + struct nft_forward_info *info, + unsigned char *ha, struct nf_flowtable *flowtable) +{ + const struct net_device_path *path; + int i; + + memcpy(info->h_dest, ha, ETH_ALEN); + + for (i = 0; i < stack->num_paths; i++) { + path = &stack->path[i]; + switch (path->type) { + case DEV_PATH_ETHERNET: + case DEV_PATH_DSA: + case DEV_PATH_VLAN: + case DEV_PATH_PPPOE: + info->indev = path->dev; + if (is_zero_ether_addr(info->h_source)) + memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); + + if (path->type == DEV_PATH_ETHERNET) + break; + if (path->type == DEV_PATH_DSA) { + i = stack->num_paths; + break; + } + + /* DEV_PATH_VLAN and DEV_PATH_PPPOE */ + if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { + info->indev = NULL; + break; + } + if (!info->outdev) + info->outdev = path->dev; + info->encap[info->num_encaps].id = path->encap.id; + info->encap[info->num_encaps].proto = path->encap.proto; + info->num_encaps++; + if (path->type == DEV_PATH_PPPOE) + memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN); + break; + case DEV_PATH_BRIDGE: + if (is_zero_ether_addr(info->h_source)) + memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); + + switch (path->bridge.vlan_mode) { + case DEV_PATH_BR_VLAN_UNTAG_HW: + info->ingress_vlans |= BIT(info->num_encaps - 1); + break; + case DEV_PATH_BR_VLAN_TAG: + if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { + info->indev = NULL; + break; + } + info->encap[info->num_encaps].id = path->bridge.vlan_id; + info->encap[info->num_encaps].proto = path->bridge.vlan_proto; + info->num_encaps++; + break; + case DEV_PATH_BR_VLAN_UNTAG: + if (WARN_ON_ONCE(info->num_encaps-- == 0)) { + info->indev = NULL; + break; + } + break; + case DEV_PATH_BR_VLAN_KEEP: + break; + } + info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; + break; + default: + info->indev = NULL; + break; + } + } + if (!info->outdev) + info->outdev = info->indev; + + info->hw_outdev = info->indev; + + if (nf_flowtable_hw_offload(flowtable) && + nft_is_valid_ether_device(info->indev)) + info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; +} + +static bool nft_flowtable_find_dev(const struct net_device *dev, + struct nft_flowtable *ft) +{ + struct nft_hook *hook; + bool found = false; + + list_for_each_entry_rcu(hook, &ft->hook_list, list) { + if (!nft_hook_find_ops_rcu(hook, dev)) + continue; + + found = true; + break; + } + + return found; +} + +static void nft_dev_forward_path(struct nf_flow_route *route, + const struct nf_conn *ct, + enum ip_conntrack_dir dir, + struct nft_flowtable *ft) +{ + const struct dst_entry *dst = route->tuple[dir].dst; + struct net_device_path_stack stack; + struct nft_forward_info info = {}; + unsigned char ha[ETH_ALEN]; + int i; + + if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) + nft_dev_path_info(&stack, &info, ha, &ft->data); + + if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) + return; + + route->tuple[!dir].in.ifindex = info.indev->ifindex; + for (i = 0; i < info.num_encaps; i++) { + route->tuple[!dir].in.encap[i].id = info.encap[i].id; + route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; + } + route->tuple[!dir].in.num_encaps = info.num_encaps; + route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; + + if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { + memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); + memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); + route->tuple[dir].out.ifindex = info.outdev->ifindex; + route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex; + route->tuple[dir].xmit_type = info.xmit_type; + } +} + +int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, + struct nf_flow_route *route, enum ip_conntrack_dir dir, + struct nft_flowtable *ft) +{ + struct dst_entry *this_dst = skb_dst(pkt->skb); + struct dst_entry *other_dst = NULL; + struct flowi fl; + + memset(&fl, 0, sizeof(fl)); + switch (nft_pf(pkt)) { + case NFPROTO_IPV4: + fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip; + fl.u.ip4.saddr = ct->tuplehash[!dir].tuple.src.u3.ip; + fl.u.ip4.flowi4_oif = nft_in(pkt)->ifindex; + fl.u.ip4.flowi4_iif = this_dst->dev->ifindex; + fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb)); + fl.u.ip4.flowi4_mark = pkt->skb->mark; + fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC; + break; + case NFPROTO_IPV6: + fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6; + fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.src.u3.in6; + fl.u.ip6.flowi6_oif = nft_in(pkt)->ifindex; + fl.u.ip6.flowi6_iif = this_dst->dev->ifindex; + fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb)); + fl.u.ip6.flowi6_mark = pkt->skb->mark; + fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC; + break; + } + + if (!dst_hold_safe(this_dst)) + return -ENOENT; + + nf_route(nft_net(pkt), &other_dst, &fl, false, nft_pf(pkt)); + if (!other_dst) { + dst_release(this_dst); + return -ENOENT; + } + + nft_default_forward_path(route, this_dst, dir); + nft_default_forward_path(route, other_dst, !dir); + + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && + route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) { + nft_dev_forward_path(route, ct, dir, ft); + nft_dev_forward_path(route, ct, !dir, ft); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nft_flow_route); diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c index e95e5f59a3d6a..b8f76c9057fda 100644 --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -20,265 +20,6 @@ struct nft_flow_offload { struct nft_flowtable *flowtable; }; -static enum flow_offload_xmit_type nft_xmit_type(struct dst_entry *dst) -{ - if (dst_xfrm(dst)) - return FLOW_OFFLOAD_XMIT_XFRM; - - return FLOW_OFFLOAD_XMIT_NEIGH; -} - -static void nft_default_forward_path(struct nf_flow_route *route, - struct dst_entry *dst_cache, - enum ip_conntrack_dir dir) -{ - route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex; - route->tuple[dir].dst = dst_cache; - route->tuple[dir].xmit_type = nft_xmit_type(dst_cache); -} - -static bool nft_is_valid_ether_device(const struct net_device *dev) -{ - if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || - dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr)) - return false; - - return true; -} - -static int nft_dev_fill_forward_path(const struct nf_flow_route *route, - const struct dst_entry *dst_cache, - const struct nf_conn *ct, - enum ip_conntrack_dir dir, u8 *ha, - struct net_device_path_stack *stack) -{ - const void *daddr = &ct->tuplehash[!dir].tuple.src.u3; - struct net_device *dev = dst_cache->dev; - struct neighbour *n; - u8 nud_state; - - if (!nft_is_valid_ether_device(dev)) - goto out; - - n = dst_neigh_lookup(dst_cache, daddr); - if (!n) - return -1; - - read_lock_bh(&n->lock); - nud_state = n->nud_state; - ether_addr_copy(ha, n->ha); - read_unlock_bh(&n->lock); - neigh_release(n); - - if (!(nud_state & NUD_VALID)) - return -1; - -out: - return dev_fill_forward_path(dev, ha, stack); -} - -struct nft_forward_info { - const struct net_device *indev; - const struct net_device *outdev; - const struct net_device *hw_outdev; - struct id { - __u16 id; - __be16 proto; - } encap[NF_FLOW_TABLE_ENCAP_MAX]; - u8 num_encaps; - u8 ingress_vlans; - u8 h_source[ETH_ALEN]; - u8 h_dest[ETH_ALEN]; - enum flow_offload_xmit_type xmit_type; -}; - -static void nft_dev_path_info(const struct net_device_path_stack *stack, - struct nft_forward_info *info, - unsigned char *ha, struct nf_flowtable *flowtable) -{ - const struct net_device_path *path; - int i; - - memcpy(info->h_dest, ha, ETH_ALEN); - - for (i = 0; i < stack->num_paths; i++) { - path = &stack->path[i]; - switch (path->type) { - case DEV_PATH_ETHERNET: - case DEV_PATH_DSA: - case DEV_PATH_VLAN: - case DEV_PATH_PPPOE: - info->indev = path->dev; - if (is_zero_ether_addr(info->h_source)) - memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); - - if (path->type == DEV_PATH_ETHERNET) - break; - if (path->type == DEV_PATH_DSA) { - i = stack->num_paths; - break; - } - - /* DEV_PATH_VLAN and DEV_PATH_PPPOE */ - if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { - info->indev = NULL; - break; - } - if (!info->outdev) - info->outdev = path->dev; - info->encap[info->num_encaps].id = path->encap.id; - info->encap[info->num_encaps].proto = path->encap.proto; - info->num_encaps++; - if (path->type == DEV_PATH_PPPOE) - memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN); - break; - case DEV_PATH_BRIDGE: - if (is_zero_ether_addr(info->h_source)) - memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); - - switch (path->bridge.vlan_mode) { - case DEV_PATH_BR_VLAN_UNTAG_HW: - info->ingress_vlans |= BIT(info->num_encaps - 1); - break; - case DEV_PATH_BR_VLAN_TAG: - if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { - info->indev = NULL; - break; - } - info->encap[info->num_encaps].id = path->bridge.vlan_id; - info->encap[info->num_encaps].proto = path->bridge.vlan_proto; - info->num_encaps++; - break; - case DEV_PATH_BR_VLAN_UNTAG: - if (WARN_ON_ONCE(info->num_encaps-- == 0)) { - info->indev = NULL; - break; - } - break; - case DEV_PATH_BR_VLAN_KEEP: - break; - } - info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; - break; - default: - info->indev = NULL; - break; - } - } - if (!info->outdev) - info->outdev = info->indev; - - info->hw_outdev = info->indev; - - if (nf_flowtable_hw_offload(flowtable) && - nft_is_valid_ether_device(info->indev)) - info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; -} - -static bool nft_flowtable_find_dev(const struct net_device *dev, - struct nft_flowtable *ft) -{ - struct nft_hook *hook; - bool found = false; - - list_for_each_entry_rcu(hook, &ft->hook_list, list) { - if (!nft_hook_find_ops_rcu(hook, dev)) - continue; - - found = true; - break; - } - - return found; -} - -static void nft_dev_forward_path(struct nf_flow_route *route, - const struct nf_conn *ct, - enum ip_conntrack_dir dir, - struct nft_flowtable *ft) -{ - const struct dst_entry *dst = route->tuple[dir].dst; - struct net_device_path_stack stack; - struct nft_forward_info info = {}; - unsigned char ha[ETH_ALEN]; - int i; - - if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) - nft_dev_path_info(&stack, &info, ha, &ft->data); - - if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) - return; - - route->tuple[!dir].in.ifindex = info.indev->ifindex; - for (i = 0; i < info.num_encaps; i++) { - route->tuple[!dir].in.encap[i].id = info.encap[i].id; - route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; - } - route->tuple[!dir].in.num_encaps = info.num_encaps; - route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; - - if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { - memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); - memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); - route->tuple[dir].out.ifindex = info.outdev->ifindex; - route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex; - route->tuple[dir].xmit_type = info.xmit_type; - } -} - -static int nft_flow_route(const struct nft_pktinfo *pkt, - const struct nf_conn *ct, - struct nf_flow_route *route, - enum ip_conntrack_dir dir, - struct nft_flowtable *ft) -{ - struct dst_entry *this_dst = skb_dst(pkt->skb); - struct dst_entry *other_dst = NULL; - struct flowi fl; - - memset(&fl, 0, sizeof(fl)); - switch (nft_pf(pkt)) { - case NFPROTO_IPV4: - fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip; - fl.u.ip4.saddr = ct->tuplehash[!dir].tuple.src.u3.ip; - fl.u.ip4.flowi4_oif = nft_in(pkt)->ifindex; - fl.u.ip4.flowi4_iif = this_dst->dev->ifindex; - fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb)); - fl.u.ip4.flowi4_mark = pkt->skb->mark; - fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC; - break; - case NFPROTO_IPV6: - fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6; - fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.src.u3.in6; - fl.u.ip6.flowi6_oif = nft_in(pkt)->ifindex; - fl.u.ip6.flowi6_iif = this_dst->dev->ifindex; - fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb)); - fl.u.ip6.flowi6_mark = pkt->skb->mark; - fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC; - break; - } - - if (!dst_hold_safe(this_dst)) - return -ENOENT; - - nf_route(nft_net(pkt), &other_dst, &fl, false, nft_pf(pkt)); - if (!other_dst) { - dst_release(this_dst); - return -ENOENT; - } - - nft_default_forward_path(route, this_dst, dir); - nft_default_forward_path(route, other_dst, !dir); - - if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && - route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) { - nft_dev_forward_path(route, ct, dir, ft); - nft_dev_forward_path(route, ct, !dir, ft); - } - - return 0; -} - static bool nft_flow_offload_skip(struct sk_buff *skb, int family) { if (skb_sec_path(skb)) From 8e935c51b65d968a022ceced3ddbdd7fda08553e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 08:25:47 +0200 Subject: [PATCH 1458/3297] netfilter: nft_flow_offload: zero device address for non-ether case [ Upstream commit e409c23c2d0630f3b95efd12428b2e58800b7645 ] LLM points out that the skip causes unitialised stack array to propagate down into dev_fill_forward_path(). Its not clear to me that there is a guarantee that a later ctx.dev->netdev_ops->ndo_fill_forward_path() would always fix this up. Cc: Felix Fietkau Fixes: 45ca3e61999e ("netfilter: nft_flow_offload: skip dst neigh lookup for ppp devices") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_flow_table_path.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index e525e3745651d..76d4ff1454398 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -52,8 +52,10 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route, struct neighbour *n; u8 nud_state; - if (!nft_is_valid_ether_device(dev)) + if (!nft_is_valid_ether_device(dev)) { + eth_zero_addr(ha); goto out; + } n = dst_neigh_lookup(dst_cache, daddr); if (!n) From d32e4301a0e5ee897a82e9b3daebb4d4e303c660 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 18 Jun 2026 10:49:24 +0200 Subject: [PATCH 1459/3297] netfilter: nf_reject: skip iphdr options when looking for icmp header [ Upstream commit af8d6ae09c0a5f8b8a0d5680203c74b3c1daa85b ] Not a big deal but this hould have used the real ip header length and not the base header size. As-is, if there are options then nf_skb_is_icmp_unreach() result will be random. Fixes: db99b2f2b3e2 ("netfilter: nf_reject: don't reply to icmp error messages") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/ipv4/netfilter/nf_reject_ipv4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index fae4aa4a5f095..c47d56576720f 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -89,7 +89,7 @@ static bool nf_skb_is_icmp_unreach(const struct sk_buff *skb) if (iph->protocol != IPPROTO_ICMP) return false; - thoff = skb_network_offset(skb) + sizeof(*iph); + thoff = skb_network_offset(skb) + ip_hdrlen(skb); tp = skb_header_pointer(skb, thoff + offsetof(struct icmphdr, type), From 7e23965d44f06ed93fb5362fb1e321b769eecc3b Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 19 Jun 2026 00:34:49 +0200 Subject: [PATCH 1460/3297] netfilter: nft_meta_bridge: fix NFT_META_BRI_IIFPVID stack leak [ Upstream commit 27dd2997746d54ebc079bb13161cc1bdd401d4a6 ] This needs to test for nonzero retval. Fixes: c54c7c685494 ("netfilter: nft_meta_bridge: add NFT_META_BRI_IIFPVID support") Closes: https://sashiko.dev/#/patchset/20260618061631.21919-1-fw%40strlen.de Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/bridge/netfilter/nft_meta_bridge.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 2e24696c33b3c..a3d0c8a82d865 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -44,7 +44,9 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr, if (!br_dev || !br_vlan_enabled(br_dev)) goto err; - br_vlan_get_pvid_rcu(in, &p_pvid); + if (br_vlan_get_pvid_rcu(in, &p_pvid)) + goto err; + nft_reg_store16(dest, p_pvid); return; } From 002ebbcc8414c6c0339caf3adb8748cf4bfba52c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:26 +0200 Subject: [PATCH 1461/3297] mailbox: imx: Forward the timeout/ error in imx_mu_generic_tx() [ Upstream commit 82ef9a635d7130ca27ec9dd88c16afc39c83a4e8 ] imx_mu_generic_tx() for the IMX_MU_TYPE_TXDB_V2 type polls on a register which may timeout and is recognized as an error. This error is siltently dropped and not dropped to the caller. Forward the error to the caller. Fixes: b5ef17917f3a7 ("mailbox: imx: fix TXDB_V2 channel race condition") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar Signed-off-by: Sasha Levin --- drivers/mailbox/imx-mailbox.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 003f9236c35e0..a80cb2c9df6ee 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -229,6 +229,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, u32 val; int ret, count; + ret = 0; switch (cp->type) { case IMX_MU_TYPE_TX: imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4); @@ -261,7 +262,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, return -EINVAL; } - return 0; + return ret; } static int imx_mu_generic_rx(struct imx_mu_priv *priv, From 502b3ae43f7988e07750eb3f8da295403fba3472 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sat, 20 Jun 2026 17:39:17 +0530 Subject: [PATCH 1462/3297] irqchip/crossbar: Fix parent domain resource leak [ Upstream commit a1074dd62faa6572921d387e8a21589ccea00efc ] irq_domain_alloc_irqs_parent() is called in allocate_gic_irq() but irq_domain_free_irqs_parent() is never called which causes a resource leak. Fix this by calling irq_domain_free_irqs_parent() in crossbar_domain_free(). Fixes: 783d31863fb82 ("irqchip: crossbar: Convert dra7 crossbar to stacked domains") Signed-off-by: Bhargav Joshi Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260620-irq-crossbar-fix-v2-2-b8e8499f468a@gmail.com Signed-off-by: Sasha Levin --- drivers/irqchip/irq-crossbar.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 66bb39e24a521..aaf1823ba43fc 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -163,6 +163,7 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, cb->write(d->hwirq, cb->safe_map); } raw_spin_unlock(&cb->lock); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); } static int crossbar_domain_translate(struct irq_domain *d, From 37e3e8a2c3bfdd503209f043f8bbfbdcf5a1d92f Mon Sep 17 00:00:00 2001 From: Hao Ge Date: Thu, 4 Jun 2026 14:59:38 +0800 Subject: [PATCH 1463/3297] alloc_tag: fix use-after-free in /proc/allocinfo after module unload [ Upstream commit 2956268efc457cb05d29c1bf94de1e8e684d7bbc ] allocinfo_start() only reinitializes the codetag iterator at position 0. For subsequent reads (position > 0), it reuses cached iterator state from the previous batch. allocinfo_stop() drops mod_lock between read batches, which allows module unload to complete and free the module memory that the cached iterator still references: CPU0 (read) CPU1 (rmmod) ---- ---- allocinfo_start(pos=0) down_read(mod_lock) allocinfo_show() ... allocinfo_stop() up_read(mod_lock) codetag_unload_module() kfree(cmod) release_module_tags() ... free_mod_mem() allocinfo_start(pos=N) down_read(mod_lock) // reuses cached iter, skips re-init allocinfo_show() ct->filename <-- UAF After free_mod_mem() frees the module's .rodata, allocinfo_show() dereferences ct->filename, ct->function which point there. Save the iterator state in allocinfo_next() and resume from it in allocinfo_start() with codetag_next_ct(), which detects module removal via idr_find() returning NULL and skips to the next module. Link: https://lore.kernel.org/20260604065938.105991-1-hao.ge@linux.dev Fixes: 9f44df50fee4 ("alloc_tag: keep codetag iterator active between read()") Signed-off-by: Hao Ge Suggested-by: Suren Baghdasaryan Acked-by: Suren Baghdasaryan Cc: Kent Overstreet Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- lib/alloc_tag.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index df6ba06a8e4a8..3082d977efaae 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -45,6 +45,7 @@ int alloc_tag_ref_offs; struct allocinfo_private { struct codetag_iterator iter; + struct codetag_iterator reported_iter; bool print_header; }; @@ -58,16 +59,20 @@ static void *allocinfo_start(struct seq_file *m, loff_t *pos) if (node == 0) { priv->print_header = true; priv->iter = codetag_get_ct_iter(alloc_tag_cttype); - codetag_next_ct(&priv->iter); + } else { + priv->iter = priv->reported_iter; } + codetag_next_ct(&priv->iter); return priv->iter.ct ? priv : NULL; } static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos) { struct allocinfo_private *priv = (struct allocinfo_private *)arg; - struct codetag *ct = codetag_next_ct(&priv->iter); + struct codetag *ct; + priv->reported_iter = priv->iter; + ct = codetag_next_ct(&priv->iter); (*pos)++; if (!ct) return NULL; From b805de2abfa346ca6653bbe58629b95be259e4f6 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:03:27 +0530 Subject: [PATCH 1464/3297] selftests/mm: restore default nr_hugepages value via exit trap in charge_reserved_hugetlb.sh [ Upstream commit a3f66e9b6e4dad38444656ffc28c28c33a4d4e1f ] Patch series "selftests/mm: fix failures and robustness improvements", v7. Powerpc systems with a 64K base page size exposed several issues while running mm selftests. Some tests assume specific hugetlb configurations, use incorrect interfaces, or fail instead of skipping when the required kernel features are not available. This series fixes these issues and improves test robustness. This patch (of 13): cleanup() resets nr_hugepages to 0 on every invocation, while the test reconfigures it again in the next iteration. This leads to repeated allocation and freeing of large numbers of hugepages, especially when the original value is high. Additionally, with set -e, failures in earlier cleanup steps (e.g., rmdir or umount returning EBUSY while background activity is still ongoing) can cause the script to exit before restoring the original value, leaving the system in a modified state. Introduce a trap on EXIT, INT, and TERM to restore the original nr_hugepages value once at script termination. This avoids unnecessary allocation churn and ensures the original value is reliably restored on all exit paths. Link: https://lore.kernel.org/cover.1779296493.git.sayalip@linux.ibm.com Link: https://lore.kernel.org/5b8fbb29cd6ceffe6752e0af104f60cec072aa10.1779296493.git.sayalip@linux.ibm.com Fixes: 7d695b1c3695 ("selftests/mm: save and restore nr_hugepages value") Signed-off-by: Sayali Patil Acked-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/charge_reserved_hugetlb.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh index fa6713892d82d..2b24d703e7ada 100755 --- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh +++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh @@ -12,6 +12,7 @@ if [[ $(id -u) -ne 0 ]]; then fi nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) +trap 'echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages' EXIT INT TERM fault_limit_file=limit_in_bytes reservation_limit_file=rsvd.limit_in_bytes @@ -65,7 +66,6 @@ function cleanup() { if [[ -e $cgroup_path/hugetlb_cgroup_test2 ]]; then rmdir $cgroup_path/hugetlb_cgroup_test2 fi - echo 0 >/proc/sys/vm/nr_hugepages echo CLEANUP DONE } @@ -585,4 +585,3 @@ if [[ $do_umount ]]; then rmdir $cgroup_path fi -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages From 58cd8ff69e33c0994c6f23dc172c106332ba01cc Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:43 +0530 Subject: [PATCH 1465/3297] selftests/mm: restore default nr_hugepages value via exit trap in hugetlb_reparenting_test.sh [ Upstream commit 7f9c0920ff3debae3cb4d60260e3daf56ab68395 ] The test modifies nr_hugepages during execution and restores it from cleanup() and again reconfigure it setup, which is invoked multiple times across test flow. This can lead to repeated allocation/freeing of hugepages. With set -e, failures in cleanup (e.g., rmdir/umount) can also cause early exit before restoring the original value at the end. Move restoration of the original nr_hugepages value to a trap handler registered for EXIT, INT, and TERM signals so it is always restored on all exit paths. This also avoids unnecessary allocation churn across repeated cleanup/setup cycles. Link: https://lore.kernel.org/29db637c3c6ba6c168f6b33f59f059a0b39c35c8.1779296493.git.sayalip@linux.ibm.com Fixes: 585a9145886a ("selftests/mm: restore default nr_hugepages value during cleanup in hugetlb_reparenting_test.sh") Signed-off-by: Sayali Patil Acked-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hugetlb_reparenting_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index 0dd31892ff679..11f914831146f 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -12,6 +12,8 @@ if [[ $(id -u) -ne 0 ]]; then fi nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) +trap 'echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages' EXIT INT TERM + usage_file=usage_in_bytes if [[ "$1" == "-cgroup-v2" ]]; then @@ -56,7 +58,6 @@ function cleanup() { rmdir "$CGROUP_ROOT"/a/b 2>/dev/null rmdir "$CGROUP_ROOT"/a 2>/dev/null rmdir "$CGROUP_ROOT"/test1 2>/dev/null - echo $nr_hugepgs >/proc/sys/vm/nr_hugepages set -e } @@ -240,4 +241,3 @@ if [[ $do_umount ]]; then rm -rf $CGROUP_ROOT fi -echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages From 8c65c58868ecc580610ef6a852b45cda5e3eae25 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:44 +0530 Subject: [PATCH 1466/3297] selftests/mm: fix hugetlb pathname construction in hugetlb_reparenting_test.sh [ Upstream commit 76f9a212a0a93572a28b58d9869e9187c4022342 ] The hugetlb_reparenting_test.sh script constructs hugetlb cgroup memory interface file names based on the configured huge page size. The script formats the size only in MB units, which causes mismatches on systems using larger huge pages where the kernel exposes normalized units (e.g. "1GB" instead of "1024MB"). As a result, the test fails to locate the corresponding cgroup files when 1GB huge pages are configured. Update the script to detect the huge page size and select the appropriate unit (MB or GB) so that the constructed paths match the kernel's hugetlb controller naming. Also print an explicit "Fail" message when a test failure occurs to improve result visibility. Link: https://lore.kernel.org/837ce751965c93f74c95d89587debf1e93281364.1779296493.git.sayalip@linux.ibm.com Fixes: e487a5d513cb ("selftest/mm: make hugetlb_reparenting_test tolerant to async reparenting") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- .../selftests/mm/hugetlb_reparenting_test.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index 11f914831146f..d724b6e45432f 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -48,6 +48,13 @@ function get_machine_hugepage_size() { } MB=$(get_machine_hugepage_size) +if (( MB >= 1024 )); then + UNIT="GB" + MB_DISPLAY=$((MB / 1024)) +else + UNIT="MB" + MB_DISPLAY=$MB +fi function cleanup() { echo cleanup @@ -88,6 +95,7 @@ function assert_with_retry() { if [[ $elapsed -ge $timeout ]]; then echo "actual = $((${actual%% *} / 1024 / 1024)) MB" echo "expected = $((${expected%% *} / 1024 / 1024)) MB" + echo FAIL cleanup exit 1 fi @@ -108,11 +116,13 @@ function assert_state() { fi assert_with_retry "$CGROUP_ROOT/a/memory.$usage_file" "$expected_a" - assert_with_retry "$CGROUP_ROOT/a/hugetlb.${MB}MB.$usage_file" "$expected_a_hugetlb" + assert_with_retry \ + "$CGROUP_ROOT/a/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_a_hugetlb" if [[ -n "$expected_b" && -n "$expected_b_hugetlb" ]]; then assert_with_retry "$CGROUP_ROOT/a/b/memory.$usage_file" "$expected_b" - assert_with_retry "$CGROUP_ROOT/a/b/hugetlb.${MB}MB.$usage_file" "$expected_b_hugetlb" + assert_with_retry \ + "$CGROUP_ROOT/a/b/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_b_hugetlb" fi } From fff7d3ea3a4c4483940ce2fc91041308d4dff179 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:45 +0530 Subject: [PATCH 1467/3297] selftests/mm: fix cgroup task placement and drop memory.current checks in hugetlb_reparenting_test.sh [ Upstream commit 7bbdc459527075c4d0b96133de25a53822397af0 ] The test currently moves the calling shell ($$) into the target cgroup before executing write_to_hugetlbfs. This results in the shell and any intermediate allocations being charged to the cgroup, introducing noise and nondeterminism in accounting. It also requires moving the shell back to the root cgroup after execution. Spawn a helper process that joins the target cgroup and exec()'s write_to_hugetlbfs. This ensures that only the workload is accounted to the cgroup and avoids unintended charging from the shell. The test currently validates both hugetlb usage and memory.current. However, memory.current includes internal memcg allocations and per-CPU batched accounting (MEMCG_CHARGE_BATCH), which are not synchronized and can vary across systems, leading to non-deterministic results. Since hugetlb memory is accounted via hugetlb..current, memory.current is not a reliable indicator here. Drop memory.current checks and rely only on hugetlb controller statistics for stable and accurate validation. Link: https://lore.kernel.org/fb57491ba83cb0a499c72922e1579b61bee514db.1779296493.git.sayalip@linux.ibm.com Fixes: 29750f71a9b4 ("hugetlb_cgroup: add hugetlb_cgroup reservation tests") Signed-off-by: Sayali Patil Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Venkat Rao Bagalkote Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- .../selftests/mm/hugetlb_reparenting_test.sh | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index d724b6e45432f..95f517c3bd167 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -105,22 +105,17 @@ function assert_with_retry() { } function assert_state() { - local expected_a="$1" - local expected_a_hugetlb="$2" - local expected_b="" + local expected_a_hugetlb="$1" local expected_b_hugetlb="" - if [ ! -z ${3:-} ] && [ ! -z ${4:-} ]; then - expected_b="$3" - expected_b_hugetlb="$4" + if [ ! -z ${2:-} ]; then + expected_b_hugetlb="$2" fi - assert_with_retry "$CGROUP_ROOT/a/memory.$usage_file" "$expected_a" assert_with_retry \ "$CGROUP_ROOT/a/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_a_hugetlb" - if [[ -n "$expected_b" && -n "$expected_b_hugetlb" ]]; then - assert_with_retry "$CGROUP_ROOT/a/b/memory.$usage_file" "$expected_b" + if [[ -n "$expected_b_hugetlb" ]]; then assert_with_retry \ "$CGROUP_ROOT/a/b/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_b_hugetlb" fi @@ -154,18 +149,17 @@ write_hugetlbfs() { local size="$3" if [[ $cgroup2 ]]; then - echo $$ >$CGROUP_ROOT/$cgroup/cgroup.procs + cg_file="$CGROUP_ROOT/$cgroup/cgroup.procs" else echo 0 >$CGROUP_ROOT/$cgroup/cpuset.mems echo 0 >$CGROUP_ROOT/$cgroup/cpuset.cpus - echo $$ >"$CGROUP_ROOT/$cgroup/tasks" - fi - ./write_to_hugetlbfs -p "$path" -s "$size" -m 0 -o - if [[ $cgroup2 ]]; then - echo $$ >$CGROUP_ROOT/cgroup.procs - else - echo $$ >"$CGROUP_ROOT/tasks" + cg_file="$CGROUP_ROOT/$cgroup/tasks" fi + + # Spawn helper to join cgroup before exec to ensure correct cgroup accounting + bash -c 'echo $$ > "$1"; exec ./write_to_hugetlbfs -p "$2" -s "$3" -m 0 -o' _ \ + "$cg_file" "$path" "$size" & pid=$! + wait "$pid" echo } @@ -203,21 +197,21 @@ if [[ ! $cgroup2 ]]; then write_hugetlbfs a "$MNT"/test $size echo Assert memory charged correctly for parent use. - assert_state 0 $size 0 0 + assert_state $size 0 write_hugetlbfs a/b "$MNT"/test2 $size echo Assert memory charged correctly for child use. - assert_state 0 $(($size * 2)) 0 $size + assert_state $(($size * 2)) $size rmdir "$CGROUP_ROOT"/a/b echo Assert memory reparent correctly. - assert_state 0 $(($size * 2)) + assert_state $(($size * 2)) rm -rf "$MNT"/* umount "$MNT" echo Assert memory uncharged correctly. - assert_state 0 0 + assert_state 0 cleanup fi @@ -231,16 +225,16 @@ echo write write_hugetlbfs a/b "$MNT"/test2 $size echo Assert memory charged correctly for child only use. -assert_state 0 $(($size)) 0 $size +assert_state $(($size)) $size rmdir "$CGROUP_ROOT"/a/b echo Assert memory reparent correctly. -assert_state 0 $size +assert_state $size rm -rf "$MNT"/* umount "$MNT" echo Assert memory uncharged correctly. -assert_state 0 0 +assert_state 0 cleanup From 31b28910abe347bf68c6eaa3bb1b20b701071005 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:46 +0530 Subject: [PATCH 1468/3297] selftests/mm: size tmpfs according to PMD page size in split_huge_page_test [ Upstream commit a2dde8a065d5c8d63b53b1c8f035017e4aeac0c2 ] The split_file_backed_thp() test mounts a tmpfs with a fixed size of "4m". This works on systems with smaller PMD page sizes, but fails on configurations where the PMD huge page size is larger (e.g. 16MB). On such systems, the fixed 4MB tmpfs is insufficient to allocate even a single PMD-sized THP, causing the test to fail. Fix this by sizing the tmpfs dynamically based on the runtime pmd_pagesize, allocating space for two PMD-sized pages. Before patch: running ./split_huge_page_test /tmp/xfs_dir_YTrI5E -------------------------------------------------- TAP version 13 1..55 ok 1 Split zero filled huge pages successful ok 2 Split huge pages to order 0 successful ok 3 Split huge pages to order 2 successful ok 4 Split huge pages to order 3 successful ok 5 Split huge pages to order 4 successful ok 6 Split huge pages to order 5 successful ok 7 Split huge pages to order 6 successful ok 8 Split huge pages to order 7 successful ok 9 Split PTE-mapped huge pages successful Please enable pr_debug in split_huge_pages_in_file() for more info. Failed to write data to testing file: Success (0) Bail out! Error occurred Planned tests != run tests (55 != 9) Totals: pass:9 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL] After patch: running ./split_huge_page_test /tmp/xfs_dir_bMvj6o -------------------------------------------------- TAP version 13 1..55 ok 1 Split zero filled huge pages successful ok 2 Split huge pages to order 0 successful ok 3 Split huge pages to order 2 successful ok 4 Split huge pages to order 3 successful ok 5 Split huge pages to order 4 successful ok 6 Split huge pages to order 5 successful ok 7 Split huge pages to order 6 successful ok 8 Split huge pages to order 7 successful ok 9 Split PTE-mapped huge pages successful Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 10 File-backed THP split to order 0 test done Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 11 File-backed THP split to order 1 test done Please enable pr_debug in split_huge_pages_in_file() for more info. Please check dmesg for more information ok 12 File-backed THP split to order 2 test done ... ok 55 Split PMD-mapped pagecache folio to order 7 at in-folio offset 128 passed Totals: pass:55 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 split_huge_page_test /tmp/xfs_dir_bMvj6o Link: https://lore.kernel.org/33e1bc10753fe82d1217613d8cd496020778cf2b.1779296493.git.sayalip@linux.ibm.com Fixes: fbe37501b252 ("mm: huge_memory: debugfs for file-backed THP split") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Reviewed-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/split_huge_page_test.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 743af3c051905..c2d4334d52133 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -484,6 +484,8 @@ static void split_file_backed_thp(int order) char tmpfs_template[] = "/tmp/thp_split_XXXXXX"; const char *tmpfs_loc = mkdtemp(tmpfs_template); char testfile[INPUT_MAX]; + unsigned long size = 2 * pmd_pagesize; + char opts[64]; ssize_t num_written, num_read; char *file_buf1, *file_buf2; uint64_t pgoff_start = 0, pgoff_end = 1024; @@ -503,7 +505,8 @@ static void split_file_backed_thp(int order) file_buf1[i] = (char)i; memset(file_buf2, 0, pmd_pagesize); - status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m"); + snprintf(opts, sizeof(opts), "huge=always,size=%lu", size); + status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, opts); if (status) ksft_exit_fail_msg("Unable to create a tmpfs for testing\n"); From a8673dbd3d4a0303562130e3f6842a23a7b63ea0 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:47 +0530 Subject: [PATCH 1469/3297] selftests/mm: free dynamically allocated PMD-sized buffers in split_huge_page_test [ Upstream commit e5d3e1422e92d54e28e81fad8bfc7c1ecb41a353 ] Dynamically allocated buffers of PMD size for file-backed THP operations (file_buf1 and file_buf2) were not freed on the success path and some failure paths. Since the function is called repeatedly in a loop for each split order, this can cause significant memory leaks. On architectures with large PMD sizes, repeated leaks could exhaust system memory and trigger the OOM killer during test execution. Ensure all allocated buffers are freed to maintain stable repeated test runs. Link: https://lore.kernel.org/060c673b376bbeeed2b1fb1d48a825e846654191.1779296493.git.sayalip@linux.ibm.com Fixes: 035a112e5fd5 ("selftests/mm: make file-backed THP split work by writing PMD size data") Signed-off-by: Sayali Patil Reviewed-by: Zi Yan Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- .../selftests/mm/split_huge_page_test.c | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index c2d4334d52133..7c7e5e8ea59dd 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -487,12 +487,15 @@ static void split_file_backed_thp(int order) unsigned long size = 2 * pmd_pagesize; char opts[64]; ssize_t num_written, num_read; - char *file_buf1, *file_buf2; + char *file_buf1 = NULL, *file_buf2 = NULL; uint64_t pgoff_start = 0, pgoff_end = 1024; int i; ksft_print_msg("Please enable pr_debug in split_huge_pages_in_file() for more info.\n"); + if (!tmpfs_loc) + ksft_exit_fail_msg("mkdtemp failed\n"); + file_buf1 = (char *)malloc(pmd_pagesize); file_buf2 = (char *)malloc(pmd_pagesize); @@ -508,8 +511,10 @@ static void split_file_backed_thp(int order) snprintf(opts, sizeof(opts), "huge=always,size=%lu", size); status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, opts); - if (status) - ksft_exit_fail_msg("Unable to create a tmpfs for testing\n"); + if (status) { + ksft_print_msg("Unable to create a tmpfs for testing\n"); + goto out; + } status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc); if (status >= INPUT_MAX) { @@ -561,10 +566,13 @@ static void split_file_backed_thp(int order) status = umount(tmpfs_loc); if (status) { - rmdir(tmpfs_loc); - ksft_exit_fail_msg("Unable to umount %s\n", tmpfs_loc); + ksft_print_msg("Unable to umount %s\n", tmpfs_loc); + goto out; } + free(file_buf1); + free(file_buf2); + status = rmdir(tmpfs_loc); if (status) ksft_exit_fail_msg("cannot remove tmp dir: %s\n", strerror(errno)); @@ -577,8 +585,10 @@ static void split_file_backed_thp(int order) close(fd); cleanup: umount(tmpfs_loc); - rmdir(tmpfs_loc); out: + free(file_buf1); + free(file_buf2); + rmdir(tmpfs_loc); ksft_exit_fail_msg("Error occurred\n"); } From 9dbfd514148dd1d256cc546f91ef58425031a509 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:48 +0530 Subject: [PATCH 1470/3297] selftest/mm: register existing mapping with userfaultfd in hugetlb-mremap [ Upstream commit 1df31d5ef58dc6bdf0f2a8b809152d835c60b28e ] Previously, register_region_with_uffd() created a new anonymous mapping and overwrote the address supplied by the caller before registering the range with userfaultfd. As a result, userfaultfd was applied to an unrelated anonymous mapping instead of the hugetlb region used by the test. Remove the extra mmap() and register the caller-provided address range directly using UFFDIO_REGISTER_MODE_MISSING, so that faults are generated for the hugetlb mapping used by the test. This ensures userfaultfd operates on the actual hugetlb test region and validates the expected fault handling. Before patch: running ./hugetlb-mremap ------------------------- TAP version 13 1..1 Map haddr: Returned address is 0x7eaa40000000 Map daddr: Returned address is 0x7daa40000000 Map vaddr: Returned address is 0x7faa40000000 Address returned by mmap() = 0x7fff9d000000 Mremap: Returned address is 0x7faa40000000 First hex is 0 First hex is 3020100 ok 1 Read same data Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 hugetlb-mremap After patch: running ./hugetb-mremap ------------------------- TAP version 13 1..1 Map haddr: Returned address is 0x7eaa40000000 Map daddr: Returned address is 0x7daa40000000 Map vaddr: Returned address is 0x7faa40000000 Registered memory at address 0x7eaa40000000 with userfaultfd Mremap: Returned address is 0x7faa40000000 First hex is 0 First hex is 3020100 ok 1 Read same data Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS] ok 1 hugetlb-mremap Link: https://lore.kernel.org/13845da872ed174316173e8996dbb5f181994017.1779296493.git.sayalip@linux.ibm.com Fixes: 12b613206474 ("mm, hugepages: add hugetlb vma mremap() test") Signed-off-by: Sayali Patil Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hugepage-mremap.c | 21 +++++--------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage-mremap.c b/tools/testing/selftests/mm/hugepage-mremap.c index 2bd1dac75c3f1..cd862a619471e 100644 --- a/tools/testing/selftests/mm/hugepage-mremap.c +++ b/tools/testing/selftests/mm/hugepage-mremap.c @@ -85,25 +85,14 @@ static void register_region_with_uffd(char *addr, size_t len) if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) ksft_exit_fail_msg("ioctl-UFFDIO_API: %s\n", strerror(errno)); - /* Create a private anonymous mapping. The memory will be - * demand-zero paged--that is, not yet allocated. When we - * actually touch the memory, it will be allocated via - * the userfaultfd. - */ - - addr = mmap(NULL, len, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (addr == MAP_FAILED) - ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); - - ksft_print_msg("Address returned by mmap() = %p\n", addr); - - /* Register the memory range of the mapping we just created for - * handling by the userfaultfd object. In mode, we request to track - * missing pages (i.e., pages that have not yet been faulted in). + /* Register the passed memory range for handling by the userfaultfd object. + * In mode, we request to track missing pages + * (i.e., pages that have not yet been faulted in). */ if (uffd_register(uffd, addr, len, true, false, false)) ksft_exit_fail_msg("ioctl-UFFDIO_REGISTER: %s\n", strerror(errno)); + + ksft_print_msg("Registered memory at address %p with userfaultfd\n", addr); } int main(int argc, char *argv[]) From 471b62966c782d407a8f6c50d630c35272db9b28 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:49 +0530 Subject: [PATCH 1471/3297] selftests/mm: ensure destination is hugetlb-backed in hugetlb-mremap [ Upstream commit f20ca8972e83f111c4300c60d74314568c8964bc ] The hugetlb-mremap selftest reserves the destination address using a anonymous base-page mapping before calling mremap() with MREMAP_FIXED, while the source region is hugetlb-backed. When remapping a hugetlb mapping into a base-page VMA may fail with: mremap: Device or resource busy This is observed on powerpc hash MMU systems where slice constraints and page size incompatibilities prevent the remap. Ensure the destination region is created using MAP_HUGETLB so that both source and destination VMAs are hugetlb-backed and compatible. Update the FLAGS macro to include MAP_HUGETLB | MAP_SHARED so that both mappings are hugetlb-backed and compatible. Also use the macro for the mmap() calls to avoid repeating the flag combination. This ensures the test reliably exercises hugetlb mremap instead of failing due to VMA type mismatch. Link: https://lore.kernel.org/367644df45c65098f23e3945c6a80f4b8a8964a6.1779296493.git.sayalip@linux.ibm.com Fixes: 12b613206474 ("mm, hugepages: add hugetlb vma mremap() test") Signed-off-by: Sayali Patil Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hugepage-mremap.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage-mremap.c b/tools/testing/selftests/mm/hugepage-mremap.c index cd862a619471e..928c1045e2954 100644 --- a/tools/testing/selftests/mm/hugepage-mremap.c +++ b/tools/testing/selftests/mm/hugepage-mremap.c @@ -31,7 +31,7 @@ #define MB_TO_BYTES(x) (x * 1024 * 1024) #define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC) -#define FLAGS (MAP_SHARED | MAP_ANONYMOUS) +#define FLAGS (MAP_HUGETLB | MAP_SHARED) static void check_bytes(char *addr) { @@ -121,23 +121,20 @@ int main(int argc, char *argv[]) /* mmap to a PUD aligned address to hopefully trigger pmd sharing. */ unsigned long suggested_addr = 0x7eaa40000000; - void *haddr = mmap((void *)suggested_addr, length, PROTECTION, - MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); + void *haddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map haddr: Returned address is %p\n", haddr); if (haddr == MAP_FAILED) ksft_exit_fail_msg("mmap1: %s\n", strerror(errno)); /* mmap again to a dummy address to hopefully trigger pmd sharing. */ suggested_addr = 0x7daa40000000; - void *daddr = mmap((void *)suggested_addr, length, PROTECTION, - MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); + void *daddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map daddr: Returned address is %p\n", daddr); if (daddr == MAP_FAILED) ksft_exit_fail_msg("mmap3: %s\n", strerror(errno)); suggested_addr = 0x7faa40000000; - void *vaddr = - mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0); + void *vaddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0); ksft_print_msg("Map vaddr: Returned address is %p\n", vaddr); if (vaddr == MAP_FAILED) ksft_exit_fail_msg("mmap2: %s\n", strerror(errno)); From 0caa28e9789411d831aa00e32c56252f7003669e Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:51 +0530 Subject: [PATCH 1472/3297] selftests/mm: skip uffd-stress test when nr_pages_per_cpu is zero [ Upstream commit f3bd00507f226a419125df6064632bcbd47d8e70 ] uffd-stress currently fails when the computed nr_pages_per_cpu evaluates to zero: nr_pages_per_cpu = bytes / page_size / nr_parallel This can occur on systems with large hugepage sizes (e.g. 1GB) and a high number of CPUs, where the total allocated memory is sufficient overall but not enough to provide at least one page per cpu. In such cases, the failure is due to insufficient test resources rather than incorrect kernel behaviour. Update the test to treat this condition as a test skip instead of reporting an error. [sayalip@linux.ibm.com: use ksft_exit_skip() instead of KSFT_SKIP] Link: https://lore.kernel.org/88202b56-1dc5-43e2-9d1f-a0823a9531f0@linux.ibm.com Link: https://lore.kernel.org/0707e9a0f1b3dd904c4a069b91db317f9c160faa.1779296493.git.sayalip@linux.ibm.com Fixes: db0f1c138f18 ("selftests/mm: print some details when uffd-stress gets bad params") Signed-off-by: Sayali Patil Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Venkat Rao Bagalkote Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/uffd-stress.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index b51c89e1cd1ae..73af98ef6850c 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -491,9 +491,8 @@ int main(int argc, char **argv) gopts->nr_pages_per_cpu = bytes / gopts->page_size / gopts->nr_parallel; if (!gopts->nr_pages_per_cpu) { - _err("pages_per_cpu = 0, cannot test (%lu / %lu / %lu)", - bytes, gopts->page_size, gopts->nr_parallel); - usage(); + ksft_exit_skip("pages_per_cpu = 0, cannot test (%zu / %lu / %lu)\n", + bytes, gopts->page_size, gopts->nr_parallel); } bounces = atoi(argv[3]); From c8add1d06512bfd2c5953300d931028aaaa79890 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 21 May 2026 12:17:53 +0530 Subject: [PATCH 1473/3297] selftests/mm: clarify alternate unmapping in compaction_test [ Upstream commit 69ba1d76d93ab818245333cf400928477ba72053 ] Add a comment explaining that every other entry in the list is unmapped to intentionally create fragmentation with locked pages before invoking check_compaction(). Link: https://lore.kernel.org/da5e0a8d5152e54152c0d2f456aac2fac35af291.1779296493.git.sayalip@linux.ibm.com Fixes: bd67d5c15cc1 ("Test compaction of mlocked memory") Signed-off-by: Sayali Patil Tested-by: Venkat Rao Bagalkote Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Liam Howlett Cc: Miaohe Lin Cc: Michal Hocko Cc: Oscar Salvador Cc: "Ritesh Harjani (IBM)" Cc: Shuah Khan Cc: Zi Yan Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/compaction_test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c index 9bc4591c7b169..be226ead936fd 100644 --- a/tools/testing/selftests/mm/compaction_test.c +++ b/tools/testing/selftests/mm/compaction_test.c @@ -261,6 +261,9 @@ int main(int argc, char **argv) mem_fragmentable_MB -= MAP_SIZE_MB; } + /* Unmap every other entry in the list to create fragmentation with + * locked pages before invoking check_compaction(). + */ for (entry = list; entry != NULL; entry = entry->next) { munmap(entry->map, MAP_SIZE); if (!entry->next) From 55fc2f99d09798b4c427ca480e55840289c1bf9a Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Thu, 11 Jun 2026 09:11:00 +0530 Subject: [PATCH 1474/3297] selftests/mm: allow PUD-level entries in compound testcase of hmm tests [ Upstream commit 224ed0e019b122d08580362abe57574a78f2b5fa ] Patch series "selftests/mm: assorted fixes for hmm-tests", v3. This series fixes a few issues in hmm-tests that show up when page-size and huge-page configuration differ from the hardcoded assumptions the tests were written for (PMD/THP sizing, default hugepage size, and related cases). It also includes a fix to exclusive_cow: the test ignored the return value of fork(), so both parent and child ran the same teardown path. This patch (of 3): The HMM compound testcase currently assumes only PMD-level mappings and fails on systems where default_hugepagesz=1G is set, because the region is then reported by the device at PUD level. Determine the mapping level (PMD or PUD) the device reports for the first page of the range and require every page to match that level exactly via ASSERT_EQ(). This accepts PUD-level mappings while preserving the expected/observed protection values printed on failure, and rejects a fragmented mapping that mixes PMD- and PUD-level entries within the same range (which a per-page OR check would have let pass). Link: https://lore.kernel.org/20260611034102.1030738-1-aboorvad@linux.ibm.com Link: https://lore.kernel.org/20260611034102.1030738-2-aboorvad@linux.ibm.com Fixes: e478425bec93 ("mm/hmm: add tests for hmm_pfn_to_map_order()") Signed-off-by: Sayali Patil Co-developed-by: Aboorva Devarajan Signed-off-by: Aboorva Devarajan Cc: Alex Sierra Cc: Alistair Popple Cc: Balbir Singh Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hmm-tests.c | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index c13b779bf1184..cfc5ed3585b3f 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -1612,8 +1612,8 @@ TEST_F(hmm2, snapshot) } /* - * Test the hmm_range_fault() HMM_PFN_PMD flag for large pages that - * should be mapped by a large page table entry. + * Test the hmm_range_fault() handling of large pages (PMD or PUD) + * that should be mapped by a large page table entry. */ TEST_F(hmm, compound) { @@ -1623,6 +1623,7 @@ TEST_F(hmm, compound) unsigned long default_hsize; int *ptr; unsigned char *m; + unsigned char prot; int ret; unsigned long i; @@ -1661,11 +1662,20 @@ TEST_F(hmm, compound) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - /* Check what the device saw. */ + /* + * Check what the device saw. The region is backed by a single huge + * page that the device reports either at PMD or at PUD level depending + * on the configured default hugepage size. Determine that level from + * the first page and require every page in the range to match it + * exactly, so that a fragmented mapping mixing levels (or a missing + * large-page bit) is still caught and reported with its actual value. + */ m = buffer->mirror; + prot = HMM_DMIRROR_PROT_WRITE | + ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD : + HMM_DMIRROR_PROT_PMD); for (i = 0; i < npages; ++i) - ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE | - HMM_DMIRROR_PROT_PMD); + ASSERT_EQ(m[i], prot); /* Make the region read-only. */ ret = mprotect(buffer->ptr, size, PROT_READ); @@ -1676,11 +1686,17 @@ TEST_F(hmm, compound) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - /* Check what the device saw. */ + /* + * Check what the device saw after mprotect(PROT_READ). Same + * approach as above: determine the mapping level from the first + * page and require every page to match it exactly. + */ m = buffer->mirror; + prot = HMM_DMIRROR_PROT_READ | + ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD : + HMM_DMIRROR_PROT_PMD); for (i = 0; i < npages; ++i) - ASSERT_EQ(m[i], HMM_DMIRROR_PROT_READ | - HMM_DMIRROR_PROT_PMD); + ASSERT_EQ(m[i], prot); munmap(buffer->ptr, buffer->size); buffer->ptr = NULL; From 9200c8149910d97887d83355886ceb1af0c4f21b Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Thu, 11 Jun 2026 09:11:02 +0530 Subject: [PATCH 1475/3297] selftests/mm: fix exclusive_cow test fork() handling [ Upstream commit cea5702144615878600d3a39b5d8b3cc34719012 ] The test ignores the return value of fork(), so both the parent and the (newly created) child run the COW verification loops and then call hmm_buffer_free() before returning into the kselftest harness, which _exit()s each side. This duplicated teardown sequence has been observed to manifest as a SIGSEGV in the test child, e.g.: hmm-tests[360141]: segfault (11) at 0 nip 10006964 lr 1000ac3c code 1 in hmm-tests[6964,10000000+30000] Fix this by adopting the same fork()-then-wait pattern already used by the nearby anon_write_child / anon_write_child_shared tests in this file: the child performs the COW verification and then _exit(0)s so it does not run the test teardown, while the parent independently verifies COW, waits for the child, and only then frees the buffer. Link: https://lore.kernel.org/20260611034102.1030738-4-aboorvad@linux.ibm.com Fixes: b659baea75469 ("mm: selftests for exclusive device memory") Signed-off-by: Aboorva Devarajan Cc: Alex Sierra Cc: Alistair Popple Cc: Balbir Singh Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Ralph Campbell Cc: Sayali Patil Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin --- tools/testing/selftests/mm/hmm-tests.c | 31 +++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index cfc5ed3585b3f..8f90fbb747a54 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -1896,6 +1896,8 @@ TEST_F(hmm, exclusive_cow) unsigned long i; int *ptr; int ret; + pid_t pid; + int status; npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; ASSERT_NE(npages, 0); @@ -1924,14 +1926,37 @@ TEST_F(hmm, exclusive_cow) ASSERT_EQ(ret, 0); ASSERT_EQ(buffer->cpages, npages); - fork(); + pid = fork(); + if (pid == -1) + ASSERT_EQ(pid, 0); - /* Fault pages back to system memory and check them. */ + if (pid == 0) { + /* + * Child verifies COW independently, then _exit(0)s so it does + * not run the test teardown. A failed ASSERT_* here makes the + * harness abort() the child, so the parent sees + * !WIFEXITED(status) below and fails in turn. + */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i]++, i); + + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i], i + 1); + + _exit(0); + } + + /* Parent: also increment to verify COW works for both processes. */ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) ASSERT_EQ(ptr[i]++, i); for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) - ASSERT_EQ(ptr[i], i+1); + ASSERT_EQ(ptr[i], i + 1); + + /* Parent: wait for child and then free the buffer. */ + ASSERT_EQ(waitpid(pid, &status, 0), pid); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0); hmm_buffer_free(buffer); } From 5dda4f164a633cfd55d445ca5a3d2664bd41abf0 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 03:32:28 +0800 Subject: [PATCH 1476/3297] net: marvell: prestera: initialize err in prestera_port_sfp_bind [ Upstream commit 62b01f72d93c7bc8fde3b2e5b5f783eca5f53324 ] prestera_port_sfp_bind() returns err after walking the ports node. If no child node matches the port's front-panel id, err is never assigned. Initialize err to 0 because absence of a matching optional port device tree node is not an error. In that case no phylink is created and port creation should continue with port->phy_link left NULL. Errors from malformed matched nodes and phylink_create() still propagate. Fixes: 52323ef75414 ("net: marvell: prestera: add phylink support") Signed-off-by: Ruoyu Wang Reviewed-by: Maxime Chevallier Acked-by: Elad Nachman Link: https://patch.msgid.link/20260617193228.1653582-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/prestera/prestera_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index 65e7ef033bde5..84ff651dd058b 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -373,7 +373,7 @@ static int prestera_port_sfp_bind(struct prestera_port *port) struct device_node *ports, *node; struct fwnode_handle *fwnode; struct phylink *phy_link; - int err; + int err = 0; if (!sw->np) return 0; From a0c5fdeb5fa257f8c6d469af266bc087cb5de6a2 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 21:57:45 +0800 Subject: [PATCH 1477/3297] tipc: fix use-after-free of the discoverer in tipc_disc_rcv() [ Upstream commit 1579342d71133da7f00daa02c75cebec7372097b ] bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(), but tipc_disc_rcv() still dereferences b->disc in RX softirq under rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv). L2 bearers are safe thanks to the synchronize_net() in tipc_disable_l2_media(), but the UDP bearer defers that call to the cleanup_bearer() workqueue, so the discoverer is freed with no grace period: BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149) Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 tipc_disc_rcv (net/tipc/discover.c:149) tipc_rcv (net/tipc/node.c:2126) tipc_udp_recv (net/tipc/udp_media.c:391) udp_rcv (net/ipv4/udp.c:2643) ip_local_deliver_finish (net/ipv4/ip_input.c:241) Freed by task 181: kfree (mm/slub.c:6565) bearer_disable (net/tipc/bearer.c:418) tipc_nl_bearer_disable (net/tipc/bearer.c:1001) The bearer is freed with kfree_rcu(); free the discoverer the same way. Add an rcu_head to struct tipc_discoverer and free it and its skb from an RCU callback. Because the RCU callback (tipc_disc_free_rcu) lives in module text, a call_rcu() that is still pending when the tipc module is unloaded would invoke a freed function. Add an rcu_barrier() to tipc_exit() after the bearer subsystem has been torn down, so all pending discoverer callbacks have run before the module text goes away. Reachable from an unprivileged user namespace: the TIPCv2 genl family is netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs CONFIG_TIPC and CONFIG_TIPC_MEDIA_UDP. Fixes: 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash values") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260617135744.3383175-3-bestswngs@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/core.c | 5 +++++ net/tipc/discover.c | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/net/tipc/core.c b/net/tipc/core.c index 434e70eabe081..1ddecea1df6e9 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -218,6 +218,11 @@ static void __exit tipc_exit(void) unregister_pernet_device(&tipc_net_ops); tipc_unregister_sysctl(); + /* TODO: Wait for all timers that called call_rcu() to finish before + * calling rcu_barrier(). + */ + rcu_barrier(); + pr_info("Deactivated\n"); } diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 775fd4f3f0725..f13a823c13c60 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -58,6 +58,7 @@ * @skb: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) + * @rcu: RCU head for deferred freeing */ struct tipc_discoverer { u32 bearer_id; @@ -69,6 +70,7 @@ struct tipc_discoverer { struct sk_buff *skb; struct timer_list timer; unsigned long timer_intv; + struct rcu_head rcu; }; /** @@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, return 0; } +static void tipc_disc_free_rcu(struct rcu_head *rp) +{ + struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer, + rcu); + + kfree_skb(d->skb); + kfree(d); +} + /** * tipc_disc_delete - destroy object sending periodic link setup requests * @d: ptr to link dest structure @@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, void tipc_disc_delete(struct tipc_discoverer *d) { timer_shutdown_sync(&d->timer); - kfree_skb(d->skb); - kfree(d); + call_rcu(&d->rcu, tipc_disc_free_rcu); } /** From ceef83f0eaf9c7115cb00aeb71295b369b0deefe Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 17 Jun 2026 13:48:13 +0800 Subject: [PATCH 1478/3297] net: ethernet: mtk_ppe: Fix rhashtable leak in mtk_ppe_init error paths [ Upstream commit 41782770be567abc6509169d0ffdada31c783a66 ] In mtk_ppe_init(), when accounting is enabled, the error paths for dmam_alloc_coherent(mib) and devm_kzalloc(acct) failures return NULL directly, bypassing the err_free_l2_flows label that destroys the rhashtable initialized earlier. While this leak only occurs during probe (not runtime) and the leaked memory is minimal (an empty rhash table), fixing it ensures proper error path cleanup consistency. Fix by changing the two return NULL statements to goto err_free_l2_flows. Fixes: 603ea5e7ffa7 ("net: ethernet: mtk_eth_soc: fix memory leak in error path") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178167550101.2217645.14579307712717502425@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_ppe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c index fa688a42a22f5..bb01a7355932a 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c @@ -918,7 +918,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib), &ppe->mib_phys, GFP_KERNEL); if (!mib) - return NULL; + goto err_free_l2_flows; ppe->mib_table = mib; @@ -926,7 +926,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) GFP_KERNEL); if (!acct) - return NULL; + goto err_free_l2_flows; ppe->acct_table = acct; } From 5636f0f3bd99b4e89527b721ff50db0df36431f3 Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Wed, 17 Jun 2026 00:30:18 +0530 Subject: [PATCH 1479/3297] octeontx2-af: mcs: Fix unsupported secy stats read [ Upstream commit d4b7440f7316e76f013f57d8b6da069a1b9c34e7 ] Secy control stats counter doesn't exist for CNF10KB platform. Skip reading this respective register for CNF10KB silicon while fetching secy stats. Fixes: 9312150af8da ("octeontx2-af: cn10k: mcs: Support for stats collection") Signed-off-by: Geetha sowjanya Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-1-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/mcs.c | 6 +++--- drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c index c1775bd01c2b4..a07e0b3d8d000 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c @@ -120,13 +120,13 @@ void mcs_get_rx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDX(id); stats->pkt_untaged_cnt = mcs_reg_read(mcs, reg); - reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id); - stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg); - if (mcs->hw->mcs_blks > 1) { reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOTAGX(id); stats->pkt_notag_cnt = mcs_reg_read(mcs, reg); + return; } + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id); + stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg); } void mcs_get_flowid_stats(struct mcs *mcs, struct mcs_flowid_stats *stats, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index 8375f18c8e074..beb514ebda029 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -478,10 +478,11 @@ static int rvu_dbg_mcs_rx_secy_stats_display(struct seq_file *filp, void *unused seq_printf(filp, "secy%d: Tagged ctrl pkts: %lld\n", secy_id, stats.pkt_tagged_ctl_cnt); seq_printf(filp, "secy%d: Untaged pkts: %lld\n", secy_id, stats.pkt_untaged_cnt); - seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt); if (mcs->hw->mcs_blks > 1) seq_printf(filp, "secy%d: pkts notag: %lld\n", secy_id, stats.pkt_notag_cnt); + else + seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt); } mutex_unlock(&mcs->stats_lock); return 0; From da603b606ceb35d29dc88ea1e1e201a5820a06e0 Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Date: Wed, 17 Jun 2026 00:30:19 +0530 Subject: [PATCH 1480/3297] octeontx2-pf: Clear stats of all resources when freeing resources [ Upstream commit fd4460721fb4062ef470ecdfdaedadfe7e415c09 ] When all MCS resources mapped to a PF are being freed then clear stats of all those resources too. Fixes: 815debbbf7b5 ("octeontx2-pf: mcs: Clear stats before freeing resource") Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-2-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c index 060c715ebad0a..9a104dafe63f1 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -211,6 +211,7 @@ static void cn10k_mcs_free_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir, clear_req->id = hw_rsrc_id; clear_req->type = type; clear_req->dir = dir; + clear_req->all = all; req = otx2_mbox_alloc_msg_mcs_free_resources(mbox); if (!req) From 2855ec137a22404ef3fae9c2b9cfb9cfcd5b3cef Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Wed, 17 Jun 2026 00:30:20 +0530 Subject: [PATCH 1481/3297] octeontx2-pf: mcs: Fix mcs resources free on PF shutdown [ Upstream commit 450d0e90b10393bd9f50c127875a9fdd4cc81c30 ] On PF shutdown, the current driver free mcs hardware resources though mcs resources are not allocated to it. This patch checks the mcs resources status and if resources are allocated then only sends mailbox message to free them. Fixes: c54ffc73601c ("octeontx2-pf: mcs: Introduce MACSEC hardware offloading") Signed-off-by: Geetha sowjanya Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1781636420-19816-3-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c index 9a104dafe63f1..7b2deb78ea4c0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -1806,11 +1806,16 @@ int cn10k_mcs_init(struct otx2_nic *pfvf) void cn10k_mcs_free(struct otx2_nic *pfvf) { + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) return; - cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); - cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); + if (!list_empty(&cfg->txsc_list)) { + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); + } + kfree(pfvf->macsec_cfg); pfvf->macsec_cfg = NULL; } From a103cdb0681e7185d1b0a12ce6a7c5416c208ccd Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 17 Jun 2026 19:34:05 -0700 Subject: [PATCH 1482/3297] net: emac: Fix NULL pointer dereference in emac_probe [ Upstream commit f623d38fe6c4e8c40b23f42cc6fe6963fa49997b ] Move devm_request_irq() after devm_platform_ioremap_resource() so that dev->emacp is mapped before the interrupt handler can fire. An early interrupt hitting emac_irq() would dereference the NULL dev->emacp and crash. Also remove redundant error message. devm_platform_ioremap_resource() already returns an error message with dev_err_probe(). Fixes: dcc34ef7c834 ("net: ibm: emac: manage emac_irq with devm") Signed-off-by: Rosen Penev Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260618023405.415644-1-rosenp@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ibm/emac/core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 4e503b3d0d2d3..e1047915e101e 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3035,6 +3035,12 @@ static int emac_probe(struct platform_device *ofdev) if (err) goto err_gone; + dev->emacp = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(dev->emacp)) { + err = PTR_ERR(dev->emacp); + goto err_gone; + } + /* Setup error IRQ handler */ dev->emac_irq = platform_get_irq(ofdev, 0); err = devm_request_irq(&ofdev->dev, dev->emac_irq, emac_irq, 0, "EMAC", @@ -3047,13 +3053,6 @@ static int emac_probe(struct platform_device *ofdev) ndev->irq = dev->emac_irq; - dev->emacp = devm_platform_ioremap_resource(ofdev, 0); - if (IS_ERR(dev->emacp)) { - dev_err(&ofdev->dev, "can't map device registers"); - err = PTR_ERR(dev->emacp); - goto err_gone; - } - /* Wait for dependent devices */ err = emac_wait_deps(dev); if (err) From 1d51aff78f078af1a80e9496c2f4643f4c0ef0a0 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 17:57:08 -0400 Subject: [PATCH 1483/3297] net/sched: act_ct: fix nf_connlabels leak on two error paths [ Upstream commit 16e088016f38cf728a0de709c3335cc5a3850476 ] tcf_ct_fill_params() calls nf_connlabels_get() (setting put_labels) when TCA_CT_LABELS is present, but two later error sites use a bare return instead of "goto err", skipping the err: nf_connlabels_put() cleanup. They also precede the "p->put_labels = put_labels" assignment, so the tcf_ct_params_free() fallback does not release the count either. Each failed RTM_NEWACTION on these paths leaks one nf_connlabels reference: net->ct.labels_used is incremented and never released. The action is reachable with CAP_NET_ADMIN over the netns, i.e. from an unprivileged user namespace on default-userns kernels. Impact: an unprivileged user with CAP_NET_ADMIN over a network namespace (e.g. via user namespaces) leaks one nf_connlabels reference per failed RTM_NEWACTION on the two error paths; net->ct.labels_used is never released. The err: label is safe to reach from both sites: p->tmpl is still NULL there (kzalloc'd, not yet assigned) and nf_ct_put(NULL) is a no-op, so no inline release is needed. Fixes: 70f06c115bcc ("sched: act_ct: switch to per-action label counting") Signed-off-by: Michael Bommarito Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260617215708.1115818-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/act_ct.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index f9cb8f7474ed3..9bf27c02ea45d 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -1293,7 +1293,8 @@ static int tcf_ct_fill_params(struct net *net, if (tb[TCA_CT_ZONE]) { if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { NL_SET_ERR_MSG_MOD(extack, "Conntrack zones isn't enabled."); - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + goto err; } tcf_ct_set_key_val(tb, @@ -1306,7 +1307,8 @@ static int tcf_ct_fill_params(struct net *net, tmpl = nf_ct_tmpl_alloc(net, &zone, GFP_KERNEL); if (!tmpl) { NL_SET_ERR_MSG_MOD(extack, "Failed to allocate conntrack template"); - return -ENOMEM; + err = -ENOMEM; + goto err; } p->tmpl = tmpl; if (tb[TCA_CT_HELPER_NAME]) { From 2066e692ec7a10af63d9c75899c9a66946cfede9 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Sat, 20 Jun 2026 16:17:44 +0800 Subject: [PATCH 1484/3297] net: airoha: Fix skb->priority underflow in airoha_dev_select_queue() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 86e51aa24686cc95bb35613059e8b94b9b81e3f0 ] In airoha_dev_select_queue(), the expression: queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; implicitly converts to unsigned arithmetic: when skb->priority is 0 (the default for unclassified traffic), (0u - 1u) wraps to UINT_MAX, and UINT_MAX % 8 = 7, routing default best-effort packets to the highest-priority QoS queue. This causes QoS inversion where the majority of traffic on a PON gateway starves actual high-priority flows (VoIP, gaming, etc.). The "- 1" offset was a leftover from the ETS offload implementation that has since been removed. The correct mapping is a direct modulo: queue = skb->priority % AIROHA_NUM_QOS_QUEUES; This maps priority 0 → queue 0 (lowest), priority 7 → queue 7 (highest), with higher priorities wrapping around. This is the standard Linux sk_prio → HW queue mapping used by other drivers. Fixes: 2b288b81560b ("net: airoha: Introduce ndo_select_queue callback") Link: https://lore.kernel.org/netdev/178185573207.2378135.3729126358670287878@gmail.com/ Acked-by: Lorenzo Bianconi Reviewed-by: Joe Damato Signed-off-by: Wayen Yan Link: https://patch.msgid.link/178194366700.2485734.5368768965976693502@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 5e7939c4f3e6f..5efd03c2deea7 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1937,7 +1937,7 @@ static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, */ channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; channel = channel % AIROHA_NUM_QOS_CHANNELS; - queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ + queue = skb->priority % AIROHA_NUM_QOS_QUEUES; queue = channel * AIROHA_NUM_QOS_QUEUES + queue; return queue < dev->num_tx_queues ? queue : 0; From a6450f7cfae57b382cbaf66a577765c9a88b3c58 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 14:55:13 +0800 Subject: [PATCH 1485/3297] ipv6: ndisc: fix NULL deref in accept_untracked_na() [ Upstream commit d186e942365acece7c56d39da05dd63bf95b280a ] accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev) and dereferences idev->cnf.accept_untracked_na without a NULL check, even though its only caller ndisc_recv_na() already fetched and NULL-checked idev for the same device. Both reads of dev->ip6_ptr run in the same RCU read-side critical section, but a concurrent addrconf_ifdown() can clear dev->ip6_ptr between them: lowering the MTU below IPV6_MIN_MTU calls addrconf_ifdown() without the synchronize_net() that orders the unregister path, so the re-fetch returns NULL and oopses: BUG: KASAN: null-ptr-deref in ndisc_recv_na (net/ipv6/ndisc.c:974) Read of size 4 at addr 0000000000000364 Call Trace: ndisc_recv_na (net/ipv6/ndisc.c:974) icmpv6_rcv (net/ipv6/icmp.c:1193) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479) ip6_input_finish (net/ipv6/ip6_input.c:534) ip6_input (net/ipv6/ip6_input.c:545) ip6_mc_input (net/ipv6/ip6_input.c:635) ipv6_rcv (net/ipv6/ip6_input.c:351) It is reachable by an unprivileged user via a network namespace. Pass the caller's already validated idev instead of re-fetching it; the idev stays alive for the whole RCU critical section, so it is safe even after dev->ip6_ptr has been cleared. Fixes: aaa5f515b16b ("net: ipv6: new accept_untracked_na option to accept na only if in-network") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260617065512.2529757-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/ndisc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index ded2d3a0660c3..9df1fba967c6e 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -966,10 +966,8 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb) return reason; } -static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) +static int accept_untracked_na(struct inet6_dev *idev, struct in6_addr *saddr) { - struct inet6_dev *idev = __in6_dev_get(dev); - switch (READ_ONCE(idev->cnf.accept_untracked_na)) { case 0: /* Don't accept untracked na (absent in neighbor cache) */ return 0; @@ -979,7 +977,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr) * same subnet as an address configured on the interface that * received the na */ - return !!ipv6_chk_prefix(saddr, dev); + return !!ipv6_chk_prefix(saddr, idev->dev); default: return 0; } @@ -1077,7 +1075,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb) */ new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE; if (!neigh && lladdr && idev && READ_ONCE(idev->cnf.forwarding)) { - if (accept_untracked_na(dev, saddr)) { + if (accept_untracked_na(idev, saddr)) { neigh = neigh_create(&nd_tbl, &msg->target, dev); new_state = NUD_STALE; } From ea24f911ead85ba3d570a31e37c52b6c949f6928 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Thu, 18 Jun 2026 18:43:35 +0800 Subject: [PATCH 1486/3297] ipv6: ioam: fix type confusion of dst_entry [ Upstream commit 9ed19e11d2146076d117d51a940643990118449b ] IOAM uses a dummy dst_entry(null_dst) to mark that the destination should not be changed after the transformation. This dst is stored in the IOAM lwt state and may be passed to dst_cache_set_ip6(). However, the IPv6 dst cache path eventually calls rt6_get_cookie(), which treats the dst_entry as part of a struct rt6_info. Since the null_dst was embedded directly as a struct dst_entry in struct ioam6_lwt, this resulted in an invalid cast and rt6_get_cookie() reading fields from the wrong object. In practice, the wrong cookie is not used while dst->obsolete is zero, but rt6_get_cookie() may also access per-cpu value when rt->sernum is zero. In this case, rt->sernum aliases ioam6_lwt::cache::reset_ts, which can become zero, making this a potential invalid pointer access. Fix this by embedding a full struct rt6_info for the dummy IPv6 route and passing its dst member to the dst APIs. Fixes: 47ce7c854563 ("net: ipv6: ioam6: fix double reallocation") Signed-off-by: Jiayuan Chen Reviewed-by: Justin Iurman Link: https://patch.msgid.link/20260618104336.48934-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/ioam6_iptunnel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index b9f6d892a566c..cfb2c41634a0b 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -35,7 +35,7 @@ struct ioam6_lwt_freq { }; struct ioam6_lwt { - struct dst_entry null_dst; + struct rt6_info null_rt; struct dst_cache cache; struct ioam6_lwt_freq freq; atomic_t pkt_cnt; @@ -176,7 +176,7 @@ static int ioam6_build_state(struct net *net, struct nlattr *nla, * it is stored in the cache. Then, +1/-1 each time we read the cache * and release it. Long story short, we're fine. */ - dst_init(&ilwt->null_dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); + dst_init(&ilwt->null_rt.dst, NULL, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); atomic_set(&ilwt->pkt_cnt, 0); ilwt->freq.k = freq_k; @@ -360,7 +360,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) /* This is how we notify that the destination does not change after * transformation and that we need to use orig_dst instead of the cache */ - if (dst == &ilwt->null_dst) { + if (dst == &ilwt->null_rt.dst) { dst_release(dst); dst = orig_dst; @@ -429,7 +429,7 @@ static int ioam6_output(struct net *net, struct sock *sk, struct sk_buff *skb) local_bh_disable(); if (orig_dst->lwtstate == dst->lwtstate) dst_cache_set_ip6(&ilwt->cache, - &ilwt->null_dst, &fl6.saddr); + &ilwt->null_rt.dst, &fl6.saddr); else dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr); local_bh_enable(); From 93f95538b611a9f69d9f0fffc2f3cc51cd91dedd Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Thu, 18 Jun 2026 12:28:12 +0300 Subject: [PATCH 1487/3297] dpaa2-switch: do not accept VLAN uppers while bridged [ Upstream commit d07d80b6a129a44538cda1549b7acf95154fb197 ] The dpaa2-switch driver does not support VLAN uppers while its ports are bridged. This scenario tried to be prevented by rejecting a bridge join while VLAN uppers exist but the reverse order was still possible. This patches adds a check so that the dpaa2-switch also does not accept VLAN uppers while bridged. Fixes: f48298d3fbfa ("staging: dpaa2-switch: move the driver out of staging") Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260618092813.432535-2-ioana.ciornei@nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 28ac76b8783c3..789a46f6938ee 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -2198,6 +2198,7 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, static int dpaa2_switch_port_prechangeupper(struct net_device *netdev, struct netdev_notifier_changeupper_info *info) { + struct ethsw_port_priv *port_priv; struct netlink_ext_ack *extack; struct net_device *upper_dev; int err; @@ -2216,6 +2217,13 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev, if (!info->linking) dpaa2_switch_port_pre_bridge_leave(netdev); + } else if (is_vlan_dev(upper_dev)) { + port_priv = netdev_priv(netdev); + if (port_priv->fdb->bridge_dev) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot accept VLAN uppers while bridged"); + return -EOPNOTSUPP; + } } return 0; From 56e5f8a409f8c62584f96658f74a5a95043d6555 Mon Sep 17 00:00:00 2001 From: Antoni Pokusinski Date: Wed, 15 Apr 2026 18:06:11 +0200 Subject: [PATCH 1488/3297] rtc: abx80x: fix the RTC_VL_CLR clearing all status flags [ Upstream commit 419719c514252a2dbb2e2976f564c83417dd6d0a ] The RTC_VL_CLR ioctl intends to clear only the battery low flag (BLF), however the current implementation writes 0 to the status register, clearing all status bits. Fix this by writing back the masked status value so that only BLF is cleared, preserving other status flags. Fixes: ffe1c5a2d427 ("rtc: abx80x: Implement RTC_VL_READ,CLR ioctls") Signed-off-by: Antoni Pokusinski Link: https://patch.msgid.link/20260415160610.127155-2-apokusinski01@gmail.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-abx80x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 5f3a3e60a19d0..862833519d7f4 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -545,7 +545,8 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) status &= ~ABX8XX_STATUS_BLF; - tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0); + tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, + status); if (tmp < 0) return tmp; From aa33b44f70bfec8d431005c42706104a25062e16 Mon Sep 17 00:00:00 2001 From: Ronan Dalton Date: Fri, 8 May 2026 15:24:49 +1200 Subject: [PATCH 1489/3297] rtc: ds1307: handle oscillator stop flag for ds1337/ds1339/ds3231 [ Upstream commit a091e1ba3b68cabc9caedafc6f81d9fe9b3b2200 ] Prior to commit 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe"), the oscillator stop flag (OSF) bit was checked during device probe for the ds1337, ds1339, ds1341, and ds3231 chips; if it was set, it would be cleared and a warning would be logged saying "SET TIME!". Since that commit, the OSF bit is no longer cleared, but the warning is still printed. Directly following that commit, there was no way to get rid of this warning because nothing cleared the OSF bit on these chips. The commit associated with the previous commit, 523923cfd5d6 ("rtc: ds1307: handle oscillator stop flag (OSF) for ds1341"), made proper use of the OSF when getting and setting the time in the RTC. However, the other RTC variants ds1337, ds1339 and ds3231 didn't have a corresponding change made. Given that the OSF bit is no longer cleared at probe time when it is set, the remaining three chips should have the same handling as the ds1341 chip has for the OSF bit. Fix the issue on the ds1337, ds1339 and ds3231 chips by applying the same logic as the ds1341 has to these chips. Note that any devices brought up between the first referenced commit and this one may begin mistrusting the time reported by the RTC until it is set again, if the bit was never explicitly cleared. Note that only the ds1339 was tested with this change, but the datasheets for the other chips contain essentially identical descriptions of the OSF bit so the same change should work. Signed-off-by: Ronan Dalton Cc: linux-rtc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: Alexandre Belloni Cc: Tyler Hicks Cc: Sasha Levin Cc: Meagan Lloyd Cc: Rodolfo Giometti Cc: Chris Packham Fixes: 48458654659c ("rtc: ds1307: remove clear of oscillator stop flag (OSF) in probe") Reviewed-by: Meagan Lloyd Reviewed-by: Tyler Hicks Link: https://patch.msgid.link/20260508032518.3696705-2-ronan.dalton@alliedtelesis.co.nz Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-ds1307.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 7205c59ff7294..edf81b975decb 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1338_BIT_OSF) return -EINVAL; break; + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); + if (ret) + return ret; + if (tmp & DS1337_BIT_OSF) + return -EINVAL; + break; case ds_1340: if (tmp & DS1340_BIT_nEOSC) return -EINVAL; @@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1340_BIT_OSF) return -EINVAL; break; - case ds_1341: - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); - if (ret) - return ret; - if (tmp & DS1337_BIT_OSF) - return -EINVAL; - break; case ds_1388: ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp); if (ret) @@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, DS1338_BIT_OSF, 0); break; - case ds_1340: - regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, - DS1340_BIT_OSF, 0); - break; + case ds_1337: + case ds_1339: case ds_1341: + case ds_3231: regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, DS1337_BIT_OSF, 0); break; + case ds_1340: + regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, + DS1340_BIT_OSF, 0); + break; case ds_1388: regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, DS1388_BIT_OSF, 0); From 68b41e68a62299f26cb16af422b5c7d1c69dafd9 Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Thu, 18 Jun 2026 01:50:26 +0800 Subject: [PATCH 1490/3297] bpf: Fix stack slot index in nospec checks [ Upstream commit d1d53aa30ab3b5ae89161c9cc840b3f7489ad386 ] check_stack_write_fixed_off() computes the byte slot for a fixed-offset stack write as -off - 1, and records each written byte in slot_type[] with (slot - i) % BPF_REG_SIZE. The Spectre v4 sanitization pre-check uses slot_type[i] instead. For a 4-byte write at fp-8 after the lower half of fp-8 has been zeroed, the pre-check scans bytes 0..3 and sees STACK_ZERO while the actual write updates bytes 7..4. That can leave the second half-slot write without nospec_result even though the bytes being overwritten still require sanitization. Use the same slot index in the sanitization pre-check that the write path uses when updating slot_type[]. Fixes: 2039f26f3aca ("bpf: Fix leakage due to insufficient speculative store bypass mitigation") Acked-by: Luis Gerhorst Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Signed-off-by: Nuoqi Gui Link: https://lore.kernel.org/r/20260618-f01-11-stack-nospec-slot-index-v3-1-780297041721@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index bf8cadd19593b..99fa175e8ef89 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5143,7 +5143,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, bool sanitize = reg && is_spillable_regtype(reg->type); for (i = 0; i < size; i++) { - u8 type = state->stack[spi].slot_type[i]; + u8 type = state->stack[spi].slot_type[(slot - i) % + BPF_REG_SIZE]; if (type != STACK_MISC && type != STACK_ZERO) { sanitize = true; From b05337635be3cac6f7c26fb56773d24e86ee0084 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 17 Jun 2026 17:01:17 +0800 Subject: [PATCH 1491/3297] bpftool: Fix vmlinux BTF leak in cgroup commands [ Upstream commit bda6a7308ef8e79cfbb7d09e48e1c7ffaa522269 ] bpftool cgroup show and tree call libbpf_find_kernel_btf() to resolve attach_btf names, but never release the returned BTF object. For cgroup tree, do_show_tree_fn() is called once for each cgroup visited by nftw(). When more than one cgroup has attached programs, each callback overwrites btf_vmlinux with a new object and loses the previous allocation. Load vmlinux BTF only once during a tree walk and release it when cgroup show or tree completes. Reset btf_vmlinux_id at the same time so batch mode starts with clean state. Fixes: 596f5fb2ea2a ("bpftool: implement cgroup tree for BPF_LSM_CGROUP") Signed-off-by: Yichong Chen Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/r/24357C69B4405079+20260617090117.280222-1-chenyichong@uniontech.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- tools/bpf/bpftool/cgroup.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index ec356deb27c9e..ce69d1e5468e3 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -78,6 +78,13 @@ static unsigned int query_flags; static struct btf *btf_vmlinux; static __u32 btf_vmlinux_id; +static void free_btf_vmlinux(void) +{ + btf__free(btf_vmlinux); + btf_vmlinux = NULL; + btf_vmlinux_id = 0; +} + static enum bpf_attach_type parse_attach_type(const char *str) { const char *attach_type_str; @@ -388,6 +395,8 @@ static int do_show(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); + exit_cgroup: close(cgroup_fd); exit: @@ -437,7 +446,9 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb, printf("%s\n", fpath); } - btf_vmlinux = libbpf_find_kernel_btf(); + if (!btf_vmlinux) + btf_vmlinux = libbpf_find_kernel_btf(); + for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) show_bpf_progs(cgroup_fd, cgroup_attach_types[i], ftw->level); @@ -540,6 +551,7 @@ static int do_show_tree(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); free(cgroup_alloced); return ret; From 3bdfa0e435f3198494bdb0f3f87c2fe7fc955191 Mon Sep 17 00:00:00 2001 From: Avinash Duduskar Date: Thu, 18 Jun 2026 04:17:19 +0530 Subject: [PATCH 1492/3297] bpf: zero-initialize the fib lookup flow struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0dfcb68a6a5ac517b22dff6a1f01cb4f126dfc57 ] bpf_ipv4_fib_lookup() and bpf_ipv6_fib_lookup() build the flow key on the stack with a bare "struct flowi4 fl4;" / "struct flowi6 fl6;" and fill it field by field, but never set flowi4_l3mdev / flowi6_l3mdev. On the non-DIRECT path the lookup goes through the fib rules whenever the netns has custom rules, which a VRF installs: bpf_ipv4_fib_lookup() -> fib_lookup() -> __fib_lookup() -> l3mdev_update_flow() reads !fl->flowi_l3mdev -> fib_rules_lookup() -> fib_rule_match() -> l3mdev_fib_rule_match() uses fl->flowi_l3mdev l3mdev_update_flow() resolves the l3mdev master from the ingress device only while the field is still zero. Left at a nonzero stack value the resolution is skipped, and l3mdev_fib_rule_match() then tests that value as an ifindex, so the VRF master is not resolved and the rule fails to match: an ingress enslaved to a VRF can fail to select its table. FIB rules matching on an L3 master device (l3mdev_fib_rule_iif_match()/ _oif_match()) read the same value, so an "ip rule iif/oif " mismatches the same way. Zero-initialize the whole flow struct rather than adding one more field assignment, so any flowi field added later is covered too. ip_route_input_slow() likewise zeroes the field before its input lookup. CONFIG_INIT_STACK_ALL_ZERO masks this by default, but it depends on compiler support (CC_HAS_AUTO_VAR_INIT_ZERO), so INIT_STACK_NONE builds, including older toolchains that fall back to it, are exposed. Built with INIT_STACK_ALL_PATTERN, a plain bpf_fib_lookup (no VLAN, no DIRECT) over a VRF slave whose destination is routed only in the VRF table returns BPF_FIB_LKUP_RET_NOT_FWDED, and resolves with this patch. On the default config the lookup succeeds either way, so ordinary testing does not catch the bug. Fixes: 40867d74c374 ("net: Add l3mdev index to flow struct and avoid oif reset for port devices") Signed-off-by: Avinash Duduskar Reviewed-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20260617224719.1428599-1-avinash.duduskar@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- net/core/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index e91659caea0f8..a3e6cd9c4a78b 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6076,7 +6076,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct neighbour *neigh; struct net_device *dev; struct fib_result res; - struct flowi4 fl4; + struct flowi4 fl4 = {}; u32 mtu = 0; int err; @@ -6216,7 +6216,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct neighbour *neigh; struct net_device *dev; struct inet6_dev *idev; - struct flowi6 fl6; + struct flowi6 fl6 = {}; int strict = 0; int oif, err; u32 mtu = 0; From 9697db03e010391c55ae75192cbdf30c5a72c114 Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Thu, 18 Jun 2026 23:35:19 -0700 Subject: [PATCH 1493/3297] bpf: Fix effective prog array index with BPF_F_PREORDER [ Upstream commit f08aaee3152d0dfc578b3f2586932d82062701dd ] replace_effective_prog() and purge_effective_progs() located the slot in the effective array by walking the program hlist and counting entries linearly. That count does not match the array layout: compute_effective_ progs() places BPF_F_PREORDER programs at the front (ancestor cgroup first, attach order within a cgroup) and the rest after them (descendant cgroup first). So when a preorder program is present, the linear hlist position no longer equals the program's index in the effective array. For replace_effective_prog() (bpf_link_update()) this overwrote the wrong slot, corrupting the effective order. For purge_effective_progs(), it could dummy out a slot belonging to a different program and leave the detached program in the array while bpf_prog_put() drops its reference, i.e. a use-after-free. Fix both by replaying compute_effective_progs()'s placement (including the per-cgroup preorder reversal) in a shared effective_prog_pos() helper. Identify the entry by its struct bpf_prog_list pointer rather than by (prog, link) value, so the lookup resolves to exactly the attachment the syscall selected even when the same bpf_prog is attached to several cgroups in the hierarchy. Fixes: 4b82b181a26c ("bpf: Allow pre-ordering for bpf cgroup progs") Signed-off-by: Amery Hung Acked-by: Yonghong Song Link: https://lore.kernel.org/r/20260619063520.2690547-2-ameryhung@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/cgroup.c | 108 +++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index c00e440fb36b6..fc2f44b60a551 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -939,19 +939,65 @@ static int cgroup_bpf_attach(struct cgroup *cgrp, return ret; } +static int effective_prog_pos(struct cgroup *cgrp, + enum cgroup_bpf_attach_type atype, + struct bpf_prog_list *target_pl) +{ + int cnt = 0, preorder_cnt = 0, fstart, bstart, init_bstart, pos = -1; + struct bpf_prog_list *pl; + struct cgroup *p = cgrp; + + /* count effective programs to find where the preorder region ends */ + do { + if (cnt == 0 || (p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + cnt += prog_list_length(&p->bpf.progs[atype], &preorder_cnt); + p = cgroup_parent(p); + } while (p); + + /* replay compute_effective_progs() placement and record target's slot */ + cnt = 0; + p = cgrp; + fstart = preorder_cnt; + bstart = preorder_cnt - 1; + do { + if (cnt > 0 && !(p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + continue; + + init_bstart = bstart; + hlist_for_each_entry(pl, &p->bpf.progs[atype], node) { + if (!prog_list_prog(pl)) + continue; + + if (pl->flags & BPF_F_PREORDER) { + if (pl == target_pl) + pos = bstart; + bstart--; + } else { + if (pl == target_pl) + pos = fstart; + fstart++; + } + cnt++; + } + + /* reverse pre-ordering progs at this cgroup level */ + if (pos >= bstart + 1 && pos <= init_bstart) + pos = bstart + 1 + init_bstart - pos; + } while ((p = cgroup_parent(p))); + + return pos; +} + /* Swap updated BPF program for given link in effective program arrays across * all descendant cgroups. This function is guaranteed to succeed. */ static void replace_effective_prog(struct cgroup *cgrp, enum cgroup_bpf_attach_type atype, - struct bpf_cgroup_link *link) + struct bpf_prog_list *pl) { struct bpf_prog_array_item *item; struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; css_for_each_descendant_pre(css, &cgrp->self) { @@ -960,27 +1006,15 @@ static void replace_effective_prog(struct cgroup *cgrp, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; + pos = effective_prog_pos(desc, atype, pl); + if (WARN_ON_ONCE(pos < 0)) + continue; - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->link == link) - goto found; - pos++; - } - } -found: - BUG_ON(!cg); progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); item = &progs->items[pos]; - WRITE_ONCE(item->prog, link->link.prog); + WRITE_ONCE(item->prog, pl->link->link.prog); } } @@ -1024,7 +1058,7 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp, cgrp->bpf.revisions[atype] += 1; old_prog = xchg(&link->link.prog, new_prog); - replace_effective_prog(cgrp, atype, link); + replace_effective_prog(cgrp, atype, pl); bpf_prog_put(old_prog); return 0; } @@ -1091,19 +1125,14 @@ static struct bpf_prog_list *find_detach_entry(struct hlist_head *progs, * recomputing the array in place. * * @cgrp: The cgroup which descendants to travers - * @prog: A program to detach or NULL - * @link: A link to detach or NULL + * @pl: The prog_list entry being detached * @atype: Type of detach operation */ -static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, - struct bpf_cgroup_link *link, +static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog_list *pl, enum cgroup_bpf_attach_type atype) { struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; /* recompute effective prog array in place */ @@ -1113,24 +1142,11 @@ static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link or prog in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; - - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->prog == prog && pl->link == link) - goto found; - pos++; - } - } - + pos = effective_prog_pos(desc, atype, pl); /* no link or prog match, skip the cgroup of this layer */ - continue; -found: + if (pos < 0) + continue; + progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); @@ -1196,7 +1212,7 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, /* if update effective array failed replace the prog with a dummy prog*/ pl->prog = old_prog; pl->link = link; - purge_effective_progs(cgrp, old_prog, link, atype); + purge_effective_progs(cgrp, pl, atype); } /* now can actually delete it from this cgroup list */ From 7550becf33014f851afa25c5393a41ab13a60ab2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 18 Jun 2026 10:45:12 +0200 Subject: [PATCH 1494/3297] power: sequencing: fix ABBA deadlock in pwrseq_device_unregister() [ Upstream commit 2d5a7d406ecece5837af1e278ffbbf6c0315560a ] The pwrseq core takes three locks in consistent order everywhere: pwrseq_sem -> pwrseq->rw_lock -> pwrseq->state_lock pwrseq_get() -> pwrseq_match_device() takes pwrseq_sem for reading, then rw_lock for reading. pwrseq_power_on()/pwrseq_power_off() take rw_lock for reading and then state_lock. pwrseq_device_unregister() is the only exception, it takes: state_lock, then rw_lock for writing and finally pwrseq_sem for writing. This created two potential ABBA deadlock situations that sashiko pointed out. - pwrseq_power_on/off() take rw_lock for reading then state_lock, while pwrseq_unregister() takes state_lock then rw_lock for writing - pwrseq_get() takes pwrseq_sem for reading then rw_lock for reading, while pwrseq_unregister() takes rw_lock for writing then pwrseq_sem for writing Reorder the unregister path to taking pwrseq_sem for writing -> rw_lock for writing and drop the state_lock entirely. This is safe as enable_count is only ever written under rw_lock held for read (via pwrseq_unit_enable()/disable(), reached only from pwrseq_power_on/off()), so holding rw_lock for writing already excludes every other writer and reader and the active-users WARN() stays race-free without state_lock. Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Closes: https://sashiko.dev/#/patchset/20260616151049.1705503-1-vulab%40iscas.ac.cn Link: https://patch.msgid.link/20260618-pwrseq-abba-deadlock-v1-1-943a3fd81c06@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/power/sequencing/core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index d5da006e1ee85..0a1bc6c20c11b 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -543,15 +543,18 @@ void pwrseq_device_unregister(struct pwrseq_device *pwrseq) struct device *dev = &pwrseq->dev; struct pwrseq_target *target; - scoped_guard(mutex, &pwrseq->state_lock) { + scoped_guard(rwsem_write, &pwrseq_sem) { guard(rwsem_write)(&pwrseq->rw_lock); + /* + * Holding rw_lock for write excludes all power on/off callers + * (they hold it for read), so it's safe to read enable_count + * here without taking the state_lock. + */ list_for_each_entry(target, &pwrseq->targets, list) WARN(target->unit->enable_count, "REMOVING POWER SEQUENCER WITH ACTIVE USERS\n"); - guard(rwsem_write)(&pwrseq_sem); - device_del(dev); } From 5e4c4ab99abc906fd2c73e284df547e2adea236b Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sat, 20 Jun 2026 23:53:19 +0800 Subject: [PATCH 1495/3297] gpiolib: initialize return value in gpiochip_set_multiple() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 99dfa46baba29513d1094c8f30bc86c6ef88543a ] gpiochip_set_multiple() falls back to setting lines one by one when the chip does not provide set_multiple(). If the fallback path receives an empty mask, the loop is skipped and ret is returned without being initialized. Initialize ret to 0 so an empty mask is treated as a successful no-op. Fixes: 9b407312755f ("gpiolib: rework the wrapper around gpio_chip::set_multiple()") Signed-off-by: Ruoyu Wang Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260620155319.79994-1-ruoyuw560@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 67f362d53d53f..9d3c33e1c9f99 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3657,7 +3657,7 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { unsigned int i; - int ret; + int ret = 0; lockdep_assert_held(&gc->gpiodev->srcu); From 9cc0f8e63e8c34cf43def35cbd305ba711181a1f Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 15 Jun 2026 11:47:37 -0700 Subject: [PATCH 1496/3297] drm/edid: fix OOB read in drm_parse_tiled_block() [ Upstream commit faaa1e1155833e7d4ce7e3cfaf64c0d636b190db ] drm_parse_tiled_block() casts the DisplayID block to a struct displayid_tiled_block and reads the full fixed layout up to tile->topology_id[7] without checking block->num_bytes. The DisplayID iterator only validates the declared payload length, so a crafted EDID can advertise a tiled-display block (tag DATA_BLOCK_TILED_DISPLAY, or DATA_BLOCK_2_TILED_DISPLAY_TOPOLOGY for v2.0) with a small num_bytes at the end of a DisplayID extension. The read then runs past the end of the exact-sized kmemdup()'d EDID allocation, a heap out-of-bounds read. Reject blocks shorter than the spec's 22-byte tiled payload before reading the fixed struct, as drm_parse_vesa_mso_data() already does. BUG: KASAN: slab-out-of-bounds in drm_edid_connector_update Read of size 2 at addr ffff888010077700 by task exploit/147 dump_stack_lvl (lib/dump_stack.c:94 ...) print_report (mm/kasan/report.c:378 ...) kasan_report (mm/kasan/report.c:595) drm_edid_connector_update (drivers/gpu/drm/drm_edid.c:7581) bochs_connector_helper_get_modes (drivers/gpu/drm/tiny/bochs.c:574) drm_helper_probe_single_connector_modes (drivers/gpu/drm/drm_probe_helper.c:426) status_store (drivers/gpu/drm/drm_sysfs.c:219) ... vfs_write (fs/read_write.c:595 fs/read_write.c:688) ksys_write (fs/read_write.c:740) Fixes: 40d9b043a89e ("drm/connector: store tile information from displayid (v3)") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Reviewed-by: Jani Nikula Link: https://patch.msgid.link/20260615184737.899892-1-xmei5@asu.edu Signed-off-by: Jani Nikula Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_edid.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e2e85345aa9a4..046b5e86a7873 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -7500,6 +7500,14 @@ static void drm_parse_tiled_block(struct drm_connector *connector, u8 num_v_tile, num_h_tile; struct drm_tile_group *tg; + /* tiled block payload per spec: cap 1 + topo 3 + size 4 + bezel 5 + id 9 = 22 */ + if (block->num_bytes < 22) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Unexpected tiled block size %u\n", + connector->base.id, connector->name, block->num_bytes); + return; + } + w = tile->tile_size[0] | tile->tile_size[1] << 8; h = tile->tile_size[2] | tile->tile_size[3] << 8; From e1e7c72a2301d6f74ec981fc459fbe5cd495a64c Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 7 Apr 2026 18:14:20 +0530 Subject: [PATCH 1497/3297] PCI: endpoint: pci-epf-vntb: Add check to detect 'db_count' value of 0 [ Upstream commit 33bd1ea748bc897c4d13437284e08c658e8d1340 ] epf_ntb->db_count value should be within 1 to MAX_DB_COUNT. Current code only checks for the upper bound, while the lower bound is unchecked. This can cause a lot of issues in the driver if the user passes 'db_count' as 0. Add a check for 0 also. While at it, remove the redundant 'db_count' assignment. Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260407124421.282766-2-mani@kernel.org Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 750a246f79c92..d3d0780fb4816 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -466,7 +466,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) { const struct pci_epc_features *epc_features; struct device *dev; - u32 db_count; int ret; dev = &ntb->epf->dev; @@ -478,14 +477,12 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return -EINVAL; } - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; - if (epc_features->msi_capable) { ret = pci_epc_set_msi(ntb->epf->epc, ntb->epf->func_no, From 9415a94cf62273b98a04635cedee5e7be2e2dd54 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 7 Apr 2026 18:14:21 +0530 Subject: [PATCH 1498/3297] PCI: endpoint: pci-epf-ntb: Add check to detect 'db_count' value of 0 [ Upstream commit d1db6d7c2485ee475a04d8354fbb5b26ea10d978 ] epf_ntb->db_count value should be within 1 to MAX_DB_COUNT. Current code only checks for the upper bound, while the lower bound is unchecked. This can cause a lot of issues in the driver if the user passes 'db_count' as 0. Add a check for 0 also. While at it, remove the redundant 'db_count' variable from epf_ntb_configure_interrupt(). Fixes: 8b821cf76150 ("PCI: endpoint: Add EP function driver to provide NTB functionality") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20260407124421.282766-3-mani@kernel.org Signed-off-by: Sasha Levin --- drivers/pci/endpoint/functions/pci-epf-ntb.c | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 7702ebb81d996..d12cf226f3282 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -559,12 +559,15 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb, struct pci_epc *epc; int ret; - if (db_count > MAX_DB_COUNT) - return -EINVAL; - ntb_epc = ntb->epc[type]; epc = ntb_epc->epc; + if (!db_count || db_count > MAX_DB_COUNT) { + dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n", + db_count, MAX_DB_COUNT); + return -EINVAL; + } + if (msix) ret = epf_ntb_configure_msix(ntb, type, db_count); else @@ -1278,7 +1281,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, u8 func_no, vfunc_no; struct pci_epc *epc; struct device *dev; - u32 db_count; int ret; ntb_epc = ntb->epc[type]; @@ -1296,17 +1298,16 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, func_no = ntb_epc->func_no; vfunc_no = ntb_epc->vfunc_no; - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; epc = ntb_epc->epc; if (msi_capable) { - ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count); + ret = pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count); if (ret) { dev_err(dev, "%s intf: MSI configuration failed\n", pci_epc_interface_string(type)); @@ -1315,7 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, } if (msix_capable) { - ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count, + ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count, ntb_epc->msix_bar, ntb_epc->msix_table_offset); if (ret) { From dd6d8e4412f805937f61f00bbfdfe831978be235 Mon Sep 17 00:00:00 2001 From: Dawid Osuchowski Date: Fri, 27 Mar 2026 08:22:32 +0100 Subject: [PATCH 1499/3297] ice: fix FDIR CTRL VSI resource leak in ice_reset_all_vfs() [ Upstream commit ebbe8868cf473f698e0fbaf436d2618b2bcda806 ] Resetting all VFs causes resource leak on VFs with FDIR filters enabled as CTRL VSIs are only invalidated and not freed. Fix by using ice_vf_ctrl_vsi_release() instead of ice_vf_ctrl_invalidate_vsi() which aligns behavior with the ice_reset_vf() function. Reproduction: echo 1 > /sys/class/net/$pf/device/sriov_numvfs ethtool -N $vf flow-type ether proto 0x9000 action 0 echo 1 > /sys/class/net/$pf/device/reset Fixes: da62c5ff9dcd ("ice: Add support for per VF ctrl VSI enabling") Signed-off-by: Dawid Osuchowski Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index e53a1e4247cb1..4f86412a9c0c3 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -801,7 +801,7 @@ void ice_reset_all_vfs(struct ice_pf *pf) * setup only when VF creates its first FDIR rule. */ if (vf->ctrl_vsi_idx != ICE_NO_VSI) - ice_vf_ctrl_invalidate_vsi(vf); + ice_vf_ctrl_vsi_release(vf); ice_vf_pre_vsi_rebuild(vf); if (ice_vf_rebuild_vsi(vf)) { From 04c082b7dc5bf4c6e239ba4ed00e6eb4819cde76 Mon Sep 17 00:00:00 2001 From: Lukasz Czapnik Date: Fri, 27 Mar 2026 08:22:35 +0100 Subject: [PATCH 1500/3297] ice: fix AQ error code comparison in ice_set_pauseparam() [ Upstream commit 2bf7744bc3221a63b95c76c94eab1dad832fa401 ] Fix unreachable code: the conditionals in ice_set_pauseparam() used the bitwise-AND operator suggesting aq_failures is a bitmap, but it is actually an enum, making the third condition logically unreachable. Replace the if-else ladder with a switch statement. Also move the aq_failures initialization to the variable declaration and remove the redundant zeroing from ice_set_fc(). Fixes: fcea6f3da546 ("ice: Add stats and ethtool support") Signed-off-by: Lukasz Czapnik Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_common.c | 1 - drivers/net/ethernet/intel/ice/ice_ethtool.c | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 4dcc3b41800b3..f71e45ab809e0 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -3919,7 +3919,6 @@ ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool ena_auto_link_update) if (!pi || !aq_failures) return -EINVAL; - *aq_failures = 0; hw = pi->hw; pcaps = kzalloc(sizeof(*pcaps), GFP_KERNEL); diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index 912bcf9fce52d..ce0f8106dcd4e 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -3488,7 +3488,7 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) struct ice_vsi *vsi = np->vsi; struct ice_hw *hw = &pf->hw; struct ice_port_info *pi; - u8 aq_failures; + u8 aq_failures = 0; bool link_up; u32 is_an; int err; @@ -3559,18 +3559,22 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) /* Set the FC mode and only restart AN if link is up */ err = ice_set_fc(pi, &aq_failures, link_up); - if (aq_failures & ICE_SET_FC_AQ_FAIL_GET) { + switch (aq_failures) { + case ICE_SET_FC_AQ_FAIL_GET: netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; - } else if (aq_failures & ICE_SET_FC_AQ_FAIL_SET) { + break; + case ICE_SET_FC_AQ_FAIL_SET: netdev_info(netdev, "Set fc failed on the set_phy_config call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; - } else if (aq_failures & ICE_SET_FC_AQ_FAIL_UPDATE) { + break; + case ICE_SET_FC_AQ_FAIL_UPDATE: netdev_info(netdev, "Set fc failed on the get_link_info call with err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); err = -EAGAIN; + break; } return err; From 4cc632fe63df8b1f849573758700ed80b9cb7d3b Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Wed, 8 Apr 2026 16:14:29 +0200 Subject: [PATCH 1501/3297] ice: call netif_keep_dst() once when entering switchdev mode [ Upstream commit c0d00c882bc432990d57052a659f9a8bd1f60687 ] netif_keep_dst() only needs to be called once for the uplink VSI, not once for each port representor. Move it from ice_eswitch_setup_repr() to ice_eswitch_enable_switchdev(). Fixes: defd52455aee ("ice: do Tx through PF netdev in slow-path") Signed-off-by: Marcin Szycik Signed-off-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Tested-by: Patryk Holda Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_eswitch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index 2e4f0969035f7..c30e27bbfe6e2 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -117,8 +117,6 @@ static int ice_eswitch_setup_repr(struct ice_pf *pf, struct ice_repr *repr) if (!repr->dst) return -ENOMEM; - netif_keep_dst(uplink_vsi->netdev); - dst = repr->dst; dst->u.port_info.port_id = vsi->vsi_num; dst->u.port_info.lower_dev = uplink_vsi->netdev; @@ -312,6 +310,8 @@ static int ice_eswitch_enable_switchdev(struct ice_pf *pf) if (ice_eswitch_br_offloads_init(pf)) goto err_br_offloads; + netif_keep_dst(uplink_vsi->netdev); + pf->eswitch.is_running = true; return 0; From 854065a75e375e570495df40e2fe9e94115250e4 Mon Sep 17 00:00:00 2001 From: John Madieu Date: Sat, 25 Apr 2026 15:49:59 +0000 Subject: [PATCH 1502/3297] rtc: isl1208: Balance enable_irq_wake() with disable_irq_wake() on cleanup [ Upstream commit 1afe4f19d6ad404621150f0e91feeccf12fb1037 ] isl1208_setup_irq() calls enable_irq_wake() after a successful IRQ request, but the driver has no remove path that balances it. The driver is devm-only, so on unbind devm releases the IRQ - but enable_irq_wake() is not undone by IRQ release, so the wake count for that IRQ stays incremented. Each rebind therefore leaks one wake reference; the leak doubles for the chip variant that has a separate evdet IRQ, since isl1208_setup_irq() is then called twice during probe. Register a devm action that calls disable_irq_wake() per IRQ. While at it, check enable_irq_wake()'s return value: on failure, propagate the error rather than silently registering a disable action for an IRQ whose wake state was never enabled. Fixes: 9ece7cd833a3 ("rtc: isl1208: Add "evdet" interrupt source for isl1219") Signed-off-by: John Madieu Link: https://patch.msgid.link/20260425154959.2796261-3-john.madieu.xa@bp.renesas.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-isl1208.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index f71a6bb77b2a1..a0df53991fbdb 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -822,6 +822,11 @@ static const struct nvmem_config isl1208_nvmem_config = { .reg_write = isl1208_nvmem_write, }; +static void isl1208_disable_irq_wake_action(void *data) +{ + disable_irq_wake((unsigned long)data); +} + static int isl1208_setup_irq(struct i2c_client *client, int irq) { int rc = devm_request_threaded_irq(&client->dev, irq, NULL, @@ -831,7 +836,15 @@ static int isl1208_setup_irq(struct i2c_client *client, int irq) client); if (!rc) { device_init_wakeup(&client->dev, true); - enable_irq_wake(irq); + rc = enable_irq_wake(irq); + if (rc) + return rc; + + rc = devm_add_action_or_reset(&client->dev, + isl1208_disable_irq_wake_action, + (void *)(unsigned long)irq); + if (rc) + return rc; } else { dev_err(&client->dev, "Unable to request irq %d, no alarm support\n", From eaffdd113f560b2c41e1860c94223003d754e945 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Fri, 29 May 2026 13:37:32 +0800 Subject: [PATCH 1503/3297] ice: dpll: set pointers to NULL after kfree in ice_dpll_deinit_info [ Upstream commit a903afff66d7379c6ece42bd18b2a17f4c79d1a9 ] ice_dpll_deinit_info() calls kfree() on several pf->dplls fields (inputs, outputs, eec.input_prio, pps.input_prio) but does not set the pointers to NULL afterward. This leaves dangling pointers in the pf->dplls structure. While not currently exploitable through existing code paths, this is unsafe because: 1. If ice_dpll_init_info() is called again after a deinit (e.g. during driver recovery), and a subsequent allocation within init fails, the error path will jump to deinit_info and call ice_dpll_deinit_info() again. Since some pointers still hold the old freed addresses, this would result in a double-free. 2. Any future code that checks these pointers before use or after free would be unprotected against use-after-free. Follow the common kernel convention of setting pointers to NULL after kfree() so that: - kfree(NULL) is a safe no-op, preventing double-free - NULL checks on these pointers become meaningful This is a preparatory fix for a subsequent patch that routes additional error paths in ice_dpll_init_info() to the deinit_info label. Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: ZhaoJinming Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_dpll.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 81267bae0e5cb..f3a7129d91d3e 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -3772,9 +3772,13 @@ ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type) static void ice_dpll_deinit_info(struct ice_pf *pf) { kfree(pf->dplls.inputs); + pf->dplls.inputs = NULL; kfree(pf->dplls.outputs); + pf->dplls.outputs = NULL; kfree(pf->dplls.eec.input_prio); + pf->dplls.eec.input_prio = NULL; kfree(pf->dplls.pps.input_prio); + pf->dplls.pps.input_prio = NULL; } /** From de80d04b13deca47a1f05e0bee6488cffe0674b5 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Fri, 29 May 2026 13:37:33 +0800 Subject: [PATCH 1504/3297] ice: dpll: fix memory leak in ice_dpll_init_info error paths [ Upstream commit 20da495f2df0fd4adc435d3d621366e8c807539c ] Several error return paths in ice_dpll_init_info() directly return without freeing previously allocated resources, causing memory leaks: - When de->input_prio allocation fails, d->inputs is leaked - When dp->input_prio allocation fails, d->inputs and de->input_prio are leaked - When ice_get_cgu_rclk_pin_info() fails, all previously allocated inputs/outputs/input_prio are leaked - When ice_dpll_init_pins_info(RCLK_INPUT) fails, same resources are leaked Fix this by jumping to the deinit_info label which properly calls ice_dpll_deinit_info() to free all allocated resources. Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: ZhaoJinming Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_dpll.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index f3a7129d91d3e..023d0de12670c 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -3826,12 +3826,16 @@ static int ice_dpll_init_info(struct ice_pf *pf, bool cgu) alloc_size = sizeof(*de->input_prio) * d->num_inputs; de->input_prio = kzalloc(alloc_size, GFP_KERNEL); - if (!de->input_prio) - return -ENOMEM; + if (!de->input_prio) { + ret = -ENOMEM; + goto deinit_info; + } dp->input_prio = kzalloc(alloc_size, GFP_KERNEL); - if (!dp->input_prio) - return -ENOMEM; + if (!dp->input_prio) { + ret = -ENOMEM; + goto deinit_info; + } ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT); if (ret) @@ -3856,12 +3860,12 @@ static int ice_dpll_init_info(struct ice_pf *pf, bool cgu) ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx, &pf->dplls.rclk.num_parents); if (ret) - return ret; + goto deinit_info; for (i = 0; i < pf->dplls.rclk.num_parents; i++) pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i; ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT); if (ret) - return ret; + goto deinit_info; de->mode = DPLL_MODE_AUTOMATIC; dp->mode = DPLL_MODE_AUTOMATIC; From 68115a7a336fc9d1e60058a3565da071a4c91d19 Mon Sep 17 00:00:00 2001 From: Mohamed Khalfella Date: Wed, 6 May 2026 15:41:23 -0700 Subject: [PATCH 1505/3297] i40e: Fix i40e_debug() to use struct i40e_hw argument [ Upstream commit 798f94603eb0737033861efd320a9159f382a7c5 ] i40e_debug() macro takes struct i40e_hw *h as first argument. But the macro body uses hw instead of h. This has been working so far because hw happens to be the name of the variable in the context where the macro is expanded. Fix the macro to use the passed argument. Fixes: 5dfd37c37a44 ("i40e: Split i40e_osdep.h") Signed-off-by: Mohamed Khalfella Reviewed-by: Aleksandr Loktionov Reviewed-by: Paul Menzel Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/i40e/i40e_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_debug.h b/drivers/net/ethernet/intel/i40e/i40e_debug.h index e9871dfb32bd4..01fd70db90866 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debug.h +++ b/drivers/net/ethernet/intel/i40e/i40e_debug.h @@ -42,7 +42,7 @@ struct device *i40e_hw_to_dev(struct i40e_hw *hw); #define i40e_debug(h, m, s, ...) \ do { \ if (((m) & (h)->debug_mask)) \ - dev_info(i40e_hw_to_dev(hw), s, ##__VA_ARGS__); \ + dev_info(i40e_hw_to_dev(h), s, ##__VA_ARGS__); \ } while (0) #endif /* _I40E_DEBUG_H_ */ From 1f6a4aec0d36647eb2f67cd69db22bd28cabe2cb Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 11 May 2026 08:27:03 +0500 Subject: [PATCH 1506/3297] rtc: msc313: fix NULL deref in shared IRQ handler at probe [ Upstream commit a369f48be8de426a7d2bca18dbd46c2ad1138803 ] msc313_rtc_probe() calls devm_request_irq() with IRQF_SHARED and &pdev->dev as the cookie, but platform_set_drvdata() is only called later after the clock setup. With a shared IRQ line, another device on the same line can trigger the handler in that window. The handler does dev_get_drvdata() on the cookie, gets NULL, and dereferences priv->rtc_base in interrupt context. Pass priv as the cookie directly so the handler reads it from dev_id without the lookup, removing the dependency on probe order. Fixes: be7d9c9161b9 ("rtc: Add support for the MSTAR MSC313 RTC") Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260511032703.48262-1-sozdayvek@gmail.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-msc313.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-msc313.c b/drivers/rtc/rtc-msc313.c index 8d7737e0e2e02..6ef9c4efd7c92 100644 --- a/drivers/rtc/rtc-msc313.c +++ b/drivers/rtc/rtc-msc313.c @@ -160,7 +160,7 @@ static const struct rtc_class_ops msc313_rtc_ops = { static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id) { - struct msc313_rtc *priv = dev_get_drvdata(dev_id); + struct msc313_rtc *priv = dev_id; u16 reg; reg = readw(priv->rtc_base + REG_RTC_STATUS_INT); @@ -206,7 +206,7 @@ static int msc313_rtc_probe(struct platform_device *pdev) priv->rtc_dev->range_max = U32_MAX; ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED, - dev_name(&pdev->dev), &pdev->dev); + dev_name(&pdev->dev), priv); if (ret) { dev_err(dev, "Could not request IRQ\n"); return ret; From 77bb0bbfcc4e777ca653174689e5e363f8ee63d1 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 19 Jun 2026 08:25:46 +0900 Subject: [PATCH 1507/3297] ksmbd: fix use-after-free of conn->preauth_info in concurrent SMB2 NEGOTIATE [ Upstream commit 0c054227479ed7e36ebccb3a558bc0ef698264f6 ] conn->preauth_info is shared connection state (struct preauth_integrity_info, kmalloc-96) that is allocated and freed by the SMB2 NEGOTIATE handler and read by the response send path. smb2_handle_negotiate() allocates conn->preauth_info, and on a deassemble_neg_contexts() failure kfrees it and sets it to NULL. Both the allocation and the free/NULL happen under ksmbd_conn_lock(conn) (the connection srv_mutex), which is held across the whole handler body. The response send path smb3_preauth_hash_rsp(), called from the send: block of __handle_ksmbd_work(), reads conn->preauth_info and dereferences conn->preauth_info->Preauth_HashValue (via ksmbd_gen_preauth_integrity_hash()) without taking conn_lock. When a client drives two SMB2 NEGOTIATE requests on the same connection, one worker can free conn->preauth_info on the failing-negotiate path while a concurrent send-path worker is reading it, producing a slab use-after-free read (KASAN-confirmed). The send-path read tested conn->preauth_info for NULL but raced with the free that occurs between the NULL check and the dereference, so the NULL guard alone does not close the window. Serialize the NEGOTIATE-branch read in smb3_preauth_hash_rsp() under ksmbd_conn_lock(conn) and re-check conn->preauth_info inside the lock. Because the negotiate handler holds conn_lock across its kfree + NULL assignment, a reader that also takes conn_lock either runs fully before the allocation or fully after the NULL store, and can never observe the freed-but-not-yet-NULLed pointer. ksmbd_gen_preauth_integrity_hash() takes no locks itself (it only computes a SHA-512 over the buffer), so no lock-ordering inversion is introduced, and conn_lock is a sleepable mutex which is safe on this send path (it already performs network I/O). Fixes: aa7253c2393f ("ksmbd: fix memory leak in smb2_handle_negotiate") Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smb2pdu.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 44b87196c5afc..c49ae51628649 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9217,10 +9217,13 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && - conn->preauth_info) - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - conn->preauth_info->Preauth_HashValue); + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) { + ksmbd_conn_lock(conn); + if (conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + conn->preauth_info->Preauth_HashValue); + ksmbd_conn_unlock(conn); + } if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { __u8 *hash_value; From b0d0eb13a0441a8ebf4f227843deaf494f1e2c33 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Thu, 18 Jun 2026 21:53:34 -0700 Subject: [PATCH 1508/3297] ipv6: Fix null-ptr-deref in fib6_nh_mtu_change(). [ Upstream commit 46c3b8191aad3d032776bf3bebf03efdf5f4b905 ] fib6_nh_mtu_change() re-fetches idev via __in6_dev_get(arg->dev) and dereferences idev->cnf.mtu6 without a NULL check. addrconf_ifdown() clears dev->ip6_ptr with RCU_INIT_POINTER() after rt6_disable_ip() has released tb6_lock, so the RA-driven MTU walk can observe a NULL idev and oops. The caller rt6_mtu_change_route() guards its own __in6_dev_get(), but this re-fetch is unguarded; nexthop-backed routes survive addrconf_ifdown()'s flush, so the walk still reaches it after ip6_ptr is nulled. Return 0 when idev is NULL, matching rt6_mtu_change_route() and the fib6_mtu() fix in commit 5ad509c1fdad ("ipv6: Fix null-ptr-deref in fib6_mtu()."). Oops: general protection fault, ... KASAN: null-ptr-deref in range [0x00000000000002a8-0x00000000000002af] RIP: 0010:fib6_nh_mtu_change+0x203/0x990 rt6_mtu_change_route+0x141/0x1d0 __fib6_clean_all+0xd0/0x160 rt6_mtu_change+0xb4/0x100 ndisc_router_discovery+0x24b5/0x2cb0 icmpv6_rcv+0x12e9/0x1710 ipv6_rcv+0x39b/0x410 Fixes: c0b220cf7d80 ("ipv6: Refactor exception functions") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260619045334.2427073-1-xmei5@asu.edu 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 b34f6a3e70519..ef66a3c86febb 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5043,6 +5043,9 @@ static int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg) struct inet6_dev *idev = __in6_dev_get(arg->dev); u32 mtu = f6i->fib6_pmtu; + if (!idev) + return 0; + if (mtu >= arg->mtu || (mtu < arg->mtu && mtu == idev->cnf.mtu6)) fib6_metric_set(f6i, RTAX_MTU, arg->mtu); From 33168db149d01be889edca8b1718e171b3ca628e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 6 Feb 2026 16:35:01 -0800 Subject: [PATCH 1509/3297] eth: bnxt: gather and report HW-GRO stats [ Upstream commit a7fc1a488f09f0fbf727fe3ca5b017e7657f152d ] Count and report HW-GRO stats as seen by the kernel. The device stats for GRO seem to not reflect the reality, perhaps they count sessions which did not actually result in any aggregation. Also they count wire packets, so we have to count super-frames, anyway. Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260207003509.3927744-2-kuba@kernel.org Signed-off-by: Jakub Kicinski Stable-dep-of: 40529e58629b ("eth: bnxt: improve the timing of stats") Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 15 +++++++++++++-- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 6 ++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 6c78fc85eafa5..2dece9aad5b95 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1803,7 +1803,8 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt *bp, struct bnxt_tpa_info *tpa_info, struct rx_tpa_end_cmp *tpa_end, struct rx_tpa_end_cmp_ext *tpa_end1, - struct sk_buff *skb) + struct sk_buff *skb, + struct bnxt_rx_sw_stats *rx_stats) { #ifdef CONFIG_INET int payload_off; @@ -1813,6 +1814,9 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt *bp, if (segs == 1) return skb; + rx_stats->rx_hw_gro_packets++; + rx_stats->rx_hw_gro_wire_packets += segs; + NAPI_GRO_CB(skb)->count = segs; skb_shinfo(skb)->gso_size = le32_to_cpu(tpa_end1->rx_tpa_end_cmp_seg_len); @@ -1986,7 +1990,8 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, } if (gro) - skb = bnxt_gro_skb(bp, tpa_info, tpa_end, tpa_end1, skb); + skb = bnxt_gro_skb(bp, tpa_info, tpa_end, tpa_end1, skb, + &cpr->sw_stats->rx); return skb; } @@ -13413,6 +13418,8 @@ static void bnxt_get_one_ring_err_stats(struct bnxt *bp, stats->rx_total_netpoll_discards += sw_stats->rx.rx_netpoll_discards; stats->rx_total_ring_discards += BNXT_GET_RING_STATS64(hw_stats, rx_discard_pkts); + stats->rx_total_hw_gro_packets += sw_stats->rx.rx_hw_gro_packets; + stats->rx_total_hw_gro_wire_packets += sw_stats->rx.rx_hw_gro_wire_packets; stats->tx_total_resets += sw_stats->tx.tx_resets; stats->tx_total_ring_discards += BNXT_GET_RING_STATS64(hw_stats, tx_discard_pkts); @@ -15844,6 +15851,8 @@ static void bnxt_get_queue_stats_rx(struct net_device *dev, int i, stats->bytes += BNXT_GET_RING_STATS64(sw, rx_bcast_bytes); stats->alloc_fail = cpr->sw_stats->rx.rx_oom_discards; + stats->hw_gro_packets = cpr->sw_stats->rx.rx_hw_gro_packets; + stats->hw_gro_wire_packets = cpr->sw_stats->rx.rx_hw_gro_wire_packets; } static void bnxt_get_queue_stats_tx(struct net_device *dev, int i, @@ -15879,6 +15888,8 @@ static void bnxt_get_base_stats(struct net_device *dev, rx->packets = bp->net_stats_prev.rx_packets; rx->bytes = bp->net_stats_prev.rx_bytes; rx->alloc_fail = bp->ring_err_stats_prev.rx_total_oom_discards; + rx->hw_gro_packets = bp->ring_err_stats_prev.rx_total_hw_gro_packets; + rx->hw_gro_wire_packets = bp->ring_err_stats_prev.rx_total_hw_gro_wire_packets; tx->packets = bp->net_stats_prev.tx_packets; tx->bytes = bp->net_stats_prev.tx_bytes; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index c80603704838d..b326a814b1f7a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1123,8 +1123,11 @@ struct bnxt_rx_sw_stats { u64 rx_l4_csum_errors; u64 rx_resets; u64 rx_buf_errors; + /* end of ethtool -S stats */ u64 rx_oom_discards; u64 rx_netpoll_discards; + u64 rx_hw_gro_packets; + u64 rx_hw_gro_wire_packets; }; struct bnxt_tx_sw_stats { @@ -1151,6 +1154,9 @@ struct bnxt_total_ring_err_stats { u64 tx_total_resets; u64 tx_total_ring_discards; u64 total_missed_irqs; + /* end of ethtool -S stats */ + u64 rx_total_hw_gro_packets; + u64 rx_total_hw_gro_wire_packets; }; struct bnxt_stats_mem { From c0057e5f762b99129f7935b9e2fbb5fd3fd6b610 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 23 Feb 2026 12:37:01 -0800 Subject: [PATCH 1510/3297] eth: bnxt: rename ring_err_stats -> ring_drv_stats [ Upstream commit 6c32b07650676ab4c112ff8e9d345b6770ae2be5 ] We recently added GRO stats to bnxt, which are maintained by the driver. Having "err" in the name of the struct for ring stats no longer makes sense (as pointed out by Michael, see Link). Rename them to "drv" stats, as these are all maintained by the driver (even if partially based on info from descriptors). Michael suggested calling these misc, happy to go back to that. IMHO "drv" is a bit more meaningful that "misc". Pure rename using sed, no functional changes. Link: https://lore.kernel.org/CACKFLimgibJ0qkM1AacZVh8MKKy-pE_AAc4KPKZ7GUqebmXW9A@mail.gmail.com Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260223203702.4137801-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Stable-dep-of: 40529e58629b ("eth: bnxt: improve the timing of stats") Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 18 ++++++++--------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 8 ++++---- .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 20 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 2dece9aad5b95..d0e37a0946649 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -13159,7 +13159,7 @@ static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init, /* Save ring stats before shutdown */ if (bp->bnapi && irq_re_init) { bnxt_get_ring_stats(bp, &bp->net_stats_prev); - bnxt_get_ring_err_stats(bp, &bp->ring_err_stats_prev); + bnxt_get_ring_drv_stats(bp, &bp->ring_drv_stats_prev); } if (irq_re_init) { bnxt_free_irq(bp); @@ -13404,8 +13404,8 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) clear_bit(BNXT_STATE_READ_STATS, &bp->state); } -static void bnxt_get_one_ring_err_stats(struct bnxt *bp, - struct bnxt_total_ring_err_stats *stats, +static void bnxt_get_one_ring_drv_stats(struct bnxt *bp, + struct bnxt_total_ring_drv_stats *stats, struct bnxt_cp_ring_info *cpr) { struct bnxt_sw_stats *sw_stats = cpr->sw_stats; @@ -13426,13 +13426,13 @@ static void bnxt_get_one_ring_err_stats(struct bnxt *bp, stats->total_missed_irqs += sw_stats->cmn.missed_irqs; } -void bnxt_get_ring_err_stats(struct bnxt *bp, - struct bnxt_total_ring_err_stats *stats) +void bnxt_get_ring_drv_stats(struct bnxt *bp, + struct bnxt_total_ring_drv_stats *stats) { int i; for (i = 0; i < bp->cp_nr_rings; i++) - bnxt_get_one_ring_err_stats(bp, stats, &bp->bnapi[i]->cp_ring); + bnxt_get_one_ring_drv_stats(bp, stats, &bp->bnapi[i]->cp_ring); } static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask) @@ -15887,9 +15887,9 @@ static void bnxt_get_base_stats(struct net_device *dev, rx->packets = bp->net_stats_prev.rx_packets; rx->bytes = bp->net_stats_prev.rx_bytes; - rx->alloc_fail = bp->ring_err_stats_prev.rx_total_oom_discards; - rx->hw_gro_packets = bp->ring_err_stats_prev.rx_total_hw_gro_packets; - rx->hw_gro_wire_packets = bp->ring_err_stats_prev.rx_total_hw_gro_wire_packets; + rx->alloc_fail = bp->ring_drv_stats_prev.rx_total_oom_discards; + rx->hw_gro_packets = bp->ring_drv_stats_prev.rx_total_hw_gro_packets; + rx->hw_gro_wire_packets = bp->ring_drv_stats_prev.rx_total_hw_gro_wire_packets; tx->packets = bp->net_stats_prev.tx_packets; tx->bytes = bp->net_stats_prev.tx_bytes; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index b326a814b1f7a..9c9212ecd8c74 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1144,7 +1144,7 @@ struct bnxt_sw_stats { struct bnxt_cmn_sw_stats cmn; }; -struct bnxt_total_ring_err_stats { +struct bnxt_total_ring_drv_stats { u64 rx_total_l4_csum_errors; u64 rx_total_resets; u64 rx_total_buf_errors; @@ -2562,7 +2562,7 @@ struct bnxt { u8 pri2cos_idx[8]; u8 pri2cos_valid; - struct bnxt_total_ring_err_stats ring_err_stats_prev; + struct bnxt_total_ring_drv_stats ring_drv_stats_prev; u16 hwrm_max_req_len; u16 hwrm_max_ext_req_len; @@ -2963,8 +2963,8 @@ int bnxt_half_open_nic(struct bnxt *bp); void bnxt_half_close_nic(struct bnxt *bp); void bnxt_reenable_sriov(struct bnxt *bp); void bnxt_close_nic(struct bnxt *, bool, bool); -void bnxt_get_ring_err_stats(struct bnxt *bp, - struct bnxt_total_ring_err_stats *stats); +void bnxt_get_ring_drv_stats(struct bnxt *bp, + struct bnxt_total_ring_drv_stats *stats); bool bnxt_rfs_capable(struct bnxt *bp, bool new_rss_ctx); int bnxt_dbg_hwrm_rd_reg(struct bnxt *bp, u32 reg_off, u16 num_words, u32 *reg_buf); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 3237515f0e7ec..8d10bf22480f6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -340,7 +340,7 @@ enum { RX_NETPOLL_DISCARDS, }; -static const char *const bnxt_ring_err_stats_arr[] = { +static const char *const bnxt_ring_drv_stats_arr[] = { "rx_total_l4_csum_errors", "rx_total_resets", "rx_total_buf_errors", @@ -500,7 +500,7 @@ static const struct { BNXT_TX_STATS_PRI_ENTRIES(tx_packets), }; -#define BNXT_NUM_RING_ERR_STATS ARRAY_SIZE(bnxt_ring_err_stats_arr) +#define BNXT_NUM_RING_DRV_STATS ARRAY_SIZE(bnxt_ring_drv_stats_arr) #define BNXT_NUM_PORT_STATS ARRAY_SIZE(bnxt_port_stats_arr) #define BNXT_NUM_STATS_PRI \ (ARRAY_SIZE(bnxt_rx_bytes_pri_arr) + \ @@ -539,7 +539,7 @@ static int bnxt_get_num_stats(struct bnxt *bp) int num_stats = bnxt_get_num_ring_stats(bp); int len; - num_stats += BNXT_NUM_RING_ERR_STATS; + num_stats += BNXT_NUM_RING_DRV_STATS; if (bp->flags & BNXT_FLAG_PORT_STATS) num_stats += BNXT_NUM_PORT_STATS; @@ -594,7 +594,7 @@ static bool is_tx_ring(struct bnxt *bp, int ring_num) static void bnxt_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *buf) { - struct bnxt_total_ring_err_stats ring_err_stats = {0}; + struct bnxt_total_ring_drv_stats ring_drv_stats = {0}; struct bnxt *bp = netdev_priv(dev); u64 *curr, *prev; u32 tpa_stats; @@ -643,12 +643,12 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, buf[j] = sw[k]; } - bnxt_get_ring_err_stats(bp, &ring_err_stats); + bnxt_get_ring_drv_stats(bp, &ring_drv_stats); skip_ring_stats: - curr = &ring_err_stats.rx_total_l4_csum_errors; - prev = &bp->ring_err_stats_prev.rx_total_l4_csum_errors; - for (i = 0; i < BNXT_NUM_RING_ERR_STATS; i++, j++, curr++, prev++) + curr = &ring_drv_stats.rx_total_l4_csum_errors; + prev = &bp->ring_drv_stats_prev.rx_total_l4_csum_errors; + for (i = 0; i < BNXT_NUM_RING_DRV_STATS; i++, j++, curr++, prev++) buf[j] = *curr + *prev; if (bp->flags & BNXT_FLAG_PORT_STATS) { @@ -752,8 +752,8 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf) ethtool_sprintf(&buf, "[%d]: %s", i, str); } } - for (i = 0; i < BNXT_NUM_RING_ERR_STATS; i++) - ethtool_puts(&buf, bnxt_ring_err_stats_arr[i]); + for (i = 0; i < BNXT_NUM_RING_DRV_STATS; i++) + ethtool_puts(&buf, bnxt_ring_drv_stats_arr[i]); if (bp->flags & BNXT_FLAG_PORT_STATS) for (i = 0; i < BNXT_NUM_PORT_STATS; i++) { From 630ce3806b706411da05731f21700271398ec5dd Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 19 Jun 2026 12:15:38 -0700 Subject: [PATCH 1511/3297] eth: bnxt: improve the timing of stats [ Upstream commit 40529e58629baa9ce72143cb46cf1b3d2ca0d465 ] Kernel selftests wait 1.25x of the promised stats refresh time (as read from ethtool -c). bnxt reports 1sec by default, but the stats update process has two steps. First device DMAs the new values, then the service task performs update in full-width SW counters. So the worst case delay is actually 2x. Note that the behavior is different for ring stats and port stats. Port stats are fetched synchronously by the service worker, so there's no risk of doubling up the delay there. The problem of stale stats impacts not only tests but real workloads which monitor egress bandwidth of a NIC. The inaccuracy causes double counting in the next cycle and spurious overload alarms. Try to read from the DMA buffer more aggressively, to mitigate timing issues between DMA and service task. The SW update should be cheap. Fixes: 51f307856b60 ("bnxt_en: Allow statistics DMA to be configurable using ethtool -C.") Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260619191538.104165-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 48 ++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 5 ++ .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 1 + 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index d0e37a0946649..98a911eae8eac 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -10367,7 +10367,7 @@ static void bnxt_accumulate_stats(struct bnxt_stats_mem *stats) stats->hw_masks, stats->len / 8, false); } -static void bnxt_accumulate_all_stats(struct bnxt *bp) +static void bnxt_accumulate_ring_stats(struct bnxt *bp) { struct bnxt_stats_mem *ring0_stats; bool ignore_zero = false; @@ -10390,6 +10390,10 @@ static void bnxt_accumulate_all_stats(struct bnxt *bp) ring0_stats->hw_masks, ring0_stats->len / 8, ignore_zero); } +} + +static void bnxt_accumulate_port_stats(struct bnxt *bp) +{ if (bp->flags & BNXT_FLAG_PORT_STATS) { struct bnxt_stats_mem *stats = &bp->port_stats; __le64 *hw_stats = stats->hw_stats; @@ -10412,6 +10416,41 @@ static void bnxt_accumulate_all_stats(struct bnxt *bp) } } +static void bnxt_accumulate_all_stats(struct bnxt *bp) +{ + bnxt_accumulate_ring_stats(bp); + bnxt_accumulate_port_stats(bp); +} + +/* Re-accumulate ring stats from DMA buffers if stale. + * uAPIs for reading sw_stats should call this first. + * + * We promise user space update frequency of bp->stats_coal_ticks but + * the update is a two step process - first device updates the DMA buffer, + * then we have to update from that buffer to driver stats in the service work. + * Worst case we would be 2x off from the desired frequency. + * Sync the stats sooner, if stale. The 20% threshold was chosen arbitrarily. + * + * Ideally we would split the user-configured time into two portions, + * i.e. also lower the DMA period by the 20%. But the DMA timer seems to have + * too coarse granularity to play such tricks. + */ +void bnxt_sync_ring_stats(struct bnxt *bp) +{ + unsigned long stale; + + if (!netif_running(bp->dev) || !bp->stats_coal_ticks) + return; + + spin_lock(&bp->stats_lock); + stale = usecs_to_jiffies(bp->stats_coal_ticks / 5); + if (time_after_eq(jiffies, bp->stats_updated_jiffies + stale)) { + bnxt_accumulate_ring_stats(bp); + bp->stats_updated_jiffies = jiffies; + } + spin_unlock(&bp->stats_lock); +} + static int bnxt_hwrm_port_qstats(struct bnxt *bp, u8 flags) { struct hwrm_port_qstats_input *req; @@ -13376,6 +13415,7 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) return; } + bnxt_sync_ring_stats(bp); bnxt_get_ring_stats(bp, stats); bnxt_add_prev_stats(bp, stats); @@ -14548,7 +14588,10 @@ static void bnxt_sp_task(struct work_struct *work) if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event)) { bnxt_hwrm_port_qstats(bp, 0); bnxt_hwrm_port_qstats_ext(bp, 0); + spin_lock(&bp->stats_lock); bnxt_accumulate_all_stats(bp); + bp->stats_updated_jiffies = jiffies; + spin_unlock(&bp->stats_lock); } if (test_and_clear_bit(BNXT_LINK_CHNG_SP_EVENT, &bp->sp_event)) { @@ -15279,6 +15322,7 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) INIT_DELAYED_WORK(&bp->fw_reset_task, bnxt_fw_reset_task); spin_lock_init(&bp->ntp_fltr_lock); + spin_lock_init(&bp->stats_lock); #if BITS_PER_LONG == 32 spin_lock_init(&bp->db_lock); #endif @@ -15837,6 +15881,7 @@ static void bnxt_get_queue_stats_rx(struct net_device *dev, int i, if (!bp->bnapi) return; + bnxt_sync_ring_stats(bp); cpr = &bp->bnapi[i]->cp_ring; sw = cpr->stats.sw_stats; @@ -15865,6 +15910,7 @@ static void bnxt_get_queue_stats_tx(struct net_device *dev, int i, if (!bp->tx_ring) return; + bnxt_sync_ring_stats(bp); bnapi = bp->tx_ring[bp->tx_ring_map[i]].bnapi; sw = bnapi->cp_ring.stats.sw_stats; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 9c9212ecd8c74..d55987e24ad6e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2601,6 +2601,10 @@ struct bnxt { #define BNXT_MIN_STATS_COAL_TICKS 250000 #define BNXT_MAX_STATS_COAL_TICKS 1000000 + /* Protects stats_updated_jiffies and writes to sw_stats */ + spinlock_t stats_lock; + unsigned long stats_updated_jiffies; + struct work_struct sp_task; unsigned long sp_event; #define BNXT_RX_MASK_SP_EVENT 0 @@ -2965,6 +2969,7 @@ void bnxt_reenable_sriov(struct bnxt *bp); void bnxt_close_nic(struct bnxt *, bool, bool); void bnxt_get_ring_drv_stats(struct bnxt *bp, struct bnxt_total_ring_drv_stats *stats); +void bnxt_sync_ring_stats(struct bnxt *bp); bool bnxt_rfs_capable(struct bnxt *bp, bool new_rss_ctx); int bnxt_dbg_hwrm_rd_reg(struct bnxt *bp, u32 reg_off, u16 num_words, u32 *reg_buf); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 8d10bf22480f6..a71025f51370d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -605,6 +605,7 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, goto skip_ring_stats; } + bnxt_sync_ring_stats(bp); tpa_stats = bnxt_get_num_tpa_ring_stats(bp); for (i = 0; i < bp->cp_nr_rings; i++) { struct bnxt_napi *bnapi = bp->bnapi[i]; From 828fad4fd418bcdb9f5d66fec0d184c52a85ec31 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 19 Jun 2026 21:27:20 +0000 Subject: [PATCH 1512/3297] ipv4: fib: Don't ignore error route in local/main tables. [ Upstream commit b72f0db64205d9ce462038ba995d5d31eff32dc1 ] When CONFIG_IP_MULTIPLE_TABLES is enabled but no rule is added, fib_lookup() performs route lookup directly on two tables. Since the first lookup does not properly bail out, the result of an error route in the merged local/main table could be overwritten by another route in the default table: # unshare -n # ip link set lo up # ip route add 192.168.0.0/24 dev lo table 253 # ip route add unreachable 192.168.0.0/24 # ip route get 192.168.0.1 192.168.0.1 dev lo table default uid 0 cache Once a random rule is added, the error route is respected: # ip rule add table 0 # ip rule del table 0 # ip route get 192.168.0.1 RTNETLINK answers: No route to host Let's fix the inconsistent behaviour. Fixes: f4530fa574df ("ipv4: Avoid overhead when no custom FIB rules are installed.") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260619212753.3367244-1-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/ip_fib.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 318593743b6e1..24023c19c7b1f 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -375,7 +375,7 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res, unsigned int flags) { struct fib_table *tb; - int err = -ENETUNREACH; + int err = -EAGAIN; flags |= FIB_LOOKUP_NOREF; if (net->ipv4.fib_has_custom_rules) @@ -389,17 +389,16 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, if (tb) err = fib_table_lookup(tb, flp, res, flags); - if (!err) + if (err != -EAGAIN) goto out; tb = rcu_dereference_rtnl(net->ipv4.fib_default); if (tb) err = fib_table_lookup(tb, flp, res, flags); -out: if (err == -EAGAIN) err = -ENETUNREACH; - +out: rcu_read_unlock(); return err; From 3db13f82ba3145a122a79cd3dc5a500bd666d5c5 Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Thu, 18 Jun 2026 21:47:48 +0800 Subject: [PATCH 1513/3297] md/raid5: use stripe state snapshot in break_stripe_batch_list() [ Upstream commit 9b249f5ffbeda24c57e6c56ed896c5ca4fc70549 ] The patch just suppress KCSAN noise. No functional change. RAID-5 can group multi full-stripe-write aka stripe_head into a batch aka batch_list, with one head_sh leading them. Call break_stripe_batch_list() when the batch is finished, or, a stripe has to be dropped out of the batch. break_stripe_batch_list() reads stripe state several times while request paths can update thost state words concurrently with lockless bitops, which reported by KCSAN. Use a snapshot to guarantees that the value used for warning, copying, and handle checks is internally consistent at current read moment. KCSAN report: ============================================== BUG: KCSAN: data-race in __add_stripe_bio / break_stripe_batch_list write (marked) to 0xffff8e89d4f0b988 of 8 bytes by task 4323 on cpu 3: __add_stripe_bio+0x35e/0x400 raid5_make_request+0x6ac/0x2930 md_handle_request+0x4a2/0xa40 md_submit_bio+0x109/0x1a0 __submit_bio+0x2ec/0x390 submit_bio_noacct_nocheck+0x457/0x710 submit_bio_noacct+0x2a7/0xc20 submit_bio+0x56/0x250 blkdev_direct_IO+0x54c/0xda0 blkdev_write_iter+0x38f/0x570 aio_write+0x22b/0x490 io_submit_one+0xa51/0xf70 read to 0xffff8e89d4f0b988 of 8 bytes by task 4290 on cpu 4: break_stripe_batch_list+0x3ce/0x480 handle_stripe_clean_event+0x720/0x9b0 handle_stripe+0x32fb/0x4500 handle_active_stripes.isra.0+0x6e0/0xa50 raid5d+0x7e0/0xba0 Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260618134748.1168360-1-chencheng@fnnas.com Signed-off-by: Yu Kuai Stable-dep-of: 55b77337bdd0 ("md/raid5: avoid R5_Overlap races while breaking stripe batches") Signed-off-by: Sasha Levin --- drivers/md/raid5.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 381f56c4b5e27..e4cbf5fcec8e7 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4853,31 +4853,35 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, { struct stripe_head *sh, *next; int i; + unsigned long state; list_for_each_entry_safe(sh, next, &head_sh->batch_list, batch_list) { list_del_init(&sh->batch_list); - WARN_ONCE(sh->state & ((1 << STRIPE_ACTIVE) | - (1 << STRIPE_SYNCING) | - (1 << STRIPE_REPLACED) | - (1 << STRIPE_DELAYED) | - (1 << STRIPE_BIT_DELAY) | - (1 << STRIPE_FULL_WRITE) | - (1 << STRIPE_BIOFILL_RUN) | - (1 << STRIPE_COMPUTE_RUN) | - (1 << STRIPE_DISCARD) | - (1 << STRIPE_BATCH_READY) | - (1 << STRIPE_BATCH_ERR)), - "stripe state: %lx\n", sh->state); - WARN_ONCE(head_sh->state & ((1 << STRIPE_DISCARD) | - (1 << STRIPE_REPLACED)), - "head stripe state: %lx\n", head_sh->state); + state = READ_ONCE(sh->state); + WARN_ONCE(state & ((1 << STRIPE_ACTIVE) | + (1 << STRIPE_SYNCING) | + (1 << STRIPE_REPLACED) | + (1 << STRIPE_DELAYED) | + (1 << STRIPE_BIT_DELAY) | + (1 << STRIPE_FULL_WRITE) | + (1 << STRIPE_BIOFILL_RUN) | + (1 << STRIPE_COMPUTE_RUN) | + (1 << STRIPE_DISCARD) | + (1 << STRIPE_BATCH_READY) | + (1 << STRIPE_BATCH_ERR)), + "stripe state: %lx\n", state); + + state = READ_ONCE(head_sh->state); + WARN_ONCE(state & ((1 << STRIPE_DISCARD) | + (1 << STRIPE_REPLACED)), + "head stripe state: %lx\n", state); set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS | (1 << STRIPE_PREREAD_ACTIVE) | (1 << STRIPE_ON_UNPLUG_LIST)), - head_sh->state & (1 << STRIPE_INSYNC)); + state & (1 << STRIPE_INSYNC)); sh->check_state = head_sh->check_state; sh->reconstruct_state = head_sh->reconstruct_state; @@ -4890,8 +4894,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, sh->dev[i].flags = head_sh->dev[i].flags & (~((1 << R5_WriteError) | (1 << R5_Overlap))); } - if (handle_flags == 0 || - sh->state & handle_flags) + + state = READ_ONCE(sh->state); + if (handle_flags == 0 || (state & handle_flags)) set_bit(STRIPE_HANDLE, &sh->state); raid5_release_stripe(sh); } @@ -4901,7 +4906,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, for (i = 0; i < head_sh->disks; i++) if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags)) wake_up_bit(&head_sh->dev[i].flags, R5_Overlap); - if (head_sh->state & handle_flags) + + state = READ_ONCE(head_sh->state); + if (state & handle_flags) set_bit(STRIPE_HANDLE, &head_sh->state); } From 4d919c9b770996365806b6c8d701912d52baa306 Mon Sep 17 00:00:00 2001 From: Chen Cheng Date: Fri, 19 Jun 2026 12:10:13 +0800 Subject: [PATCH 1514/3297] md/raid5: avoid R5_Overlap races while breaking stripe batches [ Upstream commit 55b77337bdd088c77461588e5ec094421b89911b ] KCSAN report a race in break_stripe_batch_list() vs. raid5_make_request() on sh->dev[i].flags (plain word write vs. atomic bit op).. and .. one possible scenario is: CPU1 CPU2 break_stripe_batch_list(sh1) -> handle sh2 -> lock(sh2) -> sh2->batch_head = NULL -> unlock(sh2) -> test_and_clear_bit(R5_Overlap, sh2->dev[i].flags) -> wake_up_bit(sh2->dev[i].flags) raid5_make_request() -> add_all_stripe_bios(sh2) -> lock(sh2) -> stripe_bio_overlaps(sh2) returns true batch_head is NULL, so new bio overlap exist bio on sh2 -> true -> set_bit(R5_Overlap, sh2->dev[i].flags) -> unlock(sh2) -> wait_on_bit(sh2->dev[i].flags) -> sh2->dev[i].flags = sh1->dev[i].flags & ~R5_Overlap No wait_up_bit(), CPU2 could be wait_on_bit() forever... Fix by : - Expand the protect zone. - Use batch_head's device flag's snaphot when no held head_sh->stripe_lock. - Move sh/head_sh->batch_head = NULL to the end of protected zone , and , any concurrent add_all_stripe_bios() grabs sh->stripe_lock now either: - see batch_head != null, and , is rejected by stripe_bio_overlaps() under the lock (no R5_Overlap wait ) , or , - sees batch_head == NULL, only after dev[i].flags has already been set and the prior R5_Overlap waiters worken. KCSAN report: ================================================ BUG: KCSAN: data-race in break_stripe_batch_list / raid5_make_request write (marked) to 0xffff8e89c8117548 of 8 bytes by task 4042 on cpu 0: raid5_make_request+0xea0/0x2930 md_handle_request+0x4a2/0xa40 md_submit_bio+0x109/0x1a0 __submit_bio+0x2ec/0x390 submit_bio_noacct_nocheck+0x457/0x710 submit_bio_noacct+0x2a7/0xc20 submit_bio+0x56/0x250 blkdev_direct_IO+0x54c/0xda0 blkdev_write_iter+0x38f/0x570 aio_write+0x22b/0x490 io_submit_one+0xa51/0xf70 __x64_sys_io_submit+0xf7/0x220 x64_sys_call+0x1907/0x1c60 do_syscall_64+0x130/0x570 entry_SYSCALL_64_after_hwframe+0x76/0x7e read to 0xffff8e89c8117548 of 8 bytes by task 4010 on cpu 5: break_stripe_batch_list+0x249/0x480 handle_stripe_clean_event+0x720/0x9b0 handle_stripe+0x32fb/0x4500 handle_active_stripes.isra.0+0x6e0/0xa50 raid5d+0x7e0/0xba0 md_thread+0x15a/0x2d0 kthread+0x1e3/0x220 ret_from_fork+0x37a/0x410 ret_from_fork_asm+0x1a/0x30 value changed: 0x0000000000000019 -> 0x0000000000000099 --> R5_Overlap Fixes: fb642b92c267 ("md/raid5: duplicate some more handle_stripe_clean_event code in break_stripe_batch_list") Signed-off-by: Chen Cheng Link: https://patch.msgid.link/20260619041013.1207148-1-chencheng@fnnas.com Signed-off-by: Yu Kuai Signed-off-by: Sasha Levin --- drivers/md/raid5.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index e4cbf5fcec8e7..b19d870e41719 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4886,14 +4886,14 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, sh->check_state = head_sh->check_state; sh->reconstruct_state = head_sh->reconstruct_state; spin_lock_irq(&sh->stripe_lock); - sh->batch_head = NULL; - spin_unlock_irq(&sh->stripe_lock); for (i = 0; i < sh->disks; i++) { if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up_bit(&sh->dev[i].flags, R5_Overlap); - sh->dev[i].flags = head_sh->dev[i].flags & + sh->dev[i].flags = READ_ONCE(head_sh->dev[i].flags) & (~((1 << R5_WriteError) | (1 << R5_Overlap))); } + sh->batch_head = NULL; + spin_unlock_irq(&sh->stripe_lock); state = READ_ONCE(sh->state); if (handle_flags == 0 || (state & handle_flags)) @@ -4901,11 +4901,11 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, raid5_release_stripe(sh); } spin_lock_irq(&head_sh->stripe_lock); - head_sh->batch_head = NULL; - spin_unlock_irq(&head_sh->stripe_lock); for (i = 0; i < head_sh->disks; i++) if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags)) wake_up_bit(&head_sh->dev[i].flags, R5_Overlap); + head_sh->batch_head = NULL; + spin_unlock_irq(&head_sh->stripe_lock); state = READ_ONCE(head_sh->state); if (state & handle_flags) From 49fa1be621dde8f526d36e5791aded8fafda1e9c Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 13:03:03 +0000 Subject: [PATCH 1515/3297] bpf: Disable xfrm_decode_session hook attachment [ Upstream commit 12091470c6b4c1c14b2de12dcbae2ada6cb6d20b ] BPF LSM programs can currently attach to xfrm_decode_session(). That hook may return an error, but security_skb_classify_flow() calls it from a void path and triggers BUG_ON() if an error is returned. Disable BPF attachment to the hook to prevent a BPF LSM program from turning packet classification into a full panic. Fixes: 9e4e01dfd325 ("bpf: lsm: Implement attach, detach and execution") Signed-off-by: Bradley Morgan Link: https://lore.kernel.org/r/20260619130305.27779-1-include@grrlz.net Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin --- kernel/bpf/bpf_lsm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 554985ce9025a..518c933fe944c 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -50,6 +50,9 @@ BTF_ID(func, bpf_lsm_key_getsecurity) #ifdef CONFIG_AUDIT BTF_ID(func, bpf_lsm_audit_rule_match) #endif +#ifdef CONFIG_SECURITY_NETWORK_XFRM +BTF_ID(func, bpf_lsm_xfrm_decode_session) +#endif BTF_ID(func, bpf_lsm_ismaclabel) BTF_SET_END(bpf_lsm_disabled_hooks) From a73e7ac3f3b69e9581ec7bfd889ff3e9b8c773f7 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Tue, 28 Apr 2026 11:09:17 +0200 Subject: [PATCH 1516/3297] netfilter: nf_nat: avoid invalid nat_net pointer use on failed nf_nat_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 069cfe3de2a5e16069485893cd04665ab769c1d8 ] We ran into below KASAN splat, which is mostly uninteresting, beside for having nf_nat_register_fn() in the call chain as a cause for the offending access: ================================================================== BUG: KASAN: slab-out-of-bounds in nf_nat_register_fn+0x5f9/0x640 Read of size 8 at addr ffff890031e54c20 by task iptables/9510 CPU: 0 UID: 0 PID: 9510 Comm: iptables Not tainted 6.18.18-grsec-full-20260320181326 #1 PREEMPT(voluntary) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: […] dump_stack_lvl+0xee/0x160 ffff88004117eeb8 […] print_report+0x6e/0x640 ffff88004117eee0 […] ? __phys_addr+0x8e/0x140 ffff88004117eef0 […] ? kasan_addr_to_slab+0x51/0xe0 ffff88004117ef08 […] ? complete_report_info+0xec/0x1c0 ffff88004117ef20 […] ? nf_nat_register_fn+0x5f9/0x640 ffff88004117ef48 […] kasan_report+0xbc/0x140 ffff88004117ef50 […] ? nf_nat_register_fn+0x5f9/0x640 ffff88004117ef90 […] nf_nat_register_fn+0x5f9/0x640 ffff88004117eff8 […] ? nf_nat_icmp_reply_translation+0x6e0/0x6e0 ffff88004117f070 […] nf_tables_register_hook.part.0+0xa0/0x220 ffff88004117f080 […] nf_tables_addchain.constprop.0+0x1054/0x1fc0 ffff88004117f0b8 […] ? nft_chain_lookup.part.0+0x4ce/0xac0 ffff88004117f130 […] ? nf_tables_abort+0x3d80/0x3d80 ffff88004117f190 […] ? nf_tables_dumpreset_obj+0x100/0x100 ffff88004117f1c8 […] ? nft_table_lookup.part.0+0x255/0x300 ffff88004117f310 […] ? nf_tables_newchain+0x21a4/0x2fa0 ffff88004117f358 […] nf_tables_newchain+0x21a4/0x2fa0 ffff88004117f360 […] ? nf_tables_addchain.constprop.0+0x1fc0/0x1fc0 ffff88004117f458 […] ? nla_get_range_signed+0x4a0/0x4a0 ffff88004117f488 […] ? lock_acquire+0x16f/0x320 ffff88004117f490 […] ? find_held_lock+0x3b/0xe0 ffff88004117f4b0 […] ? __nla_parse+0x45/0x80 ffff88004117f500 […] nfnetlink_rcv_batch+0xbca/0x19a0 ffff88004117f550 […] ? nfnetlink_net_exit_batch+0x120/0x120 ffff88004117f618 […] ? __sanitizer_cov_trace_switch+0x63/0xe0 ffff88004117f720 […] ? gr_acl_handle_mmap+0x1c4/0x320 ffff88004117f7c0 […] ? nla_get_range_signed+0x4a0/0x4a0 ffff88004117f7e8 […] ? gr_is_capable+0x6f/0xe0 ffff88004117f830 […] ? __nla_parse+0x45/0x80 ffff88004117f860 […] ? skb_pull+0x103/0x1a0 ffff88004117f880 […] nfnetlink_rcv+0x3db/0x4a0 ffff88004117f8b0 […] ? nfnetlink_rcv_batch+0x19a0/0x19a0 ffff88004117f8d8 […] ? netlink_lookup+0xe2/0x240 ffff88004117f900 […] netlink_unicast+0x74b/0xb00 ffff88004117f930 […] ? netlink_attachskb+0xb20/0xb20 ffff88004117f980 […] ? __check_object_size+0x3e/0xaa0 ffff88004117f998 […] ? security_netlink_send+0x51/0x160 ffff88004117f9c8 […] netlink_sendmsg+0xa03/0x1200 ffff88004117f9f8 […] ? netlink_unicast+0xb00/0xb00 ffff88004117fa70 […] ? netlink_unicast+0xb00/0xb00 ffff88004117fac8 […] ? ____sys_sendmsg+0xe2a/0x1040 ffff88004117faf8 […] ____sys_sendmsg+0xe2a/0x1040 ffff88004117fb00 […] ? kernel_recvmsg+0x300/0x300 ffff88004117fb60 […] ? reacquire_held_locks+0xe9/0x260 ffff88004117fbc8 […] ___sys_sendmsg+0x138/0x200 ffff88004117fbf8 […] ? do_recvmmsg+0x7e0/0x7e0 ffff88004117fc30 […] ? lockdep_hardirqs_on_prepare+0x101/0x1e0 ffff88004117fc50 […] ? lock_acquire+0x16f/0x320 ffff88004117fd20 […] ? lock_acquire+0x16f/0x320 ffff88004117fd58 […] ? find_held_lock+0x3b/0xe0 ffff88004117fd70 […] __sys_sendmsg+0x17a/0x260 ffff88004117fdc8 […] ? __sys_sendmsg_sock+0x80/0x80 ffff88004117fdf0 […] ? syscall_trace_enter+0x15e/0x2c0 ffff88004117fe98 […] do_syscall_64+0x7d/0x400 ffff88004117fec8 […] entry_SYSCALL_64_safe_stack+0x4a/0x60 ffff88004117fef8 ================================================================== The out-of-bounds report, though, is a red herring as it is for an access that shouldn't have happened in the first place. When nf_nat_init() fails to register its BPF kfuncs, it'll unwind and, among others, call unregister_pernet_subsys() to deregister its per-net ops. This makes the previously allocated net id available for reuse by the next caller of register_pernet_subsys(), in our case, synproxy. However, 'nat_net_id' will still hold the previously allocated value. If nf_nat.o gets build as a module, all this doesn't matter. A failed initialization routine makes the module fail to load and any dependent module won't be able to load either. However, if nf_nat.o is built-in, a failing init won't /completely/ make its functionality unavailable to dependent modules, namely the code and static data is still there, free to be called by modules like nft_chain_nat.ko. Case in point, nft_chain_nat registers hooks that'll call into nf_nat which, in our case, failed to initialize and therefore won't have a valid net id nor related net_nat object any more. Code in nf_nat, namely nf_nat_register_fn() and nf_nat_unregister_fn(), still making use of the reallocated net id, lead to a type confusion as the call to net_generic() will no longer return memory belonging to an object suited to fit 'struct nat_net' but 'struct synproxy_net' instead. The latter is only 24 bytes on 64-bit systems, much smaller than struct nat_net which is 176 bytes, perfectly explaining the OOB KASAN report. Detect and handle a failed nf_nat_init() by testing the 'nf_nat_hook' pointer which will be reset to NULL on initialization errors to prevent the usage of an invalid nat_net pointer. As this check is only needed when nf_nat.o is built-in, guard it by '#ifndef MODULE...'. Fixes: cbc1dd5b659f ("netfilter: nf_nat: Fix possible memory leak in nf_nat_init()") Signed-off-by: Mathias Krause Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_nat_core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index d3e158ecf729a..cd84f2aa08f2d 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -1187,6 +1187,16 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, struct nf_hook_ops *nat_ops; int i, ret; +#ifndef MODULE + /* If nf_nat_core is built-in and nf_nat_init() fails, dependent + * modules like nft_chain_nat.ko may still call this function. + * However, nat_net would be invalid, likely pointing to some other + * per-net structure. + */ + if (WARN_ON_ONCE(!nf_nat_hook)) + return -EOPNOTSUPP; +#endif + if (WARN_ON_ONCE(pf >= ARRAY_SIZE(nat_net->nat_proto_net))) return -EINVAL; From 329f2626ee5cb8fafdf6b58b624311529c57cb45 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 14 May 2026 16:16:28 +0200 Subject: [PATCH 1517/3297] netfilter: nf_conncount: prevent connlimit drops for early confirmed ct [ Upstream commit c8b6f36f766991e3ebebec6596daee4b04dcbc49 ] Commit 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped") introduced a regression where packets for valid connections are dropped when using connlimit for soft-limiting scenarios. The issue occurs when a new connection reuses a socket currently in the TIME_WAIT state. In this scenario, the connection tracking entry is evaluated as already confirmed. Previously, __nf_conncount_add() assumed that if a connection was confirmed and did not originate from the loopback interface, it should skip the addition and return -EEXIST. Skipping the addition triggers a garbage collection run that cleans up the TIME_WAIT connection. Consequently, the active connection count drops to 0, which xt_connlimit mishandles, leading to the false rejection of the perfectly valid new connection. Fix this by replacing the interface check with protocol-agnostic state checks. We now skip the tree insertion and preserve the lockless garbage collection optimization only if the connection is IPS_ASSURED. This allows early-confirmed setup packets (such as reused TIME_WAIT sockets or locally generated SYN-ACKs) to be properly evaluated and counted without falsely dropping. The goto check_connections path is maintained to ensure these setup packets are deduplicated correctly. This has been tested with slowhttptest and HTTP server configured locally to ensure we are not breaking soft-limiting scenarios for local or external connections. In addition, it was tested with a OVS zone limit too. Fixes: 69894e5b4c5e ("netfilter: nft_connlimit: update the count if add was skipped") Reported-by: Alejandro Olivan Alvarez Closes: https://lore.kernel.org/netfilter-devel/177349610461.3071718.4083978280323144323@eldamar.lan/ Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nf_conncount.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index e880e4b3cd82d..6b985afa4981e 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -179,17 +179,16 @@ static int __nf_conncount_add(struct net *net, return -ENOENT; if (ct && nf_ct_is_confirmed(ct)) { - /* local connections are confirmed in postrouting so confirmation - * might have happened before hitting connlimit + /* Connection is confirmed but might still be in the setup phase. + * Only skip the tracking if it is fully assured. This guarantees + * that setup packets or retransmissions are properly counted and + * deduplicated. */ - if (skb->skb_iif != LOOPBACK_IFINDEX) { + if (test_bit(IPS_ASSURED_BIT, &ct->status)) { err = -EEXIST; goto out_put; } - /* this is likely a local connection, skip optimization to avoid - * adding duplicates from a 'packet train' - */ goto check_connections; } From d3e9a7e2ce9dc01fa8765966acfa84f37e06be1b Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 11 Jun 2026 12:21:20 +0800 Subject: [PATCH 1518/3297] netfilter: nft_synproxy: stop bypassing the priv->info snapshot [ Upstream commit 11d4bc4e26fb66040a5b5d95e9abf37deac2b1fc ] nft_synproxy_eval_v4() and nft_synproxy_eval_v6() already take a whole-object READ_ONCE() snapshot of the shared priv->info state before building the SYNACK reply, but nft_synproxy_tcp_options() still masks opts->options with priv->info.options from the live shared object. When a named synproxy object is updated concurrently with SYN traffic, the eval path can then mix mss and timestamp handling from the local snapshot with an options mask taken from a newer configuration, so one SYNACK no longer reflects a coherent synproxy configuration. Use info->options so nft_synproxy_tcp_options() stays on the same local snapshot that the eval path already copied from priv->info. Fixes: ee394f96ad75 ("netfilter: nft_synproxy: add synproxy stateful object support") Signed-off-by: Runyu Xiao Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_synproxy.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c index 4d3e5a31b4125..ad3e7d27f02e9 100644 --- a/net/netfilter/nft_synproxy.c +++ b/net/netfilter/nft_synproxy.c @@ -23,14 +23,13 @@ static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = { static void nft_synproxy_tcp_options(struct synproxy_options *opts, const struct tcphdr *tcp, struct synproxy_net *snet, - struct nf_synproxy_info *info, - const struct nft_synproxy *priv) + struct nf_synproxy_info *info) { this_cpu_inc(snet->stats->syn_received); if (tcp->ece && tcp->cwr) opts->options |= NF_SYNPROXY_OPT_ECN; - opts->options &= priv->info.options; + opts->options &= info->options; opts->mss_encode = opts->mss_option; opts->mss_option = info->mss; if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) @@ -55,7 +54,7 @@ static void nft_synproxy_eval_v4(const struct nft_synproxy *priv, if (tcp->syn) { /* Initial SYN from client */ - nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + nft_synproxy_tcp_options(opts, tcp, snet, &info); synproxy_send_client_synack(net, skb, tcp, opts); consume_skb(skb); regs->verdict.code = NF_STOLEN; @@ -86,7 +85,7 @@ static void nft_synproxy_eval_v6(const struct nft_synproxy *priv, if (tcp->syn) { /* Initial SYN from client */ - nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + nft_synproxy_tcp_options(opts, tcp, snet, &info); synproxy_send_client_synack_ipv6(net, skb, tcp, opts); consume_skb(skb); regs->verdict.code = NF_STOLEN; From 33e1875d6b5b552a2e5652b40074c604199354ee Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 15 Jun 2026 20:10:44 +0200 Subject: [PATCH 1519/3297] netfilter: nft_compat: ebtables emulation must reject non-bridge targets [ Upstream commit 9dbba7e694ec045f21ede2f892fb42b81b4e1692 ] xtables targets return netfilter verdicts: NF_ACCEPT, NF_DROP, and so on. ebtables targets return incompatible verdicts: EBT_ACCEPT, EBT_DROP, ... We cannot allow fallback to NFPROTO_UNSPEC. ebtables doesn't permit this since 11ff7288beb2 ("netfilter: ebtables: reject non-bridge targets") but that commit missed the nft_compat layer. Reported-by: Ren Wei Reported-by: Wyatt Feng Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_compat.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 08f620311b03f..a77ffe2fcea75 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -389,6 +389,22 @@ static int nft_target_validate(const struct nft_ctx *ctx, return 0; } +static int nft_target_bridge_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct xt_target *target = expr->ops->data; + + /* Do not allow UNSPEC to stand-in for NFPROTO_BRIDGE + * targets: they are incompatible. ebtables targets return + * EBT_ACCEPT, DROP and so on which are not compatible with + * NF_ACCEPT, NF_DROP and so on. + */ + if (target->family != NFPROTO_BRIDGE) + return -ENOENT; + + return nft_target_validate(ctx, expr); +} + static void __nft_match_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt, @@ -916,14 +932,16 @@ nft_target_select_ops(const struct nft_ctx *ctx, ops->init = nft_target_init; ops->destroy = nft_target_destroy; ops->dump = nft_target_dump; - ops->validate = nft_target_validate; ops->data = target; ops->reduce = NFT_REDUCE_READONLY; - if (family == NFPROTO_BRIDGE) + if (family == NFPROTO_BRIDGE) { ops->eval = nft_target_eval_bridge; - else + ops->validate = nft_target_bridge_validate; + } else { ops->eval = nft_target_eval_xt; + ops->validate = nft_target_validate; + } return ops; err: From 60a6689b9a5df004fbc29148a2d8f6919190eae3 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 23 Jun 2026 10:31:06 +0800 Subject: [PATCH 1520/3297] gpio: davinci: fix IRQ domain leak on devm_kzalloc failure [ Upstream commit 4e8eb6952aa6749726c6c3763ae0032a6332c24f ] In davinci_gpio_irq_setup(), after successfully creating an IRQ domain with irq_domain_create_legacy(), a subsequent devm_kzalloc() failure in the bank loop causes the function to return -ENOMEM without removing the IRQ domain. Unlike devm-managed resources, irq_domain_create_legacy() does not auto-clean up on probe failure, so the domain is leaked. Fix by calling irq_domain_remove() before returning on allocation failure. Fixes: b5cf3fd827d2 ("gpio: davinci: Redesign driver to accommodate ngpios in one gpio chip") Signed-off-by: Qingshuang Fu Link: https://patch.msgid.link/20260623023106.117229-1-fffsqian@163.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 538f27209ce71..5de42f1143dab 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -550,8 +550,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) sizeof(struct davinci_gpio_irq_data), GFP_KERNEL); - if (!irqdata) + if (!irqdata) { + irq_domain_remove(chips->irq_domain); return -ENOMEM; + } irqdata->regs = g; irqdata->bank_num = bank; From d2a41c85beb56c5f1db39a805b238b3f47567fc5 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:20 +0900 Subject: [PATCH 1521/3297] NTB: epf: Make db_valid_mask cover only real doorbell bits [ Upstream commit 6eb7e28f1f24a28234add38755687a7ed8bc2654 ] ndev->db_count includes an unused doorbell slot due to the legacy extra offset in the peer doorbell path. db_valid_mask must cover only the real doorbell bits and exclude the unused slot. Set db_valid_mask to BIT_ULL(db_count - 1) - 1. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260513024923.451765-10-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/ntb/hw/epf/ntb_hw_epf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 15d99d08f84f9..051af2a45c6f9 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -560,7 +560,11 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) return ret; } - ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; + /* + * ndev->db_count includes an extra skipped slot due to the legacy + * doorbell layout, hence -1. + */ + ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1; ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); From 56ec2a08d27b5c6513bdca791416d014b2597106 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:21 +0900 Subject: [PATCH 1522/3297] NTB: epf: Report 0-based doorbell vector via ntb_db_event() [ Upstream commit 3147f0964cecca15e349cbfbdd6a28eb6d50379d ] ntb_db_event() expects the vector number to be relative to the first doorbell vector starting at 0. Vector 0 is reserved for link events in the EPF driver, so doorbells start at vector 1. However, both supported peers (ntb_hw_epf with pci-epf-ntb, and pci-epf-vntb) have historically skipped vector 1 and started doorbells at vector 2. Pass (irq_no - 2) to ntb_db_event() so doorbells are reported as 0..N-1. If irq_no == 1 is ever observed, warn and ignore it, since the slot is reserved in the legacy layout and reporting it as DB#0 would collide with the real DB#0 slot. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Suggested-by: Dave Jiang Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-11-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/ntb/hw/epf/ntb_hw_epf.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 051af2a45c6f9..292e2ca059ac3 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -69,6 +69,12 @@ enum epf_ntb_bar { NTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + #define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1) struct ntb_epf_dev { @@ -322,10 +328,14 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) irq_no = irq - ndev->irq_base; ndev->db_val = irq_no + 1; - if (irq_no == 0) + if (irq_no == EPF_IRQ_LINK) { ntb_link_event(&ndev->ntb); - else - ntb_db_event(&ndev->ntb, irq_no); + } else if (irq_no == EPF_IRQ_RESERVED_DB) { + dev_warn_ratelimited(ndev->dev, + "Unexpected reserved doorbell slot IRQ received\n"); + } else { + ntb_db_event(&ndev->ntb, irq_no - EPF_IRQ_DB_START); + } return IRQ_HANDLED; } From 04117aea9bc11a08c594c68ce23ed259dbddb777 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Wed, 13 May 2026 11:49:22 +0900 Subject: [PATCH 1523/3297] NTB: epf: Fix doorbell bitmask and IRQ vector handling [ Upstream commit 4fdea8dbb62d746429498e07385a3ba0b0f6d1ab ] The EPF driver currently stores the incoming doorbell as a vector number (irq_no + 1) in db_val and db_clear() clears all bits unconditionally. This breaks db_read()/db_clear() semantics when multiple doorbells are used. Store doorbells as a bitmask (BIT_ULL(vector)) and make db_clear(db_bits) clear only the specified bits. Use atomic64 operations as db_val is updated from interrupt context. Once db_val is stored as a bitmask, the ISR's doorbell vector is used not only for ntb_db_event(), but also as the bit index for BIT_ULL(). The existing ISR derives that vector by subtracting pci_irq_vector(pdev, 0) from the Linux IRQ number passed to the handler, but Linux IRQ numbers are not guaranteed to be contiguous. Pass per-vector context as request_irq() dev_id instead, so the ISR gets the device vector directly. Validate the doorbell vector before updating db_val or calling ntb_db_event(), so an unexpected vector cannot create an invalid shift or be reported to NTB clients. While at it, read and validate mw_count before requesting interrupt vectors. An unsupported memory-window count does not need IRQs, and failing before ntb_epf_init_isr() keeps the probe error path simple. Fixes: 812ce2f8d14e ("NTB: Add support for EPF PCI Non-Transparent Bridge") Suggested-by: Dave Jiang Signed-off-by: Koichiro Den Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260513024923.451765-12-den@valinux.co.jp Signed-off-by: Sasha Levin --- drivers/ntb/hw/epf/ntb_hw_epf.c | 61 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 292e2ca059ac3..200409b4e4ddf 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -6,6 +6,7 @@ * Author: Kishon Vijay Abraham I */ +#include #include #include #include @@ -77,6 +78,13 @@ enum epf_irq_slot { #define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1) +struct ntb_epf_dev; + +struct ntb_epf_irq_ctx { + struct ntb_epf_dev *ndev; + unsigned int irq_no; +}; + struct ntb_epf_dev { struct ntb_dev ntb; struct device *dev; @@ -96,9 +104,9 @@ struct ntb_epf_dev { unsigned int self_spad; unsigned int peer_spad; - int db_val; + atomic64_t db_val; u64 db_valid_mask; - int irq_base; + struct ntb_epf_irq_ctx irq_ctx[NTB_EPF_MAX_DB_COUNT + 1]; }; #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) @@ -322,11 +330,10 @@ static int ntb_epf_link_disable(struct ntb_dev *ntb) static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) { - struct ntb_epf_dev *ndev = dev; - int irq_no; - - irq_no = irq - ndev->irq_base; - ndev->db_val = irq_no + 1; + struct ntb_epf_irq_ctx *ctx = dev; + struct ntb_epf_dev *ndev = ctx->ndev; + unsigned int db_vector; + unsigned int irq_no = ctx->irq_no; if (irq_no == EPF_IRQ_LINK) { ntb_link_event(&ndev->ntb); @@ -334,7 +341,17 @@ static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) dev_warn_ratelimited(ndev->dev, "Unexpected reserved doorbell slot IRQ received\n"); } else { - ntb_db_event(&ndev->ntb, irq_no - EPF_IRQ_DB_START); + db_vector = irq_no - EPF_IRQ_DB_START; + if (ndev->db_count < NTB_EPF_MIN_DB_COUNT || + db_vector >= ndev->db_count - 1) { + dev_warn_ratelimited(ndev->dev, + "Unexpected doorbell vector %u (db_count %u)\n", + db_vector, ndev->db_count); + return IRQ_HANDLED; + } + + atomic64_or(BIT_ULL(db_vector), &ndev->db_val); + ntb_db_event(&ndev->ntb, db_vector); } return IRQ_HANDLED; @@ -361,18 +378,18 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument &= ~MSIX_ENABLE; } - ndev->irq_base = pci_irq_vector(pdev, 0); + ndev->db_count = irq - 1; for (i = 0; i < irq; i++) { + ndev->irq_ctx[i].ndev = ndev; + ndev->irq_ctx[i].irq_no = i; ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, - 0, "ntb_epf", ndev); + 0, "ntb_epf", &ndev->irq_ctx[i]); if (ret) { dev_err(dev, "Failed to request irq\n"); goto err_free_irq; } } - ndev->db_count = irq - 1; - ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL, argument | irq); if (ret) { @@ -384,7 +401,7 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) err_free_irq: while (i--) - free_irq(pci_irq_vector(pdev, i), ndev); + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); return ret; @@ -509,7 +526,7 @@ static u64 ntb_epf_db_read(struct ntb_dev *ntb) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - return ndev->db_val; + return atomic64_read(&ndev->db_val); } static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) @@ -521,7 +538,7 @@ static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - ndev->db_val = 0; + atomic64_and(~db_bits, &ndev->db_val); return 0; } @@ -562,6 +579,12 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) struct device *dev = ndev->dev; int ret; + ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); + if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { + dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); + return -EINVAL; + } + /* One Link interrupt and rest doorbell interrupt */ ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1, NTB_EPF_MAX_DB_COUNT + 1); @@ -575,14 +598,8 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) * doorbell layout, hence -1. */ ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1; - ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); - if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { - dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); - return -EINVAL; - } - return 0; } @@ -677,7 +694,7 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev) ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1); for (i = 0; i < ndev->db_count + 1; i++) - free_irq(pci_irq_vector(pdev, i), ndev); + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); } From 94defb18ac792fd16407d5a52ad0d3f5055c4b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:30 +0000 Subject: [PATCH 1524/3297] alpha/PCI: Add security_locked_down() check to pci_mmap_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 78a228f0aa0e9eba31955950c8a40a9945e2c8bb ] Currently, Alpha's pci_mmap_resource() does not check security_locked_down(LOCKDOWN_PCI_ACCESS) before allowing userspace to mmap PCI BARs. The generic version has had this check since commit eb627e17727e ("PCI: Lock down BAR access when the kernel is locked down") to prevent DMA attacks when the kernel is locked down. Add the same check to Alpha's pci_mmap_resource(). Fixes: eb627e17727e ("PCI: Lock down BAR access when the kernel is locked down") Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-12-kwilczynski@kernel.org Signed-off-by: Sasha Levin --- arch/alpha/kernel/pci-sysfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 3048758304b57..2324720c3e83f 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -71,7 +72,11 @@ static int pci_mmap_resource(struct kobject *kobj, struct resource *res = attr->private; enum pci_mmap_state mmap_type; struct pci_bus_region bar; - int i; + int i, ret; + + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; for (i = 0; i < PCI_STD_NUM_BARS; i++) if (res == &pdev->resource[i]) From a0904f7d270358efdaa2e7dff465824d83800cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Fri, 8 May 2026 04:35:33 +0000 Subject: [PATCH 1525/3297] alpha/PCI: Fix __pci_mmap_fits() overflow for zero-length BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 802a3b3f470b9bc1148f26e01fc9cbfeb4dfcb57 ] Currently, __pci_mmap_fits() computes the BAR size using "pci_resource_len() - 1", which wraps to a large value when the BAR length is zero, causing the bounds check to incorrectly succeed. Add an early return for empty resources. Fixes: 10a0ef39fbd1 ("PCI/alpha: pci sysfs resources") Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Tested-by: Magnus Lindholm Tested-by: Shivaprasad G Bhat Reviewed-by: Ilpo Järvinen Acked-by: Magnus Lindholm Link: https://patch.msgid.link/20260508043543.217179-15-kwilczynski@kernel.org Signed-off-by: Sasha Levin --- arch/alpha/kernel/pci-sysfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 2324720c3e83f..21833d1c867d7 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -37,12 +37,16 @@ static int hose_mmap_page_range(struct pci_controller *hose, static int __pci_mmap_fits(struct pci_dev *pdev, int num, struct vm_area_struct *vma, int sparse) { + resource_size_t len = pci_resource_len(pdev, num); unsigned long nr, start, size; int shift = sparse ? 5 : 0; + if (!len) + return 0; + nr = vma_pages(vma); start = vma->vm_pgoff; - size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1; + size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1; if (start < size && size - start >= nr) return 1; From 03b743586a2469744e96e9c1015096d07240935d Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 20 Jun 2026 13:15:31 -0700 Subject: [PATCH 1526/3297] net, bpf: check master for NULL in xdp_master_redirect() [ Upstream commit e82d8cc4321c373dc46e741cd2dfdaa7921fddb7 ] xdp_master_redirect() dereferences the result of netdev_master_upper_dev_get_rcu() without a NULL check, but that helper returns NULL when the receiving device has no upper-master adjacency. The reach guard only checks netif_is_bond_slave(). On bond slave release bond_upper_dev_unlink() drops the upper-master adjacency before clearing IFF_SLAVE, so an XDP_TX reaching xdp_master_redirect() in that window still passes netif_is_bond_slave() while master is already NULL, and faults on master->flags at offset 0xb0: BUG: kernel NULL pointer dereference, address: 00000000000000b0 RIP: 0010:xdp_master_redirect (net/core/filter.c:4432) Call Trace: xdp_master_redirect (net/core/filter.c:4432) bpf_prog_run_generic_xdp (include/net/xdp.h:700) do_xdp_generic (net/core/dev.c:5608) __netif_receive_skb_one_core (net/core/dev.c:6204) process_backlog (net/core/dev.c:6319) __napi_poll (net/core/dev.c:7729) net_rx_action (net/core/dev.c:7792) handle_softirqs (kernel/softirq.c:622) __dev_queue_xmit (include/linux/bottom_half.h:33) packet_sendmsg (net/packet/af_packet.c:3082) __sys_sendto (net/socket.c:2252) Kernel panic - not syncing: Fatal exception in interrupt The missing check dates back to the original code; commit 1921f91298d1 ("net, bpf: fix null-ptr-deref in xdp_master_redirect() for down master") later added the master->flags read where the fault now lands but kept the unconditional deref. Check master for NULL before use; a NULL master is treated the same as one that is not up. Fixes: 879af96ffd72 ("net, core: Add support for XDP redirection to slave device") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260620201531.180123-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- 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 a3e6cd9c4a78b..42385e7904313 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4424,7 +4424,7 @@ u32 xdp_master_redirect(struct xdp_buff *xdp) struct net_device *master, *slave; master = netdev_master_upper_dev_get_rcu(xdp->rxq->dev); - if (unlikely(!(master->flags & IFF_UP))) + if (unlikely(!master || !(master->flags & IFF_UP))) return XDP_ABORTED; slave = master->netdev_ops->ndo_xdp_get_xmit_slave(master, xdp); if (slave && slave != xdp->rxq->dev) { From 7557df1b60f20430f9525fcb71da8b3f6bf70ba7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 22 Jun 2026 11:01:08 +0000 Subject: [PATCH 1527/3297] net: do not acquire dev->tx_global_lock in netdev_watchdog_up() [ Upstream commit d09a78a2a469e4fab75108325efb813c49520809 ] Marek Szyprowski reported a deadlock during system resume when virtio_net driver is used. The deadlock occurs because netif_device_attach() is called while holding dev->tx_global_lock (via netif_tx_lock_bh() in virtnet_restore_up()). netif_device_attach() calls __netdev_watchdog_up(), which now also tries to acquire dev->tx_global_lock to synchronize with dev_watchdog(). This recursive lock acquisition results in a deadlock. Fix this by removing the tx_global_lock acquisition from netdev_watchdog_up(). The critical state (watchdog_timer and watchdog_ref_held) is already protected by dev->watchdog_lock, which was introduced in the blamed commit. Fixes: 8eed5519e496 ("net: watchdog: fix refcount tracking races") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/netdev/a443376e-5187-4268-93b3-58047ef113a8@samsung.com/ Signed-off-by: Eric Dumazet Tested-by: Marek Szyprowski Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260622110108.69541-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_generic.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index d44cee56a58e3..34573c9b8a5eb 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -569,9 +569,8 @@ void netdev_watchdog_up(struct net_device *dev) return; if (dev->watchdog_timeo <= 0) dev->watchdog_timeo = 5*HZ; - spin_lock_bh(&dev->tx_global_lock); - spin_lock(&dev->watchdog_lock); + spin_lock_bh(&dev->watchdog_lock); if (!mod_timer(&dev->watchdog_timer, round_jiffies(jiffies + dev->watchdog_timeo))) { if (!dev->watchdog_ref_held) { @@ -580,9 +579,7 @@ void netdev_watchdog_up(struct net_device *dev) dev->watchdog_ref_held = true; } } - spin_unlock(&dev->watchdog_lock); - - spin_unlock_bh(&dev->tx_global_lock); + spin_unlock_bh(&dev->watchdog_lock); } EXPORT_SYMBOL_GPL(netdev_watchdog_up); From 410629528067029096dba7ad7bc49e4a5e92708d Mon Sep 17 00:00:00 2001 From: Aleksandrova Alyona Date: Thu, 18 Jun 2026 14:05:08 +0300 Subject: [PATCH 1528/3297] net: dsa: sja1105: round up PTP perout pin duration [ Upstream commit aee5836273b07b439fb245fb43930664d8b78518 ] pin_duration is converted from the user-provided period to SJA1105 clock ticks and is later passed as the cycle_time argument to future_base_time(). Very small period values may become zero after the conversion, which can lead to a division by zero in future_base_time(). Round zero pin_duration up to 1 tick so that the smallest unsupported periods use the minimum non-zero hardware duration instead of passing zero to future_base_time(). Fixes: 747e5eb31d59 ("net: dsa: sja1105: configure the PTP_CLK pin as EXT_TS or PER_OUT") Signed-off-by: Aleksandrova Alyona Link: https://patch.msgid.link/20260618110508.53094-1-aga@itb.spb.ru Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/sja1105/sja1105_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index fefe46e2a5e61..350f958dcb2af 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -755,7 +755,7 @@ static int sja1105_per_out_enable(struct sja1105_private *priv, * 2 edges on PTP_CLK. So check for truncation which happens * at periods larger than around 68.7 seconds. */ - pin_duration = ns_to_sja1105_ticks(pin_duration / 2); + pin_duration = max_t(u64, ns_to_sja1105_ticks(pin_duration / 2), 1); if (pin_duration > U32_MAX) { rc = -ERANGE; goto out; From a9e6707322ef215d39d4655b176c094f45f0ab52 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 22 Jun 2026 11:18:25 +0000 Subject: [PATCH 1529/3297] veth: fix NAPI leak in XDP enable error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6739027cb72da26890edd424c77080d187b2a92e ] During XDP enablement in veth, if xdp_rxq_info_reg() or xdp_rxq_info_reg_mem_model() fails, the driver rolls back the changes. However, the rollback loop: for (i--; i >= start; i--) { decrements the loop index 'i' before the first iteration. This correctly skips unregistering the rxq for the failed index 'i' (as registration failed or was already cleaned up), but it also erroneously skips calling netif_napi_deli() for rq[i].xdp_napi. Since netif_napi_add() was already called for index 'i', this leaves a dangling napi_struct in the device's napi_list. When the veth device is later destroyed, the freed queue memory (which contains the leaked NAPI structure) can be reused. The subsequent device teardown iterates the NAPI list and corrupts the reallocated memory, leading to UAF. Fix this by explicitly deleting the NAPI association for the failed index 'i' before rolling back the successfully configured queues. Fixes: b02e5a0ebb17 ("xsk: Propagate napi_id to XDP socket Rx path") Reported-by: Guenter Roeck Signed-off-by: Eric Dumazet Cc: Björn Töpel Cc: Daniel Borkmann Cc: Ilias Apalodimas Cc: Michael S. Tsirkin Cc: Tariq Toukan Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260622111825.88337-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/veth.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index b00613cb07cf0..4595c61c0790b 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1136,6 +1136,8 @@ static int veth_enable_xdp_range(struct net_device *dev, int start, int end, err_reg_mem: xdp_rxq_info_unreg(&priv->rq[i].xdp_rxq); err_rxq_reg: + if (!napi_already_on) + netif_napi_del(&priv->rq[i].xdp_napi); for (i--; i >= start; i--) { struct veth_rq *rq = &priv->rq[i]; From ff127c0aa52799c16b4ad87a99d2b8f91986247b Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 22 Jun 2026 12:29:11 +0200 Subject: [PATCH 1530/3297] net: usb: lan78xx: restore VLAN and hash filters after link up [ Upstream commit 5c12248673c76f10ab900f70724c5da288c7efa5 ] Configured VLANs intermittently stop receiving traffic after a link down/up cycle, e.g. when the network cable is unplugged and plugged back in. VLAN filtering stays enabled but all VLAN-tagged frames are dropped until a VLAN is added or removed again. The LAN7801 datasheet (DS00002123E) states: "A portion of the MAC operates on clocks generated by the Ethernet PHY. During a PHY reset event, this portion of the MAC is designed to not be taken out of reset until the PHY clocks are operational" (section 8.10, MAC Reset Watchdog Timer) "After a reset event, the RFE will automatically initialize the contents of the VHF to 0h." (section 7.1.4, VHF Organization) Thus a link down/up cycle stops and restarts the PHY clock, resets the PHY-clocked portion of the MAC, and the RFE clears its VLAN/DA hash filter (VHF) memory. The VHF holds both the VLAN filter table and the multicast hash table, but the driver never reprograms either from its shadow copy once the link is back, so both stay empty. Reprogram the VLAN filter and multicast hash tables on link up. Reported-by: Sven Schuchmann Closes: https://lore.kernel.org/netdev/BEZP281MB224501E38B30BFDC4BD3D364D9E32@BEZP281MB2245.DEUP281.PROD.OUTLOOK.COM/T/#u Tested-by: Sven Schuchmann Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver") Signed-off-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260622102911.484045-1-nb@tipi-net.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/usb/lan78xx.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 858a442d6996a..4657f49473123 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1452,6 +1452,15 @@ static inline u32 lan78xx_hash(char addr[ETH_ALEN]) return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff; } +static int lan78xx_write_mchash_table(struct lan78xx_net *dev) +{ + struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); + + return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, + DP_SEL_VHF_VLAN_LEN, + DP_SEL_VHF_HASH_LEN, pdata->mchash_table); +} + static void lan78xx_deferred_multicast_write(struct work_struct *param) { struct lan78xx_priv *pdata = @@ -1462,9 +1471,7 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param) netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n", pdata->rfe_ctl); - ret = lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, - DP_SEL_VHF_VLAN_LEN, - DP_SEL_VHF_HASH_LEN, pdata->mchash_table); + ret = lan78xx_write_mchash_table(dev); if (ret < 0) goto multicast_write_done; @@ -1557,6 +1564,7 @@ static void lan78xx_set_multicast(struct net_device *netdev) } static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev); +static int lan78xx_write_vlan_table(struct lan78xx_net *dev); static int lan78xx_mac_reset(struct lan78xx_net *dev) { @@ -2514,6 +2522,17 @@ static void lan78xx_mac_link_up(struct phylink_config *config, if (ret < 0) goto link_up_fail; + /* The RFE clears the VLAN/DA hash filter (VHF) on a link down/up + * cycle, so reprogram both tables from their shadow copies. + */ + ret = lan78xx_write_vlan_table(dev); + if (ret < 0) + goto link_up_fail; + + ret = lan78xx_write_mchash_table(dev); + if (ret < 0) + goto link_up_fail; + netif_start_queue(net); return; @@ -3065,14 +3084,20 @@ static int lan78xx_set_features(struct net_device *netdev, return lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); } +static int lan78xx_write_vlan_table(struct lan78xx_net *dev) +{ + struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); + + return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0, + DP_SEL_VHF_VLAN_LEN, pdata->vlan_table); +} + static void lan78xx_deferred_vlan_write(struct work_struct *param) { struct lan78xx_priv *pdata = container_of(param, struct lan78xx_priv, set_vlan); - struct lan78xx_net *dev = pdata->dev; - lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0, - DP_SEL_VHF_VLAN_LEN, pdata->vlan_table); + lan78xx_write_vlan_table(pdata->dev); } static int lan78xx_vlan_rx_add_vid(struct net_device *netdev, From 2140c2f3f2e7b066e1ae616ede8856cafd8015e9 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sat, 20 Jun 2026 09:07:49 -0400 Subject: [PATCH 1531/3297] net/sched: cls_api: Handle TC_ACT_CONSUMED in tcf_qevent_handle [ Upstream commit a8a02897f2b479127db261de05cbf0c28b98d159 ] tcf_classify() can return TC_ACT_CONSUMED while the skb is held by the defragmentation engine (e.g. act_ct on out-of-order fragments). When that happens the skb is no longer owned by the caller and must not be touched again. tcf_qevent_handle() did not handle TC_ACT_CONSUMED: it fell through the switch and returned the skb to the caller as if classification had passed. The only qdisc that wires up qevents today is RED, via three call sites (qe_mark on RED_PROB_MARK/HARD_MARK, qe_early_drop on congestion_drop) red_enqueue() was continuing to operate on an skb it no longer owns in this case -- enqueueing it, dropping it, or updating statistics. Resulting in a UAF. tc qdisc add dev eth0 root handle 1: red ... qevent early_drop block 10 tc filter add block 10 ... action ct (with ct defrag enabled and traffic that produces out-of-order fragments, e.g. a fragmented UDP stream) Handle TC_ACT_CONSUMED in tcf_qevent_handle() the same way the ingress and egress fast paths do: treat it as stolen and return NULL without touching the skb. Unlike the TC_ACT_STOLEN case, the skb must not be dropped/freed here, as it is no longer owned by us. Fixes: 3f14b377d01d ("net/sched: act_ct: fix skb leak and crash on ooo frags") Reported-by: Zero Day Initiative Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260620130749.226642-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_api.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b03dc4fe20e52..a94de0cab29cc 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -4050,6 +4050,9 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru skb_do_redirect(skb); *ret = __NET_XMIT_STOLEN; return NULL; + case TC_ACT_CONSUMED: + *ret = __NET_XMIT_STOLEN; + return NULL; } return skb; From 56c26538f0e585cd990eb476c24827fea98a8037 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:52 +0200 Subject: [PATCH 1532/3297] ipv6: fix error handling in disable_ipv6 sysctl [ Upstream commit c779441e5070e2268bdfe77f6e2e0de926c431e3 ] When writing to the disable_ipv6 sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is overwriting that error for write operations. This results in a silent failure, it returns a successful write although the configuration was not modified at all. When modifying the "all" variant it can also modify the configuration of existing interfaces to the wrong value. Fix this by checking the return value of proc_dointvec() and returning early on failure. Fixes: 56d417b12e57 ("IPv6: Add 'autoconf' and 'disable_ipv6' module parameters") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-2-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c8c82891fe14c..87c0cecc76bb9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6447,6 +6447,8 @@ static int addrconf_sysctl_disable(const struct ctl_table *ctl, int write, lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write) ret = addrconf_disable_ipv6(ctl, valp, val); From b060606bc7e4614746dc93a245e06e923a027146 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:53 +0200 Subject: [PATCH 1533/3297] ipv6: fix error handling in ignore_routes_with_linkdown sysctl [ Upstream commit cf4f2b14401f29ccac56393ca9e4b42a2505f540 ] When writing to the ignore_routes_with_linkdown sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is overwriting that error for write operations. This results in a silent failure, it returns a successful write although the configuration was not modified at all. When modifying the "all" variant it can also modify the configuration of existing interfaces to the wrong value. Fix this by checking the return value of proc_dointvec() and returning early on failure. Fixes: 35103d11173b ("net: ipv6 sysctl option to ignore routes when nexthop link is down") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-3-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 87c0cecc76bb9..1907cdb54d1dc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6651,6 +6651,8 @@ int addrconf_sysctl_ignore_routes_with_linkdown(const struct ctl_table *ctl, lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write) ret = addrconf_fixup_linkdown(ctl, valp, val); From 2bf70e0306f8d63740a4e39e551ab46779d66d61 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:54 +0200 Subject: [PATCH 1534/3297] ipv6: fix error handling in forwarding sysctl [ Upstream commit 058b9b19f9639fe1e1225a17c540f61b65bee6ad ] When writing to the forwarding sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is overwriting that error for write operations. This results in a silent failure, it returns a successful write although the configuration was not modified at all. When modifying the "all" variant it can also modify the configuration of existing interfaces to the wrong value. Fix this by checking the return value of proc_dointvec() and returning early on failure. In addition, adjust return code of addrconf_fixup_forwarding() for successful operation. Fixes: b325fddb7f86 ("ipv6: Fix sysctl unregistration deadlock") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-4-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 1907cdb54d1dc..3ea6b4c6192d1 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -913,7 +913,7 @@ static int addrconf_fixup_forwarding(const struct ctl_table *table, int *p, int if (newf) rt6_purge_dflt_routers(net); - return 1; + return 0; } static void addrconf_linkdown_change(struct net *net, __s32 newf) @@ -6350,6 +6350,8 @@ static int addrconf_sysctl_forward(const struct ctl_table *ctl, int write, lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write) ret = addrconf_fixup_forwarding(ctl, valp, val); From ae58dbf1d78d7d5d247b112286412470a63b3eb4 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:55 +0200 Subject: [PATCH 1535/3297] ipv6: fix error handling in disable_policy sysctl [ Upstream commit 3e0e51c0ee1d08cc9d48dc17f3248d5b31cf8066 ] When writing to the disable_policy sysctl, if proc_dointvec() fails to parse the input, it returns a negative error code. The current implementation is resetting the position argument even if an error occurred during proc_dointvec() and not only during sysctl restart. Fix this by checking the return value of proc_dointvec() and returning early on failure. Fixes: df789fe75206 ("ipv6: Provide ipv6 version of "disable_policy" sysctl") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-5-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3ea6b4c6192d1..b2a8a058f2d28 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6749,6 +6749,8 @@ static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write lctl = *ctl; lctl.data = &val; ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + if (ret) + return ret; if (write && (*valp != val)) ret = addrconf_disable_policy(ctl, valp, val); From 419017dd2dda2cfa9485e350587e64206eee33e7 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:56 +0200 Subject: [PATCH 1536/3297] ipv6: fix state corruption during proxy_ndp sysctl restart [ Upstream commit 6a1b50e585f033f3e201f42a18b37f070095fb80 ] When handling proxy_ndp, if rtnl_net_trylock() fails, the operation is retried but as the value was already modified by the initial proc_dointvec() call, the restarted syscall will read the newly modified value as the 'old' state. Fix this by taking the RTNL lock before parsing the input value if the operation is a write. Fixes: c92d5491a6d9 ("netconf: add support for IPv6 proxy_ndp") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-6-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b2a8a058f2d28..530c302beb489 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6462,20 +6462,19 @@ static int addrconf_sysctl_disable(const struct ctl_table *ctl, int write, static int addrconf_sysctl_proxy_ndp(const struct ctl_table *ctl, int write, void *buffer, size_t *lenp, loff_t *ppos) { + struct net *net = ctl->extra2; int *valp = ctl->data; - int ret; int old, new; + int ret; + + if (write && !rtnl_net_trylock(net)) + return restart_syscall(); old = *valp; ret = proc_dointvec(ctl, write, buffer, lenp, ppos); new = *valp; if (write && old != new) { - struct net *net = ctl->extra2; - - if (!rtnl_net_trylock(net)) - return restart_syscall(); - if (valp == &net->ipv6.devconf_dflt->proxy_ndp) { inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_PROXY_NEIGH, @@ -6494,8 +6493,9 @@ static int addrconf_sysctl_proxy_ndp(const struct ctl_table *ctl, int write, idev->dev->ifindex, &idev->cnf); } - rtnl_net_unlock(net); } + if (write) + rtnl_net_unlock(net); return ret; } From 123b559aa6bb6aa77be7b38611eff3f56adabdbf Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 22 Jun 2026 15:08:57 +0200 Subject: [PATCH 1537/3297] ipv6: fix missing notification for ignore_routes_with_linkdown [ Upstream commit 17dc3b245de45b1f2012e3a48ec51889f544e67b ] When changing the ignore_routes_with_linkdown sysctl for a specific interface, the RTM_NEWNETCONF netlink notification was not being emitted to userspace. Fix this by emitting the notification when needed. In addition, fix bogus return value for successful "all" and specific interface write operation leading to a wrong reset of the position pointer. Fixes: 35103d11173b ("net: ipv6 sysctl option to ignore routes when nexthop link is down") Reviewed-by: Nicolas Dichtel Signed-off-by: Fernando Fernandez Mancera Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260622130857.5115-7-fmancera@suse.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/addrconf.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 530c302beb489..c98b1b919f18c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -955,11 +955,7 @@ static int addrconf_fixup_linkdown(const struct ctl_table *table, int *p, int ne NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, NETCONFA_IFINDEX_DEFAULT, net->ipv6.devconf_dflt); - rtnl_net_unlock(net); - return 0; - } - - if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) { + } else if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) { WRITE_ONCE(net->ipv6.devconf_dflt->ignore_routes_with_linkdown, newf); addrconf_linkdown_change(net, newf); if ((!newf) ^ (!old)) @@ -968,11 +964,21 @@ static int addrconf_fixup_linkdown(const struct ctl_table *table, int *p, int ne NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); + } else { + if (!newf ^ !old) { + struct inet6_dev *idev = table->extra1; + + inet6_netconf_notify_devconf(net, + RTM_NEWNETCONF, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + idev->dev->ifindex, + &idev->cnf); + } } rtnl_net_unlock(net); - return 1; + return 0; } #endif From 4e62be1490d23d7b37ea550e82f46e9d4ffa6bc2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 22 Jun 2026 08:47:53 -0700 Subject: [PATCH 1538/3297] eth: fbnic: fix ordering of heartbeat vs ownership [ Upstream commit d87363b0edfc7504ff2b144fe4cdd8154f90f42e ] When requesting ownership of the NIC (MAC/PHY control), we set up the heartbeat to look stale: /* Initialize heartbeat, set last response to 1 second in the past * so that we will trigger a timeout if the firmware doesn't respond */ fbd->last_heartbeat_response = req_time - HZ; fbd->last_heartbeat_request = req_time; The response handler then sets: fbd->last_heartbeat_response = jiffies; for which we wait via: fbnic_fw_init_heartbeat() -> fbnic_fw_heartbeat_current() The scheme is a bit odd, but it should work in principle. Fix the ordering of operations. We have to set up the stale heartbeat before we send the message. Otherwise if the response is very fast we will override it. This triggers on QEMU if we run on the core that handles the IRQ, and results in ndo_open failing with ETIMEDOUT. The change in ordering doesn't impact releasing the ownership. Both ndo_stop and heartbeat check are under rtnl_lock. Fixes: 20d2e88cc746 ("eth: fbnic: Add initial messaging to notify FW of our presence") Reviewed-by: Alexander Duyck Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260622154753.827506-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/meta/fbnic/fbnic_fw.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c index fcd9912e7ad3b..1204c12ea0fa2 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c @@ -484,15 +484,10 @@ int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership) goto free_message; } - err = fbnic_mbx_map_tlv_msg(fbd, msg); - if (err) - goto free_message; - /* Initialize heartbeat, set last response to 1 second in the past * so that we will trigger a timeout if the firmware doesn't respond */ fbd->last_heartbeat_response = req_time - HZ; - fbd->last_heartbeat_request = req_time; /* Set prev_firmware_time to 0 to avoid triggering firmware crash @@ -500,6 +495,10 @@ int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership) */ fbd->prev_firmware_time = 0; + err = fbnic_mbx_map_tlv_msg(fbd, msg); + if (err) + goto free_message; + /* Set heartbeat detection based on if we are taking ownership */ fbd->fw_heartbeat_enabled = take_ownership; From ea43e7a231aa264722aff20aa397003d754cc2b6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 19 Jun 2026 17:01:27 +0200 Subject: [PATCH 1539/3297] thermal: testing: zone: Flush work items during cleanup [ Upstream commit fb1a5dfe86d3af1e1c3ce168cf0d8d43897e0f77 ] To prevent freed module code from being executed during the thermal testing module unload, make it add a dedicated workqueue for thermal testing work items and flush it in thermal_testing_exit(). Fixes: f6a034f2df42 ("thermal: Introduce a debugfs-based testing facility") Link: https://sashiko.dev/#/patchset/20260605185212.2491144-1-sam.moelius%40trailofbits.com Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1959388.tdWV9SEqCh@rafael.j.wysocki [ rjw: Make variable d_command static ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/thermal/testing/command.c | 36 ++++++++++++++++++++--- drivers/thermal/testing/thermal_testing.h | 8 +++++ drivers/thermal/testing/zone.c | 7 ++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c index fbf7ab9729b5a..90e06c5933278 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -86,7 +86,10 @@ #include "thermal_testing.h" -struct dentry *d_testing; +struct workqueue_struct *tt_wq __ro_after_init; + +struct dentry *d_testing __ro_after_init; +static struct dentry *d_command __ro_after_init; #define TT_COMMAND_SIZE 16 @@ -203,17 +206,42 @@ static const struct file_operations tt_command_fops = { static int __init thermal_testing_init(void) { + int error; + + tt_wq = alloc_workqueue("thermal_testing", WQ_UNBOUND, 0); + if (!tt_wq) + return -ENOMEM; + d_testing = debugfs_create_dir("thermal-testing", NULL); - if (!IS_ERR(d_testing)) - debugfs_create_file("command", 0200, d_testing, NULL, - &tt_command_fops); + if (IS_ERR(d_testing)) { + error = PTR_ERR(d_testing); + goto destroy_wq; + } + + d_command = debugfs_create_file("command", 0200, d_testing, NULL, &tt_command_fops); + if (IS_ERR(d_command)) { + error = PTR_ERR(d_command); + goto remove_d_testing; + } return 0; + +remove_d_testing: + debugfs_remove(d_testing); +destroy_wq: + destroy_workqueue(tt_wq); + return error; } module_init(thermal_testing_init); static void __exit thermal_testing_exit(void) { + /* First, prevent new commands from being entered. */ + debugfs_remove(d_command); + /* Flush commands in progress (if any). */ + flush_workqueue(tt_wq); + destroy_workqueue(tt_wq); + /* Remove the directory structure and clean up. */ debugfs_remove(d_testing); tt_zone_cleanup(); } diff --git a/drivers/thermal/testing/thermal_testing.h b/drivers/thermal/testing/thermal_testing.h index c790a32aae4ed..5880c9a63dba9 100644 --- a/drivers/thermal/testing/thermal_testing.h +++ b/drivers/thermal/testing/thermal_testing.h @@ -1,4 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include + +extern struct workqueue_struct *tt_wq; + +static inline void tt_queue_work(struct work_struct *work) +{ + queue_work(tt_wq, work); +} extern struct dentry *d_testing; diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index c12c405225bbc..aa61e34e30c85 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -13,7 +13,6 @@ #include #include #include -#include #include "thermal_testing.h" @@ -208,7 +207,7 @@ int tt_add_tz(void) INIT_WORK(&tt_work->work, tt_add_tz_work_fn); tt_work->tt_zone = no_free_ptr(tt_zone); - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } @@ -270,7 +269,7 @@ int tt_del_tz(const char *arg) INIT_WORK(&tt_work->work, tt_del_tz_work_fn); tt_work->tt_zone = tt_zone; - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } @@ -359,7 +358,7 @@ int tt_zone_add_trip(const char *arg) INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); tt_work->tt_zone = no_free_ptr(tt_zone); tt_work->tt_trip = no_free_ptr(tt_trip); - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } From 5b6165d7ec38477e8cfa41bfa464959957953822 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Tue, 16 Jun 2026 15:26:17 +0800 Subject: [PATCH 1540/3297] ACPI: processor_idle: Mark LPI enter functions as __cpuidle [ Upstream commit 956ca5d72c76504824c8eb601879da9476973e15 ] When function tracing or Kprobes is enabled, entering an ACPI Low Power Idle (LPI) state triggers the following RCU splat: RCU not on for: acpi_idle_lpi_enter+0x4/0xd8 WARNING: CPU: 8 PID: 0 at include/linux/trace_recursion.h:162 function_trace_call+0x1e8/0x228 The acpi_idle_lpi_enter() function is invoked within the cpuidle path after RCU has already been disabled for the current local CPU. Consequently, ftrace's function_trace_call() expects RCU to be actively watching before recording trace data, emitting a warning if it is not. Fix this by annotating acpi_idle_lpi_enter(), the generic __weak stub, and the RISC-V implementation of acpi_processor_ffh_lpi_enter() with __cpuidle. This moves these functions into the '.cpuidle.text' section, implicitly disabling ftrace instrumentation (notrace) along this sensitive path and preventing trace-induced RCU warnings during idle entry. Fixes: a36a7fecfe60 ("ACPI / processor_idle: Add support for Low Power Idle(LPI) states") Signed-off-by: Li RongQing Acked-by: lihuisong@huawei.com Link: https://patch.msgid.link/20260616072617.2272-1-lirongqing@baidu.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- drivers/acpi/processor_idle.c | 4 ++-- drivers/acpi/riscv/cpuidle.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 4166090db6420..2468c1d971e74 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1159,7 +1159,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) return 0; } -int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +int __weak __cpuidle acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) { return -ENODEV; } @@ -1172,7 +1172,7 @@ int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) * * Return: 0 for success or negative value for error */ -static int acpi_idle_lpi_enter(struct cpuidle_device *dev, +static int __cpuidle acpi_idle_lpi_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; diff --git a/drivers/acpi/riscv/cpuidle.c b/drivers/acpi/riscv/cpuidle.c index 624f9bbdb58c4..c76dbabff702e 100644 --- a/drivers/acpi/riscv/cpuidle.c +++ b/drivers/acpi/riscv/cpuidle.c @@ -66,7 +66,7 @@ int acpi_processor_ffh_lpi_probe(unsigned int cpu) return acpi_cpu_init_idle(cpu); } -int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +int __cpuidle acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) { u32 state = lpi->address; From d0bfd7004a87f4d2436eb8ac9a7bede6eeab783e Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Wed, 24 Jun 2026 10:15:43 +0800 Subject: [PATCH 1541/3297] smb/client: preserve errors from smb2_set_sparse() [ Upstream commit 2a4b3d2db5c6fcdba889baf7b2ae5661b0beac89 ] smb2_set_sparse() converts every FSCTL_SET_SPARSE failure to false and marks sparse support as broken for the share. Callers therefore report EOPNOTSUPP even for errors such as ENOSPC or EACCES, and later sparse operations remain disabled until the share is unmounted. Return the SMB2 ioctl error directly. Set broken_sparse_sup only for EOPNOTSUPP, which indicates that the server does not support the request. Update smb3_punch_hole() to propagate the error returned by smb2_set_sparse(). Fixes: 3d1a3745d8ca ("Add sparse file support to SMB2/SMB3 mounts") Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/smb2ops.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 9c2a6fb423fc4..f714ded6b4ffc 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2096,8 +2096,9 @@ smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, } /* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ -static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) +static int smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct inode *inode, + __u8 setsparse) { struct cifsInodeInfo *cifsi; int rc; @@ -2106,31 +2107,31 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, /* if file already sparse don't bother setting sparse again */ if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) - return true; /* already sparse */ + return 0; /* already sparse */ if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) - return true; /* already not sparse */ + return 0; /* already not sparse */ /* * Can't check for sparse support on share the usual way via the * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share * since Samba server doesn't set the flag on the share, yet * supports the set sparse FSCTL and returns sparse correctly - * in the file attributes. If we fail setting sparse though we - * mark that server does not support sparse files for this share - * to avoid repeatedly sending the unsupported fsctl to server - * if the file is repeatedly extended. + * in the file attributes. If the server returns EOPNOTSUPP, mark + * that sparse files are not supported on this share to avoid + * repeatedly sending the unsupported FSCTL. */ if (tcon->broken_sparse_sup) - return false; + return -EOPNOTSUPP; rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, FSCTL_SET_SPARSE, &setsparse, 1, CIFSMaxBufSize, NULL, NULL); if (rc) { - tcon->broken_sparse_sup = true; + if (rc == -EOPNOTSUPP) + tcon->broken_sparse_sup = true; cifs_dbg(FYI, "set sparse rc = %d\n", rc); - return false; + return rc; } if (setsparse) @@ -2138,7 +2139,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, else cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); - return true; + return 0; } static int @@ -3451,10 +3452,9 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, /* Need to make file sparse, if not already, before freeing range. */ /* Consider adding equivalent for compressed since it could also work */ - if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { - rc = -EOPNOTSUPP; + rc = smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); + if (rc) goto out; - } filemap_invalidate_lock(inode->i_mapping); /* From 5fd1f0512748d35f86ce7efa95b24dbe12d95063 Mon Sep 17 00:00:00 2001 From: Fredrik M Olsson Date: Wed, 20 May 2026 16:48:54 +0200 Subject: [PATCH 1542/3297] rtc: ds1307: Fix off-by-one issue with wday for rx8130 [ Upstream commit 6882aab3c66112b33b251be95c09c8ead3e8d580 ] The RTC represent each weekday with a individual bit set in the WDAY register, where the 0th bit represent the first day of the week and the 6th bit represents the last day of the week. For each passed day the chip performs a rotary-left-shift by one to advance the weekday by one. The tm_wday field represent weekdays by a value in the range of 0-6. The fls() function return the bit index of the last bit set. To handle when there are no bits set it will return 0, and if the 0th bit is set it will return 1, and if the 1st bit is set it will return 2, and so on. In order to make the result of the fls() function fall into the expected range of 0-6 (instead of 1-7) this patch subtracts one from the result (which matches how the value is written in ds1307_set_time()). Fixes: 204756f016726 ("rtc: ds1307: Fix wday settings for rx8130") Reviewed-by: Nobuhiro Iwamatsu Signed-off-by: Fredrik M Olsson Link: https://patch.msgid.link/20260520-ds1307-rx8901-add-v2-2-e069ea32e1db@axis.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-ds1307.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index edf81b975decb..9ba5958ca7a6e 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -311,7 +311,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) t->tm_hour = bcd2bin(tmp); /* rx8130 is bit position, not BCD */ if (ds1307->type == rx_8130) - t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f); + t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f) - 1; else t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); From a69ccea6d7eb63f3309da1086c4c0da97b56afd0 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 18:08:48 +0800 Subject: [PATCH 1543/3297] rtc: cmos: unregister HPET IRQ handler on probe failure [ Upstream commit a5bb580df018b5d1c5668f05f7979044fb19e23a ] cmos_do_probe() registers cmos_interrupt() as the HPET RTC IRQ handler before requesting the RTC IRQ and registering the RTC device. If either request_irq() or devm_rtc_register_device() fails afterwards, the error path leaves the HPET RTC IRQ handler installed. This leaves a stale handler behind and make a later hpet_register_irq_handler() fail with -EBUSY. Track whether the HPET handler was registered successfully and undo the registration on the probe error path. Also mask the HPET RTC IRQ bits to match the normal shutdown cleanup. Fixes: 9d8af78b0797 ("rtc: add HPET RTC emulation to RTC_DRV_CMOS") Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260623100848.2127281-1-haoxiang_li2024@163.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-cmos.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 0743c6acd6e2c..f8068c717fb85 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -927,6 +927,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) unsigned char rtc_control; unsigned address_space; u32 flags = 0; + bool hpet_registered = false; struct nvmem_config nvmem_cfg = { .name = "cmos_nvram", .word_size = 1, @@ -1084,6 +1085,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) " failed in rtc_init()."); goto cleanup1; } + hpet_registered = true; } else rtc_cmos_int_handler = cmos_interrupt; @@ -1133,6 +1135,10 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) if (is_valid_irq(rtc_irq)) free_irq(rtc_irq, cmos_rtc.rtc); cleanup1: + if (hpet_registered) { + hpet_mask_rtc_irq_bit(RTC_IRQMASK); + hpet_unregister_irq_handler(cmos_interrupt); + } cmos_rtc.dev = NULL; cleanup0: if (RTC_IOMAPPED) From 0c11a1da41a647b6cfc00d415516053dd7f0aa62 Mon Sep 17 00:00:00 2001 From: David Yang Date: Thu, 18 Jun 2026 22:01:55 +0800 Subject: [PATCH 1544/3297] net: dsa: realtek: fix memory leak in rtl8366rb_setup_led() [ Upstream commit 056a5087d87ead77dedbe9cf5bde53b7cd4b4651 ] led_classdev_register_ext() only reads init_data.devicename - it never stores the pointer. However, the caller allocated devicename with kasprintf() but never freed it, leaking the string memory. Fix it with a stack buffer to avoid dynamic buffers completely. Fixes: 32d617005475 ("net: dsa: realtek: add LED drivers for rtl8366rb") Signed-off-by: David Yang Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260618140200.1888707-1-mmyangfl@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/realtek/rtl8366rb-leds.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/realtek/rtl8366rb-leds.c b/drivers/net/dsa/realtek/rtl8366rb-leds.c index 509ffd3f8db5c..ba50d311cb151 100644 --- a/drivers/net/dsa/realtek/rtl8366rb-leds.c +++ b/drivers/net/dsa/realtek/rtl8366rb-leds.c @@ -89,6 +89,7 @@ static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, struct led_init_data init_data = { }; enum led_default_state state; struct rtl8366rb_led *led; + char name[64]; u32 led_group; int ret; @@ -129,10 +130,9 @@ static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, init_data.fwnode = led_fwnode; init_data.devname_mandatory = true; - init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d", - dp->ds->index, dp->index, led_group); - if (!init_data.devicename) - return -ENOMEM; + snprintf(name, sizeof(name), "Realtek-%d:0%d:%d", + dp->ds->index, dp->index, led_group); + init_data.devicename = name; ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data); if (ret) { From 9dc3cf8a35906db733f1cb4f9a552aab86f65d54 Mon Sep 17 00:00:00 2001 From: Jan Klos Date: Sat, 20 Jun 2026 03:19:53 +0200 Subject: [PATCH 1545/3297] net: phy: realtek: Clear MDIO_AN_10GBT_CTRL_ADV10G bit [ Upstream commit 510a283f4d12367a3f811f382a2c89202954bbd1 ] On RTL8127A connected to a link partner that advertises 10000baseT speed cannot be changed to anything other than 10000baseT as 10GbE is always advertised regardless of any setting. Fix this by clearing MDIO_AN_10GBT_CTRL_ADV10G bit in rtl822x_config_aneg()'s call to phy_modify_mmd_changed(). Fixes: 83d962316128 ("net: phy: realtek: add RTL8127-internal PHY") Reviewed-by: Maxime Chevallier Signed-off-by: Jan Klos Link: https://patch.msgid.link/20260620011956.37181-1-honza.klos@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/realtek/realtek_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index 7c3d277efaf07..05df1a33b0e20 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -1330,7 +1330,8 @@ static int rtl822x_config_aneg(struct phy_device *phydev) ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL_MDIO_AN_10GBT_CTRL, MDIO_AN_10GBT_CTRL_ADV2_5G | - MDIO_AN_10GBT_CTRL_ADV5G, adv); + MDIO_AN_10GBT_CTRL_ADV5G | + MDIO_AN_10GBT_CTRL_ADV10G, adv); if (ret < 0) return ret; } From e0ac054416bf4e913fe96cefefe94e3331bbe6fa Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Date: Mon, 22 Jun 2026 04:30:53 +0530 Subject: [PATCH 1546/3297] octeontx2-af: Validate NIX maximum LFs correctly [ Upstream commit 1576d12a39860418d6a68b402fda71a48f04a57c ] NIX maximum number of LFs can be set via devlink command but that can be done before assigning any LFs to a PF/VF. The condition used to check whether any LFs are assigned is incorrect. This patch fixes that condition. Fixes: dd7842878633 ("octeontx2-af: Add new devlink param to configure maximum usable NIX block LFs") Signed-off-by: Subbaraya Sundeep Link: https://patch.msgid.link/1782082853-6941-1-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../marvell/octeontx2/af/rvu_devlink.c | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c index 5852a72b22306..8475d66741ea8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c @@ -1442,7 +1442,9 @@ static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id, struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; u16 max_nix0_lf, max_nix1_lf; - struct npc_mcam *mcam; + struct rvu_block *block; + int blkaddr = 0; + int free_lfs; u64 cfg; cfg = rvu_read64(rvu, BLKADDR_NIX0, NIX_AF_CONST2); @@ -1450,14 +1452,23 @@ static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id, cfg = rvu_read64(rvu, BLKADDR_NIX1, NIX_AF_CONST2); max_nix1_lf = cfg & 0xFFF; - /* Do not allow user to modify maximum NIX LFs while mcam entries - * have already been assigned. + /* Do not allow user to modify maximum NIX LFs while NIX LFs + * have already been assigned. Note that modifying NIX LFs count + * can be done only before any LF attach requests from PFs and VFs + * and not later or concurrently. */ - mcam = &rvu->hw->mcam; - if (mcam->bmap_fcnt < mcam->bmap_entries) { - NL_SET_ERR_MSG_MOD(extack, - "mcam entries have already been assigned, can't resize"); - return -EPERM; + blkaddr = rvu_get_next_nix_blkaddr(rvu, blkaddr); + while (blkaddr) { + block = &rvu->hw->block[blkaddr]; + + free_lfs = rvu_rsrc_free_count(&block->lf); + if (free_lfs != block->lf.max) { + NL_SET_ERR_MSG_MOD(extack, + "NIX LFs already assigned, can't resize"); + return -EPERM; + } + + blkaddr = rvu_get_next_nix_blkaddr(rvu, blkaddr); } if (max_nix0_lf && val.vu16 > max_nix0_lf) { From b84dd48f9da1eb132bdc06a944423cd5a1641ef1 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Mon, 22 Jun 2026 15:43:50 +0800 Subject: [PATCH 1547/3297] net: mvneta: re-enable percpu interrupt on resume [ Upstream commit fd398d6480987e4c84fff0aaab6b9d6642a93343 ] On Marvell MPIC platforms (Armada 370/XP/38x), mvneta uses a percpu IRQ disable/enable scheme for NAPI: the ISR (mvneta_percpu_isr) calls disable_percpu_irq() to mask the MPIC per-CPU interrupt and schedules NAPI poll, which calls enable_percpu_irq() on completion to unmask. If suspend occurs while NAPI poll is pending (between disable_percpu_irq in the ISR and enable_percpu_irq in poll completion), the interrupt is never re-enabled: 1. mvneta_percpu_isr: disable_percpu_irq() + napi_schedule() => MPIC masked, percpu_enabled cpumask bit cleared 2. NAPI poll does not complete before suspend proceeds (on PREEMPT_RT this is highly likely since softirqs run in ksoftirqd which gets frozen; on non-RT it can happen when softirq processing is deferred to ksoftirqd) 3. mvneta_stop_dev => napi_disable(): cancels the pending poll without executing the completion path 4. suspend_device_irqs => IRQCHIP_MASK_ON_SUSPEND: masks MPIC (already masked, but records IRQS_SUSPENDED) 5. Resume: mpic_resume checks irq_percpu_is_enabled() => false (bit was cleared in step 1) => skips unmask 6. mvneta_start_dev only restores device-level INTR_NEW_MASK, does not touch the MPIC per-CPU mask Result: MPIC per-CPU interrupt stays masked permanently. The NIC generates interrupts (INTR_NEW_CAUSE != 0) but the CPU never receives them, causing complete loss of network connectivity. Fix by calling on_each_cpu(mvneta_percpu_enable) in the resume path to unconditionally unmask the MPIC per-CPU interrupt regardless of pre-suspend state. Fixes: 12bb03b436da ("net: mvneta: Handle per-cpu interrupts") Signed-off-by: Yun Zhou Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260622074350.1666290-1-yun.zhou@windriver.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/mvneta.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 89ccb8eb82c7d..2b66e238d61bb 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5905,6 +5905,9 @@ static int mvneta_resume(struct device *device) rtnl_unlock(); mvneta_set_rx_mode(dev); + if (!pp->neta_armada3700) + on_each_cpu(mvneta_percpu_enable, pp, true); + return 0; } #endif From b15a3cc68e2450aa0edd94a74a7bdd45fcc17dd9 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 23 Jun 2026 10:57:59 +0800 Subject: [PATCH 1548/3297] net: sungem: fix probe error cleanup [ Upstream commit 36dea2f639249460d13f6ca66b2a9064187cd34d ] gem_init_one() calls gem_remove_one() when register_netdev() fails. gem_remove_one() unregisters and frees resources owned by the net_device, including the DMA block, MMIO mapping, PCI regions, and the net_device itself. gem_init_one() then falls through to its own cleanup labels and frees the same resources again. Keep the register_netdev() error path in gem_init_one(): clear drvdata so PM/remove paths do not see a half-registered device, remove the NAPI instance added during probe, and let the existing cleanup labels release the resources once. The issue was found by a local static-analysis checker for probe error paths. The reported path was manually inspected before sending this fix. Compile-tested with CONFIG_SUNGEM=y. Runtime testing was not performed because no sungem hardware is available. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Ruoyu Wang Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260623025759.3468566-1-ruoyuw560@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/sun/sungem.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 8e69d917d827c..26974ee713525 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -2986,10 +2986,10 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->max_mtu = GEM_MAX_MTU; /* Register with kernel */ - if (register_netdev(dev)) { + err = register_netdev(dev); + if (err) { pr_err("Cannot register net device, aborting\n"); - err = -ENOMEM; - goto err_out_free_consistent; + goto err_out_clear_drvdata; } /* Undo the get_cell with appropriate locking (we could use @@ -3003,8 +3003,13 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->dev_addr); return 0; +err_out_clear_drvdata: + pci_set_drvdata(pdev, NULL); + netif_napi_del(&gp->napi); + err_out_free_consistent: - gem_remove_one(pdev); + dma_free_coherent(&pdev->dev, sizeof(struct gem_init_block), + gp->init_block, gp->gblock_dvma); err_out_iounmap: gem_put_cell(gp); iounmap(gp->regs); From 2f3c0895fb20ab0901d1e879e897aa7a4ce7cf5a Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Tue, 23 Jun 2026 12:11:42 +0530 Subject: [PATCH 1549/3297] net: ethernet: sunplus: spl2sw: fix phy_node refcount leak in remove [ Upstream commit a9e29dcd8a84081177f693439d663ca9e216c9fa ] mac->phy_node is acquired via of_parse_phandle() in spl2sw_probe() and stored in the mac private data, transferring ownership of the device_node reference to mac. On driver removal, spl2sw_phy_remove() disconnects the PHY but never drops that reference, so each probe-then-remove cycle leaks one of_node refcount per port permanently. Drop the reference after phy_disconnect(). While at it, remove the redundant inner "if (ndev)" check; comm->ndev[i] was just verified non-NULL on the line above. Compile-tested only; no SP7021 hardware available. Fixes: fd3040b9394c ("net: ethernet: Add driver for Sunplus SP7021") Signed-off-by: Shitalkumar Gandhi Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/f3bdd4c91f3e2269b4e256075f9dc70808b1b8e9.1782195965.git.shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/sunplus/spl2sw_phy.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sunplus/spl2sw_phy.c b/drivers/net/ethernet/sunplus/spl2sw_phy.c index 6f899e48f51df..a4889c52e00e4 100644 --- a/drivers/net/ethernet/sunplus/spl2sw_phy.c +++ b/drivers/net/ethernet/sunplus/spl2sw_phy.c @@ -79,12 +79,14 @@ int spl2sw_phy_connect(struct spl2sw_common *comm) void spl2sw_phy_remove(struct spl2sw_common *comm) { struct net_device *ndev; + struct spl2sw_mac *mac; int i; for (i = 0; i < MAX_NETDEV_NUM; i++) if (comm->ndev[i]) { ndev = comm->ndev[i]; - if (ndev) - phy_disconnect(ndev->phydev); + mac = netdev_priv(ndev); + phy_disconnect(ndev->phydev); + of_node_put(mac->phy_node); } } From 0a8a729481c8e240f00e3ff19402e1bc40b847d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E8=87=B4=E9=82=A6=20=28XIE=20Zhibang=29?= Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 1550/3297] LoongArch: Move struct kimage forward declaration before use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d4e58d2c21d94282d512979dfa7e045c5034b0be ] arch_kimage_file_post_load_cleanup() and load_other_segments(), both inside the CONFIG_KEXEC_FILE block, take a struct kimage pointer before the forward declaration appears. Move the forward declaration above so it precedes its first use instead of relying on a transitive include. Fixes: d162feec6b6e ("LoongArch: Add preparatory infrastructure for kexec_file") Signed-off-by: 谢致邦 (XIE Zhibang) Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/include/asm/kexec.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/kexec.h b/arch/loongarch/include/asm/kexec.h index 209fa43222e1c..6be136e9f0a06 100644 --- a/arch/loongarch/include/asm/kexec.h +++ b/arch/loongarch/include/asm/kexec.h @@ -41,6 +41,8 @@ struct kimage_arch { unsigned long systable_ptr; }; +struct kimage; + #ifdef CONFIG_KEXEC_FILE extern const struct kexec_file_ops kexec_efi_ops; extern const struct kexec_file_ops kexec_elf_ops; @@ -59,7 +61,6 @@ typedef void (*do_kexec_t)(unsigned long efi_boot, unsigned long start_addr, unsigned long first_ind_entry); -struct kimage; extern const unsigned char relocate_new_kernel[]; extern const size_t relocate_new_kernel_size; extern void kexec_reboot(void); From ed295077a2214e9a9331e3d1577127def37cfa0a Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 25 Jun 2026 13:03:53 +0800 Subject: [PATCH 1551/3297] LoongArch: BPF: Fix outdated tail call comments [ Upstream commit 25d9127bb0e27275d55a5b3d0fd30b04bafffd5a ] The current LoongArch BPF JIT implementation hardcodes the number of prologue instructions skipped during a tail call as a magic number '7' in the jirl instruction. However, the accompanying comment explaining this offset is completely outdated. It inaccurately states that only a single TCC initialization instruction is bypassed, but in reality, multiple setup slots are skipped, so fix these outdated comments in __build_epilogue(). While at it, refine the comments in build_prologue() to describe the skipped setup slots (RA saving, fentry nops, and the TCC register slot) using proper dynamic tracing context. Also, remove the magic number '7' by introducing descriptive macros to formally define the prologue layout and make the tail call jump offset self-documenting. Fixes: 61319d15a560 ("LoongArch: BPF: Adjust the jump offset of tail calls") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/net/bpf_jit.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index a000620ca99da..5bfed3c4e0839 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -9,7 +9,13 @@ #define LOONGARCH_MAX_REG_ARGS 8 +#define LOONGARCH_SAVE_RA_NINSNS 1 #define LOONGARCH_LONG_JUMP_NINSNS 5 +#define LOONGARCH_TCC_SLOT_NINSNS 1 + +#define LOONGARCH_PROLOGUE_SKIP_INSNS \ + (LOONGARCH_SAVE_RA_NINSNS + LOONGARCH_LONG_JUMP_NINSNS + LOONGARCH_TCC_SLOT_NINSNS) + #define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) #define LOONGARCH_FENTRY_NINSNS 2 @@ -139,8 +145,13 @@ static void build_prologue(struct jit_ctx *ctx) stack_adjust = round_up(stack_adjust, 16); stack_adjust += bpf_stack_adjust; + /* + * Save the original return address to a temporary register to prevent + * it from being overwritten, then reserve space for the long jump and + * fentry trampoline slot for dynamically patching by ftrace at runtime. + * These instructions are bypassed during a tail call invocation. + */ move_reg(ctx, LOONGARCH_GPR_T0, LOONGARCH_GPR_RA); - /* Reserve space for the move_imm + jirl instruction */ for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) emit_insn(ctx, nop); @@ -236,10 +247,11 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_RA, 0); } else { /* - * Call the next bpf prog and skip the first instruction - * of TCC initialization. + * Tail call to the next BPF program, passing offset in number + * of instructions to jirl to bypass the initial setup slots. */ - emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 7); + emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, + LOONGARCH_GPR_T3, LOONGARCH_PROLOGUE_SKIP_INSNS); } } From 7a7c7263bbbc4653ad669301d823da7a686d1bd5 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 25 Jun 2026 13:03:53 +0800 Subject: [PATCH 1552/3297] LoongArch: BPF: Fix off-by-one error in tail call [ Upstream commit 0379d10f09bc21ba739636796669dfb4936172a3 ] The current code updates the tail call counter (TCC) using a pre-increment approach, it stores the incremented value back to memory before performing any boundary or target validation checks. This causes two major issues: 1. When a tail call fails because the target program is NULL, the TCC is incorrectly incremented and saved in memory anyway. 2. This dummy increment implicitly consumes one slot of the allowed tail call budget. As a result, the subsequent loop reaches the maximum limit prematurely, leading to a test failure where the actual loop count is 32 instead of the expected 33. Fix this by deferring the counter update. Change the branch condition to BPF_JSGE (greater or equal) so that we check the boundary first. The TCC is only incremented and stored back to memory after the boundary check and the NULL-target check both pass. Before: $ sudo ./test_progs -t tailcalls/tailcall_3 ... test_tailcall_count:FAIL:tailcall count unexpected tailcall count: actual 32 != expected 33 ... #465/3 tailcalls/tailcall_3:FAIL #465 tailcalls:FAIL After: $ sudo ./test_progs -t tailcalls/tailcall_3 #465/3 tailcalls/tailcall_3:OK #465 tailcalls:OK Summary: 1/1 PASSED, 0 SKIPPED, 0 FAILED Fixes: c0fcc955ff82 ("LoongArch: BPF: Fix the tailcall hierarchy") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/net/bpf_jit.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 5bfed3c4e0839..bb792da9088f6 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -307,12 +307,12 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) */ emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off); emit_insn(ctx, ldd, t3, REG_TCC, 0); - emit_insn(ctx, addid, t3, t3, 1); - emit_insn(ctx, std, t3, REG_TCC, 0); emit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); - if (emit_tailcall_jmp(ctx, BPF_JSGT, t3, t2, jmp_offset) < 0) + if (emit_tailcall_jmp(ctx, BPF_JSGE, t3, t2, jmp_offset) < 0) goto toofar; + emit_insn(ctx, addid, t3, t3, 1); + /* * prog = array->ptrs[index]; * if (!prog) @@ -325,6 +325,8 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0) goto toofar; + emit_insn(ctx, std, t3, REG_TCC, 0); + /* goto *(prog->bpf_func + 4); */ off = offsetof(struct bpf_prog, bpf_func); emit_insn(ctx, ldd, t3, t2, off); From c1e7286d05318403bfb444cca85728009deed5a1 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 25 Jun 2026 18:24:16 +0800 Subject: [PATCH 1553/3297] ASoC: fsl_asrc_dma: fix eDMA maxburst misalignment with channel count [ Upstream commit cf6f56990ea21172e085f0588e5bbf2089ce8f58 ] The back-end consumes data in units of the number of channels. When the maxburst value is not evenly divisible by the channel count, the DMA transfer length does not align with the FIFO frame boundary, causing wrong data to be copied and audible noise at the end of the stream. This is specific to eDMA: eDMA only responds to DMA requests from the back-end, whereas SDMA handles requests from both the front-end and the back-end and is not affected. For eDMA, when the back-end maxburst is not evenly divisible by the channel count, align it to the nearest valid boundary: - If maxburst >= channel count, override to the channel count so each transfer corresponds to exactly one audio frame. - If maxburst < channel count, override to 1 to avoid partial-frame transfers. Retain the original maxburst for SDMA or when it already aligns with the channel count. Fixes: c05f10f28ef6 ("ASoC: fsl_asrc: Add support for imx8qm & imx8qxp") Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260625102416.424911-1-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/fsl/fsl_asrc_dma.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 1bba48318e2dd..c96369da6aec9 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -288,6 +288,26 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, config_be.dst_addr_width = buswidth; config_be.dst_maxburst = dma_params_be->maxburst; + /* + * For eDMA, the back-end may report a maxburst size that is not evenly + * divisible by the channel count. This causes the DMA transfer length + * to misalign with the FIFO boundary, resulting in wrong data and + * audible noise. Align maxburst to the nearest valid boundary: + * - If maxburst >= channel count, override to the channel count so + * each transfer equals exactly one audio frame. + * - If maxburst < channel count, override to 1 to avoid partial-frame + * transfers. + */ + if (asrc->use_edma && (dma_params_be->maxburst % params_channels(params))) { + if (dma_params_be->maxburst >= params_channels(params)) { + config_be.src_maxburst = params_channels(params); + config_be.dst_maxburst = params_channels(params); + } else { + config_be.src_maxburst = 1; + config_be.dst_maxburst = 1; + } + } + memset(&audio_config, 0, sizeof(audio_config)); config_be.peripheral_config = &audio_config; config_be.peripheral_size = sizeof(audio_config); From 9075efb9b2c1d9d7a8285c937b64aa93ca0c41b7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 25 Jun 2026 06:59:36 +0000 Subject: [PATCH 1554/3297] net: udp_tunnel: prevent double queueing in udp_tunnel_nic_device_sync [ Upstream commit ecf69d4b43370c587e48d4d70289dbdb7e039d4d ] Yue Sun reported a use-after-free and debugobjects warning in udp_tunnel_nic_device_sync_work() during concurrent device operations. The workqueue core clears the internal pending bit before invoking the worker. At that point, a concurrent thread can queue the work again. When the already running worker eventually clears the work_pending flag to 0, it mistakenly clears the flag for the newly queued instance. udp_tunnel_nic_unregister() then observes work_pending as 0 and frees the structure while the second work item is still active in the queue, leading to UAF. Fix this by returning early in udp_tunnel_nic_device_sync() if work_pending is already set, preventing redundant work queueing. Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure") Reported-by: Yue Sun Suggested-by: Jakub Kicinski Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260625065938.654652-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/udp_tunnel_nic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c index 944b3cf25468e..b13e8f7092f46 100644 --- a/net/ipv4/udp_tunnel_nic.c +++ b/net/ipv4/udp_tunnel_nic.c @@ -301,7 +301,7 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) static void udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) { - if (!utn->need_sync) + if (!utn->need_sync || utn->work_pending) return; queue_work(udp_tunnel_nic_workqueue, &utn->work); From 6727f580cf4622819fadaa94ed619065d0965253 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 24 Jun 2026 10:02:50 -0500 Subject: [PATCH 1555/3297] dt-bindings: net: renesas,ether: Drop example "ethernet-phy-ieee802.3-c22" fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 14eb1d2c03b38ce3427f299967f7a4d97ebff4c2 ] Fix the Micrel PHY in the example which shouldn't have the fallback "ethernet-phy-ieee802.3-c22" compatible: Documentation/devicetree/bindings/net/renesas,ether.example.dtb: ethernet-phy@1 \ (ethernet-phy-id0022.1537): compatible: ['ethernet-phy-id0022.1537', 'ethernet-phy-ieee802.3-c22'] is too long from schema $id: http://devicetree.org/schemas/net/micrel.yaml Signed-off-by: Rob Herring (Arm) Reviewed-by: Andrew Lunn Acked-by: Conor Dooley Acked-by: Niklas Söderlund Fixes: 37a2fce09001 ("dt-bindings: sh_eth convert bindings to json-schema") Link: https://patch.msgid.link/20260624150250.131966-2-robh@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/devicetree/bindings/net/renesas,ether.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/net/renesas,ether.yaml b/Documentation/devicetree/bindings/net/renesas,ether.yaml index f0a52f47f95a0..dd7187f12a675 100644 --- a/Documentation/devicetree/bindings/net/renesas,ether.yaml +++ b/Documentation/devicetree/bindings/net/renesas,ether.yaml @@ -121,8 +121,7 @@ examples: #size-cells = <0>; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupt-parent = <&irqc0>; interrupts = <0 IRQ_TYPE_LEVEL_LOW>; From de051b146022cbaf23b960d1fd771d93507c8f52 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Wed, 24 Jun 2026 06:44:16 -0700 Subject: [PATCH 1556/3297] selftests: tls: size splice_short pipe by page size [ Upstream commit 3e52f56875c6fafee619b5c2b4ded25f2efbd2ec ] splice_short grows its pipe with (MAX_FRAGS + 1) * 0x1000 so it can queue one short vmsplice() buffer for each fragment before draining the pipe. That assumes 4K pipe buffers. On 64K-page kernels the request is rounded to 262144 bytes, which provides only four pipe buffers. The fifth one-byte vmsplice() blocks in pipe_wait_writable and the test times out before it reaches the TLS path. Request enough bytes for the same number of pipe buffers using the runtime page size, and assert that the kernel granted at least that much. If an unprivileged run cannot raise the pipe above the system pipe-max-size limit, skip the test because it cannot exercise the intended path. Fixes: 3667e9b442b9 ("selftests: tls: add test for short splice due to full skmsg") Signed-off-by: Nirmoy Das Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260624134416.3235403-1-nirmoyd@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/tls.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 5c6d8215021c9..6dc9b5da738c8 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -954,6 +954,8 @@ TEST_F(tls, splice_short) char sendbuf[0x100]; char sendchar = 'S'; int pipefds[2]; + int pipe_sz; + int ret; int i; sendchar_iov.iov_base = &sendchar; @@ -962,7 +964,11 @@ TEST_F(tls, splice_short) memset(sendbuf, 's', sizeof(sendbuf)); ASSERT_GE(pipe2(pipefds, O_NONBLOCK), 0); - ASSERT_GE(fcntl(pipefds[0], F_SETPIPE_SZ, (MAX_FRAGS + 1) * 0x1000), 0); + pipe_sz = (MAX_FRAGS + 1) * getpagesize(); + ret = fcntl(pipefds[0], F_SETPIPE_SZ, pipe_sz); + if (ret < 0 && errno == EPERM) + SKIP(return, "insufficient pipe capacity"); + ASSERT_GE(ret, pipe_sz); for (i = 0; i < MAX_FRAGS; i++) ASSERT_GE(vmsplice(pipefds[1], &sendchar_iov, 1, 0), 0); From 43a6c6fb6ec503c4a68d83ac7ca6513120120a73 Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:16 +0800 Subject: [PATCH 1557/3297] net: hns3: unify copper port ksettings configuration path [ Upstream commit d77e98f8b2b382b06be7f17e482480dd8c4c5046 ] Refactor hns3_set_link_ksettings() and hclge_set_phy_link_ksettings() to unify the configuration path for copper ports. Previously, netdevs with a native kernel phy attached bypassed the main MAC parameter caching logic and returned early via phy_ethtool_ksettings_set(). This prevented the driver from updating hdev->hw.mac.req_xxx variables for kernel PHY setups, leaving them out-of-sync during reset recovery. Clean this up by routing all copper port configurations through ops->set_phy_link_ksettings(), and perform driver-level or kernel-level PHY arbitration inside hclge_set_phy_link_ksettings() via hnae3_dev_phy_imp_supported(). This ensures that the user's intended link profiles (req_speed, req_duplex, req_autoneg) are uniformly recorded across all copper and fiber deployment topologies, laying the groundwork for stable reset recovery. For copper ports where neither IMP firmware nor a kernel PHY is available (e.g. PHY_INEXISTENT), hclge_set_phy_link_ksettings() returns -ENODEV. In hns3_set_link_ksettings(), this is caught so the configuration falls through to the existing MAC-level path (check_ksettings_param -> cfg_mac_speed_dup_h), preserving compatibility with PHY-less copper deployments. Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-2-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Stable-dep-of: c711f6d1cee9 ("net: hns3: fix permanent link down deadlock after reset") Signed-off-by: Sasha Levin --- .../ethernet/hisilicon/hns3/hns3_ethtool.c | 31 +++++++++---------- .../hisilicon/hns3/hns3pf/hclge_main.c | 28 +++++++++++++++-- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index a5eefa28454c0..483e87eabd139 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -811,12 +811,11 @@ static int hns3_get_link_ksettings(struct net_device *netdev, } static int hns3_check_ksettings_param(const struct net_device *netdev, - const struct ethtool_link_ksettings *cmd) + const struct ethtool_link_ksettings *cmd, + u8 media_type) { struct hnae3_handle *handle = hns3_get_handle(netdev); const struct hnae3_ae_ops *ops = hns3_get_ops(handle); - u8 module_type = HNAE3_MODULE_TYPE_UNKNOWN; - u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; u32 lane_num; u8 autoneg; u32 speed; @@ -836,9 +835,6 @@ static int hns3_check_ksettings_param(const struct net_device *netdev, return 0; } - if (ops->get_media_type) - ops->get_media_type(handle, &media_type, &module_type); - if (cmd->base.duplex == DUPLEX_HALF && media_type != HNAE3_MEDIA_TYPE_COPPER) { netdev_err(netdev, @@ -863,6 +859,8 @@ static int hns3_set_link_ksettings(struct net_device *netdev, struct hnae3_handle *handle = hns3_get_handle(netdev); struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(handle); const struct hnae3_ae_ops *ops = hns3_get_ops(handle); + u8 module_type = HNAE3_MODULE_TYPE_UNKNOWN; + u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; int ret; /* Chip don't support this mode. */ @@ -878,22 +876,23 @@ static int hns3_set_link_ksettings(struct net_device *netdev, cmd->base.autoneg, cmd->base.speed, cmd->base.duplex, cmd->lanes); - /* Only support ksettings_set for netdev with phy attached for now */ - if (netdev->phydev) { - if (cmd->base.speed == SPEED_1000 && - cmd->base.autoneg == AUTONEG_DISABLE) - return -EINVAL; + if (!ops->get_media_type) + return -EOPNOTSUPP; + ops->get_media_type(handle, &media_type, &module_type); - return phy_ethtool_ksettings_set(netdev->phydev, cmd); - } else if (test_bit(HNAE3_DEV_SUPPORT_PHY_IMP_B, ae_dev->caps) && - ops->set_phy_link_ksettings) { - return ops->set_phy_link_ksettings(handle, cmd); + if (media_type == HNAE3_MEDIA_TYPE_COPPER) { + if (!ops->set_phy_link_ksettings) + return -EOPNOTSUPP; + ret = ops->set_phy_link_ksettings(handle, cmd); + if (ret != -ENODEV) + return ret; + /* PHY_INEXISTENT, use MAC-level configuration */ } if (ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2) return -EOPNOTSUPP; - ret = hns3_check_ksettings_param(netdev, cmd); + ret = hns3_check_ksettings_param(netdev, cmd, media_type); if (ret) return ret; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 54d0a9ba7879b..39687e308fe00 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -3358,8 +3358,8 @@ static int hclge_get_phy_link_ksettings(struct hnae3_handle *handle, } static int -hclge_set_phy_link_ksettings(struct hnae3_handle *handle, - const struct ethtool_link_ksettings *cmd) +hclge_ethtool_ksettings_set(struct hnae3_handle *handle, + const struct ethtool_link_ksettings *cmd) { struct hclge_desc desc[HCLGE_PHY_LINK_SETTING_BD_NUM]; struct hclge_vport *vport = hclge_get_vport(handle); @@ -3400,10 +3400,32 @@ hclge_set_phy_link_ksettings(struct hnae3_handle *handle, return ret; } + linkmode_copy(hdev->hw.mac.advertising, cmd->link_modes.advertising); + return 0; +} + +static int +hclge_set_phy_link_ksettings(struct hnae3_handle *handle, + const struct ethtool_link_ksettings *cmd) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int ret = -ENODEV; + + if (hnae3_dev_phy_imp_supported(hdev)) { + ret = hclge_ethtool_ksettings_set(handle, cmd); + } else if (handle->netdev->phydev) { + if (cmd->base.speed == SPEED_1000 && + cmd->base.autoneg == AUTONEG_DISABLE) + return -EINVAL; + ret = phy_ethtool_ksettings_set(handle->netdev->phydev, cmd); + } + if (ret) + return ret; + hdev->hw.mac.req_autoneg = cmd->base.autoneg; hdev->hw.mac.req_speed = cmd->base.speed; hdev->hw.mac.req_duplex = cmd->base.duplex; - linkmode_copy(hdev->hw.mac.advertising, cmd->link_modes.advertising); return 0; } From 92d05883ef3376ca9f86533b59b7522e5a90162c Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:17 +0800 Subject: [PATCH 1558/3297] net: hns3: refactor MAC autoneg and speed configuration [ Upstream commit c01f6e6bdc1ccd21b2d07d23f50b82437b8cbf88 ] Extract the MAC autoneg and speed/duplex/lane configuration logic out of hclge_mac_init() and encapsulate it into a new dedicated helper function hclge_set_autoneg_speed_dup(). In the init path (hclge_init_ae_dev), this helper is now called after hclge_update_port_info() so that firmware-reported autoneg values are already populated before applying the link configuration. Introduce a separate req_lane_num field in struct hclge_mac to isolate the user-requested lane count from mac.lane_num, which firmware may overwrite via hclge_get_sfp_info() with stale values from a prior link lifecycle (e.g., lane_num=4 from 100G). During probe, req_lane_num is initialized to 0, which instructs firmware to auto-select the correct lane count for the current speed, rather than reusing the firmware- reported mac.lane_num that may be inconsistent with the target speed. This prevents probe failures from mismatched (speed, lane_num) pairs. In the reset path (hclge_reset_ae_dev), it runs immediately after hclge_mac_init(), using the previously cached req_* values to restore the link without re-querying firmware. Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-3-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Stable-dep-of: c711f6d1cee9 ("net: hns3: fix permanent link down deadlock after reset") Signed-off-by: Sasha Levin --- .../hisilicon/hns3/hns3pf/hclge_main.c | 55 ++++++++++++++----- .../hisilicon/hns3/hns3pf/hclge_main.h | 1 + 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 39687e308fe00..521d706c63bef 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1577,6 +1577,11 @@ static int hclge_configure(struct hclge_dev *hdev) hdev->hw.mac.req_autoneg = AUTONEG_ENABLE; hdev->hw.mac.req_duplex = DUPLEX_FULL; + /* When lane_num is 0, the firmware will automatically + * select the appropriate lane_num based on the speed. + */ + hdev->hw.mac.req_lane_num = 0; + hclge_parse_link_mode(hdev, cfg.speed_ability); hdev->hw.mac.max_speed = hclge_get_max_speed(cfg.speed_ability); @@ -2652,6 +2657,7 @@ static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed, if (ret) return ret; + hdev->hw.mac.req_lane_num = lane_num; hdev->hw.mac.req_speed = (u32)speed; hdev->hw.mac.req_duplex = duplex; @@ -2957,20 +2963,6 @@ static int hclge_mac_init(struct hclge_dev *hdev) if (!test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) hdev->hw.mac.duplex = HCLGE_MAC_FULL; - if (hdev->hw.mac.support_autoneg) { - ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg); - if (ret) - return ret; - } - - if (!hdev->hw.mac.autoneg) { - ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.req_speed, - hdev->hw.mac.req_duplex, - hdev->hw.mac.lane_num); - if (ret) - return ret; - } - mac->link = 0; if (mac->user_fec_mode & BIT(HNAE3_FEC_USER_DEF)) { @@ -11748,6 +11740,27 @@ static int hclge_set_wol(struct hnae3_handle *handle, return ret; } +static int hclge_set_autoneg_speed_dup(struct hclge_dev *hdev) +{ + int ret; + + if (hdev->hw.mac.support_autoneg) { + ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg); + if (ret) + return ret; + } + + if (!hdev->hw.mac.autoneg) { + ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.req_speed, + hdev->hw.mac.req_duplex, + hdev->hw.mac.req_lane_num); + if (ret) + return ret; + } + + return 0; +} + static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) { struct pci_dev *pdev = ae_dev->pdev; @@ -11909,6 +11922,13 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_ptp_uninit; + ret = hclge_set_autoneg_speed_dup(hdev); + if (ret) { + dev_err(&pdev->dev, + "failed to set autoneg speed duplex, ret = %d\n", ret); + goto err_ptp_uninit; + } + INIT_KFIFO(hdev->mac_tnl_log); hclge_dcb_ops_set(hdev); @@ -12239,6 +12259,13 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev) return ret; } + ret = hclge_set_autoneg_speed_dup(hdev); + if (ret) { + dev_err(&pdev->dev, + "failed to set autoneg speed duplex, ret = %d\n", ret); + return ret; + } + ret = hclge_tp_port_init(hdev); if (ret) { dev_err(&pdev->dev, "failed to init tp port, ret = %d\n", diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 032b472d23685..4ca6458625a9b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -287,6 +287,7 @@ struct hclge_mac { u8 support_autoneg; u8 speed_type; /* 0: sfp speed, 1: active speed */ u8 lane_num; + u8 req_lane_num; u32 speed; u32 req_speed; u32 max_speed; From 2d149a20275ac16ea8c579fc251eef323f7c3e76 Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:18 +0800 Subject: [PATCH 1559/3297] net: hns3: fix permanent link down deadlock after reset [ Upstream commit c711f6d1cee955e04d1cd1f76cd8abd024b27a72 ] Fix a critical race condition deadlock where the network interface remains permanently Link Down after a hardware reset under specific ethtool sequences. This issue exclusively manifests in firmware-controlled PHY topologies where the driver relies on the IMP firmware to arbitrate link parameters. Standard devices driven by the kernel's native PHY_LIB are unaffected. The deadlock occurs via the following path: 1. User disables autoneg and forces an unmatched speed, forcing link down: `ethtool -s ethx autoneg off speed 10 duplex full` 2. User re-enables autoneg: `ethtool -s ethx autoneg on`. The netdev stack passes cmd->base.speed as SPEED_UNKNOWN (0xffffffff). 3. Driver saves req_autoneg=1, but before the interface can link up, a hardware reset is triggered. 4. During reset recovery, MAC init reads the un-synchronized runtime state mac.autoneg (which is still 0/OFF), misinterprets it as forced mode, and pushes the cached SPEED_UNKNOWN into the hardware registers, causing the MAC firmware state machine to freeze. Meanwhile, PHY init reads req_autoneg=1 and enables PHY autoneg. Since the MAC is frozen with 0xffffffff and PHY is running autoneg, they mismatch permanently. Fix this by: 1. Intercepting SPEED_UNKNOWN/DUPLEX_UNKNOWN in hclge_set_phy_link_ksettings() and hclge_cfg_mac_speed_dup_h() to prevent it from corrupting the driver's cached valid configuration. 2. Save req_autoneg in hclge_set_autoneg(). 3. Aligning the state judgment in hclge_set_autoneg_speed_dup() to use req_autoneg instead of the un-synchronized runtime mac.autoneg, ensuring both MAC and PHY consistently enter the autoneg branch to eliminate configuration discrepancies during reset recovery. Fixes: 05eb60e9648c ("net: hns3: using user configure after hardware reset") Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-4-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../hisilicon/hns3/hns3pf/hclge_main.c | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 521d706c63bef..87322af852cdb 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2658,8 +2658,10 @@ static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed, return ret; hdev->hw.mac.req_lane_num = lane_num; - hdev->hw.mac.req_speed = (u32)speed; - hdev->hw.mac.req_duplex = duplex; + if (speed != SPEED_UNKNOWN) + hdev->hw.mac.req_speed = (u32)speed; + if (duplex != DUPLEX_UNKNOWN) + hdev->hw.mac.req_duplex = duplex; return 0; } @@ -2690,6 +2692,7 @@ static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable) { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; + int ret; if (!hdev->hw.mac.support_autoneg) { if (enable) { @@ -2701,7 +2704,10 @@ static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable) } } - return hclge_set_autoneg_en(hdev, enable); + ret = hclge_set_autoneg_en(hdev, enable); + if (!ret) + hdev->hw.mac.req_autoneg = enable; + return ret; } static int hclge_get_autoneg(struct hnae3_handle *handle) @@ -3416,8 +3422,10 @@ hclge_set_phy_link_ksettings(struct hnae3_handle *handle, return ret; hdev->hw.mac.req_autoneg = cmd->base.autoneg; - hdev->hw.mac.req_speed = cmd->base.speed; - hdev->hw.mac.req_duplex = cmd->base.duplex; + if (cmd->base.speed != SPEED_UNKNOWN) + hdev->hw.mac.req_speed = cmd->base.speed; + if (cmd->base.duplex != DUPLEX_UNKNOWN) + hdev->hw.mac.req_duplex = cmd->base.duplex; return 0; } @@ -11745,12 +11753,12 @@ static int hclge_set_autoneg_speed_dup(struct hclge_dev *hdev) int ret; if (hdev->hw.mac.support_autoneg) { - ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg); + ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.req_autoneg); if (ret) return ret; } - if (!hdev->hw.mac.autoneg) { + if (!hdev->hw.mac.req_autoneg) { ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.req_speed, hdev->hw.mac.req_duplex, hdev->hw.mac.req_lane_num); From d990a01b853e5b816d5e344f5e6813b2b83614a2 Mon Sep 17 00:00:00 2001 From: Shuaisong Yang Date: Wed, 24 Jun 2026 22:13:19 +0800 Subject: [PATCH 1560/3297] net: hns3: differentiate autoneg default values between copper and fiber [ Upstream commit d9d349c4e8a0acd73bac8baa3605443c0df5eb26 ] Fix a link loss issue during driver initialization on optical ports connected to forced-mode (non-autoneg) remote switches. Previously, during driver probe or initialization, hclge_configure() blindly hardcoded hdev->hw.mac.req_autoneg to AUTONEG_ENABLE for all media types. While this is necessary for copper (BASE-T) ports to establish a link, many high-speed optical (fiber) ports in data centers are connected to switches running in forced mode (fixed speed, autoneg disabled). Forcing autoneg on these optical ports during initialization causes a permanent link failure since the remote end refuses to respond to autoneg pulses. Fix this by implementing media-type differentiated initialization in hclge_init_ae_dev(). Copper ports continue to default to AUTONEG_ENABLE, while optical ports strictly inherit the preset autoneg status pre-configured by the firmware (hdev->hw.mac.autoneg), preserving native compatibility with forced-mode network environments. Fixes: 05eb60e9648c ("net: hns3: using user configure after hardware reset") Signed-off-by: Shuaisong Yang Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260624141319.271439-5-shaojijie@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 87322af852cdb..7f174453928c1 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -11930,6 +11930,13 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_ptp_uninit; + if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) { + hdev->hw.mac.req_autoneg = hdev->hw.mac.autoneg; + if (hdev->hw.mac.autoneg == AUTONEG_DISABLE && + hdev->hw.mac.speed != SPEED_UNKNOWN) + hdev->hw.mac.req_speed = hdev->hw.mac.speed; + } + ret = hclge_set_autoneg_speed_dup(hdev); if (ret) { dev_err(&pdev->dev, From f28d7b5f1578a7501ab17b10643ed1e4f729187e Mon Sep 17 00:00:00 2001 From: Jiaming Zhang Date: Thu, 25 Jun 2026 21:49:33 +0800 Subject: [PATCH 1561/3297] ALSA: FCP: Fix NULL pointer dereference in interface lookup [ Upstream commit e1e31e0ec8a609e17fd2e86b77bc00d9cbb24d7c ] A malformed USB device can provide a vendor-specific interface without any endpoint descriptors. fcp_find_fc_interface() currently selects the first vendor-specific interface and reads endpoint 0 from it, without checking whether the interface actually has any endpoints. When bNumEndpoints is zero, no endpoint array is allocated for the parsed alternate setting, so get_endpoint(..., 0) yields an invalid endpoint descriptor pointer. Dereferencing it through usb_endpoint_num() then triggers a NULL pointer dereference. Skip vendor-specific interfaces that do not have any endpoints. Fixes: 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver") Reported-by: Jiaming Zhang Closes: https://lore.kernel.org/lkml/CANypQFb1EHj0xX8bA1WxSOSK-5xca6ZNKzOQcp12=s=puY7VFw@mail.gmail.com/ Signed-off-by: Jiaming Zhang Link: https://patch.msgid.link/20260625134933.425785-1-r772577952@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/fcp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 11e9a96b46ffe..6eab5cd0cd358 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -1083,6 +1083,8 @@ static int fcp_find_fc_interface(struct usb_mixer_interface *mixer) if (desc->bInterfaceClass != 255) continue; + if (desc->bNumEndpoints < 1) + continue; epd = get_endpoint(intf->altsetting, 0); private->bInterfaceNumber = desc->bInterfaceNumber; From d6cd34d17b951f7e1dc8b3fa4d00de7f4ecc3e6a Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 25 Jun 2026 08:34:45 +0900 Subject: [PATCH 1562/3297] tracing: probes: fix typo in a log message [ Upstream commit 72c8646956ffc8050bb8be5988a0f28fc37e1ac4 ] Fix a typo ("Invalid $-variable") in a log message. Link: https://lore.kernel.org/all/20260507081041.885781-4-martin@kaiser.cx/ Fixes: ab105a4fb894 ("tracing: Use tracing error_log with probe events") Signed-off-by: Martin Kaiser Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_probe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 6149c4850382c..37447784a8fb4 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -509,7 +509,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(NO_RETVAL, "This function returns 'void' type"), \ C(BAD_STACK_NUM, "Invalid stack number"), \ C(BAD_ARG_NUM, "Invalid argument number"), \ - C(BAD_VAR, "Invalid $-valiable specified"), \ + C(BAD_VAR, "Invalid $-variable specified"), \ C(BAD_REG_NAME, "Invalid register name"), \ C(BAD_MEM_ADDR, "Invalid memory address"), \ C(BAD_IMM, "Invalid immediate value"), \ From 776f70bafd45c67b1fe63179808513c65e9a0699 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 21:58:34 +0800 Subject: [PATCH 1563/3297] spi: sh-msiof: abort transfers when reset times out [ Upstream commit 6dbaa4d288432c697cea47028480481b8b29bd6a ] sh_msiof_spi_reset_regs() asserts TX/RX reset and polls until the reset bits clear, but the poll result is ignored. sh_msiof_transfer_one() can therefore continue programming a transfer after the controller did not leave reset. Return the reset poll result from the helper and abort the transfer on timeout, matching the existing transfer path's error-return style. Fixes: fedd6940682a ("spi: sh-msiof: Add reset of registers before starting transfer") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260623135834.55442-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-sh-msiof.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 7f4135a3bda23..a4ae96290aa24 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -114,7 +114,7 @@ static irqreturn_t sh_msiof_spi_irq(int irq, void *data) return IRQ_HANDLED; } -static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) +static int sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) { u32 mask = SICTR_TXRST | SICTR_RXRST; u32 data; @@ -123,8 +123,8 @@ static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) data |= mask; sh_msiof_write(p, SICTR, data); - readl_poll_timeout_atomic(p->mapbase + SICTR, data, !(data & mask), 1, - 100); + return readl_poll_timeout_atomic(p->mapbase + SICTR, data, + !(data & mask), 1, 100); } static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, @@ -834,7 +834,9 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, int ret; /* reset registers */ - sh_msiof_spi_reset_regs(p); + ret = sh_msiof_spi_reset_regs(p); + if (ret) + return ret; /* setup clocks (clock already enabled in chipselect()) */ if (!spi_controller_is_target(p->ctlr)) From 8e0b7f94fb394d80c061b1d99d54f58f4f931ef7 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 25 Jun 2026 17:47:02 +0800 Subject: [PATCH 1564/3297] ACPI: RIMT: Only defer the IOMMU configuration in init stage [ Upstream commit b8f62414fa05144924257db283c5c35f74d21121 ] The IOMMU configuration will be deferred if the IOMMU driver isn't probed by the time. Make this deferral only in the initialization stage with driver_deferred_probe_check_state(). Otherwise the devices depends on IOMMU will be deferred forever in case the IOMMU device probe failed or it doesn't appear in the ACPI namespace. Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT") Signed-off-by: Yicong Yang Link: https://patch.msgid.link/20260625094702.11558-1-yang.yicong@picoheart.com [pjw@kernel.org: added Fixes line] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- drivers/acpi/riscv/rimt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c index 8eaa8731bddd6..4c0962e68c94c 100644 --- a/drivers/acpi/riscv/rimt.c +++ b/drivers/acpi/riscv/rimt.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -257,11 +258,11 @@ static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 rimt_fwnode = rimt_get_fwnode(node); /* - * The IOMMU drivers may not be probed yet. - * Defer the IOMMU configuration + * The IOMMU drivers may not be probed yet. Defer the IOMMU + * configuration if it's still in initialization stage. */ if (!rimt_fwnode) - return -EPROBE_DEFER; + return driver_deferred_probe_check_state(dev); /* * EPROBE_DEFER ensures IOMMU is probed before the devices that From 46dee20d30b707225fe2afe9bc1e165a489bf92a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 24 Jun 2026 19:31:48 +0800 Subject: [PATCH 1565/3297] riscv: Fix 32-bit call_on_irq_stack() frame pointer ABI [ Upstream commit c4c7756a81b5baef286bf9be1ea404f3e4dd7a3c ] call_on_irq_stack() uses struct member offsets to set up its link in the frame record list. On riscv32, struct stackframe is the wrong size to maintain stack pointer alignment, so STACKFRAME_SIZE_ON_STACK includes padding. However, the ABI requires the frame record to be placed immediately below the address stored in s0, so the padding must come before the struct members. Fix the layout by making STACKFRAME_FP and STACKFRAME_RA the negative offsets from s0, instead of the positive offsets from sp. Fixes: 82982fdd5133 ("riscv: Deduplicate IRQ stack switching") Signed-off-by: Samuel Holland Reviewed-by: Matthew Bystrin Signed-off-by: Rui Qi Link: https://lore.kernel.org/all/20240530001733.1407654-2-samuel.holland@sifive.com/ Reviewed-by: Nam Cao Link: https://patch.msgid.link/20260624113148.3723541-1-qirui.001@bytedance.com [pjw@kernel.org: cleaned up the patch tags and added Matthew's Reviewed-by] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/asm-offsets.c | 4 ++-- arch/riscv/kernel/entry.S | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index 7d42d3b8a32a7..0a5e3e892c0ea 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -497,8 +497,8 @@ void asm_offsets(void) OFFSET(SBI_HART_BOOT_STACK_PTR_OFFSET, sbi_hart_boot_data, stack_ptr); DEFINE(STACKFRAME_SIZE_ON_STACK, ALIGN(sizeof(struct stackframe), STACK_ALIGN)); - OFFSET(STACKFRAME_FP, stackframe, fp); - OFFSET(STACKFRAME_RA, stackframe, ra); + DEFINE(STACKFRAME_FP, offsetof(struct stackframe, fp) - sizeof(struct stackframe)); + DEFINE(STACKFRAME_RA, offsetof(struct stackframe, ra) - sizeof(struct stackframe)); #ifdef CONFIG_FUNCTION_TRACER DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func)); #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index d9095b5718d12..7549f03ef18ff 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -351,8 +351,8 @@ SYM_CODE_END(ret_from_fork_user_asm) SYM_FUNC_START(call_on_irq_stack) /* Create a frame record to save ra and s0 (fp) */ addi sp, sp, -STACKFRAME_SIZE_ON_STACK - REG_S ra, STACKFRAME_RA(sp) - REG_S s0, STACKFRAME_FP(sp) + REG_S ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp) + REG_S s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp) addi s0, sp, STACKFRAME_SIZE_ON_STACK /* Switch to the per-CPU shadow call stack */ @@ -370,8 +370,8 @@ SYM_FUNC_START(call_on_irq_stack) /* Switch back to the thread stack and restore ra and s0 */ addi sp, s0, -STACKFRAME_SIZE_ON_STACK - REG_L ra, STACKFRAME_RA(sp) - REG_L s0, STACKFRAME_FP(sp) + REG_L ra, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_RA)(sp) + REG_L s0, (STACKFRAME_SIZE_ON_STACK + STACKFRAME_FP)(sp) addi sp, sp, STACKFRAME_SIZE_ON_STACK ret From f4af803269ccd192a6299f1c1658b831578d2b1a Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 21:16:45 +0800 Subject: [PATCH 1566/3297] gpio: mvebu: fail probe if gpiochip registration fails [ Upstream commit 479e91fc92416a4d54d2b3150aa1e4550d9cc759 ] mvebu_gpio_probe() registers the GPIO chip with devm_gpiochip_add_data() but ignores the return value. If registration fails, probe continues and leaves later code operating on a GPIO chip that was never published to gpiolib. Return the registration error so the device fails probe cleanly. Fixes: fefe7b092345 ("gpio: introduce gpio-mvebu driver for Marvell SoCs") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260624131645.86884-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mvebu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index a7018e8ed88b1..2b5112e3210ba 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1222,7 +1222,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev) BUG(); } - devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip); + err = devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip); + if (err) + return dev_err_probe(&pdev->dev, err, + "failed to register gpiochip\n"); /* Some MVEBU SoCs have simple PWM support for GPIO lines */ if (IS_REACHABLE(CONFIG_PWM)) { From e94d53a9ac22cf43c7d6163c6cb8b0028c25aae1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 21:18:28 +0800 Subject: [PATCH 1567/3297] gpio: htc-egpio: use managed gpiochip registration [ Upstream commit 17326db5f0ab4ec1901e75d052b5ebef486b467f ] egpio_probe() registers each nested gpio_chip with gpiochip_add_data() but ignores the return value. If one registration fails, probe still returns success even though one of the chips was not published to gpiolib. Use devm_gpiochip_add_data() and fail probe if any chip registration fails. This lets devres unwind already registered chips and prevents the driver from publishing a partially initialized device. Fixes: a1635b8fe59d ("[ARM] 4947/1: htc-egpio, a driver for GPIO/IRQ expanders with fixed input/output pins") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260624131828.94139-1-pengpeng@iscas.ac.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-htc-egpio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 2eaed83214d82..8c583d6a7c96f 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -268,6 +268,7 @@ static int __init egpio_probe(struct platform_device *pdev) struct gpio_chip *chip; unsigned int irq, irq_end; int i; + int ret; /* Initialize ei data structure. */ ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL); @@ -331,7 +332,10 @@ static int __init egpio_probe(struct platform_device *pdev) chip->base = pdata->chip[i].gpio_base; chip->ngpio = pdata->chip[i].num_gpios; - gpiochip_add_data(chip, &ei->chip[i]); + ret = devm_gpiochip_add_data(&pdev->dev, chip, &ei->chip[i]); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to register gpiochip %d\n", i); } /* Set initial pin values */ From 8d501b1411548442aaf1de3268654046f5bcf4c1 Mon Sep 17 00:00:00 2001 From: Corey Leavitt Date: Wed, 24 Jun 2026 22:40:16 +0200 Subject: [PATCH 1568/3297] net: pse-pd: scope pse_control regulator handle to kref lifetime [ Upstream commit 16759757c4d28e958fd5a5a1fe0f86828872f28d ] __pse_control_release() drops psec->ps via devm_regulator_put(), which only succeeds if the devres entry added by the matching devm_regulator_get_exclusive() is still present on pcdev->dev at the time the pse_control's kref hits zero. That assumption does not hold when the controller is unbound while a pse_control still has consumers: pcdev->dev's devres list is released LIFO, so every per-attach regulator-GET devres runs (and regulator_put()s the underlying regulator) before pse_controller_unregister() itself is invoked. Any later pse_control_put() from that unbind path then reads psec->ps as a dangling pointer inside devm_regulator_put() and WARNs at drivers/regulator/devres.c:232 (devres_release() fails to find the already-released match). The pse_control's consumer handle is logically scoped to the pse_control's refcount, not to pcdev->dev's devres lifetime. Switch to the plain regulator_get_exclusive() / regulator_put() pair so the regulator put in __pse_control_release() no longer depends on the controller's devres still being present. No change to the regulator-framework-visible refcount or lifetime of the underlying regulator: a single get paired with a single put. The existing devm_regulator_register() for the per-PI rails is unchanged (those ARE correctly scoped to the controller's lifetime). This addresses only the regulator handle. The same unbind-while-held scenario also leaves __pse_control_release() reading psec->pcdev->pi[] and psec->pcdev->owner after pse_controller_unregister() has freed pcdev->pi, because the controller does not drain its outstanding pse_control references on unregister. That wider pse_control vs pcdev lifetime problem pre-dates this change and is addressed by the PSE controller notifier series, which drains phydev->psec on PSE_UNREGISTERED before pcdev->pi is freed. Link: https://lore.kernel.org/netdev/20260620112440.1734404-1-github@szelinsky.de/ Fixes: d83e13761d5b ("net: pse-pd: Use regulator framework within PSE framework") Signed-off-by: Corey Leavitt Acked-by: Kory Maincent Signed-off-by: Carlo Szelinsky Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260624204017.2752934-1-github@szelinsky.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/pse-pd/pse_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index af354483d1910..539094d628563 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -1362,7 +1362,7 @@ static void __pse_control_release(struct kref *kref) if (psec->pcdev->pi[psec->id].admin_state_enabled) regulator_disable(psec->ps); - devm_regulator_put(psec->ps); + regulator_put(psec->ps); module_put(psec->pcdev->owner); @@ -1431,8 +1431,8 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index, goto free_psec; pcdev->pi[index].admin_state_enabled = ret; - psec->ps = devm_regulator_get_exclusive(pcdev->dev, - rdev_get_name(pcdev->pi[index].rdev)); + psec->ps = regulator_get_exclusive(pcdev->dev, + rdev_get_name(pcdev->pi[index].rdev)); if (IS_ERR(psec->ps)) { ret = PTR_ERR(psec->ps); goto put_module; From 8dba7a94a269b88e500aafc25ad567ef6a423698 Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Tue, 23 Jun 2026 18:32:31 +0800 Subject: [PATCH 1569/3297] seg6: validate SRH length before reading fixed fields [ Upstream commit a75d99f46bf21b45965ce39c5cfb3b8bb5ffb1aa ] seg6_validate_srh() reads fixed SRH fields such as srh->type and srh->hdrlen before checking that the supplied length covers the fixed struct ipv6_sr_hdr fields. The BPF SEG6 encap path reaches this with a BPF program-supplied pointer and length: bpf_lwt_push_encap() and the SEG6 local BPF END_B6 and END_B6_ENCAP actions call bpf_push_seg6_encap(), which forwards the length to seg6_validate_srh() with no minimum-size guard. A 2-byte SEG6 encap header can therefore make the validator read srh->type at offset 2 beyond the caller-supplied buffer. Reject lengths shorter than the fixed SRH at the top of seg6_validate_srh(), before any field is read. This fixes the BPF helper path and keeps the common validator robust. Fixes: fe94cc290f53 ("bpf: Add IPv6 Segment Routing helpers") Signed-off-by: Nuoqi Gui Reviewed-by: Andrea Mayer Link: https://patch.msgid.link/20260623-f01-17-seg6-srh-len-v2-1-2edc40e9e3e1@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/seg6.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index a5c4c629b788c..0246ab6b2c95c 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -29,6 +29,9 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced) int max_last_entry; int trailing; + if (len < sizeof(*srh)) + return false; + if (srh->type != IPV6_SRCRT_TYPE_4) return false; From b17751a2ebc4aef3ce6be6f71ee8df92bf5ddfcd Mon Sep 17 00:00:00 2001 From: Matvey Kovalev Date: Tue, 23 Jun 2026 17:45:54 +0300 Subject: [PATCH 1570/3297] qede: fix out-of-bounds check for cqe->len_list[] [ Upstream commit f9ba47fce5932c15891c89c60e76dfaca919cb8d ] Move index check before element access. Fixes: 896f1a2493b5 ("net: qlogic/qede: fix potential out-of-bounds read in qede_tpa_cont() and qede_tpa_end()") Signed-off-by: Matvey Kovalev Link: https://patch.msgid.link/20260623144602.3521-1-matvey.kovalev@ispras.ru Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index e338bfc8b7b2f..33e18bb697740 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -961,7 +961,7 @@ static inline void qede_tpa_cont(struct qede_dev *edev, { int i; - for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++) + for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++) qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index, le16_to_cpu(cqe->len_list[i])); @@ -986,7 +986,7 @@ static int qede_tpa_end(struct qede_dev *edev, dma_unmap_page(rxq->dev, tpa_info->buffer.mapping, PAGE_SIZE, rxq->data_direction); - for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++) + for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++) qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index, le16_to_cpu(cqe->len_list[i])); if (unlikely(i > 1)) From 1681cc7974a6123f5d5740b03bc11e4784bd2542 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 26 Jun 2026 15:32:44 +0800 Subject: [PATCH 1571/3297] net: enetc: check the number of BDs needed for xdp_frame [ Upstream commit 555c5475e787802eeae0d2b91c2f66c330db2767 ] The size of xdp_redirect_arr array is ENETC_MAX_SKB_FRAGS. However, the number of fragments contained in xdp_frame may be greater than or equal to ENETC_MAX_SKB_FRAGS, which will cause the access to xdp_redirect_arr to be out of bounds. Fixes: 9d2b68cc108d ("net: enetc: add support for XDP_REDIRECT") Signed-off-by: Wei Fang Link: https://patch.msgid.link/20260626073244.2168214-1-wei.fang@oss.nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/enetc/enetc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index b5dd49337513d..6603ed6962967 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1774,6 +1774,7 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames, { struct enetc_tx_swbd xdp_redirect_arr[ENETC_MAX_SKB_FRAGS] = {0}; struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct skb_shared_info *shinfo; struct enetc_bdr *tx_ring; int xdp_tx_bd_cnt, i, k; int xdp_tx_frm_cnt = 0; @@ -1789,6 +1790,12 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames, prefetchw(ENETC_TXBD(*tx_ring, tx_ring->next_to_use)); for (k = 0; k < num_frames; k++) { + if (xdp_frame_has_frags(frames[k])) { + shinfo = xdp_get_shared_info_from_frame(frames[k]); + if (unlikely((shinfo->nr_frags + 1) > ENETC_MAX_SKB_FRAGS)) + break; + } + xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring, xdp_redirect_arr, frames[k]); From ad6215d76b644fe4cde6271ba9805ca473d40f03 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Thu, 25 Jun 2026 16:23:54 +0200 Subject: [PATCH 1572/3297] sctp: fix SCTP_RESET_STREAMS stream list length limit [ Upstream commit 2b9f5ef534184bd81b8a4772780626c40eed1fd5 ] SCTP_RESET_STREAMS carries a flexible array of u16 stream IDs, but the optlen clamps treat USHRT_MAX as a byte count and then multiply sizeof(__u16) by the fixed header size. That caps the copied and validated option buffer at about 64 KiB, which rejects valid requests containing more than about half of the u16 stream ID range. Use struct_size_t() for the maximum struct sctp_reset_streams layout instead, so the bound matches the flexible array described by srs_number_streams. Fixes: 5960cefab9df ("sctp: add a ceiling to optlen in some sockopts") Acked-by: Xin Long Signed-off-by: Yousef Alhouseen Link: https://patch.msgid.link/20260625142354.2600-1-alhouseenyousef@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/socket.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 7a641cb847384..b4f706a975be2 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4110,8 +4110,9 @@ static int sctp_setsockopt_reset_streams(struct sock *sk, if (optlen < sizeof(*params)) return -EINVAL; /* srs_number_streams is u16, so optlen can't be bigger than this. */ - optlen = min_t(unsigned int, optlen, USHRT_MAX + - sizeof(__u16) * sizeof(*params)); + optlen = min_t(unsigned int, optlen, + struct_size_t(struct sctp_reset_streams, srs_stream_list, + USHRT_MAX)); if (params->srs_number_streams * sizeof(__u16) > optlen - sizeof(*params)) @@ -4597,8 +4598,8 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, if (optlen > 0) { /* Trim it to the biggest size sctp sockopt may need if necessary */ optlen = min_t(unsigned int, optlen, - PAGE_ALIGN(USHRT_MAX + - sizeof(__u16) * sizeof(struct sctp_reset_streams))); + PAGE_ALIGN(struct_size_t(struct sctp_reset_streams, + srs_stream_list, USHRT_MAX))); kopt = memdup_sockptr(optval, optlen); if (IS_ERR(kopt)) return PTR_ERR(kopt); From bca3100f550281c2f2418652338bced3b35af0e6 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 24 Jun 2026 18:53:12 -0400 Subject: [PATCH 1573/3297] sctp: add INIT verification after cookie unpacking [ Upstream commit 414c5447fe6a200613dd46d7fdc8454622076cb1 ] In SCTP handshake, the INIT chunk is initially processed by the server and embedded into the cookie carried in INIT-ACK. The client then returns this cookie via COOKIE-ECHO, where the server unpacks it and reconstructs the original INIT chunk. When cookie authentication is enabled, the cookie contents are protected against tampering, so reusing the unpacked INIT without re-verification is safe. However, when cookie authentication is disabled, the reconstructed INIT can no longer be trusted. In this case, the INIT must be explicitly validated after unpacking to avoid processing potentially tampered data. Add sctp_verify_init() checks after cookie unpacking in COOKIE-ECHO processing paths (sctp_sf_do_5_1D_ce() and sctp_sf_do_5_2_4_dupcook()) when cookie_auth_enable is disabled. On failure, the new association is freed and the packet is discarded. Also tighten cookie validation in sctp_unpack_cookie() by verifying the embedded chunk type is SCTP_CID_INIT before treating it as an INIT chunk. Finally, update sctp_verify_init() to validate parameter bounds using the actual embedded INIT length instead of chunk->chunk_end, since the INIT stored in COOKIE-ECHO may not span the entire chunk buffer. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Xin Long Link: https://patch.msgid.link/ebcbbac574815b0850f371b4bdb02f2e602b94d3.1782341592.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/sm_make_chunk.c | 5 ++++- net/sctp/sm_statefuns.c | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 6fb18b07b6b01..a7804e47d3827 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1761,6 +1761,8 @@ struct sctp_association *sctp_unpack_cookie( bear_cookie = &cookie->c; ch = (struct sctp_chunkhdr *)(bear_cookie + 1); + if (ch->type != SCTP_CID_INIT) + goto malformed; chlen = ntohs(ch->length); if (chlen < sizeof(struct sctp_init_chunk)) goto malformed; @@ -2298,7 +2300,8 @@ int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep, * VIOLATION error. We build the ERROR chunk here and let the normal * error handling code build and send the packet. */ - if (param.v != (void *)chunk->chunk_end) + if (param.v != (void *)peer_init + + SCTP_PAD4(ntohs(peer_init->chunk_hdr.length))) return sctp_process_inv_paramlength(asoc, param.p, chunk, errp); /* The only missing mandatory param possible today is diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9b23c11cbb9ea..345395662d90d 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -705,11 +705,12 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, struct sctp_cmd_seq *commands) { struct sctp_ulpevent *ev, *ai_ev = NULL, *auth_ev = NULL; + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; - struct sctp_chunk *err_chk_p; struct sctp_chunk *repl; + enum sctp_cid cid; struct sock *sk; int error = 0; @@ -783,6 +784,19 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, } } + peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + if (security_sctp_assoc_request(new_asoc, chunk->head_skb ?: chunk->skb)) { sctp_association_free(new_asoc); return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -796,7 +810,6 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, /* This is a brand-new association, so these are not yet side * effects--it is safe to run them here. */ - peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); if (!sctp_process_init(new_asoc, chunk, &chunk->subh.cookie_hdr->c.peer_addr, peer_init, GFP_ATOMIC)) @@ -2210,10 +2223,12 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( void *arg, struct sctp_cmd_seq *commands) { + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; + struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; enum sctp_disposition retval; - struct sctp_chunk *err_chk_p; + enum sctp_cid cid; int error = 0; char action; @@ -2282,6 +2297,21 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( switch (action) { case 'A': /* Association restart. */ case 'B': /* Collision case B. */ + peer_init = (struct sctp_init_chunk *) + (chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(ep->base.sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, + commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + fallthrough; case 'D': /* Collision case D. */ /* Update socket peer label if first association. */ if (security_sctp_assoc_request((struct sctp_association *)asoc, From 4e9f4ca9dc73c2b2acba772ebadc54f644fd819a Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:43:00 +0100 Subject: [PATCH 1574/3297] MIPS: DEC: Ensure RTC platform device deregistration upon failure [ Upstream commit eacaf5ae747f7dead6cc268de17a7382d79031fc ] Switch RTC platform device registration from platform_device_register() to platform_add_devices() so as to make sure any failure will result in automatic device unregistration. Fixes: fae67ad43114 ("arch/mips/dec: switch DECstation systems to rtc-cmos") Signed-off-by: Maciej W. Rozycki Acked-by: Thomas Bogendoerfer Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/dec/platform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index 723ce16cbfc0c..a005246a0ac5d 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -38,6 +38,10 @@ static struct platform_device dec_rtc_device = { .num_resources = ARRAY_SIZE(dec_rtc_resources), }; +static struct platform_device *dec_rtc_devices[] __initdata = { + &dec_rtc_device, +}; + static struct resource dec_dz_resources[] = { { .name = "dz", .flags = IORESOURCE_MEM, }, { .name = "dz", .flags = IORESOURCE_IRQ, }, @@ -137,7 +141,7 @@ static int __init dec_add_devices(void) } num_zs = i; - ret1 = platform_device_register(&dec_rtc_device); + ret1 = platform_add_devices(dec_rtc_devices, 1); ret2 = IS_ENABLED(CONFIG_32BIT) ? platform_add_devices(dec_dz_devices, num_dz) : 0; ret3 = platform_add_devices(dec_zs_devices, num_zs); From 3d3638fe921371e52be77baefe792c126fcdfc8c Mon Sep 17 00:00:00 2001 From: Kyle Hendry Date: Sun, 21 Jun 2026 11:47:02 -0700 Subject: [PATCH 1575/3297] MIPS: mm: Add check for highmem before removing memory block [ Upstream commit 6d5fbecd0213489bc4de71a0da194d18e654fd6e ] If a device has less physical memory than the highmem threshold bootmem_init() doesn't set highstart_pfn. This results in highmem_init() wrongly disabling the entire memory range if the cpu doesn't support highmem. Add a check that highstart_pfn is non zero before removing the highmem block. Fixes: f171b55f1441 ("mips: fix HIGHMEM initialization") Signed-off-by: Kyle Hendry Signed-off-by: Thomas Bogendoerfer Signed-off-by: Sasha Levin --- arch/mips/mm/init.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index decd201504043..4d73dc6c33473 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -435,10 +435,11 @@ static inline void __init highmem_init(void) unsigned long tmp; /* - * If CPU cannot support HIGHMEM discard the memory above highstart_pfn + * If CPU cannot support HIGHMEM discard any memory above highstart_pfn */ if (cpu_has_dc_aliases) { - memblock_remove(PFN_PHYS(highstart_pfn), -1); + if (highstart_pfn) + memblock_remove(PFN_PHYS(highstart_pfn), -1); return; } From f14c3926fee38e5309749f0ba3c7ae28216afea3 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 31 Oct 2025 12:07:02 +0000 Subject: [PATCH 1576/3297] ASoC: codecs: lpass-va-macro: add SM6115 compatible [ Upstream commit 893e2fd509e968cc1d76caadee0f5d2f2c72f137 ] SM6115 does not have "macro" clock, so its bindings differ with existing SoC support in va-macro. So add dedicated compatible in both driver and dt-bindings to be able to address that difference. Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20251031120703.590201-6-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown Stable-dep-of: 0773610eef71 ("ASoC: codecs: lpass-va-macro: Fix LPASS Codec Version for SC7280") Signed-off-by: Sasha Levin --- sound/soc/codecs/lpass-va-macro.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 92c177b82a021..4e537e2f4ff18 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1723,6 +1723,7 @@ static const struct dev_pm_ops va_macro_pm_ops = { static const struct of_device_id va_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, { .compatible = "qcom,sm8550-lpass-va-macro", .data = &sm8550_va_data }, From 1797eb92f0b39ecb3348c873dcca2caa1e7cce4d Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Tue, 26 May 2026 17:03:05 +0200 Subject: [PATCH 1577/3297] ASoC: codecs: lpass-va-macro: Fix LPASS Codec Version for SC7280 [ Upstream commit 0773610eef71c30df3cb4c113c8215625d2a7c23 ] According to both the static definition in downstream... yupik-audio-overlay.dtsi: qcom,bolero-version = <4>; #define BOLERO_VERSION_2_0 0x0004) and the runtime detection: CDC_VA_TOP_CSR_CORE_ID_0=0x1 CDC_VA_TOP_CSR_CORE_ID_1=0xf SC7280 has LPASS Codec Version 2.0 and not, as declared with sm8250_va_data LPASS_CODEC_VERSION_1_0. Create new va_macro_data with .version not set to use the runtime detection and correctly get .version = LPASS_CODEC_VERSION_2_0. Fixes: 77212f300bfd ("ASoC: codecs: lpass-va-macro: set the default codec version for sm8250") Signed-off-by: Luca Weiss Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260526-sc7280-va-macro-2-0-v1-1-2c1b572fa388@fairphone.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/lpass-va-macro.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 4e537e2f4ff18..38e8b190d5c2f 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -236,6 +236,11 @@ static const struct va_macro_data sm8250_va_data = { .version = LPASS_CODEC_VERSION_1_0, }; +static const struct va_macro_data sc7280_va_data = { + .has_swr_master = false, + .has_npl_clk = false, +}; + static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, .has_npl_clk = true, @@ -1722,7 +1727,7 @@ static const struct dev_pm_ops va_macro_pm_ops = { }; static const struct of_device_id va_macro_dt_match[] = { - { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sc7280_va_data }, { .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, From 36554592e2f5cac16ff8bf73720d38a6d9ef4a20 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 26 Jun 2026 10:22:04 +0300 Subject: [PATCH 1578/3297] hwmon: adm1275: Prevent reading uninitialized stack [ Upstream commit 553f9517813912a5ab661af5504485d96824a61c ] While adding support for the ROHM BD127X0 hot-swap controllers, sashiko reported an error in device-name comparison, which can lead to reading uninitialized stack memory. Quoting Sashiko: This is a pre-existing issue, but I noticed that just before this block in adm1275_probe(), there might be an out-of-bounds stack read: ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer); if (ret < 0) { ... } for (mid = adm1275_id; mid->name[0]; mid++) { if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) break; } Since i2c_smbus_read_block_data() reads up to 32 bytes into the uninitialized stack array block_buffer without appending a null terminator, strncasecmp() could read past the valid bytes returned in ret. For example, if the device returns a shorter string like "adm12", checking it against "adm1275" up to the length of "adm1275" will continue reading into uninitialized stack bounds. Prevent reading uninitialized memory by zeroing the stack array. Signed-off-by: Matti Vaittinen Fixes: 87102808d039 ("hwmon: (pmbus/adm1275) Validate device ID") Link: https://lore.kernel.org/r/c8ad38e0cdb347261c6245de2b7965e747f28d22.1782458224.git.mazziesaccount@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/pmbus/adm1275.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index bc2a6a07dc3e2..43baa5ded35e5 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -512,7 +512,7 @@ static int adm1275_enable_vout_temp(struct adm1275_data *data, static int adm1275_probe(struct i2c_client *client) { s32 (*config_read_fn)(const struct i2c_client *client, u8 reg); - u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1] = {0}; int config, device_config; int ret; struct pmbus_driver_info *info; From 489291b6b56978cc50d34e8e13f9636ea296ba8a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 29 Jun 2026 16:08:00 -0700 Subject: [PATCH 1579/3297] hwmon: (pmbus) Fix passing events to regulator core [ Upstream commit 9ef7dacd44216bf5ea05c8aef49eba4d145f4047 ] Sashiko reports: Commit 754bd2b4a084 ("hwmon: (pmbus/core) Protect regulator operations with mutex") introduced a worker to batch regulator events over time using atomic_or(). The delayed worker then passes the combined bitmask unmodified to regulator_notifier_call_chain(). The core regulator subsystem's regulator_handle_critical() function evaluates the event parameter using a strict switch statement. If multiple distinct faults occur before the worker runs (e.g., REGULATOR_EVENT_UNDER_VOLTAGE | REGULATOR_EVENT_OVER_CURRENT), the combined bitmask fails to match any case. This leaves the reason as NULL and completely bypasses the critical hw_protection_trigger(). Fix the problem by passing events bit by bit to the regulator event handler. Reported-by: Sashiko Fixes: 754bd2b4a084 ("hwmon: (pmbus/core) Protect regulator operations with mutex") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/pmbus/pmbus_core.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 7150f12d26300..ddf64e72751cc 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3381,18 +3381,23 @@ static void pmbus_regulator_notify_worker(struct work_struct *work) int i, j; for (i = 0; i < data->info->pages; i++) { - int event; + unsigned int event; event = atomic_xchg(&data->regulator_events[i], 0); if (!event) continue; for (j = 0; j < data->info->num_regulators; j++) { - if (i == rdev_get_id(data->rdevs[j])) { + if (i != rdev_get_id(data->rdevs[j])) + continue; + while (event) { + unsigned int _event = BIT(__ffs(event)); + regulator_notifier_call_chain(data->rdevs[j], - event, NULL); - break; + _event, NULL); + event &= ~_event; } + break; } } } From 898ca04b096b9e4640300eab91468c826a1ec92b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 29 Jun 2026 16:18:18 -0700 Subject: [PATCH 1580/3297] hwmon: (aspeed-g6-pwm-tach) Guard fan RPM calculation against divide-by-zero [ Upstream commit fe87b8dc67f1b2c64e76a66e78468c533d3c44ca ] Sashiko reports: In the aspeed-g6-pwm-tacho driver, the aspeed_tach_val_to_rpm() function calculates the fan RPM using the tachometer value. However, it does not check if the tachometer value is zero before performing the division. If the hardware reports a tachometer value of 0 (which can happen due to an extremely fast pulse, a stuck edge, or a hardware glitch), the calculated tach_div evaluates to 0. The subsequent call to do_div() with tach_div as the divisor triggers a divide-by-zero exception, leading to a kernel panic. Check the divisor against zero to fix the problem. Fixes: 7e1449cd15d1 ("hwmon: (aspeed-g6-pwm-tacho): Support for ASPEED g6 PWM/Fan tach") Cc: Billy Tsai Reported-by: Sashiko Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/aspeed-g6-pwm-tach.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c index d1f7f43974824..3be5965441232 100644 --- a/drivers/hwmon/aspeed-g6-pwm-tach.c +++ b/drivers/hwmon/aspeed-g6-pwm-tach.c @@ -293,7 +293,10 @@ static int aspeed_tach_val_to_rpm(struct aspeed_pwm_tach_data *priv, u32 tach_va priv->clk_rate, tach_val, tach_div); rpm = (u64)priv->clk_rate * 60; - do_div(rpm, tach_div); + if (tach_div) + do_div(rpm, tach_div); + else + rpm = 0; return (int)rpm; } From 83df3e2594cd78aa40b1246a19879abb4891945b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 25 Jun 2026 09:05:08 -0700 Subject: [PATCH 1581/3297] eth: fbnic: don't cache shinfo across skb realloc [ Upstream commit 62b68b774f06bf52e329f254f0199bc43d350ccf ] fbnic_tx_lso() calls skb_cow_head() which may reallocate the skb including the shared info. We can't use the pointer calculated before the call. BUG: KASAN: slab-use-after-free in fbnic_tx_lso.isra.0+0x668/0x8e0 Read of size 4 at addr ff110000262edd98 by task swapper/5/0 Call Trace: fbnic_tx_lso.isra.0+0x668/0x8e0 fbnic_xmit_frame+0x622/0xba0 dev_hard_start_xmit+0xf4/0x620 Allocated by task 8653: __alloc_skb+0x11e/0x5f0 alloc_skb_with_frags+0xcc/0x6c0 sock_alloc_send_pskb+0x327/0x3f0 __ip_append_data+0x188b/0x47a0 ip_make_skb+0x24a/0x300 udp_sendmsg+0x14d2/0x21e0 Freed by task 0: kfree+0x123/0x5a0 pskb_expand_head+0x36c/0xfa0 fbnic_tx_lso.isra.0+0x500/0x8e0 fbnic_xmit_frame+0x622/0xba0 dev_hard_start_xmit+0xf4/0x620 sch_direct_xmit+0x25b/0x1100 The buggy address belongs to the object at ff110000262edc40 which belongs to the cache skbuff_small_head of size 640 The buggy address is located 344 bytes inside of freed 640-byte region [ff110000262edc40, ff110000262ede Link: https://netdev.bots.linux.dev/logs/vmksft/fbnic-qemu-dbg/results/705762/15-uso-py/stderr Fixes: b0b0f52042ac ("eth: fbnic: support TCP segmentation offload") Reviewed-by: Alexander Duyck Link: https://patch.msgid.link/20260625160508.3327986-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/meta/fbnic/fbnic_txrx.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index 6e21f6c17c658..14885c3d70697 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -194,16 +194,18 @@ static bool fbnic_tx_tstamp(struct sk_buff *skb) static bool fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, - struct skb_shared_info *shinfo, __le64 *meta, - unsigned int *l2len, unsigned int *i3len) + __le64 *meta, unsigned int *l2len, unsigned int *i3len) { unsigned int l3_type, l4_type, l4len, hdrlen; + struct skb_shared_info *shinfo; unsigned char *l4hdr; __be16 payload_len; if (unlikely(skb_cow_head(skb, 0))) return true; + shinfo = skb_shinfo(skb); + if (shinfo->gso_type & SKB_GSO_PARTIAL) { l3_type = FBNIC_TWD_L3_TYPE_OTHER; } else if (!skb->encapsulation) { @@ -258,7 +260,6 @@ fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, static bool fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) { - struct skb_shared_info *shinfo = skb_shinfo(skb); unsigned int l2len, i3len; if (fbnic_tx_tstamp(skb)) @@ -273,8 +274,8 @@ fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) *meta |= cpu_to_le64(FIELD_PREP(FBNIC_TWD_CSUM_OFFSET_MASK, skb->csum_offset / 2)); - if (shinfo->gso_size) { - if (fbnic_tx_lso(ring, skb, shinfo, meta, &l2len, &i3len)) + if (skb_is_gso(skb)) { + if (fbnic_tx_lso(ring, skb, meta, &l2len, &i3len)) return true; } else { *meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_CSO); From d8a01d27873e04bebd357dc87859aa756e0b28b2 Mon Sep 17 00:00:00 2001 From: Pengfei Zhang Date: Thu, 25 Jun 2026 15:05:17 +0800 Subject: [PATCH 1582/3297] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump [ Upstream commit 9facb861dc6b9b9ea9793ef5032a9a826f7a4229 ] inet6_dump_fib() saves its progress in cb->args[1] as a positional index within the current hash chain. Between batches, a concurrent fib6_new_table() can insert a new table at the chain head, shifting all existing entries. The saved index then lands on a different table, causing fib6_dump_table() to set w->root to the wrong table while w->node still points into the previous one. fib6_walk_continue() dereferences w->node->parent (NULL) and panics: BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: 0010:fib6_walk_continue+0x6e/0x170 Call Trace: fib6_dump_table.isra.0+0xc5/0x240 inet6_dump_fib+0xf6/0x420 rtnl_dumpit+0x30/0xa0 netlink_dump+0x15b/0x460 netlink_recvmsg+0x1d6/0x2a0 ____sys_recvmsg+0x17a/0x190 Fix by storing tb->tb6_id in cb->args[1] instead of a positional index. On resume, skip entries until the id matches; a concurrent head-insert can never match the saved id, so the walker always resumes on the correct table. Fixes: 1b43af5480c3 ("[IPV6]: Increase number of possible routing tables to 2^32") Signed-off-by: Pengfei Zhang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260625070517.965597-1-zhangfeionline@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/ip6_fib.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index ffa7733598333..940aae3d10be0 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -634,12 +634,12 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) }; const struct nlmsghdr *nlh = cb->nlh; struct net *net = sock_net(skb->sk); - unsigned int e = 0, s_e; struct hlist_head *head; struct fib6_walker *w; struct fib6_table *tb; unsigned int h, s_h; int err = 0; + u32 s_id; rcu_read_lock(); if (cb->strict_check) { @@ -699,23 +699,22 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) } s_h = cb->args[0]; - s_e = cb->args[1]; + s_id = cb->args[1]; - for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { - e = 0; + for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_id = 0) { head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry_rcu(tb, head, tb6_hlist) { - if (e < s_e) - goto next; + if (s_id && tb->tb6_id != s_id) + continue; + + s_id = 0; + cb->args[1] = tb->tb6_id; err = fib6_dump_table(tb, skb, cb); if (err != 0) goto out; -next: - e++; } } out: - cb->args[1] = e; cb->args[0] = h; unlock: From 0a7d9c7c5f1f208c523abbb4db6aea7bc1fad3db Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 27 Jun 2026 13:53:53 -0700 Subject: [PATCH 1583/3297] usbnet: gl620a: fix out-of-bounds read in genelink_rx_fixup() [ Upstream commit 8ff7f2a6da4fccaa5cc9be7251a24e71e29fbd1a ] genelink_rx_fixup() splits an aggregated RX frame into its individual packets, using a per-packet length taken from device-supplied data. That length is only bounded by GL_MAX_PACKET_LEN (1514); it is never compared against how many bytes were actually received. A malicious GeneLink (GL620A) device can therefore send a short URB whose header claims packet_count > 1 and a first packet of up to 1514 bytes. skb_put_data(gl_skb, packet->packet_data, size); then copies past the end of the receive buffer and hands the adjacent slab contents up the network stack, an out-of-bounds read that leaks kernel heap. No privilege is required: the path runs in the usbnet RX softirq as soon as the interface is up. BUG: KASAN: slab-out-of-bounds in genelink_rx_fixup (drivers/net/usb/gl620a.c:112) Read of size 1514 at addr ffff888011309708 by task ksoftirqd/0/14 Call Trace: ... __asan_memcpy (mm/kasan/shadow.c:105) genelink_rx_fixup (include/linux/skbuff.h:2814 drivers/net/usb/gl620a.c:112) usbnet_bh (drivers/net/usb/usbnet.c:572 drivers/net/usb/usbnet.c:1589) process_one_work (kernel/workqueue.c:3322) bh_worker (kernel/workqueue.c:3405) tasklet_action (kernel/softirq.c:965) handle_softirqs (kernel/softirq.c:622) run_ksoftirqd (kernel/softirq.c:1076) ... skb_pull() already verifies that the requested length fits the buffer and returns NULL otherwise. Move it ahead of the copy and check its result, so a packet that overruns the received data is rejected before it is read. Well-formed frames, whose packets are fully present, are unaffected. Fixes: 47ee3051c856 ("[PATCH] USB: usbnet (5/9) module for genesys gl620a cables") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260627205353.4000788-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/usb/gl620a.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c index 0bfa37c140591..09afd137b64e1 100644 --- a/drivers/net/usb/gl620a.c +++ b/drivers/net/usb/gl620a.c @@ -104,6 +104,9 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) return 0; } + if (!skb_pull(skb, size + 4)) + return 0; + // allocate the skb for the individual packet gl_skb = alloc_skb(size, GFP_ATOMIC); if (gl_skb) { @@ -116,9 +119,6 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) // advance to the next packet packet = (struct gl_packet *)&packet->packet_data[size]; count--; - - // shift the data pointer to the next gl_packet - skb_pull(skb, size + 4); } // skip the packet length field 4 bytes From 86d379fcf1b79bdf4bc2ac891297f30f63d041d8 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sat, 27 Jun 2026 19:32:41 +0200 Subject: [PATCH 1584/3297] net: phy: sfp: free mii_bus in sfp_i2c_mdiobus_destroy [ Upstream commit 8f31efff9206f9f0adb853cad6916086aac4d5ef ] sfp_i2c_mdiobus_create() allocates the I2C MDIO bus with mdio_i2c_alloc(), a plain (non-devm) allocation, and registers it. sfp_i2c_mdiobus_destroy() only unregisters the bus and clears sfp->i2c_mii without calling mdiobus_free(). As the only reference to the bus is then cleared, the struct mii_bus is leaked. This is hit whenever a copper/RollBall SFP module that instantiated an MDIO bus is removed: sfp_sm_main() takes the global teardown path and calls sfp_i2c_mdiobus_destroy(). sfp_cleanup(), on driver unbind, frees sfp->i2c_mii directly, which is why the leak only triggered on module hot-removal and not on unbind. Free the bus in sfp_i2c_mdiobus_destroy() to match the allocation done in sfp_i2c_mdiobus_create(). Fixes: e85b1347ace6 ("net: sfp: create/destroy I2C mdiobus before PHY probe/after PHY release") Signed-off-by: Petr Wozniak Reviewed-by: Maxime Chevallier Reviewed-by: Larysa Zaremba Link: https://patch.msgid.link/312bde8176fc429aa89524e3be250137f034ba84.1782581445.git.petr.wozniak@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/sfp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 25223cfe017b5..9683f9c2cd882 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -850,6 +850,7 @@ static int sfp_i2c_mdiobus_create(struct sfp *sfp) static void sfp_i2c_mdiobus_destroy(struct sfp *sfp) { mdiobus_unregister(sfp->i2c_mii); + mdiobus_free(sfp->i2c_mii); sfp->i2c_mii = NULL; } From 58ba00999898b1e42734d671e824cfda04c810c8 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Fri, 26 Jun 2026 17:25:30 +0800 Subject: [PATCH 1585/3297] net: libwx: fix VMDQ mask for 1-queue mode [ Upstream commit 6ab752e0b59b825c127d5c86438bee1e8b1641ea ] In wx_set_vmdq_queues(), the VMDQ mask was not set for the devices not supporting WX_FLAG_MULTI_64_FUNC, i.e., NGBE devices. A mask of 0 causes __ALIGN_MASK(1, ~vmdq->mask) to return 0, which incorrectly sets q_per_pool to 0 in wx_write_qde(). Fix the VMDQ 1-queue mask to 0x7F then ensures that __ALIGN_MASK(1, ~0x7F) correctly evaluates to 1. Fixes: c52d4b898901 ("net: libwx: Redesign flow when sriov is enabled") Signed-off-by: Jiawen Wu Reviewed-by: Larysa Zaremba Link: https://patch.msgid.link/161F704D2C983E2C+20260626092530.551028-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 1 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 3adf7048320ae..678dce480f156 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1745,6 +1745,7 @@ static bool wx_set_vmdq_queues(struct wx *wx) rss_i = 4; } } else { + vmdq_m = WX_VMDQ_1Q_MASK; /* double check we are limited to maximum pools */ vmdq_i = min_t(u16, 8, vmdq_i); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index f040b014f2dd7..12bb1e19d88d4 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -471,6 +471,7 @@ enum WX_MSCA_CMD_value { #define WX_VMDQ_4Q_MASK 0x7C #define WX_VMDQ_2Q_MASK 0x7E +#define WX_VMDQ_1Q_MASK 0x7F /****************** Manageablility Host Interface defines ********************/ #define WX_HI_MAX_BLOCK_BYTE_LENGTH 256 /* Num of bytes in range */ From ef940e042f32941aa035dac1cd96ee153e0a8c69 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 26 Jun 2026 15:52:28 -0700 Subject: [PATCH 1586/3297] net: gianfar: dispose irq mappings on probe failure and device removal [ Upstream commit dbf803bc4a8b0522c9a12560c20905a5952d1cb9 ] irq_of_parse_and_map() creates irqdomain mappings that should be balanced with irq_dispose_mapping(). The driver never called irq_dispose_mapping(), leaking mappings on probe failure and device removal. Fix by adding irq_dispose_mapping() in free_gfar_dev() and expanding its loop from priv->num_grps to MAXGROUPS so the error path also catches partially-initialized groups. All irqinfo pointers are pre-initialized to NULL in gfar_of_init(), making the NULL-guarded walk in free_gfar_dev() safe for every scenario. gfar_parse_group() itself is left as a simple parse function with no resource management; cleanup is centralized in the caller's error path. Assisted-by: opencode:big-pickle Fixes: b31a1d8b4151 ("gianfar: Convert gianfar to an of_platform_driver") Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260626225228.427392-1-rosenp@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/gianfar.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 7c0f049f09384..a11ba2c7af2a4 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -471,10 +471,13 @@ static void free_gfar_dev(struct gfar_private *priv) { int i, j; - for (i = 0; i < priv->num_grps; i++) + for (i = 0; i < MAXGROUPS; i++) for (j = 0; j < GFAR_NUM_IRQS; j++) { - kfree(priv->gfargrp[i].irqinfo[j]); - priv->gfargrp[i].irqinfo[j] = NULL; + if (priv->gfargrp[i].irqinfo[j]) { + irq_dispose_mapping(priv->gfargrp[i].irqinfo[j]->irq); + kfree(priv->gfargrp[i].irqinfo[j]); + priv->gfargrp[i].irqinfo[j] = NULL; + } } free_netdev(priv->ndev); @@ -619,7 +622,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev) static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) { const char *model; - int err = 0, i; + int err = 0, i, j; phy_interface_t interface; struct net_device *dev = NULL; struct gfar_private *priv = NULL; @@ -705,8 +708,11 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) priv->rx_list.count = 0; mutex_init(&priv->rx_queue_access); - for (i = 0; i < MAXGROUPS; i++) + for (i = 0; i < MAXGROUPS; i++) { priv->gfargrp[i].regs = NULL; + for (j = 0; j < GFAR_NUM_IRQS; j++) + priv->gfargrp[i].irqinfo[j] = NULL; + } /* Parse and initialize group specific information */ if (priv->mode == MQ_MG_MODE) { From 9b7d05cbaa60108642402100efa6aa288dd33023 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 28 Jun 2026 07:12:29 -0400 Subject: [PATCH 1587/3297] net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF [ Upstream commit e5b811fe793166aecc59b085c1b7c31262ef2316 ] The teql master->slaves singly linked list is not protected against multiple writes. It can be mod'ed concurently from teql_master_xmit(), teql_dequeue(), teql_init() and teql_destroy() without holding any list lock or RCU protection. zdi-disclosures@trendmicro.com has demonstrated that the qdisc is freed after an RCU grace period, but teql_master_xmit() running on another CPU can still hold a stale pointer into the list, resulting in a slab-use-after-free: BUG: KASAN: slab-use-after-free in teql_master_xmit+0xf0f/0x16b0 Read of size 8 at addr ffff888013fb0440 by task poc/332 Freed 512-byte region [ffff888013fb0400, ffff888013fb0600) (kmalloc-512) The fix? Add a per-master slaves_lock spinlock that serializes all mutations of master->slaves and the NEXT_SLAVE() links in teql_destroy() and teql_qdisc_init(). teql_master_xmit() also takes the same slaves_lock around those updates. Annotate master->slaves and the per-slave ->next pointer with __rcu and use the appropriate RCU accessors everywhere they are touched: rcu_assign_pointer() on the writer side (under slaves_lock), rcu_dereference_protected() for the writer-side loads (also under slaves_lock), rcu_dereference_bh() for the loads in teql_master_xmit() and rtnl_dereference() for the loads in teql_master_open()/teql_master_mtu(), which run under RTNL. Pair this with rcu_read_lock_bh()/rcu_read_unlock_bh() around the list traversal in teql_master_xmit(), so that readers either observe a fully linked list or are deferred until the in-flight mutation completes. The two early-return paths in teql_master_xmit() are updated to release the RCU-bh read-side critical section before returning, since leaving it held would disable BH on that CPU for good. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260628111229.669751-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/sch_teql.c | 125 +++++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 39 deletions(-) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index ec4039a201a2c..21dc1043547e4 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -52,7 +52,8 @@ struct teql_master { struct Qdisc_ops qops; struct net_device *dev; - struct Qdisc *slaves; + struct Qdisc __rcu *slaves; + spinlock_t slaves_lock; /* serializes writes to ->slaves */ struct list_head master_list; unsigned long tx_bytes; unsigned long tx_packets; @@ -61,7 +62,7 @@ struct teql_master { }; struct teql_sched_data { - struct Qdisc *next; + struct Qdisc __rcu *next; struct teql_master *m; struct sk_buff_head q; }; @@ -101,7 +102,9 @@ teql_dequeue(struct Qdisc *sch) if (skb == NULL) { struct net_device *m = qdisc_dev(q); if (m) { - dat->m->slaves = sch; + spin_lock_bh(&dat->m->slaves_lock); + rcu_assign_pointer(dat->m->slaves, sch); + spin_unlock_bh(&dat->m->slaves_lock); netif_wake_queue(m); } } else { @@ -132,34 +135,49 @@ teql_destroy(struct Qdisc *sch) struct Qdisc *q, *prev; struct teql_sched_data *dat = qdisc_priv(sch); struct teql_master *master = dat->m; + struct netdev_queue *txq = NULL; + bool reset_master_queue = false; if (!master) return; - prev = master->slaves; + spin_lock_bh(&master->slaves_lock); + prev = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); if (prev) { do { - q = NEXT_SLAVE(prev); - if (q == sch) { - NEXT_SLAVE(prev) = NEXT_SLAVE(q); - if (q == master->slaves) { - master->slaves = NEXT_SLAVE(q); - if (q == master->slaves) { - struct netdev_queue *txq; - - txq = netdev_get_tx_queue(master->dev, 0); - master->slaves = NULL; - - dev_reset_queue(master->dev, - txq, NULL); - } - } - skb_queue_purge(&dat->q); - break; + struct Qdisc *head, *next; + + q = rcu_dereference_protected(NEXT_SLAVE(prev), + lockdep_is_held(&master->slaves_lock)); + if (q != sch) { + prev = q; + continue; } - } while ((prev = q) != master->slaves); + next = rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock)); + rcu_assign_pointer(NEXT_SLAVE(prev), next); + + head = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); + if (q == head) { + rcu_assign_pointer(master->slaves, next); + if (q == next) { + txq = netdev_get_tx_queue(master->dev, 0); + rcu_assign_pointer(master->slaves, NULL); + reset_master_queue = true; + } + } + skb_queue_purge(&dat->q); + break; + } while (prev != rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock))); } + spin_unlock_bh(&master->slaves_lock); + + if (reset_master_queue) + dev_reset_queue(master->dev, txq, NULL); } static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, @@ -168,6 +186,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, struct net_device *dev = qdisc_dev(sch); struct teql_master *m = (struct teql_master *)sch->ops; struct teql_sched_data *q = qdisc_priv(sch); + struct Qdisc *first; if (dev->hard_header_len > m->dev->hard_header_len) return -EINVAL; @@ -184,7 +203,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, skb_queue_head_init(&q->q); - if (m->slaves) { + spin_lock_bh(&m->slaves_lock); + first = rcu_dereference_protected(m->slaves, lockdep_is_held(&m->slaves_lock)); + if (first) { if (m->dev->flags & IFF_UP) { if ((m->dev->flags & IFF_POINTOPOINT && !(dev->flags & IFF_POINTOPOINT)) || @@ -192,8 +213,10 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, !(dev->flags & IFF_BROADCAST)) || (m->dev->flags & IFF_MULTICAST && !(dev->flags & IFF_MULTICAST)) || - dev->mtu < m->dev->mtu) + dev->mtu < m->dev->mtu) { + spin_unlock_bh(&m->slaves_lock); return -EINVAL; + } } else { if (!(dev->flags&IFF_POINTOPOINT)) m->dev->flags &= ~IFF_POINTOPOINT; @@ -204,14 +227,17 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, if (dev->mtu < m->dev->mtu) m->dev->mtu = dev->mtu; } - q->next = NEXT_SLAVE(m->slaves); - NEXT_SLAVE(m->slaves) = sch; + rcu_assign_pointer(q->next, + rcu_dereference_protected(NEXT_SLAVE(first), + lockdep_is_held(&m->slaves_lock))); + rcu_assign_pointer(NEXT_SLAVE(first), sch); } else { - q->next = sch; - m->slaves = sch; + rcu_assign_pointer(q->next, sch); + rcu_assign_pointer(m->slaves, sch); m->dev->mtu = dev->mtu; m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK); } + spin_unlock_bh(&m->slaves_lock); return 0; } @@ -285,7 +311,9 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - start = master->slaves; + rcu_read_lock_bh(); + + start = rcu_dereference_bh(master->slaves); restart: nores = 0; @@ -317,10 +345,17 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; + rcu_read_unlock_bh(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -329,14 +364,21 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) busy = 1; break; case 1: - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); + rcu_read_unlock_bh(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = NEXT_SLAVE(q)) != start); + } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; @@ -345,29 +387,32 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) if (busy) { netif_stop_queue(dev); + rcu_read_unlock_bh(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; + rcu_read_unlock_bh(); dev_kfree_skb(skb); return NETDEV_TX_OK; } static int teql_master_open(struct net_device *dev) { - struct Qdisc *q; + struct Qdisc *q, *first; struct teql_master *m = netdev_priv(dev); int mtu = 0xFFFE; unsigned int flags = IFF_NOARP | IFF_MULTICAST; - if (m->slaves == NULL) + first = rtnl_dereference(m->slaves); + if (!first) return -EUNATCH; flags = FMASK; - q = m->slaves; + q = first; do { struct net_device *slave = qdisc_dev(q); @@ -389,7 +434,7 @@ static int teql_master_open(struct net_device *dev) flags &= ~IFF_BROADCAST; if (!(slave->flags&IFF_MULTICAST)) flags &= ~IFF_MULTICAST; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); m->dev->mtu = mtu; m->dev->flags = (m->dev->flags&~FMASK) | flags; @@ -417,14 +462,15 @@ static void teql_master_stats64(struct net_device *dev, static int teql_master_mtu(struct net_device *dev, int new_mtu) { struct teql_master *m = netdev_priv(dev); - struct Qdisc *q; + struct Qdisc *q, *first; - q = m->slaves; + first = rtnl_dereference(m->slaves); + q = first; if (q) { do { if (new_mtu > qdisc_dev(q)->mtu) return -EINVAL; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); } WRITE_ONCE(dev->mtu, new_mtu); @@ -444,6 +490,7 @@ static __init void teql_master_setup(struct net_device *dev) struct teql_master *master = netdev_priv(dev); struct Qdisc_ops *ops = &master->qops; + spin_lock_init(&master->slaves_lock); master->dev = dev; ops->priv_size = sizeof(struct teql_sched_data); From b4b3458ef88df4798632619f018791d4344bcd92 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 29 Jun 2026 10:21:17 +0300 Subject: [PATCH 1588/3297] bridge: stp: Fix a potential use-after-free when deleting a bridge [ Upstream commit 2a00517db8de4be7df3d483b215c5544fb30a191 ] The three STP timers are not supposed to be armed while the bridge is administratively down. They are synchronously deactivated when the bridge is put administratively down and the various call sites check for 'IFF_UP' before arming them. This check is missing from br_topology_change_detection() and it is possible to engineer a situation in which the topology change timer is armed while the bridge is administratively down, resulting in a use-after-free [1] when the bridge is deleted. Fix by adding the missing check and for good measures synchronously shutdown the three timers when the bridge is deleted. [1] ODEBUG: free active (active state 0) object: ffff88811662b9b0 object type: timer_list hint: br_topology_change_timer_expired (net/bridge/br_stp_timer.c:120) WARNING: lib/debugobjects.c:629 at debug_print_object+0x1bc/0x450, CPU#9: ip/359 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Noam Rathaus Reported-by: Neil Young Acked-by: Nikolay Aleksandrov Signed-off-by: Ido Schimmel Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260629072117.497959-1-idosch@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/bridge/br_if.c | 3 +++ net/bridge/br_stp.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index ca3a637d7cca7..5bcd1acc5da9b 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -391,6 +391,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) br_fdb_delete_by_port(br, NULL, 0, 1); + timer_shutdown_sync(&br->hello_timer); + timer_shutdown_sync(&br->topology_change_timer); + timer_shutdown_sync(&br->tcn_timer); cancel_delayed_work_sync(&br->gc_work); br_sysfs_delbr(br->dev); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 024210f954683..76a2df165ffc8 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -371,7 +371,8 @@ void br_topology_change_detection(struct net_bridge *br) { int isroot = br_is_root_bridge(br); - if (br->stp_enabled != BR_KERNEL_STP) + if (br->stp_enabled != BR_KERNEL_STP || + !(br->dev->flags & IFF_UP)) return; br_info(br, "topology change detected, %s\n", From dd0b2976b7c0f4ea806855ed19ffad967d36610e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:30 +0200 Subject: [PATCH 1589/3297] drm/panthor: Fix potential invalid pointer deref in group_process_tiler_oom() [ Upstream commit b39436d0ba1571dbcda69d20ec567344b3eecfc7 ] If heaps is an ERR_PTR(), panthor_heap_pool_put() will deref an invalid pointer. Make sure we set it to NULL in that case. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v5-0-8836a74e0ef9@collabora.com?part=2 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-4-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 6930053009d5c..749fe34b06db2 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1488,7 +1488,10 @@ static int group_process_tiler_oom(struct panthor_group *group, u32 cs_id) if (unlikely(csg_id < 0)) return 0; - if (IS_ERR(heaps) || frag_end > vt_end || vt_end >= vt_start) { + if (IS_ERR(heaps)) { + ret = -EINVAL; + heaps = NULL; + } else if (frag_end > vt_end || vt_end >= vt_start) { ret = -EINVAL; } else { /* We do the allocation without holding the scheduler lock to avoid From 1497a438ea34a0bacb78c1ac8563c73d9ab02141 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:32 +0200 Subject: [PATCH 1590/3297] drm/panthor: Don't overrule pending immediate ticks in sched_resume_tick() [ Upstream commit 6fec8b473497b7f32e604a6dd92b32b0889af3e8 ] We schedule immediate ticks when we need to process events on CSGs, but those immediate ticks don't change the resched_target because we want the other groups to stay scheduled for the remaining of the GPU timeslot they were given. Make sure these immediate ticks don't get overruled by a sched_queue_delayed_work() that would delay the tick execution. Fixes: 99820b4b7e50 ("drm/panthor: Make sure we resume the tick when new jobs are submitted") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260625-panthor-signal-from-irq-v4-0-3d2908912afa@collabora.com?part=9 Signed-off-by: Boris Brezillon Reviewed-by: Karunika Choo Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-6-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 749fe34b06db2..ff6fb8e6c38e9 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -2588,7 +2588,14 @@ static void sched_resume_tick(struct panthor_device *ptdev) else delay_jiffies = 0; - sched_queue_delayed_work(sched, tick, delay_jiffies); + /* We schedule immediate ticks when we need to process events on CSGs, + * but those don't change the resched_target because we want the other + * groups to stay scheduled for the remaining of the GPU timeslot they + * were given. Make sure those immediate ticks don't get overruled by + * a sched_queue_delayed_work() that would delay the tick execution. + */ + if (!delayed_work_pending(&sched->tick_work)) + sched_queue_delayed_work(sched, tick, delay_jiffies); } static void group_schedule_locked(struct panthor_group *group, u32 queue_mask) From a9d098b346db561ec3ab24d6b1adfc4aec4c728f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:35 +0200 Subject: [PATCH 1591/3297] drm/panthor: Fix a leak when a group is evicted before the tiler OOM is serviced [ Upstream commit 6efeb9ddb4fbf5ac30aff03e8f09ffbdf966abd0 ] A group ref is tied to the pending tiler_oom_work, so we need to release it if the cancel was effective. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260623-panthor-signal-from-irq-v3-0-2ece396f8ee0@collabora.com?part=7 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-9-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index ff6fb8e6c38e9..e329e1a4fc0b9 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1039,7 +1039,8 @@ group_unbind_locked(struct panthor_group *group) /* Tiler OOM events will be re-issued next time the group is scheduled. */ atomic_set(&group->tiler_oom, 0); - cancel_work(&group->tiler_oom_work); + if (cancel_work(&group->tiler_oom_work)) + group_put(group); for (u32 i = 0; i < group->queue_count; i++) group->queues[i]->doorbell_id = -1; From acbf1ecc22f3c33e9e3c33fae70696bf0eb7b928 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 25 Jun 2026 14:40:36 +0200 Subject: [PATCH 1592/3297] drm/panthor: Interrupt group start/resumption if group_bind_locked() fails [ Upstream commit 1f27cef1f41dac0bd254d8741766f189936c9880 ] group_bind_locked() can fail if the MMU block is stuck. This is normally a reset situation, but by the time we reset the GPU, we might have tried to resume a group that's not resident, which will probably trip out the FW. So let's avoid that by bailing out when group_bind_locked() returns an error. We don't even try to start more groups because the GPU will be reset anyway. Fixes: de8548813824 ("drm/panthor: Add the scheduler logical block") Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260623-panthor-signal-from-irq-v3-0-2ece396f8ee0@collabora.com?part=7 Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260625-panthor-misc-fixes-v1-10-b67ed973fea6@collabora.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_sched.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index e329e1a4fc0b9..85e24ca474484 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -2289,7 +2289,13 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id); csg_slot = &sched->csg_slots[csg_id]; - group_bind_locked(group, csg_id); + ret = group_bind_locked(group, csg_id); + if (ret) { + panthor_device_schedule_reset(ptdev); + ctx->csg_upd_failed_mask |= BIT(csg_id); + return; + } + csg_slot_prog_locked(ptdev, csg_id, new_csg_prio--); csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id, group->state == PANTHOR_CS_GROUP_SUSPENDED ? From 3b51d6f07a19996703c5a2d770ac72fb7538ebe3 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 1 Jun 2026 13:07:46 -0400 Subject: [PATCH 1593/3297] tracing/eprobes: Allow use of BTF names to dereference pointers [ Upstream commit 69efd863a78584b9416ed6be0e1e7349124b4a00 ] Add syntax to the parsing of eprobes to be able to typecast a trace event field that is a pointer to a structure. Currently, a dereference must be a number, where the user has to figure out manually the offset of a member of a structure that they want to dereference. But for event probes that records a field that happens to be a pointer to a structure, it cannot dereference these values with BTF naming, but must use numerical offsets. For example, to find out what device a sk_buff is pointing to in the net_dev_xmit trace event, one must first use gdb to find the offsets of the members of the structures: (gdb) p &((struct sk_buff *)0)->dev $1 = (struct net_device **) 0x10 (gdb) p &((struct net_device *)0)->name $2 = (char (*)[16]) 0x118 And then use the raw numbers to dereference: # echo 'e:xmit net.net_dev_xmit +0x118(+0x10($skbaddr)):string' >> dynamic_events If BTF is in the kernel, then instead, the skbaddr can be typecast to sk_buff and use the normal dereference logic. # echo 'e:xmit net.net_dev_xmit (sk_buff)skbaddr->dev->name:string' >> dynamic_events # echo 1 > events/eprobes/xmit/enable # cat trace [..] sshd-session-1022 [000] b..2. 860.249343: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.250061: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.250142: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.263553: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.283820: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.302716: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.322905: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.342828: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.362268: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.382335: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.400856: xmit: (net.net_dev_xmit) arg1="enp7s0" sshd-session-1022 [000] b..2. 860.419893: xmit: (net.net_dev_xmit) arg1="enp7s0" The syntax is simply: (STRUCT)(FIELD)->MEMBER[->MEMBER..] Also add comments around the #else and #endif of #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS to know what they are for. Link: https://lore.kernel.org/all/20260601130746.2139d926@gandalf.local.home/ Signed-off-by: Steven Rostedt (Google) Signed-off-by: Masami Hiramatsu (Google) Stable-dep-of: 251a8fe1b9ae ("tracing/probes: Remove WARN_ON_ONCE from parse_btf_arg") Signed-off-by: Sasha Levin --- Documentation/trace/eprobetrace.rst | 4 + kernel/trace/trace_probe.c | 173 +++++++++++++++++++++++----- kernel/trace/trace_probe.h | 5 +- 3 files changed, 154 insertions(+), 28 deletions(-) diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst index 89b5157cfab86..fe3602540569a 100644 --- a/Documentation/trace/eprobetrace.rst +++ b/Documentation/trace/eprobetrace.rst @@ -46,6 +46,10 @@ Synopsis of eprobe_events (x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char", "string", "ustring", "symbol", "symstr" and "bitfield" are supported. + (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to + a pointer to STRUCT and then derference the pointer defined by + ->MEMBER. Note that when this is used, the FIELD name does not + need to be prefixed with a '$'. Types ----- diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 25e8f5408efc8..7ed89e8af6f87 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -331,6 +331,23 @@ static int parse_trace_event_arg(char *arg, struct fetch_insn *code, return -ENOENT; } +static int parse_trace_event(char *arg, struct fetch_insn *code, + struct traceprobe_parse_context *ctx) +{ + int ret; + + if (code->data) + return -EFAULT; + ret = parse_trace_event_arg(arg, code, ctx); + if (!ret) + return 0; + if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { + code->op = FETCH_OP_COMM; + return 0; + } + return -EINVAL; +} + #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS static u32 btf_type_int(const struct btf_type *t) @@ -375,11 +392,16 @@ static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type) && BTF_INT_BITS(intdata) == 8; } +static struct btf *ctx_btf(struct traceprobe_parse_context *ctx) +{ + return ctx->struct_btf ? : ctx->btf; +} + static int check_prepare_btf_string_fetch(char *typename, struct fetch_insn **pcode, struct traceprobe_parse_context *ctx) { - struct btf *btf = ctx->btf; + struct btf *btf = ctx_btf(ctx); if (!btf || !ctx->last_type) return 0; @@ -505,6 +527,15 @@ static int query_btf_context(struct traceprobe_parse_context *ctx) return 0; } +static void clear_struct_btf(struct traceprobe_parse_context *ctx) +{ + if (ctx->struct_btf) { + btf_put(ctx->struct_btf); + ctx->struct_btf = NULL; + ctx->last_struct = NULL; + } +} + static void clear_btf_context(struct traceprobe_parse_context *ctx) { if (ctx->btf) { @@ -553,22 +584,29 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, struct fetch_insn *code = *pcode; const struct btf_member *field; u32 bitoffs, anon_offs; + bool is_struct = ctx->struct_btf != NULL; + struct btf *btf = ctx_btf(ctx); char *next; int is_ptr; s32 tid; do { - /* Outer loop for solving arrow operator ('->') */ - if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) { - trace_probe_log_err(ctx->offset, NO_PTR_STRCT); - return -EINVAL; - } - /* Convert a struct pointer type to a struct type */ - type = btf_type_skip_modifiers(ctx->btf, type->type, &tid); - if (!type) { - trace_probe_log_err(ctx->offset, BAD_BTF_TID); - return -EINVAL; + if (!is_struct) { + /* Outer loop for solving arrow operator ('->') */ + if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) { + trace_probe_log_err(ctx->offset, NO_PTR_STRCT); + return -EINVAL; + } + + /* Convert a struct pointer type to a struct type */ + type = btf_type_skip_modifiers(btf, type->type, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } } + /* Only the first type can skip being a pointer */ + is_struct = false; bitoffs = 0; do { @@ -579,7 +617,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, return is_ptr; anon_offs = 0; - field = btf_find_struct_member(ctx->btf, type, fieldname, + field = btf_find_struct_member(btf, type, fieldname, &anon_offs); if (IS_ERR(field)) { trace_probe_log_err(ctx->offset, BAD_BTF_TID); @@ -601,7 +639,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, ctx->last_bitsize = 0; } - type = btf_type_skip_modifiers(ctx->btf, field->type, &tid); + type = btf_type_skip_modifiers(btf, field->type, &tid); if (!type) { trace_probe_log_err(ctx->offset, BAD_BTF_TID); return -EINVAL; @@ -639,7 +677,7 @@ static int parse_btf_arg(char *varname, int i, is_ptr, ret; u32 tid; - if (WARN_ON_ONCE(!ctx->funcname)) + if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))) return -EINVAL; is_ptr = split_next_field(varname, &field, ctx); @@ -652,6 +690,19 @@ static int parse_btf_arg(char *varname, return -EOPNOTSUPP; } + if (ctx->flags & TPARG_FL_TEVENT) { + ret = parse_trace_event(varname, code, ctx); + if (ret < 0) { + trace_probe_log_err(ctx->offset, BAD_ATTACH_ARG); + return ret; + } + /* TEVENT is only here via a typecast */ + if (WARN_ON_ONCE(ctx->struct_btf == NULL)) + return -EINVAL; + type = ctx->last_struct; + goto found_type; + } + if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) { code->op = FETCH_OP_RETVAL; /* Check whether the function return type is not void */ @@ -708,6 +759,7 @@ static int parse_btf_arg(char *varname, found: type = btf_type_skip_modifiers(ctx->btf, tid, &tid); +found_type: if (!type) { trace_probe_log_err(ctx->offset, BAD_BTF_TID); return -EINVAL; @@ -726,7 +778,7 @@ static int parse_btf_arg(char *varname, static const struct fetch_type *find_fetch_type_from_btf_type( struct traceprobe_parse_context *ctx) { - struct btf *btf = ctx->btf; + struct btf *btf = ctx_btf(ctx); const char *typestr = NULL; if (btf && ctx->last_type) @@ -757,7 +809,67 @@ static int parse_btf_bitfield(struct fetch_insn **pcode, return 0; } -#else +static int query_btf_struct(const char *sname, struct traceprobe_parse_context *ctx) +{ + struct btf *btf = NULL; + int id; + + /* A struct_btf should only be used by a single argument */ + if (WARN_ON_ONCE(ctx->struct_btf)) { + btf_put(ctx->struct_btf); + ctx->struct_btf = NULL; + } + + id = bpf_find_btf_id(sname, BTF_KIND_STRUCT, &btf); + if (id < 0) + return id; + ctx->struct_btf = btf; + ctx->last_struct = btf_type_by_id(ctx->struct_btf, id); + return 0; +} + +static int handle_typecast(char *arg, struct fetch_insn **pcode, + struct fetch_insn *end, + struct traceprobe_parse_context *ctx) +{ + char *tmp; + int ret; + + /* Currently this only works for eprobes */ + if (!(ctx->flags & TPARG_FL_TEVENT)) { + trace_probe_log_err(ctx->offset, TYPECAST_NOT_EVENT); + return -EINVAL; + } + + tmp = strchr(arg, ')'); + if (!tmp) { + trace_probe_log_err(ctx->offset + strlen(arg), + DEREF_OPEN_BRACE); + return -EINVAL; + } + *tmp = '\0'; + ret = query_btf_struct(arg + 1, ctx); + *tmp = ')'; + + if (ret < 0) { + trace_probe_log_err(ctx->offset + 1, NO_PTR_STRCT); + return -EINVAL; + } + + tmp++; + + ctx->offset += tmp - arg; + ret = parse_btf_arg(tmp, pcode, end, ctx); + return ret; +} + +#else /* !CONFIG_PROBE_EVENTS_BTF_ARGS */ + +static void clear_struct_btf(struct traceprobe_parse_context *ctx) +{ + ctx->struct_btf = NULL; +} + static void clear_btf_context(struct traceprobe_parse_context *ctx) { ctx->btf = NULL; @@ -793,7 +905,15 @@ static int check_prepare_btf_string_fetch(char *typename, return 0; } -#endif +static int handle_typecast(char *arg, struct fetch_insn **pcode, + struct fetch_insn *end, + struct traceprobe_parse_context *ctx) +{ + trace_probe_log_err(ctx->offset, NOSUP_BTFARG); + return -EOPNOTSUPP; +} + +#endif /* CONFIG_PROBE_EVENTS_BTF_ARGS */ #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API @@ -953,16 +1073,9 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, int len; if (ctx->flags & TPARG_FL_TEVENT) { - if (code->data) - return -EFAULT; - ret = parse_trace_event_arg(arg, code, ctx); - if (!ret) - return 0; - if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { - code->op = FETCH_OP_COMM; - return 0; - } - goto inval; + if (parse_trace_event(arg, code, ctx) < 0) + goto inval; + return 0; } if (str_has_prefix(arg, "retval")) { @@ -1229,6 +1342,9 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = FETCH_OP_IMM; } break; + case '(': + ret = handle_typecast(arg, pcode, end, ctx); + break; default: if (isalpha(arg[0]) || arg[0] == '_') { /* BTF variable */ if (!tparg_is_function_entry(ctx->flags) && @@ -1561,6 +1677,9 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, } kfree(tmp); + /* struct_btf should not be passed to other arguments */ + clear_struct_btf(ctx); + return ret; } diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 37447784a8fb4..d5ea914e01b34 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -422,7 +422,9 @@ struct traceprobe_parse_context { const struct btf_param *params; /* Parameter of the function */ s32 nr_params; /* The number of the parameters */ struct btf *btf; /* The BTF to be used */ + struct btf *struct_btf; /* The BTF to be used for structs */ const struct btf_type *last_type; /* Saved type */ + const struct btf_type *last_struct; /* Saved structure */ u32 last_bitoffs; /* Saved bitoffs */ u32 last_bitsize; /* Saved bitsize */ struct trace_probe *tp; @@ -563,7 +565,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"),\ C(TOO_MANY_ARGS, "Too many arguments are specified"), \ C(TOO_MANY_EARGS, "Too many entry arguments specified"), \ - C(EVENT_TOO_BIG, "Event too big (too many fields?)"), + C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \ + C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), #undef C #define C(a, b) TP_ERR_##a From f148f86c65b8f9ef2466ad8506e721749e4957fb Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:46 +0900 Subject: [PATCH 1594/3297] tracing/probes: Remove WARN_ON_ONCE from parse_btf_arg [ Upstream commit 251a8fe1b9aedccd298b77bc28426d564c5a923f ] Sashiko found that user can cause this WARN_ON_ONCE() easily with adding a kprobe event based on a raw address with BTF parameter. Since this is not an unexpected condition, remove the WARN_ON_ONCE(). Link: https://lore.kernel.org/all/178177265367.2059927.13789953014706792126.stgit@mhiramat.tok.corp.google.com/ Link: https://sashiko.dev/#/patchset/178165816303.269421.7302603996990753309.stgit%40devnote2 Reported-by: Sashiko Fixes: b576e09701c7 ("tracing/probes: Support function parameters if BTF is available") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 7ed89e8af6f87..2a080eda8df38 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -677,7 +677,7 @@ static int parse_btf_arg(char *varname, int i, is_ptr, ret; u32 tid; - if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))) + if (!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT)) return -EINVAL; is_ptr = split_next_field(varname, &field, ctx); From e0881f5cc4d7130c5a70e96d37111f613ff06fc2 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:46 +0900 Subject: [PATCH 1595/3297] tracing/events: Fix to check the simple_tsk_fn creation [ Upstream commit cda1fbfc5313bb90daa271d45eea4a8d317a8544 ] Sashiko pointed that this sample code does not correctly handle the failure of thread creation because kthread_run() can return -errno. Check the simple_tsk_fn is correctly initialized (created) or not. Link: https://lore.kernel.org/all/178165817322.269421.3992299509400184196.stgit@devnote2/ Link: https://sashiko.dev/#/patchset/178092865666.163648.10457567771536160909.stgit%40devnote2 Fixes: 9cfe06f8cd5c ("tracing/events: add trace-events-sample") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- samples/trace_events/trace-events-sample.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c index ecc7db237f2ef..0b7a6efdb247a 100644 --- a/samples/trace_events/trace-events-sample.c +++ b/samples/trace_events/trace-events-sample.c @@ -107,6 +107,10 @@ int foo_bar_reg(void) * for consistency sake, we still take the thread_mutex. */ simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn"); + if (IS_ERR_OR_NULL(simple_tsk_fn)) { + pr_err("Failed to create simple_thread_fn\n"); + simple_tsk_fn = NULL; + } out: mutex_unlock(&thread_mutex); return 0; From 898cb5a7c4154478440b6886d387233df3597df0 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 25 Jun 2026 08:34:46 +0900 Subject: [PATCH 1596/3297] tracing: eprobe: read the complete FILTER_PTR_STRING pointer [ Upstream commit 206b25c09080cc20fd4c2bea12d59df4b7ba2121 ] For a char * element in an event, the FILTER_PTR_STRING filter type is used. When the event occurs, a pointer is stored in the ringbuffer. If an eprobe references such a char * element of a "base event", the stored pointer is truncated when it's read from the ringbuffer. $ cd /sys/kernel/tracing $ echo 'e rcu.rcu_utilization $s:x64 $s:string' > dynamic_events $ echo 1 > tracing_on $ echo 1 > events/eprobes/enable $ sleep 1 $ echo 0 > events/eprobes/enable $ cat trace -0 ...: (rcu.rcu_utilization) arg1=0x4f arg2=(fault) -0 ...: (rcu.rcu_utilization) arg1=0x2 arg2=(fault) The problem is in get_event_field val = (unsigned long)(*(char *)addr); addr points to the position in the ringbuffer where the pointer was stored. The assignment reads only the lowest byte of the pointer. Fix the cast to read the whole pointer. The output of the test above is now -0 ... arg1=0xffffffff81c7d3f3 arg2="Start scheduler-tick" -0 ... arg1=0xffffffff81c57340 arg2="End scheduler-tick" Link: https://lore.kernel.org/all/20260620145339.3234726-1-martin@kaiser.cx/ Fixes: f04dec93466a ("tracing/eprobes: Fix reading of string fields") Signed-off-by: Martin Kaiser Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_eprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index a1d402124836a..7697ce22337b6 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -315,7 +315,7 @@ get_event_field(struct fetch_insn *code, void *rec) val = (unsigned long)addr; break; case FILTER_PTR_STRING: - val = (unsigned long)(*(char *)addr); + val = *(unsigned long *)addr; break; default: WARN_ON_ONCE(1); From 62988204162fc382cfc7d9a185d40a751261e3a3 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Thu, 25 Jun 2026 08:34:48 +0900 Subject: [PATCH 1597/3297] tracing/fprobe: Fix NULL pointer dereference in fprobe_fgraph_entry() [ Upstream commit 367c49d6e283c17b56a31e7a8d964a079244264c ] fprobe_fgraph_entry() sizes a shadow-stack reservation in one walk of the per-ip fprobe list and fills it in a second walk, both under rcu_read_lock() only. A fprobe registered on an already-live ip can become visible between the two walks, so the fill walk processes an exit_handler the sizing walk did not count and used runs past reserved_words. If the sizing walk counted nothing, fgraph_data is NULL and the first write_fprobe_header() faults: Oops: general protection fault, probably for non-canonical address ... KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:fprobe_fgraph_entry+0xa38/0xf10 kernel/trace/fprobe.c:167 Call Trace: function_graph_enter_regs+0x44c/0xa10 kernel/trace/fgraph.c:677 ftrace_graph_func+0xc5/0x140 arch/x86/kernel/ftrace.c:671 __kernel_text_address+0x9/0x40 kernel/extable.c:78 arch_stack_walk+0x117/0x170 arch/x86/kernel/stacktrace.c:26 kmem_cache_free+0x188/0x580 mm/slub.c:6378 tcp_data_queue+0x18d/0x6550 net/ipv4/tcp_input.c:5590 [...] The list cannot be frozen across the two walks, so skip a node that does not fit the reservation and count it as missed. Link: https://lore.kernel.org/all/20260619184425.3824774-1-rhkrqnwk98@gmail.com/ Fixes: 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer") Signed-off-by: Sechang Lim Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/fprobe.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index b9346f4efa6dc..01f98f8a86e60 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -474,6 +474,16 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops continue; data_size = fp->entry_data_size; + /* + * The list may have grown since it was sized, so this node + * may not fit. Skip it as missed rather than overrun the + * reservation. + */ + if (fp->exit_handler && + used + FPROBE_HEADER_SIZE_IN_LONG + SIZE_IN_LONG(data_size) > reserved_words) { + fp->nmissed++; + continue; + } if (data_size && fp->exit_handler) data = fgraph_data + used + FPROBE_HEADER_SIZE_IN_LONG; else From f0069a262bd41fe4b7dfc21c018cadc605b97b66 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:48 +0900 Subject: [PATCH 1598/3297] tracing/probes: Make the $ prefix mandatory for comm access [ Upstream commit a369299c3f785cf556bbef2de2db0aa2d294c4c9 ] Since $comm or $COMM are not event field but special fetcharg variables to access current->comm, It should not be accessed without '$' prefix even with typecast. Link: https://lore.kernel.org/all/178231209724.732967.12049805699091810641.stgit@devnote2/ Fixes: 69efd863a785 ("tracing/eprobes: Allow use of BTF names to dereference pointers") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/trace_probe.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 2a080eda8df38..a7a73eb6f011e 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -341,10 +341,6 @@ static int parse_trace_event(char *arg, struct fetch_insn *code, ret = parse_trace_event_arg(arg, code, ctx); if (!ret) return 0; - if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { - code->op = FETCH_OP_COMM; - return 0; - } return -EINVAL; } @@ -1073,8 +1069,14 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, int len; if (ctx->flags & TPARG_FL_TEVENT) { - if (parse_trace_event(arg, code, ctx) < 0) + if (parse_trace_event(arg, code, ctx) < 0) { + /* 'comm' should be checked after field parsing. */ + if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { + code->op = FETCH_OP_COMM; + return 0; + } goto inval; + } return 0; } From c5d75800539bca6f42b4d213dfe5158879b2fe81 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 28 Jun 2026 18:07:23 -0400 Subject: [PATCH 1599/3297] irqchip/gic-v3-its: Fix OF node reference leak [ Upstream commit 2e1368a9d61bdf5502ddade004f223a5831c5b8c ] of_get_cpu_node() returns a referenced device node. In its_cpu_init_collection(), the Cavium 23144 workaround only uses the node to compare the CPU NUMA node, but the reference is never dropped. Use the device_node cleanup helper for the CPU node reference so it is released when leaving the workaround block, including the NUMA mismatch return path. Fixes: fbf8f40e1658 ("irqchip/gicv3-its: numa: Enable workaround for Cavium thunderx erratum 23144") Signed-off-by: Yuho Choi Signed-off-by: Thomas Gleixner Reviewed-by: Zenghui Yu (Huawei) Acked-by: Marc Zyngier Signed-off-by: Sasha Levin --- drivers/irqchip/irq-gic-v3-its.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 23158fc8d3921..a1661657391d6 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -3291,11 +3291,9 @@ static void its_cpu_init_collection(struct its_node *its) /* avoid cross node collections and its mapping */ if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { - struct device_node *cpu_node; + struct device_node *cpu_node __free(device_node) = of_get_cpu_node(cpu, NULL); - cpu_node = of_get_cpu_node(cpu, NULL); - if (its->numa_node != NUMA_NO_NODE && - its->numa_node != of_node_to_nid(cpu_node)) + if (its->numa_node != NUMA_NO_NODE && its->numa_node != of_node_to_nid(cpu_node)) return; } From 4e8d498d32b6c67d73bb8b317ff316ff37897a3e Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 23 Jun 2026 09:52:11 +0800 Subject: [PATCH 1600/3297] irqchip/ts4800: Fix missing chained handler cleanup on remove [ Upstream commit 98bf7e54cec07d514b3575c11896a8b12d50ecc4 ] The driver installs a chained handler for the parent interrupt during probe using irq_set_chained_handler_and_data(), but the remove function does not clear this handler. This leaves a dangling handler that may be called when the parent interrupt fires after the driver has been removed, potentially accessing freed memory and causing a kernel crash. Additionally, the parent_irq obtained via irq_of_parse_and_map() is not stored, making it inaccessible in the remove function. Moreover, interrupt mappings created during probe are not properly disposed. Fix this by: - Saving parent_irq in probe - Clearing the chained handler with NULL in ts4800_ic_remove() - Disposing all IRQ mappings before domain removal to prevent resource leaks Fixes: d01f8633d52e ("irqchip/ts4800: Add TS-4800 interrupt controller") Signed-off-by: Qingshuang Fu Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260623015211.109382-1-fffsqian@163.com Signed-off-by: Sasha Levin --- drivers/irqchip/irq-ts4800.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c index 1e236d5b75162..a68d261e1f6f9 100644 --- a/drivers/irqchip/irq-ts4800.c +++ b/drivers/irqchip/irq-ts4800.c @@ -28,6 +28,7 @@ struct ts4800_irq_data { void __iomem *base; struct platform_device *pdev; struct irq_domain *domain; + unsigned int parent_irq; }; static void ts4800_irq_mask(struct irq_data *d) @@ -134,6 +135,7 @@ static int ts4800_ic_probe(struct platform_device *pdev) irq_set_chained_handler_and_data(parent_irq, ts4800_ic_chained_handle_irq, data); + data->parent_irq = parent_irq; platform_set_drvdata(pdev, data); return 0; @@ -142,6 +144,14 @@ static int ts4800_ic_probe(struct platform_device *pdev) static void ts4800_ic_remove(struct platform_device *pdev) { struct ts4800_irq_data *data = platform_get_drvdata(pdev); + unsigned int hwirq; + + irq_set_chained_handler_and_data(data->parent_irq, NULL, NULL); + + for (hwirq = 0; hwirq < 8; hwirq++) + irq_dispose_mapping(irq_find_mapping(data->domain, hwirq)); + + irq_dispose_mapping(data->parent_irq); irq_domain_remove(data->domain); } From a8323fb2ab6cd6978f359daeed6688e0cadf32ba Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 29 Jun 2026 14:31:14 -0400 Subject: [PATCH 1601/3297] sctp: fix addr_wq_timer race in sctp_free_addr_wq() [ Upstream commit 976c19de0f22a857ba0112f39635f8fd7a257568 ] sctp_free_addr_wq() previously removed addr_wq_timer using timer_delete() while holding addr_wq_lock. However, timer_delete() does not guarantee that a currently running timer handler has completed. This allows a race with sctp_addr_wq_timeout_handler(), where the handler may still run after addr_waitq has been freed, acquire addr_wq_lock, and access freed memory, leading to a use-after-free. Fix this by calling timer_shutdown_sync() before taking addr_wq_lock. This guarantees that any in-flight timer handler has finished and prevents the timer from being re-armed during teardown, making subsequent cleanup safe. Fixes: 4db67e808640 ("sctp: Make the address lists per network namespace") Reported-by: Sashiko Signed-off-by: Xin Long Link: https://patch.msgid.link/5dc95f295bdb5c3f60e880dd9aa5112dc5c071cc.1782757874.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/protocol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 6ce58fc95ef51..bbeff25185b17 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -695,8 +695,9 @@ static void sctp_free_addr_wq(struct net *net) struct sctp_sockaddr_entry *addrw; struct sctp_sockaddr_entry *temp; + timer_shutdown_sync(&net->sctp.addr_wq_timer); + spin_lock_bh(&net->sctp.addr_wq_lock); - timer_delete(&net->sctp.addr_wq_timer); list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) { list_del(&addrw->list); kfree(addrw); From 124440df267dcdcc5bd4aac9888e2b3b80cf65fe Mon Sep 17 00:00:00 2001 From: Longjun Tang Date: Mon, 29 Jun 2026 10:42:30 +0800 Subject: [PATCH 1602/3297] virtio_net: disable cb when NAPI is busy-polled [ Upstream commit 1eb8fc67ca41db71c90866ff76c990d85247daef ] When busy-poll is active, napi_schedule_prep() returns false in virtqueue_napi_schedule(), so virtqueue_disable_cb() is skipped. The device may keep firing irqs until reaches virtqueue_napi_complete(). Under load (received == budget), it will lead to a large number of spurious interrupts. Fix it by disabling the callback at the virtnet_poll() entry. This keeps the callback off while we poll and it is re-enabled by virtqueue_napi_complete() when going idle. Fixes: ceef438d613f ("virtio_net: remove custom busy_poll") Acked-by: Michael S. Tsirkin Signed-off-by: Longjun Tang Link: https://patch.msgid.link/20260629024230.37325-1-lange_tang@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/virtio_net.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index dbdba0a42efe9..d2fe26bfaccd0 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -3128,6 +3128,9 @@ static int virtnet_poll(struct napi_struct *napi, int budget) unsigned int xdp_xmit = 0; bool napi_complete; + if (budget) + virtqueue_disable_cb(rq->vq); + virtnet_poll_cleantx(rq, budget); received = virtnet_receive(rq, budget, &xdp_xmit); From df9ffdceac053086f29f06b1f937824cb73f5584 Mon Sep 17 00:00:00 2001 From: Gleb Markov Date: Mon, 29 Jun 2026 16:08:54 +0300 Subject: [PATCH 1603/3297] cxgb4: Fix decode strings dump for T6 adapters [ Upstream commit 5d6dc22d62682d93f5f55f145ad792f2891de911 ] Depending on the value of chip_version, the correct decode set is selected. However, the subsequent matching with the t4 encoding type in the if-else block results in a reassignment, which leads to the loss of support for t6_decode as well as reinitializing of values t4_decode and t5_decode. The component history shows that the if-else block previously used for this purpose, as well as the execution order, was not affected by the change. Furthermore, it is suggested by the execution order that the scenario with overwriting and loss of support will be implemented. Delete the if-else block. Fixes: 6df397539cb0 ("cxgb4: Update correct encoding of SGE Ingress DMA States for T6 adapter") Signed-off-by: Gleb Markov Reviewed-by: Potnuri Bharat Teja Link: https://patch.msgid.link/20260629130856.1168-1-markov.gi@npc-ksb.ru Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 171750fad44f2..6871127427fad 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -6737,14 +6737,6 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) return; } - if (is_t4(adapter->params.chip)) { - sge_idma_decode = (const char **)t4_decode; - sge_idma_decode_nstates = ARRAY_SIZE(t4_decode); - } else { - sge_idma_decode = (const char **)t5_decode; - sge_idma_decode_nstates = ARRAY_SIZE(t5_decode); - } - if (state < sge_idma_decode_nstates) CH_WARN(adapter, "idma state %s\n", sge_idma_decode[state]); else From a03387e1f6251d754475b011ffc605b4412ecb67 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 29 Jun 2026 16:39:23 -0700 Subject: [PATCH 1604/3297] selftests: drv-net: tso: don't touch dangerous feature bits [ Upstream commit 2f7f2e311106cb838d3f3fb6ef25effdb3f8e366 ] query_nic_features() detects which offloads depend on tx-gso-partial by enabling everything, turning tx-gso-partial off, and seeing which active features drop out. Enabling all hw features is dangerous: we may end up enabling rx-fcs and loopback for example. For the ice driver we end up getting into problems with feature dependencies so the cleanup isn't successful either, and the test exits with rx-fcs and loopback enabled. Scope the feature probing just to segmentation bits. Fixes: 266b835e5e84 ("selftests: drv-net: tso: enable test cases based on hw_features") Reviewed-by: Pavan Chebbi Reviewed-by: Daniel Zahka Link: https://patch.msgid.link/20260629233923.2151144-1-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/drivers/net/hw/tso.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/tso.py b/tools/testing/selftests/drivers/net/hw/tso.py index 0998e68ebaf03..061259057fb83 100755 --- a/tools/testing/selftests/drivers/net/hw/tso.py +++ b/tools/testing/selftests/drivers/net/hw/tso.py @@ -184,28 +184,24 @@ def query_nic_features(cfg) -> None: cfg.wanted_features.add(f["name"]) cfg.hw_features = set() - hw_all_features_cmd = "" for f in features["hw"]["bits"]["bit"]: if f.get("value", False): - feature = f["name"] - cfg.hw_features.add(feature) - hw_all_features_cmd += f" {feature} on" - try: - ethtool(f"-K {cfg.ifname} {hw_all_features_cmd}") - except Exception as e: - ksft_pr(f"WARNING: failure enabling all hw features: {e}") - ksft_pr("partial gso feature detection may be impacted") + cfg.hw_features.add(f["name"]) # Check which features are supported via GSO partial cfg.partial_features = set() if 'tx-gso-partial' in cfg.hw_features: + seg_features = {f for f in cfg.hw_features if "segmentation" in f} + ethtool(f"-K {cfg.ifname} " + + " ".join(f"{f} on" for f in seg_features)) + ethtool(f"-K {cfg.ifname} tx-gso-partial off") no_partial = set() features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}}) for f in features["active"]["bits"]["bit"]: no_partial.add(f["name"]) - cfg.partial_features = cfg.hw_features - no_partial + cfg.partial_features = seg_features - no_partial ethtool(f"-K {cfg.ifname} tx-gso-partial on") restore_wanted_features(cfg) From 6b1304ce6cff058e495a2bf3d6462eb948518ebc Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Mon, 29 Jun 2026 15:41:06 +0000 Subject: [PATCH 1605/3297] net/sched: act_bpf: use rcu_dereference_bh() to read the filter [ Upstream commit adc49c7ba690c9b33b8392ec27397456b65d0893 ] tcf_bpf_act() can run from the tc egress path, which holds only rcu_read_lock_bh(), but reads prog->filter with rcu_dereference() and trips lockdep: WARNING: suspicious RCU usage net/sched/act_bpf.c:47 suspicious rcu_dereference_check() usage! 1 lock held by syz.2.1588/12756: #0: (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit net/core/dev.c:4792 tcf_bpf_act+0x6ae/0x940 net/sched/act_bpf.c:47 tcf_classify+0x6e4/0x1080 net/sched/cls_api.c:1860 sch_handle_egress net/core/dev.c:4545 [inline] __dev_queue_xmit+0x2185/0x2c00 net/core/dev.c:4808 packet_sendmsg+0x3dfa/0x5120 net/packet/af_packet.c:3114 The other tc actions and cls_bpf already use rcu_dereference_bh() here. Do the same. Fixes: 1f211a1b929c ("net, sched: add clsact qdisc") Signed-off-by: Sechang Lim Reviewed-by: Amery Hung Link: https://patch.msgid.link/20260629154112.1164986-1-rhkrqnwk98@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/act_bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index c2b5bc19e0911..284800b7d3c55 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -44,7 +44,7 @@ TC_INDIRECT_SCOPE int tcf_bpf_act(struct sk_buff *skb, tcf_lastuse_update(&prog->tcf_tm); bstats_update(this_cpu_ptr(prog->common.cpu_bstats), skb); - filter = rcu_dereference(prog->filter); + filter = rcu_dereference_bh(prog->filter); if (at_ingress) { __skb_push(skb, skb->mac_len); filter_res = bpf_prog_run_data_pointers(filter, skb); From d020e7f27bf65eecd3805404702f716b2b6d9e73 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Thu, 25 Jun 2026 14:48:07 +0000 Subject: [PATCH 1606/3297] ksmbd: reject undersized DACLs before parsing ACEs [ Upstream commit 60908f7ebcd9b6cde74ad5711fab0f49c7970949 ] parse_dacl() limits the attacker-controlled ACE count by comparing it with the number of minimal ACEs that fit in the DACL size. The DACL size field is 16 bits, but the expression subtracts sizeof(struct smb_acl). Because sizeof() is unsigned, a DACL size smaller than the ACL header underflows to a large size_t. A malicious client can reach this with: SMB2_SET_INFO (InfoType=SMB2_O_INFO_SECURITY) -> smb2_set_info_sec() -> set_info_sec() -> parse_sec_desc() -> parse_dacl() -> init_acl_state(..., 0xffff) -> init_acl_state(..., 0xffff) -> kmalloc_objs(..., 0xffff) Thus a malformed security descriptor can make num_aces pass the guard and drive large temporary ACL state and pointer-array allocations. Reject DACLs smaller than struct smb_acl before doing the subtraction, so the ACE count check cannot be bypassed by the underflow. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Signed-off-by: Haofeng Li Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smbacl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index f5864dd1dd5f1..b09bc8d9389a2 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -374,6 +374,7 @@ static void parse_dacl(struct mnt_idmap *idmap, { int i, ret; u16 num_aces = 0; + u16 dacl_size; unsigned int acl_size; char *acl_base; struct smb_ace **ppace; @@ -403,7 +404,11 @@ static void parse_dacl(struct mnt_idmap *idmap, if (num_aces <= 0) return; - if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / + dacl_size = le16_to_cpu(pdacl->size); + if (dacl_size < sizeof(struct smb_acl)) + return; + + if (num_aces > (dacl_size - sizeof(struct smb_acl)) / (offsetof(struct smb_ace, sid) + offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) return; From 5a5ac2852cd326529d02f778bc1aa6184701f4d7 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 26 Jun 2026 20:38:20 +0300 Subject: [PATCH 1607/3297] ksmbd: fix use-after-free of fp->owner.name in durable handle owner check [ Upstream commit 38637163501fd9e2f684b8cd275d0db5d79f37c6 ] Two concurrent SMB2 durable reconnects (DH2C/DHnC) on the same persistent_id race the fp->owner.name compare-read in ksmbd_vfs_compare_durable_owner() against the kfree() in ksmbd_reopen_durable_fd()'s reopen-success path. fp->owner.name is a standalone kstrdup() buffer whose lifetime is independent of the fp refcount, and the two sites share no lock: the compare reads the buffer while the reopen frees it, so the strcmp() can dereference freed memory. Commit 7ce4fc40018d ("ksmbd: fix durable reconnect double-bind race in ksmbd_reopen_durable_fd") made the fp->conn claim atomic under global_ft.lock (closing the owner.name double-free and the ksmbd_file write-UAF), but the compare-read versus reopen-free pair was left unserialized. BUG: KASAN: slab-use-after-free in strcmp+0x2c/0x80 Read of size 1 by task kworker strcmp ksmbd_vfs_compare_durable_owner smb2_check_durable_oplock smb2_open Freed by task kworker: kfree ksmbd_reopen_durable_fd smb2_open Allocated by task kworker: kstrdup session_fd_check smb2_session_logoff The buggy address belongs to the cache kmalloc-8 Serialize both sides of the race with fp->f_lock. The global durable file-table lock still protects the durable reconnect claim, but fp->owner.name is per-open state and does not need to block unrelated durable table lookups or reconnects. The teardown is left at its existing location after the reopen-success point so that an __open_id() rollback still retains owner.name for a later legitimate reconnect to verify. Fixes: 49110a8ce654 ("ksmbd: validate owner of durable handle on reconnect") Assisted-by: Henry (Claude):claude-opus-4 Signed-off-by: Gil Portnoy Co-developed-by: Namjae Jeon Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/vfs_cache.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index a1f59a8e16e28..fad426fe3d14d 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -980,16 +980,21 @@ void ksmbd_stop_durable_scavenger(void) static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, struct ksmbd_user *user) { + char *name; + if (!user) return -EINVAL; /* Duplicate the user name to ensure identity persistence */ - fp->owner.name = kstrdup(user->name, GFP_KERNEL); - if (!fp->owner.name) + name = kstrdup(user->name, GFP_KERNEL); + if (!name) return -ENOMEM; + spin_lock(&fp->f_lock); fp->owner.uid = user->uid; fp->owner.gid = user->gid; + fp->owner.name = name; + spin_unlock(&fp->f_lock); return 0; } @@ -1007,18 +1012,24 @@ static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, struct ksmbd_user *user) { - if (!user || !fp->owner.name) + bool ret = false; + + if (!user) return false; + spin_lock(&fp->f_lock); + if (!fp->owner.name) + goto out; + /* Check if the UID and GID match first (fast path) */ if (fp->owner.uid != user->uid || fp->owner.gid != user->gid) - return false; + goto out; /* Validate the account name to ensure the same SecurityContext */ - if (strcmp(fp->owner.name, user->name)) - return false; - - return true; + ret = (strcmp(fp->owner.name, user->name) == 0); +out: + spin_unlock(&fp->f_lock); + return ret; } static bool session_fd_check(struct ksmbd_tree_connect *tcon, @@ -1172,9 +1183,11 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } up_write(&ci->m_lock); + spin_lock(&fp->f_lock); fp->owner.uid = fp->owner.gid = 0; kfree(fp->owner.name); fp->owner.name = NULL; + spin_unlock(&fp->f_lock); return 0; } From 47120164c63d398eee897307352693cb95a54397 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 30 Jun 2026 17:51:48 +0300 Subject: [PATCH 1608/3297] gpio: timberdale: Return -ENOMEM on dynamic memory allocation in probe [ Upstream commit 8d7e62d5e9b2d2ff146f472a9215d7e29c7e2307 ] Out of memory situation on driver's probe is expected to be reported to the driver's framework with a proper -ENOMEM error code. Fixes: 35570ac6039e ("gpio: add GPIO driver for the Timberdale FPGA") Signed-off-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20260630145148.4081967-1-vz@kernel.org Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-timberdale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index f488939dd00a8..b77c9d16fd166 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -235,7 +235,7 @@ static int timbgpio_probe(struct platform_device *pdev) tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL); if (!tgpio) - return -EINVAL; + return -ENOMEM; tgpio->irq_base = pdata->irq_base; From a203f2c3892b19f2061fa2af2f3166c5894a0f59 Mon Sep 17 00:00:00 2001 From: Viacheslav Bocharov Date: Thu, 25 Jun 2026 14:57:18 +0300 Subject: [PATCH 1609/3297] pinctrl: meson: restore non-sleeping GPIO access [ Upstream commit 9777530157e7b82fd994327ff878c4245dadc931 ] Commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") set gpio_chip.can_sleep = true to work around gpio-shared-proxy holding a spinlock across a sleeping pinctrl config path. That locking bug is now fixed in the shared-proxy itself ("gpio: shared-proxy: always serialize with a sleeping mutex"), so the controller-wide workaround is no longer needed; the meson GPIO controller does not sleep. meson_gpio_get/set/direction_* access MMIO through regmap. The regmap_mmio bus uses fast I/O (spinlock) locking, so these value callbacks do not contain sleeping operations. Since gpio_chip.can_sleep describes the get/set value path, restore can_sleep = false. Marking the controller sleeping also broke atomic value consumers such as w1-gpio (1-Wire bitbang): w1_io.c runs its read time slot under local_irq_save() and uses the non-cansleep gpiod_set_value() / gpiod_get_value(), which with can_sleep=true trigger WARN_ON(can_sleep) in gpiolib on every transferred bit (from w1_gpio_write_bit() / w1_gpio_read_bit() via w1_reset_bus() and w1_search()). The printk and stack dump inside the IRQs-off, microsecond-scale time slot destroy the bit timing, so reset/presence detection and ROM search fail: the bus master registers but w1_master_slave_count stays at 0 and no devices are found. Verified on an Amlogic A113X board (DS18B20 on GPIOA_14): with can_sleep restored to false the warnings are gone and the sensor is detected and read again. This must not be applied or backported without the shared-proxy locking fix above; otherwise the original Khadas VIM3 splat returns on boards that genuinely share a meson GPIO. Fixes: 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping") Link: https://lore.kernel.org/all/20260105150509.56537-1-bartosz.golaszewski@oss.qualcomm.com/ Signed-off-by: Viacheslav Bocharov Acked-by: Linus Walleij Link: https://patch.msgid.link/20260625115718.1678991-3-v@baodeep.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/pinctrl/meson/pinctrl-meson.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 4507dc8b5563c..18295b15ecd9d 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -619,7 +619,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc) pc->chip.set = meson_gpio_set; pc->chip.base = -1; pc->chip.ngpio = pc->data->num_pins; - pc->chip.can_sleep = true; + pc->chip.can_sleep = false; ret = gpiochip_add_data(&pc->chip, pc); if (ret) { From fba8e250ce5f380037e10b988dfffe06ad983835 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 28 Jun 2026 13:48:47 +0000 Subject: [PATCH 1610/3297] net/sched: dualpi2: clear stale classification on filter miss [ Upstream commit bf83ee45874e9f071478bed39f9cf40cc741629f ] DualPI2 leaves previous classification state attached to an skb when filter classification returns no match. The enqueue path can then act on stale state from an earlier classification attempt. A filter miss should fall back to the default class without reusing old per-packet classification data. Initialize the classification result to CLASSIC before running the classifier. Explicit L4S, priority, and successful filter classification can still override that default. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/sch_dualpi2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 02d3c6c45aa3f..f20a798e8f36a 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -346,6 +346,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, struct tcf_proto *fl; int result; + cb->classified = DUALPI2_C_CLASSIC; + dualpi2_read_ect(skb); if (cb->ect & q->ecn_mask) { cb->classified = DUALPI2_C_L4S; @@ -359,10 +361,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, } fl = rcu_dereference_bh(q->tcf_filters); - if (!fl) { - cb->classified = DUALPI2_C_CLASSIC; + if (!fl) return NET_XMIT_SUCCESS; - } result = tcf_classify(skb, NULL, fl, &res, false); if (result >= 0) { From 374fd8122421f1729dcb5c68ab7c19fb8db3de57 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 29 Jun 2026 16:44:59 +0000 Subject: [PATCH 1611/3297] net/sched: hhf: clear heavy-hitter state on reset [ Upstream commit a225f8c20712713406ae47024b8df42deacddd4a ] HHF reset does not clear the classifier state used to identify heavy hitters. Packets after reset can therefore be scheduled using flow history from before the reset. The reset operation should return the qdisc to an empty state. Clear the heavy-hitter classifier tables when HHF is reset. Fixes: 10239edf86f1 ("net-qdisc-hhf: Heavy-Hitter Filter (HHF) qdisc") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- net/sched/sch_hhf.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 81c3a388c9437..e1c9d1edc8a99 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -461,12 +461,39 @@ static struct sk_buff *hhf_dequeue(struct Qdisc *sch) return skb; } +static void hhf_reset_classifier(struct hhf_sched_data *q) +{ + int i; + + if (!q->hh_flows) + return; + + for (i = 0; i < HH_FLOWS_CNT; i++) { + struct hh_flow_state *flow, *next; + struct list_head *head = &q->hh_flows[i]; + + list_for_each_entry_safe(flow, next, head, flowchain) { + list_del(&flow->flowchain); + kfree(flow); + } + } + WRITE_ONCE(q->hh_flows_current_cnt, 0); + + for (i = 0; i < HHF_ARRAYS_CNT; i++) { + if (q->hhf_valid_bits[i]) + bitmap_zero(q->hhf_valid_bits[i], HHF_ARRAYS_LEN); + } + q->hhf_arrays_reset_timestamp = hhf_time_stamp(); +} + static void hhf_reset(struct Qdisc *sch) { + struct hhf_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; while ((skb = hhf_dequeue(sch)) != NULL) rtnl_kfree_skbs(skb, skb); + hhf_reset_classifier(q); } static void hhf_destroy(struct Qdisc *sch) From a2038514e69371eb493083a6a897ed20fcbb8acb Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 15 Jun 2026 14:52:18 +0200 Subject: [PATCH 1612/3297] fs: refuse O_TMPFILE creation with an unmapped fsuid or fsgid [ Upstream commit 539dce1144651f7976fa418e618b0b574bf15eeb ] vfs_tmpfile() never checked that the caller's fsuid and fsgid map into the filesystem. On an idmapped mount whose idmapping does not cover the caller's fs{u,g}id, the ->tmpfile() instance initializes the new inode through inode_init_owner(), where mapped_fsuid()/mapped_fsgid() return INVALID_UID/INVALID_GID, and the tmpfile ends up owned by (uid_t)-1. Every other creation path already refuses this: may_o_create() (O_CREAT) and may_create_dentry() (mkdir, mknod, symlink, link) bail out with -EOVERFLOW via fsuidgid_has_mapping() precisely so that an object cannot be created with an owner the filesystem cannot represent. An O_TMPFILE is no exception: it is created I_LINKABLE and linkat(2) can splice it into the namespace afterwards, so the same guarantee must hold. Add the missing fsuidgid_has_mapping() check to vfs_tmpfile(). On a non-idmapped mount the caller's fs{u,g}id always map in the superblock's user namespace, so this is a no-op there and only takes effect on an idmapped mount that does not map the caller. It applies to every filesystem that sets FS_ALLOW_IDMAP and implements ->tmpfile() (tmpfs, ext4, btrfs, xfs, f2fs, ...), and to overlayfs, whose upper-layer tmpfile creation funnels through vfs_tmpfile() via backing_tmpfile_open(). Fixes: 8e5389132ab4 ("fs: introduce fsuidgid_has_mapping() helper") Link: https://patch.msgid.link/20260615-work-idmapped-tmpfile-v1-1-754a94d81f83@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/namei.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 7377020a2cba0..003c9a1b2f904 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4011,6 +4011,10 @@ int vfs_tmpfile(struct mnt_idmap *idmap, int error; int open_flag = file->f_flags; + /* A tmpfile is I_LINKABLE, so guard its owner like may_o_create(). */ + if (!fsuidgid_has_mapping(dir->i_sb, idmap)) + return -EOVERFLOW; + /* we want directory to be writable */ error = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); if (error) From 8530206911fd66ad739ca5ce95f1d069f3d42204 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 22 Jun 2026 10:08:36 +0100 Subject: [PATCH 1613/3297] afs: Fix error code in afs_extract_vl_addrs() [ Upstream commit 4897cb71d4ab1f7e1a214adb1e4b80176702368d ] The error codes on these paths are only set on the first iteration through the loop. Set the correct error code on every iteration. Fixes: 0a5143f2f89c ("afs: Implement VL server rotation") Signed-off-by: Dan Carpenter Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-3-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/vl_list.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index 9b1c20daac53b..b25830d040da0 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -93,7 +93,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, { struct afs_addr_list *alist; const u8 *b = *_b; - int ret = -EINVAL; + int ret; alist = afs_alloc_addrlist(nr_addrs); if (!alist) @@ -111,6 +111,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, case DNS_ADDRESS_IS_IPV4: if (end - b < 4) { _leave(" = -EINVAL [short inet]"); + ret = -EINVAL; goto error; } memcpy(x, b, 4); @@ -123,6 +124,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(struct afs_net *net, case DNS_ADDRESS_IS_IPV6: if (end - b < 16) { _leave(" = -EINVAL [short inet6]"); + ret = -EINVAL; goto error; } memcpy(x, b, 16); From aa24cec5b34700e498bb7b307217a365137f278a Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:38 +0100 Subject: [PATCH 1614/3297] afs: Fix double netfs initialisation in afs_root_iget() [ Upstream commit 733a984a4ee7345325e47efb505eebfe67b299bc ] Fix afs_root_iget() to leave initialisation of the netfs_inode part of the afs_vnode to afs_inode_init_from_status(). Fixes: bc899ee1c898 ("netfs: Add a netfs inode context") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-5-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/inode.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index e1cb17b857913..4890fd00b3a94 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -643,7 +643,6 @@ struct inode *afs_root_iget(struct super_block *sb, struct key *key) vnode = AFS_FS_I(inode); vnode->cb_v_check = atomic_read(&as->volume->cb_v_break); - afs_set_netfs_context(vnode); op = afs_alloc_operation(key, as->volume); if (IS_ERR(op)) { From 6eb0d929202a3ebd3222e10699f813599b2f4ac1 Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Mon, 22 Jun 2026 10:08:41 +0100 Subject: [PATCH 1615/3297] afs: use kvfree() to free memory allocated by kvcalloc() [ Upstream commit cb39654926f8e7a08ecc1dcb3941628855275940 ] op->more_files is allocated with kvcalloc() but released via afs_put_operation(), which uses kfree() internally. This mismach prevents the resource from being released properly and may lead to undefined behavior. Fix this by using kvfree() to free op->more_files to match its allocation method. Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Signed-off-by: Zilin Guan Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-8-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/fs_operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c index 8418813ee0433..ae0c9750c6b6b 100644 --- a/fs/afs/fs_operation.c +++ b/fs/afs/fs_operation.c @@ -348,7 +348,7 @@ int afs_put_operation(struct afs_operation *op) for (i = 0; i < op->nr_files - 2; i++) if (op->more_files[i].put_vnode) iput(&op->more_files[i].vnode->netfs.inode); - kfree(op->more_files); + kvfree(op->more_files); } if (op->estate) { From 083a0ddc9cd49088ba0d9cb2f98524dfb1182dd8 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Mon, 22 Jun 2026 10:08:42 +0100 Subject: [PATCH 1616/3297] afs: Remove erroneous seq |= 1 in volume lookup loop [ Upstream commit a58edda50a3ec08e6adac1d04dc3e488494e412d ] The `seq |= 1` operation in the volume lookup loop is incorrect because: seq is already incremented at start, making it odd in next iteration which triggers lock, but The `|= 1` operation causes seq to be even and unintended lockless operation Remove this erroneous operation to maintain proper lock sequencing. Fixes: 32222f09782f ("afs: Apply server breaks to mmap'd files in the call processor") Signed-off-by: Li RongQing Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-9-dhowells@redhat.com Reviewed-by: Oleg Nesterov cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/callback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 894d2bad6b6ce..833ac3178ddc7 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -140,7 +140,6 @@ static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, break; if (!need_seqretry(&cell->volume_lock, seq)) break; - seq |= 1; /* Want a lock next time */ } done_seqretry(&cell->volume_lock, seq); From b5bc1e5d5ce57271df15c8663edc721479c17761 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:44 +0100 Subject: [PATCH 1617/3297] afs: Fix bulk lookup malfunction due to change in dir_emit() API [ Upstream commit 2f79d1b93c62470fe02dbdc24770f1ae5a9e1be6 ] afs_do_lookup() and afs_do_lookup_one() use the same directory parsing code as afs_readdir() and were supplying alternative dir_context actors to retrieve dirents, but because lookup needs the vnode's uniquifier as part of the reference, but not the DT flags, the uniquifier was being passed in the dt flags argument to the lookup actors. Unfortunately, commit c644bce62b9c, added to fix overlayfs with fuse, broke this by masking off part of the uniquifier. This doesn't matter enough to be directly noticeable, instead causing bulk advance inode lookups to fail (which are retried later) and may cause dir revalidation to malfunction if the uniquifier is changed by masking. Fix this by making the afs directory parsing code take special ->actor values of AFS_LOOKUP or AFS_LOOKUP_ONE instead that tell it to call afs_lookup_filldir() or afs_lookup_one_filldir() directly rather than going through dir_emit(). dir_emit() is still used for readdir. Fixes: c644bce62b9c ("readdir: require opt-in for d_type flags") Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-11-dhowells@redhat.com cc: Amir Goldstein cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/dir.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index fa84610e0b0d9..7c3cef0835a8b 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -28,9 +28,11 @@ static int afs_d_revalidate(struct inode *dir, const struct qstr *name, static int afs_d_delete(const struct dentry *dentry); static void afs_d_iput(struct dentry *dentry, struct inode *inode); static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen, - loff_t fpos, u64 ino, unsigned dtype); + u64 ino, u32 uniquifier); +#define AFS_LOOKUP_ONE ((filldir_t)0x123UL) static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen, - loff_t fpos, u64 ino, unsigned dtype); + u64 ino, u32 uniquifier); +#define AFS_LOOKUP ((filldir_t)0x137UL) static int afs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl); static struct dentry *afs_mkdir(struct mnt_idmap *idmap, struct inode *dir, @@ -434,11 +436,18 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode, } /* found the next entry */ - if (!dir_emit(ctx, dire->u.name, nlen, - ntohl(dire->u.vnode), - (ctx->actor == afs_lookup_filldir || - ctx->actor == afs_lookup_one_filldir)? - ntohl(dire->u.unique) : DT_UNKNOWN)) { + if (ctx->actor == AFS_LOOKUP) { + if (!afs_lookup_filldir(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), + ntohl(dire->u.unique))) + return 0; + } else if (ctx->actor == AFS_LOOKUP_ONE) { + if (!afs_lookup_one_filldir(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), + ntohl(dire->u.unique))) + return 0; + } else if (!dir_emit(ctx, dire->u.name, nlen, + ntohl(dire->u.vnode), DT_UNKNOWN)) { _leave(" = 0 [full]"); return 0; } @@ -558,6 +567,7 @@ static int afs_readdir(struct file *file, struct dir_context *ctx) { afs_dataversion_t dir_version; + ctx->dt_flags_mask = UINT_MAX; return afs_dir_iterate(file_inode(file), ctx, file, &dir_version); } @@ -567,14 +577,14 @@ static int afs_readdir(struct file *file, struct dir_context *ctx) * uniquifier through dtype */ static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, - int nlen, loff_t fpos, u64 ino, unsigned dtype) + int nlen, u64 ino, u32 uniquifier) { struct afs_lookup_one_cookie *cookie = container_of(ctx, struct afs_lookup_one_cookie, ctx); _enter("{%s,%u},%s,%u,,%llu,%u", cookie->name.name, cookie->name.len, name, nlen, - (unsigned long long) ino, dtype); + (unsigned long long) ino, uniquifier); /* insanity checks first */ BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048); @@ -587,7 +597,7 @@ static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, } cookie->fid.vnode = ino; - cookie->fid.unique = dtype; + cookie->fid.unique = uniquifier; cookie->found = 1; _leave(" = false [found]"); @@ -604,7 +614,7 @@ static int afs_do_lookup_one(struct inode *dir, const struct qstr *name, { struct afs_super_info *as = dir->i_sb->s_fs_info; struct afs_lookup_one_cookie cookie = { - .ctx.actor = afs_lookup_one_filldir, + .ctx.actor = AFS_LOOKUP_ONE, .name = *name, .fid.vid = as->volume->vid }; @@ -635,14 +645,14 @@ static int afs_do_lookup_one(struct inode *dir, const struct qstr *name, * uniquifier through dtype */ static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, - int nlen, loff_t fpos, u64 ino, unsigned dtype) + int nlen, u64 ino, u32 uniquifier) { struct afs_lookup_cookie *cookie = container_of(ctx, struct afs_lookup_cookie, ctx); _enter("{%s,%u},%s,%u,,%llu,%u", cookie->name.name, cookie->name.len, name, nlen, - (unsigned long long) ino, dtype); + (unsigned long long) ino, uniquifier); /* insanity checks first */ BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048); @@ -650,7 +660,7 @@ static bool afs_lookup_filldir(struct dir_context *ctx, const char *name, if (cookie->nr_fids < 50) { cookie->fids[cookie->nr_fids].vnode = ino; - cookie->fids[cookie->nr_fids].unique = dtype; + cookie->fids[cookie->nr_fids].unique = uniquifier; cookie->nr_fids++; } @@ -791,7 +801,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry) for (i = 0; i < ARRAY_SIZE(cookie->fids); i++) cookie->fids[i].vid = dvnode->fid.vid; - cookie->ctx.actor = afs_lookup_filldir; + cookie->ctx.actor = AFS_LOOKUP; cookie->name = dentry->d_name; cookie->nr_fids = 2; /* slot 1 is saved for the fid we actually want * and slot 0 for the directory */ From 5ea289ca751c47125f6a4138c93ba10c05cd28f0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:45 +0100 Subject: [PATCH 1618/3297] afs: Fix misplaced inc of net->cells_outstanding [ Upstream commit c9c3b615a462a4023bd148f02c564e175ed10502 ] Fix net->cells_outstanding being incremented before the check for failure of idr_alloc_cyclic(), leaving the count incremented on error. Fixes: 88c853c3f5c0 ("afs: Fix cell refcounting by splitting the usage counter") Reported-by: Hillf Danton Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-12-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 71c10a05cebe5..497e876301076 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,11 +205,11 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ - atomic_inc(&net->cells_outstanding); ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, 2, INT_MAX / 2, GFP_KERNEL); if (ret < 0) goto error; + atomic_inc(&net->cells_outstanding); cell->dynroot_ino = ret; cell->debug_id = atomic_inc_return(&cell_debug_id); From 63d3f283858fae097fb09ddd4ce46bb0bc1f9d01 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:46 +0100 Subject: [PATCH 1619/3297] afs: Fix reinitialisation of the inode, in particular ->lock_work [ Upstream commit 5597fbd1e7c161914f20315a726e54025b0fdadb ] It seems that initalising afs_vnode::lock_work a single time in the slab's init function isn't sufficient for work_structs. This results in the DEBUG_OBJECTS debugging stuff producing a warning occasionally when running the generic/131 xfstest: ODEBUG: activate not available (active state 0) object: 0000000016d8760f object type: work_struct hint: afs_lock_work+0x0/0x220 WARNING: lib/debugobjects.c:629 at debug_print_object+0x4b/0x90, CPU#3: locktest/7695 ... CPU: 3 UID: 0 PID: 7695 Comm: locktest Tainted: G S 7.1.0-build3+ #2771 PREEMPT ... RIP: 0010:debug_print_object+0x65/0x90 ... Call Trace: ? __pfx_afs_lock_work+0x10/0x10 debug_object_activate+0x122/0x170 insert_work+0x25/0x60 __queue_work+0x2e0/0x340 queue_delayed_work_on+0x48/0x70 afs_fl_release_private+0x57/0x70 locks_release_private+0x5c/0xa0 locks_free_lock+0xe/0x20 posix_lock_inode+0x55f/0x5b0 locks_lock_inode_wait+0x81/0x140 ? file_write_and_wait_range+0x50/0x70 afs_lock+0xcd/0x110 fcntl_setlk+0x10d/0x260 do_fcntl+0x24e/0x5b0 __do_sys_fcntl+0x6a/0x90 do_syscall_64+0x11e/0x310 entry_SYSCALL_64_after_hwframe+0x71/0x79 Fix this by reinitialising ->lock_work after allocating an inode. Also, flush ->lock_work when the inode is being evicted to make sure it's not still running. Fixes: e8d6c554126b ("AFS: implement file locking") Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-13-dhowells@redhat.com cc: Marc Dionne cc: Thomas Gleixner cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/inode.c | 1 + fs/afs/super.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 4890fd00b3a94..f8d9ceb914cee 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -758,6 +758,7 @@ void afs_evict_inode(struct inode *inode) afs_single_writepages(inode->i_mapping, &wbc); } + flush_delayed_work(&vnode->lock_work); netfs_wait_for_outstanding_io(inode); truncate_inode_pages_final(&inode->i_data); netfs_free_folioq_buffer(vnode->directory); diff --git a/fs/afs/super.c b/fs/afs/super.c index 6a74d5e7f70aa..0612dd76a18e6 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -660,7 +660,6 @@ static void afs_i_init_once(void *_vnode) INIT_LIST_HEAD(&vnode->wb_keys); INIT_LIST_HEAD(&vnode->pending_locks); INIT_LIST_HEAD(&vnode->granted_locks); - INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); INIT_LIST_HEAD(&vnode->cb_mmap_link); seqlock_init(&vnode->cb_lock); } @@ -694,6 +693,7 @@ static struct inode *afs_alloc_inode(struct super_block *sb) init_rwsem(&vnode->rmdir_lock); INIT_WORK(&vnode->cb_work, afs_invalidate_mmap_work); + INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); _leave(" = %p", &vnode->netfs.inode); return &vnode->netfs.inode; From 0acbc09d2aca0432af45cec114f20d55ceafaec4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:47 +0100 Subject: [PATCH 1620/3297] afs: Fix callback service message parsers to pass through -EAGAIN [ Upstream commit 0f36469d7ce98b362934113c550d08bb0c784231 ] The AFS filesystem client uses an rxrpc server to listen for callback notifications. Each callback call type handler has a delivery function that parses the incoming request stream, and this should return -EAGAIN the last packet hasn't yet been seen, but all currently queued received data is consumed. afs_extract_data() does this, but the -EAGAIN return is switched to 0 inadvertantly Fix callback service message parsers to pass through -EAGAIN Fixes: d001648ec7cf ("rxrpc: Don't expose skbs to in-kernel users [ver #2]") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-14-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cmservice.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 8db2b4bbb2c0a..8dafc10ee6677 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -335,7 +335,6 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) ret = afs_extract_data(call, false); switch (ret) { case 0: break; - case -EAGAIN: return 0; default: return ret; } @@ -457,7 +456,6 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call) ret = afs_extract_data(call, false); switch (ret) { case 0: break; - case -EAGAIN: return 0; default: return ret; } From 5492799ec5d27be3bd454dcaf046bc7054f637ae Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:49 +0100 Subject: [PATCH 1621/3297] afs: Fix missing NULL pointer check in afs_break_some_callbacks() [ Upstream commit 794a01110390c1b76f59ece773fb0fbfd89c6f5c ] Fix afs_break_some_callbacks() to check to see if afs_lookup_volume_rcu() returned NULL (e.g. the specified volume is unknown). Fixes: 8230fd8217b7 ("afs: Make callback processing more efficient.") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-16-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/callback.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 833ac3178ddc7..0a09a9ca3c6b3 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -220,7 +220,11 @@ static void afs_break_some_callbacks(struct afs_server *server, rcu_read_lock(); volume = afs_lookup_volume_rcu(server->cell, vid); - if (cbb->fid.vnode == 0 && cbb->fid.unique == 0) { + if (!volume) { + /* Ignore breaks on unknown volumes. */ + rcu_read_unlock(); + *_count = 0; + } else if (cbb->fid.vnode == 0 && cbb->fid.unique == 0) { afs_break_volume_callback(server, volume); *_count -= 1; if (*_count) From 8afb1a787a2802caf1895dd96745cfd6790a6f95 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:51 +0100 Subject: [PATCH 1622/3297] afs: Fix vllist leak [ Upstream commit fc10c0ecf06f2981af5d04357612b00051e03e9e ] Fix a leak of the new vllist in afs_update_cell() in the event that it is an empty list (nr_servers == 0), in which case the old list isn't displaced unless the old list is also empty. Fixes: d5c32c89b208 ("afs: Fix cell DNS lookup") Closes: https://sashiko.dev/#/patchset/20260609081738.770127-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-18-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 497e876301076..fdc036c8e6af9 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -547,6 +547,8 @@ static int afs_update_cell(struct afs_cell *cell) rcu_assign_pointer(cell->vl_servers, vllist); cell->dns_source = vllist->source; old = p; + } else { + old = vllist; } write_unlock(&cell->vl_servers_lock); afs_put_vlserverlist(cell->net, old); From 2ffb70a8a01988046bb207b7d0af9358a8337378 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:52 +0100 Subject: [PATCH 1623/3297] afs: Fix lack of locking around modifications of net->cells_dyn_ino [ Upstream commit 55e841836c6f4646490f7b0347192b7a92d431ba ] Fix the lack of locking around modifications of net->cells_dyn_ino by taking net->cells_lock exclusively. This also requires to cell to be removed from net->cells_dyn_ino in afs_destroy_cell_work() rather than in afs_cell_destroy() as the latter runs in RCU cleanup context and sleeping locks cannot be taken there. Fixes: 1d0b929fc070 ("afs: Change dynroot to create contents on demand") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-19-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 8 +++++++- fs/afs/dynroot.c | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index fdc036c8e6af9..e85e50716459e 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,8 +205,10 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ + down_write(&net->cells_lock); ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, 2, INT_MAX / 2, GFP_KERNEL); + up_write(&net->cells_lock); if (ret < 0) goto error; atomic_inc(&net->cells_outstanding); @@ -579,7 +581,6 @@ static void afs_cell_destroy(struct rcu_head *rcu) afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers)); afs_unuse_cell(cell->alias_of, afs_cell_trace_unuse_alias); key_put(cell->anonymous_key); - idr_remove(&net->cells_dyn_ino, cell->dynroot_ino); kfree(cell->name - 1); kfree(cell); @@ -594,6 +595,11 @@ static void afs_destroy_cell_work(struct work_struct *work) afs_see_cell(cell, afs_cell_trace_destroy); timer_delete_sync(&cell->management_timer); cancel_work_sync(&cell->manager); + + down_write(&cell->net->cells_lock); + idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); + up_write(&cell->net->cells_lock); + call_rcu(&cell->rcu, afs_cell_destroy); } diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index dc9d29e3739e7..8beac43c21ce2 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -278,7 +278,7 @@ static struct dentry *afs_lookup_atcell(struct inode *dir, struct dentry *dentry } /* - * Transcribe the cell database into readdir content under the RCU read lock. + * Transcribe the cell database into readdir content under net->cells_lock. * Each cell produces two entries, one prefixed with a dot and one not. */ static int afs_dynroot_readdir_cells(struct afs_net *net, struct dir_context *ctx) From 657449e5581a41e61933b6c4e0157c22cd24b7d6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:53 +0100 Subject: [PATCH 1624/3297] afs: Fix premature cell exposure through /afs [ Upstream commit 26f17ce6fa3f05cb5965790499c1839094260de4 ] AFS cell records are prematurely exposured through the /afs dynamic root by virtue of adding them immediately to the net->cells_dyn_ino IDR when the cell is allocated rather than when it is added to the lookup tree. This allows a candidate record to be accessed, even if it's actually a duplicate or not published yet. Fix this by not adding the cell to cells_dyn_ino until it's confirmed non-duplicate and is being published. A flag is then used to record whether it is added to the IDR to make removal from the IDR conditional. Closes: https://sashiko.dev/#/patchset/20260618155141.2513212-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-20-dhowells@redhat.com Fixes: 1d0b929fc070 ("afs: Change dynroot to create contents on demand") cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/cell.c | 27 +++++++++++++++++---------- fs/afs/internal.h | 1 + 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index e85e50716459e..24b8a05712ca2 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -205,14 +205,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, cell->dns_source = vllist->source; cell->dns_status = vllist->status; smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */ - down_write(&net->cells_lock); - ret = idr_alloc_cyclic(&net->cells_dyn_ino, cell, - 2, INT_MAX / 2, GFP_KERNEL); - up_write(&net->cells_lock); - if (ret < 0) - goto error; atomic_inc(&net->cells_outstanding); - cell->dynroot_ino = ret; cell->debug_id = atomic_inc_return(&cell_debug_id); trace_afs_cell(cell->debug_id, 1, 0, afs_cell_trace_alloc); @@ -306,6 +299,13 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, goto cell_already_exists; } + ret = idr_alloc_cyclic(&net->cells_dyn_ino, candidate, + 2, INT_MAX / 2, GFP_KERNEL); + if (ret < 0) + goto cant_alloc_ino; + candidate->dynroot_ino = ret; + set_bit(AFS_CELL_FL_HAVE_INO, &candidate->flags); + cell = candidate; candidate = NULL; afs_use_cell(cell, trace); @@ -380,6 +380,11 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, _leave(" = %p [cell]", cell); return cell; +cant_alloc_ino: + up_write(&net->cells_lock); + afs_put_cell(candidate, afs_cell_trace_put_candidate); + goto error_noput; + cell_already_exists: _debug("cell exists"); cell = cursor; @@ -596,9 +601,11 @@ static void afs_destroy_cell_work(struct work_struct *work) timer_delete_sync(&cell->management_timer); cancel_work_sync(&cell->manager); - down_write(&cell->net->cells_lock); - idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); - up_write(&cell->net->cells_lock); + if (test_bit(AFS_CELL_FL_HAVE_INO, &cell->flags)) { + down_write(&cell->net->cells_lock); + idr_remove(&cell->net->cells_dyn_ino, cell->dynroot_ino); + up_write(&cell->net->cells_lock); + } call_rcu(&cell->rcu, afs_cell_destroy); } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 009064b8d6616..bea8aae5364ec 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -389,6 +389,7 @@ struct afs_cell { #define AFS_CELL_FL_NO_GC 0 /* The cell was added manually, don't auto-gc */ #define AFS_CELL_FL_DO_LOOKUP 1 /* DNS lookup requested */ #define AFS_CELL_FL_CHECK_ALIAS 2 /* Need to check for aliases */ +#define AFS_CELL_FL_HAVE_INO 3 /* Have dynroot_ino */ enum afs_cell_state state; short error; enum dns_record_source dns_source:8; /* Latest source of data from lookup */ From 158c5a0b1dfc0e6a419efe18404047a4d2dff59e Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:54 +0100 Subject: [PATCH 1625/3297] afs: Fix the volume AFS_VOLUME_RM_TREE is set on [ Upstream commit 56b4e4b26f84411d880f968a539207b0a8889c8c ] Fix afs_insert_volume_into_cell() to set AFS_VOLUME_RM_TREE on the volume replaced, not the new volume, as it's now removed from the cell's volume tree. This will cause the old volume to be removed from the tree twice and the new volume never to be removed. Fixes: 9a6b294ab496 ("afs: Fix use-after-free due to get/remove race in volume tree") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-21-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/volume.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/volume.c b/fs/afs/volume.c index 0efff3d251333..e76995ab30f02 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -40,7 +40,7 @@ static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell, goto found; } - set_bit(AFS_VOLUME_RM_TREE, &volume->flags); + set_bit(AFS_VOLUME_RM_TREE, &p->flags); rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes); } } From ce6aced2e85542a29f6d6cdf050286ddd0688185 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Jun 2026 10:08:55 +0100 Subject: [PATCH 1626/3297] afs: Fix unchecked-length string display in debug statement [ Upstream commit 903d37c97228258da71e092f8b4ab260ce81497d ] Fix afs_extract_vlserver_list() to limit the length of the displayed string in a debug statement(). Fixes: 0a5143f2f89c ("afs: Implement VL server rotation") Closes: https://sashiko.dev/#/patchset/20260618074903.2374756-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260622090856.2746629-22-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/afs/vl_list.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c index b25830d040da0..429fde817e1d5 100644 --- a/fs/afs/vl_list.c +++ b/fs/afs/vl_list.c @@ -201,6 +201,8 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, b += sizeof(*hdr); while (end - b >= sizeof(bs)) { + int nlen; + bs.name_len = afs_extract_le16(&b); bs.priority = afs_extract_le16(&b); bs.weight = afs_extract_le16(&b); @@ -210,10 +212,12 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, bs.protocol = *b++; bs.nr_addrs = *b++; + nlen = min3(bs.name_len, end - b, 255); + _debug("extract %u %u %u %u %u %u %*.*s", bs.name_len, bs.priority, bs.weight, bs.port, bs.protocol, bs.nr_addrs, - bs.name_len, bs.name_len, b); + bs.name_len, nlen, b); if (end - b < bs.name_len) break; From abe3536a4bedcc43de80b6d4d7e3d57e9ba382a5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 18 Jun 2026 10:39:22 -0400 Subject: [PATCH 1627/3297] minix: avoid overflow in bitmap block count calculation [ Upstream commit fb3e566cafc38fe3ba35e6843a2d529a3748870c ] minix_check_superblock() uses minix_blocks_needed() to verify that the on-disk imap and zmap block counts are large enough for the advertised inode and zone counts. The helper currently performs DIV_ROUND_UP() in unsigned int arithmetic. A Minix v3 image can set s_ninodes or s_zones near UINT_MAX so the addition inside DIV_ROUND_UP() wraps to zero. That makes a zero imap/zmap block count look valid, after which minix_fill_super() can dereference s_imap[0] or s_zmap[0] even though no bitmap buffers were allocated. Impact: mounting a crafted Minix v3 image whose s_ninodes or s_zones is near UINT_MAX makes minix_check_superblock() accept a zero bitmap-block count and minix_fill_super() dereference s_imap[0]/s_zmap[0], panicking the kernel. The divisor is the bitmap capacity in bits, blocksize * 8, which is always a power of two: minix_fill_super() obtains the block size through sb_set_blocksize(), and blk_validate_block_size() rejects any size that is not a power of two. Use DIV_ROUND_UP_POW2(), which divides before adding the round-up term and so cannot overflow for a power-of-two divisor. Fixes: 8c97a6ddc956 ("minix: Add required sanity checking to minix_check_superblock()") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260618143922.3066874-1-michael.bommarito@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/minix/minix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/minix/minix.h b/fs/minix/minix.h index d54273c3c9ffd..c81e48badae93 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -91,7 +91,7 @@ static inline struct minix_inode_info *minix_i(struct inode *inode) static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize) { - return DIV_ROUND_UP(bits, blocksize * 8); + return DIV_ROUND_UP_POW2(bits, blocksize * 8); } #if defined(CONFIG_MINIX_FS_NATIVE_ENDIAN) && \ From 08b2145470667962661ef131afcc8de5a62fdc32 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 9 Jun 2026 20:46:56 +0200 Subject: [PATCH 1628/3297] ovl: fix comment about locking order [ Upstream commit fd5637a2fe6dd4448392738691d63e5559fafb12 ] Forgot to update the comment when we changed the locking order. Fixes: 162d06444070c ("ovl: reorder ovl_want_write() after ovl_inode_lock()") Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260609184656.1916631-1-amir73il@gmail.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/overlayfs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index e11f310ce092a..1cc1b60fc70fb 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -800,8 +800,8 @@ static const struct address_space_operations ovl_aops = { * * This chain is valid: * - inode->i_rwsem (inode_lock[2]) - * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(inode)->lock (ovl_inode_lock[2]) + * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(lowerinode)->lock (ovl_inode_lock[1]) * * And this chain is valid: @@ -814,8 +814,8 @@ static const struct address_space_operations ovl_aops = { * held, because it is in reverse order of the non-nested case using the same * upper fs: * - inode->i_rwsem (inode_lock[1]) - * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) * - OVL_I(inode)->lock (ovl_inode_lock[1]) + * - upper_mnt->mnt_sb->s_writers (ovl_want_write[0]) */ #define OVL_MAX_NESTING FILESYSTEM_MAX_STACK_DEPTH From 1f38f65bf965fce9aa159d45c5347538f56c5973 Mon Sep 17 00:00:00 2001 From: Morduan Zang Date: Wed, 24 Jun 2026 14:26:22 +0800 Subject: [PATCH 1629/3297] iomap: guard io_size EOF trim against concurrent truncate underflow [ Upstream commit 55ec50d046c03b3724741957f7b007856e36dbe7 ] iomap: fix zero padding data issue in concurrent append writes changed ioend accounting so that io_size tracks only valid data within EOF. This trims io_size when a writeback range extends past end_pos: ioend->io_size += map_len; if (ioend->io_offset + ioend->io_size > end_pos) ioend->io_size = end_pos - ioend->io_offset; However, if end_pos ends up below ioend->io_offset, the subtraction becomes negative and is stored in size_t io_size, causing an unsigned wrap to a huge value. This can happen when writeback continues past byte-level EOF up to a block-aligned range, or when a concurrent truncate shrinks the file after end_pos was sampled in iomap_writeback_handle_eof(). A wrapped io_size can mislead append detection and corrupt completion-time size handling, since filesystem end_io paths consume io_size for decisions such as on-disk EOF updates and unwritten/COW completion ranges. Fix this by clamping io_size to zero when EOF has moved to or before the ioend start offset. This preserves the original intent of trimming io_size to valid in-EOF data while avoiding the underflow. Fixes: 51d20d1dacbe ("iomap: fix zero padding data issue in concurrent append writes") Suggested-by: Christoph Hellwig Signed-off-by: Morduan Zang Link: https://patch.msgid.link/9E38E2659B47DC2A+20260624062622.337469-1-zhangdandan@uniontech.com Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/iomap/ioend.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index fb26cfd9c4cc3..6c75fbf4d5af4 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -239,8 +239,12 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio, * appending writes. */ ioend->io_size += map_len; - if (ioend->io_offset + ioend->io_size > end_pos) - ioend->io_size = end_pos - ioend->io_offset; + if (ioend->io_offset + ioend->io_size > end_pos) { + if (ioend->io_offset >= end_pos) + ioend->io_size = 0; + else + ioend->io_size = end_pos - ioend->io_offset; + } wbc_account_cgroup_owner(wpc->wbc, folio, map_len); return map_len; From 8ab75e445c161c4cb504aa98e20ce1dd1ecd8e9a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:20 +0100 Subject: [PATCH 1630/3297] netfs: Fix netfs_create_write_req() to handle async cache object creation [ Upstream commit dbd6f56d975b23241b7bbb11bb8f562af548a0aa ] netfs_create_write_req() will skip caching if the fscache cookie is disabled, but this is a problem because async cache object creation might not have got far enough yet that has been enabled - thereby causing the call to fscache_begin_write_operation() to be skipped. Fix this by removing the checks on the cookie and delegating this to fscache_begin_write_operation(). Fixes: 7b589a9b45ae ("netfs: Fix handling of USE_PGPRIV2 and WRITE_TO_CACHE flags") Closes: https://sashiko.dev/#/patchset/20260624115737.2964520-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-3-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 03d170b9022b7..2628984934303 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -106,7 +106,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, _enter("R=%x", wreq->debug_id); ictx = netfs_inode(wreq->inode); - if (is_cacheable && netfs_is_cache_enabled(ictx)) + if (is_cacheable) fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx)); if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0) goto nomem; From 7838131e296dfbb639ea278901a53c018322c11e Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:30 +0100 Subject: [PATCH 1631/3297] netfs: Fix writethrough to use collection offload [ Upstream commit ba6a9f6533c77c628eef0c0c5c19cd316e2be1b4 ] Fix writethrough write to set NETFS_RREQ_OFFLOAD_COLLECTION on the request so that collection is processed asynchronously rather than only right at the end - and also so that asynchronous O_SYNC writes get collected at all. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Closes: https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-13-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 2628984934303..79b4085590fdb 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -627,6 +627,7 @@ struct netfs_io_request *netfs_begin_writethrough(struct kiocb *iocb, size_t len } wreq->io_streams[0].avail = true; + __set_bit(NETFS_RREQ_OFFLOAD_COLLECTION, &wreq->flags); trace_netfs_write(wreq, netfs_write_trace_writethrough); return wreq; } From 1bb33d959aabcde07d724b5eb9992462e91fa79a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:31 +0100 Subject: [PATCH 1632/3297] netfs: Fix writeback error handling [ Upstream commit ac5f95ac5d6d0f4c567b8b642825705a2bf0d79e ] Fix the error handling in writeback_iter() loop. If an error occurs, writeback_iter() needs to be called again with *error set to the error so that it can clean up iteration state. Further, the current folio needs unlocking and redirtying. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Link: https://sashiko.dev/#/patchset/20260619140646.2633762-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-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 (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 79b4085590fdb..08bcdd0a87124 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -587,8 +587,6 @@ int netfs_writepages(struct address_space *mapping, } error = netfs_write_folio(wreq, wbc, folio); - if (error < 0) - break; } while ((folio = writeback_iter(mapping, wbc, folio, &error))); netfs_end_issue_write(wreq); @@ -601,7 +599,14 @@ int netfs_writepages(struct address_space *mapping, return error; couldnt_start: - netfs_kill_dirty_pages(mapping, wbc, folio); + if (error == -ENOMEM) { + folio_redirty_for_writepage(wbc, folio); + folio_unlock(folio); + folio = writeback_iter(mapping, wbc, folio, &error); + WARN_ON_ONCE(folio != NULL); + } else { + netfs_kill_dirty_pages(mapping, wbc, folio); + } out: mutex_unlock(&ictx->wb_lock); _leave(" = %d", error); From a9b89752c2726e88ea67994fb97a0316b24d30b7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 25 Jun 2026 15:06:32 +0100 Subject: [PATCH 1633/3297] netfs: Fix folio state after ENOMEM whilst under writeback iteration [ Upstream commit b6a713fd34b9498ee2164d5d3e8460732a392efc ] Fix the state of the current folio when ENOMEM occurs during writeback iteration. The folio needs to be redirtied and unlocked before the terminal writeback_iter() is invoked. Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") Link: https://sashiko.dev/#/patchset/20260619140646.2633762-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260625140640.3116900-15-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 (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 08bcdd0a87124..76614339554ea 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -587,6 +587,10 @@ int netfs_writepages(struct address_space *mapping, } error = netfs_write_folio(wreq, wbc, folio); + if (error == -ENOMEM) { + folio_redirty_for_writepage(wbc, folio); + folio_unlock(folio); + } } while ((folio = writeback_iter(mapping, wbc, folio, &error))); netfs_end_issue_write(wreq); From 78b1074966d290d4517f42bc81e13c4349368d12 Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Tue, 16 Jun 2026 10:17:56 +0200 Subject: [PATCH 1634/3297] drm/xe/pt: Fix NULL pointer dereference in xe_pt_zap_ptes_entry() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3feeb667197bd58a17f4edfdbcad249ffcb3c864 ] The page-table walk framework may pass a NULL *child pointer for unpopulated entries. xe_pt_zap_ptes_entry() called container_of(*child) before checking for NULL, then dereferenced the result, causing a crash. Move the container_of() call after a NULL guard, so the function returns early instead of proceeding with an invalid pointer. XE_WARN_ON is kept to help root cause the issue, but we now bail instead of crashing the driver. v2: Comment that triggering XE_WARN_ON is unexpected behavior (Matt Brost) Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: Matthew Brost Cc: Thomas Hellström Reviewed-by: Matthew Brost Link: https://lore.kernel.org/r/20260616081756.286918-1-francois.dugast@intel.com Signed-off-by: Francois Dugast (cherry picked from commit b9297d19d9df5d4b6c994648570c5dcd1cac68ff) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_pt.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index a21383fba1e81..93c0a9d860f61 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -860,12 +860,20 @@ static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset, { struct xe_pt_zap_ptes_walk *xe_walk = container_of(walk, typeof(*xe_walk), base); - struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base); + struct xe_pt *xe_child; pgoff_t end_offset; - XE_WARN_ON(!*child); XE_WARN_ON(!level); + /* + * Below would be unexpected behavior that needs to be root caused + * but better warn and bail than crash the driver. + */ + if (XE_WARN_ON(!*child)) + return 0; + + xe_child = container_of(*child, typeof(*xe_child), base); + /* * Note that we're called from an entry callback, and we're dealing * with the child of that entry rather than the parent, so need to From f9a9abd7bbdab3dfe1b1155e1457dc02b5e14ea5 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Thu, 25 Jun 2026 21:56:15 +0000 Subject: [PATCH 1635/3297] drm/xe/userptr: Hold notifier_lock for write on inject test path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit dca6e08c923a44d2d66b955e03dd57a3a38c2b94 ] When CONFIG_DRM_XE_USERPTR_INVAL_INJECT=y, xe_pt_svm_userptr_pre_commit() runs vma_check_userptr() with the svm notifier_lock taken for read. The test injection causes vma_check_userptr() to call xe_vma_userptr_force_invalidate(), which feeds into xe_vma_userptr_do_inval() with drm_gpusvm_ctx.in_notifier=true. That flag tells drm_gpusvm_unmap_pages() the caller already holds notifier_lock for write and only asserts the mode. Because the caller actually holds it for read, the assertion fires: WARNING: drivers/gpu/drm/drm_gpusvm.c:1669 at \ drm_gpusvm_unmap_pages+0xd4/0x130 [drm_gpusvm_helper] Call Trace: xe_vma_userptr_do_inval+0x40d/0xfd0 [xe] xe_vma_userptr_invalidate_pass1+0x3e6/0x8d0 [xe] xe_vma_userptr_force_invalidate+0xde/0x290 [xe] vma_check_userptr.constprop.0+0x1c6/0x220 [xe] xe_pt_svm_userptr_pre_commit+0x6a3/0xc60 [xe] ... xe_vm_bind_ioctl+0x3a0a/0x4480 [xe] Acquire notifier_lock for write in pre-commit when the inject Kconfig is enabled, via new helpers xe_pt_svm_userptr_notifier_lock()/_unlock(). Rename xe_svm_assert_held_read() to xe_svm_assert_held_read_or_inject_write() so it asserts the correct mode under each build configuration. Production builds (CONFIG_DRM_XE_USERPTR_INVAL_INJECT=n) keep the existing read-mode behavior bit-for-bit. Fixes: 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") Assisted-by: Claude:claude-opus-4.7 Cc: Matthew Auld Cc: Zongyao Bai Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260625215615.3016892-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 80ccbd97ffee8ad2e73167d826fe7be548364365) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_pt.c | 43 ++++++++++++++++++++++++++++++------- drivers/gpu/drm/xe/xe_svm.h | 15 +++++++++++-- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 93c0a9d860f61..6b75794e76b12 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1061,7 +1061,7 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma) xe_pt_commit_prepare_locks_assert(vma); if (xe_vma_is_userptr(vma)) - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); } static void xe_pt_commit(struct xe_vma *vma, @@ -1388,6 +1388,33 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +/* + * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() + * and the matching late release in xe_pt_update_ops_run(). Read mode by + * default; write mode when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, + * because a userptr op in this critical section may invoke the injected + * xe_vma_userptr_force_invalidate() path that calls + * drm_gpusvm_unmap_pages() with ctx->in_notifier=true, which requires the + * lock held for write. + */ +static void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + down_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_lock(vm); +#endif +} + +static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + up_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_unlock(vm); +#endif +} + #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT @@ -1419,7 +1446,7 @@ static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma, struct xe_userptr_vma *uvma; unsigned long notifier_seq; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); if (!xe_vma_is_userptr(vma)) return 0; @@ -1449,7 +1476,7 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op, { int err = 0; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); switch (op->base.op) { case DRM_GPUVA_OP_MAP: @@ -1521,12 +1548,12 @@ static int xe_pt_svm_userptr_pre_commit(struct xe_migrate_pt_update *pt_update) if (err) return err; - xe_svm_notifier_lock(vm); + xe_pt_svm_userptr_notifier_lock(vm); list_for_each_entry(op, &vops->list, link) { err = op_check_svm_userptr(vm, op, pt_update_ops); if (err) { - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); break; } } @@ -2192,7 +2219,7 @@ static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile, vma->tile_invalidated & ~BIT(tile->id)); vma->tile_staged &= ~BIT(tile->id); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); to_userptr_vma(vma)->userptr.initial_bind = true; } @@ -2228,7 +2255,7 @@ static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile, if (!vma->tile_present) { list_del_init(&vma->combined_links.rebind); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); spin_lock(&vm->userptr.invalidated_lock); list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link); @@ -2517,7 +2544,7 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops) } if (pt_update_ops->needs_svm_lock) - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); xe_tlb_inval_job_put(mjob); xe_tlb_inval_job_put(ijob); diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h index fa757dd07954d..45bf10f866604 100644 --- a/drivers/gpu/drm/xe/xe_svm.h +++ b/drivers/gpu/drm/xe/xe_svm.h @@ -353,8 +353,19 @@ static inline void xe_svm_flush(struct xe_vm *vm) #define xe_svm_assert_in_notifier(vm__) \ lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) -#define xe_svm_assert_held_read(vm__) \ +/* + * Assert the svm notifier_lock is held. Read mode by default; write mode + * when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, because that path forces + * a userptr invalidation that ends in drm_gpusvm_unmap_pages() with + * ctx->in_notifier=true, which requires the lock held for write. + */ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) +#define xe_svm_assert_held_read_or_inject_write(vm__) \ + lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) +#else +#define xe_svm_assert_held_read_or_inject_write(vm__) \ lockdep_assert_held_read(&(vm__)->svm.gpusvm.notifier_lock) +#endif #define xe_svm_notifier_lock(vm__) \ drm_gpusvm_notifier_lock(&(vm__)->svm.gpusvm) @@ -368,7 +379,7 @@ static inline void xe_svm_flush(struct xe_vm *vm) #else #define xe_svm_assert_in_notifier(...) do {} while (0) -static inline void xe_svm_assert_held_read(struct xe_vm *vm) +static inline void xe_svm_assert_held_read_or_inject_write(struct xe_vm *vm) { } From a0a56b4480a0dc921a42cd70e9fc081a12ab8359 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Fri, 26 Jun 2026 21:06:31 +0000 Subject: [PATCH 1636/3297] drm/xe/hw_engine: Fix double-free of managed BO in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7ac3cae7a251d28e9079de07a991bd4eb2bb7fd8 ] The error path in hw_engine_init() 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: 0e1a47fcabc8 ("drm/xe: Add a helper for DRM device-lifetime BO create") Assisted-by: Claude:claude-opus-4.6 Reviewed-by: Zongyao Bai Link: https://patch.msgid.link/20260626210631.3887291-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit e459a3bdeb117be496d7f229e2ea1f6c9fe4080b) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_hw_engine.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index d8f16e25b817d..d88770370eeb0 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -619,7 +619,7 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, hwe->exl_port = xe_execlist_port_create(xe, hwe); if (IS_ERR(hwe->exl_port)) { err = PTR_ERR(hwe->exl_port); - goto err_hwsp; + goto err_name; } } else { /* GSCCS has a special interrupt for reset */ @@ -639,8 +639,6 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, return devm_add_action_or_reset(xe->drm.dev, hw_engine_fini, hwe); -err_hwsp: - xe_bo_unpin_map_no_vm(hwe->hwsp); err_name: hwe->name = NULL; From adc7dda728ca3e340a413e3bbc10cf159e1866a4 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 27 May 2026 20:37:35 +0200 Subject: [PATCH 1637/3297] drm/xe/pf: Don't attempt to process FAST_REQ or EVENT relays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ed8b0d731892c68b41ecbd27c952af284816dec1 ] Currently defined VF/PF relay actions use regular REQUEST messages only and the PF shouldn't attempt to handle FAST_REQUEST nor EVENT messages as this would result in breaking the VFPF ABI protocol and also might trigger an assert on the PF side. Fixes: 98e62805921c ("drm/xe/pf: Add SR-IOV GuC Relay PF services") Signed-off-by: Michal Wajdeczko Reviewed-by: Michał Winiarski Link: https://patch.msgid.link/20260527183735.22616-1-michal.wajdeczko@intel.com (cherry picked from commit 1714d360fc5ae2e0886a69e979095d9c7ff3568a) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_guc_relay.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c index e5dc94f3e6181..09febc2b152eb 100644 --- a/drivers/gpu/drm/xe/xe_guc_relay.c +++ b/drivers/gpu/drm/xe/xe_guc_relay.c @@ -677,12 +677,17 @@ static int relay_action_handler(struct xe_guc_relay *relay, u32 origin, return relay_testloop_action_handler(relay, origin, msg, len, response, size); type = FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]); + relay_assert(relay, guc_hxg_type_is_action(type)); - if (IS_SRIOV_PF(relay_to_xe(relay))) - ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, response, size); - else + if (IS_SRIOV_PF(relay_to_xe(relay))) { + if (type == GUC_HXG_TYPE_REQUEST) + ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, + response, size); + else + ret = -EOPNOTSUPP; + } else { ret = -EOPNOTSUPP; - + } if (type == GUC_HXG_TYPE_EVENT) relay_assert(relay, ret <= 0); From 2265b2b1c5aaa276f584c1a6246bb8c5ea90025b Mon Sep 17 00:00:00 2001 From: David Windsor Date: Mon, 29 Jun 2026 20:13:33 -0400 Subject: [PATCH 1638/3297] x86/uprobes: Keep shadow stack in sync for emulated CALLs [ Upstream commit abf08854d224085e2ebb3ba660e7995909f47d6a ] Uprobe CALL emulation updates the normal user stack, but not the CET user shadow stack. The subsequent RET then sees a stale shadow stack entry and raises #CP. Update the relative CALL emulation and XOL CALL fixup paths to keep the shadow stack in sync. Fixes: 488af8ea7131 ("x86/shstk: Wire in shadow stack interface") Signed-off-by: David Windsor Signed-off-by: Peter Zijlstra (Intel) Acked-by: Oleg Nesterov Acked-by: Jiri Olsa Tested-by: Jiri Olsa Link: https://patch.msgid.link/8b5b1c7407b98f31664ad7b6a6faf20d2d4a6cad.1782777969.git.dwindsor@gmail.com Signed-off-by: Sasha Levin --- arch/x86/kernel/uprobes.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 46aed82243964..852d54b2e4137 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -1269,9 +1269,15 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs long correction = utask->vaddr - utask->xol_vaddr; regs->ip += correction; } else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) { + unsigned long retaddr = utask->vaddr + auprobe->defparam.ilen; + int err; + regs->sp += sizeof_long(regs); /* Pop incorrect return address */ - if (emulate_push_stack(regs, utask->vaddr + auprobe->defparam.ilen)) + if (emulate_push_stack(regs, retaddr)) return -ERESTART; + err = shstk_update_last_frame(retaddr); + if (err) + return err; } /* popf; tell the caller to not touch TF */ if (auprobe->defparam.fixups & UPROBE_FIX_SETF) @@ -1361,6 +1367,10 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs) */ if (emulate_push_stack(regs, new_ip)) return false; + if (shstk_push(new_ip) == -EFAULT) { + regs->sp += sizeof_long(regs); + return false; + } } else if (!check_jmp_cond(auprobe, regs)) { offs = 0; } From c9170c83b0e0fc2065a4c2bca5bf1f90c5880156 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 1 Jul 2026 13:13:25 +0200 Subject: [PATCH 1639/3297] uprobes/x86: Use proper mm_struct in __in_uprobe_trampoline [ Upstream commit 169328645663bae30e9abad4012d52441e085a71 ] In the unregister path we use __in_uprobe_trampoline check with current->mm for the VMA lookup, which is wrong, because we are in the tracer context, not the traced process. Add mm_struct pointer argument to __in_uprobe_trampoline and changing related callers to pass proper mm_struct pointer. Fixes: ba2bfc97b462 ("uprobes/x86: Add support to optimize uprobes") Reported-by: syzbot+61ce80689253f42e6d80@syzkaller.appspotmail.com Signed-off-by: Jiri Olsa Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Oleg Nesterov Acked-by: Andrii Nakryiko Tested-by: syzbot+61ce80689253f42e6d80@syzkaller.appspotmail.com Link: https://patch.msgid.link/20260701111337.53943-2-jolsa@kernel.org Signed-off-by: Sasha Levin --- arch/x86/kernel/uprobes.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 852d54b2e4137..ae347e3837851 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -761,9 +761,9 @@ void arch_uprobe_clear_state(struct mm_struct *mm) destroy_uprobe_trampoline(tramp); } -static bool __in_uprobe_trampoline(unsigned long ip) +static bool __in_uprobe_trampoline(struct mm_struct *mm, unsigned long ip) { - struct vm_area_struct *vma = vma_lookup(current->mm, ip); + struct vm_area_struct *vma = vma_lookup(mm, ip); return vma && vma_is_special_mapping(vma, &tramp_mapping); } @@ -776,14 +776,14 @@ static bool in_uprobe_trampoline(unsigned long ip) rcu_read_lock(); if (mmap_lock_speculate_try_begin(mm, &seq)) { - found = __in_uprobe_trampoline(ip); + found = __in_uprobe_trampoline(mm, ip); retry = mmap_lock_speculate_retry(mm, seq); } rcu_read_unlock(); if (retry) { mmap_read_lock(mm); - found = __in_uprobe_trampoline(ip); + found = __in_uprobe_trampoline(mm, ip); mmap_read_unlock(mm); } return found; @@ -1044,7 +1044,7 @@ static int copy_from_vaddr(struct mm_struct *mm, unsigned long vaddr, void *dst, return 0; } -static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr) +static bool __is_optimized(struct mm_struct *mm, uprobe_opcode_t *insn, unsigned long vaddr) { struct __packed __arch_relative_insn { u8 op; @@ -1053,7 +1053,7 @@ static bool __is_optimized(uprobe_opcode_t *insn, unsigned long vaddr) if (!is_call_insn(insn)) return false; - return __in_uprobe_trampoline(vaddr + 5 + call->raddr); + return __in_uprobe_trampoline(mm, vaddr + 5 + call->raddr); } static int is_optimized(struct mm_struct *mm, unsigned long vaddr) @@ -1064,7 +1064,7 @@ static int is_optimized(struct mm_struct *mm, unsigned long vaddr) err = copy_from_vaddr(mm, vaddr, &insn, 5); if (err) return err; - return __is_optimized((uprobe_opcode_t *)&insn, vaddr); + return __is_optimized(mm, (uprobe_opcode_t *)&insn, vaddr); } static bool should_optimize(struct arch_uprobe *auprobe) From 86652704a7fd41a5a8459027e08640bbd1952826 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 2 Jul 2026 09:25:00 +0100 Subject: [PATCH 1640/3297] cifs: Fix missing credit release on failure in cifs_issue_read() [ Upstream commit c16b8c4cfb4fe2244cc33e469a93c1ab8684146b ] Fix missing release of credits in the failure path in cifs_issue_read() lest retrying the subreq just overwrites the credits value. Fixes: 69c3c023af25 ("cifs: Implement netfslib hooks") Link: https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com Signed-off-by: David Howells Acked-by: Paulo Alcantara (Red Hat) cc: linux-cifs@vger.kernel.org cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index b0dc071a9de4f..a34457d5143e1 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -242,6 +242,7 @@ static void cifs_issue_read(struct netfs_io_subrequest *subreq) return; failed: + add_credits_and_wake_if(rdata->server, &rdata->credits, 0); subreq->error = rc; netfs_read_subreq_terminated(subreq); } From 232a2f2fce9b92b825cc2955a28f6e448d2d59ed Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Fri, 26 Jun 2026 17:58:37 +0900 Subject: [PATCH 1641/3297] ata: sata_gemini: unwind clocks on IDE pinctrl errors [ Upstream commit c0ace4130e813acbabdfaa28d4e94a849c2ffdd7 ] gemini_sata_bridge_init() prepares and enables both SATA PCLKs, then disables them again while keeping the clocks prepared for later bridge start and stop operations. If gemini_setup_ide_pins() fails after that, gemini_sata_probe() returns directly and skips the existing out_unprep_clk unwind path. Route the IDE pinctrl failure through out_unprep_clk so the clocks prepared by gemini_sata_bridge_init() are unprepared before probe fails. Fixes: d872ced29d5f ("ata: sata_gemini: Introduce explicit IDE pin control") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Niklas Cassel Reviewed-by: Linus Walleij Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/sata_gemini.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/sata_gemini.c b/drivers/ata/sata_gemini.c index 530ee26b30121..56ae2820df588 100644 --- a/drivers/ata/sata_gemini.c +++ b/drivers/ata/sata_gemini.c @@ -353,7 +353,7 @@ static int gemini_sata_probe(struct platform_device *pdev) if (sg->ide_pins) { ret = gemini_setup_ide_pins(dev); if (ret) - return ret; + goto out_unprep_clk; } dev_info(dev, "set up the Gemini IDE/SATA nexus\n"); From 793b55c3f36f56ab7897b3873460841372dc1004 Mon Sep 17 00:00:00 2001 From: Karuna Ramkumar Date: Thu, 2 Jul 2026 02:01:42 +0000 Subject: [PATCH 1642/3297] ata: libata-scsi: limit simulated SCSI command copy to response length [ Upstream commit cd64be0ecd399fa2b1ab60b3aaf2b2b744243467 ] The function ata_scsi_rbuf_fill() is used to copy the response of emulated SCSI commands from ata_scsi_rbuf to the SCSI command's scatterlist. Currently, sg_copy_from_buffer() is called with the size argument set to ATA_SCSI_RBUF_SIZE (2048 bytes). Since ata_scsi_rbuf is zeroed out before the simulation actor is invoked, copying the full buffer size causes the remainder of the SCSI command's transfer buffer (beyond the actual response length 'len') to be overwritten with zeroes. This clobbers any pre-existing sentinel values or data in the caller's buffer tail, even though the correct residual count is reported via scsi_set_resid(). Fix this by passing the actual response length 'len' as the copy size to sg_copy_from_buffer(), ensuring that the tail of the caller's buffer remains untouched. Also, add a defensive check to ensure that the actor does not return a length exceeding the static buffer capacity. If this occurs, trigger a WARN_ON(), fail the command with an aborted command error, and return immediately without copying any data. The fix was tested by invoking an SCSI SG_IO INQUIRY on an ATA disk on vanilla build, and build with the fix. Confirmed that the input buffer's tail end remains unmodified with the fix. Fixes: 5251ae224d8d ("ata: libata-scsi: Return residual for emulated SCSI commands") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Karuna Ramkumar Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/libata-scsi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index c7d2addf34874..46ef1ef6f8128 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1930,8 +1930,13 @@ static void ata_scsi_rbuf_fill(struct ata_device *dev, struct scsi_cmnd *cmd, memset(ata_scsi_rbuf, 0, ATA_SCSI_RBUF_SIZE); len = actor(dev, cmd, ata_scsi_rbuf); if (len) { + if (WARN_ON(len > ATA_SCSI_RBUF_SIZE)) { + ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); + spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags); + return; + } sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), - ata_scsi_rbuf, ATA_SCSI_RBUF_SIZE); + ata_scsi_rbuf, len); cmd->result = SAM_STAT_GOOD; if (scsi_bufflen(cmd) > len) scsi_set_resid(cmd, scsi_bufflen(cmd) - len); From d354e523c6f740db758cafcd4c11bb7913285ed8 Mon Sep 17 00:00:00 2001 From: Georgiy Osokin Date: Sun, 17 May 2026 15:06:39 +0300 Subject: [PATCH 1643/3297] HID: picolcd: prevent NULL pointer dereference in picolcd_send_and_wait() [ Upstream commit 0021eb09041f021c079be1022934a280f7f176c0 ] In picolcd_send_and_wait(), an integer overflow of the signed loop counter 'k' can theoretically lead to a NULL pointer dereference of 'raw_data'. If the loop executes more than INT_MAX times, 'k' becomes negative, making the condition 'k < size' true even when 'size' is 0. Change the type of 'k' to 'unsigned int' to prevent the overflow and eliminate the out-of-bounds access. Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. [jkosina@suse.com: extended hash length] Fixes: fabdbf2fd22fa17 ("HID: picoLCD: split driver code") Signed-off-by: Georgiy Osokin Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-picolcd_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 297103be33815..282f11ec5df74 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -72,7 +72,8 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, struct picolcd_pending *work; struct hid_report *report = picolcd_out_report(report_id, hdev); unsigned long flags; - int i, j, k; + int i, j; + unsigned int k; if (!report || !data) return NULL; From dd395744e4ed87956fcbf81ecc6a20c51e35fa4e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 16 Jun 2026 11:26:56 +0000 Subject: [PATCH 1644/3297] HID: core: Fix OOB read in hid_get_report for numbered reports [ Upstream commit af1a9b65ebe8a948eda805c14b78d4d0767cb1b5 ] When a caller passes a size of 0 to hid_report_raw_event() for a numbered report, the function originally called hid_get_report() before performing any size validation. Inside hid_get_report(), if the report is numbered (report_enum->numbered is true), it unconditionally dereferences data[0] to extract the report ID. With a size of 0, this results in an out-of-bounds read or kernel panic. Fix this by moving the numbered report size validation check before the call to hid_get_report(), ensuring that size is at least 1 before dereferencing the data pointer. Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event") Signed-off-by: Lee Jones Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6a1600af30e20..4df51bbd0d54c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2041,6 +2041,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * u8 *cdata = data; int ret = 0; + if (report_enum->numbered && (size < 1 || bufsize < 1)) { + hid_warn_ratelimited(hid, + "Event data for numbered report is too short (%d vs %zu)\n", + size, bufsize); + return -EINVAL; + } + report = hid_get_report(report_enum, data); if (!report) return 0; From 4c65c3d9f660b5fd8d5ebde59e84ae42bf4ebe7a Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Fri, 26 Jun 2026 02:28:45 +0100 Subject: [PATCH 1645/3297] arm64/mm: Optimize TLB flush in unmap_hotplug_[pmd|pud]_range() [ Upstream commit ff4c5a0de1f2ef7737a8688a86e19301e567020d ] Commit 48478b9f7913 ("arm64/mm: Enable batched TLB flush in unmap_hotplug_range") inadvertently introduced redundant TLB invalidation when clearing a block entry, resulting in unnecessary broadcast invalidation on CPUs without support for range-based invalidation. Re-introduce the old behaviour, along with some expanded comments to help people working in this area next time around. Cc: Catalin Marinas Cc: Will Deacon Cc: Ryan Roberts Cc: David Hildenbrand Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Reported-by: Ben Hutchings Closes: https://lore.kernel.org/all/b0d5836032ce3135bfc473f6bff791306d086925.camel@decadent.org.uk/ Fixes: 48478b9f7913 ("arm64/mm: Enable batched TLB flush in unmap_hotplug_range()") Signed-off-by: Anshuman Khandual Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Catalin Marinas [will: Reword comments and commit message] Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/mm/mmu.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index ada93622ccf2c..971cffdb8b48c 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1515,7 +1515,13 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr, if (free_mapped) { /* CONT blocks are not supported in the vmemmap */ WARN_ON(pmd_cont(pmd)); - flush_tlb_kernel_range(addr, addr + PMD_SIZE); + /* + * Invalidating a block entry requires just + * a single overlapping TLB invalidation, + * so limit the range of the flush to a single + * page. + */ + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); free_hotplug_page_range(pmd_page(pmd), PMD_SIZE, altmap); } @@ -1545,7 +1551,8 @@ static void unmap_hotplug_pud_range(p4d_t *p4dp, unsigned long addr, if (pud_sect(pud)) { pud_clear(pudp); if (free_mapped) { - flush_tlb_kernel_range(addr, addr + PUD_SIZE); + /* See comment in unmap_hotplug_pmd_range(). */ + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); free_hotplug_page_range(pud_page(pud), PUD_SIZE, altmap); } From 61a959b82f1aecd6d2d35c208013dd077cac9d10 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:13 +0000 Subject: [PATCH 1646/3297] HID: bpf: Fix hid_bpf_get_data() range check [ Upstream commit 2d044049421dd48212b28646a850749d4a2d57fa ] hid_bpf_get_data() returns a pointer into the HID-BPF context data when the caller-provided offset and size fit inside ctx->allocated_size. The current check adds rdwr_buf_size and offset before comparing the result against ctx->allocated_size. Since both values are unsigned, a very large size can wrap the sum below ctx->allocated_size and make the helper return a pointer even though the requested range is not contained in the backing buffer. Use check_add_overflow() to reject wrapped range ends before comparing the requested range end against ctx->allocated_size. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- drivers/hid/bpf/hid_bpf_dispatch.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index eb9f40c988d02..e6cf551b41e8e 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "hid_bpf_dispatch.h" const struct hid_ops *hid_ops; @@ -296,13 +297,15 @@ __bpf_kfunc __u8 * hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size) { struct hid_bpf_ctx_kern *ctx_kern; + size_t end; if (!ctx) return NULL; ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - if (rdwr_buf_size + offset > ctx->allocated_size) + if (check_add_overflow(rdwr_buf_size, offset, &end) || + end > ctx->allocated_size) return NULL; return ctx_kern->data + offset; From 91ac1d7fd51e3497630156d9a932f33a6d48dd3d Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:14 +0000 Subject: [PATCH 1647/3297] selftests/hid: Load only requested struct_ops maps [ Upstream commit 5aad55011a37d999cf99d14bafb6a093a1a70466 ] The HID selftest skeleton contains several struct_ops maps, but each test usually wants to load only the programs named by that test. load_programs() disabled auto-attach for all maps, but left struct_ops autocreate enabled. libbpf can enable autoload for programs referenced by autocreated struct_ops maps, so an unrelated program can be loaded and fail even when the current test does not use it. Disable autocreate for all struct_ops maps by default, then re-enable it only for the maps selected by the test before loading the skeleton. Signed-off-by: Yiyang Chen Fixes: f64c1a459339 ("selftests/hid: disable struct_ops auto-attach") Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- tools/testing/selftests/hid/hid_bpf.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 1e979fb3542ba..269256e1decd8 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -86,6 +86,20 @@ static void load_programs(const struct test_program programs[], self->skel = hid__open(); ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open"); + /* + * Disable all struct_ops maps by default so libbpf does not autoload + * programs referenced by maps that are unrelated to the current test. + */ + bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) { + if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) { + err = bpf_map__set_autocreate(iter_map, false); + ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'", + bpf_map__name(iter_map)); + } + + bpf_map__set_autoattach(iter_map, false); + } + for (int i = 0; i < progs_count; i++) { struct bpf_program *prog; struct bpf_map *map; @@ -102,6 +116,10 @@ static void load_programs(const struct test_program programs[], ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'", programs[i].name + 4); + err = bpf_map__set_autocreate(map, true); + ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'", + programs[i].name + 4); + /* hid_id is the first field of struct hid_bpf_ops */ ops_hid_id = bpf_map__initial_value(map, NULL); ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data"); @@ -109,13 +127,6 @@ static void load_programs(const struct test_program programs[], *ops_hid_id = self->hid.hid_id; } - /* we disable the auto-attach feature of all maps because we - * only want the tested one to be manually attached in the next - * call to bpf_map__attach_struct_ops() - */ - bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) - bpf_map__set_autoattach(iter_map, false); - err = hid__load(self->skel); ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err); From a6185c21d5510cd0328a3149c0281e09dc2d8c10 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:15 +0000 Subject: [PATCH 1648/3297] selftests/hid: Cover hid_bpf_get_data() size overflow [ Upstream commit eebbef7c468a5cb58c4772849ee5066441166cf0 ] Add a HID-BPF regression check for hid_bpf_get_data() requests whose size would overflow when added to the offset. The new rdesc fixup callback asks for offset 2 and size ~0ULL, then records whether the helper returns NULL. A vulnerable kernel returns a non-NULL pointer because the runtime check wraps the addition. A fixed kernel rejects the request. The callback records the helper result without dereferencing any returned pointer. The callback reports the helper result through BSS and returns 0 intentionally. hid_rdesc_fixup return values are consumed as report descriptor fixup results, so a positive test-result value would be interpreted as a replacement report descriptor size. Also add KHDR_INCLUDES to the HID selftest build so hid_bpf.c sees the current kernel UAPI HID definitions on systems whose installed headers do not provide enum hid_report_type. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- tools/testing/selftests/hid/Makefile | 2 +- tools/testing/selftests/hid/hid_bpf.c | 11 +++++++++++ tools/testing/selftests/hid/progs/hid.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 2839d2612ce3a..7cdf133812a99 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -24,7 +24,7 @@ CXX ?= $(CROSS_COMPILE)g++ HOSTPKG_CONFIG := pkg-config -CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) +CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES) CFLAGS += -I$(OUTPUT)/tools/include LDLIBS += -lelf -lz -lrt -lpthread diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 269256e1decd8..b851339308c21 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -898,6 +898,17 @@ TEST_F(hid_bpf, test_rdesc_fixup) ASSERT_EQ(rpt_desc.value[4], 0x42); } +TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow) +{ + const struct test_program progs[] = { + { .name = "hid_rdesc_fixup_get_data_overflow" }, + }; + + LOAD_PROGRAMS(progs); + + ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1); +} + static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 5ecc845ef7921..b21fbb13c926f 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -13,6 +13,7 @@ struct attach_prog_args { __u64 callback_check = 52; __u64 callback2_check = 52; +__u64 get_data_overflow_check; SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) @@ -240,6 +241,20 @@ struct hid_bpf_ops rdesc_fixup = { .hid_rdesc_fixup = (void *)hid_rdesc_fixup, }; +SEC("?struct_ops.s/hid_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx) +{ + if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */)) + get_data_overflow_check = 1; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops rdesc_fixup_get_data_overflow = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow, +}; + SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) { From 66f57dc92aeb60f65fabd6f5b407326fc4f9a703 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 30 Jun 2026 22:23:47 +0000 Subject: [PATCH 1649/3297] arm64/sysreg: Fix BWE field encoding in ID_AA64DFR2_EL1 [ Upstream commit 6666336d8fe5f3f0cd3f8da2865ca78110a6e170 ] Commit 93d7356e4b30 ("arm64: sysreg: Describe ID_AA64DFR2_EL1 fields") encodes the FEAT_BWE2 value of the BWE field as '0b0002'. Binary literals only accept the digits 0 and 1, so the intended value is 2, i.e. 0b0010. The macro generated by gen-sysreg.awk currently expands to #define ID_AA64DFR2_EL1_BWE_FEAT_BWE2 UL(0b0002) is not legal C and would fail to compile if any in-tree code referenced it. At present no caller uses this enum value, so the kernel still builds cleanly, but the bug is latent. Fix the typo by using the correct binary literal 0b0010. Cc: Bin Guo Fixes: 93d7356e4b30 ("arm64: sysreg: Describe ID_AA64DFR2_EL1 fields") Signed-off-by: Jia He Reviewed-by: Mark Brown Reviewed-by: Oliver Upton Acked-by: Marc Zyngier Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/tools/sysreg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 1c6cdf9d54bba..de46df0e3b115 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1793,7 +1793,7 @@ Res0 15:8 UnsignedEnum 7:4 BWE 0b0000 NI 0b0001 FEAT_BWE - 0b0002 FEAT_BWE2 + 0b0010 FEAT_BWE2 EndEnum UnsignedEnum 3:0 STEP 0b0000 NI From b153cfe84b1340c69a13d0957665a2bfcf21239c Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 29 Jun 2026 21:51:21 -0700 Subject: [PATCH 1650/3297] net: usb: net1080: validate packet_len before pad-byte access in rx_fixup [ Upstream commit 03f384bc0cb8d4a1301d4f5b0baef2d980258383 ] For an even packet_len, net1080_rx_fixup() reads the pad byte at skb->data[packet_len] before the skb->len != packet_len check further down, and packet_len is only bounded against NC_MAX_PACKET. A malicious NetChip 1080 device can send a short frame advertising a large even packet_len (e.g. 0x4000), so the pad-byte read lands past the end of the skb: BUG: KASAN: slab-out-of-bounds in net1080_rx_fixup Read of size 1 at addr ffff8880106c83c6 by task ksoftirqd/0/14 ... net1080_rx_fixup (drivers/net/usb/net1080.c:384) usbnet_bh (drivers/net/usb/usbnet.c:1589) process_one_work (kernel/workqueue.c:3322) bh_worker (kernel/workqueue.c:3708) tasklet_action (kernel/softirq.c:965) handle_softirqs (kernel/softirq.c:622) ... Reject the frame when packet_len >= skb->len before reading. Fixes: 904813cd8a0b ("[PATCH] USB: usbnet (4/9) module for net1080 cables") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630045121.1565324-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/usb/net1080.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c index 5d4a1fd2b5244..19f6e1222d931 100644 --- a/drivers/net/usb/net1080.c +++ b/drivers/net/usb/net1080.c @@ -381,7 +381,7 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_trim(skb, skb->len - sizeof *trailer); if ((packet_len & 0x01) == 0) { - if (skb->data [packet_len] != PAD_BYTE) { + if (packet_len >= skb->len || skb->data[packet_len] != PAD_BYTE) { dev->net->stats.rx_frame_errors++; netdev_dbg(dev->net, "bad pad\n"); return 0; From 4a4a1d41c6e901e773bcf795f562a47fa71f692a Mon Sep 17 00:00:00 2001 From: Qihang Date: Wed, 1 Jul 2026 10:26:17 +0800 Subject: [PATCH 1651/3297] gue: validate REMCSUM private option length [ Upstream commit d335dcc6f521571d57117b8deeebc940836e5450 ] GUE private flags can indicate that remote checksum offload metadata is present. The private flags field itself is accounted for by guehdr_flags_len(), but guehdr_priv_flags_len() currently returns 0 even when GUE_PFLAG_REMCSUM is set. This lets a packet with only the private flags field pass validate_gue_flags(), after which gue_remcsum() and gue_gro_remcsum() read the missing REMCSUM start/offset fields from the following bytes. Account for GUE_PLEN_REMCSUM when GUE_PFLAG_REMCSUM is present so that malformed packets are rejected during option validation. Fixes: c1aa8347e73e ("gue: Protocol constants for remote checksum offload") Signed-off-by: Qihang Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- include/net/gue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/gue.h b/include/net/gue.h index dfca298bec9cc..caefd6da86939 100644 --- a/include/net/gue.h +++ b/include/net/gue.h @@ -80,7 +80,7 @@ static inline size_t guehdr_flags_len(__be16 flags) static inline size_t guehdr_priv_flags_len(__be32 flags) { - return 0; + return (flags & GUE_PFLAG_REMCSUM) ? GUE_PLEN_REMCSUM : 0; } /* Validate standard and private flags. Returns non-zero (meaning invalid) From a597a722fb71a534138c20359b655726622b5f17 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sun, 28 Jun 2026 16:05:54 +0800 Subject: [PATCH 1652/3297] netfilter: xt_u32: reject invalid shift counts [ Upstream commit 64cdf7d30ac18e43df6c48004435febb965809a8 ] u32_match_it() executes rule-supplied shift operands on a 32-bit value. A malformed u32 rule can provide a shift count of 32 or more, triggering an undefined shift out-of-bounds during packet evaluation. Validate XT_U32_LEFTSH and XT_U32_RIGHTSH operands in u32_mt_checkentry() and reject malformed rules before they reach the packet path. Fixes: 1b50b8a371e9 ("[NETFILTER]: Add u32 match") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/xt_u32.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index 1ae7f926b0df7..dabbaa742874c 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -106,7 +106,7 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) { const struct xt_u32 *data = par->matchinfo; const struct xt_u32_test *ct; - unsigned int i; + unsigned int i, j; if (data->ntests > ARRAY_SIZE(data->tests)) return -EINVAL; @@ -117,6 +117,16 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) if (ct->nnums > ARRAY_SIZE(ct->location) || ct->nvalues > ARRAY_SIZE(ct->value)) return -EINVAL; + + for (j = 1; j < ct->nnums; ++j) { + switch (ct->location[j].nextop) { + case XT_U32_LEFTSH: + case XT_U32_RIGHTSH: + if (ct->location[j].number >= 32) + return -EINVAL; + break; + } + } } return 0; From e702f6dd5d21e331f55fd9168c0210542008546d Mon Sep 17 00:00:00 2001 From: Feng Wu Date: Thu, 25 Jun 2026 08:44:25 +0000 Subject: [PATCH 1653/3297] netfilter: xt_rateest: fix u64 truncation in xt_rateest_mt() [ Upstream commit 444853cd438201007da5359821adcc2995655ab1 ] On links faster than ~34 Gbps, where byte rate may exceed 2^32-1 (~ 4.3 GBps), the comparison result becomes incorrect because the truncated value no longer reflects the actual estimator rate. Fix by changing the local variables to u64. Fixes: 1c0d32fde5bd ("net_sched: gen_estimator: complete rewrite of rate estimators") Signed-off-by: Feng Wu Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/xt_rateest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index b1d736c15fcbe..7c05b63425784 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -16,7 +16,7 @@ xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; struct gnet_stats_rate_est64 sample = {0}; - u_int32_t bps1, bps2, pps1, pps2; + u64 bps1, bps2, pps1, pps2; bool ret = true; gen_estimator_read(&info->est1->rate_est, &sample); From 3d441be2b1c5e98167302fa1c7b61960a067b112 Mon Sep 17 00:00:00 2001 From: Zhixing Chen Date: Wed, 1 Jul 2026 18:09:30 +0800 Subject: [PATCH 1654/3297] netfilter: ip6tables: mark malformed IPv6 extension headers for hotdrop [ Upstream commit 43ccc20b5a733226417832cf16ef45322e594990 ] The ah, hbh and rt matches check that the fixed extension header is present, then use the header length field to derive the advertised extension header length for matching. For the ah match, add the missing advertised-length check. For hbh and rt, update the existing advertised-length checks. In all three cases, set hotdrop to true before returning false when the advertised extension header length exceeds the available skb data. Returning false treats the packet as a rule mismatch. Set hotdrop to true and drop malformed packets so they cannot bypass rules intended to drop packets with these IPv6 extension headers. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zhixing Chen Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/ipv6/netfilter/ip6t_ah.c | 5 +++++ net/ipv6/netfilter/ip6t_hbh.c | 1 + net/ipv6/netfilter/ip6t_rt.c | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 70da2f2ce064c..1258783ed8762 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -56,6 +56,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par) } hdrlen = ipv6_authlen(ah); + if (skb->len - ptr < hdrlen) { + /* Packet smaller than its length field */ + par->hotdrop = true; + return false; + } pr_debug("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen); pr_debug("RES %04X ", ah->reserved); diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index 450dd53846a2f..6d1a5d2026a67 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -75,6 +75,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(oh); if (skb->len - ptr < hdrlen) { /* Packet smaller than it's length field */ + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 5561bd9cea818..278b52752f364 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -56,7 +56,8 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(rh); if (skb->len - ptr < hdrlen) { - /* Pcket smaller than its length field */ + /* Packet smaller than its length field */ + par->hotdrop = true; return false; } From b29b67c729de032112b2ac7b366a32d303ba5150 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 2 Jul 2026 14:33:09 +0200 Subject: [PATCH 1655/3297] netfilter: nft_set_rbtree: get command skips end element with open interval [ Upstream commit d63611cbe8af99dd61b118ee6e5b5e3e518250b2 ] The get command on intervals provide partial matches such as subranges for usability reasons. However, an open interval has no closing end element. If the closing element matches within the range of the open internal, ie. its closest match is the start element of the open range, then, return 0 but offer no matching element to userspace through netlink as a special case. Userspace provides at least a matching start element in this case and the closing end element matching the open interal is ignored. Another possibility is to report the matching start element of the open interval for this end interval. However, this results in duplicated matching being listed in userspace because userspace does not expect a start element as response to a end element. Fixes: 2aa34191f06f ("netfilter: nft_set_rbtree: use binary search array in get command") Reported-by: Melbin K Mathew Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 3 +++ net/netfilter/nft_set_rbtree.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 64e6a95439abd..0842916c19f50 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -6552,6 +6552,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set, if (err < 0) return err; + if (!elem.priv) + return 0; + err = -ENOMEM; skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); if (skb == NULL) diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 154bf2772e27d..a698420ab2b8c 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -184,10 +184,14 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set, if (!interval || nft_set_elem_expired(interval->from)) return ERR_PTR(-ENOENT); - if (flags & NFT_SET_ELEM_INTERVAL_END) + if (flags & NFT_SET_ELEM_INTERVAL_END) { + if (!interval->to) + return NULL; + rbe = container_of(interval->to, struct nft_rbtree_elem, ext); - else + } else { rbe = container_of(interval->from, struct nft_rbtree_elem, ext); + } return &rbe->priv; } From 4eef84b09a3836919360c4232b0f16651a155eec Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Fri, 3 Jul 2026 13:04:46 +0800 Subject: [PATCH 1656/3297] netfilter: xt_connmark: reject invalid shift parameters [ Upstream commit 1b47026fb4b35bac850ad6e8a4ad7fc018e09ebc ] Revision 2 of the CONNMARK target accepts user-controlled shift parameters and applies them to 32-bit mark values in connmark_tg_shift(). A shift_bits value of 32 or more triggers an undefined-shift bug when the rule is evaluated. Invalid shift_dir values are also accepted and silently fall back to the left-shift path. Reject invalid revision-2 shift parameters in connmark_tg_check() so malformed rules fail at installation time, before they can reach the packet path. Fixes: 472a73e00757 ("netfilter: xt_conntrack: Support bit-shifting for CONNMARK & MARK targets.") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Reviewed-by: Ren Wei Reviewed-by: Phil Sutter Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/xt_connmark.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 4277084de2e70..2cf27f7d59b95 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -112,6 +112,16 @@ static int connmark_tg_check(const struct xt_tgchk_param *par) return ret; } +static int connmark_tg_check_v2(const struct xt_tgchk_param *par) +{ + const struct xt_connmark_tginfo2 *info = par->targinfo; + + if (info->shift_dir > D_SHIFT_RIGHT || info->shift_bits >= 32) + return -EINVAL; + + return connmark_tg_check(par); +} + static void connmark_tg_destroy(const struct xt_tgdtor_param *par) { nf_ct_netns_put(par->net, par->family); @@ -162,7 +172,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV4, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, @@ -183,7 +193,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV6, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, From 6a802de97a8bf8d5f1e4a048c67de9a8c2d2f9eb Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:16 +0300 Subject: [PATCH 1657/3297] net/mlx5: LAG, MPESW, Fix missing complete() on devcom error [ Upstream commit d4b85f9a668b9c44216bb78daf4ec1a915cc92d1 ] mlx5_mpesw_work() returned without calling complete() when mlx5_lag_get_devcom_comp() returned NULL. A caller that queued the work and waited on mpesww->comp would block indefinitely. Funnel the early-return path through a new "complete" label so the waiter is always woken. Fixes: b430c1b4f63b ("net/mlx5: Replace global mlx5_intf_lock with HCA devcom component lock") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index c217998604fdb..fc914ee259869 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -139,8 +139,10 @@ static void mlx5_mpesw_work(struct work_struct *work) struct mlx5_lag *ldev = mpesww->lag; devcom = mlx5_lag_get_devcom_comp(ldev); - if (!devcom) - return; + if (!devcom) { + mpesww->result = -ENODEV; + goto complete; + } mlx5_devcom_comp_lock(devcom); mutex_lock(&ldev->lock); @@ -156,6 +158,7 @@ static void mlx5_mpesw_work(struct work_struct *work) unlock: mutex_unlock(&ldev->lock); mlx5_devcom_comp_unlock(devcom); +complete: complete(&mpesww->comp); } From 420aabb32da4381d8d7cdcaa6a77fad9eaceb0a4 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:49 +0300 Subject: [PATCH 1658/3297] net/mlx5e: Fix HV VHCA stats zero-sized buffer allocation [ Upstream commit 25f6b929c7e379cbea7cb8caa67b49b2d1efae17 ] mlx5e_hv_vhca_stats_create() is called from mlx5e_nic_enable(), before mlx5e_open(). At that point priv->stats_nch is still zero, because it is only ever incremented in mlx5e_channel_stats_alloc(), which is reached only from mlx5e_open_channel(). mlx5e_hv_vhca_stats_buf_size() therefore returns 0, and kvzalloc(0, GFP_KERNEL) returns ZERO_SIZE_PTR ((void *)16) rather than NULL. The "if (!buf)" guard does not catch this, and mlx5e_hv_vhca_stats_create() completes "successfully" with priv->stats_agent.buf set to ZERO_SIZE_PTR. Once channels are opened (priv->stats_nch > 0) and the hypervisor enables stats reporting, mlx5e_hv_vhca_stats_work() recomputes buf_len using the new non-zero stats_nch and calls memset(buf, 0, buf_len) on ZERO_SIZE_PTR, faulting at address 0x10. Allocate the buffer based on priv->max_nch, which is set in mlx5e_priv_init() and is the upper bound on stats_nch: - Add a separate helper mlx5e_hv_vhca_stats_buf_max_size() that returns sizeof(per_ring_stats) * max(max_nch, stats_nch), and use it for the kvzalloc() in mlx5e_hv_vhca_stats_create(). - Keep mlx5e_hv_vhca_stats_buf_size() (which returns based on stats_nch) for the worker's active payload size, so the wire format (block->rings = stats_nch) and the amount of data filled by mlx5e_hv_vhca_fill_stats() are unchanged. The max(max_nch, stats_nch) guard handles the rare case where mlx5e_attach_netdev() recomputes max_nch downward across a detach/resume cycle while priv->stats_nch persists (mlx5e_detach_netdev does not call mlx5e_priv_cleanup, so stats_nch is only reset when the netdev is destroyed). Without the guard, the worker could compute buf_len from stats_nch and overrun the smaller buffer allocated based on the reduced max_nch. Allocating a non-zero buffer also makes the kvzalloc() failure path in mlx5e_hv_vhca_stats_create() reachable for the first time: it returns early without (re)creating the agent. Clear priv->stats_agent.{agent,buf} in mlx5e_hv_vhca_stats_destroy() after freeing them, so that if a later create() bails out on this path, a subsequent teardown does not double-free the stale agent/buffer left from a previous enable/disable cycle. This mirrors the existing mlx5e pattern of preallocating arrays of size max_nch (e.g. priv->channel_stats) and lazily populating entries up to stats_nch on demand. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 195863b2c013e..72f3ca4dd076b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -54,6 +54,12 @@ static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) priv->stats_nch); } +static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) +{ + return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * + max(priv->max_nch, priv->stats_nch)); +} + static void mlx5e_hv_vhca_stats_work(struct work_struct *work) { struct mlx5e_hv_vhca_stats_agent *sagent; @@ -122,7 +128,7 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent) void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) { - int buf_len = mlx5e_hv_vhca_stats_buf_size(priv); + int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv); struct mlx5_hv_vhca_agent *agent; priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL); @@ -155,5 +161,7 @@ void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) return; mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); + priv->stats_agent.agent = NULL; kvfree(priv->stats_agent.buf); + priv->stats_agent.buf = NULL; } From 60fddda7207d81fea71463abd403f0b10f74f2e1 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:50 +0300 Subject: [PATCH 1659/3297] net/mlx5e: Fix HV VHCA stats agent registration race [ Upstream commit 89b25b5f46f488ea3b29b3444864c76944c9075b ] mlx5e_hv_vhca_stats_create() registers the stats agent through mlx5_hv_vhca_agent_create(). The helper publishes the agent in hv_vhca->agents[type] under agents_lock and immediately schedules an asynchronous control invalidation on the HV VHCA workqueue before returning to mlx5e. The asynchronous invalidation invokes the control agent's invalidate callback, which reads the hypervisor control block and forwards the command to mlx5e_hv_vhca_stats_control(). That callback may either: - call cancel_delayed_work_sync(&priv->stats_agent.work), or - call queue_delayed_work(priv->wq, &sagent->work, sagent->delay). However, the delayed_work and priv->stats_agent.agent are only initialized after mlx5_hv_vhca_agent_create() returns to mlx5e: agent = mlx5_hv_vhca_agent_create(...); /* publish + invalidate */ ... priv->stats_agent.agent = agent; /* too late */ INIT_DELAYED_WORK(&priv->stats_agent.work, ...); /* too late */ If the asynchronous control path runs before the two assignments above, it can: - Operate on an uninitialized delayed_work whose timer.function is NULL. queue_delayed_work() calls add_timer() unconditionally, so when the timer expires the timer softirq invokes a NULL function pointer. - Re-initialize the timer later through INIT_DELAYED_WORK() while the timer is already enqueued in the timer wheel, corrupting the hlist (entry.pprev cleared while the previous bucket node still points at this entry). - When the worker eventually runs, mlx5e_hv_vhca_stats_work() reads sagent->agent (NULL) and dereferences it inside mlx5_hv_vhca_agent_write(). Fix this by: - Initializing priv->stats_agent.work before invoking mlx5_hv_vhca_agent_create(), so the work is always in a valid state when the control callback observes it. - Adding a struct mlx5_hv_vhca_agent **ctx_update out-parameter to mlx5_hv_vhca_agent_create(). The helper writes the agent pointer to *ctx_update before publishing into hv_vhca->agents[] and triggering the agents_update flow, so any callback subsequently invoked from that flow already sees a valid priv->stats_agent.agent. This avoids having the control callback participate in agent initialization. While at it, access priv->stats_agent.agent with READ_ONCE()/WRITE_ONCE() for the cross-CPU access with the worker, and clear priv->stats_agent.buf on the agent_create() failure path. Fixes: cef35af34d6d ("net/mlx5e: Add mlx5e HV VHCA stats agent") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../mellanox/mlx5/core/en/hv_vhca_stats.c | 21 +++++++++++-------- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 8 +++++-- .../ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 6 ++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 72f3ca4dd076b..cdaf77650164b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -73,7 +73,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work) sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work); priv = container_of(sagent, struct mlx5e_priv, stats_agent); buf_len = mlx5e_hv_vhca_stats_buf_size(priv); - agent = sagent->agent; + agent = READ_ONCE(sagent->agent); buf = sagent->buf; memset(buf, 0, buf_len); @@ -135,11 +135,14 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) if (!priv->stats_agent.buf) return; + INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); + agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca, MLX5_HV_VHCA_AGENT_STATS, mlx5e_hv_vhca_stats_control, NULL, mlx5e_hv_vhca_stats_cleanup, - priv); + priv, + &priv->stats_agent.agent); if (IS_ERR_OR_NULL(agent)) { if (IS_ERR(agent)) @@ -148,20 +151,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) agent); kvfree(priv->stats_agent.buf); - return; + priv->stats_agent.buf = NULL; } - - priv->stats_agent.agent = agent; - INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); } void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) { - if (IS_ERR_OR_NULL(priv->stats_agent.agent)) + struct mlx5_hv_vhca_agent *agent; + + agent = READ_ONCE(priv->stats_agent.agent); + if (IS_ERR_OR_NULL(agent)) return; - mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); - priv->stats_agent.agent = NULL; + mlx5_hv_vhca_agent_destroy(agent); + WRITE_ONCE(priv->stats_agent.agent, NULL); kvfree(priv->stats_agent.buf); priv->stats_agent.buf = NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c index 30564d9b00e9f..779342ebc843e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c @@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca) return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL, NULL, mlx5_hv_vhca_control_agent_invalidate, - NULL, NULL); + NULL, NULL, NULL); } static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent) @@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleaup)(struct mlx5_hv_vhca_agent *agent), - void *priv) + void *priv, + struct mlx5_hv_vhca_agent **ctx_update) { struct mlx5_hv_vhca_agent *agent; @@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, agent->invalidate = invalidate; agent->cleanup = cleaup; + if (ctx_update) + WRITE_ONCE(*ctx_update, agent); + mutex_lock(&hv_vhca->agents_lock); hv_vhca->agents[type] = agent; mutex_unlock(&hv_vhca->agents_lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h index f240ffe5116c5..8b3974cf0ee49 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h @@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context); + void *context, + struct mlx5_hv_vhca_agent **ctx_update); void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent); int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent, @@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context) + void *context, + struct mlx5_hv_vhca_agent **ctx_update) { return NULL; } From 5c7e3755abf663f033de24f917b77685e9543045 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:51 +0300 Subject: [PATCH 1660/3297] net/mlx5e: Fix publication race for priv->channel_stats[] [ Upstream commit 5a799714e8ca0bce9ea40694f49914cf1adbbaa9 ] mlx5e_channel_stats_alloc() publishes a new entry to priv->channel_stats[] and then increments priv->stats_nch as a publication token, but neither store carries any memory barrier: priv->channel_stats[ix] = kvzalloc_node(...); if (!priv->channel_stats[ix]) return -ENOMEM; priv->stats_nch++; Concurrent readers compute the loop bound from priv->stats_nch and then dereference priv->channel_stats[i] using plain accesses, e.g. for (i = 0; i < priv->stats_nch; i++) { struct mlx5e_channel_stats *cs = priv->channel_stats[i]; ... cs->rq.packets ... } On weakly-ordered architectures (ARM, PowerPC, RISC-V) the writes to channel_stats[ix] and stats_nch may become visible to other CPUs out of program order. A reader can observe stats_nch == N while still seeing channel_stats[N-1] == NULL, leading to a NULL pointer dereference in the channel_stats loop. This has been observed in production on BlueField-3 DPUs (arm64), where ovs-vswitchd queries netdev statistics over netlink during NIC bringup, racing mlx5e_open_channel() -> mlx5e_channel_stats_alloc() on another CPU: Unable to handle kernel NULL pointer dereference at virtual address 0x840 Hardware name: BlueField-3 DPU pc : mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] Call trace: mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] dev_get_stats+0x50/0xc0 ovs_vport_get_stats+0x38/0xac [openvswitch] ovs_vport_cmd_fill_info+0x194/0x290 [openvswitch] ovs_vport_cmd_get+0xbc/0x10c [openvswitch] genl_family_rcv_msg_doit+0xd0/0x160 genl_rcv_msg+0xec/0x1f0 netlink_rcv_skb+0x64/0x130 genl_rcv+0x40/0x60 netlink_unicast+0x2fc/0x370 netlink_sendmsg+0x1dc/0x454 ... __arm64_sys_sendmsg+0x2c/0x40 Add mlx5e_stats_nch_write() and mlx5e_stats_nch_read() helpers in en.h that wrap the smp_store_release()/smp_load_acquire() pair on stats_nch. The release/acquire pair establishes the contract: stats_nch == N => channel_stats[0..N-1] are visible and non-NULL. Publish the stats_nch increment via mlx5e_stats_nch_write() in the writer (mlx5e_channel_stats_alloc()), and read stats_nch via mlx5e_stats_nch_read() in all readers: mlx5e RX/TX queue stats, mlx5e_get_base_stats(), ethtool channels stats, IPoIB stats, the sw_stats fold and the HV VHCA stats agent. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Reviewed-by: Cosmin Ratiu Reviewed-by: Nimrod Oren Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-4-tariqt@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++++++++++++ .../ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 ++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 14 ++++++++------ drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 9 +++++---- .../net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 3 ++- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 5bced924a24fb..3d8276a0692fc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -963,6 +963,18 @@ struct mlx5e_priv { struct ethtool_fec_hist_range *fec_ranges; }; +static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv) +{ + /* Pairs with smp_store_release in mlx5e_stats_nch_write(). */ + return smp_load_acquire(&priv->stats_nch); +} + +static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n) +{ + /* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */ + smp_store_release(&priv->stats_nch, n); +} + struct mlx5e_dev { struct net_device *netdev; struct devlink_port dl_port; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index cdaf77650164b..631f802105d5b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch, static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, int buf_len) { + u16 nch = mlx5e_stats_nch_read(priv); int ch, i = 0; - for (ch = 0; ch < priv->stats_nch; ch++) { + for (ch = 0; ch < nch; ch++) { void *buf = data + i; if (WARN_ON_ONCE(buf + @@ -50,8 +51,9 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) { - return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * - priv->stats_nch); + u16 nch = mlx5e_stats_nch_read(priv); + + return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch; } static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) @@ -106,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent, sagent = &priv->stats_agent; block->version = MLX5_HV_VHCA_STATS_VERSION; - block->rings = priv->stats_nch; + block->rings = mlx5e_stats_nch_read(priv); if (!block->command) { cancel_delayed_work_sync(&priv->stats_agent.work); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a696fb88dbef9..f660ef37b0a63 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2718,7 +2718,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu) GFP_KERNEL, cpu_to_node(cpu)); if (!priv->channel_stats[ix]) return -ENOMEM; - priv->stats_nch++; + mlx5e_stats_nch_write(priv, priv->stats_nch + 1); return 0; } @@ -3951,9 +3951,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s) { + u16 nch = mlx5e_stats_nch_read(priv); int i; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq; struct mlx5e_rq_stats *rq_stats = &channel_stats->rq; @@ -5411,7 +5412,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i, struct mlx5e_rq_stats *xskrq_stats; struct mlx5e_rq_stats *rq_stats; - if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch) + if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv)) return; channel_stats = priv->channel_stats[i]; @@ -5430,7 +5431,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i, struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_sq_stats *sq_stats; - if (!priv->stats_nch) + if (!mlx5e_stats_nch_read(priv)) return; /* no special case needed for ptp htb etc since txq2sq_stats is kept up @@ -5447,6 +5448,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, struct netdev_queue_stats_tx *tx) { struct mlx5e_priv *priv = netdev_priv(dev); + u16 nch = mlx5e_stats_nch_read(priv); struct mlx5e_ptp *ptp_channel; int i, tc; @@ -5455,7 +5457,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, rx->bytes = 0; rx->alloc_fail = 0; - for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) { + for (i = priv->channels.params.num_channels; i < nch; i++) { struct netdev_queue_stats_rx rx_i = {0}; mlx5e_get_queue_stats_rx(dev, i, &rx_i); @@ -5480,7 +5482,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, tx->packets = 0; tx->bytes = 0; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; /* handle two cases: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index a8af84fc97638..5ae501f448338 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -516,6 +516,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c) static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) { struct mlx5e_sw_stats *s = &priv->stats.sw; + u16 nch = mlx5e_stats_nch_read(priv); int i; memset(s, 0, sizeof(*s)); @@ -523,7 +524,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) for (i = 0; i < priv->channels.num; i++) /* for active channels only */ mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]); - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; @@ -2585,7 +2586,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; } static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) { - int max_nch = priv->stats_nch; + int max_nch = mlx5e_stats_nch_read(priv); return (NUM_RQ_STATS * max_nch) + (NUM_CH_STATS * max_nch) + @@ -2598,8 +2599,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) @@ -2631,8 +2632,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 79ae3a51a4b3e..58fd7a893b6b6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -117,10 +117,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv) static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv) { + u16 nch = mlx5e_stats_nch_read(priv); struct rtnl_link_stats64 s = {}; int i, j; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats; struct mlx5e_rq_stats *rq_stats; From 1e71a40d101547380590db582213c1f1dce1f041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Emil=20Schulz=20=C3=98stergaard?= Date: Tue, 30 Jun 2026 14:20:13 +0200 Subject: [PATCH 1661/3297] net: microchip: vcap: fix races on the shared Super VCAP block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d7a8d500d7e42837bd8dce40cb52c97c6e8706a9 ] The VCAP instances on a chip are not independent, yet they are locked independently. On sparx5 and lan969x the IS0 and IS2 instances are backed by the same Super VCAP hardware block and share its cache and command registers: every access drives the shared VCAP_SUPER_CTRL register and moves data through the shared cache registers. Accessing one instance therefore races with accessing another. The per-instance admin->lock cannot prevent this, as each instance takes a different lock. The locking issue is mostly disguised by the fact that the core usage of the vcap api runs under rtnl. However, the full rule dump in debugfs decodes rules straight from hardware (a READ command followed by a cache read) and runs outside rtnl, so it races a concurrent tc-flower rule write to another Super VCAP instance. Besides corrupting the dump, the read repopulates the shared cache between the writers cache fill and its write command, so the writer commits the wrong data and corrupts the hardware entry. Introduce vcap_lock() and vcap_unlock() helpers and route every rule lock site in the VCAP API and its debugfs code through them. Replace the per-instance admin->lock with a single mutex in struct vcap_control that serializes access to all instances. The helpers reach it through a new admin->vctrl back-pointer, and the clients initialise and destroy the control lock instead of a per-instance one. No path holds more than one instance lock, so collapsing them onto a single mutex cannot self-deadlock. Fixes: 71c9de995260 ("net: microchip: sparx5: Add VCAP locking to protect rules") Signed-off-by: Jens Emil Schulz Østergaard Link: https://patch.msgid.link/20260630-microchip_fix_vcap_locking-v1-1-f60a4596734d@microchip.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../microchip/lan966x/lan966x_vcap_impl.c | 5 +- .../microchip/sparx5/sparx5_vcap_impl.c | 5 +- .../net/ethernet/microchip/vcap/vcap_api.c | 72 +++++++++++-------- .../net/ethernet/microchip/vcap/vcap_api.h | 3 +- .../microchip/vcap/vcap_api_debugfs.c | 8 +-- .../microchip/vcap/vcap_api_debugfs_kunit.c | 3 +- .../ethernet/microchip/vcap/vcap_api_kunit.c | 3 +- .../microchip/vcap/vcap_api_private.h | 3 + 8 files changed, 60 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 2a37fc1ba4bcd..dca60c818d4b8 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin) kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); - mutex_destroy(&admin->lock); kfree(admin); } @@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, if (!admin) return ERR_PTR(-ENOMEM); - mutex_init(&admin->lock); + admin->vctrl = ctrl; INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); @@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x) ctrl->ops = &lan966x_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { cfg = &lan966x_vcap_inst_cfg[i]; @@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x) lan966x_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 25066ddb8d4d6..f426f37dd6191 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -1930,7 +1930,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin) { if (!admin) return; - mutex_destroy(&admin->lock); kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); @@ -1950,7 +1949,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = ctrl; admin->vtype = cfg->vtype; admin->vinst = cfg->vinst; admin->ingress = cfg->ingress; @@ -2059,6 +2058,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5) ctrl->ops = &sparx5_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) { cfg = &consts->vcaps_cfg[idx]; admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg); @@ -2097,5 +2097,6 @@ void sparx5_vcap_destroy(struct sparx5 *sparx5) list_del(&admin->list); sparx5_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 2687765abe520..49eef025d2900 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -934,6 +934,16 @@ static bool vcap_rule_exists(struct vcap_control *vctrl, u32 id) return false; } +void vcap_lock(struct vcap_admin *admin) +{ + mutex_lock(&admin->vctrl->lock); +} + +void vcap_unlock(struct vcap_admin *admin) +{ + mutex_unlock(&admin->vctrl->lock); +} + /* Find a rule with a provided rule id return a locked vcap */ static struct vcap_rule_internal * vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) @@ -943,11 +953,11 @@ vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) if (ri->data.id == id) return ri; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return NULL; } @@ -961,14 +971,14 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie == cookie) { id = ri->data.id; break; } } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (id) return id; } @@ -985,11 +995,11 @@ int vcap_admin_rule_count(struct vcap_admin *admin, int cid) int count = 0; list_for_each_entry(elem, &admin->rules, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); if (elem->data.vcap_chain_id >= min_cid && elem->data.vcap_chain_id < max_cid) ++count; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return count; } @@ -2266,7 +2276,7 @@ int vcap_add_rule(struct vcap_rule *rule) if (ret) return ret; /* Insert the new rule in the list of vcap rules */ - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); vcap_rule_set_state(ri); ret = vcap_insert_rule(ri, &move); @@ -2302,7 +2312,7 @@ int vcap_add_rule(struct vcap_rule *rule) goto out; } out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return ret; } EXPORT_SYMBOL_GPL(vcap_add_rule); @@ -2330,7 +2340,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, if (vctrl->vcaps[admin->vtype].rows == 0) return ERR_PTR(-EINVAL); - mutex_lock(&admin->lock); + vcap_lock(admin); /* Check if a rule with this id already exists */ if (vcap_rule_exists(vctrl, id)) { err = -EINVAL; @@ -2369,13 +2379,13 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, goto out_free; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return (struct vcap_rule *)ri; out_free: kfree(ri); out_unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return ERR_PTR(err); } @@ -2446,7 +2456,7 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id) return ERR_PTR(-ENOENT); rule = vcap_decode_rule(elem); - mutex_unlock(&elem->admin->lock); + vcap_unlock(elem->admin); return rule; } EXPORT_SYMBOL_GPL(vcap_get_rule); @@ -2483,7 +2493,7 @@ int vcap_mod_rule(struct vcap_rule *rule) err = vcap_write_counter(ri, &ctr); out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } EXPORT_SYMBOL_GPL(vcap_mod_rule); @@ -2570,7 +2580,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) admin->last_used_addr = elem->addr; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_del_rule); @@ -2585,7 +2595,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) if (ret) return ret; - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry_safe(ri, next_ri, &admin->rules, list) { vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size); list_del(&ri->list); @@ -2598,7 +2608,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) list_del(&eport->list); kfree(eport); } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return 0; } @@ -3016,7 +3026,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the offloadable rules */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { /* Is the rule in the lookup defined by the chain */ if (!(ri->data.vcap_chain_id >= chain && @@ -3034,7 +3044,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3074,7 +3084,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the rules on the chain */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.vcap_chain_id != chain) continue; @@ -3089,7 +3099,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3133,9 +3143,9 @@ static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev, eport->cookie = cookie; eport->src_cid = src_cid; eport->dst_cid = dst_cid; - mutex_lock(&admin->lock); + vcap_lock(admin); list_add_tail(&eport->list, &admin->enabled); - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (vcap_path_exist(vctrl, ndev, src_cid)) { /* Enable chained lookups */ @@ -3185,9 +3195,9 @@ static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev, dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid); } - mutex_lock(&found->lock); + vcap_lock(found); list_del(&eport->list); - mutex_unlock(&found->lock); + vcap_unlock(found); kfree(eport); return 0; } @@ -3270,9 +3280,9 @@ int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_write_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3291,9 +3301,9 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_read_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3395,7 +3405,7 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, /* Iterate all rules in each VCAP instance */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie != cookie) continue; @@ -3412,12 +3422,12 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, if (err) goto unlock; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return err; unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_get_rule_count_by_cookie); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index 6069ad95c27e9..05b4b02e59ef5 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -164,7 +164,7 @@ struct vcap_admin { struct list_head list; /* for insertion in vcap_control */ struct list_head rules; /* list of rules */ struct list_head enabled; /* list of enabled ports */ - struct mutex lock; /* control access to rules */ + struct vcap_control *vctrl; /* the control instance owning this vcap */ enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ int first_cid; /* first chain id in this vcap */ @@ -275,6 +275,7 @@ struct vcap_control { const struct vcap_info *vcaps; /* client supplied vcap models */ const struct vcap_statistics *stats; /* client supplied vcap stats */ struct list_head list; /* list of vcap instances */ + struct mutex lock; /* serialize access to all vcap instances */ }; #endif /* __VCAP_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 59bfbda29bb3d..e0c65c7ab23e1 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_debugfs); @@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin_raw(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c index 9c9d380421255..ac2a3b8c4f321 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index 16eb3de60eb6d..70683855cbfce 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 844bdf6b5f450..b4057fbe3d188 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -50,6 +50,9 @@ struct vcap_stream_iter { /* Check that the control has a valid set of callbacks */ int vcap_api_check(struct vcap_control *ctrl); +/* Serialize access to the vcap instances of a control */ +void vcap_lock(struct vcap_admin *admin); +void vcap_unlock(struct vcap_admin *admin); /* Erase the VCAP cache area used or encoding and decoding */ void vcap_erase_cache(struct vcap_rule_internal *ri); From b066420e57f3402a52c998678b4678252ac9bb63 Mon Sep 17 00:00:00 2001 From: Shigeru Yoshida Date: Wed, 1 Jul 2026 01:46:20 +0900 Subject: [PATCH 1662/3297] qede: fix off-by-one in BD ring consumption on build_skb failure [ Upstream commit a0a558ca7e75b49e71f8c545c30e8c005e6e4e2f ] qede_rx_build_skb() and qede_tpa_rx_build_skb() do not check for a NULL return from qede_build_skb(). When it returns NULL under memory pressure, the functions still consume a BD from the ring before returning NULL. The callers then recycle additional BDs, resulting in one extra BD being consumed (off-by-one). This desynchronizes the BD ring, which can corrupt DMA page reference counts and lead to SLUB freelist corruption. Commit 4e910dbe3650 ("qede: confirm skb is allocated before using") added a NULL check inside qede_build_skb() to prevent a NULL pointer dereference, but did not address the missing NULL checks in the callers, making this off-by-one reachable. Fix this by adding NULL checks for the return value of qede_build_skb() in both qede_rx_build_skb() and qede_tpa_rx_build_skb(), returning NULL immediately before any BD ring manipulation. Fixes: 8a8633978b84 ("qede: Add build_skb() support.") Signed-off-by: Shigeru Yoshida Reviewed-by: Jamie Bainbridge Link: https://patch.msgid.link/20260630164623.3152625-1-syoshida@redhat.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 33e18bb697740..c11e0d8f98aa5 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev, struct sk_buff *skb; skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; + bd->page_offset += rxq->rx_buf_seg_size; if (bd->page_offset == PAGE_SIZE) { @@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev, } skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; if (unlikely(qede_realloc_rx_buffer(rxq, bd))) { /* Incr page ref count to reuse on allocation failure so From 1b12612c367e4be9b0814c0468e7e687835315b4 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 10:41:09 -0700 Subject: [PATCH 1663/3297] net: qualcomm: rmnet: validate MAP frame length before ingress parsing [ Upstream commit f0f1887a9e30712a1df03e152dce6fb91344b1f3 ] When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes the skb straight to __rmnet_map_ingress_handler(), skipping the length validation that rmnet_map_deaggregate() performs on the aggregated path. The parser then dereferences the MAP header and csum header/trailer based on the on-wire pkt_len without checking skb->len, so a short frame is read out of bounds: BUG: KASAN: slab-out-of-bounds in rmnet_map_checksum_downlink_packet Read of size 1 at addr ffff88801118ed00 by task exploit/147 Call Trace: ... rmnet_map_checksum_downlink_packet (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413) __rmnet_map_ingress_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96) rmnet_rx_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129) __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089) netif_receive_skb (net/core/dev.c:6460) tun_get_user (drivers/net/tun.c:1955) tun_chr_write_iter (drivers/net/tun.c:2001) vfs_write (fs/read_write.c:688) ksys_write (fs/read_write.c:740) do_syscall_64 (arch/x86/entry/syscall_64.c:94) ... Factor that validation out of rmnet_map_deaggregate() into rmnet_map_validate_packet_len() and run it on the no-aggregation path too. The MAP header is bounds-checked first, since this path can receive a frame shorter than the header. Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation") Reported-by: Weiming Shi Suggested-by: Subash Abhinov Kasiviswanathan Signed-off-by: Xiang Mei Reviewed-by: Subash Abhinov Kasiviswanathan Link: https://patch.msgid.link/20260630174110.2003121-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../ethernet/qualcomm/rmnet/rmnet_handlers.c | 5 +- .../net/ethernet/qualcomm/rmnet/rmnet_map.h | 1 + .../ethernet/qualcomm/rmnet/rmnet_map_data.c | 72 ++++++++++--------- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 9f3479500f85a..d055a2628d8c9 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb, consume_skb(skb); } else { - __rmnet_map_ingress_handler(skb, port); + if (rmnet_map_validate_packet_len(skb, port)) + __rmnet_map_ingress_handler(skb, port); + else + kfree_skb(skb); } } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index b70284095568c..60ca8b780c88a 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port); void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size, u32 count, u32 time); +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 8b4640c5d61eb..305ae15ae8f39 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -333,54 +333,62 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, return map_header; } -/* Deaggregates a single packet - * A whole new buffer is allocated for each portion of an aggregated frame. - * Caller should keep calling deaggregate() on the source skb until 0 is - * returned, indicating that there are no more packets to deaggregate. Caller - * is responsible for freeing the original skb. - */ -struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, - struct rmnet_port *port) +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port) { struct rmnet_map_v5_csum_header *next_hdr = NULL; struct rmnet_map_header *maph; void *data = skb->data; - struct sk_buff *skbn; - u8 nexthdr_type; u32 packet_len; - if (skb->len == 0) - return NULL; + if (skb->len < sizeof(*maph)) + return 0; maph = (struct rmnet_map_header *)skb->data; + + /* Some hardware can send us empty frames. Catch them */ + if (!maph->pkt_len) + return 0; + packet_len = ntohs(maph->pkt_len) + sizeof(*maph); if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - } else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) { - if (!(maph->flags & MAP_CMD_FLAG)) { - packet_len += sizeof(*next_hdr); - if (maph->flags & MAP_NEXT_HEADER_FLAG) - next_hdr = data + sizeof(*maph); - else - /* Mapv5 data pkt without csum hdr is invalid */ - return NULL; - } + } else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) && + !(maph->flags & MAP_CMD_FLAG)) { + /* Mapv5 data pkt without csum hdr is invalid */ + if (!(maph->flags & MAP_NEXT_HEADER_FLAG)) + return 0; + + packet_len += sizeof(*next_hdr); + next_hdr = data + sizeof(*maph); } - if (((int)skb->len - (int)packet_len) < 0) - return NULL; + if (skb->len < packet_len) + return 0; - /* Some hardware can send us empty frames. Catch them */ - if (!maph->pkt_len) - return NULL; + if (next_hdr && + u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) != + RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) + return 0; - if (next_hdr) { - nexthdr_type = u8_get_bits(next_hdr->header_info, - MAPV5_HDRINFO_HDR_TYPE_FMASK); - if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) - return NULL; - } + return packet_len; +} + +/* Deaggregates a single packet + * A whole new buffer is allocated for each portion of an aggregated frame. + * Caller should keep calling deaggregate() on the source skb until 0 is + * returned, indicating that there are no more packets to deaggregate. Caller + * is responsible for freeing the original skb. + */ +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, + struct rmnet_port *port) +{ + struct sk_buff *skbn; + u32 packet_len; + + packet_len = rmnet_map_validate_packet_len(skb, port); + if (!packet_len) + return NULL; skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); if (!skbn) From 6f9b23eb92a894ae1118893996943990ee0b860e Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 1 Jul 2026 12:19:12 -0400 Subject: [PATCH 1664/3297] net/sched: act_pedit: fix TOCTOU heap OOB write in tc offload [ Upstream commit 8b519cbcabe836a441369fbec1a8a6518a709251 ] There is a TOCTOU race condition in flower lockless approach between sizing a flow_rule buffer and filling it. zdi-disclosures@trendmicro.com reports: The cls_flower classifier operates with TCF_PROTO_OPS_DOIT_UNLOCKED (fl_change runs without RTNL), while RTM_NEWACTION holds RTNL, so the independent locking domains make the race reachable in practice. KASAN confirms: BUG: KASAN: slab-out-of-bounds in tcf_pedit_offload_act_setup+0x81b/0x930 Write of size 4 at addr ffff888001f27520 by task poc-toctou/312 The buggy address is located 0 bytes to the right of allocated 288-byte region [ffff888001f27400, ffff888001f27520) (cache kmalloc-512) Note: The result is a heap OOB write attacker-controlled content into the adjacent slab object (requires CAP_NET_ADMIN). The fix introduces reading tcfp_nkeys under act->tcfa_lock in all places using a new tcf_pedit_nkeys_locked() which replaces the old tcf_pedit_nkeys(). Additionally we close the remaining TOCTOU window between the sizing read and the fill reads by more careful accounting. Rather than silently truncating the key count, which leads to incorrect action semantics offloaded to hardware and secondary OOB writes if the remaining capacity is zero or consumed by prior actions, we enforce remaining capacity checks and return -ENOSPC if the required space exceeds the remaining capacity. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260701161912.125355-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/tc_act/tc_pedit.h | 18 ++++++++---------- net/sched/act_api.c | 13 +++++++++---- net/sched/act_pedit.c | 13 +++++++++++-- net/sched/cls_api.c | 22 +++++++++++++++++----- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h index cb7b82f2cbc7f..97754ea0a8279 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -37,17 +37,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a) return false; } -static inline int tcf_pedit_nkeys(const struct tc_action *a) +/* Must be called with act->tcfa_lock held to ensure consistency of parallel + * reads of the same action's pedit keys (e.g. flow_offload count vs fill). + * Note, this is only used for pedit offload. + */ +static inline int tcf_pedit_nkeys_locked(const struct tc_action *a) { - struct tcf_pedit_parms *parms; - int nkeys; - - rcu_read_lock(); - parms = to_pedit_parms(a); - nkeys = parms->tcfp_nkeys; - rcu_read_unlock(); - - return nkeys; + lockdep_assert_held(&a->tcfa_lock); + return rcu_dereference_protected(to_pedit(a)->parms, + lockdep_is_held(&a->tcfa_lock))->tcfp_nkeys; } static inline u32 tcf_pedit_htype(const struct tc_action *a, int index) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 6afeeb6b590a2..f948646985dde 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -148,10 +148,15 @@ static void offload_action_hw_count_dec(struct tc_action *act, static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act) { - if (is_tcf_pedit(act)) - return tcf_pedit_nkeys(act); - else - return 1; + unsigned int count; + + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + count = tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + return count; + } + return 1; } static bool tc_act_skip_hw(u32 flags) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index c0a5f5d78dacd..5aa91fb145a27 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -563,9 +563,18 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, { if (bind) { struct flow_action_entry *entry = entry_data; + int nkeys = tcf_pedit_nkeys_locked(act); int k; - for (k = 0; k < tcf_pedit_nkeys(act); k++) { + /* If the required keys exceed the remaining capacity return + * -ENOSPC to abort the offload and fallback to software. + */ + if (nkeys > *index_inc) { + NL_SET_ERR_MSG_MOD(extack, "Not enough space to offload all pedit keys"); + return -ENOSPC; + } + + for (k = 0; k < nkeys; k++) { switch (tcf_pedit_cmd(act, k)) { case TCA_PEDIT_KEY_EX_CMD_SET: entry->id = FLOW_ACTION_MANGLE; @@ -602,7 +611,7 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, return -EOPNOTSUPP; } - for (k = 1; k < tcf_pedit_nkeys(act); k++) { + for (k = 1; k < tcf_pedit_nkeys_locked(act); k++) { if (cmd != tcf_pedit_cmd(act, k)) { NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload"); return -EOPNOTSUPP; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index a94de0cab29cc..3b0a438977cd5 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3887,12 +3887,21 @@ int tc_setup_action(struct flow_action *flow_action, entry = &flow_action->entries[j]; spin_lock_bh(&act->tcfa_lock); + + /* Abort the offload if we have exhausted the allocated capacity */ + if (j >= flow_action->num_entries) { + NL_SET_ERR_MSG_MOD(extack, "Flow action buffer overflow"); + err = -ENOSPC; + goto err_out_locked; + } + err = tcf_act_get_user_cookie(entry, act); if (err) goto err_out_locked; - index = 0; - err = tc_setup_offload_act(act, entry, &index, extack); + index = flow_action->num_entries - j; + err = tc_setup_offload_act(act, entry, &index, + extack); if (err) goto err_out_locked; @@ -3946,10 +3955,13 @@ unsigned int tcf_exts_num_actions(struct tcf_exts *exts) int i; tcf_exts_for_each_action(i, act, exts) { - if (is_tcf_pedit(act)) - num_acts += tcf_pedit_nkeys(act); - else + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + num_acts += tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + } else { num_acts++; + } } return num_acts; } From 3bfb96d9bc6a7ed0b99c7db329cc2e22a28d84bb Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 11:32:27 -0700 Subject: [PATCH 1665/3297] net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket [ Upstream commit 9d160b35cc34a2ba8229d07651468a7848325135 ] smc_cdc_rx_handler() looks up the connection by token under the link group's conns_lock, drops the lock, and then dereferences conn and the smc_sock derived from it, ending in sock_hold(&smc->sk) inside smc_cdc_msg_recv(). No reference is held across the lock release. The only reference pinning the socket while the connection is discoverable in the link group is taken in smc_lgr_register_conn() (sock_hold) and dropped in __smc_lgr_unregister_conn() (sock_put), both under conns_lock. Once the handler drops conns_lock, a concurrent close() -> smc_release() -> smc_conn_free() -> smc_lgr_unregister_conn() can drop that reference and free the smc_sock, so the handler's later sock_hold() runs on freed memory: WARNING: lib/refcount.c:25 at refcount_warn_saturate Workqueue: rxe_wq do_work refcount_warn_saturate (lib/refcount.c:25) smc_cdc_msg_recv (net/smc/smc_cdc.c:430) smc_cdc_rx_handler (net/smc/smc_cdc.c:502) smc_wr_rx_tasklet_fn (net/smc/smc_wr.c:445) tasklet_action_common (kernel/softirq.c:938) handle_softirqs (kernel/softirq.c:622) Kernel panic - not syncing: panic_on_warn set Only SMC-R is affected. The SMC-D receive tasklet is stopped by tasklet_kill(&conn->rx_tsklet) in smc_conn_free() before the connection is unregistered, so it cannot run concurrently with the free. Take the socket reference while still holding conns_lock, so the registration reference can no longer be the last one, and drop it once the handler is done. Fixes: d7b0e37c1ac1 ("net/smc: restructure CDC message reception") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630183227.2044998-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/smc/smc_cdc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 619b3bab38248..32d6d03df3214 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -470,9 +470,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) { struct smc_link *link = (struct smc_link *)wc->qp->qp_context; struct smc_cdc_msg *cdc = buf; + struct smc_sock *smc = NULL; struct smc_connection *conn; struct smc_link_group *lgr; - struct smc_sock *smc; if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved)) return; /* short message */ @@ -483,21 +483,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) lgr = smc_get_lgr(link); read_lock_bh(&lgr->conns_lock); conn = smc_lgr_find_conn(ntohl(cdc->token), lgr); - read_unlock_bh(&lgr->conns_lock); - if (!conn || conn->out_of_sync) + if (!conn || conn->out_of_sync) { + read_unlock_bh(&lgr->conns_lock); return; + } smc = container_of(conn, struct smc_sock, conn); + sock_hold(&smc->sk); + read_unlock_bh(&lgr->conns_lock); if (cdc->prod_flags.failover_validation) { smc_cdc_msg_validate(smc, cdc, link); - return; + goto out; } if (smc_cdc_before(ntohs(cdc->seqno), conn->local_rx_ctrl.seqno)) /* received seqno is old */ - return; + goto out; smc_cdc_msg_recv(smc, cdc); +out: + sock_put(&smc->sk); } static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = { From 026c236f0eefeebaae05c400ce102e6f05b2ec85 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 1 Jul 2026 12:23:29 +0000 Subject: [PATCH 1666/3297] amt: fix size calculation in amt_get_size() [ Upstream commit 9e05e91a9a847ed57926414bd7c2c5e54d6c56c6 ] amt_get_size() incorrectly used sizeof(struct iphdr) for the sizes of IFLA_AMT_DISCOVERY_IP, IFLA_AMT_REMOTE_IP, and IFLA_AMT_LOCAL_IP. These attributes contain IPv4 addresses (__be32), not full IP headers. Replace sizeof(struct iphdr) with sizeof(__be32) to avoid over-allocating netlink message space. Fixes: b9022b53adad ("amt: add control plane of amt interface") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260701122329.3562825-1-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/amt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 902c817a0dea1..e4d1aa26d7bbd 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3301,9 +3301,9 @@ static size_t amt_get_size(const struct net_device *dev) nla_total_size(sizeof(__u16)) + /* IFLA_AMT_GATEWAY_PORT */ nla_total_size(sizeof(__u32)) + /* IFLA_AMT_LINK */ nla_total_size(sizeof(__u32)) + /* IFLA_MAX_TUNNELS */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_DISCOVERY_IP */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_REMOTE_IP */ - nla_total_size(sizeof(struct iphdr)); /* IFLA_AMT_LOCAL_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_DISCOVERY_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_REMOTE_IP */ + nla_total_size(sizeof(__be32)); /* IFLA_AMT_LOCAL_IP */ } static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev) From 23a83bac3356e7b211bcdcf581a31f7b536a2c24 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:12:59 +0800 Subject: [PATCH 1667/3297] Bluetooth: 6lowpan: hold L2CAP conn across debugfs control [ Upstream commit 518aa9505fa10ea5662349e5d2efd8c9e32a820b ] get_l2cap_conn() looks up an LE hci_conn under hdev protection, but then drops that protection before reading hcon->l2cap_data and before lowpan_control_write() later dereferences conn->hcon. A disconnect or device close can tear down the same L2CAP connection in that window. The buggy scenario involves two paths, with each column showing the order within that path: 6LoWPAN control write: HCI disconnect/device close: 1. get_l2cap_conn() finds hcon 1. hci_disconn_cfm() dispatches and hcon->l2cap_data. the L2CAP disconnect callback. 2. get_l2cap_conn() drops hdev 2. l2cap_conn_del() clears protection and returns conn. hcon->l2cap_data and drops the L2CAP connection reference. 3. lowpan_control_write() reads 3. hci_conn_del() removes and drops conn->hcon. the HCI connection. Take a reference to the L2CAP connection with l2cap_conn_hold_unless_zero() while hdev is still locked, and drop that reference after the debugfs command's last use of conn. This mirrors the existing L2CAP ACL receive-side handoff and keeps the connection dereferenceable after leaving hdev protection. Export the existing helper so the bluetooth_6lowpan module can use the same lifetime primitive. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in lowpan_control_write+0x374/0x520 The buggy address belongs to the object at ffff888111b9d000 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes inside of freed 1024-byte region [ffff888111b9d000, ffff888111b9d400) Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 lowpan_control_write+0x374/0x520 (net/bluetooth/6lowpan.c:1131) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __debugfs_file_get+0xf7/0x400 full_proxy_write+0x9e/0xd0 vfs_write+0x1b0/0x810 ksys_write+0xd2/0x170 dnotify_flush+0x32/0x220 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 l2cap_conn_add+0x45/0x520 l2cap_chan_connect+0xac6/0xd90 l2cap_sock_connect+0x216/0x350 __sys_connect+0x101/0x130 __x64_sys_connect+0x40/0x50 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 hci_conn_hash_flush+0xc0/0x140 hci_dev_close_sync+0x41a/0xb00 hci_dev_close+0x12f/0x160 hci_sock_ioctl+0x157/0x570 sock_do_ioctl+0xf7/0x210 sock_ioctl+0x32f/0x490 __x64_sys_ioctl+0xc7/0x110 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f kasan_record_aux_stack+0xa7/0xc0 insert_work+0x32/0x100 __queue_work+0x262/0xa60 queue_work_on+0xad/0xb0 l2cap_connect_cfm+0x4ef/0x670 hci_le_remote_feat_complete_evt+0x247/0x430 hci_event_packet+0x360/0x6f0 hci_rx_work+0x2ae/0x7a0 process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: 6b8d4a6a0314 ("Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed one") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/6lowpan.c | 21 +++++++++++++++------ net/bluetooth/l2cap_core.c | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 5325c1b8dcd8b..35512e92c215e 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -997,16 +997,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, hci_dev_lock(hdev); hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type); - hci_dev_unlock(hdev); - hci_dev_put(hdev); - - if (!hcon) + if (!hcon) { + hci_dev_unlock(hdev); + hci_dev_put(hdev); return -ENOENT; + } - *conn = (struct l2cap_conn *)hcon->l2cap_data; + *conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data); BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type); + hci_dev_unlock(hdev); + hci_dev_put(hdev); + return 0; } @@ -1153,18 +1156,22 @@ static ssize_t lowpan_control_write(struct file *fp, if (conn) { struct lowpan_peer *peer; - if (!is_bt_6lowpan(conn->hcon)) + if (!is_bt_6lowpan(conn->hcon)) { + l2cap_conn_put(conn); return -EINVAL; + } peer = lookup_peer(conn); if (peer) { BT_DBG("6LoWPAN connection already exists"); + l2cap_conn_put(conn); return -EALREADY; } BT_DBG("conn %p dst %pMR type %d user %u", conn, &conn->hcon->dst, conn->hcon->dst_type, addr_type); + l2cap_conn_put(conn); } ret = bt_6lowpan_connect(&addr, addr_type); @@ -1180,6 +1187,8 @@ static ssize_t lowpan_control_write(struct file *fp, return ret; ret = bt_6lowpan_disconnect(conn, addr_type); + if (conn) + l2cap_conn_put(conn); if (ret < 0) return ret; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 908198959e3bc..0b4e84f02c2d8 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7715,6 +7715,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c) return c; } +EXPORT_SYMBOL(l2cap_conn_hold_unless_zero); int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) From dfc8373893b1876bb367700eac9d776316dabd96 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:13:28 +0800 Subject: [PATCH 1668/3297] Bluetooth: MGMT: Fix adv monitor add failure cleanup [ Upstream commit 384a4b2fef9ffe5e270ee5558975c0504881c5fb ] hci_add_adv_monitor() publishes a new adv_monitor in hdev->adv_monitors_idr before the powered MSFT setup step. The MSFT offload add path can then fail either locally before the controller add command completes, or in the MSFT add callback. In the current queued management add flow, hci_cmd_sync_work() still invokes mgmt_add_adv_patterns_monitor_complete() with the original pending command after msft_add_monitor_pattern() returns. The buggy scenario involves two paths, with each column showing the order within that path: MSFT add handling MGMT completion 1. insert monitor and handle 1. receive sync error 2. send MSFT add command 2. call add-monitor completion 3. callback sees bad response 3. load cmd->user_data 4. callback frees monitor 4. read monitor->handle Local MSFT setup failures have the other half of the same ownership bug: they return an error after the IDR insertion, but no later code removes the failed monitor from the IDR. Keep ownership with the pending management command until its completion. For normal management adds, the MSFT add callback now records successful controller state and returns errors to its caller. The management completion frees the monitor on non-success after copying the response handle, while resume/reregister callback-error cleanup remains in the MSFT callback. The success path keeps the existing bookkeeping. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x19f/0x330 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] kasan_report+0xe0/0x110 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? 0xffffffffc00d00da ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? hci_cmd_sync_work+0x1ab/0x210 [bluetooth] hci_cmd_sync_work+0x1c0/0x210 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] process_one_work+0x4fd/0xbc0 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hci_cmd_sync_work+0x10/0x10 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Allocated by task 471 on cpu 3 at 285.205389s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 add_adv_patterns_monitor_rssi+0xd5/0x230 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] __sys_sendto+0x2bc/0x2d0 __x64_sys_sendto+0x76/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 454 on cpu 2 at 285.217112s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 msft_add_monitor_sync+0x54a/0x570 [bluetooth] hci_add_adv_monitor+0x133/0x180 [bluetooth] hci_cmd_sync_work+0x187/0x210 [bluetooth] process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: a2a4dedf88ab ("Bluetooth: advmon offload MSFT add monitor") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/mgmt.c | 2 ++ net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 90da285a00cdf..410910f64c1c4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5296,6 +5296,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED) monitor->state = ADV_MONITOR_STATE_REGISTERED; hci_update_passive_scan(hdev); + } else { + hci_free_adv_monitor(hdev, monitor); } mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index c560d84676696..dea81c8e8b201 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -291,7 +291,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode, monitor->state = ADV_MONITOR_STATE_OFFLOADED; unlock: - if (status) + if (status && msft->resuming) hci_free_adv_monitor(hdev, monitor); hci_dev_unlock(hdev); From da4d8eea0c5f33ea35c0e1d9e5f3e439f3b987ce Mon Sep 17 00:00:00 2001 From: Sungwoo Kim Date: Wed, 24 Jun 2026 17:33:04 -0400 Subject: [PATCH 1669/3297] Bluetooth: sco: Fix a race condition in sco_sock_timeout() [ Upstream commit 0f8a5dcc66648b6e1458a9f3ba4c5a0463a228fc ] sco_sock_timeout() runs asynchronously and lock_sock(sk). If the socket is closing while the timer is running, it holds the same lock (lock_sock(sk)) twice, leading to a deadlock. CPU 0 CPU 1 ==================== ====================== sco_sock_close() sco_sock_timeout() lock_sock(sk) // <-- LOCK __sco_sock_close() sco_chan_del() sco_conn_put() sco_conn_free() disable_delayed_work_sync() lock(sk) // <-- SAME LOCK Fix this by moving disable_delayed_work_sync() outside of lock_sock(sk), ensuring that no lock_sock(sk) is held before sco_sock_timeout(). Lockdep splat: WARNING: possible circular locking dependency detected 6.13.0-rc4 #7 Not tainted syz-executor292/9514 is trying to acquire lock: ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_lock_acquire sect/v6.13-rc4/./include/linux/rcupdate.h:337 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_read_lock sect/v6.13-rc4/./include/linux/rcupdate.h:849 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4137 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: __flush_work+0xd1/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 but task is already holding lock: ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: sco_sock_close+0x25/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:524 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}: lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 lock_sock_nested+0x48/0x130 sect/v6.13-rc4/net/core/sock.c:3622 lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] sco_sock_timeout+0xbe/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:158 process_one_work sect/v6.13-rc4/kernel/workqueue.c:3229 [inline] process_scheduled_works+0xa99/0x18f0 sect/v6.13-rc4/kernel/workqueue.c:3310 worker_thread+0x8a9/0xd80 sect/v6.13-rc4/kernel/workqueue.c:3391 kthread+0x2c6/0x360 sect/v6.13-rc4/kernel/kthread.c:389 ret_from_fork+0x4e/0x80 sect/v6.13-rc4/arch/x86/kernel/process.c:147 ret_from_fork_asm+0x1a/0x30 sect/v6.13-rc4/arch/x86/entry/entry_64.S:244 -> #0 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}: check_prev_add sect/v6.13-rc4/kernel/locking/lockdep.c:3161 [inline] check_prevs_add sect/v6.13-rc4/kernel/locking/lockdep.c:3280 [inline] validate_chain+0x1888/0x5760 sect/v6.13-rc4/kernel/locking/lockdep.c:3904 __lock_acquire+0x13b4/0x2120 sect/v6.13-rc4/kernel/locking/lockdep.c:5226 lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 touch_work_lockdep_map sect/v6.13-rc4/kernel/workqueue.c:3909 [inline] start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4163 [inline] __flush_work+0x70f/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 __cancel_work_sync sect/v6.13-rc4/kernel/workqueue.c:4351 [inline] disable_delayed_work_sync+0xbb/0xf0 sect/v6.13-rc4/kernel/workqueue.c:4514 sco_conn_free sect/v6.13-rc4/net/bluetooth/sco.c:95 [inline] kref_put sect/v6.13-rc4/./include/linux/kref.h:65 [inline] sco_conn_put+0x18f/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:107 sco_chan_del+0xe2/0x210 sect/v6.13-rc4/net/bluetooth/sco.c:236 sco_sock_close+0x8f/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:526 sco_sock_release+0x62/0x2d0 sect/v6.13-rc4/net/bluetooth/sco.c:1300 __sock_release+0xe1/0x2d0 sect/v6.13-rc4/net/socket.c:640 sock_close+0x1c/0x30 sect/v6.13-rc4/net/socket.c:1408 __fput+0x2bd/0xa80 sect/v6.13-rc4/fs/file_table.c:450 __fput_sync+0x15e/0x1c0 sect/v6.13-rc4/fs/file_table.c:535 __do_sys_close sect/v6.13-rc4/fs/open.c:1554 [inline] __se_sys_close sect/v6.13-rc4/fs/open.c:1539 [inline] __x64_sys_close+0x93/0x120 sect/v6.13-rc4/fs/open.c:1539 do_syscall_x64 sect/v6.13-rc4/arch/x86/entry/common.c:52 [inline] do_syscall_64+0xee/0x210 sect/v6.13-rc4/arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn") Acked-by: Dave Tian Signed-off-by: Sungwoo Kim Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/sco.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index a19ae1b39bc02..15b9c234bce10 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -566,10 +566,23 @@ static void __sco_sock_close(struct sock *sk) /* Must be called on unlocked socket. */ static void sco_sock_close(struct sock *sk) { + struct sco_conn *conn; + + lock_sock(sk); + conn = sco_pi(sk)->conn; + if (conn) + sco_conn_hold(conn); + release_sock(sk); + + if (conn) + disable_delayed_work_sync(&conn->timeout_work); + lock_sock(sk); - sco_sock_clear_timer(sk); __sco_sock_close(sk); release_sock(sk); + + if (conn) + sco_conn_put(conn); } static void sco_sock_init(struct sock *sk, struct sock *parent) From b69b1ab121fe835e7601b0b587c9f565e4913b21 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 1 Jul 2026 18:46:39 +0300 Subject: [PATCH 1670/3297] Bluetooth: ISO: exclude RFU bits from ISO_SDU_Length [ Upstream commit fd076d8deeab6f9f18ef13400f89e1f550df665b ] slen contains ISO_SDU_Length (12 bits), RFU (2 bits), Packet_Status_Flags (2 bits). Exclude the RFU bits from hci_iso_data_len. Also add masks to the pack macro. Fixes: 4de0fc599eb9 ("Bluetooth: Add definitions for CIS connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- include/net/bluetooth/hci.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cb4c02d007590..2613cfe5b8a39 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -2993,8 +2993,9 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb) #define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2)) /* ISO data length and flags pack/unpack */ -#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14))) -#define hci_iso_data_len(h) ((h) & 0x3fff) +#define hci_iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \ + (((f) & 0x3) << 14))) +#define hci_iso_data_len(h) ((h) & 0x0fff) #define hci_iso_data_flags(h) ((h) >> 14) /* codec transport types */ From bfc9e7be289df11e8e38c98cd78019d67fdd0bd5 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 1 Jul 2026 09:06:14 -0700 Subject: [PATCH 1671/3297] Bluetooth: bpa10x: avoid OOB read of revision string in bpa10x_setup() [ Upstream commit dd068ef044128db655f48323a4acfd5907e04903 ] bpa10x_setup() sends the vendor command 0xfc0e and passes the response to bt_dev_info() and hci_set_fw_info() as a "%s" string starting at skb->data + 1, without checking the length: bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); hci_set_fw_info(hdev, "%s", skb->data + 1); A device that returns a one-byte response (status only) leaves skb->data + 1 past the end of the data, and the %s walk reads adjacent slab memory until it meets a NUL. The same happens when the payload is not NUL-terminated within skb->len. The out-of-bounds bytes end up in the kernel log and the firmware-info debugfs file. Print the revision string with a bounded "%.*s" limited to skb->len - 1 instead. This keeps the string readable for well-behaved devices while never reading past the received data, and does not fail setup, so a device returning a short or unterminated response keeps working. Fixes: ddd68ec8f484 ("Bluetooth: bpa10x: Read revision information in setup stage") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reported-by: Xiang Mei Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/bpa10x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index e305d04aac9d0..989fca42ce567 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -255,9 +255,13 @@ static int bpa10x_setup(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); + /* Bounded print: the device controls skb->len. */ + if (skb->len > 1) { + int len = skb->len - 1; - hci_set_fw_info(hdev, "%s", skb->data + 1); + bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1)); + hci_set_fw_info(hdev, "%.*s", len, skb->data + 1); + } kfree_skb(skb); return 0; From 0c602cb8f148a36bfb39c25ade400bdadc023c4f Mon Sep 17 00:00:00 2001 From: Stig Hornang Date: Fri, 12 Jun 2026 16:38:18 +0200 Subject: [PATCH 1672/3297] Bluetooth: L2CAP: fix tx ident leak for commands without a response [ Upstream commit 6e1930ece855a4c256f1c7e6632d634cfb9888b5 ] Commit 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") changed ident allocation to use an IDA, releasing idents in l2cap_put_ident() when the matching response command is received. But identifiers allocated for commands that have no response defined are never released. In particular L2CAP_LE_CREDITS is sent repeatedly for the lifetime of an LE CoC channel, so a peer streaming data to the host exhausts the 1-255 ident range after 254 credit packets. From then on l2cap_get_ident() fails: kernel: Bluetooth: Unable to allocate ident: -28 and every subsequent L2CAP_LE_CREDITS packet is sent with ident 0, which is invalid (Core Spec, Vol 3, Part A, Section 4: "Signaling identifier 0x00 is an invalid identifier and shall never be used in any command"). Remote stacks that validate the ident drop these commands, never receive new credits, and the channel stalls permanently. With default socket buffers this happens after roughly 0.5 MB of received data (the exact amount depends on the socket receive buffer): < ACL Data TX: Handle 2048 flags 0x00 dlen 12 LE L2CAP: LE Flow Control Credit (0x16) ident 0 len 4 Source CID: 64 Credits: 1 Release the ident immediately after sending L2CAP_LE_CREDITS since no response will ever release it. Use a local variable instead of chan->ident so that an ident that an EXT_FLOWCTL channel may be waiting on (e.g. a pending reconfigure) is not overwritten by a credit packet. Also add the missing L2CAP_LE_CONN_RSP case to l2cap_put_ident() so idents allocated for outgoing L2CAP_LE_CONN_REQ commands are released when the response arrives. Fixes: 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") Link: https://bugzilla.kernel.org/show_bug.cgi?id=221629 Assisted-by: Claude:claude-opus-4.8 Assisted-by: Fable:5 Signed-off-by: Stig Hornang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0b4e84f02c2d8..0289d418ac70a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4832,6 +4832,7 @@ static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id) case L2CAP_ECHO_RSP: case L2CAP_INFO_RSP: case L2CAP_CONN_PARAM_UPDATE_RSP: + case L2CAP_LE_CONN_RSP: case L2CAP_ECRED_CONN_RSP: case L2CAP_ECRED_RECONF_RSP: /* First do a lookup since the remote may send bogus ids that @@ -6729,6 +6730,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) struct l2cap_conn *conn = chan->conn; struct l2cap_le_credits pkt; u16 return_credits = l2cap_le_rx_credits(chan); + int ident; if (chan->mode != L2CAP_MODE_LE_FLOWCTL && chan->mode != L2CAP_MODE_EXT_FLOWCTL) @@ -6746,9 +6748,18 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) pkt.cid = cpu_to_le16(chan->scid); pkt.credits = cpu_to_le16(return_credits); - chan->ident = l2cap_get_ident(conn); + ident = l2cap_get_ident(conn); - l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + l2cap_send_cmd(conn, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + + /* L2CAP_LE_CREDITS has no response so the ident is never released by + * l2cap_put_ident() - release it right away, otherwise the tx_ida + * range is exhausted after 254 packets and from then on credits are + * sent with the invalid ident 0, which some remote stacks ignore, + * stalling the channel. + */ + if (ident > 0) + ida_free(&conn->tx_ida, ident); } void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail) From ec5e96aee75d27779b9a860307679f13f33adb0c Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sun, 7 Jun 2026 15:24:30 +0800 Subject: [PATCH 1673/3297] ring-buffer: Fix event length with forced 8-byte alignment [ Upstream commit c37e0a4b79a6bbb96ce5ffe279d7c001e20529e0 ] When RB_FORCE_8BYTE_ALIGNMENT is true, rb_calculate_event_length() reserves the space of event->array[0] for placing the data length and rb_update_event() stores the data length in event->array[0] accordingly. As a result the whole event length will add extra 4 bytes for sizeof(event.array[0]) unconditionally. But ring_buffer_event_length() only subtracts the sizeof(event->array[0]) for events larger than RB_MAX_SMALL_DATA + sizeof(event->array[0]). As a result, small events on architectures with RB_FORCE_8BYTE_ALIGNMENT=true report a data length that is 4 bytes larger than expected. To fix it, add the RB_FORCE_8BYTE_ALIGNMENT as a condition to subtract the size of that length field whenever RB_FORCE_8BYTE_ALIGNMENT is true. This issue is observed in a riscv64 kernel with CONFIG_HAVE_64BIT_ALIGNED_ACCESS set to y, when we run ftrace selftest trace_marker_raw.tc, we get the weird log: for cases where the id is 1..100, the number of data field is 8*N, but once id exceeds 100, the number of data field becomes 8*N+4: # 1 buf: 58 00 00 00 80 5e d1 63 (number of data field is 8*1) ... # a buf: 58 ... (number of data field is 8*2) ... # 64 buf: 58 ... (number of data field is 8*13) # 65 buf: 58 ... (number of data field is 8*13+4) After applying this change, the number of data field keeps being 8*N+4 consistently. Link: https://patch.msgid.link/20260607072431.125633-2-hui.wang@canonical.com Fixes: 2271048d1b3b ("ring-buffer: Do 8 byte alignment for 64 bit that can not handle 4 byte align") Signed-off-by: Hui Wang Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/ring_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index bef1c05b9b715..fefa16e51e0ef 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -281,7 +281,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event) if (event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX) return length; length -= RB_EVNT_HDR_SIZE; - if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0])) + if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]) || + RB_FORCE_8BYTE_ALIGNMENT) length -= sizeof(event->array[0]); return length; } From b3eeb586f94c706c456cd18ca290a6aeddca3e77 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 15 Jun 2026 23:15:32 -0700 Subject: [PATCH 1674/3297] accel/amdxdna: Use unsigned long for nr_pages in amdxdna_hmm_register() [ Upstream commit 18aaebdf43366954345a6721ed2bff3ac3c9fa61 ] nr_pages is declared as u32 in amdxdna_hmm_register(), which may not be large enough to represent the number of pages for large mappings. Use unsigned long for nr_pages to avoid potential overflow. Fixes: ac49797c1815 ("accel/amdxdna: Add GEM buffer object management") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260616061532.3533469-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index a1c9cc4a2b9d8..084b22b774193 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -196,7 +196,7 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, unsigned long len = vma->vm_end - vma->vm_start; unsigned long addr = vma->vm_start; struct amdxdna_umap *mapp; - u32 nr_pages; + unsigned long nr_pages; int ret; if (!xdna->dev_info->ops->hmm_invalidate) From e8a4c9fc437b16aef38f86ce3275677e36924259 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 30 Jun 2026 15:15:51 -0400 Subject: [PATCH 1675/3297] net/tls: Consume empty data records in tls_sw_read_sock() [ Upstream commit 3be28e2c9cd0230cb51fd4967df095273afd3848 ] A peer may send a zero-length TLS application_data record; TLS 1.3 explicitly permits these as a traffic-analysis countermeasure (RFC 8446, Section 5.1). After decryption such a record has full_len == 0. tls_sw_read_sock() hands it to the read_actor, which has no payload to consume and returns zero. The loop treats a zero return as backpressure (used <= 0), requeues the skb at the head of rx_list, and stops. rx_list is serviced head-first on the next call, so the empty record is dequeued, fails the same way, and is requeued again; every later record on the connection is blocked behind it. tls_sw_recvmsg() does not stall on this: a zero-length data record copies nothing and falls through to consume_skb(). Mirror that in the read_sock() path by recognizing an empty data record before the actor runs, consuming it, and continuing. Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()") Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260630191551.875664-1-cel@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index c80ae566ad4e6..f19b504efa5d0 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2424,6 +2424,17 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, goto read_sock_requeue; } + /* An empty data record (legal in TLS 1.3) gives a zero + * read_actor return, indistinguishable from the consumer + * stalling; the used <= 0 path would requeue it at the + * head of rx_list and block all later records. Consume it + * here instead. + */ + if (rxm->full_len == 0) { + consume_skb(skb); + continue; + } + used = read_actor(desc, skb, rxm->offset, rxm->full_len); if (used <= 0) { if (!copied) From a3010b732d620218d869370f690b06d81e10d45e Mon Sep 17 00:00:00 2001 From: Enrico Pozzobon Date: Wed, 1 Jul 2026 16:47:23 +0200 Subject: [PATCH 1676/3297] net: usb: lan78xx: disable VLAN filter in promiscuous mode [ Upstream commit 60444706aa17616efc03190d099ac347e28b3d0a ] The hardware VLAN filter (RFE_CTL_VLAN_FILTER_) drops VLAN-tagged frames whose VID has not been registered via lan78xx_vlan_rx_add_vid(). It is left enabled in promiscuous mode, so packet capture (e.g. tcpdump or Wireshark) does not see tagged frames for unregistered VIDs. Clear the filter while the interface is promiscuous and restore it from NETIF_F_HW_VLAN_CTAG_FILTER otherwise. Enforce the same condition in lan78xx_set_features() so netdev_update_features() cannot re-enable the filter while promiscuous. Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver") Signed-off-by: Enrico Pozzobon Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260701-lan78xx-vlan-promisc-v3-1-232266d32743@dissecto.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/usb/lan78xx.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 4657f49473123..851dbe1090dcb 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1499,6 +1499,17 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param) return; } +static void lan78xx_update_vlan_filter(struct lan78xx_priv *pdata, + struct net_device *netdev, + netdev_features_t features) +{ + if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && + !(netdev->flags & IFF_PROMISC)) + pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; + else + pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; +} + static void lan78xx_set_multicast(struct net_device *netdev) { struct lan78xx_net *dev = netdev_priv(netdev); @@ -1533,6 +1544,8 @@ static void lan78xx_set_multicast(struct net_device *netdev) } } + lan78xx_update_vlan_filter(pdata, dev->net, dev->net->features); + if (netdev_mc_count(dev->net)) { struct netdev_hw_addr *ha; int i; @@ -3074,10 +3087,7 @@ static int lan78xx_set_features(struct net_device *netdev, else pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_; - if (features & NETIF_F_HW_VLAN_CTAG_FILTER) - pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; - else - pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; + lan78xx_update_vlan_filter(pdata, netdev, features); spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); From d7b5497e0e45be7aabeb120ce01f54f34de57888 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 24 Nov 2025 08:20:52 +0800 Subject: [PATCH 1677/3297] gpio: dwapb: Use modern PM macros [ Upstream commit 3f19e57cbfb55d743d60aeebf5d5c48cc7fd5d4e ] Use the modern PM macros for the suspend and resume functions to be automatically dropped by the compiler when CONFIG_PM or CONFIG_PM_SLEEP are disabled, without having to use #ifdef guards. This has the advantage of always compiling these functions in, independently of any Kconfig option. Thanks to that, bugs and other regressions are subsequently easier to catch. Signed-off-by: Jisheng Zhang Acked-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20251124002105.25429-2-jszhang@kernel.org Signed-off-by: Bartosz Golaszewski Stable-dep-of: d775b9451eb8 ("gpio: dwapb: Defer clock gating until noirq") Signed-off-by: Sasha Levin --- drivers/gpio/gpio-dwapb.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index b42ff46d292bd..4986c465c9a86 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -79,7 +79,6 @@ struct dwapb_platform_data { unsigned int nports; }; -#ifdef CONFIG_PM_SLEEP /* Store GPIO context across system-wide suspend/resume transitions */ struct dwapb_context { u32 data; @@ -92,7 +91,6 @@ struct dwapb_context { u32 int_deb; u32 wake_en; }; -#endif struct dwapb_gpio_port_irqchip { unsigned int nr_irqs; @@ -103,9 +101,7 @@ struct dwapb_gpio_port { struct gpio_generic_chip chip; struct dwapb_gpio_port_irqchip *pirq; struct dwapb_gpio *gpio; -#ifdef CONFIG_PM_SLEEP struct dwapb_context *ctx; -#endif unsigned int idx; }; @@ -363,7 +359,6 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) return 0; } -#ifdef CONFIG_PM_SLEEP static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); @@ -378,9 +373,6 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) return 0; } -#else -#define dwapb_irq_set_wake NULL -#endif static const struct irq_chip dwapb_irq_chip = { .name = DWAPB_DRIVER_NAME, @@ -390,7 +382,7 @@ static const struct irq_chip dwapb_irq_chip = { .irq_set_type = dwapb_irq_set_type, .irq_enable = dwapb_irq_enable, .irq_disable = dwapb_irq_disable, - .irq_set_wake = dwapb_irq_set_wake, + .irq_set_wake = pm_sleep_ptr(dwapb_irq_set_wake), .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; @@ -759,7 +751,6 @@ static int dwapb_gpio_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int dwapb_gpio_suspend(struct device *dev) { struct dwapb_gpio *gpio = dev_get_drvdata(dev); @@ -844,15 +835,14 @@ static int dwapb_gpio_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend, - dwapb_gpio_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, + dwapb_gpio_suspend, dwapb_gpio_resume); static struct platform_driver dwapb_gpio_driver = { .driver = { .name = DWAPB_DRIVER_NAME, - .pm = &dwapb_gpio_pm_ops, + .pm = pm_sleep_ptr(&dwapb_gpio_pm_ops), .of_match_table = dwapb_of_match, .acpi_match_table = dwapb_acpi_match, }, From 6c736c5ccf4a3aed68fc4042963d9b07629ef362 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 19 Mar 2026 17:53:38 -0700 Subject: [PATCH 1678/3297] gpio: dwapb: reduce allocation to single kzalloc [ Upstream commit 9a5bf2f53b76b1619c602f9e751fe4c0e64713ca ] Instead of kzalloc + kcalloc, Combine the two using a flexible array member. Allows using __counted_by for extra runtime analysis. Move counting variable to right after allocation as required by __counted_by. Signed-off-by: Rosen Penev Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260320005338.30355-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski Stable-dep-of: d775b9451eb8 ("gpio: dwapb: Defer clock gating until noirq") Signed-off-by: Sasha Levin --- drivers/gpio/gpio-dwapb.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 4986c465c9a86..15cebc8b5d663 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -75,8 +75,8 @@ struct dwapb_port_property { }; struct dwapb_platform_data { - struct dwapb_port_property *properties; unsigned int nports; + struct dwapb_port_property properties[] __counted_by(nports); }; /* Store GPIO context across system-wide suspend/resume transitions */ @@ -114,11 +114,11 @@ static inline struct dwapb_gpio *to_dwapb_gpio(struct gpio_chip *gc) struct dwapb_gpio { struct device *dev; void __iomem *regs; - struct dwapb_gpio_port *ports; unsigned int nr_ports; unsigned int flags; struct reset_control *rst; struct clk_bulk_data clks[DWAPB_NR_CLOCKS]; + struct dwapb_gpio_port ports[] __counted_by(nr_ports); }; static inline u32 gpio_reg_v2_convert(unsigned int offset) @@ -585,14 +585,10 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev) if (nports == 0) return ERR_PTR(-ENODEV); - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, struct_size(pdata, properties, nports), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); - pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL); - if (!pdata->properties) - return ERR_PTR(-ENOMEM); - pdata->nports = nports; i = 0; @@ -714,22 +710,17 @@ static int dwapb_gpio_probe(struct platform_device *pdev) if (IS_ERR(pdata)) return PTR_ERR(pdata); - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, ports, pdata->nports), GFP_KERNEL); if (!gpio) return -ENOMEM; - gpio->dev = &pdev->dev; gpio->nr_ports = pdata->nports; + gpio->dev = &pdev->dev; err = dwapb_get_reset(gpio); if (err) return err; - gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports, - sizeof(*gpio->ports), GFP_KERNEL); - if (!gpio->ports) - return -ENOMEM; - gpio->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gpio->regs)) return PTR_ERR(gpio->regs); From 5e15cf51982f85383e32d907e8f71b85ad3cf213 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Thu, 2 Jul 2026 17:22:12 +0800 Subject: [PATCH 1679/3297] gpio: dwapb: Defer clock gating until noirq [ Upstream commit d775b9451eb8f52021dea6483ad758fef81dbf1e ] GPIO consumers such as gpio-keys can enable IRQ wake and adjust the wake trigger type from their suspend callbacks. If the DWAPB controller suspends first, masking interrupts and disabling its clocks in the normal suspend phase prevents that late wake configuration from reliably reaching the hardware. Systems with real DWAPB bus clocks then fail to wake from s2idle through GPIO keys. Save the register context in the normal suspend callback, but defer IRQ masking and clock gating until suspend_noirq. At that point all consumers have finished configuring wake IRQs, so keep the clocks enabled when wake lines are armed and only gate them when no wake source is active. Resume_noirq reenables clocks, if they were gated, before the normal resume path restores registers. Propagate wake requests to the parent irqchip while keeping the local wake mask in sync with failures. Fixes: 6437c7ba69c3 ("gpio: dwapb: Add wakeup source support") Signed-off-by: Jia Wang Link: https://patch.msgid.link/20260702-gpio-dwapb-wakeup-v2-1-203f2f33429f@ultrarisc.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-dwapb.c | 79 +++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 15cebc8b5d663..0259c65973323 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -118,6 +118,7 @@ struct dwapb_gpio { unsigned int flags; struct reset_control *rst; struct clk_bulk_data clks[DWAPB_NR_CLOCKS]; + bool clocks_on_for_wake; struct dwapb_gpio_port ports[] __counted_by(nr_ports); }; @@ -365,11 +366,24 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable) struct dwapb_gpio *gpio = to_dwapb_gpio(gc); struct dwapb_context *ctx = gpio->ports[0].ctx; irq_hw_number_t bit = irqd_to_hwirq(d); + u32 wake_en = ctx->wake_en; if (enable) - ctx->wake_en |= BIT(bit); + wake_en |= BIT(bit); else - ctx->wake_en &= ~BIT(bit); + wake_en &= ~BIT(bit); + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + if (d->parent_data && !!ctx->wake_en != !!wake_en) { + int err; + + err = irq_chip_set_wake_parent(d, enable); + if (err) + return err; + } +#endif + + ctx->wake_en = wake_en; return 0; } @@ -749,6 +763,8 @@ static int dwapb_gpio_suspend(struct device *dev) int i; scoped_guard(gpio_generic_lock_irqsave, gen_gc) { + gpio->clocks_on_for_wake = false; + for (i = 0; i < gpio->nr_ports; i++) { unsigned int offset; unsigned int idx = gpio->ports[i].idx; @@ -770,11 +786,38 @@ static int dwapb_gpio_suspend(struct device *dev) ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY); ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL); ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE); + } + } + } + + return 0; +} + +static int dwapb_gpio_suspend_noirq(struct device *dev) +{ + struct dwapb_gpio *gpio = dev_get_drvdata(dev); + struct gpio_generic_chip *gen_gc = &gpio->ports[0].chip; + bool wake_enabled = false; + int i; + + scoped_guard(gpio_generic_lock_irqsave, gen_gc) { + for (i = 0; i < gpio->nr_ports; i++) { + unsigned int idx = gpio->ports[i].idx; + struct dwapb_context *ctx = gpio->ports[i].ctx; - /* Mask out interrupts */ + if (idx == 0) { + wake_enabled = ctx->wake_en; dwapb_write(gpio, GPIO_INTMASK, ~ctx->wake_en); + break; } } + + gpio->clocks_on_for_wake = wake_enabled; + } + + if (wake_enabled) { + device_set_wakeup_path(dev); + return 0; } clk_bulk_disable_unprepare(DWAPB_NR_CLOCKS, gpio->clks); @@ -782,18 +825,27 @@ static int dwapb_gpio_suspend(struct device *dev) return 0; } -static int dwapb_gpio_resume(struct device *dev) +static int dwapb_gpio_resume_noirq(struct device *dev) { struct dwapb_gpio *gpio = dev_get_drvdata(dev); - struct gpio_chip *gc = &gpio->ports[0].chip.gc; - struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc); - int i, err; + int err; + + if (gpio->clocks_on_for_wake) + return 0; err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks); - if (err) { + if (err) dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n"); - return err; - } + + return err; +} + +static int dwapb_gpio_resume(struct device *dev) +{ + struct dwapb_gpio *gpio = dev_get_drvdata(dev); + struct gpio_chip *gc = &gpio->ports[0].chip.gc; + struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(gc); + int i; guard(gpio_generic_lock_irqsave)(gen_gc); @@ -827,8 +879,11 @@ static int dwapb_gpio_resume(struct device *dev) return 0; } -static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, - dwapb_gpio_suspend, dwapb_gpio_resume); +static const struct dev_pm_ops dwapb_gpio_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend, dwapb_gpio_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(dwapb_gpio_suspend_noirq, + dwapb_gpio_resume_noirq) +}; static struct platform_driver dwapb_gpio_driver = { .driver = { From 1cd434ac1c222448bc121bd2e2dd7a95e8704670 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 17 Jun 2026 11:58:22 +0100 Subject: [PATCH 1680/3297] tracing: Make tracepoint_printk static as not exported [ Upstream commit d5c6785f94ead7cd76b38af301ac63cb673485bc ] The tracepoint_printk symbol is not exported, so make it static to remove the following sparse warning: kernel/trace/trace.c:90:5: warning: symbol 'tracepoint_printk' was not declared. Should it be static? Fixes: dd293df6395a2 ("tracing: Move trace sysctls into trace.c") Link: https://patch.msgid.link/20260617105822.904164-1-ben.dooks@codethink.co.uk Signed-off-by: Ben Dooks Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 55c9f51a2fda0..5b1693d58af06 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -88,7 +88,7 @@ void __init disable_tracing_selftest(const char *reason) /* Pipe tracepoints to printk */ static struct trace_iterator *tracepoint_print_iter; -int tracepoint_printk; +static int tracepoint_printk; static bool tracepoint_printk_stop_on_boot __initdata; static bool traceoff_after_boot __initdata; static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key); From 267809e2c56fbea486f7250c8a4acddcc3c54dc5 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 15:12:28 -0700 Subject: [PATCH 1681/3297] accel/amdxdna: Fix potential amdxdna_umap lifetime race [ Upstream commit 14f172eff9c19f8043a9858845f33cd034f3a41e ] amdxdna_umap_release() calls the blocking mmu_interval_notifier_remove() before removing the object from abo->mem.umap_list. If aie2_populate_range() runs concurrently, it may obtain a reference to an amdxdna_umap that is being released, leading to a potential use-after-free. Use kref_get_unless_zero() in aie2_populate_range() when acquiring a reference. If the reference count has already dropped to zero, release is in progress and the entry is skipped. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260706221228.434099-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/aie2_ctx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 80de2906a26fc..612ad6302622a 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -794,7 +794,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) found = false; down_write(&xdna->notifier_lock); list_for_each_entry(mapp, &abo->mem.umap_list, node) { - if (mapp->invalid) { + if (mapp->invalid && kref_get_unless_zero(&mapp->refcnt)) { found = true; break; } @@ -805,7 +805,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo) up_write(&xdna->notifier_lock); return 0; } - kref_get(&mapp->refcnt); + up_write(&xdna->notifier_lock); XDNA_DBG(xdna, "populate memory range %lx %lx", From 762116dfa72865c82151970960f7cf34f44b21c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Fri, 3 Jul 2026 12:33:35 -0300 Subject: [PATCH 1682/3297] drm/v3d: Reject invalid indirect BO handle in indirect CSD setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2f8b8593c7832fad655290cef9e99af05b1b52b3 ] v3d_get_cpu_indirect_csd_params() looks up the indirect buffer object from a userspace-supplied handle but never checks the result. A bogus or stale handle makes drm_gem_object_lookup() return NULL, which is then stored in info->indirect and only dereferenced later when the indirect CSD job runs, turning a userspace mistake into a NULL pointer dereference in the kernel. Bail out with -ENOENT as soon as the lookup fails, so the bad handle is rejected at submission time. Fixes: 18b8413b25b7 ("drm/v3d: Create a CPU job extension for a indirect CSD job") Reviewed-by: Iago Toral Quiroga Signed-off-by: Maíra Canal Link: https://patch.msgid.link/20260703-v3d-cpu-job-fixes-v3-2-bc51b1f3eeb5@igalia.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/v3d/v3d_submit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 95b9e68b0dfe5..1274e6010bea2 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -462,6 +462,8 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv, sizeof(indirect_csd.wg_uniform_offsets)); info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); + if (!info->indirect) + return -ENOENT; return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit, &info->job, &info->clean_job, From 72119397cdff64d8c011addd7d39e3332aa46c0d Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 1 Jul 2026 20:26:52 -0700 Subject: [PATCH 1683/3297] net: mdio: select REGMAP_MMIO instead of depending on it [ Upstream commit 1a3267a8c9ecabb8e27f5cbda6d19295d5e41beb ] REGMAP_MMIO is a hidden (non-user-visible) tristate symbol. Using depends on it is incorrect because there is no way for the user to enable it directly. Change to select, which is the convention used by every other driver in the tree that needs REGMAP_MMIO. Fixes: 8057cbb8335c ("net: mdio: mscc-miim: Add depend of REGMAP_MMIO on MDIO_MSCC_MIIM") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260702032653.1580616-1-rosenp@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/mdio/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index 44380378911b7..4da50e55b6be0 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -123,7 +123,8 @@ config MDIO_MVUSB config MDIO_MSCC_MIIM tristate "Microsemi MIIM interface support" - depends on HAS_IOMEM && REGMAP_MMIO + depends on HAS_IOMEM + select REGMAP_MMIO help This driver supports the MIIM (MDIO) interface found in the network switches of the Microsemi SoCs; it is recommended to switch on From 7aa0e64fea778a9e3df73e64da95367ff8ad2ea5 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:07:59 +0000 Subject: [PATCH 1684/3297] net/sched: cake: reject overhead values that underflow length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b7f97cae7ec1b6c3c32843c42be218690d310467 ] CAKE accepts signed overhead values and stores them in an s16, but the adjusted packet length calculation uses unsigned arithmetic. A negative effective length can therefore wrap to a large value. Such configurations make rate accounting depend on integer wraparound rather than on the packet size userspace intended to model. A static netlink lower bound is not enough because packets reaching CAKE can be smaller than any reasonable manual-overhead allowance. Fold the signed overhead adjustment into the existing datapath MPU clamp so negative adjusted lengths are clamped before link-layer framing adjustments. Fixes: a729b7f0bd5b ("sch_cake: Add overhead compensation support to the rate shaper") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260702000758.297407.e5c888d9d99d.cake-overhead-underflow@trailofbits.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/sch_cake.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index f2ad946a519c2..c19eeed7e7cfb 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1368,10 +1368,7 @@ static u32 cake_calc_overhead(struct cake_sched_data *q, u32 len, u32 off) if (q->min_netlen > len) q->min_netlen = len; - len += q->rate_overhead; - - if (len < q->rate_mpu) - len = q->rate_mpu; + len = max((s32)len + q->rate_overhead, (s32)q->rate_mpu); if (q->atm_mode == CAKE_ATM_ATM) { len += 47; From 9579d625171a140dc56c903f58ee50fdc8a0c243 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Thu, 2 Jul 2026 09:04:51 +0530 Subject: [PATCH 1685/3297] octeontx2-pf: check DMAC extraction support before filtering [ Upstream commit 235acadd310533ba386ae61ad155b72bee381559 ] Currently, configuring a VF MAC address via the PF (e.g., 'ip link set vf 0 mac ') blindly attempts to install a DMAC-based hardware filter. However, the hardware parser profile might not support DMAC extraction. Check if the hardware parsing profile supports DMAC extraction before adding the filter. Additionally, emit a warning message to inform the operator if the MAC filter installation fails due to missing DMAC extraction support. Update config->mac only after hardware programming succeeds in otx2_set_vf_mac(). Fixes: f0c2982aaf98 ("octeontx2-pf: Add support for SR-IOV management functions") Signed-off-by: Suman Ghosh Signed-off-by: Nitin Shetty J Reviewed-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260702033451.2969880-1-nshettyj@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index e0369466c9fd4..e30fac4263c76 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -2530,10 +2530,42 @@ EXPORT_SYMBOL(otx2_ioctl); static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac) { + struct npc_get_field_status_req *freq; + struct npc_get_field_status_rsp *frsp; struct npc_install_flow_req *req; int err; mutex_lock(&pf->mbox.lock); + + /* Skip installing the DMAC filter if the hardware parser profile + * does not support DMAC extraction. + */ + freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox); + if (!freq) { + err = -ENOMEM; + goto out; + } + + freq->field = NPC_DMAC; + err = otx2_sync_mbox_msg(&pf->mbox); + if (err) + goto out; + + frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp + (&pf->mbox.mbox, 0, &freq->hdr); + if (IS_ERR(frsp)) { + err = PTR_ERR(frsp); + goto out; + } + + if (!frsp->enable) { + netdev_warn(pf->netdev, + "VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n", + vf); + err = -EOPNOTSUPP; + goto out; + } + req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox); if (!req) { err = -ENOMEM; @@ -2572,13 +2604,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) if (!is_valid_ether_addr(mac)) return -EINVAL; - config = &pf->vf_configs[vf]; - ether_addr_copy(config->mac, mac); - ret = otx2_do_set_vf_mac(pf, vf, mac); - if (ret == 0) - dev_info(&pdev->dev, - "Load/Reload VF driver\n"); + if (ret == 0) { + config = &pf->vf_configs[vf]; + ether_addr_copy(config->mac, mac); + dev_info(&pdev->dev, "Load/Reload VF driver\n"); + } return ret; } From 7cc438c99bba324a0562b1bafa747b10f7e251df Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Wed, 8 Jul 2026 16:02:04 +0530 Subject: [PATCH 1686/3297] perf/x86/amd/core: Avoid enabling BRS from the SVM reload path [ Upstream commit 07c60dda9c059c09f83d42a3ebda2e7cc1cf3bc2 ] Branch Sampling (BRS) and Last Branch Record (LBR) are mutually exclusive hardware features, and users of both are tracked via cpuc->lbr_users. When SVM is toggled on a CPU, the host perf events are reprogrammed to update the HostOnly filter bit (set when virtualization is enabled, cleared when it is disabled). On PerfMonV2-capable processors, this reprogramming is performed by calling amd_pmu_enable_all() to rewrite the event selectors. However, amd_pmu_enable_all() also calls amd_brs_enable_all(), which enables BRS whenever cpuc->lbr_users > 0. Having active LBR events satisfies this gating on processors that have LBR but not BRS. The kernel then tries to set the BRS enable bit in DebugExtnCfg (MSR 0xc000010f). Since that bit is deprecated on such hardware, the write results in a #GP: Call Trace: amd_pmu_enable_all+0x1d/0x90 amd_pmu_disable_virt+0x62/0xb0 kvm_arch_disable_virtualization_cpu+0xa/0x40 [kvm] hardware_disable_nolock+0x1a/0x30 [kvm] __flush_smp_call_function_queue+0x9b/0x410 __sysvec_call_function+0x18/0xc0 sysvec_call_function+0x69/0x90 asm_sysvec_call_function+0x16/0x20 RIP: 0010:cpuidle_enter_state+0xc4/0x450 ? cpuidle_enter_state+0xb7/0x450 cpuidle_enter+0x29/0x40 cpuidle_idle_call+0xf5/0x160 do_idle+0x7b/0xe0 cpu_startup_entry+0x26/0x30 start_secondary+0x115/0x140 secondary_startup_64_no_verify+0x194/0x19b Fix this by ensuring that BRS is not enabled from the event selector reprogramming path even when cpuc->lbr_users > 0. Fixes: bae19fdd7e9e ("perf/x86/amd/core: Fix reloading events for SVM") Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: Peter Zijlstra Link: https://patch.msgid.link/702fa204d574b03d14e3664c7d4b201db048bbfd.1783506528.git.sandipan.das@amd.com Signed-off-by: Sasha Levin --- arch/x86/events/amd/core.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index e7d30908f25e5..1325a49b3f665 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -751,13 +751,11 @@ static void amd_pmu_enable_event(struct perf_event *event) x86_pmu_enable_event(event); } -static void amd_pmu_enable_all(int added) +static void __amd_pmu_enable_all(void) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; - amd_brs_enable_all(); - for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { /* only activate events which are marked as active */ if (!test_bit(idx, cpuc->active_mask)) @@ -772,6 +770,12 @@ static void amd_pmu_enable_all(int added) } } +static void amd_pmu_enable_all(int added) +{ + amd_brs_enable_all(); + __amd_pmu_enable_all(); +} + static void amd_pmu_v2_enable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -1556,7 +1560,7 @@ static inline void amd_pmu_reload_virt(void) * set global enable bits once again */ amd_pmu_v2_disable_all(); - amd_pmu_enable_all(0); + __amd_pmu_enable_all(); amd_pmu_v2_enable_all(0); return; } From 3bfcce441c552133adeeb99c294d0ce8a62612ef Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 7 Jul 2026 16:23:58 -0700 Subject: [PATCH 1687/3297] gpio: mvebu: free generic chips on unbind [ Upstream commit b11c513ad943f35cf5e8007d3a56279c79b7ed4b ] irq_alloc_domain_generic_chips() allocates generic chip data that must be freed via irq_domain_remove_generic_chips(). The devres action mvebu_gpio_remove_irq_domain() only called irq_domain_remove(), which only frees the generic chips if IRQ_DOMAIN_FLAG_DESTROY_GC is set. Call irq_domain_remove_generic_chips() explicitly before irq_domain_remove() instead. Fixes: 812d47889a8e ("gpio/mvebu: Use irq_domain_add_linear") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260707232358.1218077-1-rosenp@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mvebu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 2b5112e3210ba..39a7ad6585f25 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1111,6 +1111,7 @@ static void mvebu_gpio_remove_irq_domain(void *data) { struct irq_domain *domain = data; + irq_domain_remove_generic_chips(domain); irq_domain_remove(domain); } From 75e984fe0cb9e7fbde0c8ee838c61ce8573d3ea3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:54 +0000 Subject: [PATCH 1688/3297] ipv4: igmp: Fix potential UAF in igmp_gq_start_timer() [ Upstream commit 7b19c0f81ed1fdaec6bc522569be367199a9edf3 ] A race condition exists between device teardown (inetdev_destroy) and incoming IGMP query processing (igmp_rcv), leading to a Use-After-Free in the IGMP timer callback. During device destruction, inetdev_destroy() drops the primary reference to in_device, which can drop its refcount to 0. The actual freeing of in_device memory is deferred via RCU (using call_rcu()). Concurrently, igmp_rcv() runs under RCU read lock and obtains the in_device pointer. Because the memory is RCU-protected, CPU-0 can safely dereference in_device even if its refcount has hit 0. However, if CPU-0 calls igmp_gq_start_timer() and re-arms the timer, it attempts to acquire a reference using in_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the in_device memory is still scheduled to be freed after the RCU grace period (as the free callback does not check the refcount again), the device is freed while the timer is still armed. When the timer expires, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not arm the timer. A similar issue in IPv6 MLD is fixed in a subsequent patch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Zero Day Initiative Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-2-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/linux/inetdevice.h | 5 +++++ net/ipv4/igmp.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index dccbeb25f7014..6032eea2539a6 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -293,6 +293,11 @@ static inline void in_dev_put(struct in_device *idev) #define __in_dev_put(idev) refcount_dec(&(idev)->refcnt) #define in_dev_hold(idev) refcount_inc(&(idev)->refcnt) +static inline bool in_dev_hold_safe(struct in_device *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + #endif /* __KERNEL__ */ static __inline__ __be32 inet_make_mask(int logmask) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 449cb4ba6153e..894b11b79019a 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -235,16 +235,20 @@ static void igmp_gq_start_timer(struct in_device *in_dev) return; in_dev->mr_gq_running = 1; - if (!mod_timer(&in_dev->mr_gq_timer, exp)) - in_dev_hold(in_dev); + if (in_dev_hold_safe(in_dev)) { + if (mod_timer(&in_dev->mr_gq_timer, exp)) + in_dev_put(in_dev); + } } static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) { - int tv = get_random_u32_below(delay); + if (in_dev_hold_safe(in_dev)) { + int tv = get_random_u32_below(delay); - if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) - in_dev_hold(in_dev); + if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2)) + in_dev_put(in_dev); + } } static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) From 9ce741c22df4fd9546e30306317ac7df3607e48f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:55 +0000 Subject: [PATCH 1689/3297] ipv6: mcast: Fix potential UAF in MLD delayed work [ Upstream commit 9b26518b6896a16b809b1e42986f4ebac7bccc1e ] A race condition exists between device teardown and incoming MLD query processing, leading to a Use-After-Free in the MLD delayed work. During device destruction, the primary reference to inet6_dev is dropped, which can drop its refcount to 0. The actual freeing of inet6_dev memory is deferred via RCU. Concurrently, the packet receive path runs under RCU read lock and obtains the inet6_dev pointer. Because the memory is RCU-protected, CPU-0 can safely dereference inet6_dev even if its refcount has hit 0. However, if CPU-0 calls igmp6_event_query() and schedules delayed work, it attempts to acquire a reference using in6_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the inet6_dev memory is still scheduled to be freed after the RCU grace period, the device is freed while the work is still scheduled. When the work runs, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in6_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not schedule the work. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-3-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/addrconf.h | 5 +++++ net/ipv6/mcast.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index c5a215ec14215..a106e8d6bd6ef 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -440,6 +440,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev) refcount_inc(&idev->refcnt); } +static inline bool in6_dev_hold_safe(struct inet6_dev *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + /* called with rcu_read_lock held */ static inline bool ip6_ignore_linkdown(const struct net_device *dev) { diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index f4b3cb4838709..c63f97c79c49b 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1080,8 +1080,10 @@ static void mld_gq_start_work(struct inet6_dev *idev) mc_assert_locked(idev); idev->mc_gq_running = 1; - if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_gq_stop_work(struct inet6_dev *idev) @@ -1099,8 +1101,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_ifc_stop_work(struct inet6_dev *idev) @@ -1118,8 +1122,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_dad_stop_work(struct inet6_dev *idev) @@ -1405,18 +1411,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld, void igmp6_event_query(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_query_lock); - if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_query_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_query_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } @@ -1573,18 +1584,23 @@ static void mld_query_work(struct work_struct *work) void igmp6_event_report(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_report_lock); - if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_report_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_report_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } From d269eb67d2e58f7eb55c895e07fbf9f66d77cc51 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Fri, 22 May 2026 18:39:06 +0900 Subject: [PATCH 1690/3297] ipv4: igmp: annotate data-races around im->users [ Upstream commit 061c0aa740d5d3847cd600a74c66a165bee1fbe0 ] /proc/net/igmp walks IPv4 multicast memberships under RCU and prints im->users without holding RTNL, while multicast join and leave paths update the field while holding RTNL. Annotate this intentional lockless snapshot with READ_ONCE() and the matching writers with WRITE_ONCE(). Signed-off-by: Yuyang Huang Reviewed-by: Jiayuan Chen Reviewed-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260522093906.39764-1-sigefriedhyy@gmail.com Signed-off-by: Jakub Kicinski Stable-dep-of: 3546deaa0c30 ("ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer()") Signed-off-by: Sasha Levin --- net/ipv4/igmp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 894b11b79019a..980f6fa77a6bb 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1531,7 +1531,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, } if (im) { - im->users++; + WRITE_ONCE(im->users, im->users + 1); ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0); goto out; } @@ -1540,7 +1540,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, if (!im) goto out; - im->users = 1; + WRITE_ONCE(im->users, 1); im->interface = in_dev; in_dev_hold(in_dev); im->multiaddr = addr; @@ -1774,7 +1774,10 @@ void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) (i = rtnl_dereference(*ip)) != NULL; ip = &i->next_rcu) { if (i->multiaddr == addr) { - if (--i->users == 0) { + int new_users = i->users - 1; + + WRITE_ONCE(i->users, new_users); + if (new_users == 0) { ip_mc_hash_remove(in_dev, i); *ip = i->next_rcu; in_dev->mc_count--; @@ -2968,7 +2971,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) delta = im->timer.expires - jiffies; seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", - im->multiaddr, im->users, + im->multiaddr, READ_ONCE(im->users), im->tm_running, im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, im->reporter); From 27506827a01f6dd7a49fc9572a37012db245164b Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Fri, 5 Jun 2026 10:43:18 +0900 Subject: [PATCH 1691/3297] ipv4: igmp: annotate data-races around timer-related fields [ Upstream commit 3289d17b7a1321e103b8aec4ad82675c03c4764f ] /proc/net/igmp walks the multicast list locklessly under RCU and reads timer-related fields (im->tm_running, im->reporter, im->timer.expires) to print the timer state of multicast memberships. Concurrently, these fields are modified under im->lock spinlock in timer management paths (igmp_stop_timer(), igmp_start_timer(), and igmp_timer_expire()). Fix this intentional lockless snapshot by annotating the lockless reads with READ_ONCE() and the updates with WRITE_ONCE(). Signed-off-by: Yuyang Huang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260605014318.3890804-3-yuyanghuang@google.com Signed-off-by: Paolo Abeni Stable-dep-of: 3546deaa0c30 ("ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer()") Signed-off-by: Sasha Levin --- net/ipv4/igmp.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 980f6fa77a6bb..d8993d8285a54 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -207,8 +207,8 @@ static void igmp_stop_timer(struct ip_mc_list *im) spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) refcount_dec(&im->refcnt); - im->tm_running = 0; - im->reporter = 0; + WRITE_ONCE(im->tm_running, 0); + WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); } @@ -218,7 +218,7 @@ static void igmp_start_timer(struct ip_mc_list *im, int max_delay) { int tv = get_random_u32_below(max_delay); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); if (refcount_inc_not_zero(&im->refcnt)) { if (mod_timer(&im->timer, jiffies + tv + 2)) ip_ma_put(im); @@ -258,7 +258,7 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) if (timer_delete(&im->timer)) { if ((long)(im->timer.expires-jiffies) < max_delay) { add_timer(&im->timer); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); spin_unlock_bh(&im->lock); return; } @@ -848,12 +848,12 @@ static void igmp_timer_expire(struct timer_list *t) struct in_device *in_dev = im->interface; spin_lock(&im->lock); - im->tm_running = 0; + WRITE_ONCE(im->tm_running, 0); if (im->unsolicit_count && --im->unsolicit_count) igmp_start_timer(im, unsolicited_report_interval(in_dev)); - im->reporter = 1; + WRITE_ONCE(im->reporter, 1); spin_unlock(&im->lock); if (IGMP_V1_SEEN(in_dev)) @@ -1315,7 +1315,7 @@ static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) return; - reporter = im->reporter; + reporter = READ_ONCE(im->reporter); igmp_stop_timer(im); if (!in_dev->dead) { @@ -2953,6 +2953,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) struct ip_mc_list *im = v; struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); char *querier; + int tm_running; long delta; #ifdef CONFIG_IP_MULTICAST @@ -2968,13 +2969,14 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); } - delta = im->timer.expires - jiffies; + tm_running = READ_ONCE(im->tm_running); + delta = READ_ONCE(im->timer.expires) - jiffies; seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", im->multiaddr, READ_ONCE(im->users), - im->tm_running, - im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, - im->reporter); + tm_running, + tm_running ? jiffies_delta_to_clock_t(delta) : 0, + READ_ONCE(im->reporter)); } return 0; } From f60ec3058a85447008b88b762c859d336163acb3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:56 +0000 Subject: [PATCH 1692/3297] ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer() [ Upstream commit 3546deaa0c30a14c7cdb5dc8f2432cb428f0cd36 ] When a timer is deleted and not re-armed in igmp_mod_timer(), or stopped in igmp_stop_timer(), the code currently decrements the reference counter of the multicast list entry @im using refcount_dec(&im->refcnt). However, both functions can be called from the RCU reader path: - igmp_mod_timer() via igmp_heard_query() -> for_each_pmc_rcu() - igmp_stop_timer() via igmp_rcv() -> igmp_heard_report() If the group im was concurrently removed from the list by ip_mc_dec_group(), its reference count might have already been decremented to 1. In this case, timer_delete() succeeds, and refcount_dec() decrements the refcount from 1 to 0. Since refcount_dec() does not free the object when it hits 0 (unlike ip_ma_put()), the im structure is leaked. Fix this by using ip_ma_put(im) instead of refcount_dec(&im->refcnt), and deferring the put until after the spinlock is released. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-4-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/igmp.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d8993d8285a54..aca035eb58929 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -204,13 +204,18 @@ static void ip_sf_list_clear_all(struct ip_sf_list *psf) static void igmp_stop_timer(struct ip_mc_list *im) { + bool put = false; + spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) - refcount_dec(&im->refcnt); + put = true; WRITE_ONCE(im->tm_running, 0); WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } /* It must be called with locked im->lock */ @@ -253,6 +258,8 @@ static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) { + bool put = false; + spin_lock_bh(&im->lock); im->unsolicit_count = 0; if (timer_delete(&im->timer)) { @@ -262,10 +269,13 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) spin_unlock_bh(&im->lock); return; } - refcount_dec(&im->refcnt); + put = true; } igmp_start_timer(im, max_delay); spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } From 238c612357b5a25f03eacf356f95034f8551f218 Mon Sep 17 00:00:00 2001 From: Tamaki Yanagawa Date: Fri, 3 Jul 2026 16:22:57 +0000 Subject: [PATCH 1693/3297] netfilter: nft_lookup: fix catchall element handling with inverted lookups [ Upstream commit e6107a4c74b54cb33e3bce162a63048ae5a6b198 ] nft_lookup_eval() decides whether a lookup matched (`found`) from the direct set lookup and priv->invert before falling back to the catchall element used by interval sets (e.g. nft_set_rbtree) for the open-ended default range. Since `found` is never recomputed after `ext` is replaced by the catchall lookup, inverted lookups (NFT_LOOKUP_F_INV, "!= @set") can wrongly match or wrongly skip the catchall element, producing the wrong verdict. Fold the catchall lookup into `ext` before computing `found`, matching the order already used by nft_objref_map_eval(). Fixes: aaa31047a6d2 ("netfilter: nftables: add catch-all set element support") Signed-off-by: Tamaki Yanagawa Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nft_lookup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 58c5b14889c47..beca9c7705e01 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr, bool found; ext = nft_set_do_lookup(net, set, ®s->data[priv->sreg]); + if (!ext) + ext = nft_set_catchall_lookup(net, set); + found = !!ext ^ priv->invert; if (!found) { - ext = nft_set_catchall_lookup(net, set); - if (!ext) { - regs->verdict.code = NFT_BREAK; - return; - } + regs->verdict.code = NFT_BREAK; + return; } if (ext) { From d340e351a0a74264b36b5880690eda8347548741 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:22 +0800 Subject: [PATCH 1694/3297] ipvs: pass parsed transport offset to state handlers [ Upstream commit bae7ce7bafb59e42dc0e0e2999fdd9d1cffe3866 ] IPVS callers already parse the packet into struct ip_vs_iphdr before updating connection state. For IPv6 this records the real transport-header offset after extension headers in iph.len. Pass this parsed transport offset through ip_vs_set_state() and the protocol state_transition() callback so protocol handlers can use the same packet context as scheduling and NAT handling. This patch only changes the common callback plumbing and adapts the protocol callback signatures; TCP and SCTP start using the value in follow-up patches. Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Stable-dep-of: 2500fa3958b1 ("ipvs: use parsed transport offset in TCP state lookup") Signed-off-by: Sasha Levin --- include/net/ip_vs.h | 3 ++- net/netfilter/ipvs/ip_vs_core.c | 10 +++++----- net/netfilter/ipvs/ip_vs_proto_sctp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_tcp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 2163f32ef6abc..2be04ca409d1f 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -516,7 +516,8 @@ struct ip_vs_protocol { void (*state_transition)(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd); + struct ip_vs_proto_data *pd, + unsigned int iph_len); int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 5ea7ab8bf4dcc..32e7230952102 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -220,10 +220,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) static inline void ip_vs_set_state(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, unsigned int iph_len) { if (likely(pd->pp->state_transition)) - pd->pp->state_transition(cp, direction, skb, pd); + pd->pp->state_transition(cp, direction, skb, pd, iph_len); } static inline int @@ -625,7 +625,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_in_stats(cp, skb); /* set state */ - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len); /* transmit the first SYN packet */ ret = cp->packet_xmit(skb, cp, pd->pp, iph); @@ -1306,7 +1306,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, after_nat: ip_vs_out_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len); skb->ipvs_property = 1; if (!(cp->flags & IP_VS_CONN_F_NFCT)) ip_vs_notrack(skb); @@ -2055,7 +2055,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet"); ip_vs_in_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp, &iph); /* do not touch skb anymore */ diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 63c78a1f3918a..394367b7b3887 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -468,7 +468,8 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void sctp_state_transition(struct ip_vs_conn *cp, int direction, - const struct sk_buff *skb, struct ip_vs_proto_data *pd) + const struct sk_buff *skb, struct ip_vs_proto_data *pd, + unsigned int iph_len) { spin_lock_bh(&cp->lock); set_sctp_state(pd, cp, direction, skb); diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index ede4fa3b63f52..dc8394383c148 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -580,7 +580,8 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void tcp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { struct tcphdr _tcph, *th; diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index ffbebda547fc1..dbd4155bb0752 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -445,7 +445,8 @@ static const char * udp_state_name(int state) static void udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { if (unlikely(!pd)) { pr_err("UDP no ns data\n"); From d73f4249776dd970ad65a69cfc51613dd8a034bb Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:23 +0800 Subject: [PATCH 1695/3297] ipvs: use parsed transport offset in TCP state lookup [ Upstream commit 2500fa3958b1ba51c2b065e39db1b04dfa7e23a2 ] TCP state handling reparses the skb to find the TCP header. For IPv6 it uses sizeof(struct ipv6hdr), while the surrounding IPVS code already parsed the packet with ip_vs_fill_iph_skb() and has the real transport-header offset in iph.len. This makes TCP state handling look at the wrong bytes when an IPv6 packet carries extension headers. Use the parsed transport offset passed down from ip_vs_set_state() when reading the TCP header. For IPv4 and for IPv6 packets without extension headers, the passed offset matches the previous value. Fixes: 0bbdd42b7efa6 ("IPVS: Extend protocol DNAT/SNAT and state handlers") Link: https://lore.kernel.org/netdev/20260705125659.37744-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/ipvs/ip_vs_proto_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index dc8394383c148..b382810156b2c 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -585,13 +585,7 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, { struct tcphdr _tcph, *th; -#ifdef CONFIG_IP_VS_IPV6 - int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - int ihl = ip_hdrlen(skb); -#endif - - th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph); if (th == NULL) return; From 7510451a58c271131d353243c636964334e0b5b4 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 15:34:28 +0800 Subject: [PATCH 1696/3297] ipvs: fix PMTU for GUE/GRE tunnel ICMP errors [ Upstream commit 6b335af0d0d1ff44ac579d106953bf19299e5233 ] When an ICMP Fragmentation Needed error is received for a tunneled IPVS connection, ip_vs_in_icmp() recomputes the MTU that the original packet can use by subtracting the tunnel overhead from the reported next-hop MTU. The current code always subtracts sizeof(struct iphdr), which is only the IPIP overhead. For GUE and GRE tunnels, ipvs_udp_decap() and ipvs_gre_decap() already compute the additional tunnel header length, but that value is scoped to the decapsulation block and is lost before the ICMP_FRAG_NEEDED handling. As a result, the ICMP error sent back to the client advertises an MTU that is too large, so PMTUD can fail to converge for GUE/GRE-tunneled real servers. With a reported next-hop MTU of 1400, a GUE tunnel currently returns 1380 to the client. The correct value is 1368: 1400 - sizeof(struct iphdr) - sizeof(struct udphdr) - sizeof(struct guehdr) Hoist the tunnel header length into the main ip_vs_in_icmp() scope and subtract sizeof(struct iphdr) + ulen in the Fragmentation Needed path. The IPIP path keeps ulen as 0, so its existing 1400 - 20 = 1380 result is unchanged. Fixes: 508f744c0de3 ("ipvs: strip udp tunnel headers from icmp errors") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Stable-dep-of: 3f7a535ff0fa ("ipvs: ensure inner headers in ICMP errors are in headroom") Signed-off-by: Sasha Levin --- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 32e7230952102..a7dc5014e0fe7 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1589,6 +1589,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + int ulen = 0; *related = 1; @@ -1653,7 +1654,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Error for our tunnel must arrive at LOCAL_IN */ (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) { __u8 iproto; - int ulen; /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) @@ -1758,8 +1758,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr)) - mtu -= sizeof(struct iphdr); + if (mtu > 68 + sizeof(struct iphdr) + ulen) + mtu -= sizeof(struct iphdr) + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of From 8f48cfe657409fb5c7ba0521b14da6d47546d9cf Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 7 Jul 2026 21:25:46 +0300 Subject: [PATCH 1697/3297] ipvs: ensure inner headers in ICMP errors are in headroom [ Upstream commit 3f7a535ff0fa627a0132803e4c2f903ceffcbc1c ] Sashiko points out that after stripping the outer headers with pskb_pull() we should ensure the inner IP headers in ICMP errors from tunnels are present in the skb headroom for functions like ipv4_update_pmtu(), icmp_send() and IP_VS_DBG(). Also, add more checks for the length of the inner headers. Fixes: f2edb9f7706d ("ipvs: implement passive PMTUD for IPIP packets") Link: https://sashiko.dev/#/patchset/20260702073430.67680-1-zhaoyz24%40mails.tsinghua.edu.cn Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/ipvs/ip_vs_core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index a7dc5014e0fe7..594c7b24007d6 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1589,6 +1589,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + unsigned int hlen_ipip; int ulen = 0; *related = 1; @@ -1626,9 +1627,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ raddr = (union nf_inet_addr *)&cih->daddr; + hlen_ipip = cih->ihl * 4; /* Special case for errors for IPIP/UDP/GRE tunnel packets */ tunnel = false; @@ -1644,9 +1646,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Only for known tunnel */ if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP) return NF_ACCEPT; - offset += cih->ihl * 4; + offset += hlen_ipip; cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ tunnel = true; } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ @@ -1658,7 +1660,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) return NF_ACCEPT; - offset2 = offset + cih->ihl * 4; + offset2 = offset + hlen_ipip; if (cih->protocol == IPPROTO_UDP) { ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, raddr, &iproto); @@ -1727,6 +1729,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, } if (tunnel) { + unsigned int hlen_orig = cih->ihl * 4; __be32 info = ic->un.gateway; __u8 type = ic->type; __u8 code = ic->code; @@ -1743,6 +1746,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, goto ignore_tunnel; offset2 -= ihl + sizeof(_icmph); skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_ipip)) + goto ignore_tunnel; IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n", outer_proto, &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, mtu); @@ -1758,8 +1764,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr) + ulen) - mtu -= sizeof(struct iphdr) + ulen; + if (mtu > 68 + hlen_ipip + ulen) + mtu -= hlen_ipip + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of @@ -1768,6 +1774,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (pskb_pull(skb, offset2) == NULL) goto ignore_tunnel; skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_orig)) + goto ignore_tunnel; IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n", &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, type, code, ntohl(info)); From df0e3e70f6995465e6e357bae1d1f0fa9fffd77a Mon Sep 17 00:00:00 2001 From: Rongguang Wei Date: Fri, 26 Jun 2026 13:58:20 +0800 Subject: [PATCH 1698/3297] s390/zcrypt: Remove the empty file [ Upstream commit 575f87b9d4882b0d621192fdd754d7e09dbd5789 ] The files has no real user because CEX2 and CEX3 device drivers are removed, also remove these empty files. Fixes: 5ac8c72462cd ("s390/zcrypt: remove CEX2 and CEX3 device drivers") Signed-off-by: Rongguang Wei Reviewed-by: Holger Dengler Signed-off-by: Vasily Gorbik Signed-off-by: Sasha Levin --- drivers/s390/crypto/zcrypt_cex2a.c | 0 drivers/s390/crypto/zcrypt_cex2a.h | 0 drivers/s390/crypto/zcrypt_cex2c.c | 0 drivers/s390/crypto/zcrypt_cex2c.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2a.h delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.c delete mode 100644 drivers/s390/crypto/zcrypt_cex2c.h diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2c.c b/drivers/s390/crypto/zcrypt_cex2c.c deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/drivers/s390/crypto/zcrypt_cex2c.h b/drivers/s390/crypto/zcrypt_cex2c.h deleted file mode 100644 index e69de29bb2d1d..0000000000000 From b784cd1c24d89bf71be2efe1e948ff7c03371e57 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:27:10 +0800 Subject: [PATCH 1699/3297] cifs: validate DFS referral string offsets [ Upstream commit 027a84ac6b50c12ef767c15abfc58aa865820e9e ] parse_dfs_referrals() validates that the response header and referral array fit in the received buffer, but each referral also contains string offsets supplied by the server. Those offsets are used to compute the DfsPath and NetworkAddress string pointers without checking whether they still point inside the response buffer. A malformed referral can therefore make the computed pointer exceed the end of the buffer. The resulting negative max_len is then passed to cifs_strndup_from_utf16(), and the non-Unicode path forwards it to kstrndup() as a size_t, allowing strnlen() to read out of bounds. Validate each string offset before deriving the string pointer. Fixes: 4ecce920e13a ("CIFS: move DFS response parsing out of SMB1 code") Signed-off-by: Guangshuo Li Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/misc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 3770f8348541d..f378437113129 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -1032,6 +1032,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); /* copy DfsPath */ + if (le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; node->path_name = cifs_strndup_from_utf16(temp, max_len, @@ -1042,6 +1046,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, } /* copy link target UNC */ + if (le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; node->node_name = cifs_strndup_from_utf16(temp, max_len, From 13965a7b190f37b89548f0197cbf2603f27cc9c7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:18 -0400 Subject: [PATCH 1700/3297] SUNRPC: release lower rpc_clnt if killed waiting for XPRT_LOCKED [ Upstream commit 29bacb096d43bd04062f6655e50ceeed27fe6d2f ] xs_tcp_tls_setup_socket() creates a temporary "lower" rpc_clnt with rpc_create() to drive the inner TLS handshake, then waits for XPRT_LOCKED on its xprt with TASK_KILLABLE so a stuck handshake can be aborted by signal. When the wait is interrupted, the function jumps to out_unlock without releasing lower_clnt. The success path and the out_close error path both call rpc_shutdown_client(lower_clnt); only the killed-wait path skips it, leaking the clnt and its underlying xprt. Call rpc_shutdown_client() on this path before joining out_unlock. xprt_release_write() is not needed here because XPRT_LOCKED was never acquired. Fixes: 26e8bfa30dac ("SUNRPC/TLS: Lock the lower_xprt during the tls handshake") Signed-off-by: Chuck Lever Tested-by: Michael Nemanov Reviewed-by: Michael Nemanov Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtsock.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 3aa987e7f0724..310f8952018e5 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2733,8 +2733,11 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) lower_xprt = rcu_dereference(lower_clnt->cl_xprt); rcu_read_unlock(); - if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) + if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) { + /* XPRT_LOCKED was never acquired. */ + rpc_shutdown_client(lower_clnt); goto out_unlock; + } status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec); if (status) { From 5b0427ba582d143a364301f825f4e32272f06d2d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:19 -0400 Subject: [PATCH 1701/3297] SUNRPC: pin upper rpc_clnt across the TLS connect_worker [ Upstream commit 46bc86c833956219bbfd246c1ffd832a479c5199 ] The TLS connect path has a use-after-free: nothing pins the upper rpc_clnt across the delayed connect_worker. xs_connect() stores task->tk_client in sock_xprt::clnt as a raw pointer and queues the worker; for TLS-secured transports that worker is xs_tcp_tls_setup_socket(), which reads several fields out of the saved pointer (cl_timeout, cl_program, cl_prog, cl_vers, cl_cred, cl_stats) to construct the args for the inner handshake rpc_clnt. The xprt does not reference the rpc_clnt; the rpc_clnt references the xprt. xs_destroy() does cancel the connect_worker, but it runs only when the xprt's refcount drops to zero, which cannot happen until the rpc_clnt releases its cl_xprt reference in rpc_free_client_work(). When a TLS handshake fails fatally (for example, an mTLS mount whose client cert does not match the server), the connecting task is woken with -EACCES and exits, the mount caller invokes rpc_shutdown_client(), and the upper rpc_clnt is freed before the queued connect_worker fires. xs_tcp_tls_setup_socket() then dereferences the freed clnt, producing the refcount_t underflow Michael Nemanov reported. Take a reference on the upper rpc_clnt in xs_connect() for TLS transports via a new rpc_hold_client() helper, and drop it in the connect_worker's exit path with rpc_release_client(). The xprt_lock_connect() / xprt_unlock_connect() pairing already serialises xs_connect() with xs_tcp_tls_setup_socket(), so the take and release are balanced one-for-one. The non-TLS connect worker (xs_tcp_setup_socket) never reads sock_xprt::clnt, so leave that path alone and avoid the clnt-holds-xprt-holds-clnt cycle that would otherwise prevent xprt destruction. Reported-by: Michael Nemanov Closes: https://lore.kernel.org/linux-nfs/40e3d522-dfcf-4fc1-9c55-b5e81f1536d5@vastdata.com/ Fixes: 75eb6af7acdf ("SUNRPC: Add a TCP-with-TLS RPC transport class") Signed-off-by: Chuck Lever Tested-by: Michael Nemanov Reviewed-by: Michael Nemanov Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- include/linux/sunrpc/clnt.h | 1 + net/sunrpc/clnt.c | 19 +++++++++++++++++-- net/sunrpc/xprtsock.c | 11 ++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index f8b406b0a1af5..3c2b8c355ab3a 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -190,6 +190,7 @@ int rpc_switch_client_transport(struct rpc_clnt *, const struct rpc_timeout *); void rpc_shutdown_client(struct rpc_clnt *); +void rpc_hold_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); void rpc_task_release_transport(struct rpc_task *); void rpc_task_release_client(struct rpc_task *); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 8ca354ecfd02a..a142e20ce7e7c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1026,8 +1026,23 @@ rpc_free_auth(struct rpc_clnt *clnt) return NULL; } -/* - * Release reference to the RPC client +/** + * rpc_hold_client - acquire a reference on an rpc_clnt + * @clnt: rpc_clnt to pin + * + * Pairs with rpc_release_client(). + */ +void rpc_hold_client(struct rpc_clnt *clnt) +{ + refcount_inc(&clnt->cl_count); +} + +/** + * rpc_release_client - release a reference on an rpc_clnt + * @clnt: rpc_clnt to release + * + * Pairs with rpc_hold_client(). The rpc_clnt's resources are + * freed once its reference count drops to zero. */ void rpc_release_client(struct rpc_clnt *clnt) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 310f8952018e5..6b64c6c494941 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2760,6 +2760,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) out_unlock: current_restore_flags(pflags, PF_MEMALLOC); upper_transport->clnt = NULL; + rpc_release_client(upper_clnt); xprt_unlock_connect(upper_xprt, upper_transport); return; @@ -2807,7 +2808,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task) } else dprintk("RPC: xs_connect scheduled xprt %p\n", xprt); - transport->clnt = task->tk_client; + /* + * Only the TLS connect_worker reads transport->clnt; pinning + * the upper rpc_clnt unconditionally would form a cycle with + * cl_xprt and prevent xprt destruction. + */ + if (xprt->xprtsec.policy != RPC_XPRTSEC_NONE) { + rpc_hold_client(task->tk_client); + transport->clnt = task->tk_client; + } queue_delayed_work(xprtiod_workqueue, &transport->connect_worker, delay); From 14e03ecd3b1b5fc03c082b27a2f8889f8290c30e Mon Sep 17 00:00:00 2001 From: Cao Guanghui Date: Wed, 17 Jun 2026 14:00:52 +0800 Subject: [PATCH 1702/3297] dm era: fix NULL pointer dereference in metadata_open() [ Upstream commit 9ae672606c17891d90b282e3490b817620549599 ] metadata_open() returns NULL when kzalloc_obj() fails, but the caller era_ctr() only checks IS_ERR(md). Since IS_ERR(NULL) returns false, the NULL pointer is treated as a valid result and later assigned to era->md, leading to a NULL pointer dereference when the metadata is accessed. Fix this by returning ERR_PTR(-ENOMEM) on allocation failure, consistent with dm-cache-metadata.c, dm-thin-metadata.c, and dm-clone-metadata.c which all use ERR_PTR(-ENOMEM) for the same pattern. Fixes: eec40579d848 ("dm: add era target") Signed-off-by: Cao Guanghui Reviewed-by: Su Yue Reviewed-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka Signed-off-by: Sasha Levin --- drivers/md/dm-era-target.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 9c84e9d13eca6..5cc71dbc7e039 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -810,8 +810,10 @@ static struct era_metadata *metadata_open(struct block_device *bdev, int r; struct era_metadata *md = kzalloc(sizeof(*md), GFP_KERNEL); - if (!md) - return NULL; + if (!md) { + DMERR("could not allocate metadata struct"); + return ERR_PTR(-ENOMEM); + } md->bdev = bdev; md->block_size = block_size; From 346e2d666a29ae7233c56b356a0487eb1d42589b Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 8 Jul 2026 18:57:22 -0500 Subject: [PATCH 1703/3297] regulator: core: regulator_lock_two() should test for EDEADLK not EDEADLOCK [ Upstream commit d38f8bd771c4999b797d7074b348cf201414bd34 ] Compare against -EDEADLK, which is what ww_mutex_lock() actually returns and what every other deadlock check in this file already uses. Function regulator_lock_two() acquires two regulators via regulator_lock_nested() -> ww_mutex_lock(). On contention, ww_mutex_lock() returns -EDEADLK, which is the caller's signal to drop the lock it holds and retry the acquisition in the canonical order. However, regulator_lock_two() tests the return value against -EDEADLOCK rather than -EDEADLK. On most architectures, EDEADLK and EDEADLOCK are the same value, so the comparison happens to be correct and the bug is invisible. But on MIPS, SPARC, and PowerPC, those two errors have different values. The test is wrong: a genuine -EDEADLK backoff no longer matches -EDEADLOCK, so instead of unlocking and retrying, the code falls into WARN_ON(ret) and returns with only one of the two regulators locked. In practice, this is a bug only on MIPS, because the regulator core is not built or used on the other two platforms. In general, EDEADLK is preferred over EDEADLOCK for new code. Fixes: cba6cfdc7c3f ("regulator: core: Avoid lockdep reports when resolving supplies") Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260708235722.2953579-1-ttabi@nvidia.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 761a3f9058593..019606bc36b9c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -232,7 +232,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, ret = regulator_lock_nested(rdev1, ww_ctx); WARN_ON(ret); ret = regulator_lock_nested(rdev2, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); goto exit; } @@ -248,7 +248,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1, swap(held, contended); ret = regulator_lock_nested(contended, ww_ctx); - if (ret != -EDEADLOCK) { + if (ret != -EDEADLK) { WARN_ON(ret); break; } From 1550b07bca2bba9eb69a5f5f855a1fa2513ddc90 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Thu, 2 Jul 2026 10:59:49 +0800 Subject: [PATCH 1704/3297] selftests/net: fix EVP_MD_CTX leak in tcp_mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f4ef35efbb49527293309f668ea73ec5de9b8e7a ] In tcp_mmap.c, both child_thread() and main() allocate an EVP_MD_CTX via EVP_MD_CTX_new() when integrity checking is enabled, but neither function releases the context. child_thread() misses the free in its common cleanup block, and main() returns without freeing the context. This results in a SHA256 context leak on every run that uses the ‑i (integrity) option. Add the missing EVP_MD_CTX_free() calls to the appropriate cleanup paths to fix the leak. Fixes: 5c5945dc695c ("selftests/net: Add SHA256 computation over data sent in tcp_mmap") Signed-off-by: Wang Yan Link: https://patch.msgid.link/20260702025949.442523-1-wangyan01@kylinos.cn Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- tools/testing/selftests/net/tcp_mmap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c index 4fcce5150850d..2544ae35d07a8 100644 --- a/tools/testing/selftests/net/tcp_mmap.c +++ b/tools/testing/selftests/net/tcp_mmap.c @@ -313,6 +313,8 @@ void *child_thread(void *arg) tcp_info_get_rcv_mss(fd)); } error: + if (ctx) + EVP_MD_CTX_free(ctx); munmap(buffer, buffer_sz); close(fd); if (zflg) @@ -606,6 +608,8 @@ int main(int argc, char *argv[]) EVP_DigestFinal_ex(ctx, digest, &digest_len); send(fd, digest, (size_t)SHA256_DIGEST_LENGTH, 0); } + if (ctx) + EVP_MD_CTX_free(ctx); close(fd); munmap(buffer, buffer_sz); return 0; From ee3f7566bcf331fe669e104fd925e5946fb6869d Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 3 Jul 2026 22:14:23 +0800 Subject: [PATCH 1705/3297] net/mlx5: Fix L3 tunnel entropy refcount leak [ Upstream commit c914307e1d41c2cb7bcdcbfde4cd2f214f6aa027 ] mlx5_tun_entropy_refcount_inc() counts both VXLAN and L2-to-L3 tunnel reformat entries as entropy-enabling users. The matching decrement path only handled VXLAN, leaving L2-to-L3 tunnel entries counted after release. Handle MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL in mlx5_tun_entropy_refcount_dec() as well so the enabling entry refcount remains balanced. Fixes: f828ca6a2fb6 ("net/mlx5e: Add support for hw encapsulation of MPLS over UDP") Signed-off-by: Li RongQing Reviewed-by: Simon Horman Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260703141423.1723-1-lirongqing@baidu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c index 4571c56ec3c9b..97f6097d4c705 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c @@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, int reformat_type) { mutex_lock(&tun_entropy->lock); - if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN) + if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN || + reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL) tun_entropy->num_enabling_entries--; else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE && --tun_entropy->num_disabling_entries == 0) From 3de77d2f34c2bc2acaedabc2c5e0a561b85c283a Mon Sep 17 00:00:00 2001 From: Harman Kalra Date: Thu, 2 Jul 2026 10:26:16 +0530 Subject: [PATCH 1706/3297] octeontx2-af: fix VF bringup affecting PF promiscuous state [ Upstream commit fabb881df322da25442f98d23f5fa371e3c78ec4 ] Mbox handling of nix_set_rx_mode for a VF with promiscuous and all_multi flags set to false causes deletion of the PF's promiscuous and allmulti MCAM rules. This occurs because the APIs that enable/disable these rules operate only on the PF, even when the mbox request is made via a VF interface. Guard both rvu_npc_enable_allmulti_entry() and rvu_npc_enable_promisc_entry() disable paths with an is_vf() check so that a VF bringing up or tearing down its interface cannot inadvertently clear the PF's MCAM rules. Fixes: 967db3529eca ("octeontx2-af: add support for multicast/promisc packet replication feature") Signed-off-by: Harman Kalra Signed-off-by: Nitin Shetty J Link: https://patch.msgid.link/20260702045616.3002773-2-nshettyj@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index c9c65ac69ead8..3563cfc679049 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4539,7 +4539,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base); } else { - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false); } @@ -4549,7 +4549,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, pfvf->rx_chan_base, pfvf->rx_chan_cnt); else - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false); return 0; From 327595e7c34e36fdb5c9245596404d66fa84aede Mon Sep 17 00:00:00 2001 From: Anas Khan Date: Thu, 2 Jul 2026 16:58:20 +0530 Subject: [PATCH 1707/3297] drm/xe: remove duplicate include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 91426ce50d14a49bde53b3ad1e48393556ba92cd ] xe_pci.c includes twice, separated only by the include. Drop the redundant second include; this is a non-functional cleanup flagged by scripts/checkincludes.pl. Fixes: 6cad22853cb8 ("drm/xe/kunit: Add stub to read_gmdid") Signed-off-by: Anas Khan Link: https://patch.msgid.link/20260702112820.34675-1-anxkhn28@gmail.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 84ed5b0a925721aaf069d36e18a99db966ff4e80) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/tests/xe_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c index 663a79ec960d9..b607d9422ef8d 100644 --- a/drivers/gpu/drm/xe/tests/xe_pci.c +++ b/drivers/gpu/drm/xe/tests/xe_pci.c @@ -9,7 +9,6 @@ #include #include -#include #include #define PLATFORM_CASE(platform__, graphics_step__) \ From 1627e7d5c9b09721a141d07cedb178882f1ded67 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 20:35:20 +0800 Subject: [PATCH 1708/3297] smb: client: fix overflow in passthrough ioctl bounds check [ Upstream commit a4f27ad055392fa164f5649e89a3637b033c5fcc ] smb2_ioctl_query_info() validates the PASSTHRU_FSCTL response payload before copying it to userspace. The payload offset and length both come from 32-bit fields. The bounds check currently adds OutputOffset and qi.input_buffer_length directly, so the addition can wrap in 32-bit arithmetic before the result is compared against the response buffer length. A malicious server can use a large OutputOffset and a small OutputCount to make the wrapped sum pass the bounds check. The later copy_to_user() then reads from io_rsp + OutputOffset, outside the response buffer. Use size_add() for the offset plus length check so overflow is treated as out of bounds. Fixes: 2b1116bbe898 ("CIFS: Use common error handling code in smb2_ioctl_query_info()") Signed-off-by: Guangshuo Li Signed-off-by: Steve French Signed-off-by: Sasha Levin --- 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 f714ded6b4ffc..d4095710948e4 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -1764,8 +1764,8 @@ smb2_ioctl_query_info(const unsigned int xid, if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); if (qi.input_buffer_length > 0 && - le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length - > rsp_iov[1].iov_len) { + size_add(le32_to_cpu(io_rsp->OutputOffset), + qi.input_buffer_length) > rsp_iov[1].iov_len) { rc = -EFAULT; goto out; } From b65e46eed9e52483e949e026f9b445f9201284fd Mon Sep 17 00:00:00 2001 From: Przemyslaw Korba Date: Mon, 25 May 2026 10:38:03 +0200 Subject: [PATCH 1709/3297] idpf: add padding to PTP virtchnl structures commit d1e8f9fd6b98307bc8d2863c7baa465d8a5a43be upstream. Add padding to virtchnl2 PTP structures to match the Control Plane expected message sizes: * virtchnl2_ptp_get_dev_clk_time: 8 -> 16 bytes * virtchnl2_ptp_set_dev_clk_time: 8 -> 16 bytes * virtchnl2_ptp_get_cross_time: 16 -> 24 bytes The FW expects the above sizes and PTP negotiation fails due to the mismatch. Previously neither the FW nor the driver checked message/reply sizes strictly, so the problem appeared only after recent validation improvements. reproduction steps: ptp4l -i -m Observe: failed to open /dev/ptp0: Permission denied Fixes: bf27283ba594 ("virtchnl: add PTP virtchnl definitions") Cc: stable@vger.kernel.org Reviewed-by: Aleksandr Loktionov Reviewed-by: Alexander Lobakin Signed-off-by: Przemyslaw Korba Tested-by: Samuel Salin Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/idpf/virtchnl2.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/drivers/net/ethernet/intel/idpf/virtchnl2.h index 02ae447cc24ac..39fea65c075c7 100644 --- a/drivers/net/ethernet/intel/idpf/virtchnl2.h +++ b/drivers/net/ethernet/intel/idpf/virtchnl2.h @@ -1572,13 +1572,15 @@ VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_vport_tx_tstamp_latches); * struct virtchnl2_ptp_get_dev_clk_time - Associated with message * VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME. * @dev_time_ns: Device clock time value in nanoseconds + * @pad: Padding for future extensions * * PF/VF sends this message to receive the time from the main timer. */ struct virtchnl2_ptp_get_dev_clk_time { __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_get_dev_clk_time); +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time); /** * struct virtchnl2_ptp_get_cross_time: Associated with message @@ -1586,26 +1588,30 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_get_dev_clk_time); * @sys_time_ns: System counter value expressed in nanoseconds, read * synchronously with device time * @dev_time_ns: Device clock time value expressed in nanoseconds + * @pad: Padding for future extensions * * PF/VF sends this message to receive the cross time. */ struct virtchnl2_ptp_get_cross_time { __le64 sys_time_ns; __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_cross_time); +VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time); /** * struct virtchnl2_ptp_set_dev_clk_time: Associated with message * VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME. * @dev_time_ns: Device time value expressed in nanoseconds to set + * @pad: Padding for future extensions * * PF/VF sends this message to set the time of the main timer. */ struct virtchnl2_ptp_set_dev_clk_time { __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_set_dev_clk_time); +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time); /** * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message From 2d8b3c3e129973a51ae924bdcf6993a76b828814 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 08:37:09 +0000 Subject: [PATCH 1710/3297] mlxsw: fix refcount leak in mlxsw_sp_port_lag_join() commit 41c8c1d65b32beacd8d916a22457b4f6e47f45af upstream. When mlxsw_sp_port_lag_index_get() fails, mlxsw_sp_port_lag_join() returns an error without releasing the lag reference obtained by the earlier mlxsw_sp_lag_get(). All other error paths in the function jump to the cleanup label that ends with mlxsw_sp_lag_put(), so this is a single missed release. Fix the leak by replacing the bare 'return err' with a goto to the existing error cleanup label, which will drop the reference safely. Cc: stable@vger.kernel.org Fixes: 0d65fc13042f ("mlxsw: spectrum: Implement LAG port join/leave") Signed-off-by: Wentao Liang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260609083709.209743-1-vulab@iscas.ac.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 9a2d64a0a8588..b8c120585d17a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4363,7 +4363,7 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, lag_id = lag->lag_id; err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index); if (err) - return err; + goto err_lag_uppers_bridge_join; err = mlxsw_sp_lag_uppers_bridge_join(mlxsw_sp_port, lag_dev, extack); From 3a2b47d1b4b3de54d030a7fdb6a322c970513ee3 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 08:47:30 +0000 Subject: [PATCH 1711/3297] mlxsw: fix refcount leak in mlxsw_sp_vrs_lpm_tree_replace() commit 21cf8dc478a49e8de039c2739b1646a774cb1944 upstream. When mlxsw_sp_vrs_lpm_tree_replace() fails after replacing some VRs, the error rollback loop does not correctly revert the preceding replacements. The loop decrements the index but fails to update the vr pointer, which still points to the VR that caused the failure. As a result, the condition and the rollback call always operate on the same VR, potentially calling mlxsw_sp_vr_lpm_tree_replace() multiple times on it while never rolling back the earlier VRs. Those VRs continue to hold a reference to new_tree acquired via mlxsw_sp_lpm_tree_hold(), leaking the reference count of new_tree. Fix by reinitializing vr inside the error loop with the updated index: vr = &mlxsw_sp->router->vrs[i]; so that the loop correctly iterates over all VRs that were actually replaced. Cc: stable@vger.kernel.org Fixes: fc922bb0dd94 ("mlxsw: spectrum_router: Use one LPM tree for all virtual routers") Signed-off-by: Wentao Liang Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260609084730.215732-1-vulab@iscas.ac.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2d0e89bd2fb9c..a1a266479c95e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1019,6 +1019,7 @@ static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, err_tree_replace: for (i--; i >= 0; i--) { + vr = &mlxsw_sp->router->vrs[i]; if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id)) continue; mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, From d8715b5a8fdb23fef576700e71d0c253dbeddad4 Mon Sep 17 00:00:00 2001 From: Zhang Tianci Date: Thu, 26 Feb 2026 19:55:50 +0800 Subject: [PATCH 1712/3297] vduse: Fix race in vduse_dev_msg_sync and vduse_dev_read_iter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ae9c13b6fd79087cc5a216ee1649b6f012c2a238 upstream. There is one race case in vduse_dev_msg_sync and vduse_dev_read_iter: vduse_dev_read_iter(): lock(msg_lock); dequeue_msg(send_list); unlock(msg_lock); vduse_dev_msg_sync(): wait_timeout() finish lock(msg_lock); check msg->complete is false list_del(msg); <- double list_del() crash! To fix this case, we shall ensure vduse_msg is on send_list or recv_list outside the msg_lock critical section. Fixes: c8a6153b6c59 ("vduse: Introduce VDUSE - vDPA Device in Userspace") Cc: stable@vger.kernel.org Signed-off-by: Zhang Tianci Reviewed-by: Xie Yongji Acked-by: Jason Wang Acked-by: Eugenio Pérez Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260226115550.1814-3-zhangtianci.1997@bytedance.com> Signed-off-by: Greg Kroah-Hartman --- drivers/vdpa/vdpa_user/vduse_dev.c | 37 ++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index ecc3c5b57634b..db7af4b3fd4ad 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -331,6 +331,7 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) struct file *file = iocb->ki_filp; struct vduse_dev *dev = file->private_data; struct vduse_dev_msg *msg; + struct vduse_dev_request req; int size = sizeof(struct vduse_dev_request); ssize_t ret; @@ -342,12 +343,11 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) msg = vduse_dequeue_msg(&dev->send_list); if (msg) break; + spin_unlock(&dev->msg_lock); - ret = -EAGAIN; if (file->f_flags & O_NONBLOCK) - goto unlock; + return -EAGAIN; - spin_unlock(&dev->msg_lock); ret = wait_event_interruptible_exclusive(dev->waitq, !list_empty(&dev->send_list)); if (ret) @@ -355,17 +355,34 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) spin_lock(&dev->msg_lock); } + + memcpy(&req, &msg->req, sizeof(req)); + /* + * We must ensure vduse_msg is on send_list or recv_list before unlock + * dev->msg_lock. Because vduse_dev_msg_sync() may be timeout when we + * copy data to userspace, and will call list_del() for this msg. + */ + vduse_enqueue_msg(&dev->recv_list, msg); spin_unlock(&dev->msg_lock); - ret = copy_to_iter(&msg->req, size, to); - spin_lock(&dev->msg_lock); + + ret = copy_to_iter(&req, size, to); if (ret != size) { + /* + * Roll back: move msg back to send_list if still pending. + * + * NOTE: + * vduse_find_msg() must use req.request_id instead of `msg`. + * A malicious userspace may reply to this request, and wake up + * the caller, after which `msg` will have already been freed. + * And here vduse_find_msg() will return NULL then do nothing. + */ + spin_lock(&dev->msg_lock); + msg = vduse_find_msg(&dev->recv_list, req.request_id); + if (msg) + vduse_enqueue_msg_head(&dev->send_list, msg); + spin_unlock(&dev->msg_lock); ret = -EFAULT; - vduse_enqueue_msg_head(&dev->send_list, msg); - goto unlock; } - vduse_enqueue_msg(&dev->recv_list, msg); -unlock: - spin_unlock(&dev->msg_lock); return ret; } From fb4293173db2d474d8fbc0e5ecf4943e6df2b40e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:53 +0300 Subject: [PATCH 1713/3297] ASoC: SOF: ipc4-control: Fix TOCTOU in sof_ipc4_bytes_put commit 3ad673e7139cf214afd24321a829aad6575f4163 upstream. In sof_ipc4_bytes_put(), the copy size is derived from the old data->size in the buffer rather than the incoming new data's size field from ucontrol. If the new data has a different size, the copy uses the wrong length: it may truncate valid data or copy stale bytes. Fix by validating and using the incoming data's sof_abi_hdr.size from ucontrol before copying. Fixes: a062c8899fed ("ASoC: SOF: ipc4-control: Add support for bytes control get and put") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc4-control.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 453ed1643b89c..e50bc0dad18f7 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -455,6 +455,8 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; + const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data; size_t size; int ret; @@ -465,15 +467,16 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, return -EINVAL; } - /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { + /* Validate the new data's size, not the old one */ + if (new_hdr->size > scontrol->max_size - sizeof(*new_hdr)) { dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + new_hdr->size, + scontrol->max_size - sizeof(*new_hdr)); return -EINVAL; } - size = data->size + sizeof(*data); + size = new_hdr->size + sizeof(*new_hdr); /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); From 711d912b18763af62a63aa8f2419a774eb63bba4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:55 +0300 Subject: [PATCH 1714/3297] ASoC: SOF: ipc3-control: Use overflow checks in control_update size calc commit 8791977d7289f6e9d2b014f60a5455f053a7bc04 upstream. In sof_ipc3_control_update(), the expected_size calculation uses firmware-provided cdata->num_elems in arithmetic that could overflow on 32-bit platforms, wrapping to a small value. This would allow the cdata->rhdr.hdr.size comparison to pass with mismatched sizes, potentially leading to out-of-bounds access in snd_sof_update_control. Use check_mul_overflow() and check_add_overflow() to detect and reject overflowed size calculations. Fixes: 10f461d79c2d ("ASoC: SOF: Add IPC3 topology control ops") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 2b1befad6d5c0..23b0ae3ad4146 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -626,16 +626,28 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ return; } - expected_size = sizeof(struct sof_ipc_ctrl_data); switch (cdata->type) { case SOF_CTRL_TYPE_VALUE_CHAN_GET: case SOF_CTRL_TYPE_VALUE_CHAN_SET: - expected_size += cdata->num_elems * - sizeof(struct sof_ipc_ctrl_value_chan); + if (check_mul_overflow((size_t)cdata->num_elems, + sizeof(struct sof_ipc_ctrl_value_chan), + &expected_size)) + return; + if (check_add_overflow(expected_size, + sizeof(struct sof_ipc_ctrl_data), + &expected_size)) + return; break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: - expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); + if (check_add_overflow((size_t)cdata->num_elems, + sizeof(struct sof_abi_hdr), + &expected_size)) + return; + if (check_add_overflow(expected_size, + sizeof(struct sof_ipc_ctrl_data), + &expected_size)) + return; break; default: return; From 0c4fbdaca225b97122b61b68c5353caa33a253c3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:57 +0300 Subject: [PATCH 1715/3297] ASoC: SOF: ipc3-control: Fix TOCTOU in bytes_put and bytes_get commit 1f97760417b5faa60e9642fd0ed61eb17d0b1b39 upstream. In sof_ipc3_bytes_put(), the size used for the memcpy is derived from the old data->size already in the buffer, not the incoming new data's size field. If the new data has a different size, the copy length is wrong: it may truncate valid data or copy stale bytes. Similarly, sof_ipc3_bytes_get() checks data->size against max_size without accounting for the sizeof(struct sof_ipc_ctrl_data) offset of the flex array within the allocation. Fix bytes_put to validate and use the incoming data's sof_abi_hdr.size from ucontrol before copying. Fix bytes_get to subtract sizeof(*cdata) from the bounds check to match the actual available space. Fixes: 544ac8858f24 ("ASoC: SOF: Add bytes_get/put control IPC ops for IPC3") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 23b0ae3ad4146..e8654edab90bc 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -315,10 +315,13 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { + if (data->size > scontrol->max_size - sizeof(*cdata) - + sizeof(*data)) { dev_err_ratelimited(scomp->dev, "%u bytes of control data is invalid, max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + data->size, + scontrol->max_size - sizeof(*cdata) - + sizeof(*data)); return -EINVAL; } @@ -336,6 +339,8 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; struct sof_abi_hdr *data = cdata->data; + const struct sof_abi_hdr *new_hdr = + (const struct sof_abi_hdr *)ucontrol->value.bytes.data; size_t size; if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { @@ -344,14 +349,18 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, return -EINVAL; } - /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > scontrol->max_size - sizeof(*data)) { - dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", - data->size, scontrol->max_size - sizeof(*data)); + /* Validate the new data's size, not the old one */ + if (new_hdr->size > scontrol->max_size - sizeof(*cdata) - + sizeof(*new_hdr)) { + dev_err_ratelimited(scomp->dev, + "data size too big %u bytes max is %zu\n", + new_hdr->size, + scontrol->max_size - sizeof(*cdata) - + sizeof(*new_hdr)); return -EINVAL; } - size = data->size + sizeof(*data); + size = new_hdr->size + sizeof(*new_hdr); /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); From d34deef34c99bb4b3ebd2ac51058857827a20e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 3 Jun 2026 14:57:54 -0300 Subject: [PATCH 1716/3297] ASoC: SOF: topology: validate vendor array size before parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8468dd79cfb2ffbdeaf7c353f63d64941cb8ba05 upstream. sof_parse_token_sets() reads array->size while iterating over topology private data. The loop condition only checks that some data remains, so a malformed topology with a truncated trailing vendor array can make the parser read the size field before a full vendor-array header is available. Validate that the remaining private data contains a complete snd_soc_tplg_vendor_array header before reading array->size. The declared array size check also needs to remain signed. asize is an int, but sizeof(*array) has type size_t, so comparing them directly promotes negative asize values to unsigned and lets them pass the check, as reported in the stable review thread reference below. Cast sizeof(*array) to int when validating the declared array size. This rejects negative, zero and otherwise too-small sizes before the parser dispatches to the tuple-specific code. Link: https://lore.kernel.org/stable/CANiDSCsjR5NHqu_Ui5cOqWdJgFqmYsQ9WR8O7m0WOhngaYXFpw@mail.gmail.com/t/#m9b3be379221e79327cc13fd71009287368ef4f23 Fixes: 215e5fe75881 ("ASoC: SOF: topology: reject invalid vendor array size in token parser") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260603-sof-topology-array-size-signed-v1-1-84f97879a4ef@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/topology.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 4c8dba285408a..d6134df874ee5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -733,10 +733,13 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp, int ret; while (array_size > 0 && total < count * token_instance_num) { + if (array_size < (int)sizeof(*array)) + return -EINVAL; + asize = le32_to_cpu(array->size); /* validate asize */ - if (asize < sizeof(*array)) { + if (asize < (int)sizeof(*array)) { dev_err(scomp->dev, "error: invalid array size 0x%x\n", asize); return -EINVAL; From 61a55fa24a5d737436018764a647fe5b6cb36371 Mon Sep 17 00:00:00 2001 From: Li Xiasong Date: Thu, 11 Jun 2026 21:56:47 +0800 Subject: [PATCH 1717/3297] tipc: restrict socket queue dumps in enqueue tracepoints commit acd7df8d955480a6f6e5bb809da67b1500cc3cf4 upstream. tipc_sk_enqueue() runs with sk->sk_lock.slock held while the socket is owned by user context. The spinlock protects the backlog queue in this path, but it does not serialize against the socket owner consuming or purging sk_receive_queue. KASAN reported: CPU: 14 UID: 0 PID: 1050 Comm: tipc3 Not tainted 7.1.0-rc6+ #126 PREEMPT(lazy) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 Call Trace: dump_stack_lvl+0x76/0xa0 lib/dump_stack.c:123 print_report+0xce/0x5b0 mm/kasan/report.c:482 kasan_report+0xc6/0x100 mm/kasan/report.c:597 __asan_report_load4_noabort+0x14/0x30 mm/kasan/report_generic.c:380 tipc_skb_dump+0x1327/0x16f0 net/tipc/trace.c:73 tipc_list_dump+0x208/0x2e0 net/tipc/trace.c:187 tipc_sk_dump+0xaf6/0xd60 net/tipc/socket.c:3996 trace_event_raw_event_tipc_sk_class+0x312/0x5a0 net/tipc/trace.h:188 tipc_sk_rcv+0xb1d/0x1d50 net/tipc/socket.c:2497 tipc_node_xmit+0x1c3/0x1440 net/tipc/node.c:1689 __tipc_sendmsg+0x97a/0x1440 net/tipc/socket.c:1512 tipc_sendmsg+0x52/0x80 net/tipc/socket.c:1400 sock_sendmsg+0x2f6/0x3e0 net/socket.c:825 splice_to_socket+0x7f9/0x1010 fs/splice.c:884 do_splice+0xe21/0x2330 fs/splice.c:936 __do_splice+0x153/0x260 fs/splice.c:1431 __x64_sys_splice+0x150/0x230 fs/splice.c:1616 x64_sys_call+0xeb5/0x2790 arch/x86/entry/syscall_64.c:41 do_syscall_64+0xf3/0x620 arch/x86/entry/syscall_64.c:63 entry_SYSCALL_64_after_hwframe+0x76/0x7e arch/x86/entry/entry_64.S:130 RIP: 0033:0x71624e8aafe2 Code: 08 0f 85 71 3a ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 RSP: 002b:0000716157ffed68 EFLAGS: 00000246 ORIG_RAX: 0000000000000113 RAX: ffffffffffffffda RBX: 0000716157fff6c0 RCX: 000071624e8aafe2 RDX: 000000000000005f RSI: 0000000000000000 RDI: 0000000000000066 RBP: 0000716157ffed90 R08: 0000000000008000 R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffff00 R13: 0000000000000021 R14: 0000000000000000 R15: 00007fff89799c40 The TIPC_DUMP_ALL tracepoints in tipc_sk_enqueue() also dump sk_receive_queue and can therefore dereference skbs that the socket owner has already dequeued or freed. Restrict these dumps to TIPC_DUMP_SK_BKLGQ, which matches the queue protected by the held spinlock. Keep the change limited to the enqueue path, where the unsafe queue dump is reachable while the socket is owned by user context. Fixes: 01e661ebfbad ("tipc: add trace_events for tipc socket") Cc: stable@vger.kernel.org Signed-off-by: Li Xiasong Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260611135647.3666727-1-lixiasong1@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/tipc/socket.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index edaf44b512190..677e17ec2939b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2455,17 +2455,17 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, atomic_set(dcnt, 0); lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt); if (likely(!sk_add_backlog(sk, skb, lim))) { - trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_SK_BKLGQ, "bklg & rcvq >90% allocated!"); continue; } - trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL, "err_overload!"); + trace_tipc_sk_dump(sk, skb, TIPC_DUMP_SK_BKLGQ, "err_overload!"); /* Overload => reject message back to sender */ onode = tipc_own_addr(sock_net(sk)); sk_drops_inc(sk); if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) { - trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_SK_BKLGQ, "@sk_enqueue!"); __skb_queue_tail(xmitq, skb); } From 22100a8f73d4ae4f17697dae93d4e2e1d283a6ec Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:54:55 -0400 Subject: [PATCH 1718/3297] net: qrtr: fix 32-bit integer overflow in qrtr_endpoint_post() commit 20054869770c7df060c5ecee3e8bbf9029c47191 upstream. qrtr_endpoint_post() validates an incoming packet with if (!size || len != ALIGN(size, 4) + hdrlen) goto err; where size comes from the wire. On 32-bit, size_t is 32 bits and ALIGN(size, 4) wraps to 0 for size >= 0xfffffffd, so the check passes and skb_put_data(skb, data + hdrlen, size) writes past the hdrlen-sized skb and oopses the kernel. 64-bit is unaffected. This is the 32-bit residual of ad9d24c9429e2 ("net: qrtr: fix OOB Read in qrtr_endpoint_post"), which fixed only the 64-bit case. Reject any size that cannot fit the buffer before the ALIGN. Fixes: ad9d24c9429e2 ("net: qrtr: fix OOB Read in qrtr_endpoint_post") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260611125455.2352279-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/qrtr/af_qrtr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 2c009793f1931..305523cebe3ba 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -496,7 +496,7 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) if (cb->dst_port == QRTR_PORT_CTRL_LEGACY) cb->dst_port = QRTR_PORT_CTRL; - if (!size || len != ALIGN(size, 4) + hdrlen) + if (!size || size > len || len != ALIGN(size, 4) + hdrlen) goto err; if ((cb->type == QRTR_TYPE_NEW_SERVER || From e62adb157c2eaad9ad4867ec6cd9af5b9a51b1c7 Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Tue, 9 Jun 2026 16:34:37 +0800 Subject: [PATCH 1719/3297] net: atm: reject out-of-range traffic classes in QoS validation commit cdf19f380e46192e7084be559638aab1f6ed86a2 upstream. Reject ATM traffic classes above ATM_ANYCLASS in check_tp(). SO_ATMQOS stores the supplied QoS after check_qos() succeeds, so accepting larger values leaves invalid traffic_class values in vcc->qos. That bad state later reaches pvc_info(), which indexes class_name[] with vcc->qos.{rx,tp}.traffic_class. Values above ATM_ANYCLASS cause an out-of-bounds read when /proc/net/atm/pvc is read. Tighten the existing QoS validation so invalid traffic_class values are rejected at the point where user supplied QoS is accepted. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Reviewed-by: Simon Horman Link: https://patch.msgid.link/58f02c6f73d9818fd5d2022e1116759fdde6116b.1780965530.git.zcliangcn@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/atm/common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/atm/common.c b/net/atm/common.c index c4edc1111bf01..f4c6e0131c921 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -720,6 +720,8 @@ static int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos) static int check_tp(const struct atm_trafprm *tp) { /* @@@ Should be merged with adjust_tp */ + if (tp->traffic_class > ATM_ANYCLASS) + return -EINVAL; if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS) return 0; if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr && From eebf439aa7a1369cb14f355584b4ffb03988cd71 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 00:00:13 +0800 Subject: [PATCH 1720/3297] octeontx2-pf: clear stale mailbox IRQ state before request_irq() commit f918554fb7246e89b98ef90abe80801f038258b3 upstream. otx2_register_mbox_intr() currently installs the PF mailbox IRQ handler before clearing stale mailbox interrupt state. The function itself then comments that the local interrupt bits must be cleared first to avoid spurious interrupts, but that clear happens only after request_irq() has already exposed the handler to irq delivery. A running system can reach this during PF mailbox interrupt registration while stale or latched RVU_PF_INT state is still present. If delivery happens in the request_irq()-to-clear window, otx2_pfaf_mbox_intr_handler() can run before local quiesce and touch the same pf->mbox and pf->mbox_wq carrier that probe and teardown later reuse or destroy. Move the stale mailbox interrupt clear ahead of request_irq(), but keep interrupt enabling after the handler is installed. This closes the pre-clear early-IRQ window without creating a new enable-before-handler window. Fixes: 5a6d7c9daef3 ("octeontx2-pf: Mailbox communication with AF") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Simon Horman Reviewed-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260611160014.3202224-2-runyu.xiao@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index e30fac4263c76..21bb47e9ba61c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1119,9 +1119,16 @@ int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) { struct otx2_hw *hw = &pf->hw; struct msg_req *req; + u64 mbox_int_mask; char *irq_name; int err; + mbox_int_mask = !is_cn20k(pf->pdev) ? BIT_ULL(0) : + BIT_ULL(0) | BIT_ULL(1); + + /* Clear stale mailbox interrupt state before installing the handler. */ + otx2_write64(pf, RVU_PF_INT, mbox_int_mask); + /* Register mailbox interrupt handler */ if (!is_cn20k(pf->pdev)) { irq_name = &hw->irq_name[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE]; @@ -1147,17 +1154,8 @@ int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) return err; } - /* Enable mailbox interrupt for msgs coming from AF. - * First clear to avoid spurious interrupts, if any. - */ - if (!is_cn20k(pf->pdev)) { - otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); - otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0)); - } else { - otx2_write64(pf, RVU_PF_INT, BIT_ULL(0) | BIT_ULL(1)); - otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0) | - BIT_ULL(1)); - } + /* Enable mailbox interrupt for msgs coming from AF. */ + otx2_write64(pf, RVU_PF_INT_ENA_W1S, mbox_int_mask); if (!probe_af) return 0; From 908391d801b24dc242026c7ccf37185a3d443d36 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 00:00:14 +0800 Subject: [PATCH 1721/3297] octeontx2-vf: clear stale mailbox IRQ state before request_irq() commit 0b352f04b9be2c83c0240aa6dae7257fefa90464 upstream. otx2vf_register_mbox_intr() currently installs the VF mailbox IRQ handler before clearing stale mailbox interrupt state. The code then says that local interrupt bits should be cleared first to avoid spurious interrupts, but that clear still happens only after request_irq() has already made the handler reachable. A running system can reach this during VF mailbox interrupt registration while stale or latched RVU_VF_INT state is still present. If delivery happens in the request_irq()-to-clear window, otx2vf_vfaf_mbox_intr_handler() can run before local quiesce and touch the same vf->mbox and vf->mbox_wq carrier that probe and teardown later reuse or destroy. Move the stale mailbox interrupt clear ahead of request_irq(), but keep interrupt enabling after the handler is installed. This closes the pre-clear early-IRQ window without creating a new enable-before-handler window. Fixes: 3184fb5ba96e ("octeontx2-vf: Virtual function driver support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Reviewed-by: Simon Horman Reviewed-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260611160014.3202224-3-runyu.xiao@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../ethernet/marvell/octeontx2/nic/otx2_vf.c | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c index 25381f079b97d..5534c2c8db0fb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c @@ -251,9 +251,17 @@ static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf) { struct otx2_hw *hw = &vf->hw; struct msg_req *req; + u64 mbox_int_mask; char *irq_name; int err; + mbox_int_mask = !is_cn20k(vf->pdev) ? BIT_ULL(0) : + BIT_ULL(0) | BIT_ULL(1) | + BIT_ULL(2) | BIT_ULL(3); + + /* Clear stale mailbox interrupt state before installing the handler. */ + otx2_write64(vf, RVU_VF_INT, mbox_int_mask); + /* Register mailbox interrupt handler */ irq_name = &hw->irq_name[RVU_VF_INT_VEC_MBOX * NAME_SIZE]; snprintf(irq_name, NAME_SIZE, "RVUVF%d AFVF Mbox", ((vf->pcifunc & @@ -274,18 +282,8 @@ static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf) return err; } - /* Enable mailbox interrupt for msgs coming from PF. - * First clear to avoid spurious interrupts, if any. - */ - if (!is_cn20k(vf->pdev)) { - otx2_write64(vf, RVU_VF_INT, BIT_ULL(0)); - otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0)); - } else { - otx2_write64(vf, RVU_VF_INT, BIT_ULL(0) | BIT_ULL(1) | - BIT_ULL(2) | BIT_ULL(3)); - otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0) | - BIT_ULL(1) | BIT_ULL(2) | BIT_ULL(3)); - } + /* Enable mailbox interrupt for msgs coming from PF. */ + otx2_write64(vf, RVU_VF_INT_ENA_W1S, mbox_int_mask); if (!probe_pf) return 0; From 5526d1997aea6c9bd865ca4d4894b52e799d735c Mon Sep 17 00:00:00 2001 From: Yong Wang Date: Thu, 11 Jun 2026 02:37:43 +0800 Subject: [PATCH 1722/3297] net: ife: require ETH_HLEN to be pullable in ife_decode() commit 9406f6012b7343661efb516a11c62d4db2b62f75 upstream. ife decode may return after making only the outer IFE header and metadata pullable. The caller then passes the decapsulated packet to eth_type_trans(), which expects the inner Ethernet header to be accessible from the linear data area. With a malformed IFE frame, the inner Ethernet header may still be shorter than ETH_HLEN in the linear area, which can lead to a crash in the original code. Fix this by extending the pull check in ife_decode() so that the inner Ethernet header is also guaranteed to be pullable before returning. Fixes: ef6980b6becb ("introduce IFE action") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Signed-off-by: Yong Wang Signed-off-by: Ren Wei Link: https://patch.msgid.link/20260610183814.1648888-2-n05ec@lzu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ife/ife.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ife/ife.c b/net/ife/ife.c index be05b690b9ef2..7a75947a31e31 100644 --- a/net/ife/ife.c +++ b/net/ife/ife.c @@ -79,7 +79,7 @@ void *ife_decode(struct sk_buff *skb, u16 *metalen) if (unlikely(ifehdrln < 2)) return NULL; - if (unlikely(!pskb_may_pull(skb, total_pull))) + if (unlikely(!pskb_may_pull(skb, total_pull + ETH_HLEN))) return NULL; ifehdr = (struct ifeheadr *)(skb->data + skb->dev->hard_header_len); From 89edae416141265b169378a8f434c752176749fa Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 3 Jun 2026 12:06:11 +0100 Subject: [PATCH 1723/3297] arm64: fpsimd: Fix type mismatch in sve_{save,load}_state() commit ae24f6b06e90681ec36b9c21c3f5c09618350f5a upstream. The sve_save_state() and sve_load_state() functions take a 32-bit int argument that describes whether to save/restore the FFR. Their assembly implementations consume the entire 64-bit register containing this 32-bit value, and will attempt to save/restore the FFR if any bit of that 64-bit register is non-zero. Per the AAPCS64 parameter passing rules, the callee is responsible for any necessary widening, and the upper 32-bits are permitted to contain arbitrary values. If the upper 32 bits are non-zero, this could result in an unexpected attempt to save/restore the FFR, and consequently could lead to unexpected traps/undefs/faults. In practice compilers are very unlikely to generate code where the upper 32-bits would be non-zero, but they are permitted to do so. Fix this by only consuming the low 32 bits of the register, and update comments accordingly. The hyp code __sve_save_state() and __sve_restore_state() functions don't have the same latent bug as they override the full 64-bit register containing the argument. Fixes: 9f5848665788 ("arm64/sve: Make access to FFR optional") Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Fuad Tabba Cc: James Morse Cc: Marc Zyngier Cc: Mark Brown Cc: Oliver Upton Cc: Vladimir Murzin Cc: Will Deacon Cc: stable@vger.kernel.org Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry-fpsimd.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S index fb441702495fa..28af4bcd97a50 100644 --- a/arch/arm64/kernel/entry-fpsimd.S +++ b/arch/arm64/kernel/entry-fpsimd.S @@ -38,10 +38,10 @@ SYM_FUNC_END(fpsimd_load_state) * * x0 - pointer to buffer for state * x1 - pointer to storage for FPSR - * x2 - Save FFR if non-zero + * w2 - Save FFR if non-zero */ SYM_FUNC_START(sve_save_state) - sve_save 0, x1, x2, 3 + sve_save 0, x1, w2, 3 ret SYM_FUNC_END(sve_save_state) @@ -50,10 +50,10 @@ SYM_FUNC_END(sve_save_state) * * x0 - pointer to buffer for state * x1 - pointer to storage for FPSR - * x2 - Restore FFR if non-zero + * w2 - Restore FFR if non-zero */ SYM_FUNC_START(sve_load_state) - sve_load 0, x1, x2, 4 + sve_load 0, x1, w2, 4 ret SYM_FUNC_END(sve_load_state) From e8dc96a42571e7a09795ed59fc6f6269e88dc93a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 28 May 2026 14:03:24 +0200 Subject: [PATCH 1724/3297] arm64: dts: s32g3: Fix SWT8 watchdog address commit 65210e81f7837a871a17237d15e4b1191d5e8771 upstream. Add missing hex annotation to fix the SWT8 watchdog address in 'reg' property, as reported by dtc W=1: s32g3.dtsi:863.27-869.5: Warning (simple_bus_reg): /soc@0/watchdog@40500000: simple-bus unit address format error, expected "269fb20" Lack of hex '0x' meant address would be interpreted as decimal thus completely different value used as this device MMIO. If device was enabled this could lead to corruption of other device address space and broken boot. Cc: stable@vger.kernel.org Fixes: 6db84f042745 ("arm64: dts: s32g3: Add the Software Timer Watchdog (SWT) nodes") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Daniel Lezcano Signed-off-by: Frank Li Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/freescale/s32g3.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/s32g3.dtsi b/arch/arm64/boot/dts/freescale/s32g3.dtsi index be3a582ebc1bf..2698b826e6c6e 100644 --- a/arch/arm64/boot/dts/freescale/s32g3.dtsi +++ b/arch/arm64/boot/dts/freescale/s32g3.dtsi @@ -806,7 +806,7 @@ swt8: watchdog@40500000 { compatible = "nxp,s32g3-swt", "nxp,s32g2-swt"; - reg = <40500000 0x1000>; + reg = <0x40500000 0x1000>; clocks = <&clks 0x3a>, <&clks 0x3b>, <&clks 0x3b>; clock-names = "counter", "module", "register"; status = "disabled"; From 508e55e81870d444078dcd3a6cbc9c27b13062fd Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 5 Mar 2026 13:06:16 -0500 Subject: [PATCH 1725/3297] ARM: dts: imx6ul-var-som: fix warning for non-existent dc-supply property commit 0372cc5776e7fd5570884aed0c1e9a8a546cad66 upstream. The dc-supply property is non-existent in Linux now, nor when this DTS file was created when importing it from Variscite own kernel. Therefore remove it to fix this warning: imx6ul-var-som-concerto.dtb: cpu@0 (arm,cortex-a7): Unevaluated properties are not allowed ('dc-supply' was unexpected) from schema $id: http://devicetree.org/schemas/arm/cpus.yaml Fixes: 9d6a67d9c7a9 ("ARM: dts: imx6ul: Add Variscite VAR-SOM-MX6UL SoM support") Cc: stable@kernel.org Signed-off-by: Hugo Villeneuve Signed-off-by: Frank Li Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi index 4e536e0252def..3fb5e2fb68777 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-var-som.dtsi @@ -45,10 +45,6 @@ assigned-clock-rates = <786432000>; }; -&cpu0 { - dc-supply = <®_gpio_dvfs>; -}; - &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet1>, <&pinctrl_enet1_gpio>, <&pinctrl_enet1_mdio>; From e2e3fb995175c34136a10ae3b8bc277a5ba55d6c Mon Sep 17 00:00:00 2001 From: Nickolay Goppen Date: Wed, 29 Apr 2026 12:30:11 +0300 Subject: [PATCH 1726/3297] arm64: dts: qcom: sdm630: describe adsp_mem region properly commit ce414263e9ebe5080381a50cbdf9065c29816202 upstream. Downstream [1] this region is marked as shared, reusable and dynamic so describe it that way. [1]: https://github.com/xiaomi-sdm660/android_kernel_xiaomi_sdm660/blob/11-EAS/arch/arm/boot/dts/qcom/sdm660.dtsi#L448 Fixes: b190fb010664 ("arm64: dts: qcom: sdm630: Add sdm630 dts file") Cc: stable@vger.kernel.org Reviewed-by: Ekansh Gupta Reviewed-by: Dmitry Baryshkov Signed-off-by: Nickolay Goppen Link: https://lore.kernel.org/r/20260429-qcom-sdm660-cdsp-adsp-fastrpc-dts-fix-v5-4-16bc82e622ad@mainlining.org Signed-off-by: Bjorn Andersson Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/sdm630.dtsi | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index 876a6871745cf..1f2c8a4dcecf7 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -494,9 +494,12 @@ no-map; }; - adsp_mem: adsp-region@f6000000 { - reg = <0x0 0xf6000000 0x0 0x800000>; - no-map; + adsp_mem: adsp-region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x80000000 0x0 0x80000000>; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; + reusable; }; qseecom_mem: qseecom-region@f6800000 { From 68f9773754f05a533a36493986b809d9d33c4a41 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 21 Apr 2026 11:45:06 +0200 Subject: [PATCH 1727/3297] arm64: dts: rockchip: fix Ethernet PHY not found on PX30 Ringneck commit ae653cb854f36d1555681ce70ca3d80d0ec73516 upstream. When not passing the PHY ID with an ethernet-phy-idX.Y compatible property, the MDIO bus will attempt to auto-detect the PHY by reading its registers and then probing the appropriate driver. For this to work, the PHY needs to be in a working state. Unfortunately, the net subsystem doesn't control the PHY reset GPIO when attempting to auto-detect the PHY. This means the PHY needs to be in a working state when entering the Linux kernel. This historically has been the case for this device, but only because the bootloader was taking care of initializing the Ethernet controller even when not using it. We're attempting to support the removal of the network stack in the bootloader, which means the Linux kernel will be entered with the PHY still in reset and now Ethernet doesn't work anymore. The devices in the field only ever had a TI DP83825, so let's simply bypass the auto-detection mechanism entirely by passing the appropriate PHY IDs via the compatible. Note that this is only an issue since commit e463625af7f9 ("arm64: dts: rockchip: move reset to dedicated eth-phy node on ringneck") as before that commit the reset was done by the MAC controller before starting the MDIO auto-detection mechanism, via the snps,reset-* properties. Cc: stable@vger.kernel.org Fixes: e463625af7f9 ("arm64: dts: rockchip: move reset to dedicated eth-phy node on ringneck") Signed-off-by: Quentin Schulz Link: https://patch.msgid.link/20260421-px30-eth-phy-v2-2-68c375b120fd@cherry.de Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi index 4203b335a2633..973b4c5880e24 100644 --- a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi @@ -344,7 +344,7 @@ &mdio { dp83825: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id2000.a140"; reg = <0x0>; pinctrl-names = "default"; pinctrl-0 = <&phy_rst>; From 4fd52ac541ce1f2a2212b98320b60e911d9bbe71 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:18 +0100 Subject: [PATCH 1728/3297] ARM: dts: stm32: stm32mp15x-mecio1-io: Fix ADC sampling times commit 8407e611faf80ce790a393addf7b44cc595742af upstream. Increase the minimum ADC sample times for all configured channels on ADC1 and ADC2 to ensure measurement accuracy meets specifications. The default 5us sample time is insufficient for the internal sampling capacitor to fully charge. Increase the default time to 20us to relax the input impedance requirements. Additionally, the phint0_ain and phint1_ain channels require a much longer sampling period due to their specific circuit design. Increase their sample times to 200us. Remove stale comments regarding clock cycles that no longer match the updated timings. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-3-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- .../arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index 915ba2526f451..fe7880a34e684 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -104,80 +104,79 @@ channel@0 { reg = <0>; - /* 16.5 ck_cycles sampling time */ - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "p24v_stp"; }; channel@1 { reg = <1>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "p24v_hpdcm"; }; channel@2 { reg = <2>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain0"; }; channel@3 { reg = <3>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm1_i2"; }; channel@5 { reg = <5>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpout1_i"; }; channel@6 { reg = <6>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain1"; }; channel@9 { reg = <9>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpout0_i"; }; channel@10 { reg = <10>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <200000>; label = "phint0_ain"; }; channel@13 { reg = <13>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <200000>; label = "phint1_ain"; }; channel@15 { reg = <15>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm0_i1"; }; channel@16 { reg = <16>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "lsin"; }; channel@18 { reg = <18>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm0_i2"; }; channel@19 { reg = <19>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "hpdcm1_i1"; }; }; @@ -187,14 +186,13 @@ channel@2 { reg = <2>; - /* 16.5 ck_cycles sampling time */ - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain2"; }; channel@6 { reg = <6>; - st,min-sample-time-ns = <5000>; + st,min-sample-time-ns = <20000>; label = "ain3"; }; }; From a98bda2305f3f66a993d835e9e948d92734c1b50 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:19 +0100 Subject: [PATCH 1729/3297] ARM: dts: stm32: stm32mp15x-mecio1-io: Move divergent mecio1 ADC channels to board files commit 70f1d8fcbd121a40f51b6c846d41e8cbb38ba210 upstream. Move the divergent adc1 channel definitions out of the common mecio1-io.dtsi file and into the specific Revision 0 and Revision 1 board files. The original common file contained incorrect schematic labels for the Revision 0 hardware (e.g., labeling ana0 as p24v_hpdcm instead of ain_aux0) and failed to account for physical signal routing changes between the board revisions. Retain only the strictly shared channels in the common include file. Map the correct channels and schematic labels directly within stm32mp151c-mecio1r0.dts and stm32mp153c-mecio1r1.dts. Crucially, ensure that the required 200us sample time follows the phint1_ain signal to its new physical location on channel 3 for the Revision 1 hardware. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-4-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts | 50 +++++++++++++++++++ arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts | 50 +++++++++++++++++++ .../arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 50 +------------------ 3 files changed, 101 insertions(+), 49 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts index a5ea1431c3991..4e795ad42928b 100644 --- a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts +++ b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts @@ -36,6 +36,56 @@ }; }; +&adc1 { + channel@0 { + reg = <0>; + st,min-sample-time-ns = <20000>; + label = "ain_aux0"; + }; + + channel@1 { + reg = <1>; + st,min-sample-time-ns = <20000>; + label = "ain_aux1"; + }; + + channel@3 { + reg = <3>; + st,min-sample-time-ns = <20000>; + label = "hpdcm1_i2"; + }; + + channel@5 { + reg = <5>; + st,min-sample-time-ns = <20000>; + label = "pout1_i"; + }; + + channel@9 { + reg = <9>; + st,min-sample-time-ns = <20000>; + label = "pout0_i"; + }; + + channel@13 { + reg = <13>; + st,min-sample-time-ns = <200000>; + label = "phint1_ain"; + }; + + channel@15 { + reg = <15>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i1"; + }; + + channel@18 { + reg = <18>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i2"; + }; +}; + &clk_hse { clock-frequency = <25000000>; }; diff --git a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts index 16b814c19350c..d32816093e47d 100644 --- a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts +++ b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts @@ -36,6 +36,56 @@ }; }; +&adc1 { + channel@0 { + reg = <0>; + st,min-sample-time-ns = <20000>; + label = "p24v_hpdcm"; + }; + + channel@1 { + reg = <1>; + st,min-sample-time-ns = <20000>; + label = "p24v_stp"; + }; + + channel@3 { + reg = <3>; + st,min-sample-time-ns = <200000>; + label = "phint1_ain"; + }; + + channel@5 { + reg = <5>; + st,min-sample-time-ns = <20000>; + label = "hpout1_i"; + }; + + channel@9 { + reg = <9>; + st,min-sample-time-ns = <20000>; + label = "hpout0_i"; + }; + + channel@13 { + reg = <13>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i2"; + }; + + channel@15 { + reg = <15>; + st,min-sample-time-ns = <20000>; + label = "hpdcm1_i2"; + }; + + channel@18 { + reg = <18>; + st,min-sample-time-ns = <20000>; + label = "hpdcm0_i1"; + }; +}; + &clk_hse { clock-frequency = <24000000>; }; diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index fe7880a34e684..f1d8e4324a49c 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -90,7 +90,7 @@ }; &adc { - /* ANA0, ANA1 are dedicated pins and don't need pinctrl: only in6. */ + /* ANA0, ANA1 are dedicated pins and don't need pinctrl. */ pinctrl-0 = <&adc12_pins_mecsbc>; pinctrl-names = "default"; vdd-supply = <&v3v3>; @@ -102,78 +102,30 @@ &adc1 { status = "okay"; - channel@0 { - reg = <0>; - st,min-sample-time-ns = <20000>; - label = "p24v_stp"; - }; - - channel@1 { - reg = <1>; - st,min-sample-time-ns = <20000>; - label = "p24v_hpdcm"; - }; - channel@2 { reg = <2>; st,min-sample-time-ns = <20000>; label = "ain0"; }; - channel@3 { - reg = <3>; - st,min-sample-time-ns = <20000>; - label = "hpdcm1_i2"; - }; - - channel@5 { - reg = <5>; - st,min-sample-time-ns = <20000>; - label = "hpout1_i"; - }; - channel@6 { reg = <6>; st,min-sample-time-ns = <20000>; label = "ain1"; }; - channel@9 { - reg = <9>; - st,min-sample-time-ns = <20000>; - label = "hpout0_i"; - }; - channel@10 { reg = <10>; st,min-sample-time-ns = <200000>; label = "phint0_ain"; }; - channel@13 { - reg = <13>; - st,min-sample-time-ns = <200000>; - label = "phint1_ain"; - }; - - channel@15 { - reg = <15>; - st,min-sample-time-ns = <20000>; - label = "hpdcm0_i1"; - }; - channel@16 { reg = <16>; st,min-sample-time-ns = <20000>; label = "lsin"; }; - channel@18 { - reg = <18>; - st,min-sample-time-ns = <20000>; - label = "hpdcm0_i2"; - }; - channel@19 { reg = <19>; st,min-sample-time-ns = <20000>; From ead9f10428c7342e5524bf6d8f7057fe048fe6c1 Mon Sep 17 00:00:00 2001 From: Judith Mendez Date: Tue, 24 Mar 2026 09:02:47 -0500 Subject: [PATCH 1730/3297] arm64: dts: ti: k3-am62a7-sk: Add bootph-all tag to vqmmc commit d8fe8442366ffd8306575028bda92389d0dfb674 upstream. Add bootph-all property to vqmmc voltage regulator node and its corresponding pinmux node to make it available during all boot phases. This allows to run tuning early in SPL stages of boot. Fixes: 8f023012eb4a ("arm64: dts: ti: k3-am62a: Enable UHS mode support for SD cards") Cc: stable@vger.kernel.org Signed-off-by: Judith Mendez Link: https://patch.msgid.link/20260324140247.1200631-1-jm@ti.com Signed-off-by: Nishanth Menon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/ti/k3-am62a7-sk.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts index b41d8dd159ad1..a902b88062093 100644 --- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts @@ -154,6 +154,7 @@ gpios = <&main_gpio0 31 GPIO_ACTIVE_HIGH>; states = <1800000 0x0>, <3300000 0x1>; + bootph-all; }; leds { @@ -396,6 +397,7 @@ pinctrl-single,pins = < AM62AX_IOPAD(0x07c, PIN_OUTPUT, 7) /* (N22) GPMC0_CLK.GPIO0_31 */ >; + bootph-all; }; main_ecap0_pins_default: main-ecap0-default-pins { From bd938c985ab345d52177eb1a268d0d64ba0c8bae Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:17 +0100 Subject: [PATCH 1731/3297] ARM: dts: stm32: stm32mp15x-mecio1-io: Enable internal ADC reference commit c84f22405085d91cd5f0c5b967318371c07904ba upstream. Switch the ADC reference supply from the general 3.3V rail to the internal 2.5V VREFBUF regulator. The ADC circuits on this board are designed for the internal 2.5V reference. Without this change, all ADC measurement values are incorrect. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-2-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index f1d8e4324a49c..f91b3d1f037b5 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -95,7 +95,7 @@ pinctrl-names = "default"; vdd-supply = <&v3v3>; vdda-supply = <&v3v3>; - vref-supply = <&v3v3>; + vref-supply = <&vrefbuf>; status = "okay"; }; @@ -362,6 +362,13 @@ phy-supply = <&v3v3>; }; +&vrefbuf { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + vdda-supply = <&v3v3>; + status = "okay"; +}; + &pinctrl { adc12_pins_mecsbc: adc12-ain-mecsbc-0 { pins { From c632a27f35cff488310a40461c6b138029a4256c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Apr 2026 11:07:24 +0200 Subject: [PATCH 1732/3297] arm64: dts: imx8ulp-evk: Correct Type-C int GPIO flags commit b4f5c46163b3fe3ec7ed8a76dff8b7e80a776a1f upstream. IRQ_TYPE_xxx flags are not correct in the context of GPIO flags. These are simple defines so they could be used in DTS but they will not have the same meaning: IRQ_TYPE_EDGE_FALLING = 2 = GPIO_SINGLE_ENDED. Correct the Type-C int-gpios to use proper flags, assuming the author of the code wanted similar logical behavior: IRQ_TYPE_EDGE_FALLING => GPIO_ACTIVE_LOW Fixes: c4b4593ecb0b ("arm64: dts: imx8ulp-evk: enable usb nodes and add ptn5150 nodes") Cc: stable@vger.kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Frank Li Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/freescale/imx8ulp-evk.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts b/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts index 290a49bea2f76..5dea66c1e7aa0 100644 --- a/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8ulp-evk.dts @@ -166,7 +166,7 @@ ptn5150_1: typec@1d { compatible = "nxp,ptn5150"; reg = <0x1d>; - int-gpios = <&gpiof 3 IRQ_TYPE_EDGE_FALLING>; + int-gpios = <&gpiof 3 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_typec1>; status = "disabled"; @@ -182,7 +182,7 @@ ptn5150_2: typec@3d { compatible = "nxp,ptn5150"; reg = <0x3d>; - int-gpios = <&gpiof 5 IRQ_TYPE_EDGE_FALLING>; + int-gpios = <&gpiof 5 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_typec2>; status = "disabled"; From 804821b69b2d164eef35d3dbb7eaebe354e9eeec Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:20 +0100 Subject: [PATCH 1733/3297] ARM: dts: stm32: stm32mp15x-mecio1-io: Fix GPIO names typo commit b04ccecb714de913e360f0866c66f38e1606e89b upstream. The reset pins for the LPOUT lines were incorrectly prefixed with "GPOUT" instead of "LPOUT" in the gpio-line-names array. Fix these typos so the pin names consistently match the LPOUT0-4 signals they belong to. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-5-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index f91b3d1f037b5..e50e9ae085e81 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -185,14 +185,14 @@ &gpioe { gpio-line-names = "HPOUT0_RESETN", "HPOUT1", "HPOUT1_ALERTN", "", "", "", "HPOUT1_RESETN", - "LPOUT0", "LPOUT0_ALERTN", "GPOUT0_RESETN", - "LPOUT1", "LPOUT1_ALERTN", "GPOUT1_RESETN", - "LPOUT2", "LPOUT2_ALERTN", "GPOUT2_RESETN"; + "LPOUT0", "LPOUT0_ALERTN", "LPOUT0_RESETN", + "LPOUT1", "LPOUT1_ALERTN", "LPOUT1_RESETN", + "LPOUT2", "LPOUT2_ALERTN", "LPOUT2_RESETN"; }; &gpiof { - gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "GPOUT3_RESETN", - "LPOUT4", "LPOUT4_ALERTN", "GPOUT4_RESETN", + gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "LPOUT3_RESETN", + "LPOUT4", "LPOUT4_ALERTN", "LPOUT4_RESETN", "", "", "", "", "", "", "", "", "", ""; From 7da4d1a6b74004d02adfa06a0b49495e12636903 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:21 +0100 Subject: [PATCH 1734/3297] ARM: dts: stm32: stm32mp15x-mecio1-io: Move gpio-line-names to board files commit 4f5069609ac99894c0632d8b8c4c016f85199de9 upstream. Move the gpio-line-names properties out of the common mecio1-io.dtsi file and into the specific board dts files. The pinout originally defined in the common include file belonged to the mecio1r0 (Revision 0) hardware. This is moved 1:1 into the stm32mp151c-mecio1r0.dts file without any modifications. A large number of GPIO pins are swapped on the mecio1r1 (Revision 1) hardware, so a new, board-specific gpio-line-names mapping is added to stm32mp153c-mecio1r1.dts to reflect those hardware changes. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20260318105123.819807-6-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts | 64 +++++++++++++++ arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts | 80 +++++++++++++++++++ .../arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 63 --------------- 3 files changed, 144 insertions(+), 63 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts index 4e795ad42928b..06ab774658162 100644 --- a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts +++ b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts @@ -96,3 +96,67 @@ assigned-clock-rates = <125000000>; /* Clock PLL3 to 625Mhz in tf-a. */ st,eth-clk-sel; }; + +&gpiod { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "", "", "", + "STP_RESETN", "STP_ENABLEN", "HPOUT0", "HPOUT0_ALERTN"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog_d_mecsbc>; +}; + +&gpioe { + gpio-line-names = "HPOUT0_RESETN", "HPOUT1", "HPOUT1_ALERTN", "", + "", "", "HPOUT1_RESETN", + "LPOUT0", "LPOUT0_ALERTN", "LPOUT0_RESETN", + "LPOUT1", "LPOUT1_ALERTN", "LPOUT1_RESETN", + "LPOUT2", "LPOUT2_ALERTN", "LPOUT2_RESETN"; +}; + +&gpiof { + gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "LPOUT3_RESETN", + "LPOUT4", "LPOUT4_ALERTN", "LPOUT4_RESETN", + "", "", + "", "", "", "", + "", "", "", ""; +}; + +&gpiog { + gpio-line-names = "LPOUT5", "LPOUT5_ALERTN", "", "LPOUT5_RESETN", + "", "", "", "", + "", "", "", "", + "", "", "", ""; +}; + +&gpioh { + gpio-line-names = "", "", "", "", + "", "", "", "", + "GPIO0_RESETN", "", "", "", + "", "", "", ""; +}; + +&gpioi { + gpio-line-names = "", "", "", "", + "", "", "", "", + "HPDCM0_SLEEPN", "HPDCM1_SLEEPN", "GPIO1_RESETN", "", + "", "", "", ""; +}; + +&gpioj { + gpio-line-names = "HSIN10", "HSIN11", "HSIN12", "HSIN13", + "HSIN14", "HSIN15", "", "", + "", "", "", "", + "", "RTD_RESETN", "", ""; +}; + +&gpiok { + gpio-line-names = "", "", "HSIN0", "HSIN1", + "HSIN2", "HSIN3", "HSIN4", "HSIN5"; +}; + +&gpioz { + gpio-line-names = "", "", "", "HSIN6", + "HSIN7", "HSIN8", "HSIN9", ""; +}; + diff --git a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts index d32816093e47d..2b3989303cd15 100644 --- a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts +++ b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts @@ -90,6 +90,86 @@ clock-frequency = <24000000>; }; +&gpioa { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "", "GPIO1_RESETN", "", + "", "", "", "LPOUT5"; +}; + +&gpiob { + gpio-line-names = "", "", "", "", + "LPOUT4_RESETN", "", "", "", + "", "LPOUT4_ALERTN", "", "", + "", "", "", ""; +}; + +&gpioc { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "LPOUT4", "", "", + "", "", "", ""; +}; + +&gpiod { + gpio-line-names = "LPOUT2", "", "LPOUT3_RESETN", "", + "LPOUT2_ALERTN", "", "MECIO_ADDR0", "", + "HPOUT1_ALERTN", "HPOUT1_RESETN", "", "", + "", "", "HPOUT0", "HPOUT1"; +}; + +&gpioe { + gpio-line-names = "LPOUT0_RESETN", "", "", "", + "", "LPOUT3", "LPOUT5_ALERTN", "", + "", "", "", "", + "", "", "", "HSIN_RESETN"; +}; + +&gpiof { + gpio-line-names = "LPOUT5_RESETN", "", "", "HPOUT0_ALERTN", + "", "LPOUT1", "", "", + "", "", "", "", + "", "", "", ""; +}; + +&gpiog { + gpio-line-names = "", "", "", "HPOUT0_RESETN", + "", "", "LPOUT3_ALERTN", "", + "", "", "GPIO0_RESETN", "", + "", "", "", "LPOUT2_RESETN"; +}; + +&gpioh { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "LPOUT0", "", "", + "", "LPOUT0_ALERTN", "STP_ENABLEN", "STP_RESETN"; +}; + +&gpioi { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "", "SPE_RESETN", "", + "HPDCM0_SLEEPN", "", "", ""; +}; + +&gpioj { + gpio-line-names = "", "", "", "", + "", "", "", "MECIO_ADDR1", + "", "", "", "", + "", "", "", "LPOUT1_RESETN"; +}; + +&gpiok { + gpio-line-names = "", "", "RTD_RESETN", "", + "", "LPOUT1_ALERTN", "", ""; +}; + +&gpioz { + gpio-line-names = "", "", "", "", + "HPDCM1_SLEEPN", "", "", ""; +}; + &m_can1 { pinctrl-names = "default", "sleep"; pinctrl-0 = <&m_can1_pins_b>; diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index e50e9ae085e81..69a502ec36d4d 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -173,69 +173,6 @@ }; }; -&gpiod { - gpio-line-names = "", "", "", "", - "", "", "", "", - "", "", "", "", - "STP_RESETN", "STP_ENABLEN", "HPOUT0", "HPOUT0_ALERTN"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog_d_mecsbc>; -}; - -&gpioe { - gpio-line-names = "HPOUT0_RESETN", "HPOUT1", "HPOUT1_ALERTN", "", - "", "", "HPOUT1_RESETN", - "LPOUT0", "LPOUT0_ALERTN", "LPOUT0_RESETN", - "LPOUT1", "LPOUT1_ALERTN", "LPOUT1_RESETN", - "LPOUT2", "LPOUT2_ALERTN", "LPOUT2_RESETN"; -}; - -&gpiof { - gpio-line-names = "LPOUT3", "LPOUT3_ALERTN", "LPOUT3_RESETN", - "LPOUT4", "LPOUT4_ALERTN", "LPOUT4_RESETN", - "", "", - "", "", "", "", - "", "", "", ""; -}; - -&gpiog { - gpio-line-names = "LPOUT5", "LPOUT5_ALERTN", "", "LPOUT5_RESETN", - "", "", "", "", - "", "", "", "", - "", "", "", ""; -}; - -&gpioh { - gpio-line-names = "", "", "", "", - "", "", "", "", - "GPIO0_RESETN", "", "", "", - "", "", "", ""; -}; - -&gpioi { - gpio-line-names = "", "", "", "", - "", "", "", "", - "HPDCM0_SLEEPN", "HPDCM1_SLEEPN", "GPIO1_RESETN", "", - "", "", "", ""; -}; - -&gpioj { - gpio-line-names = "HSIN10", "HSIN11", "HSIN12", "HSIN13", - "HSIN14", "HSIN15", "", "", - "", "", "", "", - "", "RTD_RESETN", "", ""; -}; - -&gpiok { - gpio-line-names = "", "", "HSIN0", "HSIN1", - "HSIN2", "HSIN3", "HSIN4", "HSIN5"; -}; - -&gpioz { - gpio-line-names = "", "", "", "HSIN6", - "HSIN7", "HSIN8", "HSIN9", ""; -}; - &i2c2 { pinctrl-names = "default"; pinctrl-0 = <&i2c2_pins_a>; From f550bf32b9a064bc0f60af9579c39586d3cfea81 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:22 +0100 Subject: [PATCH 1735/3297] ARM: dts: stm32: stm32mp15x-mecio1-io: Fix expander gpio line typo commit dfb93c4acce8ad9c4f573128b2cf7ddb936e0de7 upstream. Fix a copy-paste error in the GPIO line names for the TCA6416 expander (gpio@20). The common mecio1-io include file was originally defined using the mecio1r1 (Revision 1) hardware layout, but incorrectly labeled pin 13 as "HSIN9_BIAS" instead of the actual "HSIN7_BIAS" present in the schematics. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260318105123.819807-7-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index 69a502ec36d4d..1808289f8193b 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -186,7 +186,7 @@ #gpio-cells = <2>; gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", "", "", "HSIN_VREF0_LVL", "HSIN_VREF1_LVL", - "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN9_BIAS", + "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", "", "", "", ""; }; From 1ee200a1764f89ecc0f48620a3a3e2439c9565b9 Mon Sep 17 00:00:00 2001 From: David Jander Date: Wed, 18 Mar 2026 11:51:23 +0100 Subject: [PATCH 1736/3297] ARM: dts: stm32: stm32mp15x-mecio1-io: Move expander gpio-line-names to board files commit a0d6c2a06fffff47bcca4d5bfdab4cc428a315fc upstream. Move the gpio-line-names properties for the I2C GPIO expanders (gpio0 and gpio1) out of the common mecio1-io.dtsi file and into the specific board dts files. The layout originally defined in the common include file belonged to the mecio1r1 (Revision 1) hardware. This layout is moved 1:1 into the stm32mp153c-mecio1r1.dts file. The mecio1r0 (Revision 0) hardware utilizes a completely different pinout for these expanders. A new, accurate mapping reflecting the Revision 0 schematics is added to stm32mp151c-mecio1r0.dts. Fixes: 8267753c891c ("ARM: dts: stm32: Add MECIO1 and MECT1S board variants") Co-developed-by: Oleksij Rempel Signed-off-by: David Jander Signed-off-by: Oleksij Rempel Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260318105123.819807-8-o.rempel@pengutronix.de Signed-off-by: Alexandre Torgue Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts | 14 ++++++++++++++ arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts | 14 ++++++++++++++ arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi | 8 -------- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts index 06ab774658162..862782d20d10e 100644 --- a/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts +++ b/arch/arm/boot/dts/st/stm32mp151c-mecio1r0.dts @@ -97,6 +97,20 @@ st,eth-clk-sel; }; +&gpio0 { + gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", + "HSIN4_BIAS", "", "STP_VREF0_LVL", "HSIN_VREF0_LVL", + "STP0_FB_BIAS", "STP1_FB_BIAS", "STP2_FB_BIAS", "STP3_FB_BIAS", + "", "", "", ""; +}; + +&gpio1 { + gpio-line-names = "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", "HSIN8_BIAS", + "HSIN9_BIAS", "", "STP_VREF1_LVL", "HSIN_VREF1_LVL", + "STP4_FB_BIAS", "STP5_FB_BIAS", "STP6_FB_BIAS", "", + "", "", "LSIN8_BIAS", "LSIN9_BIAS"; +}; + &gpiod { gpio-line-names = "", "", "", "", "", "", "", "", diff --git a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts index 2b3989303cd15..739cc18c3d3af 100644 --- a/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts +++ b/arch/arm/boot/dts/st/stm32mp153c-mecio1r1.dts @@ -90,6 +90,20 @@ clock-frequency = <24000000>; }; +&gpio0 { + gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", + "", "", "HSIN_VREF0_LVL", "HSIN_VREF1_LVL", + "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", + "", "", "", ""; +}; + +&gpio1 { + gpio-line-names = "HSIN8_BIAS", "HSIN9_BIAS", "HSIN10_BIAS", "HSIN11_BIAS", + "", "", "HSIN_VREF2_LVL", "HSIN_VREF3_LVL", + "HSIN12_BIAS", "HSIN13_BIAS", "HSIN14_BIAS", "HSIN15_BIAS", + "", "", "LSIN8_BIAS", "LSIN9_BIAS"; +}; + &gpioa { gpio-line-names = "", "", "", "", "", "", "", "", diff --git a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi index 1808289f8193b..1a4f5a523eb33 100644 --- a/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15x-mecio1-io.dtsi @@ -184,10 +184,6 @@ reg = <0x20>; gpio-controller; #gpio-cells = <2>; - gpio-line-names = "HSIN0_BIAS", "HSIN1_BIAS", "HSIN2_BIAS", "HSIN3_BIAS", - "", "", "HSIN_VREF0_LVL", "HSIN_VREF1_LVL", - "HSIN4_BIAS", "HSIN5_BIAS", "HSIN6_BIAS", "HSIN7_BIAS", - "", "", "", ""; }; gpio1: gpio@21 { @@ -195,10 +191,6 @@ reg = <0x21>; gpio-controller; #gpio-cells = <2>; - gpio-line-names = "HSIN8_BIAS", "HSIN9_BIAS", "HSIN10_BIAS", "HSIN11_BIAS", - "", "", "HSIN_VREF2_LVL", "HSIN_VREF3_LVL", - "HSIN12_BIAS", "HSIN13_BIAS", "HSIN14_BIAS", "HSIN15_BIAS", - "", "", "LSIN8_BIAS", "LSIN9_BIAS"; }; }; From 199b570d7fca1aa70e596f10a6276997becffb6d Mon Sep 17 00:00:00 2001 From: Yanfei Xu Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 1737/3297] LoongArch: KVM: Validate irqchip index in irqfd routing commit 3474037904c20ff915e3ebab0ab5c1e41bbe549e upstream. Sashiko reported that the irqchip index is not validated for LoongArch. Add validation and reject out-of-range irqchip indexes to avoid indexing past the routing table's chip array. Cc: stable@vger.kernel.org Fixes: 1928254c5ccb ("LoongArch: KVM: Add irqfd support") Closes: https://lore.kernel.org/kvm/20260525051714.485D51F000E9@smtp.kernel.org/ Reported-by: Sashiko Reviewed-by: Bibo Mao Signed-off-by: Yanfei Xu Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/irqfd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/irqfd.c b/arch/loongarch/kvm/irqfd.c index 9a39627aecf07..cba3be5bad0ad 100644 --- a/arch/loongarch/kvm/irqfd.c +++ b/arch/loongarch/kvm/irqfd.c @@ -53,7 +53,8 @@ int kvm_set_routing_entry(struct kvm *kvm, e->irqchip.irqchip = ue->u.irqchip.irqchip; e->irqchip.pin = ue->u.irqchip.pin; - if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) + if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS || + e->irqchip.irqchip >= KVM_NR_IRQCHIPS) return -EINVAL; return 0; From efe27b19a15c384cad7c80de399f3107ab070e6d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 1738/3297] LoongArch: KVM: Check irq validity in kvm_vcpu_ioctl_interrupt() commit 09b318ab77b7a4fc9987fd98d1525fc55ddc2617 upstream. Function kvm_vcpu_ioctl_interrupt() can be called from userspace, here add irq validility cheking in kvm_vcpu_ioctl_interrupt(). Cc: stable@vger.kernel.org Fixes: f45ad5b8aa93 ("LoongArch: KVM: Implement vcpu interrupt operations") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/vcpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index a7510b403f526..8a5b594ffb04f 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1461,6 +1461,10 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu) int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) { int intr = (int)irq->irq; + unsigned int vector = abs(intr); + + if (vector >= EXCCODE_INT_NUM) + return -EINVAL; if (intr > 0) kvm_queue_irq(vcpu, intr); From 45f2e6505fcf63b0bda2490751210c78cfe5842b Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 1739/3297] LoongArch: KVM: Check the return values for put_user() commit fb89e0fe2dc4246c86ad0eb0fa2b7cb2f8ba9728 upstream. put_user() may return -EFAULT, so, when the user space address is invalid, the caller should return -EFAULT. Cc: stable@vger.kernel.org Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/vcpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 8a5b594ffb04f..0e2aa809c4b97 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1061,7 +1061,8 @@ static int kvm_loongarch_cpucfg_get_attr(struct kvm_vcpu *vcpu, return -ENXIO; } - put_user(val, uaddr); + if (put_user(val, uaddr)) + return -EFAULT; return ret; } From f3efcef6648ba9be40b0712c569ded7918e8c6ba Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 11 Jun 2026 20:46:40 +0800 Subject: [PATCH 1740/3297] LoongArch: KVM: Fix FPU register width with user access API commit f4caaac76379daf4a617d9134b6087fb2636fb31 upstream. At the beginning, only 64 bit FPU is supported. With FPU register get interface, 64 bit FPU data is copied to user space, the same with FPU register set API. However with LSX and LASX supported in later, there should be FPU data copied with bigger width. So here fixes this issue, copy the whole 256 bit FPU data from/to user space. Cc: stable@vger.kernel.org Fixes: db1ecca22edf ("LoongArch: KVM: Add LSX (128bit SIMD) support") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/vcpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 0e2aa809c4b97..4248a96322b63 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1266,7 +1266,7 @@ int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) fpu->fcc = vcpu->arch.fpu.fcc; fpu->fcsr = vcpu->arch.fpu.fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], FPU_REG_WIDTH / 64); + memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], sizeof(union fpureg)); return 0; } @@ -1278,7 +1278,7 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) vcpu->arch.fpu.fcc = fpu->fcc; vcpu->arch.fpu.fcsr = fpu->fcsr; for (i = 0; i < NUM_FPU_REGS; i++) - memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], FPU_REG_WIDTH / 64); + memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], sizeof(union fpureg)); return 0; } From 33d79ad6ecedf7baf4ac495716798b51a3d90b8e Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 11 Jun 2026 20:46:43 +0800 Subject: [PATCH 1741/3297] LoongArch: KVM: Return full old CSR value from kvm_emu_xchg_csr() commit ebd50de14f1a06b7e0206083904bcc62b9ba65be upstream. The LoongArch CSRXCHG instruction returns the full old CSR value in rd after applying the masked update. kvm_emu_xchg_csr() currently masks the saved value before returning it to the guest, so rd receives only the bits selected by the write mask. That breaks the architectural behavior and makes a zero mask return 0 instead of the previous CSR value. So, keep the masked CSR update, but return the unmodified old CSR value. Cc: stable@vger.kernel.org Fixes: da50f5a693ff ("LoongArch: KVM: Implement handle csr exception") Reviewed-by: Bibo Mao Signed-off-by: Qiang Ma Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kvm/exit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index 7c3258476432b..7a3828de2f1fb 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -103,7 +103,6 @@ static unsigned long kvm_emu_xchg_csr(struct kvm_vcpu *vcpu, int csrid, old = kvm_read_sw_gcsr(csr, csrid); val = (old & ~csr_mask) | (val & csr_mask); kvm_write_sw_gcsr(csr, csrid, val); - old = old & csr_mask; } else pr_warn_once("Unsupported csrxchg 0x%x with pc %lx\n", csrid, vcpu->arch.pc); From 9f8eaef40e955cd0aecd4e157af2a749611d6214 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Mon, 15 Jun 2026 14:41:19 +0530 Subject: [PATCH 1742/3297] powerpc/pseries/Kconfig: Enable CONFIG_VPA_PMU to be used with KVM commit fe179677b6dcb4b658586038a811f87265e97777 upstream. Currently, CONFIG_VPA_PMU is not enabled by default, and consequently cannot be used for KVM guests at all, unless explicitly enabled on host kernel. Mark CONFIG_VPA_PMU as "default m" to ensure it is available when KVM is being used. Cc: stable@vger.kernel.org # v6.13+ Suggested-by: Sean Christopherson Reviewed-by: Amit Machhiwal Reviewed-by: Harsh Prateek Bora Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Gautam Menghani [Maddy: Changed tag order] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260615091120.84169-1-gautam@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 3e042218d6cd8..adc59d78d3ad5 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -154,6 +154,7 @@ config HV_PERF_CTRS config VPA_PMU tristate "VPA PMU events" depends on KVM_BOOK3S_64_HV && HV_PERF_CTRS + default m help Enable access to the VPA PMU counters via perf. This enables code that support measurement for KVM on PowerVM(KoP) feature. From 5fb75c5272950b3ebe8bdee7abfdafd44f38313b Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 24 Jun 2026 14:19:10 +0800 Subject: [PATCH 1743/3297] KVM: s390: pci: Fix GISC refcount leak on AIF enable failure commit 7b69729046a4c58f4cb457184e5ac4aaa179bff4 upstream. kvm_s390_gisc_register() registers the guest ISC before pinning the guest interrupt forwarding pages and allocating the AISB bit. If any of the later setup steps fails, the function unwinds the pinned pages and other local state, but does not unregister the GISC reference. Add the missing kvm_s390_gisc_unregister() to the error unwind path. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Matthew Rosato Tested-by: Matthew Rosato Acked-by: Claudio Imbrenda Reviewed-by: Christian Borntraeger Signed-off-by: Claudio Imbrenda Message-ID: <20260624061910.2794734-1-haoxiang_li2024@163.com> Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 26cb4568732cd..983767d09c3fd 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -329,6 +329,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, unpin1: unpin_user_page(aibv_page); out: + kvm_s390_gisc_unregister(kvm, fib->fmt0.isc); return rc; } From 79fdd2aa774e44847cd9bb7edc811e73e3dc7bfe Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 5 Jun 2026 05:59:15 +0900 Subject: [PATCH 1744/3297] KVM: arm64: vgic: Check the interrupt is still ours before migrating it commit 0074b82cdfcb5fd13710a0ac308ade68ac6f6fbe upstream. vgic_prune_ap_list() drops both ap_list_lock and irq_lock while migrating an interrupt to another vCPU. After reacquiring the locks it only checks that the affinity is unchanged (target_vcpu == vgic_target_oracle(irq)) before moving the interrupt, which assumes that an interrupt whose affinity is preserved is still queued on this vCPU's ap_list. That assumption no longer holds if the interrupt is taken off the ap_list while the locks are dropped. vgic_flush_pending_lpis() removes the interrupt from the list and sets irq->vcpu to NULL, but leaves enabled/pending/target_vcpu untouched. As the interrupt is still enabled and pending, vgic_target_oracle() returns the same target_vcpu, so the affinity check passes and list_del() is run a second time on an entry that has already been removed. Also check that the interrupt is still assigned to this vCPU (irq->vcpu == vcpu) before moving it. Fixes: 0919e84c0fc1 ("KVM: arm/arm64: vgic-new: Add IRQ sync/flush framework") Signed-off-by: Hyunwoo Kim Link: https://patch.msgid.link/aiHnI1mu6SGQrgnz@v4bel Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 8d20c53faef02..246a9dd177199 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -735,15 +735,16 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) raw_spin_lock(&irq->irq_lock); /* - * If the affinity has been preserved, move the - * interrupt around. Otherwise, it means things have - * changed while the interrupt was unlocked, and we - * need to replay this. + * If the interrupt is still ours and its affinity has + * been preserved, move it around. Otherwise, it means + * things have changed while the interrupt was unlocked + * (it may even have been taken off the list with its + * affinity left untouched), and we need to replay this. * * In all cases, we cannot trust the list not to have * changed, so we restart from the beginning. */ - if (target_vcpu == vgic_target_oracle(irq)) { + if (irq->vcpu == vcpu && target_vcpu == vgic_target_oracle(irq)) { struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu; list_del(&irq->ap_list); From d19dca8194ebed371e624331c6be2cb73b562caf Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 15 Jun 2026 19:16:25 +0100 Subject: [PATCH 1745/3297] KVM: arm64: vgic: Handle race between interrupt affinity change and LPI disabling commit 7258770e5814f15e8308ebda82ac9acf6964ba8e upstream. Hyunwoo Kim reports some really bad races should the following situation occur: - LPI-I is pending in vcpu-B's AP list - vcpu-A writes to vcpu-B's RD to disable its LPIs - vcpu-C moves I from B to C If the last two race nicely enough, vgic_prune_ap_list() can drop the irq and AP list locks, reacquire them, and in the interval the irq has been freed. UAF follows. The fix is two-fold: - Before dropping the irq and ap_list locks, take a reference on the irq - Do not try to handle migration of the pending bit: there is no expectation that this state is retained, as per the architecture With that, we're sure that the interrupt is still around, and we safely remove it from the AP list as it has no target at this stage (unless another interrupt fires, but that's another story). Reported-by: Hyunwoo Kim Tested-by: Hyunwoo Kim Link: https://lore.kernel.org/r/ailsCnyoS82r_QRz@v4bel Link: https://patch.msgid.link/20260615181625.3029352-1-maz@kernel.org Fixes: 5dd4b924e390a ("KVM: arm/arm64: vgic: Add refcounting for IRQs") Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 246a9dd177199..5ba673289ef43 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -188,6 +188,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu) list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) { if (irq->intid >= VGIC_MIN_LPI) { raw_spin_lock(&irq->irq_lock); + irq->pending_latch = false; list_del(&irq->ap_list); irq->vcpu = NULL; raw_spin_unlock(&irq->irq_lock); @@ -712,7 +713,11 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) continue; } - /* This interrupt looks like it has to be migrated. */ + /* + * This interrupt looks like it has to be migrated, + * make sure it is kept alive while locks are dropped. + */ + vgic_get_irq_ref(irq); raw_spin_unlock(&irq->irq_lock); raw_spin_unlock(&vgic_cpu->ap_list_lock); @@ -757,6 +762,8 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) raw_spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock); raw_spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock); + deleted_lpis |= vgic_put_irq_norelease(vcpu->kvm, irq); + if (target_vcpu_needs_kick) { kvm_make_request(KVM_REQ_IRQ_PENDING, target_vcpu); kvm_vcpu_kick(target_vcpu); From df72596278b0e22dac5ef2881e9221a3a2c4ed11 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Thu, 9 Jul 2026 09:54:04 -0400 Subject: [PATCH 1746/3297] KVM: s390: pci: Fix handling of AIF enable without AISB commit 3e3aa6da87d30a0064a17b836685cd43c90a3572 upstream. When a guest seeks to register IRQs without a summary bit specified, ensure that the associated GAITE then stores 0 for the guest AISB location instead of virt_to_phys(page_address(NULL)). Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Farhan Ali Signed-off-by: Matthew Rosato Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 983767d09c3fd..20f738fa65569 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -301,9 +301,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, gaite->gisc = fib->fmt0.isc; gaite->count++; - gaite->aisbo = fib->fmt0.aisbo; - gaite->aisb = virt_to_phys(page_address(aisb_page) + (fib->fmt0.aisb & - ~PAGE_MASK)); + if (fib->fmt0.sum == 1) { + gaite->aisbo = fib->fmt0.aisbo; + gaite->aisb = virt_to_phys(page_address(aisb_page) + + (fib->fmt0.aisb & ~PAGE_MASK)); + } else { + gaite->aisbo = 0; + gaite->aisb = 0; + } aift->kzdev[zdev->aisb] = zdev->kzdev; spin_unlock_irq(&aift->gait_lock); From ba06690b28be950bd46d938d9b919c4024292627 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Jun 2026 15:36:32 -0700 Subject: [PATCH 1747/3297] KVM: SEV: Do not allow intra-host migration/mirroring of SNP VMs commit 6ee4140788234a6fabf59e6a50e38cdb936008cd upstream. The intra-host migration/mirroring feature is not fully implemented for SEV-SNP VMs. The proper migration requires additional SNP-specific state such as guest_req_mutex, guest_req_buf, and guest_resp_buf to be transferred or initialized on the destination. The SNP VM mirroring requires vmsa features to be copied as well otherwise ASID would be bound to SNP range while VM is detected as a SEV VM. Reject SNP source VMs in migration/mirroring until proper SNP state transfer is implemented. Fixes: 1dfe571c12cf ("KVM: SEV: Add initial SEV-SNP support") Reported-by: Chris Mason Reported-by: Sashiko Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Tom Lendacky Signed-off-by: Atish Patra Link: https://patch.msgid.link/20260602-sev_snp_fixes-v3-1-24bfd3ae047c@meta.com Cc: stable@vger.kernel.org [sean: let lines poke past 80 chars, tag for stable] Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 8b807f8eb0672..36b8488aba6b0 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2086,8 +2086,9 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) if (ret) return ret; + /* Do not allow SNP VM migration until additional state transfer is implemented */ if (kvm->arch.vm_type != source_kvm->arch.vm_type || - sev_guest(kvm) || !sev_guest(source_kvm)) { + sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm)) { ret = -EINVAL; goto out_unlock; } @@ -2831,8 +2832,9 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) * disallow out-of-band SEV/SEV-ES init if the target is already an * SEV guest, or if vCPUs have been created. KVM relies on vCPUs being * created after SEV/SEV-ES initialization, e.g. to init intercepts. + * Also do not allow SNP VM mirroring until additional state transfer is implemented. */ - if (sev_guest(kvm) || !sev_guest(source_kvm) || + if (sev_guest(kvm) || !sev_guest(source_kvm) || sev_snp_guest(source_kvm) || is_mirroring_enc_context(source_kvm) || kvm->created_vcpus) { ret = -EINVAL; goto e_unlock; From 97542f15dc4cf6cd3fdc035e482dca54246ddf48 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 24 Jun 2026 15:05:16 -0700 Subject: [PATCH 1748/3297] KVM: x86: Ignore pending PV EOI if the vCPU has since disabled PV EOIs commit 9285e4070df2c40585c3d7ec9571faa7a2b97e17 upstream. Ignore KVM's internal "service pending PV EOI" request if the vCPU has disabled PV EOIs since the request was made. Asserting that PV EOIs are enabled can fail if reading guest memory in pv_eoi_get_user() fails, i.e. if pv_eoi_test_and_clr_pending() bails early, *and* the vCPU also disables PV EOIs. kernel BUG at arch/x86/kvm/lapic.c:3338! Oops: invalid opcode: 0000 [#1] SMP CPU: 4 UID: 1000 PID: 890 Comm: pv_eoi_test Not tainted 7.0.0-d585aa5894d8-vm #337 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:kvm_lapic_sync_from_vapic+0x12b/0x140 [kvm] Call Trace: kvm_arch_vcpu_ioctl_run+0x1075/0x1c30 [kvm] kvm_vcpu_ioctl+0x2d5/0x980 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb5/0xb40 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Modules linked in: kvm_intel kvm irqbypass ---[ end trace 0000000000000000 ]--- Fixes: ae7a2a3fb6f8 ("KVM: host side for eoi optimization") Cc: stable@vger.kernel.org Reviewed-by: Kai Huang Link: https://patch.msgid.link/20260624220516.3033391-1-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 9448d34af51ae..f895019a55e57 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -3379,6 +3379,12 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu, struct kvm_lapic *apic) { int vector; + + if (unlikely(!pv_eoi_enabled(vcpu))) { + __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention); + return; + } + /* * PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host * and KVM_PV_EOI_ENABLED in guest memory as follows: @@ -3390,8 +3396,6 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu, * KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset: * -> host enabled PV EOI, guest executed EOI. */ - BUG_ON(!pv_eoi_enabled(vcpu)); - if (pv_eoi_test_and_clr_pending(vcpu)) return; vector = apic_set_eoi(apic); From d1379888cc4230bac647ec24ab83306afbd03e88 Mon Sep 17 00:00:00 2001 From: leixiang Date: Mon, 22 Jun 2026 15:51:01 +0800 Subject: [PATCH 1749/3297] KVM: x86: Nullify irqfd->producer if updating IRTE for bypass fails commit ed446e8aa894883c08892cfee69782fdf8f6c3ca upstream. Nullify irqfd->producer if updating the IRTE for bypass fails, as leaving a dangling pointer will result in a use-after-free if the irqfd is reachable through KVM's routing, but the producer is freed separately. E.g. for VFIO PCI, the producer is embedded in struct "vfio_pci_irq_ctx" and freed when the vector is disabled, which can happen independent of routing updates. Fixes: 77e1b8332d1d ("KVM: x86: Decouple device assignment from IRQ bypass") Cc: stable@vger.kernel.org Signed-off-by: leixiang Link: https://patch.msgid.link/1782119051448443.14545.seg@mailgw.kylinos.cn [sean: drop PPC change, massage changelog] Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/irq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index 7cc8950005b6e..b40eb5d3d6a59 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -488,8 +488,10 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons, if (irqfd->irq_entry.type == KVM_IRQ_ROUTING_MSI) { ret = kvm_pi_update_irte(irqfd, &irqfd->irq_entry); - if (ret) + if (ret) { kvm->arch.nr_possible_bypass_irqs--; + irqfd->producer = NULL; + } } spin_unlock_irq(&kvm->irqfds.lock); From 7996013b85687034d2e820cef94d6404192e3a3d Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 14 Jul 2026 09:03:06 -0700 Subject: [PATCH 1750/3297] KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to invalid guest state commit 2f2312c422fd2695da772cecb30c69994b795964 upstream. Put all vmcs12 pages if KVM synthesizes a nested VM-Exit due to invalid guest while emulating VMLAUNCH or VMRESUME. The invalid guest state path doesn't use nested_vmx_vmexit() as that API is intended to be used if and only if L2 is active, and the open coded equivalent neglects to put the vmcs12 pages. Failure to put the vmcs12 pages leaks any pinned pages (and/or mappings) if L1 retries VMLAUNCH/VMRESUME. Note, the !from_vmenter scenario doesn't suffer the same problem, as vmx_get_nested_state_pages() only gets/pins/maps the vmcs12 pages if L2 is active, i.e. if a "full" VM-Exit is guaranteed before KVM will retry getting vmcs12 pages. Fixes: 96c66e87deee ("KVM/nVMX: Use kvm_vcpu_map when mapping the virtual APIC page") Fixes: 3278e0492554 ("KVM/nVMX: Use kvm_vcpu_map when mapping the posted interrupt descriptor table") Fixes: fe1911aa443e ("KVM: nVMX: Use kvm_vcpu_map() to get/pin vmcs12's APIC-access page") Reported-by: Minh Nguyen Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/nested.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 36574b6b637d7..a5a525ae8e783 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -3799,6 +3799,8 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, if (!from_vmentry) return NVMX_VMENTRY_VMEXIT; + nested_put_vmcs12_pages(vcpu); + load_vmcs12_host_state(vcpu, vmcs12); vmcs12->vm_exit_reason = exit_reason.full; if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx)) From 7099e7148f81c605bbc319b16ce0131540341560 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sat, 27 Jun 2026 11:51:05 +0100 Subject: [PATCH 1751/3297] KVM: Move kvm_io_bus_get_dev() locking responsibilities to callers commit 3a07249981629ace483ebbef81ef6b34c2d2afec upstream. kvm_io_bus_get_dev() returns a device that is only matched by the address, and nothing else. This can cause a lifetime issue if the matched device is not the expected type, as by the time the caller can introspect the object, it might be gone (the srcu lock having been dropped). Given that there is only a single user of this helper, the simplest option is to move the locking responsibility to the caller, which can keep the srcu lock held for as long as it wants. Note that this aligns with other kvm_io_bus*() helpers, which already require the srcu lock to be held by the callers. Reported-by: Will Deacon Fixes: 8a39d00670f07 ("KVM: kvm_io_bus: Add kvm_io_bus_get_dev() call") Link: https://lore.kernel.org/all/20260626111344.802555-1-maz@kernel.org Cc: stable@vger.kernel.org Reviewed-by: Oliver Upton Link: https://patch.msgid.link/20260627105105.1005990-1-maz@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic-its.c | 2 ++ virt/kvm/kvm_main.c | 16 +++++----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index fa58668bcf1b7..d2636d3391090 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -507,6 +507,8 @@ static struct vgic_its *__vgic_doorbell_to_its(struct kvm *kvm, gpa_t db) struct kvm_io_device *kvm_io_dev; struct vgic_io_device *iodev; + guard(srcu)(&kvm->srcu); + kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, db); if (!kvm_io_dev) return ERR_PTR(-EINVAL); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 8d55bbc6f34a7..bec634ac68cbb 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -6059,25 +6059,19 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr) { struct kvm_io_bus *bus; - int dev_idx, srcu_idx; - struct kvm_io_device *iodev = NULL; + int dev_idx; - srcu_idx = srcu_read_lock(&kvm->srcu); + lockdep_assert_held(&kvm->srcu); bus = kvm_get_bus_srcu(kvm, bus_idx); if (!bus) - goto out_unlock; + return NULL; dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1); if (dev_idx < 0) - goto out_unlock; - - iodev = bus->range[dev_idx].dev; - -out_unlock: - srcu_read_unlock(&kvm->srcu, srcu_idx); + return NULL; - return iodev; + return bus->range[dev_idx].dev; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_io_bus_get_dev); From 5000bcae71c869cba6674c326fec2d8ad659ae3a Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Sun, 14 Jun 2026 22:13:24 -0700 Subject: [PATCH 1752/3297] KVM: arm64: nv: Drop bogus WARN for write to ZCR_EL2 commit 9f1667098c6ae7ec81a9a56859cfdacb822aa0d0 upstream. It is entirely possible for a guest to write to the ZCR_EL2 sysreg alias while in a nested context, as it is expected if FEAT_NV2 is advertised to the L1 hypervisor. Get rid of the bogus WARN which, since the hyp vectors were installed at this point, has the effect of a hyp_panic... Cc: stable@vger.kernel.org Fixes: 0cfc85b8f5cf ("KVM: arm64: nv: Load guest FP state for ZCR_EL2 trap") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260615051324.830045-1-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/include/hyp/switch.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 018f930d40dcb..0818cf63d64c5 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -568,8 +568,6 @@ static inline bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) return false; break; case ESR_ELx_EC_SYS64: - if (WARN_ON_ONCE(!is_hyp_ctxt(vcpu))) - return false; fallthrough; case ESR_ELx_EC_SVE: if (!sve_guest) From 09f35145f3a4aacea4d9b914ad895bf5af8fc4ae Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 15 Jun 2026 14:11:16 +0100 Subject: [PATCH 1753/3297] KVM: arm64: nv: Write ESR_EL2 for injected nested SError exceptions commit e2cb1f4578625e71f461d5c1ce70984193389cbb upstream. kvm_inject_el2_exception() writes ESR_EL2 for synchronous exceptions but not for SError. enter_exception64() does not write ESR_ELx for any exception type, so the constructed syndrome is dropped. A guest L2 hypervisor taking a nested SError observes stale ESR_EL2. This affects both kvm_inject_nested_serror() and the EASE path in kvm_inject_nested_sea(). Write ESR_EL2 for except_type_serror, matching except_type_sync. Fixes: 77ee70a07357 ("KVM: arm64: nv: Honor SError exception routing / masking") Reported-by: sashiko Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260615131116.390977-1-tabba@google.com Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/emulate-nested.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 2d04fb56746ea..8778188407047 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2729,6 +2729,7 @@ static void kvm_inject_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2, break; case except_type_serror: kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SERR); + vcpu_write_sys_reg(vcpu, esr_el2, ESR_EL2); break; default: WARN_ONCE(1, "Unsupported EL2 exception injection %d\n", type); From 884b44256041ec6b2dcbe8e6a67384d26145cba1 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 12:08:21 +0800 Subject: [PATCH 1754/3297] KVM: arm64: nv: Fix SPSR_EL2 restore in kvm_hyp_handle_mops() commit ff1022c3de46753eb7eba2f6efd990569e66ff95 upstream. kvm_hyp_handle_mops() resets the single-step state machine as part of rewinding state for a MOPS exception by modifying vcpu_cpsr() and writing the result directly into hardware. In the case of nested virtualization, vcpu_cpsr() is a synthetic value such that the rest of KVM can deal with vEL2 cleanly. That means the value requires translation before being written into hardware, which is unfortunately missing from the MOPS handler. Fix it by directly modifying SPSR_EL2 and avoiding the synthetic state altogether, which will be resynchronized on the next 'full' exit back to KVM. Fixes: 2de451a329cf ("KVM: arm64: Add handler for MOPS exceptions") Reported-by: Zhong Wang Reported-by: Xuanqing Shi Link: https://lore.kernel.org/all/ajE4lHQevXNHpl1M@Air.local/ Cc: stable@vger.kernel.org Signed-off-by: Weiming Shi Link: https://patch.msgid.link/20260617040820.2194831-2-bestswngs@gmail.com Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/include/hyp/switch.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 0818cf63d64c5..e1ad60d3f3677 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -419,16 +419,19 @@ static inline bool __populate_fault_info(struct kvm_vcpu *vcpu) static inline bool kvm_hyp_handle_mops(struct kvm_vcpu *vcpu, u64 *exit_code) { + u64 spsr; + *vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR); arm64_mops_reset_regs(vcpu_gp_regs(vcpu), vcpu->arch.fault.esr_el2); write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR); /* * Finish potential single step before executing the prologue - * instruction. + * instruction. Modify the hardware SPSR_EL2 directly, as vcpu_cpsr() + * may hold a synthetic (vEL2) value for a guest hypervisor. */ - *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS; - write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR); + spsr = read_sysreg_el2(SYS_SPSR); + write_sysreg_el2(spsr & ~DBG_SPSR_SS, SYS_SPSR); return true; } From 5c50db5bcbb9073cb2fd97be51b962de92f429e9 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:02 -0700 Subject: [PATCH 1755/3297] KVM: arm64: nv: Respect read-only PFN when mapping L1 VNCR commit 2684e02bac41c5220f6c1ab2bdcc957b71812977 upstream. KVM currently maps the L1 VNCR into the host stage-1 by relying entirely on the permissions of the guest stage-1. At the same time, it is entirely possible that the backing PFN is read-only (e.g. RO memslot), meaning that the L1 VNCR should use at most a read-only mapping. Cache the writability of the PFN in the VNCR TLB and use it to constrain the resulting fixmap permissions. Promote VNCR permission faults to an SEA in the case where the guest attempts to write to a read-only endpoint. Conveniently, this also plugs a page leak found by Sashiko [*] resulting from the early return for a read-only PFN. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Link: https://lore.kernel.org/kvm/20260608082603.16AEC1F00893@smtp.kernel.org/ Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-2-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index b1f52f82c9ef6..6a2880a18252d 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -24,6 +24,7 @@ struct vncr_tlb { struct s1_walk_result wr; u64 hpa; + bool hpa_writable; /* -1 when not mapped on a CPU */ int cpu; @@ -1226,7 +1227,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) if (!*is_gmem) { pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, &writable, &page); - if (is_error_noslot_pfn(pfn) || (write_fault && !writable)) + if (is_error_noslot_pfn(pfn)) return -EFAULT; } else { ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL); @@ -1235,6 +1236,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) write_fault, false, false); return ret; } + + writable = !(memslot->flags & KVM_MEM_READONLY); } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { @@ -1245,28 +1248,41 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) vt->gva = va; vt->hpa = pfn << PAGE_SHIFT; + vt->hpa_writable = writable; vt->valid = true; vt->cpu = -1; kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu); - kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw); + kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable); } - if (vt->wr.pw) + if (vt->wr.pw && vt->hpa_writable) mark_page_dirty(vcpu->kvm, gfn); return 0; } -static void inject_vncr_perm(struct kvm_vcpu *vcpu) +static void handle_vncr_perm(struct kvm_vcpu *vcpu) { struct vncr_tlb *vt = vcpu->arch.vncr_tlb; u64 esr = kvm_vcpu_get_esr(vcpu); + u64 fsc; + + /* + * Promote to an external abort if the stage-1 permits writes but the + * HPA is read-only (e.g. RO memslot). + */ + if (kvm_is_write_fault(vcpu) && vt->wr.pw && !vt->hpa_writable) + fsc = ESR_ELx_FSC_EXTABT; + /* + * Otherwise, inject a permission fault using the guest's translation + * level rather than the host's. + */ + else + fsc = ESR_ELx_FSC_PERM_L(vt->wr.level); - /* Adjust the fault level to reflect that of the guest's */ esr &= ~ESR_ELx_FSC; - esr |= FIELD_PREP(ESR_ELx_FSC, - ESR_ELx_FSC_PERM_L(vt->wr.level)); + esr |= FIELD_PREP(ESR_ELx_FSC, fsc); kvm_inject_nested_sync(vcpu, esr); } @@ -1312,7 +1328,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) return kvm_handle_guest_sea(vcpu); if (esr_fsc_is_permission_fault(esr)) { - inject_vncr_perm(vcpu); + handle_vncr_perm(vcpu); } else if (esr_fsc_is_translation_fault(esr)) { bool valid, is_gmem = false; int ret; @@ -1360,7 +1376,7 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) break; case -EPERM: /* Hack to deal with POE until we get kernel support */ - inject_vncr_perm(vcpu); + handle_vncr_perm(vcpu); break; case 0: break; @@ -1417,7 +1433,7 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) vt->cpu = smp_processor_id(); - if (vt->wr.pw && vt->wr.pr) + if (vt->hpa_writable && vt->wr.pw && vt->wr.pr) prot = PAGE_KERNEL; else if (vt->wr.pr) prot = PAGE_KERNEL_RO; From 4ead4def04659739c399bfcb063f8a906194c79f Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:03 -0700 Subject: [PATCH 1756/3297] KVM: arm64: nv: Inject SEA if kvm_translate_vncr() can't resolve PFN commit 9f3e83345a56280efffe235c65593c7e544c0fcc upstream. kvm_handle_vncr_abort() assumes that s1_walk_result conveys an abort when kvm_translate_vncr() returns -EFAULT. This is not always the case as it's possible to encounter 'late' failures on the output of S1 translation, e.g. a GFN outside of the memslots. Fix it by preparing an external abort before returning from kvm_translate_vncr(). Get rid of the BUG_ON() in the fault injection path while at it. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-3-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/kvm_nested.h | 8 ++++++++ arch/arm64/kvm/at.c | 8 -------- arch/arm64/kvm/nested.c | 10 ++++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index f7c06a840963d..4c19688102396 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -353,6 +353,14 @@ struct s1_walk_result { bool failed; }; +static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) +{ + wr->fst = fst; + wr->ptw = s1ptw; + wr->s2 = s1ptw; + wr->failed = true; +} + int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va); int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa, diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index e6de6aac6ede2..5cdf3faaabee2 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -10,14 +10,6 @@ #include #include -static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) -{ - wr->fst = fst; - wr->ptw = s1ptw; - wr->s2 = s1ptw; - wr->failed = true; -} - #define S1_MMU_DISABLED (-127) static int get_ia_size(struct s1_walk_info *wi) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 6a2880a18252d..6565f20c74feb 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1220,15 +1220,19 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) gfn = vt->wr.pa >> PAGE_SHIFT; memslot = gfn_to_memslot(vcpu->kvm, gfn); - if (!memslot) + if (!memslot) { + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); return -EFAULT; + } *is_gmem = kvm_slot_has_gmem(memslot); if (!*is_gmem) { pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, &writable, &page); - if (is_error_noslot_pfn(pfn)) + if (is_error_noslot_pfn(pfn)) { + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); return -EFAULT; + } } else { ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL); if (ret) { @@ -1367,8 +1371,6 @@ int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) * Translation failed, inject the corresponding * exception back to EL2. */ - BUG_ON(!vt->wr.failed); - esr &= ~ESR_ELx_FSC; esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); From d5436e18e4fc2886ac306304d884ea3b92e1edbf Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:05 -0700 Subject: [PATCH 1757/3297] KVM: arm64: nv: Inject SEA if guest VNCR isn't normal memory commit 4bd7dbe0b2243e6aa735cae4d5e1ff988b30b2a6 upstream. When constructing an L1 VNCR mapping, KVM unconditionally uses cacheable memory attributes, even if the underlying PFN isn't memory. This gets particularly hairy if the endpoint doesn't support cacheable memory attributes, potentially throwing an SError on writeback... While KVM does permit cacheable memory attributes on certain PFNMAP VMAs, kvm_translate_vncr() isn't currently grabbing the VMA. So do the simpler thing for now and just reject everything that isn't memory. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-5-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 6565f20c74feb..1ad295c844251 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1244,6 +1244,17 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) writable = !(memslot->flags & KVM_MEM_READONLY); } + /* + * FIXME: This check is too restrictive as KVM allows cacheable memory + * attributes for PFNMAP VMAs that have cacheable attributes in host + * stage-1. + */ + if (!pfn_is_map_memory(pfn)) { + kvm_release_faultin_page(vcpu->kvm, page, true, false); + fail_s1_walk(&vt->wr, ESR_ELx_FSC_EXTABT, false); + return -EINVAL; + } + scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) { kvm_release_faultin_page(vcpu->kvm, page, true, false); From 6854cf33dddb212bd7c3d69189623876e7334075 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:32 +0530 Subject: [PATCH 1758/3297] fbdev: metronomefb: fix potential memory leak in metronomefb_probe() commit 894632b862a39b3fe1cb5de06fbae86225ea64de upstream. The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup(). Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/metronomefb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index 6f0942c6e5f1c..83c614963a0a6 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -645,12 +645,14 @@ static int metronomefb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &metronomefb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_free_irq; retval = fb_alloc_cmap(&info->cmap, 8, 0); if (retval < 0) { dev_err(&dev->dev, "Failed to allocate colormap\n"); - goto err_free_irq; + goto err_fbdefio; } /* set cmap */ @@ -673,6 +675,8 @@ static int metronomefb_probe(struct platform_device *dev) err_cmap: fb_dealloc_cmap(&info->cmap); +err_fbdefio: + fb_deferred_io_cleanup(info); err_free_irq: board->cleanup(par); err_csum_table: From e8c9aae8c950869f65af8b9dd2f71f861ea6e433 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:31 +0530 Subject: [PATCH 1759/3297] fbdev: broadsheetfb: fix potential memory leak in broadsheetfb_probe() commit 9d18a4e4234fd3ee0d0eed8ccbbb50cb76b2232c upstream. The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup(). Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/broadsheetfb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index c8ba098a8c42f..582f1ee4c9b6d 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -1072,12 +1072,14 @@ static int broadsheetfb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &broadsheetfb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_vfree; retval = fb_alloc_cmap(&info->cmap, 16, 0); if (retval < 0) { dev_err(&dev->dev, "Failed to allocate colormap\n"); - goto err_vfree; + goto err_fbdefio; } /* set cmap */ @@ -1121,6 +1123,8 @@ static int broadsheetfb_probe(struct platform_device *dev) board->cleanup(par); err_cmap: fb_dealloc_cmap(&info->cmap); +err_fbdefio: + fb_deferred_io_cleanup(info); err_vfree: vfree(videomemory); err_fb_rel: From d684ce2db92b1093bcca2b42e5160a5fe23eb496 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:30 +0530 Subject: [PATCH 1760/3297] fbdev: hecubafb: fix potential memory leak in hecubafb_probe() commit cbef2a305a8a72969b86f96b7c07b86edde61aff upstream. The memory allocated for pagerefs in fb_deferred_io_init() is not freed on the error path. Fix it by calling fb_deferred_io_cleanup(). Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/hecubafb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c index 3547d58a29cf2..dd2af980f3d8b 100644 --- a/drivers/video/fbdev/hecubafb.c +++ b/drivers/video/fbdev/hecubafb.c @@ -192,7 +192,9 @@ static int hecubafb_probe(struct platform_device *dev) info->flags = FBINFO_VIRTFB; info->fbdefio = &hecubafb_defio; - fb_deferred_io_init(info); + retval = fb_deferred_io_init(info); + if (retval) + goto err_fbdefio; retval = register_framebuffer(info); if (retval < 0) @@ -209,6 +211,8 @@ static int hecubafb_probe(struct platform_device *dev) return 0; err_fbreg: + fb_deferred_io_cleanup(info); +err_fbdefio: framebuffer_release(info); err_fballoc: vfree(videomemory); From 9423e1f105270af33971a8e3e02f0db2c231f226 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 14 May 2026 21:02:02 -0400 Subject: [PATCH 1761/3297] fbdev: sm712: Fix operator precedence in big_swap macro commit 5dbe5b65df0b0c0ec77492427c274b7b5011890e upstream. The big_swap(p) macro was intended to swap bytes within 16-bit halves of a 32-bit value. However, because the bitwise shift operators (<<, >>) have higher precedence than the bitwise AND operator (&), the original code failed to perform any shifting on the masked bits. For example, 'p & 0xff00ff00 >> 8' was evaluated as 'p & (0xff00ff00 >> 8)', effectively neutralizing the intended swap. Fix this by adding parentheses to ensure the bitwise AND is performed before the shift, correctly implementing the byte swap logic. Fixes: 1461d66728648 ("staging: sm7xxfb: merge sm712fb with fbdev") Cc: stable@vger.kernel.org Signed-off-by: Li RongQing Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/sm712.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/sm712.h b/drivers/video/fbdev/sm712.h index c7ebf03b8d537..83fe25fc61f2c 100644 --- a/drivers/video/fbdev/sm712.h +++ b/drivers/video/fbdev/sm712.h @@ -101,7 +101,7 @@ struct modeinit { #define mmio_addr 0x00800000 #define seqw17() smtc_seqw(0x17, 0x30) #define big_pixel_depth(p, d) {if (p == 24) {p = 32; d = 32; } } -#define big_swap(p) ((p & 0xff00ff00 >> 8) | (p & 0x00ff00ff << 8)) +#define big_swap(p) (((p & 0xff00ff00) >> 8) | ((p & 0x00ff00ff) << 8)) #else #define pal_rgb(r, g, b, val) val #define big_addr 0 From febb5b4f67ace78d6b6862cc3c853b64710afb6c Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:41 +0530 Subject: [PATCH 1762/3297] fbdev: efifb: fix memory leak in efifb_probe() commit 9b6eaf101656958397a6012bf43f6e2e42c9e5cb upstream. Since commit 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") the string returned from fb_get_options() is expected to be freed by the caller, but the string is not freed in efifb_probe(). Fix that by freeing the option string after setup. Fixes: 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/efifb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index 0e1bd3dba2554..cf901f5654e81 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -375,6 +375,7 @@ static int efifb_probe(struct platform_device *dev) if (fb_get_options("efifb", &option)) return -ENODEV; efifb_setup(si, option); + kfree(option); /* We don't get linelength from UGA Draw Protocol, only from * EFI Graphics Protocol. So if it's not in DMI, and it's not From c2c795a320e7ef9aa69c7f4bd2c9ac07064eff9a Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:33 +0530 Subject: [PATCH 1763/3297] fbdev: radeon: fix potential memory leak in radeonfb_pci_register() commit df8c1101c9a08859da612b5d0a08d55d475522c6 upstream. The function radeonfb_pci_register() allocates memory for modelist (by calling radeon_check_modes() which calls fb_add_videomode()). The memory is appended to info->modelist, but is not freed in subsequent error paths. Fix this by calling fb_destroy_modelist(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/aty/radeon_base.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index 1d2c57ba25a3b..4e228072ea4a1 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -2476,6 +2476,7 @@ static int radeonfb_pci_register(struct pci_dev *pdev, return 0; err_unmap_fb: iounmap(rinfo->fb_base); + fb_destroy_modelist(&info->modelist); err_unmap_rom: kfree(rinfo->mon1_EDID); kfree(rinfo->mon2_EDID); From 146b708bc75f1e6cf70df31195632c9946cb70fd Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:35 +0530 Subject: [PATCH 1764/3297] fbdev: i740fb: fix potential memory leak in i740fb_probe() commit 5936063409af230a2c88b8700c47b89a19fd70b5 upstream. In i740fb_probe(), the memory allocated in fb_videomode_to_modelist() for modelist is not freed in the error paths. Fix that by calling fb_destroy_modelist(). Fixes: 5350c65f4f15 ("Resurrect Intel740 driver: i740fb") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/i740fb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/i740fb.c b/drivers/video/fbdev/i740fb.c index 9b74dae71472c..c14a19382769a 100644 --- a/drivers/video/fbdev/i740fb.c +++ b/drivers/video/fbdev/i740fb.c @@ -1152,6 +1152,7 @@ static int i740fb_probe(struct pci_dev *dev, const struct pci_device_id *ent) fb_dealloc_cmap(&info->cmap); err_alloc_cmap: err_find_mode: + fb_destroy_modelist(&info->modelist); if (par->ddc_registered) i2c_del_adapter(&par->ddc_adapter); pci_iounmap(dev, par->regs); From ad54698255a4b988cae1ad908c158c1d4128887c Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:37 +0530 Subject: [PATCH 1765/3297] fbdev: s3fb: fix potential memory leak in s3_pci_probe() commit 3b0ed04bc852887a9164e1bbf521652e8ef3eb92 upstream. In s3_pci_probe(), the memory allocated for modelist using fb_videomode_to_modelist() is not freed in subsequent error paths. Fix that by calling fb_destroy_modelist() Fixes: 86c0f043a737 ("s3fb: add DDC support") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/s3fb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/s3fb.c b/drivers/video/fbdev/s3fb.c index ba30e5568cabc..d7ee2ff07c7c9 100644 --- a/drivers/video/fbdev/s3fb.c +++ b/drivers/video/fbdev/s3fb.c @@ -1446,6 +1446,7 @@ static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) err_alloc_cmap: err_find_mode: #ifdef CONFIG_FB_S3_DDC + fb_destroy_modelist(&info->modelist); if (par->ddc_registered) i2c_del_adapter(&par->ddc_adapter); if (par->mmio) From 12fe6a56506ed3bf0aaf6130a29102ce1fce62da Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:40 +0530 Subject: [PATCH 1766/3297] fbdev: uvesafb: fix potential memory leak in uvesafb_probe() commit 033e56fed09047ee63072e9f58789f40c1c7079d upstream. Due to an incorrect goto label, memory allocated for modedb and modelist in uvesafb_vbe_init() is not freed in some error paths. Fix this by updating the goto label. Fixes: 8bdb3a2d7df4 ("uvesafb: the driver core") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/uvesafb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 5d52fd00806e9..b806da0538bc6 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -1697,14 +1697,14 @@ static int uvesafb_probe(struct platform_device *dev) i = uvesafb_vbe_init_mode(info); if (i < 0) { err = -EINVAL; - goto out; + goto out_mode; } else { mode = &par->vbe_modes[i]; } if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { err = -ENXIO; - goto out; + goto out_mode; } uvesafb_init_info(info, mode); From e1ca9b8559e0f5959228626bdeaae530a1ee0337 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:38 +0530 Subject: [PATCH 1767/3297] fbdev: tdfxfb: fix potential memory leak in tdfxfb_probe() commit bb019d755366cc3e777a12d4bf457ff289837370 upstream. In tdfxfb_probe(), the memory allocated for modelist using fb_videomode_to_modelist() when CONFIG_FB_3DFX_I2C is defined, is not freed in the subsequent error paths. Fix that by calling fb_destroy_modelist(). Fixes: 215059d2421f ("tdfxfb: make use of DDC information about connected monitor") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/tdfxfb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index 531fb8478e207..c5095358209e1 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -1550,6 +1550,7 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_err_iobase: #ifdef CONFIG_FB_3DFX_I2C + fb_destroy_modelist(&info->modelist); tdfxfb_delete_i2c_busses(default_par); #endif arch_phys_wc_del(default_par->wc_cookie); From d81860691e4cfa14d596136ea2727f244e9e5950 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:34 +0530 Subject: [PATCH 1768/3297] fbdev: carminefb: fix potential memory leak in alloc_carmine_fb() commit 6fcca16a2b19c37f60693c56cbc0c923364ff3ef upstream. The memory allocated for modelist in fb_videomode_to_modelist() is not freed in the subsequent error path. Fix that by calling fb_destroy_modelist() Fixes: 2ece5f43b041 ("fbdev: add the carmine FB driver") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/carminefb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/carminefb.c b/drivers/video/fbdev/carminefb.c index 2bdd675958913..37a5ee4da13ce 100644 --- a/drivers/video/fbdev/carminefb.c +++ b/drivers/video/fbdev/carminefb.c @@ -589,6 +589,7 @@ static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base, return 0; err_dealloc_cmap: + fb_destroy_modelist(&info->modelist); fb_dealloc_cmap(&info->cmap); err_free_fb: framebuffer_release(info); From 58bc18e03481b62f0ec53fe47f36615d52660a7d Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:42 +0530 Subject: [PATCH 1769/3297] fbdev: vesafb: fix memory leak in vesafb_probe() commit b15d708995c01bbffe7dcd634a31959f6805bed3 upstream. Since commit 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") the string returned from fb_get_options() is expected to be freed by the caller. But the string is not freed in vesafb_probe(). Fix that by freeing the option string after setup. Fixes: 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/vesafb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/vesafb.c b/drivers/video/fbdev/vesafb.c index a81df88651433..634d4c9898d1f 100644 --- a/drivers/video/fbdev/vesafb.c +++ b/drivers/video/fbdev/vesafb.c @@ -267,6 +267,7 @@ static int vesafb_probe(struct platform_device *dev) /* ignore error return of fb_get_options */ fb_get_options("vesafb", &option); vesafb_setup(option); + kfree(option); if (si->orig_video_isVGA != VIDEO_TYPE_VLFB) return -ENODEV; From 009a8514745b16c468acc25aa00539abbf38e1b5 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:36 +0530 Subject: [PATCH 1770/3297] fbdev: nvidia: fix potential memory leak in nvidiafb_probe() commit 85f5e38c162bdf9dbbe197275d416402712f3707 upstream. In nvidiafb_probe(), the memory allocated for modelist in nvidia_set_fbinfo() is not freed in the subsequent error paths. Fix that by calling fb_destroy_modelist(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/nvidia/nvidia.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c index 72b85f475605d..c9dcc9e899487 100644 --- a/drivers/video/fbdev/nvidia/nvidia.c +++ b/drivers/video/fbdev/nvidia/nvidia.c @@ -1421,6 +1421,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent) err_out_iounmap_fb: iounmap(info->screen_base); + fb_destroy_modelist(&info->modelist); err_out_free_base1: fb_destroy_modedb(info->monspecs.modedb); nvidia_delete_i2c_busses(par); From 4ebe2c3a7db6332aab3655f8c552ca387c1dc199 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 14 May 2026 13:54:39 +0530 Subject: [PATCH 1771/3297] fbdev: tridentfb: fix potential memory leak in trident_pci_probe() commit 7a35ec619d9af8ee128320975c1252b8ad65f1e8 upstream. In trident_pci_probe(), the memory allocated for modelist using fb_videomode_to_modelist() is not freed in subsequent error paths. Fix that by calling fb_destroy_modelist(). Fixes: 6a5e3bd0c8bc ("tridentfb: Add DDC support") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/tridentfb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c index 516cf2a187575..2f7338de09128 100644 --- a/drivers/video/fbdev/tridentfb.c +++ b/drivers/video/fbdev/tridentfb.c @@ -1706,6 +1706,7 @@ static int trident_pci_probe(struct pci_dev *dev, return 0; out_unmap2: + fb_destroy_modelist(&info->modelist); if (default_par->ddc_registered) i2c_del_adapter(&default_par->ddc_adapter); kfree(info->pixmap.addr); From 121577383b5cf221e86581e0f2bcca4c66f17469 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:58 +0300 Subject: [PATCH 1772/3297] ASoC: SOF: ipc3-control: Fix heap overflow in bytes_ext put/get commit fd46668d538993218eea19c6925c868ac0f2630c upstream. The ipc_control_data buffer is allocated as kzalloc(max_size), where max_size covers the entire struct sof_ipc_ctrl_data including its flexible array payload. However, the bounds checks in bytes_ext_put and _bytes_ext_get compared user data lengths against max_size directly, ignoring that cdata->data sits at an offset of sizeof(struct sof_ipc_ctrl_data) bytes into the allocation. This allowed writing up to sizeof(struct sof_ipc_ctrl_data) bytes past the end of the heap buffer from unprivileged userspace via the ALSA TLV kcontrol interface, and similarly allowed over-reading adjacent heap data on the get path. Fix all bounds checks to subtract sizeof(*cdata) from max_size so they reflect the actual space available at the cdata->data offset. Also fix the error-path restore in bytes_ext_put which wrote to cdata->data instead of cdata, causing the same overflow. Fixes: 67ec2a091630 ("ASoC: SOF: Add bytes_ext control IPC ops for IPC3") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index e8654edab90bc..17c12abeb5d02 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -398,9 +398,17 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, } /* be->max is coming from topology */ - if (header.length > scontrol->max_size) { - dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n", - header.length, scontrol->max_size); + if (header.length > scontrol->max_size - sizeof(*cdata)) { + dev_err_ratelimited(scomp->dev, "Bytes data size %u exceeds max %zu\n", + header.length, scontrol->max_size - sizeof(*cdata)); + return -EINVAL; + } + + /* Ensure the data is large enough to contain the ABI header */ + if (header.length < sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, + "Bytes data size %u less than ABI header %zu\n", + header.length, sizeof(struct sof_abi_hdr)); return -EINVAL; } @@ -436,7 +444,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - sizeof(struct sof_abi_hdr)) { dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); goto err_restore; } @@ -452,7 +460,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, err_restore: /* If we have an issue, we restore the old, valid bytes control data */ if (scontrol->old_ipc_control_data) { - memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size); + memcpy(cdata, scontrol->old_ipc_control_data, scontrol->max_size); kfree(scontrol->old_ipc_control_data); scontrol->old_ipc_control_data = NULL; } @@ -491,10 +499,13 @@ static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, } /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", + if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - + sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, + "User data size %u exceeds max size %zu\n", cdata->data->size, - scontrol->max_size - sizeof(struct sof_abi_hdr)); + scontrol->max_size - sizeof(*cdata) - + sizeof(struct sof_abi_hdr)); return -EINVAL; } From d3abaedf6a58469610136d2dace1a85cddf7afcf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 11:34:56 +0300 Subject: [PATCH 1773/3297] ASoC: SOF: ipc3-control: Validate size in snd_sof_update_control commit 390aa4c9339bb0ec0bc8d554e830faf93ca9d49e upstream. In snd_sof_update_control(), firmware-provided cdata->num_elems is checked against local_cdata->data->size but never against the actual allocation size. If local_cdata->data->size was previously set to an inconsistent value, the memcpy could write past the allocated buffer. Add a bounds check to ensure num_elems fits within the available space in the ipc_control_data allocation before copying. Fixes: 10f461d79c2d ("ASoC: SOF: Add IPC3 topology control ops") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260609083458.31193-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/sof/ipc3-control.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 17c12abeb5d02..d1697401b1da0 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -555,6 +555,15 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol, return; } + /* Verify the size fits within the allocation */ + if (cdata->num_elems > scontrol->max_size - sizeof(*local_cdata) - + sizeof(*local_cdata->data)) { + dev_err(scomp->dev, + "cdata binary size %u exceeds buffer\n", + cdata->num_elems); + return; + } + /* copy the new binary data */ memcpy(local_cdata->data, cdata->data, cdata->num_elems); } else if (cdata->num_elems != scontrol->num_channels) { From e0f276f1918a202e9c3ac72baffd311cecb6b8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:55:47 -0300 Subject: [PATCH 1774/3297] ASoC: mediatek: mt8192: Check runtime resume during probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e24d5dde56a50946020b134fa8448869093db76a upstream. The MT8192 AFE probe enables runtime PM temporarily while reinitializing the regmap cache from hardware, but it uses pm_runtime_get_sync() without checking the return value. If runtime resume fails, probe keeps going without the device necessarily being accessible, and pm_runtime_get_sync() may leave the PM usage count incremented. The regmap_reinit_cache() failure path also returns before dropping the temporary PM reference and before clearing pm_runtime_bypass_reg_ctl. Use pm_runtime_resume_and_get() so resume failures do not leak a usage count, and clear the temporary bypass flag after dropping the probe PM reference on all regmap_reinit_cache() outcomes. Fixes: 125ab5d588b0 ("ASoC: mediatek: mt8192: add platform driver") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8192-probe-cleanup-v1-2-1bb834d05b72@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index 3d32fe46118ec..dfae512c470ba 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2218,15 +2218,19 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) { + afe_priv->pm_runtime_bypass_reg_ctl = false; + return dev_err_probe(dev, ret, "failed to resume device\n"); + } ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config); - if (ret) - return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n"); - pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; + if (ret) + return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n"); + regcache_cache_only(afe->regmap, true); regcache_mark_dirty(afe->regmap); From 51c367230e30ed80b49d5330831c1f2c59405b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:55:46 -0300 Subject: [PATCH 1775/3297] ASoC: mediatek: mt8192: Release reserved memory on cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 965e17ae6751c5d3302430c8ebd650e72d45a85f upstream. The MT8192 AFE probe calls of_reserved_mem_device_init() and falls back to preallocated buffers when no reserved memory region is available. When the reserved memory assignment succeeds, however, the driver never releases it. Register a devm cleanup action after a successful reserved-memory assignment so the assignment is released on probe failure and driver unbind. Fixes: ec4a10ca4a68 ("ASoC: mediatek: use reserved memory or enable buffer pre-allocation") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8192-probe-cleanup-v1-1-1bb834d05b72@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index dfae512c470ba..db0ae44a86afe 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2155,6 +2155,11 @@ static const dai_register_cb dai_register_cbs[] = { mt8192_dai_memif_register, }; +static void mt8192_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -2184,6 +2189,10 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) { dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); afe->preallocate_buffers = true; + } else { + ret = devm_add_action_or_reset(dev, mt8192_afe_release_reserved_mem, dev); + if (ret) + return ret; } /* init audio related clock */ From 4b068759d308777a99ca809a1d514bbf7ae3bef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:41:49 -0300 Subject: [PATCH 1776/3297] ASoC: mediatek: mt8183: Check runtime resume during probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f0334fbfd107682d0c95f3f71e25f6127038e2b9 upstream. The MT8183 AFE probe uses pm_runtime_get_sync() before reading hardware defaults into the regmap cache, but does not check whether runtime resume failed. If regmap_reinit_cache() then fails, the temporary runtime PM usage count is also not released. Use pm_runtime_resume_and_get() so resume failures abort probe without leaking a usage count, and release the temporary reference before handling the regmap cache result. Fixes: a94aec035a12 ("ASoC: mediatek: mt8183: add platform driver") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8183-probe-cleanup-v1-2-4f4f5593c8d1@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index a7fef772760a6..7463018ea3811 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -833,17 +833,21 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(dev); - - ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); + ret = pm_runtime_resume_and_get(dev); if (ret) { - dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret); + afe_priv->pm_runtime_bypass_reg_ctl = false; goto err_pm_disable; } + ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; + if (ret) { + dev_err(dev, "regmap_reinit_cache fail, ret %d\n", ret); + goto err_pm_disable; + } + regcache_cache_only(afe->regmap, true); regcache_mark_dirty(afe->regmap); From e42d8322b67f21493a832ec338aad1c93bd01ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 10:41:48 -0300 Subject: [PATCH 1777/3297] ASoC: mediatek: mt8183: Release reserved memory on cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bee65e00c0924ebecf97718d95dcf4a05ee36471 upstream. The MT8183 AFE probe can assign reserved memory with of_reserved_mem_device_init(), but the assignment is never released on driver removal or later probe failures. Register a devm cleanup action so the reserved memory assignment is released consistently, matching newer Mediatek AFE drivers. Fixes: ec4a10ca4a68 ("ASoC: mediatek: use reserved memory or enable buffer pre-allocation") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-mt8183-probe-cleanup-v1-1-4f4f5593c8d1@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 7463018ea3811..2634699534db7 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -766,6 +766,11 @@ static const dai_register_cb dai_register_cbs[] = { mt8183_dai_memif_register, }; +static void mt8183_afe_release_reserved_mem(void *data) +{ + of_reserved_mem_device_release(data); +} + static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -794,6 +799,12 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) { dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); afe->preallocate_buffers = true; + } else { + ret = devm_add_action_or_reset(dev, + mt8183_afe_release_reserved_mem, + dev); + if (ret) + return ret; } /* initial audio related clock */ From ca028334343a140efda4b22e53cbce2c5e94a489 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 16 Jun 2026 18:02:57 +0100 Subject: [PATCH 1778/3297] ASoC: qcom: q6apm: fix NULL pointer dereference in graph_callback commit 2e9261761b35f0b67b7487688cd1365f535be0b3 upstream. When q6apm_free_fragments() is called it frees rx_data.buf/tx_data.buf and sets them to NULL under graph->lock. A late DSP buffer-done response can race with this: graph_callback() passes the !graph->ar_graph guard (not yet NULL), acquires the lock, but then dereferences a now-NULL buf pointer to read buf[token].phys, crashing at virtual address 0x10. Add a NULL check for buf inside the mutex-protected section in both the write-done (DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2) and read-done (DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2) handlers and bail out cleanly if buffers have already been freed. This problem is only shown up recently while apr bus was updated to process the commands per service rather from single global queue. Fixes: 5477518b8a0e ("ASoC: qdsp6: audioreach: add q6apm support") Cc: Stable@vger.kernel.org Assisted-by: Claude:claude-4-6-sonnet Reported-by: Val Packett Closes: https://lore.kernel.org/all/133ced18-1aa9-475d-80d8-6120678bdde4@packett.cool/ Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260616170257.9381-1-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/qcom/qdsp6/q6apm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index d425cf1bcbb42..6326649ba31b4 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -536,6 +536,10 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) token = hdr->token & APM_WRITE_TOKEN_MASK; done = data->payload; + if (!graph->rx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->rx_data.buf[token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ @@ -570,6 +574,10 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) client_event = APM_CLIENT_EVENT_DATA_READ_DONE; mutex_lock(&graph->lock); rd_done = data->payload; + if (!graph->tx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->tx_data.buf[hdr->token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ From aff589556ed772cb1c0c2d7b4d91ec45c0c39416 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Tue, 19 May 2026 17:23:28 -0400 Subject: [PATCH 1779/3297] netfilter: nf_conntrack_irc: fix parse_dcc() off-by-one OOB read commit ef6400ca25a13fd6dedbe8ef4a1d0979bbbfe88a upstream. parse_dcc() treats data_end as an inclusive end pointer, but its only caller passes data_limit = ib_ptr + datalen, which points one past the last valid byte. The newline search loop iterates while tmp <= data_end, so when no newline is present, *tmp is read at tmp == data_end, one byte beyond the region filled by skb_header_pointer(). irc_buffer is kmalloc'd as MAX_SEARCH_SIZE + 1 bytes and datalen is capped at MAX_SEARCH_SIZE, so the stray read does not fault. The byte is uninitialized or stale; if it contains an ASCII digit, simple_strtoul will consume it and produce a wrong DCC IP or port in the conntrack expectation. The extra allocation byte is also a fragile guard: if the cap or allocation size changes, this becomes a real out-of-bounds read. Change the loop and its post-loop check to use strict less-than, consistent with the caller's exclusive-end convention. Update the function comment accordingly. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_irc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 0f50ea92ced9d..454532838870a 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -64,7 +64,7 @@ static const char *const dccprotos[] = { /* tries to get the ip_addr and port out of a dcc command * return value: -1 on failure, 0 on success * data pointer to first byte of DCC command data - * data_end pointer to last byte of dcc command data + * data_end one past end of data * ip returns parsed ip of dcc command * port returns parsed port of dcc command * ad_beg_p returns pointer to first byte of addr data @@ -82,10 +82,10 @@ static int parse_dcc(char *data, const char *data_end, __be32 *ip, /* Make sure we have a newline character within the packet boundaries * because simple_strtoul parses until the first invalid character. */ - for (tmp = data; tmp <= data_end; tmp++) + for (tmp = data; tmp < data_end; tmp++) if (*tmp == '\n') break; - if (tmp > data_end || *tmp != '\n') + if (tmp >= data_end || *tmp != '\n') return -1; *ad_beg_p = data; From a1b672a3b537242ac698dbbd11273d1a100892db Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 11 Apr 2026 19:57:21 +0100 Subject: [PATCH 1780/3297] netfilter: nfnl_cthelper: apply per-class values when updating policies commit d738feccb98cb224ebabecb703e98f5008276bff upstream. When a userspace conntrack helper with multiple expectation classes is updated via nfnetlink, every class ends up with the first class's max_expected and timeout values. nfnl_cthelper_update_policy_all() validates each new policy into the corresponding slot of the temporary new_policy array, but the second loop that commits the values into the live helper dereferences new_policy as a pointer instead of indexing it, so every iteration reads new_policy[0] regardless of i. An update that changes per-class values is silently collapsed onto class 0's values with no error returned to userspace. Index the temporary array by i in the commit loop so each class gets its own validated values. Fixes: 2c422257550f ("netfilter: nfnl_cthelper: fix runtime expectation policy updates") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 18a57af99d27e..eee6dbebb2b13 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -351,8 +351,8 @@ static int nfnl_cthelper_update_policy_all(struct nlattr *tb[], for (i = 0; i < helper->expect_class_max + 1; i++) { policy = (struct nf_conntrack_expect_policy *) &helper->expect_policy[i]; - policy->max_expected = new_policy->max_expected; - policy->timeout = new_policy->timeout; + policy->max_expected = new_policy[i].max_expected; + policy->timeout = new_policy[i].timeout; } err: From 07f9ddbf5e799c24a3a52ec9bd7b729a6f6d69d5 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Thu, 11 Jun 2026 15:21:42 +0800 Subject: [PATCH 1781/3297] netfilter: xt_cluster: reject template conntracks in hash match commit 5feba91006ec92da57acc1cc2e34df623b98541e upstream. xt_cluster_mt() treats any non-NULL nf_ct_get() result as a fully initialized conntrack and passes it to xt_cluster_hash(). This causes a state confusion bug when the raw table CT target attaches a template conntrack to skb->_nfct before normal conntrack processing. Templates carry IPS_TEMPLATE status but do not have a valid tuple for hashing yet, so xt_cluster_hash() can hit its WARN_ON() path on the zeroed l3num field. Reject template conntracks before hashing them. This matches existing netfilter handling for template objects and avoids hashing incomplete conntrack state. Fixes: 0269ea493734 ("netfilter: xtables: add cluster match") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 908fd5f2c3c84..eaf2511d63f0e 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -107,7 +107,7 @@ xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par) } ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL) + if (!ct || nf_ct_is_template(ct)) return false; if (ct->master) From 0ca505346c5e2905ab7b5313af801fcf38f594a8 Mon Sep 17 00:00:00 2001 From: Haoze Xie Date: Mon, 8 Jun 2026 13:43:44 +0800 Subject: [PATCH 1782/3297] netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst commit c9c9b37f8c5505224e8d206184df3bb668ee00cf upstream. The br_netfilter fake rtable is embedded in struct net_bridge and is attached to bridged packets with skb_dst_set_noref(). If such a packet is queued to NFQUEUE, __nf_queue() upgrades that fake dst with skb_dst_force(). At that point the queued skb can hold a real dst reference after bridge teardown has started. The problem is not that every bridged packet needs its own dst reference. The problem is that NFQUEUE can keep the bridge private fake dst alive after unregister begins. Fix this by keeping the bridge fake dst model unchanged and pinning the bridge master device only while the packet sits in NFQUEUE. Record the bridge device in nf_queue_entry when the queued skb carries a bridge fake dst, take a device reference for the queue lifetime, and drop it when the queue entry is freed. Also make sure queued entries are reaped when that bridge device goes down, and drop the redundant nf_bridge_info_exists() test from the fake dst detection. This keeps netdev_priv(br->dev) alive until verdict completion, so the embedded fake rtable and its metrics backing storage cannot be freed out from under dst_release(). It also avoids the constant refcount bump and avoids using ipv4-specific dst helpers for IPv6 bridge traffic. Fixes: 34666d467cbf ("netfilter: bridge: move br_netfilter out of the core") 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 | 14 ++++++++++++++ net/netfilter/nfnetlink_queue.c | 3 +++ 3 files changed, 18 insertions(+) diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 3978c3174cdbe..fc3e81c07364f 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -18,6 +18,7 @@ struct nf_queue_entry { unsigned int id; unsigned int hook_index; /* index in hook_entries->hook[] */ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + struct net_device *bridge_dev; struct net_device *physin; struct net_device *physout; #endif diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index dd416c8532c55..c0c9ea5fc4e19 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -67,6 +67,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry) nf_queue_sock_put(state->sk); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + dev_put(entry->bridge_dev); dev_put(entry->physin); dev_put(entry->physout); #endif @@ -83,6 +84,8 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) { #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) const struct sk_buff *skb = entry->skb; + struct dst_entry *dst = skb_dst(skb); + struct net_device *dev = NULL; if (nf_bridge_info_exists(skb)) { entry->physin = nf_bridge_get_physindev(skb, entry->state.net); @@ -91,6 +94,16 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) entry->physin = NULL; entry->physout = NULL; } + + if (entry->state.pf == NFPROTO_BRIDGE && + dst && (dst->flags & DST_FAKE_RTABLE)) + dev = dst_dev_rcu(dst); + + /* Must hold a reference on the bridge device: dst_hold() protects + * the dst itself, but the fake rtable is embedded in bridge-private + * storage that netdevice teardown can free independently. + */ + entry->bridge_dev = dev; #endif } @@ -107,6 +120,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry) dev_hold(state->out); #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + dev_hold(entry->bridge_dev); dev_hold(entry->physin); dev_hold(entry->physout); #endif diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index aa870e29b5004..a65d9a5cb0f06 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1253,6 +1253,9 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) if (physinif == ifindex || physoutif == ifindex) return 1; + + if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex) + return 1; #endif if (entry->skb_dev && entry->skb_dev->ifindex == ifindex) return 1; From 02b6b0e892aea582590671796fd6eff5b93ea93f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 16 Jun 2026 13:26:26 +0200 Subject: [PATCH 1783/3297] netfilter: nft_set_pipapo: don't leak bad clone into future transaction commit 47e65eff50691f0a5b79d325e28d83ec1da43bcf upstream. On memory allocation failure the cloned nft_pipapo_match can enter a bad state: - some fields can have their lookup tables resized while others did not - bits might have been toggled - scratch map can be undersized which also means m->bsize_max can be lower than what is required This means that the next insertion in the same batch can trigger out-of-bounds writes. Furthermore, a failure in the first can result in the bad clone to leak into the next transaction because the abort callback is never executed in this case (the upper layer saw an error and no attempt to allocate a transactional request was made). Record a state for the nft_pipapo_match structure: - NEW (pristine clone) - MOD (modified clone with good state) - ERR (potentially bogus content) Then make it so that deletes and insertions fail when the clone entered ERR state. In case the very first insert attempt results in an error, free the clone right away. Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges") Cc: stable@vger.kernel.org Reported-and-tested-by: Seesee Reviewed-by: Stefano Brivio Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_set_pipapo.c | 34 +++++++++++++++++++++++++++++----- net/netfilter/nft_set_pipapo.h | 8 ++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 394b78a00a6a5..b377bef60b212 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -342,6 +342,8 @@ #include "nft_set_pipapo_avx2.h" #include "nft_set_pipapo.h" +static void nft_pipapo_abort(const struct nft_set *set); + /** * pipapo_refill() - For each set bit, set bits from selected mapping table item * @map: Bitmap to be scanned for set bits @@ -1298,7 +1300,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, const u8 *start_p, *end_p; int i, bsize_max, err = 0; - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return -ENOMEM; if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) @@ -1369,8 +1371,10 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, else ret = pipapo_expand(f, start, end, f->groups * f->bb); - if (ret < 0) - return ret; + if (ret < 0) { + err = ret; + goto abort; + } if (f->bsize > bsize_max) bsize_max = f->bsize; @@ -1386,7 +1390,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, err = pipapo_realloc_scratch(m, bsize_max); if (err) - return err; + goto abort; m->bsize_max = bsize_max; } else { @@ -1398,7 +1402,26 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, pipapo_map(m, rulemap, e); + m->state = NFT_PIPAPO_CLONE_MOD; return 0; +abort: + DEBUG_NET_WARN_ON_ONCE(m->state == NFT_PIPAPO_CLONE_ERR); + + /* Two rollback cases: + * 1) no previous changes. nft_pipapo_abort is not + * guaranteed to be invoked (there might be no further + * add/delete requests coming after this). + * + * 2) we had previous changes: there are transaction + * records pointing to this set. Leave the rollback to + * the transaction handling. + */ + if (m->state == NFT_PIPAPO_CLONE_NEW) + nft_pipapo_abort(set); /* releases m */ + else + m->state = NFT_PIPAPO_CLONE_ERR; + + return err; } /** @@ -1476,6 +1499,7 @@ static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old) dst++; } + new->state = NFT_PIPAPO_CLONE_NEW; return new; out_mt: @@ -1899,7 +1923,7 @@ nft_pipapo_deactivate(const struct net *net, const struct nft_set *set, /* removal must occur on priv->clone, if we are low on memory * we have no choice and must fail the removal request. */ - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return NULL; e = pipapo_get(m, (const u8 *)elem->key.val.data, diff --git a/net/netfilter/nft_set_pipapo.h b/net/netfilter/nft_set_pipapo.h index 9aee9a9eaeb75..659908c225d64 100644 --- a/net/netfilter/nft_set_pipapo.h +++ b/net/netfilter/nft_set_pipapo.h @@ -134,9 +134,16 @@ struct nft_pipapo_scratch { unsigned long __map[]; }; +enum nft_pipapo_clone_state { + NFT_PIPAPO_CLONE_NEW, + NFT_PIPAPO_CLONE_MOD, + NFT_PIPAPO_CLONE_ERR, +}; + /** * struct nft_pipapo_match - Data used for lookup and matching * @field_count: Amount of fields in set + * @state: add/delete state; used from control plane * @bsize_max: Maximum lookup table bucket size of all fields, in longs * @scratch: Preallocated per-CPU maps for partial matching results * @rcu: Matching data is swapped on commits @@ -144,6 +151,7 @@ struct nft_pipapo_scratch { */ struct nft_pipapo_match { u8 field_count; + enum nft_pipapo_clone_state state:8; unsigned int bsize_max; struct nft_pipapo_scratch * __percpu *scratch; struct rcu_head rcu; From 2bcf2c5052fb5e73e255140ab43f056aef409c27 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 1 Jul 2026 07:44:17 +0200 Subject: [PATCH 1784/3297] netfilter: nf_nat_sip: reload possible stale data pointer commit 77e43bcb7ec177e293a5c3f1b91a2c5aebfb6c68 upstream. quoting sashiko: ------------------------------------------------------------------------ [..] noticed a potential memory bug and header corruption involving the SIP NAT helper. In net/netfilter/nf_nat_sip.c:nf_nat_sip(): if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, 0, 0, NULL, 0)) { If a cloned or fragmented SKB is reallocated by skb_ensure_writable(), the old data buffer is freed. However, nf_nat_sip() fails to update *dptr to point to the new buffer. It also appears to use nf_nat_mangle_udp_packet() on what could be a TCP packet, which would overwrite the sequence number with a checksum update. ------------------------------------------------------------------------ nf_conntrack_sip linerizes skbs, hence no fragmented skb can be seen. But clones are possible, so rebuild dptr. Disable nf_nat_mangle_udp_packet() branch for TCP streams. It doesn't look like this can ever happen, else we should have received bug reports about this, so just check the conntrack is UDP and drop otherwise. The calling conntrack_sip set ->forced_dport for SIP_HDR_VIA_UDP messages, so I don't think this is ever expected to be true for a TCP stream. Fixes: 7266507d8999 ("netfilter: nf_ct_sip: support Cisco 7941/7945 IP phones") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_nat_sip.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 00838c0cc5bb2..f7be30c69b5c8 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -283,13 +283,24 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, /* Mangle destination port for Cisco phones, then fix up checksums */ if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { + int doff = *dptr - (const char *)skb->data; struct udphdr *uh; + if (doff <= 0) { + DEBUG_NET_WARN_ON_ONCE(1); + return NF_DROP; + } + + /* ct_sip_info->forced_dport only expected with UDP */ + if (nf_ct_protonum(ct) != IPPROTO_UDP) + return NF_DROP; + if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } + *dptr = skb->data + doff; uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; From a58230f3a7c4f6c3261786bc1efb72c42e68cd25 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 16:36:29 -0700 Subject: [PATCH 1785/3297] netfilter: nf_conntrack_reasm: guard mac_header adjustment after IPv6 defrag commit 3b08fed5b7e0d5e3a25d73ef3ba09cd33ade16c9 upstream. nf_ct_frag6_reasm() slides the packet head forward to drop the IPv6 fragment header and then unconditionally advances skb->mac_header: skb->mac_header += sizeof(struct frag_hdr); On the NF_INET_LOCAL_OUT defrag path the skb has no link-layer header yet, so skb->mac_header is still the "not set" sentinel (u16)~0U. Adding sizeof(struct frag_hdr) wraps it to a small value (0xffff + 8 == 7), after which skb_mac_header_was_set() wrongly reports a MAC header is present and skb_mac_header() points into the headroom. The reassembler has done this unconditional add since it was introduced; it was harmless while mac_header was a bare pointer, but wrong once mac_header became a u16 offset whose unset state is the ~0U sentinel tested by skb_mac_header_was_set(). The sibling net/ipv6/reassembly.c does the same relocation and does guard the adjustment; mirror the guard here. Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter/nf_conntrack_reasm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 7edca05dbfc93..599c49bf0a0af 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -348,7 +348,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb, skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0]; memmove(skb->head + sizeof(struct frag_hdr), skb->head, (skb->data - skb->head) - sizeof(struct frag_hdr)); - skb->mac_header += sizeof(struct frag_hdr); + if (skb_mac_header_was_set(skb)) + skb->mac_header += sizeof(struct frag_hdr); skb->network_header += sizeof(struct frag_hdr); skb_reset_transport_header(skb); From 3cd9a5792cbea81139c24320986dd0db69e9b5d0 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Wed, 8 Jul 2026 13:27:28 +0800 Subject: [PATCH 1786/3297] netfilter: nf_conncount: fix zone comparison in tuple dedup commit f62c41b4910e65da396ec9a8c40c1fe7fe82e449 upstream. The "already exists" dedup logic in __nf_conncount_add() decides whether a connection has already been counted and can be skipped instead of incrementing the connlimit count. It compares the conntrack zone of a list entry with the zone of the connection being added using nf_ct_zone_id() and nf_ct_zone_equal(), passing conn->zone.dir or zone->dir as the direction argument. Those helpers take enum ip_conntrack_dir values: IP_CT_DIR_ORIGINAL is 0 and IP_CT_DIR_REPLY is 1. However, zone->dir is a u8 bitmask: NF_CT_ZONE_DIR_ORIG is 1, NF_CT_ZONE_DIR_REPL is 2 and NF_CT_DEFAULT_ZONE_DIR is 3. Passing that bitmask as the enum direction shifts the meaning of every non-zero value. An ORIG-only zone passes 1 and is tested as REPLY, while REPL-only and default zones pass 2 or 3 and test bits beyond the valid direction range. In those cases nf_ct_zone_id() can fall back to NF_CT_DEFAULT_ZONE_ID instead of using the real zone id, so different zones can be treated as equal and dedup collapses to tuple equality alone. nf_conncount stores and compares the original-direction tuple for a connection. If an skb already has an attached conntrack entry, get_ct_or_tuple_from_skb() explicitly copies ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, regardless of the packet's ctinfo. Therefore the zone comparison in the tuple dedup path must use IP_CT_DIR_ORIGINAL as well; the zone direction bitmask describes where a zone id applies, not which direction this conncount tuple represents. Fix the two dedup comparisons by passing IP_CT_DIR_ORIGINAL directly. Do not special-case NF_CT_DEFAULT_ZONE_DIR and do not compare raw zone ids: using the existing helpers with IP_CT_DIR_ORIGINAL preserves the direction-aware NF_CT_DEFAULT_ZONE_ID fallback. A default bidirectional zone contains the ORIG bit, so it naturally returns the real zone id; reply-only zones continue to fall back for original-direction tuple comparisons. Fixes: 21ba8847f857 ("netfilter: nf_conncount: Fix garbage collection with zones") Fixes: b36e4523d4d5 ("netfilter: nf_conncount: fix garbage collection confirm race") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conncount.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index 6b985afa4981e..aca365f9f0aea 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -207,8 +207,8 @@ static int __nf_conncount_add(struct net *net, /* Not found, but might be about to be confirmed */ if (PTR_ERR(found) == -EAGAIN) { if (nf_ct_tuple_equal(&conn->tuple, &tuple) && - nf_ct_zone_id(&conn->zone, conn->zone.dir) == - nf_ct_zone_id(zone, zone->dir)) + nf_ct_zone_id(&conn->zone, IP_CT_DIR_ORIGINAL) == + nf_ct_zone_id(zone, IP_CT_DIR_ORIGINAL)) goto out_put; /* already exists */ } else { collect++; @@ -219,7 +219,7 @@ static int __nf_conncount_add(struct net *net, found_ct = nf_ct_tuplehash_to_ctrack(found); if (nf_ct_tuple_equal(&conn->tuple, &tuple) && - nf_ct_zone_equal(found_ct, zone, zone->dir)) { + nf_ct_zone_equal(found_ct, zone, IP_CT_DIR_ORIGINAL)) { /* * We should not see tuples twice unless someone hooks * this into a table without "-p tcp --syn". From b2dbbedfa935cc60f127f0153c741effba37181c Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Tue, 7 Jul 2026 19:00:14 +0800 Subject: [PATCH 1787/3297] netfilter: ecache: fix inverted time_after() check commit b06163ce52ec0561f202fbfb9b08090cb61f512e upstream. ecache_work_evict_list() redelivers DESTROY events for conntracks that were moved to the per-netns dying_list after event delivery failed. It sets a 10ms deadline: stop = jiffies + ECACHE_MAX_JIFFIES but then tests: time_after(stop, jiffies) This condition is true while the deadline is still in the future, so the worker returns STATE_RESTART after the first successful redelivery in the usual case. ecache_work() maps STATE_RESTART to delay 0, which turns the redelivery path into one dying conntrack per workqueue dispatch and makes the sent > 16 batching/cond_resched() path effectively unreachable. A conntrack netlink listener whose receive queue is congested can make DESTROY event delivery fail with -ENOBUFS. With sustained conntrack churn, entries then accumulate on the dying_list and are only drained at the degraded one-entry-per-dispatch rate once delivery succeeds again, wasting CPU on back-to-back workqueue reschedules and prolonging conntrack memory/resource pressure. In a KASAN QEMU test with CONFIG_NF_CONNTRACK_EVENTS=y and nf_conntrack.enable_hooks=1, a congested DESTROY listener caused 8192 nf_ct_delete() calls to return false and move entries to the dying_list. After closing the listener, the unfixed kernel needed 7670 ecache_work() entries to destroy 7669 conntracks. With this change, the same 8192 entries were destroyed by 2 ecache_work() entries. Swap the comparison so the worker restarts only after the deadline has expired. Fixes: 2ed3bf188b33 ("netfilter: ecache: use dedicated list for event redelivery") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_ecache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 9df159448b897..cc8d8e85169fb 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -77,7 +77,7 @@ static enum retry_state ecache_work_evict_list(struct nf_conntrack_net *cnet) hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, &evicted_list); - if (time_after(stop, jiffies)) { + if (time_after(jiffies, stop)) { ret = STATE_RESTART; break; } From 679ced28a9dc2f6dc679eb05027d779693e60902 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sat, 13 Jun 2026 18:27:15 +0800 Subject: [PATCH 1788/3297] netfilter: xt_nat: reject unsupported target families commit 5d1a2240935ea47e2673d0ea17fdb058e4dc91dd upstream. xt_nat SNAT and DNAT target handlers assume IP-family conntrack state is present and can dereference a NULL pointer when instantiated from an unsupported family through nft_compat. A bridge-family compat rule can therefore trigger a NULL-dereference in nf_nat_setup_info(). Reject non-IP families in xt_nat_checkentry() so unsupported targets cannot be installed. Keep NFPROTO_INET allowed for valid inet NAT compat users and leave the runtime fast path unchanged. [ The crash was fixed via 9dbba7e694ec ("netfilter: nft_compat: ebtables emulation must reject non-bridge targets"), so this patch is no longer critical. Nevertheless, NAT is only relevant for ipv4/ipv6, so this extra family check is a good idea in any case. ] Fixes: c7232c9979cb ("netfilter: add protocol independent NAT core") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_nat.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c index b4f7bbc3f3caf..51c7f7ce88d9a 100644 --- a/net/netfilter/xt_nat.c +++ b/net/netfilter/xt_nat.c @@ -26,6 +26,15 @@ static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par) static int xt_nat_checkentry(const struct xt_tgchk_param *par) { + switch (par->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return -EINVAL; + } + return nf_ct_netns_get(par->net, par->family); } From 1c4f67c89fd27c4df4c70b135c2c59627698b3c0 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 8 Jul 2026 18:11:50 +0000 Subject: [PATCH 1789/3297] netfilter: bridge: fix stale prevhdr pointer in br_ip6_fragment() commit 86f3ce81dd2b4b0aa2c3016c989a943e4b1b643d upstream. br_ip6_fragment() gets prevhdr, a pointer into the skb head, from ip6_find_1stfragopt(), then calls skb_checksum_help(). For a cloned skb skb_checksum_help() reallocates the head via pskb_expand_head(), leaving prevhdr dangling. It is later dereferenced in ip6_frag_next(), causing a use-after-free write. Save prevhdr's offset before skb_checksum_help() and recompute it after, like commit ef0efcd3bd3f ("ipv6: Fix dangling pointer when ipv6 fragment"). BUG: KASAN: slab-use-after-free in ip6_frag_next (net/ipv6/ip6_output.c:857) Write of size 1 at addr ffff888013ff5016 by task exploit/141 Call Trace: ... kasan_report (mm/kasan/report.c:595) ip6_frag_next (net/ipv6/ip6_output.c:857) br_ip6_fragment (net/ipv6/netfilter.c:212) nf_ct_bridge_post (net/bridge/netfilter/nf_conntrack_bridge.c:407) nf_hook_slow (net/netfilter/core.c:619) br_forward_finish (net/bridge/br_forward.c:66) __br_forward (net/bridge/br_forward.c:115) maybe_deliver (net/bridge/br_forward.c:191) br_flood (net/bridge/br_forward.c:245) br_handle_frame_finish (net/bridge/br_input.c:229) br_handle_frame (net/bridge/br_input.c:442) ... packet_sendmsg (net/packet/af_packet.c:3114) ... do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Kernel panic - not syncing: Fatal exception in interrupt Fixes: 764dd163ac92 ("netfilter: nf_conntrack_bridge: add support for IPv6") Cc: stable@vger.kernel.org Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 46540a5a43317..c3065439f6242 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -134,7 +134,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, ktime_t tstamp = skb->tstamp; struct ip6_frag_state state; u8 *prevhdr, nexthdr = 0; - unsigned int mtu, hlen; + unsigned int mtu, hlen, nexthdr_offset; int hroom, err = 0; __be32 frag_id; @@ -143,6 +143,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, goto blackhole; hlen = err; nexthdr = *prevhdr; + nexthdr_offset = prevhdr - skb_network_header(skb); mtu = skb->dev->mtu; if (frag_max_size > mtu || @@ -161,6 +162,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, (err = skb_checksum_help(skb))) goto blackhole; + prevhdr = skb_network_header(skb) + nexthdr_offset; hroom = LL_RESERVED_SPACE(skb->dev); if (skb_has_frag_list(skb)) { unsigned int first_len = skb_pagelen(skb); From c4d6442ac3ed00041fe4e1df715717ed78a7d37f Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Apr 2026 22:15:26 +0800 Subject: [PATCH 1790/3297] gpu: host1x: Fix device reference leak in host1x_device_parse_dt() error path commit e75717f9aec04355777be41070890c6a815c76df upstream. After device_initialize(), the embedded struct device in struct host1x_device should be released through the device core with put_device(). In host1x_device_add(), if host1x_device_parse_dt() fails, the current error path frees the object directly with kfree(device). That bypasses the normal device lifetime handling and leaks the reference held on the embedded struct device. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device() in the host1x_device_parse_dt() failure path. Fixes: f4c5cf88fbd50 ("gpu: host1x: Provide a proper struct bus_type") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260413141526.2961841-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/host1x/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 8564b6aa30b03..3801eab63282f 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -452,7 +452,7 @@ static int host1x_device_add(struct host1x *host1x, err = host1x_device_parse_dt(device, driver); if (err < 0) { - kfree(device); + put_device(&device->dev); return err; } From c6854d9f4e1bd7f0fc16f94bb080feebc3b1ed16 Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Fri, 1 May 2026 18:10:54 +0530 Subject: [PATCH 1791/3297] soc: ti: k3-ringacc: Fix access mode for k3_ringacc_ring_pop_tail_io/proxy commit b920352cfd2b0fcd1249ff006618c939b64fc8f7 upstream. k3_ringacc_ring_pop_tail_io() and k3_ringacc_ring_pop_tail_proxy() incorrectly use K3_RINGACC_ACCESS_MODE_POP_HEAD instead of K3_RINGACC_ACCESS_MODE_POP_TAIL. This will result in ring elements being popped in the reverse order of that which the caller expects. Fix this. Fixes: 3277e8aa2504 ("soc: ti: k3: add navss ringacc driver") Cc: stable@vger.kernel.org Signed-off-by: Siddharth Vadapalli Reviewed-by: Hari Prasath Gujulan Elango Link: https://patch.msgid.link/20260501124129.362192-1-s-vadapalli@ti.com Signed-off-by: Nishanth Menon Signed-off-by: Greg Kroah-Hartman --- drivers/soc/ti/k3-ringacc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c index 7602b8a909b05..e2ca380812d29 100644 --- a/drivers/soc/ti/k3-ringacc.c +++ b/drivers/soc/ti/k3-ringacc.c @@ -1012,7 +1012,7 @@ static int k3_ringacc_ring_pop_head_proxy(struct k3_ring *ring, void *elem) static int k3_ringacc_ring_pop_tail_proxy(struct k3_ring *ring, void *elem) { return k3_ringacc_ring_access_proxy(ring, elem, - K3_RINGACC_ACCESS_MODE_POP_HEAD); + K3_RINGACC_ACCESS_MODE_POP_TAIL); } static int k3_ringacc_ring_access_io(struct k3_ring *ring, void *elem, @@ -1083,7 +1083,7 @@ static int k3_ringacc_ring_pop_io(struct k3_ring *ring, void *elem) static int k3_ringacc_ring_pop_tail_io(struct k3_ring *ring, void *elem) { return k3_ringacc_ring_access_io(ring, elem, - K3_RINGACC_ACCESS_MODE_POP_HEAD); + K3_RINGACC_ACCESS_MODE_POP_TAIL); } /* From e9cdf741ffcb4630fe75c4910457b749d86664bd Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Fri, 27 Mar 2026 08:12:25 +0800 Subject: [PATCH 1792/3297] soc: fsl: qe: panic on ioremap() failure in qe_reset() commit a0fe29d20e7822182e12324905af5115c1b3aed3 upstream. When ioremap() fails in qe_reset(), the global pointer qe_immr remains NULL, leading to a subsequent NULL pointer dereference when the pointer is accessed. Since this happens early in the boot process, a failure to map a few bytes of I/O memory indicates a fatal error from which the system cannot recover. Follow the same pattern as qe_sdma_init() and panic immediately when ioremap() fails. This avoids a silent NULL pointer dereference later and makes the error explicit. Fixes: 986585385131 ("[POWERPC] Add QUICC Engine (QE) infrastructure") Cc: stable@vger.kernel.org Signed-off-by: Wang Jun <1742789905@qq.com> Link: https://lore.kernel.org/r/tencent_FED49CF5331CC0C7910618883332A08E2606@qq.com [chleroy: Rearranged change to reduce churn] Signed-off-by: Christophe Leroy (CS GROUP) Signed-off-by: Greg Kroah-Hartman --- drivers/soc/fsl/qe/qe.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 70b6eddb867b9..3ecc4ce9cfa25 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -89,6 +89,9 @@ void qe_reset(void) if (qe_immr == NULL) qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE); + if (!qe_immr) + panic("QE:ioremap failed!"); + qe_snums_init(); qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, From fc633a598206d4f23af782db7c0b5f3a82751d2c Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Thu, 18 Jun 2026 13:55:14 -0400 Subject: [PATCH 1793/3297] selinux: check connect-related permissions on TCP Fast Open commit 44c74d27d1b9aaa99fa8a83640c1223575262b80 upstream. Similar to Landlock, SELinux was not updated when TCP Fast Open support was introduced to ensure connect-related permissions are checked when using TCP Fast Open. Update its socket_sendmsg() hook to call selinux_socket_connect() when MSG_FASTOPEN is passed. Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-security-module/20260616201615.275032-1-hexlabsecurity@proton.me/ Link: https://lore.kernel.org/linux-security-module/20260617180526.15627-2-matthieu@buffet.re/ Reported-by: Bryam Vargas Reported-by: Matthieu Buffet Reported-by: Mikhail Ivanov Signed-off-by: Stephen Smalley Tested-by: Bryam Vargas Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/hooks.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f96ee8f372e3b..099c761578d95 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5242,7 +5242,24 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return sock_has_perm(sock->sk, SOCKET__WRITE); + int rc; + struct sockaddr *const addr = msg->msg_name; + const int addrlen = msg->msg_namelen; + + rc = sock_has_perm(sock->sk, SOCKET__WRITE); + if (rc) + return rc; + + if (addr && (msg->msg_flags & MSG_FASTOPEN) && + (sk_is_tcp(sock->sk) || + (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM && + sock->sk->sk_protocol == IPPROTO_MPTCP))) { + rc = selinux_socket_connect(sock, addr, addrlen); + if (rc) + return rc; + } + + return 0; } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, From d61a80b17254be7230bc5544f8e62ddf21ab38e2 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 25 Jun 2026 23:53:36 +0000 Subject: [PATCH 1794/3297] selinux: avoid sk_socket dereference in selinux_sctp_bind_connect() commit 56acfeb10019e200ab6787d01f8d7cbe0f01526f upstream. selinux_sctp_bind_connect() dereferences sk->sk_socket to pass a struct socket * to selinux_socket_bind() and selinux_socket_connect_helper(). However, when the hook is invoked from the ASCONF softirq path (sctp_process_asconf), there is no file reference guaranteeing that sk->sk_socket is non-NULL. The setsockopt callers (bindx, connectx, set_primary, sendmsg connect) hold a file reference and are not affected. Both selinux_socket_bind() and selinux_socket_connect_helper() immediately resolve sock->sk, never using the struct socket * for anything else. Refactor the inner logic into helpers that take a struct sock * directly so that selinux_sctp_bind_connect() never needs to touch sk->sk_socket at all. Cc: stable@vger.kernel.org Fixes: d452930fd3b9 ("selinux: Add SCTP support") Suggested-by: Stephen Smalley Signed-off-by: Tristan Madani Reviewed-by: Stephen Smalley Tested-by: Stephen Smalley Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/hooks.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 099c761578d95..8cd07bcb851b8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4974,9 +4974,8 @@ static int selinux_socket_socketpair(struct socket *socka, Need to determine whether we should perform a name_bind permission check between the socket and the port number. */ -static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) +static int __selinux_socket_bind(struct sock *sk, struct sockaddr *address, int addrlen) { - struct sock *sk = sock->sk; struct sk_security_struct *sksec = selinux_sock(sk); u16 family; int err; @@ -5106,13 +5105,17 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in return -EAFNOSUPPORT; } +static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) +{ + return __selinux_socket_bind(sock->sk, address, addrlen); +} + /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst */ -static int selinux_socket_connect_helper(struct socket *sock, +static int selinux_socket_connect_helper(struct sock *sk, struct sockaddr *address, int addrlen) { - struct sock *sk = sock->sk; struct sk_security_struct *sksec = selinux_sock(sk); int err; @@ -5201,7 +5204,7 @@ static int selinux_socket_connect(struct socket *sock, int err; struct sock *sk = sock->sk; - err = selinux_socket_connect_helper(sock, address, addrlen); + err = selinux_socket_connect_helper(sk, address, addrlen); if (err) return err; @@ -5703,13 +5706,11 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, int len, err = 0, walk_size = 0; void *addr_buf; struct sockaddr *addr; - struct socket *sock; if (!selinux_policycap_extsockclass()) return 0; /* Process one or more addresses that may be IPv4 or IPv6 */ - sock = sk->sk_socket; addr_buf = address; while (walk_size < addrlen) { @@ -5738,14 +5739,14 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, case SCTP_PRIMARY_ADDR: case SCTP_SET_PEER_PRIMARY_ADDR: case SCTP_SOCKOPT_BINDX_ADD: - err = selinux_socket_bind(sock, addr, len); + err = __selinux_socket_bind(sk, addr, len); break; /* Connect checks */ case SCTP_SOCKOPT_CONNECTX: case SCTP_PARAM_SET_PRIMARY: case SCTP_PARAM_ADD_IP: case SCTP_SENDMSG_CONNECT: - err = selinux_socket_connect_helper(sock, addr, len); + err = selinux_socket_connect_helper(sk, addr, len); if (err) return err; From 3a134c3fb5f0d21871d65d0855d8bf6e5900645d Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Tue, 14 Jul 2026 14:57:59 +0200 Subject: [PATCH 1795/3297] selinux: fix incorrect execmem checks on overlayfs commit 9fe595fad54d4ac6a402edb3f60bec859d52cea6 upstream. The commit fixing the overlayfs mmap() and mprotect() access checks failed to skip the execmem check in __file_map_prot_check() for the case where the "mounter check" is being performed. This check should be performed only against the credentials of the task that is calling mmap()/mprotect(), since it doesn't pertain to the file itself, but rather just gates the ability of the calling task to get an executable memory mapping in general. The purpose of the "mounter check" is to guard against using an overlayfs mount to gain file access that would otherwise be denied to the mounter. For execmem this is not relevant, as there is no further file access granted based on it (notice that the file's context is not used as the target in the check), so checking it also against the mounter credentials would be incorrect. Fix this by passing a boolean to [__]file_map_prot_check() and selinux_mmap_file_common() that indicates if we are doing the "mounter check" and skiping the execmem check in that case. Since this boolean also indicates if we use current_cred() or the mounter cred as the subject, also remove the "cred" argument from these functions and determine it based on the boolean and the file struct. Cc: stable@vger.kernel.org Fixes: 82544d36b172 ("selinux: fix overlayfs mmap() and mprotect() access checks") Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/selinux/hooks.c | 42 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8cd07bcb851b8..af188e19c1308 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3950,9 +3950,9 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, static int default_noexec __ro_after_init; -static int __file_map_prot_check(const struct cred *cred, - const struct file *file, unsigned long prot, - bool shared, bool bf_user_file) +static int __file_map_prot_check(const struct file *file, unsigned long prot, + bool shared, bool mounter_check, + bool bf_user_file) { struct inode *inode = NULL; bool prot_exec = prot & PROT_EXEC; @@ -3965,10 +3965,10 @@ static int __file_map_prot_check(const struct cred *cred, inode = file_inode(file); } - if (default_noexec && prot_exec && + if (!mounter_check && default_noexec && prot_exec && (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { int rc; - u32 sid = cred_sid(cred); + u32 sid = current_sid(); /* * We are making executable an anonymous mapping or a private @@ -3981,6 +3981,8 @@ static int __file_map_prot_check(const struct cred *cred, } if (file) { + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); /* "read" always possible, "write" only if shared */ u32 av = FILE__READ; if (shared && prot_write) @@ -3994,11 +3996,11 @@ static int __file_map_prot_check(const struct cred *cred, return 0; } -static inline int file_map_prot_check(const struct cred *cred, - const struct file *file, - unsigned long prot, bool shared) +static inline int file_map_prot_check(const struct file *file, + unsigned long prot, bool shared, + bool mounter_check) { - return __file_map_prot_check(cred, file, prot, shared, false); + return __file_map_prot_check(file, prot, shared, mounter_check, false); } static int selinux_mmap_addr(unsigned long addr) @@ -4014,12 +4016,14 @@ static int selinux_mmap_addr(unsigned long addr) return rc; } -static int selinux_mmap_file_common(const struct cred *cred, struct file *file, - unsigned long prot, bool shared) +static int selinux_mmap_file_common(struct file *file, unsigned long prot, + bool shared, bool mounter_check) { if (file) { int rc; struct common_audit_data ad; + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; @@ -4028,15 +4032,16 @@ static int selinux_mmap_file_common(const struct cred *cred, struct file *file, return rc; } - return file_map_prot_check(cred, file, prot, shared); + return file_map_prot_check(file, prot, shared, mounter_check); } static int selinux_mmap_file(struct file *file, unsigned long reqprot __always_unused, unsigned long prot, unsigned long flags) { - return selinux_mmap_file_common(current_cred(), file, prot, - (flags & MAP_TYPE) == MAP_SHARED); + return selinux_mmap_file_common(file, prot, + (flags & MAP_TYPE) == MAP_SHARED, + false); } /** @@ -4068,8 +4073,9 @@ static int selinux_mmap_backing_file(struct vm_area_struct *vma, if (vma->vm_flags & VM_EXEC) prot |= PROT_EXEC; - return selinux_mmap_file_common(backing_file->f_cred, backing_file, - prot, vma->vm_flags & VM_SHARED); + return selinux_mmap_file_common(backing_file, prot, + vma->vm_flags & VM_SHARED, + true); } static int selinux_file_mprotect(struct vm_area_struct *vma, @@ -4130,11 +4136,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, } } - rc = __file_map_prot_check(cred, file, prot, shared, backing_file); + rc = __file_map_prot_check(file, prot, shared, false, backing_file); if (rc) return rc; if (backing_file) { - rc = file_map_prot_check(file->f_cred, file, prot, shared); + rc = file_map_prot_check(file, prot, shared, true); if (rc) return rc; } From 6dd51d84a9502553e58beade72823258871b8111 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 25 May 2026 01:55:53 +0200 Subject: [PATCH 1796/3297] leds: uleds: Fix potential buffer overread commit c19fe864f667afc49d1391d764e20b66555bcf7a upstream. The name string supplied by userspace is not guaranteed to be null-terminated, so using strchr() on it might result in a buffer overread. The same thing will happen when said string is used by the LED class device. Fix this by using strnchr() instead and explicitly check that the name string is properly null-terminated. Cc: stable@vger.kernel.org Fixes: e381322b0190 ("leds: Introduce userspace LED class driver") Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260524235553.189134-1-W_Armin@gmx.de Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/leds/uleds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c index 374a841f18c37..48ca73c106bc0 100644 --- a/drivers/leds/uleds.c +++ b/drivers/leds/uleds.c @@ -102,7 +102,8 @@ static ssize_t uleds_write(struct file *file, const char __user *buffer, name = udev->user_dev.name; if (!name[0] || !strcmp(name, ".") || !strcmp(name, "..") || - strchr(name, '/')) { + strnchr(name, sizeof(udev->user_dev.name), '/') || + !strnchr(name, sizeof(udev->user_dev.name), '\0')) { ret = -EINVAL; goto out; } From 16a42c88c4667fa0bd944e438a667e059551f1f1 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 16 Apr 2026 00:26:27 +0800 Subject: [PATCH 1797/3297] mfd: sm501: Fix reference leak on failed device registration commit 8c2f0b42fc252e1bf1c7746447091a468e784ca1 upstream. When platform_device_register() fails in sm501_register_device(), the embedded struct device in pdev has already been initialized by device_initialize(), but the failure path only reports the error and returns without dropping the device reference for the current platform device: sm501_register_device() -> platform_device_register(pdev) -> device_initialize(&pdev->dev) -> setup_pdev_dma_masks(pdev) -> platform_device_add(pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: b6d6454fdb66f ("[PATCH] mfd: SM501 core driver") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260415162627.3558789-1-lgs201920130244@gmail.com Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/sm501.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 50bf3260f65d4..c56f5005eb913 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -704,9 +704,11 @@ static int sm501_register_device(struct sm501_devdata *sm, if (ret >= 0) { dev_dbg(sm->dev, "registered %s\n", pdev->name); list_add_tail(&smdev->list, &sm->devices); - } else + } else { dev_err(sm->dev, "error registering %s (%d)\n", pdev->name, ret); + platform_device_put(pdev); + } return ret; } From 19ffeb30fdfce63f8d6aca71bcdddb3f69d46278 Mon Sep 17 00:00:00 2001 From: Ali Ahmet MEMIS Date: Sun, 26 Apr 2026 08:09:28 -0700 Subject: [PATCH 1798/3297] tools/power/x86/intel-speed-select: Harden daemon pidfile open commit 607af438e6430893a822964c841a1994b33acccc upstream. Avoid symlink-based pidfile clobbering by opening the pidfile with O_NOFOLLOW and validating it with fstat() before locking/writing. The daemon currently uses a fixed pidfile path under /tmp. A local unprivileged user can pre-create a symlink at that path and cause a root-run daemon instance to write into an attacker-chosen file. Fixes: 7fd786dfbd2c ("tools/power/x86/intel-speed-select: OOB daemon mode") Signed-off-by: Ali Ahmet MEMIS Signed-off-by: Srinivas Pandruvada Cc: stable@kernel.org Signed-off-by: Greg Kroah-Hartman --- tools/power/x86/intel-speed-select/isst-daemon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index 66df21b2b5737..acedb74328494 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -148,6 +148,7 @@ static void daemonize(char *rundir, char *pidfile) { int pid, sid, i; char str[10]; + struct stat st; struct sigaction sig_actions; sigset_t sig_set; int ret; @@ -200,11 +201,17 @@ static void daemonize(char *rundir, char *pidfile) if (ret == -1) exit(EXIT_FAILURE); - pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); + pid_file_handle = open(pidfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (pid_file_handle == -1) { /* Couldn't open lock file */ exit(1); } + + if (fstat(pid_file_handle, &st) == -1) + exit(1); + + if (!S_ISREG(st.st_mode)) + exit(1); /* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { From 1a1d6e3ef6cf5851cf0c70dab37de94c80699852 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 23 Jun 2026 07:15:05 -0700 Subject: [PATCH 1799/3297] x86/video: Only fall back to vga_default_device() without screen info commit 596b3678326d3d1aed7c19423b6746f1ce09688a upstream. Some multi GPU systems may have a VGA compatible device, but that might not be used for display. If due to enumeration order this device is found before the one actually used for display then multiple devices may show the boot_display attribute, confusing userspace. When screen info is valid, use it exclusively to find the primary device so that only the device backing the framebuffer is reported. Only when no framebuffer has been set up does it make sense to fall back to the default VGA device. This ensures at most one primary graphics device, preferably the one with the framebuffer. Fixes: ad90860bd10ee ("fbcon: Use screen info to find primary device") Closes: https://lore.kernel.org/linux-pci/20260618081803.2790848-1-aaron.ma@canonical.com/#t Reported-by: Aaron Ma Suggested-by: Thomas Zimmermann Signed-off-by: Mario Limonciello Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Thomas Zimmermann Tested-by: Aaron Ma Cc: Link: https://patch.msgid.link/20260623141505.1816786-1-mario.limonciello@amd.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/video/video-common.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/arch/x86/video/video-common.c b/arch/x86/video/video-common.c index e0aeee99bc99e..e5fb3cf885fbd 100644 --- a/arch/x86/video/video-common.c +++ b/arch/x86/video/video-common.c @@ -43,21 +43,26 @@ bool video_is_primary_device(struct device *dev) if (!pci_is_display(pdev)) return false; - if (pdev == vga_default_device()) - return true; - #ifdef CONFIG_SCREEN_INFO numres = screen_info_resources(si, res, ARRAY_SIZE(res)); - for (i = 0; i < numres; ++i) { - if (!(res[i].flags & IORESOURCE_MEM)) - continue; + if (numres > 0) { + for (i = 0; i < numres; ++i) { + if (!(res[i].flags & IORESOURCE_MEM)) + continue; + + if (pci_find_resource(pdev, &res[i])) + return true; + } - if (pci_find_resource(pdev, &res[i])) - return true; + return false; } #endif - return false; + /* + * No framebuffer was set up by the firmware/bootloader, so fall back + * to the default VGA device. + */ + return pdev == vga_default_device(); } EXPORT_SYMBOL(video_is_primary_device); From f7c67c97b37c11f2a95c204092fb8159f2779e8f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 13 Jul 2026 21:49:25 +0200 Subject: [PATCH 1800/3297] x86/boot: Validate console=uart8250 baud rate to fix early boot hang commit ffa0aa5b625fe0bed7463ac613f8b06676ff4542 upstream. When the baud rate is empty, 0, invalid, or overflows to 0 when stored as an int, the system will hang during early boot because of a division by zero in early_serial_init(). Fall back to DEFAULT_BAUD when the resulting baud rate is 0 to prevent an early system hang. Fixes: ce0aa5dd20e4 ("x86, setup: Make the setup code also accept console=uart8250") Signed-off-by: Thorsten Blum Signed-off-by: Ingo Molnar Cc: "H. Peter Anvin" Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260713194924.126472-3-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/early_serial_console.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/boot/early_serial_console.c b/arch/x86/boot/early_serial_console.c index 023bf1c3de8b7..5b83beab89e15 100644 --- a/arch/x86/boot/early_serial_console.c +++ b/arch/x86/boot/early_serial_console.c @@ -117,7 +117,7 @@ static unsigned int probe_baud(int port) static void parse_console_uart8250(void) { char optstr[64], *options; - int baud = DEFAULT_BAUD; + int baud; int port = 0; /* @@ -136,10 +136,13 @@ static void parse_console_uart8250(void) else return; - if (options && (options[0] == ',')) - baud = simple_strtoull(options + 1, &options, 0); - else + if (options && (options[0] == ',')) { + baud = simple_strtoull(options + 1, NULL, 0); + if (!baud) + baud = DEFAULT_BAUD; + } else { baud = probe_baud(port); + } if (port) early_serial_init(port, baud); From 394e2bdf7594e9190d6abc2c5e98c48b1a34b017 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 21 Jun 2026 19:00:10 +0200 Subject: [PATCH 1801/3297] x86/boot: Reject too long acpi_rsdp= values commit d130041a7b96f79cd4c7079a6c2431a6db4c9619 upstream. cmdline_find_option() returns the full length of the parsed acpi_rsdp= value. get_cmdline_acpi_rsdp() then silently truncates values which do not fit in the val[] buffer. Prevent boot_kstrtoul() from parsing a truncated value and then the kernel from silently using the wrong RSDP address, see discussion in Link:. Issue a warning so that the user is aware that s/he supplied a malformed value and can get feedback instead of silent crashes. [ bp: Make commit message more precise. ] Fixes: 3c98e71b42a7 ("x86/boot: Add "acpi_rsdp=" early parsing") Signed-off-by: Thorsten Blum Signed-off-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20260617130417.36651-4-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/compressed/acpi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index f196b1d1ddf86..aed27604c11f0 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -184,10 +184,15 @@ static unsigned long get_cmdline_acpi_rsdp(void) char val[MAX_ADDR_LEN] = { }; int ret; - ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN); + ret = cmdline_find_option("acpi_rsdp", val, sizeof(val)); if (ret < 0) return 0; + if (ret >= sizeof(val)) { + warn("acpi_rsdp= value too long; ignoring"); + return 0; + } + if (boot_kstrtoul(val, 16, &addr)) return 0; #endif From 046f6244da9b68e463a849b21446b9424e531491 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 22:04:49 +0530 Subject: [PATCH 1802/3297] perf/x86/amd/brs: Fix kernel address leakage commit 47915e855fb38b42133e31ba917d99565f862154 upstream. A user-only branch stack can contain branches that originate from the kernel. As a result, kernel addresses are exposed to user space even when PERF_SAMPLE_BRANCH_USER is requested. On AMD processors supporting X86_FEATURE_BRS (Zen 3 only), perf can still report entries such as SYSRET/interrupt returns for which the branch-from addresses are in the kernel. E.g. $ perf record -j any,u -c 4000 -e branch-brs -o - -- \ perf bench syscall basic --loop 1000 | \ perf script -i - -F brstack|tr ' ' '\n'| \ grep -E '0x[89a-f][0-9a-f]{15}' ... 0xffffffff810001c4/0x72e2e32955eb/-/-/-/0//- 0xffffffff810001c4/0x72e2d94a9821/-/-/-/0//- 0xffffffff810001c4/0x72e2d94ffa1b/-/-/-/0//- ... BRS provides no hardware branch filtering, so privilege level filtering is performed entirely in software. However, amd_brs_match_plm() only validates the branch-to address against the requested privilege levels. For branches from the kernel to user space, the branch-from address is left unchecked and is leaked. Extend the software filter to also validate the branch-from address, so that any branch record whose branch-from address is in the kernel is dropped when PERF_SAMPLE_BRANCH_USER is requested. Fixes: 8910075d61a3 ("perf/x86/amd: Enable branch sampling priv level filtering") Reported-by: Sashiko Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://patch.msgid.link/f05931c4f89a146c364bd5dc6b8170b1ac611c65.1783701239.git.sandipan.das@amd.com Closes: https://lore.kernel.org/all/20260710110235.F3FD81F000E9@smtp.kernel.org/ Signed-off-by: Greg Kroah-Hartman --- arch/x86/events/amd/brs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/events/amd/brs.c b/arch/x86/events/amd/brs.c index 06f35a6b58a5b..dc564688f3d73 100644 --- a/arch/x86/events/amd/brs.c +++ b/arch/x86/events/amd/brs.c @@ -259,13 +259,13 @@ void amd_brs_disable_all(void) amd_brs_disable(); } -static bool amd_brs_match_plm(struct perf_event *event, u64 to) +static bool amd_brs_match_plm(struct perf_event *event, u64 from, u64 to) { int type = event->attr.branch_sample_type; int plm_k = PERF_SAMPLE_BRANCH_KERNEL | PERF_SAMPLE_BRANCH_HV; int plm_u = PERF_SAMPLE_BRANCH_USER; - if (!(type & plm_k) && kernel_ip(to)) + if (!(type & plm_k) && (kernel_ip(to) || kernel_ip(from))) return 0; if (!(type & plm_u) && !kernel_ip(to)) @@ -338,11 +338,11 @@ void amd_brs_drain(void) */ to = (u64)(((s64)to << shift) >> shift); - if (!amd_brs_match_plm(event, to)) - continue; - rdmsrq(brs_from(brs_idx), from); + if (!amd_brs_match_plm(event, from, to)) + continue; + perf_clear_branch_entry_bitfields(br+nr); br[nr].from = from; From 5ab0eba9c8819506bcb72348fd701f8a9006f95e Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 16:15:27 +0530 Subject: [PATCH 1803/3297] perf/x86/amd/lbr: Fix kernel address leakage commit 2a892294b83f541115c94b0bb637f39bef187657 upstream. A user-only branch stack can contain branches that originate from the kernel. As a result, kernel addresses are exposed to user space even when PERF_SAMPLE_BRANCH_USER is requested. On AMD processors supporting X86_FEATURE_AMD_LBR_V2, perf can still report SYSRET/ERET entries for which the branch-from addresses are in the kernel. E.g. $ perf record -e cycles -o - -j any,save_type,u -- \ perf bench syscall basic --loop 1000 | \ perf script -i - -F brstack|tr ' ' '\n'| \ grep -E '0x[89a-f][0-9a-f]{15}' ... 0xffffffff81001268/0x717a90a38f1a/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a39157/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a2c628/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a41b60/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a90a260db/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8bef1c30/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH 0xffffffff81001268/0x717a8e4d3c90/M/-/-/0/ERET/NON_SPEC_CORRECT_PATH ... The reason is that the hardware filter only considers the privilege level applicable to the branch target. Extend software filtering to also validate the branch-from addresses against br_sel, so that any branch record whose branch-from address is in the kernel is dropped when PERF_SAMPLE_BRANCH_USER is requested. Fixes: f4f925dae741 ("perf/x86/amd/lbr: Add LbrExtV2 hardware branch filter support") Reported-by: Ian Rogers Signed-off-by: Sandipan Das Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Peter Zijlstra Link: https://patch.msgid.link/a898a29725f6b2f30518354cdc2e432db66c43cf.1783680119.git.sandipan.das@amd.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/events/amd/lbr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/events/amd/lbr.c b/arch/x86/events/amd/lbr.c index d24da377df776..b9722e60d8bc7 100644 --- a/arch/x86/events/amd/lbr.c +++ b/arch/x86/events/amd/lbr.c @@ -125,7 +125,8 @@ static void amd_pmu_lbr_filter(void) } /* If type does not correspond, then discard */ - if (type == X86_BR_NONE || (br_sel & type) != type) { + if (type == X86_BR_NONE || (br_sel & type) != type || + (!(br_sel & X86_BR_KERNEL) && kernel_ip(cpuc->lbr_entries[i].from))) { cpuc->lbr_entries[i].from = 0; /* mark invalid */ compress = true; } From 221ee479a49fb2fcc5d64b8eb94bd1d70e622280 Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Tue, 16 Jun 2026 23:47:33 +0800 Subject: [PATCH 1804/3297] cpufreq: schedutil: Fix uncleared need_freq_update on the .adjust_perf() path commit 9ef450ca74e43dacf9a2a15db7a851052c78dcf0 upstream. The need_freq_update flag makes sugov_should_update_freq() return true regardless of the rate_limit_us throttling, and is cleared in sugov_update_next_freq(). sugov_update_single_freq() and sugov_update_shared() go through that helper, so the flag does not persist there. However, sugov_update_single_perf(), used by drivers implementing the .adjust_perf() callback (e.g. intel_pstate or amd-pstate in passive mode) calls cpufreq_driver_adjust_perf() directly and never goes through sugov_update_next_freq(), so the need_freq_update flag is not cleared in that path. Before commit 75da043d8f88 ("cpufreq/sched: Set need_freq_update in ignore_dl_rate_limit()"), this was effectively harmless because sugov_should_update_freq() still honored the rate limit even when need_freq_update was set. After that change, the flag forces sugov_should_update_freq() to always return true, so once set, it stays effective indefinitely on the .adjust_perf() path. As a result, cpufreq_driver_adjust_perf() gets called on every scheduler utilization update (with the runqueue lock held) rather than being throttled by rate_limit_us, even if the driver itself may skip redundant hardware updates. Clear need_freq_update at the end of the adjust_perf path as well. Fixes: 75da043d8f88 ("cpufreq/sched: Set need_freq_update in ignore_dl_rate_limit()") Signed-off-by: Zhongqiu Han Reviewed-by: Hongyan Xia Reviewed-by: Christian Loehle Cc: All applicable [ rjw: Subject and changelog edits ] Link: https://patch.msgid.link/20260616154733.2405236-1-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- kernel/sched/cpufreq_schedutil.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 307f3076635eb..e82bb019b9202 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -486,6 +486,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, sg_cpu->util, max_cap); + sg_policy->need_freq_update = false; sg_policy->last_freq_update_time = time; } From 8514585aa9553b648a0b19fa38083c5a0af4cce8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Jul 2026 19:19:55 +0200 Subject: [PATCH 1805/3297] cpufreq: intel_pstate: Set non-turbo capacity to HWP_GUARANTEED_PERF() commit 27d80e0f8b8dff97503fc0061754b1d3800cb961 upstream. Setting cpu->capacity_perf to cpu->pstate.max_pstate_physical in the "no turbo" case is inconsistent with what happens elsewhere in the driver and causes arch_scale_cpu_capacity() to be incorrect. It also skews arch_scale_freq_capacity() which ends up differing from 1024 for the guaranteed P-state. Address that by setting capacity_perf to HWP_GUARANTEED_PERF() in the "no turbo" case. Fixes: 929ebc93ccaa ("cpufreq: intel_pstate: Set asymmetric CPU capacity on hybrid systems") Signed-off-by: Rafael J. Wysocki Tested-by: Ricardo Neri Cc: All applicable Link: https://patch.msgid.link/12928972.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/intel_pstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 532e85ab0619b..e6d6a30cf6c90 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1054,12 +1054,14 @@ static void hybrid_clear_cpu_capacity(unsigned int cpunum) static void hybrid_get_capacity_perf(struct cpudata *cpu) { + u64 hwp_cap = READ_ONCE(cpu->hwp_cap_cached); + if (READ_ONCE(global.no_turbo)) { - cpu->capacity_perf = cpu->pstate.max_pstate_physical; + cpu->capacity_perf = HWP_GUARANTEED_PERF(hwp_cap); return; } - cpu->capacity_perf = HWP_HIGHEST_PERF(READ_ONCE(cpu->hwp_cap_cached)); + cpu->capacity_perf = HWP_HIGHEST_PERF(hwp_cap); } static void hybrid_set_capacity_of_cpus(void) From fa1ebae4206e6afc8caa642e9eb1bbe39a9a724f Mon Sep 17 00:00:00 2001 From: Sumanth Korikkar Date: Mon, 6 Jul 2026 12:46:31 +0200 Subject: [PATCH 1806/3297] s390/perf_cpum_cf: Add missing array_index_nospec() to __hw_perf_event_init() commit 49145bce539117db4b6e9e83c0e5ef528e361050 upstream. ev variable is userspace controlled via event->attr.config and used as an array index after bounds checking, but without speculation barriers. Add the missing array_index_nospec() call to prevent speculative execution. Cc: stable@vger.kernel.org Fixes: 212188a596d1 ("[S390] perf: add support for s390x CPU counters") Signed-off-by: Sumanth Korikkar Reviewed-by: Ilya Leoshkevich Acked-by: Thomas Richter Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/perf_cpum_cf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 04457d88e5892..fbe21b1db3eed 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -769,6 +770,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) return -EOPNOTSUPP; + ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_user)); ev = cpumf_generic_events_user[ev]; } } else if (!attr->exclude_kernel && attr->exclude_user) { @@ -779,6 +781,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) return -EOPNOTSUPP; + ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_basic)); ev = cpumf_generic_events_basic[ev]; } } From e6b43acd34b219b807e65096ce207b087942779d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:45:41 +0200 Subject: [PATCH 1807/3297] batman-adv: gw: acquire ethernet header only after skb realloc commit 77880a3be88d378d60cc1e8f8ec70430e2ed0518 upstream. The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: 6c413b1c22a2 ("batman-adv: send every DHCP packet as bat-unicast") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/gateway_client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index ff341c270d915..39d03f1cc0196 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -683,12 +683,13 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct batadv_gw_node *gw_node = NULL; struct batadv_gw_node *curr_gw = NULL; struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; - struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct ethhdr *ethhdr; bool out_of_range = false; u8 curr_tq_avg; unsigned short vid; vid = batadv_get_vid(skb, 0); + ethhdr = (struct ethhdr *)skb->data; if (is_multicast_ether_addr(ethhdr->h_dest)) goto out; From 85a71a81854e0e191ad0e533eabb4eff54866feb Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 06:44:13 +0200 Subject: [PATCH 1808/3297] batman-adv: retrieve ethhdr after potential skb realloc on RX commit 035e1fed892d3d06002a73ff73668f618a514644 upstream. pskb_may_pull() in batadv_interface_rx() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. This was done correctly for the VLAN header but missed for the ethernet header which is later used for the TT and AP isolation handling. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Fixes: c78296665c3d ("batman-adv: Check skb size before using encapsulated ETH+VLAN header") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/mesh-interface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index dcdc82f6af8cf..8a0423510d96e 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -432,6 +432,7 @@ void batadv_interface_rx(struct net_device *mesh_iface, if (!pskb_may_pull(skb, VLAN_ETH_HLEN)) goto dropped; + ethhdr = eth_hdr(skb); vhdr = skb_vlan_eth_hdr(skb); /* drop batman-in-batman packets to prevent loops */ From b8afcf799b2cc92c41beebd029e53ed18960184a Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:35:35 +0200 Subject: [PATCH 1809/3297] batman-adv: access unicast_ttvn skb->data only after skb realloc commit 7141990add3f75436f2933cb310654cad3b1e3e9 upstream. The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. This was done correctly for the ethernet header but missed for the unicast_packet pointer. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/routing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 41951c7a1c50b..44e1f5746ee54 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -855,8 +855,8 @@ static bool batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, if (skb_cow(skb, sizeof(*unicast_packet)) < 0) return false; - unicast_packet = (struct batadv_unicast_packet *)skb->data; vid = batadv_get_vid(skb, hdr_len); + unicast_packet = (struct batadv_unicast_packet *)skb->data; ethhdr = (struct ethhdr *)(skb->data + hdr_len); /* do not reroute multicast frames in a unicast header */ From 3b4c70c40f2e135a50cd38fc61c7d23a296a9981 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:45:41 +0200 Subject: [PATCH 1810/3297] batman-adv: dat: acquire ARP hw source only after skb realloc commit 48067b2ae4504500a7093d9e1e16b42e70330480 upstream. The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: b61ec31c8575 ("batman-adv: Snoop DHCPACKs for DAT") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 860db505e8697..653dfe15c19bf 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1747,6 +1747,7 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, struct ethhdr *ethhdr; __be32 ip_src, yiaddr; unsigned short vid; + int hdr_size_tmp; __be16 proto; u8 *hw_src; @@ -1763,8 +1764,10 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr)) return; + hdr_size_tmp = hdr_size; + vid = batadv_dat_get_vid(skb, &hdr_size_tmp); + ethhdr = (struct ethhdr *)(skb->data + hdr_size); hw_src = ethhdr->h_source; - vid = batadv_dat_get_vid(skb, &hdr_size); batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid); batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); From e5e18886aadd3870e2d84e32a9678317fab1dd9e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 08:45:41 +0200 Subject: [PATCH 1811/3297] batman-adv: bla: reacquire gw address after skb realloc commit cdf3b5af2bc4431e58629e8ad2086b1e9185c761 upstream. The pskb_may_pull() called by batadv_bla_is_backbone_gw() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: 9e794b6bf4a2 ("batman-adv: drop unicast packets from other backbone gw") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/routing.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 44e1f5746ee54..9de5bc552bfdc 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1029,6 +1029,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, hdr_size); batadv_orig_node_put(orig_node_gw); if (is_gw) { + orig_addr_gw = eth_hdr(skb)->h_source; batadv_dbg(BATADV_DBG_BLA, bat_priv, "%s(): Dropped unicast pkt received from another backbone gw %pM.\n", __func__, orig_addr_gw); From 8f76277d02176cd739945bba3379448e2e22e799 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 28 Jun 2026 10:37:07 +0200 Subject: [PATCH 1812/3297] batman-adv: dat: ensure accessible eth_hdr proto field commit 26560c4a03dc4d607331600c187f59ab2df5f341 upstream. When batadv_get_vid() accesses the proto field of the ethernet header, it is not checking if the data itself is accessible. The caller is responsible for it. But in contrast to other call sites, batadv_dat_get_vid() and its caller didn't make sure this is true. This could have caused an out-of-bounds access. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 23 +++++++++++++++++++++++ net/batman-adv/main.c | 3 +++ 2 files changed, 26 insertions(+) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 653dfe15c19bf..6005277c6f6e6 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1066,6 +1066,9 @@ static u16 batadv_arp_get_type(struct batadv_priv *bat_priv, * @skb: the buffer containing the packet to extract the VID from * @hdr_size: the size of the batman-adv header encapsulating the packet * + * The caller must ensure that at least @hdr_size + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: If the packet embedded in the skb is vlan tagged this function * returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS * is returned. @@ -1148,6 +1151,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1243,6 +1250,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1305,6 +1316,10 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) return; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + return; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1353,6 +1368,10 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1807,6 +1826,10 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, if (batadv_forw_packet_is_rebroadcast(forw_packet)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(forw_packet->skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size); diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index a4d33ee0fda59..36ce95059bdc7 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -566,6 +566,9 @@ void batadv_recv_handler_unregister(u8 packet_type) * @skb: the buffer containing the packet * @header_len: length of the batman header preceding the ethernet header * + * The caller must ensure that at least @header_len + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the * skb is vlan tagged. Otherwise BATADV_NO_FLAGS. */ From 9e16b6751a8206de0b865d99bb02771e6751d12d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 11:46:21 +0200 Subject: [PATCH 1813/3297] batman-adv: ensure minimal ethernet header on TX commit 49df66b7993c80b80c7eb9a84ba5b3410c8296a0 upstream. As documented in commit 8bd67ebb50c0 ("net: bridge: xmit: make sure we have at least eth header len bytes"), it is possible by for a local user with eBPF TC hook access to attach a tc filter which truncates the packet and redirects to an batadv interface. But the code assumes that at least ETH_HLEN bytes are available and thus might read outside of the available buffer. The batadv_interface_tx() must therefore always check itself if enough data is available for the ethernet header and don't rely on min_header_len. Cc: stable@vger.kernel.org Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Reported-by: Sashiko Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/mesh-interface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 8a0423510d96e..afd615a8fd441 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -193,6 +193,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; + if (!pskb_may_pull(skb, ETH_HLEN)) + goto dropped; + /* reset control block to avoid left overs from previous users */ memset(skb->cb, 0, sizeof(struct batadv_skb_cb)); From ee878decf9e570c5ff52c88690de75881e7d8f2a Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 19:32:40 +0200 Subject: [PATCH 1814/3297] batman-adv: dat: fix tie-break for candidate selection commit 98052bdaf6ac1639a63ffc10244eeeab1f62ed2b upstream. The original version of the candidate selection for DAT attempted to compare both candidate and max_orig_node to identify which has the smaller MAC address. This comparison is required as tie-break when a hash collision happened. But the used function returned 0 when the function was not equal and a non-zero value when it was equal. As result, the actually selected node was dependent on the order of entries in the orig_hash and not actually on the mac addresses. The last originator in the hash collision would always win. To have a proper ordering, it must diff the actual MAC address bytes and reject the candidate when the diff is not smaller than 0. Cc: stable@vger.kernel.org Fixes: 785ea1144182 ("batman-adv: Distributed ARP Table - create DHT helper functions") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 6005277c6f6e6..c9ef48b5e69eb 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -545,7 +545,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, * the one with the lowest address */ if (tmp_max == max && max_orig_node && - batadv_compare_eth(candidate->orig, max_orig_node->orig)) + memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0) goto out; ret = true; From 6a65ac8a81e903bb4b555c1d13532f5cb0167a4a Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 21:06:23 +0200 Subject: [PATCH 1815/3297] batman-adv: tt: avoid request storms during pending request commit 27c7d40008231ae4140d35501b60087a9de2d2c3 upstream. batadv_send_tt_request() allocates a tt_req_node when none exists for the destination originator node. This should prevent that a multiple TT requests are send at the same time to an originator. But if allocation of the send buffer failed, this request must be cleaned up again. But indicator for such a failure is "ret == false". But the actual implementation is checking for "ret == true". The check must be inverted to not loose the information about the TT request directly after it was attempted to be sent out. This should avoid potential request storms. Cc: stable@vger.kernel.org Fixes: 335fbe0f5d25 ("batman-adv: tvlv - convert tt query packet to use tvlv unicast packets") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/translation-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 83dfd804a143a..79bcce09ca05f 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2963,7 +2963,7 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv, out: batadv_hardif_put(primary_if); - if (ret && tt_req_node) { + if (!ret && tt_req_node) { spin_lock_bh(&bat_priv->tt.req_list_lock); if (!hlist_unhashed(&tt_req_node->list)) { hlist_del_init(&tt_req_node->list); From 2c989ab8e20543212f0f4fc7d7f092a529074b8c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 20:45:24 +0200 Subject: [PATCH 1816/3297] batman-adv: fix VLAN priority offset commit fdb3be00ba4dafa313e699d6b5b90d13f22f3f25 upstream. The batadv_skb_set_priority() receives an SKB with the inner ethernet header at position "offset". When it tries to extract the IPv4 and IPv6 header, it needs to skip the ethernet header to get access to the IP header. But for VLAN header, it performs the access with the struct vlan_ethhdr. This struct contains both both the ethernet header and the VLAN header. It is therefore incorrect to skip over the whole vlan_ethhdr size to get access to the vlan_ethhdr. Cc: stable@vger.kernel.org Fixes: c54f38c9aa22 ("batman-adv: set skb priority according to content") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 36ce95059bdc7..b95fcd17be692 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -354,7 +354,7 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset) switch (ethhdr->h_proto) { case htons(ETH_P_8021Q): - vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr), + vhdr = skb_header_pointer(skb, offset, sizeof(*vhdr), &vhdr_tmp); if (!vhdr) return; From 8f54162e07d3eea1e4ff21464cf2a815d79b6510 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:28:31 +0200 Subject: [PATCH 1817/3297] batman-adv: frag: free unfragmentable packet commit 6b628425aed49a1c7a4ffc997583840fc582d32b upstream. The caller of batadv_frag_send_packet() assume that the skb provided to the function are always consumed. But the pre-check for an empty payload or the zero fragment size returned an error without any further actions. A failed pre-check must use the same error handling code as the rest of the function. Cc: stable@vger.kernel.org Fixes: ee75ed88879a ("batman-adv: Fragment and send skbs larger than mtu") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/fragmentation.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 4779741e7273e..520bf30d89884 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -516,8 +516,10 @@ int batadv_frag_send_packet(struct sk_buff *skb, mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); max_fragment_size = mtu - header_size; - if (skb->len == 0 || max_fragment_size == 0) - return -EINVAL; + if (skb->len == 0 || max_fragment_size == 0) { + ret = -EINVAL; + goto free_skb; + } num_fragments = (skb->len - 1) / max_fragment_size + 1; max_fragment_size = (skb->len - 1) / num_fragments + 1; From c945f6007e7851e74706c72f98763023699bcd97 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 4 Jul 2026 09:46:09 +0200 Subject: [PATCH 1818/3297] batman-adv: clean untagged VLAN on netdev registration failure commit 8669a550c752d86baebc5fdc83b8ff35c4372c0e upstream. When an mesh interface is registered, it creates an untagged struct batadv_meshif_vlan on top of it via the NETDEV_REGISTER notifier. But in this process, another receiver of this notification can veto the registration. The netdev registration will be aborted because of this veto. The register_netdevice() call will try to clean up the net_device using unregister_netdevice_queue() - which only uses the .priv_destructor to free private resources. In this situation, .dellink will not be called. The cleanup of the untagged batadv_meshif_vlan must thefore be done in the destructor to avoid a leak of this object. Cc: stable@vger.kernel.org Fixes: 5d2c05b21337 ("batman-adv: add per VLAN interface attribute framework") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/main.c | 8 ++++++++ net/batman-adv/mesh-interface.c | 13 ++----------- net/batman-adv/mesh-interface.h | 2 ++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index b95fcd17be692..b9b0bef448030 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -245,6 +245,7 @@ int batadv_mesh_init(struct net_device *mesh_iface) void batadv_mesh_free(struct net_device *mesh_iface) { struct batadv_priv *bat_priv = netdev_priv(mesh_iface); + struct batadv_meshif_vlan *vlan; atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); @@ -259,6 +260,13 @@ void batadv_mesh_free(struct net_device *mesh_iface) batadv_mcast_free(bat_priv); + /* destroy the "untagged" VLAN */ + vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); + if (vlan) { + batadv_meshif_destroy_vlan(bat_priv, vlan); + batadv_meshif_vlan_put(vlan); + } + /* Free the TT and the originator tables only after having terminated * all the other depending components which may use these structures for * their purposes. diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index afd615a8fd441..0d598bf39bfc2 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -593,8 +593,8 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) * @bat_priv: the bat priv with all the mesh interface information * @vlan: the object to remove */ -static void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, - struct batadv_meshif_vlan *vlan) +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan) { /* explicitly remove the associated TT local entry because it is marked * with the NOPURGE flag @@ -1089,22 +1089,13 @@ static int batadv_meshif_newlink(struct net_device *dev, static void batadv_meshif_destroy_netlink(struct net_device *mesh_iface, struct list_head *head) { - struct batadv_priv *bat_priv = netdev_priv(mesh_iface); struct batadv_hard_iface *hard_iface; - struct batadv_meshif_vlan *vlan; while (!list_empty(&mesh_iface->adj_list.lower)) { hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next); batadv_hardif_disable_interface(hard_iface); } - /* destroy the "untagged" VLAN */ - vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); - if (vlan) { - batadv_meshif_destroy_vlan(bat_priv, vlan); - batadv_meshif_vlan_put(vlan); - } - unregister_netdevice_queue(mesh_iface, head); } diff --git a/net/batman-adv/mesh-interface.h b/net/batman-adv/mesh-interface.h index 53756c5a45e04..5e1e83e04ffbc 100644 --- a/net/batman-adv/mesh-interface.h +++ b/net/batman-adv/mesh-interface.h @@ -21,6 +21,8 @@ void batadv_interface_rx(struct net_device *mesh_iface, bool batadv_meshif_is_valid(const struct net_device *net_dev); extern struct rtnl_link_ops batadv_link_ops; int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid); +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan); void batadv_meshif_vlan_release(struct kref *ref); struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv, unsigned short vid); From a90f4fff9025371bce5371daa610af957de8dd6a Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 21:04:03 +0200 Subject: [PATCH 1819/3297] batman-adv: frag: fix primary_if leak on failed linearization commit 353d2c1d5492e53ae34f490a84494124dc3d3531 upstream. If the skb has a frag_list, it must be linearized before it can be split using skb_split(). But when this step failed, it must not only free the skb but also take care of the reference to the already found primary_if. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a063f2fba3fa ("batman-adv: Don't skb_split skbuffs with frag_list") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/fragmentation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 520bf30d89884..c9eb27ded616a 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -545,7 +545,7 @@ int batadv_frag_send_packet(struct sk_buff *skb, */ if (skb_has_frag_list(skb) && __skb_linearize(skb)) { ret = -ENOMEM; - goto free_skb; + goto put_primary_if; } /* Create one header to be copied to all fragments */ From d2b657c9653fcebca828a2ead13f444e0da68817 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:47:45 +0200 Subject: [PATCH 1820/3297] batman-adv: mcast: avoid OOB read of num_dests header commit 38eaed28e250895d56f4b7989bd65479a511c5c3 upstream. Before the access to struct batadv_tvlv_mcast_tracker's num_dests, it is attempted to check whether enough space is actually in the network header. But instead of using offsetofend() to check for the whole size (2) which must be accessible, offsetof() of is called. The latter is always returning 0. The comparison with the network header length will always return that enough data is available - even when only 1 or 0 bytes are accessible. Instead of using offsetofend(), use the more common check for the whole header. Cc: stable@vger.kernel.org Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/multicast_forw.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/multicast_forw.c b/net/batman-adv/multicast_forw.c index b8668a80b94a1..1404a3b7adfb1 100644 --- a/net/batman-adv/multicast_forw.c +++ b/net/batman-adv/multicast_forw.c @@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, { struct batadv_tvlv_mcast_tracker *mcast_tracker; struct batadv_neigh_node *neigh_node; - unsigned long offset, num_dests_off; struct sk_buff *nexthop_skb; unsigned char *skb_net_hdr; bool local_recv = false; unsigned int tvlv_len; + unsigned long offset; bool xmitted = false; u8 *dest, *next_dest; u16 num_dests; @@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, /* (at least) TVLV part needs to be linearized */ SKB_LINEAR_ASSERT(skb); - /* check if num_dests is within skb length */ - num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests); - if (num_dests_off > skb_network_header_len(skb)) + /* check if batadv_tvlv_mcast_tracker header is within skb length */ + if (sizeof(*mcast_tracker) > skb_network_header_len(skb)) return -EINVAL; skb_net_hdr = skb_network_header(skb); From 3256c05d5a9db34346eaf20f52dddde984852d77 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 22:27:13 +0200 Subject: [PATCH 1821/3297] batman-adv: tt: prevent TVLV OOB check overflow commit 7a581d9aaba8c82bd6177fa36b2588eea77f6e2b upstream. A TT unicast TVLV contains the number of VLANs stored in it. This number is an u16 and gets multiplied by the size of the struct batadv_tvlv_tt_vlan_data (8 bytes). The size can therefore overflow the u16 used to store the tt_vlan_len. All additional safety checks to prevent out-of-bounds access of the TVLV buffer are invalid due to this overflow. Using size_t prevents this overflow and ensures that the safety checks compare against the actual buffer requirements. Cc: stable@vger.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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 79bcce09ca05f..877475c688252 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -4025,7 +4025,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv, u16 tvlv_value_len) { struct batadv_tvlv_tt_data *tt_data; - u16 tt_vlan_len, tt_num_entries; + u16 tt_num_entries; + size_t tt_vlan_len; char tt_flag; bool ret; From d8e2f3e1bc207749ad58b12f40c41b0b4cd5f4e9 Mon Sep 17 00:00:00 2001 From: Shyam Prasad N Date: Thu, 14 May 2026 23:38:07 +0530 Subject: [PATCH 1822/3297] cifs: invalidate cfid on unlink/rename/rmdir commit f71d68d2b473e47db260c27c98212829590d8bae upstream. Today we do not invalidate the cached_dirent or the entire parent cfid when a dentry in a dir has been removed/moved. This change invalidates the parent cfid so that we don't serve directory contents from the cache. Cc: Signed-off-by: Shyam Prasad N Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/inode.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 7cd718dd7cb70..594798a6b0fb4 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -28,6 +28,23 @@ #include "cached_dir.h" #include "reparse.h" +static void cifs_invalidate_cached_dir(struct cifs_tcon *tcon, + struct dentry *parent) +{ + struct cached_fid *parent_cfid = NULL; + + if (!tcon || !parent) + return; + + if (!open_cached_dir_by_dentry(tcon, parent, &parent_cfid)) { + mutex_lock(&parent_cfid->dirents.de_mutex); + parent_cfid->dirents.is_valid = false; + parent_cfid->dirents.is_failed = true; + mutex_unlock(&parent_cfid->dirents.de_mutex); + close_cached_dir(parent_cfid); + } +} + /* * Set parameters for the netfs library */ @@ -2063,6 +2080,9 @@ static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyren cifs_set_file_info(inode, attrs, xid, full_path, origattr); out_reval: + if (!rc && dentry->d_parent) + cifs_invalidate_cached_dir(tcon, dentry->d_parent); + if (inode) { cifs_inode = CIFS_I(inode); cifs_inode->time = 0; /* will force revalidate to get info @@ -2372,7 +2392,6 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) } rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb); - cifs_put_tlink(tlink); cifsInode = CIFS_I(d_inode(direntry)); @@ -2382,6 +2401,8 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) i_size_write(d_inode(direntry), 0); clear_nlink(d_inode(direntry)); spin_unlock(&d_inode(direntry)->i_lock); + if (direntry->d_parent) + cifs_invalidate_cached_dir(tcon, direntry->d_parent); } /* force revalidate to go get info when needed */ @@ -2396,6 +2417,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) inode_set_ctime_current(d_inode(direntry)); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + cifs_put_tlink(tlink); rmdir_exit: free_dentry_path(page); @@ -2663,6 +2685,12 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir, } /* force revalidate to go get info when needed */ + if (!rc) { + cifs_invalidate_cached_dir(tcon, source_dentry->d_parent); + if (target_dentry->d_parent != source_dentry->d_parent) + cifs_invalidate_cached_dir(tcon, target_dentry->d_parent); + } + CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; cifs_rename_exit: From 88ba8454685087dd2b0edc07af8c1feb61574258 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 21 May 2026 10:36:24 +0200 Subject: [PATCH 1823/3297] mfd: tps6586x: Fix OF node refcount commit 3001ed2b4e06da2276c42ace6551617065a5b1f9 upstream. Platform devices created with platform_device_alloc() call platform_device_release() when the last reference to the device's kobject is dropped. This function calls of_node_put() unconditionally. This works fine for devices created with platform_device_register_full() but users of the split approach (platform_device_alloc() + platform_device_add()) must bump the reference of the of_node they assign manually. Add the missing call to of_node_get(). Cc: stable@vger.kernel.org Fixes: 62f6b0879304 ("tps6586x: Add device tree support") Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260521-pdev-fwnode-ref-v1-1-88c324a1b8d2@oss.qualcomm.com Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/tps6586x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 8d5fe2b60bfa5..f5f8054466033 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -397,7 +397,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x, pdev->dev.parent = tps6586x->dev; pdev->dev.platform_data = subdev->platform_data; - pdev->dev.of_node = subdev->of_node; + pdev->dev.of_node = of_node_get(subdev->of_node); ret = platform_device_add(pdev); if (ret) { From 96dd35f1942cdd5db7675eee1086193cc2e16001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Sevens?= Date: Mon, 23 Mar 2026 12:47:37 +0000 Subject: [PATCH 1824/3297] HID: playstation: validate num_touch_reports in DualShock 4 reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 82a4fc46330910b4c1d9b189561439d468e3ff11 upstream. The DualShock 4 HID driver fails to validate the num_touch_reports field received from the device in both USB and Bluetooth input reports. A malicious device could set this field to a value larger than the allocated size of the touch_reports array (3 for USB, 4 for Bluetooth), leading to an out-of-bounds read in dualshock4_parse_report(). This can result in kernel memory disclosure when processing malicious HID reports. Validate num_touch_reports against the array size for the respective connection types before processing the touch data. Signed-off-by: Benoît Sevens Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-playstation.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 12fad45ac1065..4a17fd0c12f89 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -2377,6 +2377,12 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * struct dualshock4_input_report_usb *usb = (struct dualshock4_input_report_usb *)data; + if (usb->num_touch_reports > ARRAY_SIZE(usb->touch_reports)) { + hid_err(hdev, "DualShock4 USB input report has invalid num_touch_reports=%d\n", + usb->num_touch_reports); + return -EINVAL; + } + ds4_report = &usb->common; num_touch_reports = min_t(u8, usb->num_touch_reports, ARRAY_SIZE(usb->touch_reports)); @@ -2392,6 +2398,12 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * return -EILSEQ; } + if (bt->num_touch_reports > ARRAY_SIZE(bt->touch_reports)) { + hid_err(hdev, "DualShock4 BT input report has invalid num_touch_reports=%d\n", + bt->num_touch_reports); + return -EINVAL; + } + ds4_report = &bt->common; num_touch_reports = min_t(u8, bt->num_touch_reports, ARRAY_SIZE(bt->touch_reports)); From 77eb0cf57009ec4ae5044db1a8096d1d787765a4 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 12 Apr 2026 21:47:42 +0300 Subject: [PATCH 1825/3297] Bluetooth: SCO: fix sleeping under spinlock in sco_conn_ready commit b819db93d73f4593636299e229914052b89e3ef2 upstream. sco_conn_ready calls sleeping functions under conn->lock spinlock. The critical section can be reduced: conn->hcon is modified only with hdev->lock held. It is guaranteed to be held in sco_conn_ready, so conn->lock is not needed to guard it. Move taking conn->lock after lock_sock(parent). This also follows the lock ordering lock_sock() > conn->lock elsewhere in the file. Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/sco.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 15b9c234bce10..bd013f26cacc9 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1407,26 +1407,24 @@ static void sco_conn_ready(struct sco_conn *conn) sk->sk_state_change(sk); release_sock(sk); } else { - sco_conn_lock(conn); - - if (!conn->hcon) { - sco_conn_unlock(conn); + if (!conn->hcon) return; - } + + lockdep_assert_held(&conn->hcon->hdev->lock); parent = sco_get_sock_listen(&conn->hcon->src); - if (!parent) { - sco_conn_unlock(conn); + if (!parent) return; - } lock_sock(parent); + sco_conn_lock(conn); + sk = sco_sock_alloc(sock_net(parent), NULL, BTPROTO_SCO, GFP_ATOMIC, 0); if (!sk) { - release_sock(parent); sco_conn_unlock(conn); + release_sock(parent); return; } @@ -1447,9 +1445,9 @@ static void sco_conn_ready(struct sco_conn *conn) /* Wake up parent */ parent->sk_data_ready(parent); - release_sock(parent); - sco_conn_unlock(conn); + + release_sock(parent); } } From 7199c78c3a3e399a4dc439d845826793880ccedc Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 18 Apr 2026 18:41:12 +0300 Subject: [PATCH 1826/3297] Bluetooth: SCO: hold sk properly in sco_conn_ready commit 4e37f6452d586b95c346a9abdd2fb80b67794f39 upstream. sk deref in sco_conn_ready must be done either under conn->lock, or holding a refcount, to avoid concurrent close. conn->sk and parent sk is currently accessed without either, and without checking parent->sk_state: [Task 1] [Task 2] sco_sock_release sco_conn_ready sk = conn->sk lock_sock(sk) conn->sk = NULL lock_sock(sk) release_sock(sk) sco_sock_kill(sk) UAF on sk deref and similarly for access to sco_get_sock_listen() return value. Fix possible UAF by holding sk refcount in sco_conn_ready() and making sco_get_sock_listen() increase refcount. Also recheck after lock_sock that the socket is still valid. Adjust conn->sk locking so it's protected also by lock_sock() of the associated socket if any. Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/sco.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index bd013f26cacc9..285a98594ba43 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -482,9 +482,13 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src) sk1 = sk; } + sk = sk ? sk : sk1; + if (sk) + sock_hold(sk); + read_unlock(&sco_sk_list.lock); - return sk ? sk : sk1; + return sk; } static void sco_sock_destruct(struct sock *sk) @@ -527,11 +531,13 @@ static void sco_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->sk_state); /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ + lock_sock(sk); if (sco_pi(sk)->conn) { sco_conn_lock(sco_pi(sk)->conn); sco_pi(sk)->conn->sk = NULL; sco_conn_unlock(sco_pi(sk)->conn); } + release_sock(sk); /* Kill poor orphan */ bt_sock_unlink(&sco_sk_list, sk); @@ -1395,17 +1401,28 @@ static int sco_sock_release(struct socket *sock) static void sco_conn_ready(struct sco_conn *conn) { - struct sock *parent; - struct sock *sk = conn->sk; + struct sock *parent, *sk; + + sco_conn_lock(conn); + sk = sco_sock_hold(conn); + sco_conn_unlock(conn); BT_DBG("conn %p", conn); if (sk) { lock_sock(sk); - sco_sock_clear_timer(sk); - sk->sk_state = BT_CONNECTED; - sk->sk_state_change(sk); + + /* conn->sk may have become NULL if racing with sk close, but + * due to held hdev->lock, it can't become different sk. + */ + if (conn->sk) { + sco_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + } + release_sock(sk); + sock_put(sk); } else { if (!conn->hcon) return; @@ -1420,13 +1437,15 @@ static void sco_conn_ready(struct sco_conn *conn) sco_conn_lock(conn); + /* hdev->lock guarantees conn->sk == NULL still here */ + + if (parent->sk_state != BT_LISTEN) + goto release; + sk = sco_sock_alloc(sock_net(parent), NULL, BTPROTO_SCO, GFP_ATOMIC, 0); - if (!sk) { - sco_conn_unlock(conn); - release_sock(parent); - return; - } + if (!sk) + goto release; sco_sock_init(sk, parent); @@ -1445,9 +1464,10 @@ static void sco_conn_ready(struct sco_conn *conn) /* Wake up parent */ parent->sk_data_ready(parent); +release: sco_conn_unlock(conn); - release_sock(parent); + sock_put(parent); } } From 78955fdce8ff654e6d33a2fa90882a1e7eb26330 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Wed, 13 May 2026 17:28:40 +0800 Subject: [PATCH 1827/3297] jbd2: fix integer underflow in jbd2_journal_initialize_fast_commit() commit 289a2ca0c9b7eae74f93fc213b0b971669b8683d upstream. jbd2_journal_initialize_fast_commit() validates journal capacity by checking (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS). Both j_last and num_fc_blks are unsigned, so when num_fc_blks exceeds j_last the subtraction wraps to a large value, bypassing the bounds check. The resulting underflow corrupts j_last, j_fc_first, and j_free, leading to journal abort. Fix by checking num_fc_blks against j_last before the subtraction, returning -EFSCORRUPTED. Fixes: 6866d7b3f2bb ("ext4 / jbd2: add fast commit initialization") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Fixes: e029c5f27987 ("ext4: make num of fast commit blocks configurable") Reviewed-by: Baokun Li Fixes: e029c5f279872 ("ext4: make num of fast commit blocks configurable") Reviewed-by: Zhang Yi Reviewed-by: Jan Kara Link: https://patch.msgid.link/SYBPR01MB7881663C927DE9D7BBF4D1DFAF062@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/jbd2/journal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index d02fc04e60d08..fa0fb738db0c4 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -2261,6 +2261,8 @@ jbd2_journal_initialize_fast_commit(journal_t *journal) unsigned long long num_fc_blks; num_fc_blks = jbd2_journal_get_num_fc_blks(sb); + if (num_fc_blks > journal->j_last) + return -EFSCORRUPTED; if (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS) return -ENOSPC; From f4ca396bdd60b4e0a665b2589bfc4ddbea9bda2b Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 19 May 2026 11:20:13 +0530 Subject: [PATCH 1828/3297] nvdimm/btt: Free arenas on btt_init() error paths commit 1a6b6442a982d0ca5fb6a1a39b6f6dfd760eda57 upstream. The arenas allocated by discover_arenas() or create_arenas() are not freed on some error paths in btt_init(). This leaks memory when BTT initialization fails. Call free_arenas() from the affected error paths to release the allocations. [ as: commit message and log edits ] Fixes: 5212e11fde4d ("nd_btt: atomic sector updates") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260519-nvdimmleaks-v1-2-592300fb7a43@cse.iitm.ac.in Signed-off-by: Alison Schofield Signed-off-by: Greg Kroah-Hartman --- drivers/nvdimm/btt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index a933db961ed70..72c642583aaf7 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1589,7 +1589,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, if (btt->init_state != INIT_READY && nd_region->ro) { dev_warn(dev, "%s is read-only, unable to init btt metadata\n", dev_name(&nd_region->dev)); - return NULL; + goto err; } else if (btt->init_state != INIT_READY) { btt->num_arenas = (rawsize / ARENA_MAX_SIZE) + ((rawsize % ARENA_MAX_SIZE) ? 1 : 0); @@ -1599,25 +1599,28 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, ret = create_arenas(btt); if (ret) { dev_info(dev, "init: create_arenas: %d\n", ret); - return NULL; + goto err; } ret = btt_meta_init(btt); if (ret) { dev_err(dev, "init: error in meta_init: %d\n", ret); - return NULL; + goto err; } } ret = btt_blk_init(btt); if (ret) { dev_err(dev, "init: error in blk_init: %d\n", ret); - return NULL; + goto err; } btt_debugfs_init(btt); return btt; +err: + free_arenas(btt); + return NULL; } /** From 30d490bb2c4cd220e7dedf862ab6a09eb5dcbad5 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 19 May 2026 11:20:12 +0530 Subject: [PATCH 1829/3297] nvdimm/btt: Free arena sub-allocations on discover_arenas() error path commit 13fe4cd9ddd0aacb7777812328be525a11ea3fea upstream. Memory allocated by btt_freelist_init(), btt_rtt_init(), and btt_maplocks_init() is not freed on some discover_arenas() error paths. This leaks memory when arena discovery fails. Add the missing kfree() calls to release the allocations before returning an error. [ as: commit message and log edits ] Fixes: 5212e11fde4d ("nd_btt: atomic sector updates") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260519-nvdimmleaks-v1-1-592300fb7a43@cse.iitm.ac.in Signed-off-by: Alison Schofield Signed-off-by: Greg Kroah-Hartman --- drivers/nvdimm/btt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index 72c642583aaf7..eed4f15f42578 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -921,6 +921,9 @@ static int discover_arenas(struct btt *btt) return ret; out: + kfree(arena->freelist); + kfree(arena->rtt); + kfree(arena->map_locks); kfree(arena); free_arenas(btt); return ret; From 3f9ee75a97a769be258784c22b89657acb5ed9bd Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 22 May 2026 09:39:06 -0400 Subject: [PATCH 1830/3297] sunrpc: pin svc_xprt across the asynchronous TLS handshake callback commit 4f988f3a2808fb659f3880c282041ff067acad78 upstream. svc_tcp_handshake() stores the raw svc_xprt pointer in tls_handshake_args.ta_data and submits the request through tls_server_hello_x509(). The handshake core takes only sock_hold(req->hr_sk); nothing references the embedding struct svc_sock that svc_tcp_handshake_done() reaches via container_of(). Two close races leave the in-flight callback writing through a freed svc_sock. svc_sock_free() calls tls_handshake_cancel() and discards its return value: a false return means handshake_complete() has already set HANDSHAKE_F_REQ_COMPLETED but hp_done() may not have finished, yet svc_sock_free() proceeds to kfree(svsk). The cancel-loser fall-through inside svc_tcp_handshake() itself produces the same window: when wait_for_completion_interruptible_timeout() returns <= 0 (timeout or signal) and tls_handshake_cancel() returns false, the function does not drain, returns, and svc_handle_xprt() calls svc_xprt_received(), which clears XPT_BUSY and can drop the last reference. A concurrent close then runs svc_sock_free() while svc_tcp_handshake_done() is still updating xpt_flags and walking svsk->sk_handshake_done. The corruption surfaces as set_bit/clear_bit RMW into the freed xpt_flags slab slot and as complete_all() walking and writing the freed wait_queue_head_t list embedded in sk_handshake_done -- a slab-corruption primitive, not a benign read. The path is reachable on any TLS-enabled NFS server whenever a connection close overlaps the tlshd downcall delivery window; the interruptible wait means signal delivery suffices, not just SVC_HANDSHAKE_TO expiry. Take svc_xprt_get(xprt) immediately before tls_server_hello_x509() so the in-flight callback owns its own reference. Release it on the two edges where the callback is guaranteed not to fire -- submission failure from tls_server_hello_x509() and a successful tls_handshake_cancel() -- and at the tail of svc_tcp_handshake_done() after complete_all(). Fixes: b3cbf98e2fdf ("SUNRPC: Support TLS handshake in the server-side TCP socket code") Cc: stable@vger.kernel.org Signed-off-by: Chris Mason Assisted-by: kres (claude-opus-4-7) [cel: rewrote commit message to describe the actual change] Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/svcsock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 7b90abc5cf0ee..0b28b7776d34d 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -462,6 +462,7 @@ static void svc_tcp_handshake_done(void *data, int status, key_serial_t peerid) } clear_bit(XPT_HANDSHAKE, &xprt->xpt_flags); complete_all(&svsk->sk_handshake_done); + svc_xprt_put(xprt); } /** @@ -485,9 +486,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) clear_bit(XPT_TLS_SESSION, &xprt->xpt_flags); init_completion(&svsk->sk_handshake_done); + /* Pin the transport across the asynchronous handshake callback. */ + svc_xprt_get(xprt); + ret = tls_server_hello_x509(&args, GFP_KERNEL); if (ret) { trace_svc_tls_not_started(xprt); + svc_xprt_put(xprt); goto out_failed; } @@ -496,6 +501,7 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) if (ret <= 0) { if (tls_handshake_cancel(sk)) { trace_svc_tls_timed_out(xprt); + svc_xprt_put(xprt); goto out_close; } } From 65b23bec1fca6e9ebdc3e6041ebf8c6ab074141b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 22 May 2026 09:39:07 -0400 Subject: [PATCH 1831/3297] sunrpc: wait for in-flight TLS handshake callback when cancel loses race commit d00e32f84ca1a77cb67a3fbf59f58dada95f5a21 upstream. When wait_for_completion_interruptible_timeout() in svc_tcp_handshake() returns 0 (timeout) or -ERESTARTSYS (signal) and tls_handshake_cancel() then returns false, handshake_complete() has won the cancellation race: it has set HANDSHAKE_F_REQ_COMPLETED and is about to invoke svc_tcp_handshake_done(), but the callback's side effects on xpt_flags and on svsk->sk_handshake_done have not yet committed. The current code reads xpt_flags immediately to decide whether the session succeeded. Two races result. If the callback has executed set_bit(XPT_TLS_SESSION) but not yet clear_bit(XPT_HANDSHAKE), svc_tcp_handshake() sees a session, enqueues the transport, and returns. svc_xprt_received() then clears XPT_BUSY, a worker thread picks the transport up, the dispatcher in svc_handle_xprt() observes XPT_HANDSHAKE still set, and xpo_handshake is invoked a second time. That svc_tcp_handshake() calls init_completion(&svsk->sk_handshake_done) while the original callback concurrently calls complete_all() on it, corrupting the embedded swait_queue. If the callback has set HANDSHAKE_F_REQ_COMPLETED but not yet entered svc_tcp_handshake_done(), svc_tcp_handshake() reads XPT_TLS_SESSION as clear and tears the connection down even though the handshake is about to succeed. Wait for the callback to commit before inspecting xpt_flags. The completion is guaranteed to fire because handshake_complete() invokes svc_tcp_handshake_done() unconditionally once it has set HANDSHAKE_F_REQ_COMPLETED. Fixes: b3cbf98e2fdf ("SUNRPC: Support TLS handshake in the server-side TCP socket code") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/svcsock.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 0b28b7776d34d..9c14558bc552b 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -504,6 +504,10 @@ static void svc_tcp_handshake(struct svc_xprt *xprt) svc_xprt_put(xprt); goto out_close; } + /* Cancellation lost to handshake_complete(): the + * callback is in flight and should finish quickly. + */ + wait_for_completion(&svsk->sk_handshake_done); } if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags)) { From 66014ab165cb01bfef5836939412a8ef98d5d5ff Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Thu, 21 May 2026 15:06:32 +0800 Subject: [PATCH 1832/3297] sunrpc: harden rq_procinfo lifecycle to prevent double-free commit 18d216788bef06332ff8901670ecf1ed8f6eb614 upstream. The svc_release_rqst() function executes the callback inside rqstp->rq_procinfo->pc_release. However, if a worker thread begins processing a new request and encounters an early error path (e.g., unsupported protocol, short frame, or bad auth) before a valid rq_procinfo is installed, a stale release hook can be re-triggered against reused state from the previous RPC, resulting in a double-free or use-after-free vulnerability. Harden the lifecycle of rq_procinfo by: 1. Ensuring svc_release_rqst() always clears rq_procinfo after the optional pc_release() call, regardless of whether the hook exists. 2. Explicitly clearing rq_procinfo at request entry in svc_process() before any early decode or drop paths. 3. Ensuring svc_process_bc() does the same at backchannel entry. This guarantees that error flows will not encounter a non-NULL stale rq_procinfo pointer when there is nothing to release. Fixes: d9adbb6e10bf ("sunrpc: delay pc_release callback until after the reply is sent") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Suggested-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/svc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 4704dce7284ec..1f4bf5b290e4a 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1529,6 +1529,12 @@ static void svc_release_rqst(struct svc_rqst *rqstp) if (procp && procp->pc_release) procp->pc_release(rqstp); + + /* + * A subsequent svc_release_rqst() on this rqstp must not + * re-invoke pc_release against released state. + */ + rqstp->rq_procinfo = NULL; } /** @@ -1547,6 +1553,9 @@ void svc_process(struct svc_rqst *rqstp) svc_xprt_deferred_close(rqstp->rq_xprt); #endif + /* Discard a stale release hook from a previous RPC. */ + rqstp->rq_procinfo = NULL; + /* * Setup response xdr_buf. * Initially it has just one page @@ -1603,6 +1612,7 @@ void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp) int proc_error; /* Build the svc_rqst used by the common processing routine */ + rqstp->rq_procinfo = NULL; rqstp->rq_xid = req->rq_xid; rqstp->rq_prot = req->rq_xprt->prot; rqstp->rq_bc_net = req->rq_xprt->xprt_net; From 1161c4b5bd0048c8148e919f818a33cff3623ef0 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 14 May 2026 16:56:04 -0400 Subject: [PATCH 1833/3297] lockd: Plug nlm_file leak when nlm_do_fopen() fails commit f16a1513452edb532fec81e591c64c320866719c upstream. A client can repeatedly drive nlm_do_fopen() failures by presenting file handles that the underlying export rejects. After kzalloc_obj() succeeds in nlm_lookup_file(), the freshly allocated nlm_file is not yet inserted into nlm_files[]. The nlm_do_fopen() failure path jumps to out_unlock, which releases nlm_file_mutex and returns without freeing the allocation, so each failure leaks one nlm_file. Route the failure through out_free so kfree() runs before the function returns. Fixes: 7f024fcd5c97 ("Keep read and write fds with each nlm_file") Cc: stable@vger.kernel.org Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/lockd/svcsubs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 7ea204eadfcac..2c09e7a8cc78a 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -154,7 +154,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, nfserr = nlm_do_fopen(rqstp, file, mode); if (nfserr) - goto out_unlock; + goto out_free; hlist_add_head(&file->f_list, &nlm_files[hash]); From 46d59ff421824b6483549d87f14efffbbbd1f6cb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 14 May 2026 16:56:06 -0400 Subject: [PATCH 1834/3297] lockd: Plug nlm_file refcount leak on cached nlm_do_fopen() failure commit 70a38f87bed7f0694fd07988b47b2db1e10d8df3 upstream. The cached-file path in nlm_lookup_file() reaches the found: label unconditionally, even when nlm_do_fopen() fails. At that label *result and file->f_count are updated before the error is returned. The wrappers nlm3svc_lookup_file() and nlm4svc_lookup_file() then bail out of their switch without copying *result back to their caller, so the proc handler's local nlm_file pointer remains NULL and the cleanup path skips nlm_release_file(). The f_count increment is never released, and nlm_traverse_files() can no longer reap the file because its refcount never returns to zero between requests. Short-circuit the cached path so neither *result nor f_count is touched when nlm_do_fopen() fails on a hashed nlm_file. Fixes: 7f024fcd5c97 ("Keep read and write fds with each nlm_file") Cc: stable@vger.kernel.org Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/lockd/svcsubs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 2c09e7a8cc78a..d11beea76ddb5 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -138,6 +138,8 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, mutex_lock(&file->f_mutex); nfserr = nlm_do_fopen(rqstp, file, mode); mutex_unlock(&file->f_mutex); + if (nfserr) + goto out_unlock; goto found; } nlm_debug_print_fh("creating file for", &lock->fh); From 69e18135e2a004a79505451dbef07314ea16e1eb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 19 May 2026 09:34:21 -0400 Subject: [PATCH 1835/3297] SUNRPC: Bound-check xdr_buf_to_bvec() stores before writing commit 42f5b80dda6b86e424054baf1475df686c403d5c upstream. xdr_buf_to_bvec() writes a bio_vec into the caller's array before testing whether that slot is in range, and the head branch performs the store with no check at all. When the caller's budget is exactly used up, the next store lands one element past the end of the array. The overflow label returns count - 1, which masks the surplus store but cannot undo it. rq_bvec, the array passed by nfsd_vfs_write(), is allocated to exactly rq_maxpages entries with no slack. The OOB store can land in adjacent slab memory; the bv_len and bv_offset fields written there are derived from client-supplied RPC payload sizes. Move the in-range check ahead of the store in the head, page-loop, and tail branches. With the check at the top of each sequence, count is incremented only after a successful store, so the overflow label can return count directly. Reported-by: Chris Mason Fixes: 2eb2b9358181 ("SUNRPC: Convert svc_tcp_sendmsg to use bio_vecs directly") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/xdr.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 70efc727a9cd8..fe17a6e8372b4 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -152,6 +152,8 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, unsigned int count = 0; if (head->iov_len) { + if (unlikely(count >= bvec_size)) + goto bvec_overflow; bvec_set_virt(bvec++, head->iov_base, head->iov_len); ++count; } @@ -165,25 +167,27 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, while (remaining > 0) { len = min_t(unsigned int, remaining, PAGE_SIZE - offset); + if (unlikely(count >= bvec_size)) + goto bvec_overflow; bvec_set_page(bvec++, *pages++, len, offset); remaining -= len; offset = 0; - if (unlikely(++count > bvec_size)) - goto bvec_overflow; + ++count; } } if (tail->iov_len) { - bvec_set_virt(bvec, tail->iov_base, tail->iov_len); - if (unlikely(++count > bvec_size)) + if (unlikely(count >= bvec_size)) goto bvec_overflow; + bvec_set_virt(bvec, tail->iov_base, tail->iov_len); + ++count; } return count; bvec_overflow: pr_warn_once("%s: bio_vec array overflow\n", __func__); - return count - 1; + return count; } EXPORT_SYMBOL_GPL(xdr_buf_to_bvec); From 51aad3d89a2dd2bd34713785b0f2fd5177eb33b6 Mon Sep 17 00:00:00 2001 From: Wasim Nazir Date: Wed, 18 Mar 2026 17:19:16 +0530 Subject: [PATCH 1836/3297] remoteproc: qcom: Fix leak when custom dump_segments addition fails commit ecf9fc18e62c58eae1ceb65dab2bccb8a724de2d upstream. Free allocated minidump_region 'name' in qcom_add_minidump_segments() when failing before adding the region to 'dump_segments'. Otherwise, the 'name' is not tracked and is never freed by qcom_minidump_cleanup(). Return error when adding to 'dump_segments' fails. Cc: stable@vger.kernel.org # v5.11 Fixes: 8ed8485c4f05 ("remoteproc: qcom: Add capability to collect minidumps") Reviewed-by: Mukesh Ojha Signed-off-by: Wasim Nazir Link: https://lore.kernel.org/r/20260318-rproc-memleak-v2-1-ade70ab858f2@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Greg Kroah-Hartman --- drivers/remoteproc/qcom_common.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 8c8688f99f0ab..5bbbe36d09640 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -110,6 +110,7 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy struct minidump_region __iomem *ptr; struct minidump_region region; int seg_cnt, i; + int ret = 0; dma_addr_t da; size_t size; char *name; @@ -130,17 +131,22 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy if (le32_to_cpu(region.valid) == MINIDUMP_REGION_VALID) { name = kstrndup(region.name, MAX_REGION_NAME_LENGTH - 1, GFP_KERNEL); if (!name) { - iounmap(ptr); - return -ENOMEM; + ret = -ENOMEM; + break; } da = le64_to_cpu(region.address); size = le64_to_cpu(region.size); - rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, name); + ret = rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, + name); + if (ret) { + kfree(name); + break; + } } } iounmap(ptr); - return 0; + return ret; } void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, From 620a37ea7d6264a8d0d8791b5e1ffffbb62d8bc9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:46 +0200 Subject: [PATCH 1837/3297] MIPS: ip22-gio: fix gio device memory leak commit 7de9a1b45f5a95b58145653c525c8fb80292d9ab upstream. The gio device release callback was never wired up so gio devices are not freed when the last reference is dropped. Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28") Cc: stable@vger.kernel.org # 3.3 Cc: Thomas Bogendoerfer Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/sgi-ip22/ip22-gio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index 19b70928d6dc3..f31d27ae0ebcf 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -101,6 +101,8 @@ int gio_device_register(struct gio_device *giodev) { giodev->dev.bus = &gio_bus_type; giodev->dev.parent = &gio_bus; + giodev->dev.release = gio_release_dev; + return device_register(&giodev->dev); } EXPORT_SYMBOL_GPL(gio_device_register); From 2c551f14f55e4ce0ca84d2870b491b9cac670120 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:45 +0200 Subject: [PATCH 1838/3297] MIPS: ip22-gio: fix kfree() of static object commit c62cdd3e919bdf84c37ec46810f87cdb1736e822 upstream. The gio bus root device is a statically allocated object which must not be freed by kfree() on failure to register the device or bus. Fixes: 82242d28ff8b ("MIPS: IP22: Add missing put_device call") Cc: stable@vger.kernel.org # 3.17 Cc: Levente Kurusa Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/sgi-ip22/ip22-gio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index f31d27ae0ebcf..6b828de5c368c 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -30,7 +30,6 @@ static struct { static void gio_bus_release(struct device *dev) { - kfree(dev); } static struct device gio_bus = { From 11a3bc25f2c309bbe33cd3be57f2743ffde2330a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 24 Apr 2026 12:28:47 +0200 Subject: [PATCH 1839/3297] MIPS: ip22-gio: fix device reference leak in probe commit b82930a4c5dbc5c4df39c0f93d968c239f2c6885 upstream. The gio probe function needlessly takes a device reference which is never released and therefore prevents unbound gio devices from being freed. Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28") Cc: stable@vger.kernel.org # 3.3 Cc: Thomas Bogendoerfer Signed-off-by: Johan Hovold Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/sgi-ip22/ip22-gio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c index 6b828de5c368c..a3bd7354a4e08 100644 --- a/arch/mips/sgi-ip22/ip22-gio.c +++ b/arch/mips/sgi-ip22/ip22-gio.c @@ -133,13 +133,9 @@ static int gio_device_probe(struct device *dev) if (!drv->probe) return error; - gio_dev_get(gio_dev); - match = gio_match_device(drv->id_table, gio_dev); if (match) error = drv->probe(gio_dev, match); - if (error) - gio_dev_put(gio_dev); return error; } From d3fd2d358df0ca712183509cbbed6a16bde1d17e Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:23 +0100 Subject: [PATCH 1840/3297] MIPS: DEC: Ensure 32-bit stack location for o32 prom_printf() commit 5ff79e8bdc75db51e30298a75939e2308e7658e0 upstream. In 64-bit configurations calling any firmware entry points from a kernel thread other than the initial one will result in a situation where the stack has been placed in the XKPHYS 64-bit memory segment. Consequently the stack pointer is no longer a 32-bit value and when the 32-bit firmware code called uses 32-bit ALU operations to manipulate the stack pointer, the calculated result is incorrect (in fact in the 64-bit MIPS ISA almost all 32-bit ALU operations will produce an unpredictable result when executed on 64-bit data) and control goes astray. This may happen when no final console driver has been enabled in the configuration and consequently the initial console continues being used late into bootstrap, or with an upcoming change that will switch the zs driver to use a platform device, which in turn will make the console handover happen only after other kernel threads have already been started, and the kernel will hang at: pid_max: default: 32768 minimum: 301 or somewhat later, but always before: cblist_init_generic: Setting adjustable number of callback queues. has been printed. It seems that only the prom_printf() entry point is affected. Of all the other entry points wired only rex_slot_address() and rex_gettcinfo() are called from a kernel thread other than the initial one, specifically kernel_init(), and they are leaf functions that do no business with the stack, having worked with no issue ever since 64-bit support was added for the platform back in 2002. To address this issue then, arrange for the stack to be switched in the o32 wrapper as required for prom_printf() only, by supplying call_o32() with a pointer to a chunk of initdata space, which is placed in the CKSEG0 32-bit compatibility segment, observing that prom_printf() is only called from console output handler and therefore with the console lock held, implying no need for this code to be reentrant. Other firmware entry points may be called with interrupts enabled and no lock held, and may therefore require that call_o32() be reentrant. They trigger no issue at this point and "if it ain't broke, don't fix it," so just leave them alone. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.12+ Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/prom/init.c | 6 +++++- arch/mips/include/asm/dec/prom.h | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/arch/mips/dec/prom/init.c b/arch/mips/dec/prom/init.c index 8d74d7d6c05b4..e46c7acceffff 100644 --- a/arch/mips/dec/prom/init.c +++ b/arch/mips/dec/prom/init.c @@ -3,7 +3,7 @@ * init.c: PROM library initialisation code. * * Copyright (C) 1998 Harald Koerfgen - * Copyright (C) 2002, 2004 Maciej W. Rozycki + * Copyright (C) 2002, 2004, 2026 Maciej W. Rozycki */ #include #include @@ -20,6 +20,10 @@ #include +#ifdef CONFIG_64BIT +unsigned long o32_stk[O32_STK_SIZE] __initdata = { 0 }; +#endif + int (*__rex_bootinit)(void); int (*__rex_bootread)(void); int (*__rex_getbitmap)(memmap *); diff --git a/arch/mips/include/asm/dec/prom.h b/arch/mips/include/asm/dec/prom.h index 8fcad6984389c..af068e634f1e3 100644 --- a/arch/mips/include/asm/dec/prom.h +++ b/arch/mips/include/asm/dec/prom.h @@ -4,7 +4,7 @@ * * DECstation PROM interface. * - * Copyright (C) 2002 Maciej W. Rozycki + * Copyright (C) 2002, 2026 Maciej W. Rozycki * * Based on arch/mips/dec/prom/prom.h by the Anonymous. */ @@ -97,6 +97,17 @@ extern int (*__pmax_close)(int); #ifdef CONFIG_64BIT +#define O32_STK_SIZE 512 +extern unsigned long o32_stk[]; + +/* Switch the stack if outside the 32-bit address space. */ +static inline unsigned long *o32_get_stk(void) +{ + long fp = (long)__builtin_frame_address(0); + + return fp != (int)fp ? o32_stk + O32_STK_SIZE : NULL; +} + /* * On MIPS64 we have to call PROM functions via a helper * dispatcher to accommodate ABI incompatibilities. @@ -128,7 +139,7 @@ int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), void *, char *, ...)); #define prom_getchar() _prom_getchar(__prom_getchar, NULL) #define prom_getenv(x) _prom_getenv(__prom_getenv, NULL, x) -#define prom_printf(x...) _prom_printf(__prom_printf, NULL, x) +#define prom_printf(x...) _prom_printf(__prom_printf, o32_get_stk(), x) #else /* !CONFIG_64BIT */ From a39d281f207b9ce108fd6ce1e9a736530efb6e1d Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:54 +0800 Subject: [PATCH 1841/3297] mm/mm_init: fix pageblock migratetype for ZONE_DEVICE compound pages commit 94405c6136839f7c462249c8b4b957bcb9527a9d upstream. The memmap_init_zone_device() function only initializes the migratetype of the first pageblock of a compound page. If the compound page size exceeds pageblock_nr_pages (e.g., 1GB hugepages with 2MB pageblocks), subsequent pageblocks in the compound page remain uninitialized. Move the migratetype initialization out of __init_zone_device_page() and into a separate pageblock_migratetype_init_range() function. This iterates over the entire PFN range of the memory, ensuring that all pageblocks are correctly initialized. Also remove the stale confusing comment about MEMINIT_HOTPLUG above the migratetype setting since it is an obsolete relic from commit 966cf44f637e ("mm: defer ZONE_DEVICE page initialization to the point where we init pgmap") and no longer makes sense here. Link: https://lore.kernel.org/20260428081855.1249045-6-songmuchun@bytedance.com Fixes: c4386bd8ee3a ("mm/memremap: add ZONE_DEVICE support for compound pages") Signed-off-by: Muchun Song Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/mm_init.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 7712d887b6963..9087dce0c51b4 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -666,6 +666,20 @@ static inline void fixup_hashdist(void) static inline void fixup_hashdist(void) {} #endif /* CONFIG_NUMA */ +#ifdef CONFIG_ZONE_DEVICE +static __meminit void pageblock_migratetype_init_range(unsigned long pfn, + unsigned long nr_pages, int migratetype) +{ + const unsigned long end = pfn + nr_pages; + + for (pfn = pageblock_align(pfn); pfn < end; pfn += pageblock_nr_pages) { + init_pageblock_migratetype(pfn_to_page(pfn), migratetype, false); + if (IS_ALIGNED(pfn, PAGES_PER_SECTION)) + cond_resched(); + } +} +#endif + /* * Initialize a reserved page unconditionally, finding its zone first. */ @@ -1028,21 +1042,6 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, page_folio(page)->pgmap = pgmap; page->zone_device_data = NULL; - /* - * Mark the block movable so that blocks are reserved for - * movable at startup. This will force kernel allocations - * to reserve their blocks rather than leaking throughout - * the address space during boot when many long-lived - * kernel allocations are made. - * - * Please note that MEMINIT_HOTPLUG path doesn't clear memmap - * because this is done early in section_activate() - */ - if (pageblock_aligned(pfn)) { - init_pageblock_migratetype(page, MIGRATE_MOVABLE, false); - cond_resched(); - } - /* * ZONE_DEVICE pages other than MEMORY_TYPE_GENERIC are released * directly to the driver page allocator which will set the page count @@ -1139,6 +1138,9 @@ void __ref memmap_init_zone_device(struct zone *zone, __init_zone_device_page(page, pfn, zone_idx, nid, pgmap); + if (IS_ALIGNED(pfn, PAGES_PER_SECTION)) + cond_resched(); + if (pfns_per_compound == 1) continue; @@ -1146,6 +1148,8 @@ void __ref memmap_init_zone_device(struct zone *zone, compound_nr_pages(altmap, pgmap)); } + pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE); + pr_debug("%s initialised %lu pages in %ums\n", __func__, nr_pages, jiffies_to_msecs(jiffies - start)); } From a3d81de441233a92ec21469cd0c4eb3c26b95cd8 Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Fri, 24 Apr 2026 09:10:13 +0800 Subject: [PATCH 1842/3297] power: supply: cpcap-battery: Fix missing nvmem_device_put() causing reference leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a2c14ff63e0e02e3c832385e523e9cc81301171c upstream. In cpcap_battery_detect_battery_type(), the reference to an nvmem device obtained via nvmem_device_find() is not released with nvmem_device_put() on the success or read-failure paths, causing a permanent reference leak. The driver’s retry logic on subsequent battery property reads can compound this leak, preventing the nvmem device from ever being freed. Found by code review. Signed-off-by: Ma Ke Cc: stable@vger.kernel.org Fixes: fd46821e85de ("power: supply: cpcap-battery: Add battery type auto detection for mapphone devices") Link: https://patch.msgid.link/20260424011013.879639-1-make24@iscas.ac.cn Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/cpcap-battery.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 507fdc1c866d5..3aebbac56e1d0 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -415,10 +415,13 @@ static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata) if (IS_ERR_OR_NULL(nvmem)) { ddata->check_nvmem = true; dev_info_once(ddata->dev, "Can not find battery nvmem device. Assuming generic lipo battery\n"); - } else if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) { - battery_id = 0; - ddata->check_nvmem = true; - dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n"); + } else { + if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) { + battery_id = 0; + ddata->check_nvmem = true; + dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n"); + } + nvmem_device_put(nvmem); } switch (battery_id) { From 9227b387eee50dbf0e2fc67047a877177f121f74 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 14:33:38 +0200 Subject: [PATCH 1843/3297] power: supply: max17042: fix OF node reference imbalance commit 68d234144b7dffd1f50b07ba74d0d6e833ef43a4 upstream. The driver reuses the OF node of the parent multi-function device but fails to take another reference to balance the one dropped by the platform bus code when unbinding the MFD and deregistering the child devices. Fix this by using the intended helper for reusing OF nodes. Fixes: 0cd4f1f77ad4 ("power: supply: max17042: add platform driver variant") Cc: stable@vger.kernel.org # 6.14 Cc: Dzmitry Sankouski Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260407123338.2677375-1-johan@kernel.org Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/max17042_battery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index a32ff503f45f7..ad7e3b5b1fa31 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -1165,7 +1165,8 @@ static int max17042_platform_probe(struct platform_device *pdev) if (!i2c) return -EINVAL; - dev->of_node = dev->parent->of_node; + device_set_of_node_from_dev(dev, dev->parent); + id = platform_get_device_id(pdev); irq = platform_get_irq(pdev, 0); From 1697d253f51cf5e3825a3423ff49e128a3502ab2 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sat, 28 Mar 2026 12:25:34 +0530 Subject: [PATCH 1844/3297] mm/hugetlb: fix hugetlb cgroup rsvd charge/uncharge mismatch commit 15807d0ddde37407af72859426b654f3d1972b00 upstream. In alloc_hugetlb_folio(), a single h_cg pointer is used for both the rsvd and non-rsvd hugetlb cgroup charges. When map_chg is set, hugetlb_cgroup_charge_cgroup_rsvd() stores the charged cgroup in h_cg, but the immediately following hugetlb_cgroup_charge_cgroup() overwrites h_cg with the non-rsvd cgroup pointer. As a result, hugetlb_cgroup_commit_charge_rsvd() stores the wrong (non-rsvd) cgroup pointer into the folio's rsvd slot. When the folio is later freed, free_huge_folio() unconditionally calls both hugetlb_cgroup_uncharge_folio() and hugetlb_cgroup_uncharge_folio_rsvd(). The rsvd uncharge reads back the wrong cgroup from the folio and decrements a counter that was never charged for that cgroup, causing a page_counter underflow: page_counter underflow: -512 nr_pages=512 WARNING: mm/page_counter.c:61 at page_counter_cancel Fix this by introducing a separate h_cg_rsvd pointer exclusively for the rsvd charge path, keeping the rsvd and non-rsvd charges fully independent through their charge, commit, and error uncharge paths. Link: https://lore.kernel.org/20260328065534.346053-1-kartikey406@gmail.com Fixes: 08cf9faf7558 ("hugetlb_cgroup: support noreserve mappings") Reported-by: syzbot+226c1f947186f8fef796@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=226c1f947186f8fef796 Signed-off-by: Deepanshu Kartikey Reviewed-by: Muchun Song Cc: David Hildenbrand Cc: Oscar Salvador Cc: Mina Almasry Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/hugetlb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 9ac5df16b1a92..e0b36a634eb62 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2959,6 +2959,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, map_chg_state map_chg; int ret, idx; struct hugetlb_cgroup *h_cg = NULL; + struct hugetlb_cgroup *h_cg_rsvd = NULL; gfp_t gfp = htlb_alloc_mask(h) | __GFP_RETRY_MAYFAIL; idx = hstate_index(h); @@ -3009,7 +3010,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, */ if (map_chg) { ret = hugetlb_cgroup_charge_cgroup_rsvd( - idx, pages_per_huge_page(h), &h_cg); + idx, pages_per_huge_page(h), &h_cg_rsvd); if (ret) goto out_subpool_put; } @@ -3051,7 +3052,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, */ if (map_chg) { hugetlb_cgroup_commit_charge_rsvd(idx, pages_per_huge_page(h), - h_cg, folio); + h_cg_rsvd, folio); } spin_unlock_irq(&hugetlb_lock); @@ -3106,7 +3107,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, out_uncharge_cgroup_reservation: if (map_chg) hugetlb_cgroup_uncharge_cgroup_rsvd(idx, pages_per_huge_page(h), - h_cg); + h_cg_rsvd); out_subpool_put: /* * put page to subpool iff the quota of subpool's rsv_hpages is used From 722e6c54bde6391fb95f0357f555aca887c7b18c Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:51 +0800 Subject: [PATCH 1845/3297] mm/memory_hotplug: fix incorrect altmap passing in error path commit 2fac4afa0e2e68841334c78c1821e49f74fbc66a upstream. In create_altmaps_and_memory_blocks(), when arch_add_memory() succeeds with memmap_on_memory enabled, the vmemmap pages are allocated from params.altmap. If create_memory_block_devices() subsequently fails, the error path calls arch_remove_memory() with a NULL altmap instead of params.altmap. This is a bug that could lead to memory corruption. Since altmap is NULL, vmemmap_free() falls back to freeing the vmemmap pages into the system buddy allocator via free_pages() instead of the altmap. arch_remove_memory() then immediately destroys the physical linear mapping for this memory. This injects unowned pages into the buddy allocator, causing machine checks or memory corruption if the system later attempts to allocate and use those freed pages. Fix this by passing params.altmap to arch_remove_memory() in the error path. Link: https://lore.kernel.org/20260428081855.1249045-3-songmuchun@bytedance.com Fixes: 6b8f0798b85a ("mm/memory_hotplug: split memmap_on_memory requests across memblocks") Signed-off-by: Muchun Song Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Reviewed-by: Georgi Djakov Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Mike Rapoport (Microsoft) Cc: Nicholas Piggin Cc: Oscar Salvador Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memory_hotplug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 6a7714179c205..b7030bcd9b03e 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1489,7 +1489,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, ret = create_memory_block_devices(cur_start, memblock_size, nid, params.altmap, group); if (ret) { - arch_remove_memory(cur_start, memblock_size, NULL); + arch_remove_memory(cur_start, memblock_size, params.altmap); kfree(params.altmap); goto out; } From 640627f07c79be6fc1a572fc749490a8f62844c2 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 21:29:40 -0700 Subject: [PATCH 1846/3297] mm/damon/core: make charge_addr_from aware of end-address exclusivity commit 5a2d162e22bf33eb89d53e802d0fc1ec422e19b6 upstream. DAMON region end address is exclusive one, but charge_addr_from is assigned assuming the end address is inclusive. As a result, DAMOS action to next up to min_region_sz memory can be skipped. This is quite negligible user impact. But, the bug is a bug that can be very simply fixed. Fix the wrong assignment to respect the exclusiveness of the address. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260428042942.118230-1-sj@kernel.org Link: https://lore.kernel.org/20260428032324.115663-1-sj@kernel.org [1] Fixes: 50585192bc2e ("mm/damon/schemes: skip already charged targets and regions") Signed-off-by: SeongJae Park Cc: # 5.16.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 00c80b7c8c3f6..afd1a372ecd78 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1950,7 +1950,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t, quota->charged_sz += sz; if (quota->esz && quota->charged_sz >= quota->esz) { quota->charge_target_from = t; - quota->charge_addr_from = r->ar.end + 1; + quota->charge_addr_from = r->ar.end; } } if (s->action != DAMOS_STAT) From ccd6b70798737573a5e7cf2759aa99a24bd1de1b Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Wed, 6 May 2026 15:55:54 +0800 Subject: [PATCH 1847/3297] fs/ntfs3: fix syncing wrong inode on DIRSYNC cross-directory rename commit 932fa0c1496286e39e14e27194c1ee7c2181629f upstream. In ntfs3_rename(), when IS_DIRSYNC(new_dir) is true, the code syncs the renamed file inode instead of the target directory new_dir: if (IS_DIRSYNC(new_dir)) ntfs_sync_inode(inode); /* should be new_dir */ DIRSYNC requires that directory metadata changes are written to disk synchronously. Since new_dir was modified (a new directory entry was added), it is new_dir that must be synced to satisfy the guarantee, not the renamed file itself. This bug has existed since the initial ntfs3 implementation and was carried through the refactoring in commit 78ab59fee07f ("fs/ntfs3: Rework file operations"). Fix by syncing new_dir instead of inode. Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 612923d93f4dc..747a2a66c488d 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -340,7 +340,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, ntfs_sync_inode(dir); if (IS_DIRSYNC(new_dir)) - ntfs_sync_inode(inode); + ntfs_sync_inode(new_dir); } if (dir_ni != new_dir_ni) From 09fddd52c1b0cef2c086d61be8f3d5dc92e36565 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 2 Jun 2026 15:21:03 +0200 Subject: [PATCH 1848/3297] fs/ntfs3: bound DeleteIndexEntryAllocation memmove length commit fc4626bb3656362de8b0ecd56605d47a19ec3518 upstream. In do_action()'s DeleteIndexEntryAllocation case, e->size comes from an on-disk INDEX_BUFFER entry. When e->size makes e + e->size point past hdr + hdr->used, PtrOffset(e1, Add2Ptr(hdr, used)) returns a negative ptrdiff_t that is silently cast to a quasi-infinite size_t when passed to memmove(). The memmove then walks past the destination buffer. The sibling DeleteIndexEntryRoot case at fslog.c:3540-3543 already carries the corresponding guard: if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize || Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) || used + esize > le32_to_cpu(hdr->total)) { goto dirty_vol; } Apply the same shape to the allocation-path case. Also reject esize == 0: memmove(e, e, ...) is a no-op and leaves hdr->used unchanged, hiding a malformed entry from the existing check_index_header() walk. Reproduced under UML+KASAN on mainline 8d90b09e6741 by mounting a crafted NTFS image: the unguarded memmove takes a length of 0xffffffffffffff00 and the kernel oopses in memmove+0x81/0x1a0 on the do_action+0x36a2 frame. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 8395ca003af03..18e2b4d47e6f6 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3572,9 +3572,23 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, } e1 = Add2Ptr(e, esize); - nsize = esize; used = le32_to_cpu(hdr->used); + /* + * Reject crafted entries whose e->size makes e + esize + * point past the INDEX_HDR's used boundary. Without this, + * PtrOffset(e1, hdr + used) underflows to a quasi-infinite + * size_t when fed to the memmove() below. + * + * Also reject esize == 0: memmove(e, e, ...) is a no-op and + * leaves hdr->used unchanged, masking the crafted entry. + */ + if (!esize || Add2Ptr(e, esize) > Add2Ptr(hdr, used) || + PtrOffset(e1, Add2Ptr(hdr, used)) < esize) + goto dirty_vol; + + nsize = esize; + memmove(e, e1, PtrOffset(e1, Add2Ptr(hdr, used))); hdr->used = cpu_to_le32(used - nsize); From d240cd98f5f7b65c90f6b2b6abe3232ccdc405ab Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 12:34:05 -0400 Subject: [PATCH 1849/3297] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass commit 5e7b598660cfa8e5af172cf4c65cffc126333307 upstream. In log_replay()'s analysis pass, after find_dp() returns a valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple, the copy_lcns block walks lrh->lcns_follow further entries: t16 = le16_to_cpu(lrh->lcns_follow); for (i = 0; i < t16; i++) { size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn)); dp->page_lcns[j + i] = lrh->page_lcns[i]; } find_dp() only validates that target_vcn falls within [dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST cluster is covered. The walk through the further entries is not bounded against dp->lcns_follow. For a malformed LRH where target_vcn = dp->vcn + dp->lcns_follow - 1 and lrh->lcns_follow > 1, the i > 0 writes overflow the dp's allocated page_lcns[] array. Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard. Reproduced under UML+KASAN on mainline 8d90b09e6741 as a slab-out-of-bounds write of size 8 from log_replay+0x68d4 on the mount path. This is distinct from Pavitra Jha's 2026-05-02 patch ("fs/ntfs3: validate lcns_follow in log_replay conversion", <20260502154252.164586-1-jhapavitra98@gmail.com>) which addresses the separate version-0 dirty-page-table conversion path's memmove(&dp->vcn, ...) call. The two fixes are complementary; both should land. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes, fixed conflicts] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 18e2b4d47e6f6..54eab22c31a3c 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3363,8 +3363,8 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), attr_run(attr), - le32_to_cpu(attr->size) - - le16_to_cpu(attr->nres.run_off), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), &t64)) { goto dirty_vol; } @@ -4578,22 +4578,34 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - t32 = le32_to_cpu(dp->lcns_follow); - if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { - err = -EINVAL; - goto out; - } - - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - if (j >= t32 || i >= t32 - j) { - err = -EINVAL; - goto out; - } - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } + + /* + * find_dp() only validates that target_vcn is the first + * cluster covered by dp. The walk through lrh->lcns_follow + * further entries must stay within the allocated + * dp->page_lcns[] array, which is sized by dp->lcns_follow. + */ + if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 > + le32_to_cpu(dp->lcns_follow)) { + err = -EINVAL; + log->set_dirty = true; + goto out; + } + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; case DeleteDirtyClusters: { From 50b5e83384e7fed3d11d18b79ff350e9d6d89861 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 2 Jun 2026 15:15:47 +0200 Subject: [PATCH 1850/3297] fs/ntfs3: bound attr_off in UpdateResidentValue against data_off commit d1570c48f49a693974d000251030370ee2e83539 upstream. In do_action()'s UpdateResidentValue case (fslog.c:3307), lrh->attr_off and lrh->redo_len come from the on-disk LRH. When they satisfy aoff + dlen < attr->res.data_off, the assignment attr->res.data_size = cpu_to_le32(aoff + dlen - data_off); underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1, data_off=0x18). Subsequent code that reads attr->res.data_size to walk the resident attribute payload would then read up to 4 GiB past the 1024-byte MFT record allocation. The existing mi_enum_attr() defense in fs/ntfs3/record.c:287 catches the corrupted data_size on the next attribute walk and fails the mount, but only on the path that walks all attributes. A read site that picks an attribute by name and reads its data_size without re-validating is not covered. Validate aoff against data_off and asize at the source. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe: with aoff=0x10 and data_off=0x18, the post-assignment data_size is 0xfffffff9 (mount then fails at -22 from mi_enum_attr). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 54eab22c31a3c..beef22ea8f176 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3311,6 +3311,17 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, nsize = ALIGN(nsize, 8); data_off = le16_to_cpu(attr->res.data_off); + /* + * aoff comes from the on-disk lrh->attr_off. Forbid + * writes that begin below the resident attribute's + * data_off (which would overwrite the resident header), + * and forbid aoff + dlen < data_off, which would make + * the data_size assignment below underflow to ~4 GiB. + */ + if (aoff < data_off || aoff + dlen < data_off || + aoff + dlen > asize) + goto dirty_vol; + if (nsize < asize) { memmove(Add2Ptr(attr, aoff), data, dlen); data = NULL; // To skip below memmove(). From 7adb38279812c9c06b0e3fa7382f4d7887f3fa2d Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Mon, 1 Jun 2026 10:57:56 +0200 Subject: [PATCH 1851/3297] fs/ntfs3: validate lcns_follow in log_replay conversion commit 6a4c53a2e26a865565bd6a460961e8d6fcb32329 upstream. log_replay() converts DIR_PAGE_ENTRY_32 records into DIR_PAGE_ENTRY records when replaying version 0 restart tables. During this conversion, the memmove() length is derived directly from the on-disk lcns_follow field: memmove(&dp->vcn, &dp0->vcn_low, 2 * sizeof(u64) + le32_to_cpu(dp->lcns_follow) * sizeof(u64)); check_rstbl() validates restart table structure, but does not constrain per-entry lcns_follow values relative to the entry size. A malformed filesystem image can provide an oversized lcns_follow value, causing the conversion memmove() to access memory beyond the bounds of the allocated restart table buffer. The same field is later used to bound iteration over page_lcns[], so validating lcns_follow during conversion also prevents downstream out-of-bounds access from the same malformed metadata. Compute the maximum valid lcns_follow from the already-validated restart table entry size and reject entries that exceed this bound. Reuse the existing t16/t32 scratch variables already declared in log_replay() to avoid introducing new declarations. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha [almaz.alexandrovich@paragon-software.com: fixed the conflicts] Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index beef22ea8f176..22ab83221d55d 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -4261,13 +4261,26 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) if (rst->major_ver) goto end_conv_1; /* reduce tab pressure. */ + t16 = le16_to_cpu(dptbl->size); + if (t16 < sizeof(struct DIR_PAGE_ENTRY)) { + log->set_dirty = true; + goto out; + } + + t32 = (t16 - sizeof(struct DIR_PAGE_ENTRY)) / sizeof(u64); + dp = NULL; while ((dp = enum_rstbl(dptbl, dp))) { struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp; - // NOTE: Danger. Check for of boundary. + u32 lcns = le32_to_cpu(dp->lcns_follow); + + if (lcns > t32) { + log->set_dirty = true; + goto out; + } + memmove(&dp->vcn, &dp0->vcn_low, - 2 * sizeof(u64) + - le32_to_cpu(dp->lcns_follow) * sizeof(u64)); + 2 * sizeof(u64) + lcns * sizeof(u64)); } end_conv_1: From 908c9243ba309997b73cbda3e4c563d0fb345ee9 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 13 Apr 2026 09:31:17 -0400 Subject: [PATCH 1852/3297] fs/ntfs3: add depth limit to indx_find_buffer to prevent stack overflow commit 1ebd684b8f627f75bc3e03f8b2ad8400fd1f02cd upstream. indx_find_buffer() recursively descends the B+ tree index with no depth limit. A crafted NTFS image with circular index node references causes unbounded recursion, overflowing the kernel stack and panicking the system. This is reachable by mounting a malicious NTFS filesystem (e.g. from a USB drive via desktop automount) and deleting a file whose index entry triggers the rebalancing fallback path in indx_delete_entry(). Add a depth parameter and bail out with -EINVAL when it reaches the fnd->nodes array bound, matching the constraint already enforced by fnd_push() in indx_find(). The related function indx_find() was previously patched for a similar infinite-loop issue (commit 1732053c8a6b), but indx_find_buffer() was missed. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") 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: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/index.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 050b3709e0204..77cdc5487ff65 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -2014,13 +2014,21 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, static struct indx_node *indx_find_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, const struct INDEX_ROOT *root, - __le64 vbn, struct indx_node *n) + __le64 vbn, struct indx_node *n, + int depth) { int err; const struct NTFS_DE *e; struct indx_node *r; const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr; + /* + * Limit recursion depth to prevent stack overflow from crafted + * images. Use the same bound as the fnd->nodes array (20). + */ + if (depth > ARRAY_SIZE(((struct ntfs_fnd *)NULL)->nodes)) + return ERR_PTR(-EINVAL); + /* Step 1: Scan one level. */ for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { if (!e) @@ -2041,7 +2049,8 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, if (err) return ERR_PTR(err); - r = indx_find_buffer(indx, ni, root, vbn, n); + r = indx_find_buffer(indx, ni, root, vbn, n, + depth + 1); if (r) return r; } @@ -2446,7 +2455,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, fnd_clear(fnd); - in = indx_find_buffer(indx, ni, root, sub_vbn, NULL); + in = indx_find_buffer(indx, ni, root, sub_vbn, NULL, 0); if (IS_ERR(in)) { err = PTR_ERR(in); goto out; From be306b8d9143a9c076c804a7ca025d69caf9c448 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 05:51:35 -0400 Subject: [PATCH 1853/3297] fs/ntfs3: bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation} commit 3e127829e57f5190f612412ece4541cb96d5ec7a upstream. In do_action()'s UpdateRecordDataRoot (fslog.c:3489) and UpdateRecordDataAllocation (fslog.c:3697) cases, the memmove destination is `Add2Ptr(e, le16_to_cpu(e->view.data_off))`, where e->view.data_off comes from an on-disk NTFS_DE inside an INDEX_ROOT or INDEX_BUFFER. Neither case validates view.data_off + dlen against e->size; the existing check_if_index_root / check_if_alloc_index helpers walk the entry chain and validate the entry's offset, but not its internal view fields. The neighbouring read sites (e.g., fs/ntfs3/index.c when iterating view entries) check view.data_off + view.data_size <= e->size. Apply the same bound at the two memmove sites. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe instrumentation: with view.data_off forced to 0xFFFC, the memmove writes 32 bytes past the end of the NTFS_DE. This is similar in shape to Pavitra Jha's 2026-05-02 patch "fs/ntfs3: prevent oob in case UpdateRecordDataRoot" (<20260502105008.21827-1-jhapavitra98@gmail.com>) which proposes calling ntfs3_bad_de_range(); that helper does not exist in mainline. This patch uses inline checks. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Reported-by: Pavitra Jha Closes: https://lore.kernel.org/ntfs3/20260502105008.21827-1-jhapavitra98@gmail.com/ Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 22ab83221d55d..f4edf0471064a 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3510,6 +3510,18 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); + /* + * e->view.data_off and dlen come from the on-disk + * INDEX_ROOT entry / LRH. The neighbouring read sites + * (e.g. fs/ntfs3/index.c) check that + * view.data_off + view.data_size <= e->size; mirror that + * bound here so the memmove cannot reach past the entry. + */ + if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || + le16_to_cpu(e->view.data_off) + dlen > + le16_to_cpu(e->size)) + goto dirty_vol; + memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); mi->dirty = true; @@ -3716,6 +3728,12 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, goto dirty_vol; } + /* See UpdateRecordDataRoot for the rationale. */ + if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || + le16_to_cpu(e->view.data_off) + dlen > + le16_to_cpu(e->size)) + goto dirty_vol; + memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); a_dirty = true; From 0fad25687d4d3fa1fdd313d31b9cb5817c425029 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 19:41:40 -0400 Subject: [PATCH 1854/3297] ntfs3: cap RESTART_TABLE free-chain walker at rt->used commit 9611f644302c07d21bc8af97e3e06a3d30064253 upstream. A crafted NTFS3 disk image triggers an in-kernel infinite loop at mount time, hanging the mounting thread and firing the soft-lockup watchdog within ~22s on multi-CPU hosts (panic with kernel.softlockup_panic=1). The bug is reachable from desktop USB auto-mount on distributions where udisks2 routes the NTFS signature to the in-tree ntfs3 driver (Arch family and an increasing fraction of Fedora / openSUSE / RHEL deployments); CAP_SYS_ADMIN-class manual mount elsewhere. check_rstbl()'s second walker iterates the free-entry singly-linked list headed by rt->first_free with no upper bound on iteration count: for (off = ff; off;) { if (off == RESTART_ENTRY_ALLOCATED) return false; off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off)); if (off > ts - sizeof(__le32)) return false; } The existing guards cover three exits: end-of-list (off == 0), the in-use marker (off == RESTART_ENTRY_ALLOCATED), and out-of-bounds (off > ts - sizeof(__le32)). None of the three prevents an in-bounds cycle. A crafted on-disk RESTART_TABLE whose free chain contains a self-loop or A->B->A cycle whose offsets satisfy: - in range [sizeof(struct RESTART_TABLE), ts - sizeof(__le32)] - (off - sizeof(struct RESTART_TABLE)) % rsize == 0 passes all existing guards and spins the mount-time thread forever. Reproduced in UML by hand-forging a 2 MB NTFS3 image whose journal RESTART_TABLE first_free = 0x18 and whose entry at offset 0x18 stores 0x18 as its next pointer; mount of the forged image with the in-tree ntfs3 driver never returns. Bound the walker by rt->used. Each entry on a legitimate free chain is unique, and the total slot count is ne = le16_to_cpu (rt->used). A traversal that visits more than ne slots is by construction malformed; reject it as a corrupt RESTART_TABLE. After this patch, mount of the forged image returns with -EINVAL and a log_replay failure message, and mkntfs-produced legitimate images mount cleanly (verified in the same UML harness). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/fslog.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index f4edf0471064a..a0503a3344237 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -764,8 +764,19 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) /* * Walk through the list headed by the first entry to make * sure none of the entries are currently being used. + * + * Bound traversal by ne (rt->used) to defeat a crafted on-disk + * cycle in the free chain. Each entry in a legitimate free + * list is unique, so a chain that visits more than ne slots + * is malformed. Without this guard, an attacker-controlled + * RESTART_TABLE with a self-loop or A->B->A cycle whose + * offsets satisfy the existing alignment + in-bounds guards + * spins forever at mount time. */ - for (off = ff; off;) { + for (off = ff, i = 0; off; i++) { + if (i > ne) + return false; + if (off == RESTART_ENTRY_ALLOCATED) return false; From aaa1f956c0fc41089a4a534da7df91552a08a47c Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 19:33:05 -0400 Subject: [PATCH 1855/3297] ntfs3: bound to_move in indx_insert_into_root before hdr_insert_head commit 9b6926ac9c970ae0b2c2fe6289b16e9aa10b6a67 upstream. indx_insert_into_root() promotes a full resident $INDEX_ROOT into $INDEX_ALLOCATION and copies all non-last resident root entries into a newly allocated INDEX_BUFFER via hdr_insert_head(). The source byte count 'to_move' is summed from the on-disk resident entry sizes and is independent of the destination buffer size, which comes from root->index_block_size (via indx->index_bits). A crafted NTFS image that keeps a valid, full resident root but shrinks root->index_block_size down to 512 after the root has been populated makes hdr_insert_head() memcpy attacker-controlled resident entry bytes past the end of the kmalloc(1u << indx->index_bits) allocation returned by indx_new(). For a 512-byte destination and a resident root whose non-last entries total 560 bytes, the memcpy overruns by 120 bytes and a following memmove extends the highest written offset to 136 bytes past the allocation. The overflow bytes are a direct copy of on-disk entries (via kmemdup), so they are fully attacker-controlled. The write is reachable from unprivileged open(O_CREAT) on a mounted crafted NTFS image: a single sufficiently long create in a directory whose resident root is already full forces root promotion and triggers the copy. This is a controlled out-of-bounds write of 120-136 bytes past a kmalloc(index_block_size) allocation, with attacker-controlled content. It is a bounded adjacent-heap corruption primitive; it is not an arbitrary-address write. Successful exploitation into a named victim object depends on the surrounding slab layout. Reject the copy at the sink. The destination's INDEX_HDR already reports hdr_total (the payload capacity of the new buffer) and hdr_used (the bytes already consumed by the terminal END entry installed by indx_new()); require that to_move fits in the remaining payload before calling hdr_insert_head(). On mismatch, fail with -EINVAL and mark the filesystem as having a detected on-disk inconsistency, which is the same behaviour as the surrounding validation in this function. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/index.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 77cdc5487ff65..d274c05424867 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1743,6 +1743,22 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, hdr_used = le32_to_cpu(hdr->used); hdr_total = le32_to_cpu(hdr->total); + /* + * The destination INDEX_BUFFER has 'hdr_total' bytes of payload + * available after the header, of which 'hdr_used' are already + * consumed by the single terminal END entry installed by + * indx_new(). A crafted image can present a resident root whose + * non-last entries (summing to 'to_move') exceed what fits in + * this buffer; copying them unchecked would overrun the + * kmalloc(1u << indx->index_bits) allocation backing the new + * buffer. Reject the copy in that case. + */ + if (to_move > hdr_total - hdr_used) { + err = -EINVAL; + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + goto out_put_n; + } + /* Copy root entries into new buffer. */ hdr_insert_head(hdr, re, to_move); From f3624cc069195001c88df7a291af215f2133ff2c Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 18:57:12 -0400 Subject: [PATCH 1856/3297] ntfs3: validate split-point offset in indx_insert_into_buffer commit f1df9d771df47aa40de6d70949c28720ae1e430d upstream. indx_insert_into_buffer() computes used = used1 - to_copy - sp_size; memmove(de_t, Add2Ptr(sp, sp_size), used - le32_to_cpu(hdr1->de_off)); where sp and sp_size come from hdr_find_split(). hdr_find_split() walks entries by le16_to_cpu(e->size) without validating that each step stays within hdr->used or that the size field is at least sizeof(struct NTFS_DE). index_hdr_check(), the on-load gatekeeper, only validates header-level fields (used, total, de_off) and does not walk per-entry sizes. A crafted NTFS image whose leaf INDEX_HDR reports used == total but contains one interior NTFS_DE with size = 0xFFF0 therefore passes validation, descends to indx_insert_into_buffer() through the ntfs_create() -> indx_insert_entry() path, and makes hdr_find_split() return an sp whose sp_size (0xFFF0) greatly exceeds the remaining bytes in the buffer. The u32 subtraction underflows and the memmove count becomes a near-4-GiB value, producing an out-of-bounds kernel write that corrupts adjacent allocations and panics the kernel. Reproduced on 7.0.0-rc7 with UML + KASAN via a crafted image and a single 'touch' inside the mounted directory; crash site resolves to fs/ntfs3/index.c at the memmove. Trigger requires only local mount of an attacker-supplied filesystem image (USB, loopback, or removable media auto-mount). Reject the split whenever the chosen sp plus its declared size already extends past hdr1->used. This is the minimal fix; it preserves the existing hdr_find_split() contract and relies on the same out: cleanup path as the pre-existing error returns. A prior OOB read in the very same indx_insert_into_buffer() memmove was fixed in commit b8c44949044e ("fs/ntfs3: Fix OOB read in indx_insert_into_buffer") by tightening hdr_find_e(), but that fix does not cover the split-point size field path addressed here: sp is returned by hdr_find_split(), not hdr_find_e(), and the underflow is driven by sp->size rather than hdr->used exceeding hdr->total. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Reported-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/index.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index d274c05424867..a9c5dcc23a5ce 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1863,6 +1863,20 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, memcpy(up_e, sp, sp_size); used1 = le32_to_cpu(hdr1->used); + + /* + * hdr_find_split does not validate per-entry sizes, so a crafted + * NTFS_DE whose le16 size field is out of range can place sp such + * that (PtrOffset(hdr1, sp) + sp_size) exceeds used1. Without this + * guard the u32 'used = used1 - to_copy - sp_size' underflows and + * the subsequent memmove count becomes a near-4-GiB value, + * triggering an out-of-bounds kernel write. + */ + if (PtrOffset(hdr1, sp) + sp_size > used1) { + err = -EINVAL; + goto out; + } + hdr1_saved = kmemdup(hdr1, used1, GFP_NOFS); if (!hdr1_saved) { err = -ENOMEM; From ff05a98150ebb2b03919a9c05c576681e86abbb7 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Sat, 18 Apr 2026 13:11:18 +0000 Subject: [PATCH 1857/3297] ntfs3: fix out-of-bounds read in decompress_lznt commit 7160a57192fb16d7a6fa9b7f5c7ac341d2444a89 upstream. decompress_lznt() does not validate array index bounds before accessing the decompression table. A corrupted NTFS3 image with invalid compressed data can trigger an out-of-bounds read. Add index bounds checking to prevent the OOB access. Reported-by: syzbot+39b2fb0f2638669008ec@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Signed-off-by: Konstantin Komarov Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/lznt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/lznt.c b/fs/ntfs3/lznt.c index fdc9b2ebf3410..f818d97850049 100644 --- a/fs/ntfs3/lznt.c +++ b/fs/ntfs3/lznt.c @@ -240,7 +240,7 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, if (up - unc > LZNT_CHUNK_SIZE) return -EINVAL; /* Correct index */ - while (unc + s_max_off[index] < up) + while (index < ARRAY_SIZE(s_max_off) - 1 && unc + s_max_off[index] < up) index += 1; /* Check the current flag for zero. */ From 1f18aac2637220b5847d073447498e81ddca10b2 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 23:16:56 +0000 Subject: [PATCH 1858/3297] landlock: Fix LANDLOCK_SCOPE_SIGNAL bypass on the SIGIO path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4b80320ca7ed03d6e683f95b6066565dc97b9f92 upstream. LANDLOCK_SCOPE_SIGNAL must prevent a sandboxed process from signaling processes outside its Landlock domain. It can be bypassed through the asynchronous SIGIO delivery path. A sandboxed process that owns any file or socket can arm it with fcntl(fd, F_SETOWN, -pgid), fcntl(fd, F_SETSIG, SIGKILL) and O_ASYNC, so that an I/O event makes the kernel deliver the chosen signal to the whole process group. As the head of its process group's task list (the default position right after fork()) that group can also hold the non-sandboxed process that launched it, e.g. a supervisor or a security monitor. The sandbox can thus kill or signal the processes LANDLOCK_SCOPE_SIGNAL is meant to protect from it. The scope is enforced in hook_file_send_sigiotask() against the Landlock domain recorded at F_SETOWN time, not the live domain of the sender. control_current_fowner() decides whether to record that domain and skips recording it when the fowner target is in the caller's thread group, which is safe only for a single-task target (PIDTYPE_PID, PIDTYPE_TGID). For a process group (PIDTYPE_PGID) pid_task() returns only one member; recording is skipped whenever that member shares the caller's thread group, and hook_file_send_sigiotask() then lets the signal fan out to the whole group unchecked. Record the domain for every non single-process target so the scope is enforced against each group member at delivery time. That recording is necessary but not sufficient on its own: the kernel signals a process group through its members' thread-group leaders, and the leader of the registrant's own process can carry a different Landlock domain than the sibling thread that armed the owner. domain_is_scoped() would then deny that leader, even though commit 18eb75f3af40 ("landlock: Always allow signals between threads of the same process") requires same-process delivery to be allowed. hook_task_kill() avoids this by evaluating same_thread_group() live, per recipient; the SIGIO path instead delegates the whole decision to a single registration-time check, which a process-group fan-out cannot honor. So also record the registrant's thread group next to its domain and exempt it at delivery: hook_file_send_sigiotask() allows the signal whenever the recipient belongs to the registrant's own process, restoring the same-process guarantee while keeping out-of-domain group members blocked. The direct kill() path (hook_task_kill) already evaluates the live domain and is unaffected. Fixes: 18eb75f3af40 ("landlock: Always allow signals between threads of the same process") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Günther Noack Link: https://patch.msgid.link/56bffc24f3d0d08b45a686a48e99766b0a0821fa.1780614610.git.hexlabsecurity@proton.me [mic: Check pid_type earlier and improve comment, fix commit message, fix comment formatting] Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- security/landlock/fs.c | 14 ++++++++++++++ security/landlock/fs.h | 10 ++++++++++ security/landlock/task.c | 11 +++++++++++ 3 files changed, 35 insertions(+) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index a2ed0e76938aa..4f8dfeadbbe22 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1798,6 +1798,14 @@ static bool control_current_fowner(struct fown_struct *const fown) */ lockdep_assert_held(&fown->lock); + /* + * A process-group or session owner (PIDTYPE_PGID/PIDTYPE_SID) fans the + * signal out to every member at delivery time, so record the domain and + * let hook_file_send_sigiotask() check the live scope per recipient. + */ + if (fown->pid_type != PIDTYPE_PID && fown->pid_type != PIDTYPE_TGID) + return true; + /* * Some callers (e.g. fcntl_dirnotify) may not be in an RCU read-side * critical section. @@ -1814,6 +1822,7 @@ static void hook_file_set_fowner(struct file *file) { struct landlock_ruleset *prev_dom; struct landlock_cred_security fown_subject = {}; + struct pid *prev_tg, *fown_tg = NULL; size_t fown_layer = 0; if (control_current_fowner(file_f_owner(file))) { @@ -1826,21 +1835,26 @@ static void hook_file_set_fowner(struct file *file) if (new_subject) { landlock_get_ruleset(new_subject->domain); fown_subject = *new_subject; + fown_tg = get_pid(task_tgid(current)); } } prev_dom = landlock_file(file)->fown_subject.domain; + prev_tg = landlock_file(file)->fown_tg; landlock_file(file)->fown_subject = fown_subject; + landlock_file(file)->fown_tg = fown_tg; #ifdef CONFIG_AUDIT landlock_file(file)->fown_layer = fown_layer; #endif /* CONFIG_AUDIT*/ /* May be called in an RCU read-side critical section. */ landlock_put_ruleset_deferred(prev_dom); + put_pid(prev_tg); } static void hook_file_free_security(struct file *file) { + put_pid(landlock_file(file)->fown_tg); landlock_put_ruleset_deferred(landlock_file(file)->fown_subject.domain); } diff --git a/security/landlock/fs.h b/security/landlock/fs.h index bf9948941f2fb..911b83669e207 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -78,6 +78,16 @@ struct landlock_file_security { * euid. */ struct landlock_cred_security fown_subject; + /** + * @fown_tg: Thread group of the task that set the file owner, pinned + * while @fown_subject holds a domain. It lets + * hook_file_send_sigiotask() always allow a SIGIO delivered to the + * owner's own process -- e.g. the thread-group leader reached through a + * process-group owner -- matching the same-process exemption of + * hook_task_kill(). NULL when no domain is recorded. Protected by + * file->f_owner->lock, like @fown_subject. + */ + struct pid *fown_tg; }; #ifdef CONFIG_AUDIT diff --git a/security/landlock/task.c b/security/landlock/task.c index 2385017418ca5..492a5ff62b082 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -407,6 +407,17 @@ static int hook_file_send_sigiotask(struct task_struct *tsk, if (!subject->domain) return 0; + /* + * Always allow delivery to the file owner's own process, including a + * thread-group leader reached through a process-group owner. This + * mirrors hook_task_kill()'s same-process exemption and preserves the + * guarantee of commit 18eb75f3af40 ("landlock: Always allow signals + * between threads of the same process"), which the registration-time + * check cannot honor for a process-group target. + */ + if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg) + return 0; + scoped_guard(rcu) { is_scoped = domain_is_scoped(subject->domain, From 193e6471e985c2f25d09b3fe96ec52f4eab89bd4 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 08:53:21 +0800 Subject: [PATCH 1859/3297] power: supply: charger-manager: fix refcount leak in is_full_charged() commit 4373cfa38ead58f980362c841b0d0bdf8c4d956c upstream. In is_full_charged(), power_supply_get_by_name() is called to obtain a reference to the fuel_gauge power supply. If the voltage check (uV >= desc->fullbatt_uV) succeeds, the function returns true directly without releasing the reference, leaking the refcount. Fix this by setting a flag and jumping to the out label where power_supply_put() properly drops the reference. Cc: stable@vger.kernel.org Fixes: e132fc6bb89b ("power: supply: charger-manager: Make decisions focussed on battery status") Signed-off-by: WenTao Liang Link: https://patch.msgid.link/20260611005322.53096-1-vulab@iscas.ac.cn Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/charger-manager.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index c49e0e4d02f77..c3644018b6bb4 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -303,8 +303,10 @@ static bool is_full_charged(struct charger_manager *cm) if (cm->battery_status == POWER_SUPPLY_STATUS_FULL && desc->fullbatt_vchkdrop_uV) uV += desc->fullbatt_vchkdrop_uV; - if (uV >= desc->fullbatt_uV) - return true; + if (uV >= desc->fullbatt_uV) { + is_full = true; + goto out; + } } } From bd2e9be9ebb64264087eee9674b94b4d6f2e7362 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 23:17:05 +0000 Subject: [PATCH 1860/3297] selftests/landlock: Test SCOPE_SIGNAL on the SIGIO/fowner pgid path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 76579d09beedaffe7fe76e9c05644f73983e1ceb upstream. Add regression tests for the LANDLOCK_SCOPE_SIGNAL handling of the asynchronous SIGIO delivery path (fcntl(F_SETOWN)) with a process-group owner. sigio_to_pgid_members covers the bypass: a sandboxed process at the head of its process group's PGID hlist (the default after fork()) arms F_SETOWN(-pgrp) + O_ASYNC and triggers the fan-out; the in-domain owner must be signaled (proving the trigger fired) while the non-sandboxed member of the group, outside the domain, must not. sigio_to_pgid_self covers the same-process guarantee: the owner is registered from a sandboxed non-leader thread, whose domain differs from the thread-group leader the kernel signals for a process-group owner. That leader belongs to the owner's own process and must still be signaled. Without the fix the first test sees the out-of-domain member signaled and the second sees the owner's own leader denied. Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Günther Noack Link: https://patch.msgid.link/43370e89f7a896a583bf33d1cd171d02630e61bf.1780614610.git.hexlabsecurity@proton.me [mic: Fix comment] Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- .../selftests/landlock/scoped_signal_test.c | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index d8bf33417619f..f24f2c28f62e5 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -559,4 +559,186 @@ TEST_F(fown, sigurg_socket) _metadata->exit_code = KSFT_FAIL; } +/* + * Checks that LANDLOCK_SCOPE_SIGNAL is enforced on the asynchronous SIGIO + * delivery path (fcntl(F_SETOWN)) when the file owner is a process group. + * + * A sandboxed process sitting at the head of its process group's PID hlist (the + * default position right after fork()) used to escape the fcntl(F_SETOWN, + * -pgrp) domain recording: pid_task(pgrp, PIDTYPE_PGID) resolved to the process + * itself, so the same-thread-group exemption skipped recording its Landlock + * domain. At SIGIO time that domain was then unset and the signal fanned out + * to every group member, including non-sandboxed processes outside the domain. + */ +TEST(sigio_to_pgid_members) +{ + int trigger[2], sync_child[2]; + char buf; + pid_t child; + int status, i; + + drop_caps(_metadata); + + /* + * Isolates the test in its own process group so the SIGIO fan-out stays + * bounded to this parent and the child forked below. + */ + ASSERT_EQ(0, setpgid(0, 0)); + + /* The non-sandboxed parent is the protected (out-of-domain) target. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC)); + ASSERT_EQ(0, pipe2(sync_child, O_CLOEXEC)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + /* + * The child inherits the parent's new process group and, just + * attached with hlist_add_head_rcu(), is now the head of the + * pgid hlist: this is the case that used to skip the recording. + */ + EXPECT_EQ(0, close(sync_child[0])); + + /* In-domain positive control: the child must be signaled. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL); + + /* Owns the SIGIO source for the whole process group. */ + ASSERT_EQ(0, fcntl(trigger[0], F_SETSIG, SIGURG)); + ASSERT_EQ(0, fcntl(trigger[0], F_SETOWN, -getpgrp())); + ASSERT_EQ(0, fcntl(trigger[0], F_SETFL, O_ASYNC)); + + /* Fans SIGURG out to every member of the process group. */ + ASSERT_EQ(1, write(trigger[1], ".", 1)); + + /* + * The sandboxed child is in its own domain and must always be + * signaled: this proves the SIGIO actually fired. + */ + for (i = 0; i < 1000 && !signal_received; i++) + usleep(1000); + EXPECT_EQ(1, signal_received); + + ASSERT_EQ(1, write(sync_child[1], ".", 1)); + EXPECT_EQ(0, close(sync_child[1])); + + _exit(_metadata->exit_code); + return; + } + EXPECT_EQ(0, close(sync_child[1])); + EXPECT_EQ(0, close(trigger[0])); + EXPECT_EQ(0, close(trigger[1])); + + /* Waits for the child to generate the SIGIO. */ + ASSERT_EQ(1, read(sync_child[0], &buf, 1)); + EXPECT_EQ(0, close(sync_child[0])); + + /* Lets a delivered-but-pending signal run our handler, if any. */ + for (i = 0; i < 100 && !signal_received; i++) + usleep(1000); + + /* + * SCOPE_SIGNAL must block the fan-out to this non-sandboxed parent, + * which is outside the child's Landlock domain. Before the fix the + * parent was signaled here. + */ + EXPECT_EQ(0, signal_received); + + ASSERT_EQ(child, waitpid(child, &status, 0)); + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; +} + +static void *thread_setown_scoped(void *arg) +{ + const int fd = *(int *)arg; + int ruleset_fd; + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_SIGNAL, + }; + + /* Sandboxes only this non-leader thread (no thread syncing). */ + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + if (ruleset_fd < 0) + return (void *)THREAD_ERROR; + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) || + landlock_restrict_self(ruleset_fd, 0)) { + close(ruleset_fd); + return (void *)THREAD_ERROR; + } + close(ruleset_fd); + + /* Makes this process group own the SIGIO source. */ + if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) || + fcntl(fd, F_SETFL, O_ASYNC)) + return (void *)THREAD_ERROR; + + return (void *)THREAD_SUCCESS; +} + +/* + * Checks that the SIGIO fan-out is still delivered to the file owner's own + * process when fcntl(F_SETOWN, -pgrp) was issued from a sandboxed non-leader + * thread. + * + * The Landlock domain is recorded for a process-group owner (so out-of-domain + * members stay blocked, see sigio_to_pgid_members), but the kernel signals a + * process group through its members' thread-group leaders. Here the leader is + * not sandboxed and thus has a different domain than the registering thread, so + * the registration-time check cannot tell that it belongs to the owner's own + * process. hook_file_send_sigiotask() must recognize it through the recorded + * thread group and allow the delivery, matching the same-process guarantee of + * commit 18eb75f3af40. Without that exemption the leader is wrongly denied and + * never signaled. + */ +TEST(sigio_to_pgid_self) +{ + int trigger[2]; + pthread_t thread; + enum thread_return ret = THREAD_INVALID; + int i; + + drop_caps(_metadata); + + /* Bounds the SIGIO fan-out to this process. */ + ASSERT_EQ(0, setpgid(0, 0)); + + /* The non-sandboxed thread-group leader is the SIGIO target. */ + ASSERT_EQ(0, setup_signal_handler(SIGURG)); + signal_received = 0; + + ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC)); + + /* + * Registers the process-group fowner from a sibling thread that + * sandboxes only itself, so its domain differs from the leader's. + */ + ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped, + &trigger[0])); + ASSERT_EQ(0, pthread_join(thread, (void **)&ret)); + ASSERT_EQ(THREAD_SUCCESS, ret); + + /* Fans SIGURG out to the process group. */ + ASSERT_EQ(1, write(trigger[1], ".", 1)); + + for (i = 0; i < 1000 && !signal_received; i++) + usleep(1000); + + /* + * Same-process delivery must always be allowed, even though the owner + * was registered from a sandboxed sibling thread. + */ + EXPECT_EQ(1, signal_received); + + EXPECT_EQ(0, close(trigger[0])); + EXPECT_EQ(0, close(trigger[1])); +} + TEST_HARNESS_MAIN From 1caee6e084a96ada94658f261ced377d85af3f03 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Tue, 26 May 2026 10:16:51 -0400 Subject: [PATCH 1861/3297] mips: sched: Fix CPUMASK_OFFSTACK memory corruption commit 98e37db4a34d3af3fb2f4648295c25b5e40b20e3 upstream. This patch addresses a critical memory management flaw. When CONFIG_CPUMASK_OFFSTACK is enabled, cpumask_var_t is a pointer. Consequently, sizeof(new_mask) evaluates to the pointer size, causing copy_from_user() to clobber the mask pointer. Furthermore, the old logic performed copy_from_user() before allocating the mask. Fix this by allocating new_mask first. To handle variable-sized user masks correctly, use cpumask_size() to truncate overly large user masks or pad undersized masks with zeros before copying the data directly into the allocated buffer. Fixes: 295cbf6d63165 ("[MIPS] Move FPU affinity code into separate file.") Cc: stable@vger.kernel.org Signed-off-by: Aaron Tomlin Signed-off-by: Thomas Bogendoerfer Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/mips-mt-fpaff.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c index 10172fc4f627b..4fead87d2f433 100644 --- a/arch/mips/kernel/mips-mt-fpaff.c +++ b/arch/mips/kernel/mips-mt-fpaff.c @@ -71,11 +71,16 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, struct task_struct *p; int retval; - if (len < sizeof(new_mask)) - return -EINVAL; - - if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) - return -EFAULT; + if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) + return -ENOMEM; + if (len < cpumask_size()) + cpumask_clear(new_mask); + else if (len > cpumask_size()) + len = cpumask_size(); + if (copy_from_user(new_mask, user_mask_ptr, len)) { + retval = -EFAULT; + goto out_free_new_mask; + } cpus_read_lock(); rcu_read_lock(); @@ -84,7 +89,8 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, if (!p) { rcu_read_unlock(); cpus_read_unlock(); - return -ESRCH; + retval = -ESRCH; + goto out_free_new_mask; } /* Prevent p going away */ @@ -95,13 +101,9 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, retval = -ENOMEM; goto out_put_task; } - if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) { - retval = -ENOMEM; - goto out_free_cpus_allowed; - } if (!alloc_cpumask_var(&effective_mask, GFP_KERNEL)) { retval = -ENOMEM; - goto out_free_new_mask; + goto out_free_cpus_allowed; } if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) { retval = -EPERM; @@ -142,13 +144,13 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len, } out_unlock: free_cpumask_var(effective_mask); -out_free_new_mask: - free_cpumask_var(new_mask); out_free_cpus_allowed: free_cpumask_var(cpus_allowed); out_put_task: put_task_struct(p); cpus_read_unlock(); +out_free_new_mask: + free_cpumask_var(new_mask); return retval; } From f322955d9a1c344ed943752f05aacea7bc22e025 Mon Sep 17 00:00:00 2001 From: Zishun Yi Date: Sat, 6 Jun 2026 20:17:58 -0600 Subject: [PATCH 1862/3297] riscv: cacheinfo: Fix node reference leak in populate_cache_leaves commit bf4a195f063b0a0805c1417f6aad1dd32ea48f0f upstream. Currently, the while loop drops the reference to prev in each iteration. If the loop terminates early due to a break, the final of_node_put(np) correctly drops the reference to the current node. However, if the loop terminates naturally because np == NULL, calling of_node_put(np) is a no-op. This leaves the last valid node stored in prev without its reference dropped, resulting in a node reference leak. Fix this by changing the final `of_node_put(np)` to `of_node_put(prev)`. Fixes: 94f9bf118f1e ("RISC-V: Fix of_node_* refcount") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Zishun Yi Link: https://patch.msgid.link/20260509074040.1747800-1-vulab@iscas.ac.cn Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/cacheinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c index 26b085dbdd073..6c9a1ef2d45af 100644 --- a/arch/riscv/kernel/cacheinfo.c +++ b/arch/riscv/kernel/cacheinfo.c @@ -133,7 +133,7 @@ int populate_cache_leaves(unsigned int cpu) ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); levels = level; } - of_node_put(np); + of_node_put(prev); return 0; } From f18c561eb9c51f1ba3adb6117926a98a66599848 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:47 -0700 Subject: [PATCH 1863/3297] mm/damon/sysfs-schemes: fix dir put orders in access_pattern_add_dirs() commit d58fdbe37a829fd2e5803dd4e5a72992dd8c5368 upstream. Patch series "mm/damon/sysfs-schemes: fix wrong directories put orders in error paths". Error paths of damon_sysfs_access_pattern_add_dirs() and damon_sysfs_scheme_add_dirs() functions put references to directories in wrong orders. As a result, uninitialized memory dereference and/or memory leak can happen. Fix those. This patch (of 2): In access_pattern_add_dirs(), error handling path puts references starting from setup failed directories. If the failure happpened from the initial allication in the setup functions, uninitialized memory dereference happen. The allocation failures will not commonly happen, but the consequence is quite bad. Fix the wrong reference put orders. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-2-sj@kernel.org Link: https://lore.kernel.org/20260617060005.86852-1-sj@kernel.org [1] Fixes: 7e84b1f8212a ("mm/damon/sysfs: support DAMON-based Operation Schemes") Signed-off-by: SeongJae Park Cc: # 5.18.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/sysfs-schemes.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 4a432a07c5c3d..1705ca8d2f14f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1615,22 +1615,19 @@ static int damon_sysfs_access_pattern_add_dirs( err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->sz, "sz"); if (err) - goto put_sz_out; + return err; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->nr_accesses, "nr_accesses"); if (err) - goto put_nr_accesses_sz_out; + goto put_sz_out; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->age, "age"); if (err) - goto put_age_nr_accesses_sz_out; + goto put_nr_accesses_sz_out; return 0; -put_age_nr_accesses_sz_out: - kobject_put(&access_pattern->age->kobj); - access_pattern->age = NULL; put_nr_accesses_sz_out: kobject_put(&access_pattern->nr_accesses->kobj); access_pattern->nr_accesses = NULL; From 40a04601a3f66cabd6629c258a07af645a658865 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:48 -0700 Subject: [PATCH 1864/3297] mm/damon/sysfs-schemes: put stats for scheme_add_dirs() internal error commit 05ea83ee88ca70f8932906d9f2617ff996f45b50 upstream. damon_sysfs_scheme_add_dirs() setup the tried_regions directory after the stats directory setup is completed. When the tried_regions directory setup is failed, the setup function ensures the reference for the tried regions directory is released. Hence the error path should put references on setup succeeded directory objects, starting from the stats directory. However, the error path is putting the tried_regions directory instead of the stats directory. As a direct result, the stats directory object is leaked. Worse yet, if the tried_regions directory setup failed from the initial allocation, the scheme->tried_regions field remains uninitialized. The following kobject_put(&scheme->tried_regions->kobj) call in the error path will dereference the uninitialized memory. The setup failures should not be common. But once it happens, the consequence is quite bad. Fix this issue by correctly putting the stats directory instead of the tried_regions directory. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-3-sj@kernel.org Link: https://lore.kernel.org/20260617005223.96813-1-sj@kernel.org [1] Fixes: 5181b75f438d ("mm/damon/sysfs-schemes: implement schemes/tried_regions 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 1705ca8d2f14f..adf60f672b8b8 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2132,12 +2132,12 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme) goto put_filters_watermarks_quotas_access_pattern_out; err = damon_sysfs_scheme_set_tried_regions(scheme); if (err) - goto put_tried_regions_out; + goto put_stats_out; return 0; -put_tried_regions_out: - kobject_put(&scheme->tried_regions->kobj); - scheme->tried_regions = NULL; +put_stats_out: + kobject_put(&scheme->stats->kobj); + scheme->stats = NULL; put_filters_watermarks_quotas_access_pattern_out: kobject_put(&scheme->ops_filters->kobj); scheme->ops_filters = NULL; From 43b987ed35be9be21a303d1036d4241fec9943df Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:27 +0100 Subject: [PATCH 1865/3297] fs/proc/task_mmu: fix hugetlb self-deadlock in pagemap_scan_pte_hole() commit e92d92bbafb264dc0518d52b846a3c07ed8d523f upstream. A PAGEMAP_SCAN ioctl requesting PM_SCAN_WP_MATCHING on a hugetlb VMA hangs the calling thread, unkillably, as soon as the scan reaches an unpopulated part of the range: do_pagemap_scan() walk_page_range() walk_hugetlb_range() hugetlb_vma_lock_read() # take the vma lock for read ... pagemap_scan_pte_hole() # ... ->pte_hole() for a hole uffd_wp_range() change_protection() hugetlb_change_protection() hugetlb_vma_lock_write() # ... and block taking it for write walk_hugetlb_range() holds the hugetlb vma lock for read across the whole walk. A present entry goes to ->hugetlb_entry(); an unpopulated one goes to ->pte_hole(), i.e. pagemap_scan_pte_hole(). To write-protect the hole that handler calls uffd_wp_range(), which on a hugetlb VMA reaches hugetlb_change_protection() and takes the same vma lock for write. The thread then blocks in down_write() waiting for the read lock it is itself holding. The populated path avoids this: pagemap_scan_hugetlb_entry() write-protects the entry inline under the page-table lock and never enters hugetlb_change_protection(). Do the same for holes. Fault in the page table and install the uffd-wp marker directly with make_uffd_wp_huge_pte() under the page-table lock, rather than routing through uffd_wp_range(). That is the same sequence hugetlb_change_protection() runs for an unpopulated entry, minus the vma write lock -- which is safe to skip because PMD sharing is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() runs at registration), leaving nothing for that lock to serialise against. Link: https://lore.kernel.org/20260529172331.356655-4-kas@kernel.org Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Assisted-by: Claude:claude-opus-4-8 Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 59 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 4c5adfd4fc1f1..7e7fd3a5e68bc 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2851,8 +2851,62 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask, return ret; } + +/* + * Write-protect the unpopulated hugetlb entries covering [addr, end) by + * installing uffd-wp markers inline, exactly as pagemap_scan_hugetlb_entry() + * does for populated entries. + * + * walk_hugetlb_range() currently calls ->pte_hole() once per huge page, so the + * loop normally runs a single iteration; it is written to cover the full range + * in case the walker ever coalesces adjacent holes. + * + * The obvious route -- uffd_wp_range() -> hugetlb_change_protection() -- + * cannot be used here: it takes hugetlb_vma_lock_write(), but the page-table + * walker (walk_hugetlb_range()) already holds hugetlb_vma_lock_read() on the + * same VMA, so the scanning thread would deadlock against itself. PMD sharing + * is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() at registration), so + * the vma lock guards nothing that matters for these entries anyway. + */ +static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma, + unsigned long addr, unsigned long end) +{ + struct hstate *h = hstate_vma(vma); + unsigned long psize = huge_page_size(h); + struct mm_struct *mm = vma->vm_mm; + spinlock_t *ptl; + pte_t *ptep; + pte_t pte; + + for (addr = ALIGN_DOWN(addr, psize); addr < end; addr += psize) { + ptep = huge_pte_alloc(mm, vma, addr, psize); + if (!ptep) + return -ENOMEM; + + i_mmap_lock_write(vma->vm_file->f_mapping); + ptl = huge_pte_lock(h, mm, ptep); + pte = huge_ptep_get(mm, addr, ptep); + make_uffd_wp_huge_pte(vma, addr, ptep, pte); + /* + * A none entry has no cached translation, so installing the + * marker needs no TLB flush. Flush only if a fault populated + * the entry between huge_pte_alloc() and the page table lock. + */ + if (!huge_pte_none(pte)) + flush_hugetlb_tlb_range(vma, addr, addr + psize); + spin_unlock(ptl); + i_mmap_unlock_write(vma->vm_file->f_mapping); + } + + return 0; +} #else #define pagemap_scan_hugetlb_entry NULL +static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma, + unsigned long addr, unsigned long end) +{ + return 0; +} #endif static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, @@ -2872,7 +2926,10 @@ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, if (~p->arg.flags & PM_SCAN_WP_MATCHING) return ret; - err = uffd_wp_range(vma, addr, end - addr, true); + if (is_vm_hugetlb_page(vma)) + err = pagemap_scan_hugetlb_hole_wp(vma, addr, end); + else + err = uffd_wp_range(vma, addr, end - addr, true); if (err < 0) ret = err; From 4d67bdef35c329bf63f8d2521556b0458ae3d145 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Fri, 29 May 2026 18:23:26 +0100 Subject: [PATCH 1866/3297] fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry() commit 1b074e3270e1c061c829150c742eb83bad4dddd1 upstream. The partial-page check compares against HPAGE_SIZE (PMD_SIZE), which is wrong for gigantic hugetlb hstates (e.g. 1G). The walker hands the callback a huge_page_size()-sized range, never start + HPAGE_SIZE, so the comparison always declares it partial and aborts the WP. Compare against the actual hstate's page size. Link: https://lore.kernel.org/20260529172331.356655-3-kas@kernel.org Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 7e7fd3a5e68bc..38b9d47426ca9 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2834,7 +2834,7 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask, if (~categories & PAGE_IS_WRITTEN) goto out_unlock; - if (end != start + HPAGE_SIZE) { + if (end != start + huge_page_size(hstate_vma(vma))) { /* Partial HugeTLB page WP isn't possible. */ pagemap_scan_backout_range(p, start, end); p->arg.walk_end = start; From b6a6fb6803d5248ed51805cea451c6cc0279994a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Sat, 13 Jun 2026 21:10:05 +0000 Subject: [PATCH 1867/3297] proc: only bump parent nlink when registering directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 16b02eb4b9b272c221255c20d34ccd5db53a3ed3 upstream. proc_register() increments the parent directory's link count for every entry it registers, while remove_proc_entry() and remove_proc_subtree() decrement it only when the removed entry is a directory. Regular files thus inflate the parent's count while they exist, and leak one link permanently on every create and remove cycle. For example, /proc/bus/pci/00 with twenty-two device files and no subdirectories reports nlink 24 instead of 2, and SR-IOV VF enable and disable cycles, each creating and removing the VF config space entries under /proc/bus/pci/, inflate the link count of that directory without bound. Before commit e06689bf5701 ("proc: change ->nlink under proc_subdir_lock"), the increment lived in proc_mkdir_data() and proc_create_mount_point(), and was therefore applied only to directories. Moving it into proc_register() to bring it under proc_subdir_lock dropped the S_ISDIR check. Thus, move the nlink accounting into pde_subdir_insert() and pde_erase(), only updating it for directories in both, so the link count is always changed together with the directory entry itself. Fixes: e06689bf5701 ("proc: change ->nlink under proc_subdir_lock") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Krzysztof Wilczyński Link: https://patch.msgid.link/20260613211005.921692-1-kwilczynski@kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/proc/generic.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 501889856461b..12b817f4ffcfd 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -112,6 +112,8 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir, /* Add new node and rebalance tree. */ rb_link_node(&de->subdir_node, parent, new); rb_insert_color(&de->subdir_node, root); + if (S_ISDIR(de->mode)) + dir->nlink++; return true; } @@ -404,7 +406,6 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir, write_unlock(&proc_subdir_lock); goto out_free_inum; } - dir->nlink++; write_unlock(&proc_subdir_lock); return dp; @@ -702,6 +703,8 @@ static void pde_erase(struct proc_dir_entry *pde, struct proc_dir_entry *parent) { rb_erase(&pde->subdir_node, &parent->subdir); RB_CLEAR_NODE(&pde->subdir_node); + if (S_ISDIR(pde->mode)) + parent->nlink--; } /* @@ -727,8 +730,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) de = NULL; } else { pde_erase(de, parent); - if (S_ISDIR(de->mode)) - parent->nlink--; } } write_unlock(&proc_subdir_lock); @@ -787,8 +788,6 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) continue; } next = de->parent; - if (S_ISDIR(de->mode)) - next->nlink--; write_unlock(&proc_subdir_lock); proc_entry_rundown(de); From 07ed8b178548014d3d3466b2d088fc9eb84bf5fd Mon Sep 17 00:00:00 2001 From: Jinjiang Tu Date: Fri, 26 Jun 2026 09:32:52 +0800 Subject: [PATCH 1868/3297] fs/proc: fix KPF_KSM reported for all anonymous pages commit 81401cebfc1598306b0a981b5f9ee5b58c1aac52 upstream. Reading /proc/kpageflags for any anonymous page returns KPF_KSM set, even when KSM is not in use. As a result, tools misclassify all anonymous pages as KSM merged. In stable_page_flags(), if the page is anonymous, then use (mapping & FOLIO_MAPPING_KSM) check to identify if the anonymous page is KSM page. However, FOLIO_MAPPING_KSM is FOLIO_MAPPING_ANON | FOLIO_MAPPING_ANON_KSM, (mapping & FOLIO_MAPPING_KSM) check returns true for all anonymous pages. To fix it, use FOLIO_MAPPING_ANON_KSM instead. Link: https://lore.kernel.org/20260629033122.774318-1-tujinjiang@huawei.com Link: https://lore.kernel.org/20260626013252.2846774-1-tujinjiang@huawei.com Fixes: dee3d0bef2b0 ("proc: rewrite stable_page_flags()") Signed-off-by: Jinjiang Tu Acked-by: David Hildenbrand (Arm) Acked-by: Zi Yan Reviewed-by: Xu Xin Cc: Chengming Zhou Cc: Kefeng Wang Cc: Luiz Capitulino Cc: Matthew Wilcox (Oracle) Cc: Miaohe Lin Cc: Nanyong Sun Cc: Svetly Todorov Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/page.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/page.c b/fs/proc/page.c index fc64f23e05e58..f612524c92a83 100644 --- a/fs/proc/page.c +++ b/fs/proc/page.c @@ -174,7 +174,7 @@ u64 stable_page_flags(const struct page *page) u |= 1 << KPF_MMAP; if (is_anon) { u |= 1 << KPF_ANON; - if (mapping & FOLIO_MAPPING_KSM) + if ((mapping & FOLIO_MAPPING_FLAGS) == FOLIO_MAPPING_KSM) u |= 1 << KPF_KSM; } From 749e2051da3b0704b645001e7038c1029c252668 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Sun, 14 Jun 2026 23:04:37 +0530 Subject: [PATCH 1869/3297] powerpc/dt_cpu_ftrs: Set CPU_FTR_P11_PVR for Power11 and later processors commit e4de1b9cb3b5c981e4fe9bca253a7fb9161f5acd upstream. When using device tree CPU features (dt-cpu-ftrs), the kernel bypasses the traditional cputable-based CPU identification and instead derives CPU features from the device tree's "ibm,powerpc-cpu-features" node provided by firmware. However, CPU_FTR_P11_PVR is a kernel-internal feature flag used to identify Power11 and later processors, and is not represented in the device tree's ISA feature set. While ISA v3.1 support (indicated by CPU_FTR_ARCH_31) is present on both Power10 and Power11, the CPU_FTR_P11_PVR flag is specifically needed by code that must distinguish between Power10 and Power11 processors. Without this flag set, code that checks for Power11 using cpu_has_feature(CPU_FTR_P11_PVR) will incorrectly return false on Power11+ systems using dt-cpu-ftrs, leading to incorrect behavior. This issue manifests specifically in powernv environments (bare-metal or QEMU TCG with powernv machine type), where skiboot/OPAL firmware provides the "ibm,powerpc-cpu-features" node, causing the kernel to use dt-cpu-ftrs. The issue does not affect pseries guests, where SLOF firmware does not provide this node, causing the kernel to fall back to the traditional cputable path (identify_cpu) which correctly sets CPU_FTR_P11_PVR during PVR-based CPU identification. In powernv TCG guests, the missing flag causes KVM code to trigger warnings when attempting to create KVM guests, as cpu_features shows 0x000c00eb8f4fb187 (missing bit 53) instead of the correct 0x002c00eb8f4fb187 (with bit 53 set). Fix this by setting CPU_FTR_P11_PVR for all processors with PVR >= PVR_POWER11 when ISA v3.1 support is detected in cpufeatures_setup_start(). This approach ensures forward compatibility with future processor generations. Fixes: 96e266e3bcd6 ("KVM: PPC: Book3S HV: Add Power11 capability support for Nested PAPR guests") Cc: stable@vger.kernel.org # v6.13+ Signed-off-by: Amit Machhiwal Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Reviewed-by: Christophe Leroy (CS GROUP) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260614173437.26352-1-amachhiw@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/dt_cpu_ftrs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 3af6c06af02f3..e5853daa6a486 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -704,6 +704,15 @@ static void __init cpufeatures_setup_start(u32 isa) if (isa >= ISA_V3_1) { cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_31; cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_1; + + /* + * CPU_FTR_P11_PVR is a kernel-internal flag to identify + * Power11 and later processors. While ISA v3.1 is supported + * by Power10+, this flag specifically indicates Power11+ + * for code that needs to distinguish between P10 and P11. + */ + if (PVR_VER(mfspr(SPRN_PVR)) >= PVR_POWER11) + cur_cpu_spec->cpu_features |= CPU_FTR_P11_PVR; } } From da5234df0941665f3a3f5b80f3dab94046537be0 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:18:55 +0800 Subject: [PATCH 1870/3297] mm/mm_init: fix uninitialized struct pages for ZONE_DEVICE commit cd681403a87085562499d60325b7b45d3be11217 upstream. If DAX memory is hotplugged into an unoccupied subsection of an early section, section_activate() reuses the unoptimized boot memmap. However, compound_nr_pages() still assumes that vmemmap optimization is in effect and initializes only the reduced number of struct pages. As a result, the remaining tail struct pages are left uninitialized, which can later lead to unexpected behavior or crashes. Fix this by treating early sections as unoptimized when calculating how many struct pages to initialize. Link: https://lore.kernel.org/20260428081855.1249045-7-songmuchun@bytedance.com Fixes: 6fd3620b3428 ("mm/page_alloc: reuse tail struct pages for compound devmaps") Signed-off-by: Muchun Song Acked-by: David Hildenbrand (Arm) Acked-by: Mike Rapoport (Microsoft) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Oscar Salvador Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/mm_init.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 9087dce0c51b4..1b977fa9e8bee 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1072,10 +1072,17 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn, * of how the sparse_vmemmap internals handle compound pages in the lack * of an altmap. See vmemmap_populate_compound_pages(). */ -static inline unsigned long compound_nr_pages(struct vmem_altmap *altmap, +static inline unsigned long compound_nr_pages(unsigned long pfn, + struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { - if (!vmemmap_can_optimize(altmap, pgmap)) + /* + * If DAX memory is hot-plugged into an unoccupied subsection + * of an early section, the unoptimized boot memmap is reused. + * See section_activate(). + */ + if (early_section(__pfn_to_section(pfn)) || + !vmemmap_can_optimize(altmap, pgmap)) return pgmap_vmemmap_nr(pgmap); return VMEMMAP_RESERVE_NR * (PAGE_SIZE / sizeof(struct page)); @@ -1145,7 +1152,7 @@ void __ref memmap_init_zone_device(struct zone *zone, continue; memmap_init_compound(page, pfn, zone_idx, nid, pgmap, - compound_nr_pages(altmap, pgmap)); + compound_nr_pages(pfn, altmap, pgmap)); } pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE); From 4e4beef747c687bd087988cf1cbd1e979dfd938e Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Tue, 26 May 2026 13:47:15 +0200 Subject: [PATCH 1871/3297] kcov: use WRITE_ONCE() for selftest mode stores commit 9a79524d1420e6b79a6868208c264f4518d1318e upstream. The KCOV selftest enables coverage by setting current->kcov_mode to KCOV_MODE_TRACE_PC without installing a coverage area. If an interrupt records coverage in that window, the access should fault and expose the bug. When building for QEMU raspi0 (Raspberry Pi Zero, ARMv6, CONFIG_CPU_V6K=y, CONFIG_CURRENT_POINTER_IN_TPIDRURO=y) with GCC 13.3.0, the store that enables the mode is removed. The generated kcov_init() code only stores zero after the wait loop: mrc 15, 0, r3, cr13, cr0, {3} str r4, [r3, #2028] where r4 is zero. There is no store of KCOV_MODE_TRACE_PC before the loop, so the selftest reports success without exercising coverage. Use WRITE_ONCE() for the temporary mode stores. With the same compiler and config, kcov_init() contains the intended mode store: mov r3, #2 mrc 15, 0, r2, cr13, cr0, {3} str r3, [r2, #2028] Now that the KCOV selftest is actually executed, it may expose KCOV instrumentation issues depending on the kernel config. That is expected for a selftest that was intended to catch coverage from interrupt paths. Link: https://lore.kernel.org/20260526114715.38280-1-kmehltretter@gmail.com Fixes: 6cd0dd934b03 ("kcov: Add interrupt handling self test") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Reviewed-by: Alexander Potapenko Cc: Andrey Konovalov Cc: Dmitry Vyukov Cc: Kees Cook Cc: Marco Elver Cc: Peter Zijlstra Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- kernel/kcov.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/kcov.c b/kernel/kcov.c index 6563141f5de9e..ab8678b7b5e0c 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -1095,10 +1095,10 @@ static void __init selftest(void) * potentially traced functions in this region. */ start = jiffies; - current->kcov_mode = KCOV_MODE_TRACE_PC; + WRITE_ONCE(current->kcov_mode, KCOV_MODE_TRACE_PC); while ((jiffies - start) * MSEC_PER_SEC / HZ < 300) ; - current->kcov_mode = 0; + WRITE_ONCE(current->kcov_mode, 0); pr_err("done running self test\n"); } #endif From d8dcbbfa0d695a5244059aa34a2e81f3e8df1082 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 16:45:27 +0800 Subject: [PATCH 1872/3297] mtd: slram: remove failed entries from the device list commit 36f1648644d769c496a8e47e53603e863e358d73 upstream. register_device() links a new slram_mtdlist entry before allocating all of the state needed by the entry. If a later allocation, memremap(), or mtd_device_register() fails, the partially initialized entry remains on the global list. A later cleanup can then dereference or free invalid state from that failed entry. Unwind the partially initialized entry and clear the list tail on each failure path after the entry has been linked. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Ruoyu Wang Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/devices/slram.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 8297b366a0669..a476e53562a4d 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -129,6 +129,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, static int register_device(char *name, unsigned long start, unsigned long length) { slram_mtd_list_t **curmtd; + int ret = -ENOMEM; curmtd = &slram_mtdlist; while (*curmtd) { @@ -155,14 +156,15 @@ static int register_device(char *name, unsigned long start, unsigned long length if (!(*curmtd)->mtdinfo) { E("slram: Cannot allocate new MTD device.\n"); - return(-ENOMEM); + goto err_free_list; } if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = memremap(start, length, MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) { E("slram: memremap failed\n"); - return -EIO; + ret = -EIO; + goto err_free_priv; } ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; @@ -183,10 +185,8 @@ static int register_device(char *name, unsigned long start, unsigned long length if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) { E("slram: Failed to register new device\n"); - memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); - kfree((*curmtd)->mtdinfo->priv); - kfree((*curmtd)->mtdinfo); - return(-EAGAIN); + ret = -EAGAIN; + goto err_unmap; } T("slram: Registered device %s from %luKiB to %luKiB\n", name, (start / 1024), ((start + length) / 1024)); @@ -194,6 +194,16 @@ static int register_device(char *name, unsigned long start, unsigned long length ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); return(0); + +err_unmap: + memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); +err_free_priv: + kfree((*curmtd)->mtdinfo->priv); +err_free_list: + kfree((*curmtd)->mtdinfo); + kfree(*curmtd); + *curmtd = NULL; + return ret; } static void unregister_devices(void) From 8d610017c992de705b304d3d727a6e3a86af6149 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 21 Apr 2026 02:41:09 -0700 Subject: [PATCH 1873/3297] 9p: skip nlink update in cacheless mode to fix WARN_ON commit 574aa0b4799470ac814479f1138d19efe6262255 upstream. v9fs_dec_count() unconditionally calls drop_nlink() on regular files, even when the inode's nlink is already zero. In cacheless mode the client refetches inode metadata from the server (the source of truth) on every operation, so by the time v9fs_remove() returns, the locally cached nlink may already reflect the post-unlink value: 1. Client initiates unlink, server processes it and sets nlink to 0 2. Client refetches inode metadata (nlink=0) before unlink returns 3. Client's v9fs_remove() completes successfully 4. Client calls v9fs_dec_count() which calls drop_nlink() on nlink=0 This race is easily triggered under heavy unlink workloads, such as stress-ng's unlink stressor, producing the following warning: WARNING: fs/inode.c:417 at drop_nlink+0x4c/0xc8 Call trace: drop_nlink+0x4c/0xc8 v9fs_remove+0x1e0/0x250 [9p] v9fs_vfs_unlink+0x20/0x38 [9p] vfs_unlink+0x13c/0x258 ... In cacheless mode the server is authoritative and the inode is on its way out, so locally adjusting nlink buys nothing. Skip v9fs_dec_count() entirely when neither CACHE_META nor CACHE_LOOSE is set, which both avoids the warning and removes a class of nlink races (two concurrent unlinkers observing nlink > 0 and both calling drop_nlink()) that an nlink == 0 guard alone would only narrow rather than close. Fixes: ac89b2ef9b55 ("9p: don't maintain dir i_nlink if the exported fs doesn't either") Cc: stable@vger.kernel.org Suggested-by: Dominique Martinet Signed-off-by: Breno Leitao Message-ID: <20260421-9p-v2-1-48762d294fad@debian.org> Signed-off-by: Dominique Martinet Signed-off-by: Greg Kroah-Hartman --- fs/9p/vfs_inode.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index e2225bfa55035..b2368334f6eea 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -488,10 +488,19 @@ static int v9fs_at_to_dotl_flags(int flags) * - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more * than EXT4_LINK_MAX (65000) links. * + * In cacheless mode the server is the source of truth for nlink and the + * inode is going away immediately, so locally adjusting i_nlink buys + * nothing and races with concurrent metadata fetches that may already + * have observed the post-unlink value (nlink == 0). + * * @inode: inode whose nlink is being dropped */ static void v9fs_dec_count(struct inode *inode) { + struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); + + if (!(v9ses->cache & (CACHE_META | CACHE_LOOSE))) + return; if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) drop_nlink(inode); } From a7bbf83dfebdc91609da4fb9a3669819b4375fdc Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Wed, 3 Jun 2026 00:10:50 +0400 Subject: [PATCH 1874/3297] power: supply: bq257xx: Fix VSYSMIN clamping logic commit b6c6b9260a92dacef6edef8e93bc2767b86b1dfe upstream. The minimal system voltage (VSYSMIN) is meant to protect the battery from dangerous over-discharge. When the device tree provides a value for the minimum design voltage of the battery, the user should not be allowed to set a lower VSYSMIN, as that would defeat the purpose of this protection. Flip the clamping logic when setting VSYSMIN to ensure that battery design voltage is respected. Cc: stable@vger.kernel.org Fixes: 1cc017b7f9c7 ("power: supply: bq257xx: Add support for BQ257XX charger") Tested-by: Chris Morgan Signed-off-by: Alexey Charkov Link: https://patch.msgid.link/20260603-bq25792-v7-2-d487bed276d0@flipper.net Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/bq257xx_charger.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/bq257xx_charger.c b/drivers/power/supply/bq257xx_charger.c index 02c7d8b61e82b..7ca4ae6109027 100644 --- a/drivers/power/supply/bq257xx_charger.c +++ b/drivers/power/supply/bq257xx_charger.c @@ -128,9 +128,8 @@ static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval) * @vsys: voltage value to set in uV. * * This function takes a requested minimum system voltage value, clamps - * it between the minimum supported value by the charger and a user - * defined minimum system value, and then writes the value to the - * appropriate register. + * it between the user defined minimum system value and the maximum supported + * value by the charger, and then writes the value to the appropriate register. * * Return: Returns 0 on success or error if an error occurs. */ @@ -139,7 +138,7 @@ static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys) unsigned int reg; int vsys_min = pdata->vsys_min; - vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min); + vsys = clamp(vsys, vsys_min, BQ25703_MINVSYS_MAX_UV); reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV); reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg); From 83405848e40302811d90b22b216f1f10794d0d61 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 13 May 2026 19:42:35 +0200 Subject: [PATCH 1875/3297] scsi: smartpqi: Use shost_to_hba() in pqi_scan_finished() commit 57db1307afb1f83d45f5ff53b93f8d040100d13e upstream. shost_to_hba() is used everywhere except to obtain pqi_ctrl_info from shosti, except in pqi_scan_finished(), where shost_priv() is used. This causes one pointer dereference to be missed, as shost->hostdata is a pointer in smartpqi. Fix it. Fixes: 6c223761eb54 ("smartpqi: initial commit of Microsemi smartpqi driver") Signed-off-by: Martin Wilck Reviewed-by: Don Brace Cc: Don Brace Cc: storagedev@microchip.com Cc: stable@vger.kernel.org Reviewed-by: Hannes Reinecke Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260513174236.430465-2-mwilck@suse.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/smartpqi/smartpqi_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 5a6e1bb57e7c8..090e786470b39 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -2644,7 +2644,7 @@ static int pqi_scan_finished(struct Scsi_Host *shost, { struct pqi_ctrl_info *ctrl_info; - ctrl_info = shost_priv(shost); + ctrl_info = shost_to_hba(shost); return !mutex_is_locked(&ctrl_info->scan_mutex); } From 8d263bae573dc6cd8a7f68eeea3cf4112fc90cd7 Mon Sep 17 00:00:00 2001 From: Ionut Nechita Date: Tue, 19 May 2026 16:52:33 +0300 Subject: [PATCH 1876/3297] scsi: sas: Skip opt_sectors when DMA reports no real optimization hint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit be8fcd4a8217a916344c88a4b1b84f5736dda17e upstream. sas_host_setup() unconditionally sets shost->opt_sectors from dma_opt_mapping_size(). When the IOMMU is disabled or in passthrough mode and no DMA ops provide an opt_mapping_size callback, dma_opt_mapping_size() returns min(dma_max_mapping_size(), SIZE_MAX) which equals dma_max_mapping_size() — a hard upper bound, not an optimization hint. On a Dell PowerEdge R750 with mpt3sas (Broadcom SAS3816, FW 33.15.00.00) and intel_iommu=off the following values are observed: dma_opt_mapping_size() = dma_max_mapping_size() (no real hint) shost->max_sectors = 32767 opt_sectors = min(32767, huge >> 9) = 32767 optimal_io_size = 32767 << 9 = 16776704 → round_down(16776704, 4096) = 16773120 The SAS disk (SAMSUNG MZILT800HBHQ0D3) does not report an Optimal Transfer Length in VPD page B0, so sdkp->opt_xfer_blocks remains 0. sd_revalidate_disk() then uses min_not_zero(0, opt_sectors) = opt_sectors, propagating the bogus value into the block device's optimal_io_size (visible as OPT-IO = 16773120 in lsblk --topology). mkfs.xfs picks up optimal_io_size and minimum_io_size and computes: swidth = 16773120 / 4096 = 4095 sunit = 8192 / 4096 = 2 Since 4095 % 2 != 0, XFS rejects the geometry: SB stripe unit sanity check failed This makes it impossible to create XFS filesystems (e.g. for /var/lib/docker) during system bootstrap. Fix this by introducing a sas_dma_setup_opt_sectors() helper that sets opt_sectors only when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), indicating a genuine DMA optimization constraint. The helper computes min(opt_sectors, max_sectors) first, then rounds down to a power of two so that filesystem geometry calculations always produce clean results. When the two DMA values are equal, no backend provided a real hint, so opt_sectors stays at 0 ("no preference"). [mkp: implemented hch's suggestion] Fixes: 4cbfca5f7750 ("scsi: scsi_transport_sas: cap shost opt_sectors according to DMA optimal limit") Cc: stable@vger.kernel.org Reviewed-by: John Garry Signed-off-by: Ionut Nechita Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260519135238.373784-2-ionut.nechita@windriver.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_transport_sas.c | 41 +++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 351b028ef8938..bbbc2f448e7d1 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -220,12 +221,45 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) * SAS host attributes */ +/* + * Set shost->opt_sectors from the DMA optimal mapping size, but only + * when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), + * indicating a genuine optimization hint from an IOMMU or DMA backend. + * When the two are equal (e.g. IOMMU disabled / passthrough), no real + * hint exists, so leave opt_sectors at 0 to avoid bogus optimal_io_size + * values that break filesystem geometry (e.g. mkfs.xfs stripe alignment). + */ +static void sas_dma_setup_opt_sectors(struct Scsi_Host *shost) +{ + struct device *dma_dev = shost->dma_dev; + size_t opt = dma_opt_mapping_size(dma_dev); + size_t max = dma_max_mapping_size(dma_dev); + unsigned int opt_sectors; + + /* opt >= max means no real hint was provided by the DMA layer */ + if (opt >= max) + return; + + /* Clamp to max_sectors to avoid overflow in sector arithmetic */ + opt_sectors = min_t(unsigned int, opt >> SECTOR_SHIFT, + shost->max_sectors); + + /* Guard against zero before rounddown_pow_of_two() */ + if (!opt_sectors) + return; + + /* + * Round down to power-of-two so filesystem geometry calculations + * (e.g. XFS stripe width/unit) always produce clean divisors. + */ + shost->opt_sectors = rounddown_pow_of_two(opt_sectors); +} + static int sas_host_setup(struct transport_container *tc, struct device *dev, struct device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - struct device *dma_dev = shost->dma_dev; INIT_LIST_HEAD(&sas_host->rphy_list); mutex_init(&sas_host->lock); @@ -237,10 +271,7 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev, dev_printk(KERN_ERR, dev, "fail to a bsg device %d\n", shost->host_no); - if (dma_dev->dma_mask) { - shost->opt_sectors = min_t(unsigned int, shost->max_sectors, - dma_opt_mapping_size(dma_dev) >> SECTOR_SHIFT); - } + sas_dma_setup_opt_sectors(shost); return 0; } From bf53557a96d4cf50995778d413acaa40e66c59fb Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Fri, 22 May 2026 16:56:03 +0100 Subject: [PATCH 1877/3297] openrisc: Add full instruction cache invalidate functions commit 1be031de802ba486a7605b52eda825f128b026e2 upstream. Add functions to invalidate all cache lines which we will use for static_key patching. On OpenRISC there is no instruction to invalidate an entire cache so we loop and invalidate cache lines one by one. This is not extremely expensive on OpenRISC as we usually have only a few hundred cache lines. I considered using the invalidate cache page or range functions. However, tracking which ranges need invalidation would have been more expensive than flushing all pages. Cc: stable@vger.kernel.org Signed-off-by: Stafford Horne Signed-off-by: Greg Kroah-Hartman --- arch/openrisc/include/asm/cacheflush.h | 4 ++++ arch/openrisc/kernel/smp.c | 21 +++++++++++++++++++++ arch/openrisc/mm/cache.c | 16 ++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/arch/openrisc/include/asm/cacheflush.h b/arch/openrisc/include/asm/cacheflush.h index cd8f971c0fecc..7b8c043a831df 100644 --- a/arch/openrisc/include/asm/cacheflush.h +++ b/arch/openrisc/include/asm/cacheflush.h @@ -26,6 +26,7 @@ extern void local_icache_page_inv(struct page *page); extern void local_dcache_range_flush(unsigned long start, unsigned long end); extern void local_dcache_range_inv(unsigned long start, unsigned long end); extern void local_icache_range_inv(unsigned long start, unsigned long end); +extern void local_icache_all_inv(void); /* * Data cache flushing always happen on the local cpu. Instruction cache @@ -35,10 +36,13 @@ extern void local_icache_range_inv(unsigned long start, unsigned long end); #ifndef CONFIG_SMP #define dcache_page_flush(page) local_dcache_page_flush(page) #define icache_page_inv(page) local_icache_page_inv(page) +#define icache_all_inv() local_icache_all_inv() #else /* CONFIG_SMP */ #define dcache_page_flush(page) local_dcache_page_flush(page) #define icache_page_inv(page) smp_icache_page_inv(page) +#define icache_all_inv() smp_icache_all_inv() extern void smp_icache_page_inv(struct page *page); +extern void smp_icache_all_inv(void); #endif /* CONFIG_SMP */ /* diff --git a/arch/openrisc/kernel/smp.c b/arch/openrisc/kernel/smp.c index 86da4bc5ee0bb..46e16ad8d2559 100644 --- a/arch/openrisc/kernel/smp.c +++ b/arch/openrisc/kernel/smp.c @@ -326,3 +326,24 @@ void smp_icache_page_inv(struct page *page) on_each_cpu(ipi_icache_page_inv, page, 1); } EXPORT_SYMBOL(smp_icache_page_inv); + +static void ipi_icache_all_inv(void *arg) +{ + local_icache_all_inv(); +} + +void smp_icache_all_inv(void) +{ + if (num_online_cpus() < 2) { + local_icache_all_inv(); + return; + } + + /* + * Ensure stores complete before we request remote icaches + * to invalidate. + */ + mb(); + + on_each_cpu(ipi_icache_all_inv, NULL, 1); +} diff --git a/arch/openrisc/mm/cache.c b/arch/openrisc/mm/cache.c index f33df46dae4e5..2667d90691b59 100644 --- a/arch/openrisc/mm/cache.c +++ b/arch/openrisc/mm/cache.c @@ -63,6 +63,22 @@ void local_icache_page_inv(struct page *page) } EXPORT_SYMBOL(local_icache_page_inv); +void local_icache_all_inv(void) +{ + if (cpu_cache_is_present(SPR_UPR_ICP)) { + unsigned long iccfgr = mfspr(SPR_ICCFGR); + unsigned long sets = 1 << ((iccfgr & SPR_ICCFGR_NCS) >> 3); + unsigned long block_size = 16 << ((iccfgr & SPR_ICCFGR_CBS) >> 7); + unsigned long paddr = 0; + unsigned long end = sets * block_size; + + while (paddr < end) { + mtspr(SPR_ICBIR, paddr); + paddr += block_size; + } + } +} + void local_dcache_range_flush(unsigned long start, unsigned long end) { cache_loop(start, end, SPR_DCBFR, SPR_UPR_DCP); From b6337e3687d3d6652b951f534dfc62fbbdcdb9a4 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Sat, 18 Apr 2026 13:10:48 +0000 Subject: [PATCH 1878/3297] ocfs2: use kzalloc for quota recovery bitmap allocation commit 93c8c6ea90be9e9df8fe14048ad4e3caad0770a6 upstream. ocfs2 quota recovery allocates a bitmap buffer with kmalloc and does not fully initialize it. This can lead to use of uninitialized bits during quota recovery from a corrupted filesystem image. Use kzalloc instead to ensure the bitmap is zero-initialized. Link: https://lore.kernel.org/20260418131048.1052507-1-tristmd@gmail.com Reported-by: syzbot+7ea0b96c4ddb49fd1a70@syzkaller.appspotmail.com Signed-off-by: Tristan Madani Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/quota_local.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index de7f12858729a..bd3eb098097f5 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(struct super_block *sb, if (!rc) return -ENOMEM; rc->rc_chunk = chunk; - rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS); + rc->rc_bitmap = kzalloc(sb->s_blocksize, GFP_NOFS); if (!rc->rc_bitmap) { kfree(rc); return -ENOMEM; From ace3a0c839f3bfe1779c6708e7709c824b96dc2f Mon Sep 17 00:00:00 2001 From: Bastien Curutchet Date: Tue, 26 May 2026 09:10:00 +0200 Subject: [PATCH 1879/3297] mtd: rawnand: pl353: fix probe resource allocation commit 19ed11aee966d91beebdef9d32ce926474872f79 upstream. During probe(), the devm_ioremap() is called with the parent device instead of the current one. So when the module is unloaded, the register area isn't released. Target the pl35x device in the devm_ioremap() instead of its parent. Cc: stable@vger.kernel.org Fixes: 08d8c62164a3 ("mtd: rawnand: pl353: Add support for the ARM PL353 SMC NAND controller") Signed-off-by: Bastien Curutchet Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/pl35x-nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index 50d4305729f4a..81dde993ee9b0 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -1152,7 +1152,7 @@ static int pl35x_nand_probe(struct platform_device *pdev) nfc->controller.ops = &pl35x_nandc_ops; INIT_LIST_HEAD(&nfc->chips); - nfc->conf_regs = devm_ioremap_resource(&smc_amba->dev, &smc_amba->res); + nfc->conf_regs = devm_ioremap_resource(nfc->dev, &smc_amba->res); if (IS_ERR(nfc->conf_regs)) return PTR_ERR(nfc->conf_regs); From a8874c34c4a973f9922908a4b8be1d1278f01e42 Mon Sep 17 00:00:00 2001 From: Vasiliy Kovalev Date: Wed, 15 Apr 2026 18:52:37 +0300 Subject: [PATCH 1880/3297] net/9p: fix infinite loop in p9_client_rpc on fatal signal commit 6b4f48728faa8bb514368f7eacda05565dea8696 upstream. When p9_client_rpc() is called with type P9_TFLUSH and the transport has no peer (e.g. fd transport backed by pipes with no 9p server), a fatal signal causes an infinite loop: again: err = io_wait_event_killable(req->wq, ...) /* SIGKILL wakes the task, returns -ERESTARTSYS */ if (err == -ERESTARTSYS && c->status == Connected && type == P9_TFLUSH) { sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; } clear_thread_flag() clears TIF_SIGPENDING before jumping back to io_wait_event_killable(). signal_pending_state() checks TIF_SIGPENDING, finds it zero, and the task goes to sleep again. The task can only wake on the next signal delivery that calls signal_wake_up() and sets TIF_SIGPENDING again. When that happens the loop repeats, clears TIF_SIGPENDING, and sleeps again indefinitely. This is triggered in practice by coredump_wait(): when a thread in a multi-threaded process causes a coredump (e.g. via SIGSYS from Syscall User Dispatch), coredump_wait() sends SIGKILL to all other threads and waits for them to call mm_release(). If one of those threads is blocked in p9_client_rpc() over an fd transport with no peer, it enters the P9_TFLUSH loop and never calls mm_release(), so coredump_wait() stalls forever: INFO: task syz.0.18:676 blocked for more than 143 seconds. Not tainted 6.12.77+ #1 task:syz.0.18 state:D stack:27600 pid:676 tgid:673 ppid:630 flags:0x00000004 Call Trace: context_switch kernel/sched/core.c:5344 [inline] __schedule+0xcb4/0x5d50 kernel/sched/core.c:6724 __schedule_loop kernel/sched/core.c:6801 [inline] schedule+0xe5/0x350 kernel/sched/core.c:6816 schedule_timeout+0x253/0x290 kernel/time/timer.c:2593 do_wait_for_common kernel/sched/completion.c:95 [inline] __wait_for_common+0x409/0x600 kernel/sched/completion.c:116 wait_for_common kernel/sched/completion.c:127 [inline] wait_for_completion_state+0x1d/0x40 kernel/sched/completion.c:264 coredump_wait fs/coredump.c:448 [inline] do_coredump+0x854/0x4350 fs/coredump.c:629 get_signal+0x1425/0x2730 kernel/signal.c:2903 arch_do_signal_or_restart+0x81/0x880 arch/x86/kernel/signal.c:337 exit_to_user_mode_loop kernel/entry/common.c:111 [inline] exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline] __syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline] syscall_exit_to_user_mode+0xf9/0x160 kernel/entry/common.c:218 do_syscall_64+0x102/0x220 arch/x86/entry/common.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fix: check fatal_signal_pending() before clearing TIF_SIGPENDING in the P9_TFLUSH retry loop. At that point TIF_SIGPENDING is still set, so fatal_signal_pending() works correctly. If a fatal signal is pending, jump to recalc_sigpending to restore TIF_SIGPENDING and return -ERESTARTSYS to the caller. The same defect is present in stable kernels back to 5.4. On those kernels the infinite loop is broken earlier by a second SIGKILL from the parent process (e.g. kill_and_wait() retrying after a timeout), resulting in a zombie process and a shutdown delay rather than a permanent D-state hang, but the underlying flaw is the same. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 91b8534fa8f5 ("9p: make rpc code common and rework flush code") Closes: https://syzkaller.appspot.com/bug?extid=3ce7863f8fc836a427e7 Cc: stable@vger.kernel.org Signed-off-by: Vasiliy Kovalev Message-ID: <20260415155237.182891-1-kovalev@altlinux.org> Signed-off-by: Dominique Martinet Signed-off-by: Greg Kroah-Hartman --- net/9p/client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/9p/client.c b/net/9p/client.c index 9c9d249dabaea..08ae4c44d7305 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -723,6 +723,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) if (err == -ERESTARTSYS && c->status == Connected && type == P9_TFLUSH) { + if (fatal_signal_pending(current)) + goto recalc_sigpending; sigpending = 1; clear_thread_flag(TIF_SIGPENDING); goto again; From 4bbfcf9c7e46cae58257150bf834853559382e28 Mon Sep 17 00:00:00 2001 From: Arseniy Krasnov Date: Tue, 5 May 2026 11:30:30 +0300 Subject: [PATCH 1881/3297] mtd: rawnand: fix condition in 'nand_select_target()' commit 8507c2cc9e4fa402401819f44d1e8a5ef4d11d8b upstream. 'cs' here must be in range [0:nanddev_ntargets[. Cc: stable@vger.kernel.org Fixes: 32813e288414 ("mtd: rawnand: Get rid of chip->numchips") Signed-off-by: Arseniy Krasnov Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index b4bd73d7cd05c..a269d24476c42 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -174,7 +174,7 @@ void nand_select_target(struct nand_chip *chip, unsigned int cs) * cs should always lie between 0 and nanddev_ntargets(), when that's * not the case it's a bug and the caller should be fixed. */ - if (WARN_ON(cs > nanddev_ntargets(&chip->base))) + if (WARN_ON(cs >= nanddev_ntargets(&chip->base))) return; chip->cur_cs = cs; From d5d5a21fb33cd9b963aea99da81e4dacd452cd95 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Thu, 11 Jun 2026 14:35:10 -0700 Subject: [PATCH 1882/3297] ocfs2: avoid moving extents to occupied clusters commit 22920541c35a9f23f219038ba5874c843a7c4419 upstream. For non-auto OCFS2_IOC_MOVE_EXT operations, userspace supplies a physical me_goal. ocfs2_move_extent() initializes new_phys_cpos from that goal and expects ocfs2_probe_alloc_group() to replace it with a free run in the target block group. The probe currently leaves *phys_cpos unchanged if the scan reaches the end of the group without finding a free run. An occupied goal at the last bit can therefore survive the probe and be passed to __ocfs2_move_extent(), which copies file data into a cluster still owned by another inode before the bitmap is updated. When the probe does find a free run, it also subtracts move_len from the ending bit. The start of an N-bit run ending at i is i - N + 1, so the current calculation can report the bit immediately before the free run. Clear *phys_cpos before scanning and use the correct free-run start. Callers already treat a zero result as -ENOSPC, so failed probes no longer continue with an occupied caller-controlled goal. Link: https://lore.kernel.org/20260611213510.16956-1-kylebot@openai.com Fixes: e6b5859cccfa ("Ocfs2/move_extents: helper to probe a proper region to move in an alloc group.") Fixes: 236b9254f8d1 ("ocfs2: fix non-auto defrag path not working issue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Kyle Zeng Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/move_extents.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c index 26e150c5f25eb..831c6c648f04f 100644 --- a/fs/ocfs2/move_extents.c +++ b/fs/ocfs2/move_extents.c @@ -534,6 +534,8 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, u32 base_cpos = ocfs2_blocks_to_clusters(inode->i_sb, le64_to_cpu(gd->bg_blkno)); + *phys_cpos = 0; + for (i = base_bit; i < le16_to_cpu(gd->bg_bits); i++) { used = ocfs2_test_bit(i, (unsigned long *)gd->bg_bitmap); @@ -555,7 +557,7 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, last_free_bits++; if (last_free_bits == move_len) { - i -= move_len; + i = i - move_len + 1; *goal_bit = i; *phys_cpos = base_cpos + i; break; From bd73971fad89d5ee4ea0ba9b92d2ea08733c4a64 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Thu, 11 Jun 2026 09:46:38 -0500 Subject: [PATCH 1883/3297] ocfs2: fix NULL h_transaction deref in ocfs2_assure_trans_credits commit f9ab30c96b0f00c20c6dac93681bdae3a033d229 upstream. [BUG] A direct write over unwritten extents can panic the kernel in ocfs2_assure_trans_credits() when the journal aborts during DIO completion. The crash is a general protection fault from a NULL pointer dereference. [CAUSE] ocfs2_dio_end_io_write() loops over a direct write's unwritten extents, marking each written under a single journal handle. If the journal aborts (for example after an I/O error) while the extent tree is being updated, the handle is left aborted with its transaction pointer cleared. The extent merge treats that failure as not critical and reports success, so the loop keeps using the handle. ocfs2_assure_trans_credits() reads the handle's remaining credits without first checking whether the handle is aborted, and that read dereferences the cleared transaction pointer. [FIX] A journal abort is recorded in the handle itself, so callers are expected to test the handle rather than rely on a returned error. Make ocfs2_assure_trans_credits() do that, as the other ocfs2 journal helpers already do, and return -EROFS when the handle is aborted. Link: https://lore.kernel.org/airKTsM1fRVN-Wj7@dev Fixes: be346c1a6eeb ("ocfs2: fix DIO failure due to insufficient transaction credits") Signed-off-by: Ian Bridges Reported-by: syzbot+e9c15ff790cea6a0cfae@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e9c15ff790cea6a0cfae Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/journal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index bbfa41d37ec90..8c07610692d06 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -476,8 +476,12 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks) */ int ocfs2_assure_trans_credits(handle_t *handle, int nblocks) { - int old_nblks = jbd2_handle_buffer_credits(handle); + int old_nblks; + if (is_handle_aborted(handle)) + return -EROFS; + + old_nblks = jbd2_handle_buffer_credits(handle); trace_ocfs2_assure_trans_credits(old_nblks); if (old_nblks >= nblocks) return 0; From 671889c553ea55e2da6a4f3b15f4c0fa40f2f0d1 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 10 Jun 2026 19:23:11 -0500 Subject: [PATCH 1884/3297] ocfs2: fix UBSAN array-index-out-of-bounds in ocfs2_sum_rightmost_rec commit 452a8467be8143747292218212671deeb186d2ae upstream. [BUG] On-disk corruption setting l_next_free_rec to 0 in an inode's embedded extent list triggers a UBSAN panic on the next write to that file. [CAUSE] ocfs2_sum_rightmost_rec() computes i = le16_to_cpu(el->l_next_free_rec) - 1 and accesses el->l_recs[i] without validating i. When l_next_free_rec is 0, i becomes -1; when l_next_free_rec exceeds l_count, i falls past the end of the array. Either case violates the __counted_by_le(l_count) annotation on l_recs[] and triggers UBSAN. [FIX] Validate the inode's embedded extent list when the inode is read, in ocfs2_validate_inode_block(): l_count must be non-zero and no larger than the inode block can hold, and l_next_free_rec must not exceed l_count. A corrupt list is rejected at read time, before the b-tree code can index l_recs[] out of bounds. Link: https://lore.kernel.org/ain_780qc0P4ypNd@dev Signed-off-by: Ian Bridges Reported-by: syzbot+be16e33db01e6644db7a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=be16e33db01e6644db7a Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index a840dde611c01..2e93e85f9062b 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1536,6 +1536,38 @@ int ocfs2_validate_inode_block(struct super_block *sb, } } + if (ocfs2_dinode_has_extents(di)) { + struct ocfs2_extent_list *el = &di->id2.i_list; + u16 count = le16_to_cpu(el->l_count); + u16 next_free = le16_to_cpu(el->l_next_free_rec); + + if (count == 0) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count is zero\n", + (unsigned long long)bh->b_blocknr); + goto bail; + } + /* + * The exact capacity depends on i_xattr_inline_size, another + * unvalidated on-disk field. Inline xattrs only shrink the + * list, so the no-xattr maximum is a safe upper bound that a + * valid l_count never exceeds. + */ + if (count > ocfs2_extent_recs_per_inode(sb)) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count %u exceeds max %u\n", + (unsigned long long)bh->b_blocknr, count, + ocfs2_extent_recs_per_inode(sb)); + goto bail; + } + if (next_free > count) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_next_free_rec %u exceeds l_count %u\n", + (unsigned long long)bh->b_blocknr, next_free, count); + goto bail; + } + } + rc = 0; bail: From 499714de42ab4d2e778cbb10186684142a1222eb Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Sun, 31 May 2026 21:16:45 +0800 Subject: [PATCH 1885/3297] ocfs2: add journal NULL check in ocfs2_checkpoint_inode() commit a291c77c034b7a81849ce9b71cc9ecda9e587d89 upstream. During unmount, ocfs2_journal_shutdown() frees the journal and sets osb->journal to NULL. Later, when VFS evicts remaining cached inodes, ocfs2_evict_inode() -> ocfs2_clear_inode() -> ocfs2_checkpoint_inode() -> ocfs2_ci_fully_checkpointed() dereferences osb->journal, causing a NULL pointer dereference. Fix this by adding a NULL check for osb->journal in ocfs2_checkpoint_inode(). If the journal is NULL, it has already been fully flushed and destroyed during shutdown, so there is nothing to checkpoint. Link: https://lore.kernel.org/20260531131645.3650299-1-joseph.qi@linux.alibaba.com Reported-by: Farhad Alemi Fixes: da5e7c87827e ("ocfs2: cleanup journal init and shutdown") Signed-off-by: Joseph Qi Tested-by: Farhad Alemi Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/journal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 6397170f302f2..f8b3b2a3d6309 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode) if (ocfs2_mount_local(osb)) return; + if (!osb->journal) + return; + if (!ocfs2_ci_fully_checkpointed(INODE_CACHE(inode))) { /* WARNING: This only kicks off a single * checkpoint. If someone races you and adds more From 4db3b6a2a8ecf2a89d26a4090ace4072c6fad050 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:02 -0400 Subject: [PATCH 1886/3297] ocfs2: reject dinodes with non-canonical i_mode type commit 5366a017099c6a3c443be908a05f26fd72af12a1 upstream. Patch series "ocfs2: harden inode validators against forged metadata", v2. This series adds three structural checks to OCFS2 dinode validation so malformed on-disk fields are rejected before ocfs2_populate_inode() copies them into the in-core inode. The checks cover: - i_mode values whose type bits do not name a canonical POSIX file type; - non-device dinodes whose id1.dev1.i_rdev field is non-zero; and - non-inline dinodes that claim non-zero i_size while i_clusters is zero, covering directories unconditionally and regular files on non-sparse volumes. The normal read path reports these through ocfs2_error(), matching the existing suballoc-slot, inline-data, chain-list, and refcount checks. The online filecheck path uses the same structural predicates but keeps its own reporting contract, returning OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(). This patch (of 3): ocfs2_validate_inode_block() currently accepts any non-zero i_mode value. ocfs2_populate_inode() then copies that mode verbatim into inode->i_mode and dispatches on i_mode & S_IFMT to the file/dir/symlink/special_file iops; an unrecognised type falls through to ocfs2_special_file_iops and init_special_inode(). Reject dinodes whose type bits do not name one of the seven canonical POSIX file types. Use fs_umode_to_ftype(), the same generic file-type conversion helper OCFS2 already uses for directory entries, so the accepted inode type set matches the kernel file-type vocabulary instead of open-coding a local switch. Apply the same structural check to the online filecheck read path. filecheck keeps its own error namespace, so it reports malformed i_mode through the filecheck logger and OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(), but it must not allow a malformed dinode to proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-1-michael.bommarito@gmail.com Link: https://lore.kernel.org/20260519110404.1803902-2-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 2e93e85f9062b..388b53adf497f 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -64,7 +64,12 @@ static int ocfs2_filecheck_read_inode_block_full(struct inode *inode, static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, struct buffer_head *bh); static int ocfs2_filecheck_repair_inode_block(struct super_block *sb, - struct buffer_head *bh); + struct buffer_head *bh); + +static bool ocfs2_valid_inode_mode(umode_t mode) +{ + return fs_umode_to_ftype(mode) != FT_UNKNOWN; +} void ocfs2_set_inode_flags(struct inode *inode) { @@ -1505,6 +1510,24 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Reject dinodes whose i_mode does not name one of the seven + * canonical POSIX file types. ocfs2_populate_inode() copies + * i_mode verbatim into inode->i_mode and then dispatches via + * switch (mode & S_IFMT) to file/dir/symlink/special_file iops; + * an unrecognised type falls into ocfs2_special_file_iops with + * init_special_inode(), which interprets i_rdev. Constrain the + * type here so the dispatch only ever sees a value mkfs.ocfs2 / + * VFS can produce. + */ + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1633,6 +1656,15 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le32_to_cpu(di->i_fs_generation)); rc = -OCFS2_FILECHECK_ERR_GENERATION; + goto bail; + } + + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: @@ -1820,4 +1852,3 @@ const struct ocfs2_caching_operations ocfs2_inode_caching_ops = { .co_io_lock = ocfs2_inode_cache_io_lock, .co_io_unlock = ocfs2_inode_cache_io_unlock, }; - From 5e512d370a01b594d2dc01a8ad954af51d8c322f Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:03 -0400 Subject: [PATCH 1887/3297] ocfs2: reject dinodes whose i_rdev disagrees with the file type commit 51407c2d249987a466416890a5c931a2354a46aa upstream. id1.dev1.i_rdev is the device-number arm of the ocfs2_dinode id1 union. It is only meaningful for character and block device inodes. For any other user-visible file type the on-disk value must be zero. ocfs2_populate_inode() currently copies id1.dev1.i_rdev into inode->i_rdev before the S_IFMT switch decides whether the inode is a special file. A non-device inode with a non-zero i_rdev can therefore publish stale or attacker-controlled device state into the in-core inode. System inodes legitimately use other arms of the same union, so keep the cross-check restricted to non-system inodes. Factor that predicate into a helper and use it in both the normal validator and online filecheck path; filecheck reports the malformed dinode through OCFS2_FILECHECK_ERR_INVALIDINO instead of ocfs2_error(). Link: https://lore.kernel.org/20260519110404.1803902-3-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 388b53adf497f..62ec596089006 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -71,6 +71,16 @@ static bool ocfs2_valid_inode_mode(umode_t mode) return fs_umode_to_ftype(mode) != FT_UNKNOWN; } +static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + + return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1528,6 +1538,41 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * id1.dev1.i_rdev is the device-number arm of the id1 union and + * is only meaningful for character and block device inodes. For + * any other regular user-visible file type the on-disk value + * must be zero. ocfs2_populate_inode() currently runs + * + * inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); + * + * unconditionally, before the S_IFMT switch decides whether the + * inode is a special file. As a result, an i_rdev value present + * on a non-device inode is silently published into the in-core + * inode; a subsequent forced re-read or in-core mode mutation + * (cluster peer with raw write access to the shared LUN, + * on-disk corruption, or a separately forged dinode) can then + * expose the attacker-controlled device number to + * init_special_inode() without ever showing an unusual i_mode + * at validation time. + * + * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1 + * and journal1 arms of the same union (allocator i_used / + * i_total counters and the journal ij_flags / + * ij_recovery_generation pair); those bytes are not an i_rdev + * and must not be checked here. Restrict the cross-check to + * non-system inodes, which is the full attacker-controllable + * surface. + */ + if (ocfs2_dinode_has_unexpected_rdev(di)) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1665,6 +1710,16 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le16_to_cpu(di->i_mode)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_unexpected_rdev(di)) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From 2174c68f623b74bd8690a4a6a4a52882d8ac3dc3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:04 -0400 Subject: [PATCH 1888/3297] ocfs2: reject non-inline dinodes with i_size and zero i_clusters commit 7ebc672fab7a76e1e47e0f2fc1ee48118d27fde4 upstream. On a volume mounted without OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, a non-inline regular file with non-zero i_size and zero i_clusters is structurally malformed: the extent map declares no allocated clusters yet the size header claims content exists. Keep rejecting that shape, but express it through a shared predicate so the same invariant is available to normal inode reads and online filecheck. The same zero-cluster shape is also malformed for non-inline directories. ocfs2 directory growth allocates backing storage before advancing i_size, and ocfs2_dir_foreach_blk_el() later walks until ctx->pos reaches i_size_read(inode). A forged directory dinode with a huge i_size and no clusters would repeatedly fail on holes while advancing through the claimed size. Sparse regular files remain exempt: on sparse-alloc volumes, truncate can legitimately grow i_size without allocating clusters. System inodes and inline-data dinodes also retain their separate storage rules. Mirror the check in ocfs2_filecheck_validate_inode_block() as well. filecheck reports through its own error namespace, so malformed size/cluster state is logged as a filecheck invalid-inode result rather than via ocfs2_error(), but it must not proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-4-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/inode.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 62ec596089006..d247d69349e4a 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -81,6 +81,24 @@ static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; } +static bool ocfs2_dinode_has_size_without_clusters(struct super_block *sb, + struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) + return false; + if (!le64_to_cpu(di->i_size) || le32_to_cpu(di->i_clusters)) + return false; + + if (S_ISDIR(mode)) + return true; + + return !ocfs2_sparse_alloc(OCFS2_SB(sb)) && S_ISREG(mode); +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1573,6 +1591,33 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Non-inline directories must not have i_size without allocated + * clusters: directory growth adds storage before advancing i_size, + * and readdir walks i_size block-by-block. A forged directory + * with zero clusters and a huge i_size would repeatedly fault on + * holes while advancing through the claimed size. + * + * Non-inline regular files have the same invariant on non-sparse + * volumes. Sparse regular files are different: truncate can + * legitimately grow i_size without allocating clusters, so keep + * the sparse-alloc carveout for S_IFREG only. System inodes and + * inline-data dinodes have their own storage rules. + */ + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + rc = ocfs2_error(sb, + "Invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + rc = ocfs2_error(sb, + "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1720,6 +1765,21 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, le16_to_cpu(di->i_mode), (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From 012683accbb7d2bcf1264f2a0ecd6aa1dcea4d68 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:40 -0600 Subject: [PATCH 1889/3297] fpga: dfl: add bounds check in dfh_get_param_size() commit 9e8bc49f91f3f81d957c4f1c1f09fe94e2f88f6a upstream. dfh_get_param_size() can return a parameter size larger than the feature region because the loop bounds check is evaluated before incrementing size. If the EOP (End of Parameters) bit is set in the same iteration, the inflated size is returned without re-validation against max. This can cause create_feature_instance() to call memcpy_fromio() with a size exceeding the ioremap'd region when a malicious FPGA device provides crafted DFHv1 parameter headers. Add a bounds check after the size increment to ensure the accumulated size never exceeds the feature boundary. Fixes: 4747ab89b4a6 ("fpga: dfl: add basic support for DFHv1") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-2-sebasjosue84@gmail.com Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/dfl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 449c3a082e232..cc5123ebe3e1b 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -1133,6 +1133,8 @@ static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max) return -EINVAL; size += next * sizeof(u64); + if (size > max) + return -EINVAL; if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) return size; From 3ebe0ee6527e85cb448a085ade59404332067af2 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 27 May 2026 10:46:13 +0200 Subject: [PATCH 1890/3297] bus: mhi: host: pci_generic: Fix the physical function check commit 32845111e8ea6ef5e837b6d25080e69e99bd4561 upstream. Commit b4d01c5b9a9d ("bus: mhi: host: pci_generic: Read SUBSYSTEM_VENDOR_ID for VF's to check status") added the check for physical function by checking for 'pdev->is_physfn. But 'pdev->is_physfn' is only set for the physical function of a SR-IOV capable device. But for the non-SR-IOV device this variable will be 0. So this check ended up breaking the health check functionality for all non-SR-IOV devices. Fix it by checking for '!pdev->is_virtfn' to make sure that the check is only skipped for virtual functions. Cc: stable@vger.kernel.org # 6.18 Reported-by: Slark Xiao Tested-by: Slark Xiao Fixes: b4d01c5b9a9d ("bus: mhi: host: pci_generic: Read SUBSYSTEM_VENDOR_ID for VF's to check status") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/host/pci_generic.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index b929c7d6c49aa..646f3bcdc636a 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -1197,7 +1197,7 @@ static void mhi_pci_recovery_work(struct work_struct *work) dev_warn(&pdev->dev, "device recovery started\n"); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); pm_runtime_forbid(&pdev->dev); @@ -1227,7 +1227,7 @@ static void mhi_pci_recovery_work(struct work_struct *work) set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); return; @@ -1318,7 +1318,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl_config = info->config; /* Initialize health check monitor only for Physical functions */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_setup(&mhi_pdev->health_check_timer, health_check, 0); mhi_cntrl = &mhi_pdev->mhi_cntrl; @@ -1340,7 +1340,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl->mru = info->mru_default; mhi_cntrl->name = info->name; - if (pdev->is_physfn) + if (!pdev->is_virtfn) mhi_pdev->reset_on_remove = info->reset_on_remove; if (info->edl_trigger) @@ -1389,7 +1389,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); /* start health check */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); /* Allow runtime suspend only if both PME from D3Hot and M3 are supported */ @@ -1417,7 +1417,7 @@ static void mhi_pci_remove(struct pci_dev *pdev) pci_disable_sriov(pdev); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete_sync(&mhi_pdev->health_check_timer); cancel_work_sync(&mhi_pdev->recovery_work); @@ -1449,7 +1449,7 @@ static void mhi_pci_reset_prepare(struct pci_dev *pdev) dev_info(&pdev->dev, "reset\n"); - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); /* Clean up MHI state */ @@ -1495,7 +1495,7 @@ static void mhi_pci_reset_done(struct pci_dev *pdev) } set_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status); - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); } @@ -1561,7 +1561,7 @@ static int __maybe_unused mhi_pci_runtime_suspend(struct device *dev) if (test_and_set_bit(MHI_PCI_DEV_SUSPENDED, &mhi_pdev->status)) return 0; - if (pdev->is_physfn) + if (!pdev->is_virtfn) timer_delete(&mhi_pdev->health_check_timer); cancel_work_sync(&mhi_pdev->recovery_work); @@ -1614,7 +1614,7 @@ static int __maybe_unused mhi_pci_runtime_resume(struct device *dev) } /* Resume health check */ - if (pdev->is_physfn) + if (!pdev->is_virtfn) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); /* It can be a remote wakeup (no mhi runtime_get), update access time */ From fc74244e0cc2520b9a42f5e9fa285c7588ea76db Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 2 Mar 2026 14:26:12 +0530 Subject: [PATCH 1891/3297] bus: mhi: ep: Protect mhi_ep_handle_syserr() in the error path commit 519ddf194b158b91439319f6b977b8a465fda0fb upstream. All the callers of mhi_ep_handle_syserr() except mhi_ep_process_cmd_ring() are holding the 'state_lock' to avoid the race in setting the MHI state. So do the same in mhi_ep_process_cmd_ring() for sanity. Fixes: e827569062a8 ("bus: mhi: ep: Add support for processing command rings") Cc: stable@vger.kernel.org # 5.18 Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260302085612.18725-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/ep/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 4bb007d193331..2f58ad0f14b65 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -232,7 +232,9 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele ret = mhi_ep_create_device(mhi_cntrl, ch_id); if (ret) { dev_err(dev, "Error creating device for channel (%u)\n", ch_id); + mutex_lock(&mhi_cntrl->state_lock); mhi_ep_handle_syserr(mhi_cntrl); + mutex_unlock(&mhi_cntrl->state_lock); return ret; } } From e5824d5b841d99a2bcdd4e2c256643293bbc22c1 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 01:38:41 +0800 Subject: [PATCH 1892/3297] net: thunderbolt: Fix frags[] overflow by bounding frame_count commit 55d9895f89970501fe126d1026b586b04a224c27 upstream. tbnet_poll() assembles a multi-frame ThunderboltIP packet into one skb. The first frame goes into the skb linear area and every further frame is added as a page fragment. skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, hdr_size, frame_size, TBNET_RX_PAGE_SIZE - hdr_size); A packet of frame_count frames therefore ends up with frame_count - 1 fragments. tbnet_check_frame() only bounds the peer supplied frame_count to TBNET_RING_SIZE / 4 (64), which is far above MAX_SKB_FRAGS (17 by default). A peer that sends a packet of 19 or more small frames pushes nr_frags past MAX_SKB_FRAGS, so skb_add_rx_frag() writes past skb_shinfo()->frags[] and corrupts memory after the shared info. Tighten the start of packet bound to MAX_SKB_FRAGS + 1 so a packet can never produce more fragments than frags[] can hold. This matches the recent skb frags overflow fixes in other receive paths, for example f0813bcd2d9d ("net: wwan: t7xx: fix potential skb->frags overflow in RX path") and 600dc40554dc ("net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()"). Fixes: e69b6c02b4c3 ("net: Add support for networking over Thunderbolt cable") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Acked-by: Mika Westerberg Link: https://patch.msgid.link/178163152194.2486768.14724194232649760778@maoyixie.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/thunderbolt/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index dcaa62377808c..5d7d11d75b0b8 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -786,8 +786,12 @@ static bool tbnet_check_frame(struct tbnet *net, const struct tbnet_frame *tf, return true; } - /* Start of packet, validate the frame header */ - if (frame_count == 0 || frame_count > TBNET_RING_SIZE / 4) { + /* Start of packet, validate the frame header. tbnet_poll() puts the + * first frame in the skb linear area and every further frame in a page + * fragment, so a packet may not span more than MAX_SKB_FRAGS + 1 frames + * without overflowing skb_shinfo()->frags[]. + */ + if (frame_count == 0 || frame_count > MAX_SKB_FRAGS + 1) { net->stats.rx_length_errors++; return false; } From c9ef79e34bc1eac4fd59051e5c7b96a74e59d46f Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Mon, 18 May 2026 13:07:42 -0600 Subject: [PATCH 1893/3297] fpga: microchip-spi: fix zero header_size OOB read in mpf_ops_parse_header() commit 43a1974da6bc7ce8f4d1dc1d03d56997428c29c3 upstream. mpf_ops_parse_header() reads header_size from the bitstream at MPF_HEADER_SIZE_OFFSET (24). When header_size is zero, the expression *(buf + header_size - 1) reads one byte before the buffer start. Since initial_header_size is set to 71 in mpf_ops, the fpga-mgr core guarantees the buffer is large enough to reach MPF_HEADER_SIZE_OFFSET. The only real gap is the zero header_size case, which cannot be resolved by providing a larger buffer, so return -EINVAL. Fixes: 5f8d4a900830 ("fpga: microchip-spi: add Microchip MPF FPGA manager") Cc: stable@vger.kernel.org Signed-off-by: Sebastian Alba Vives Reviewed-by: Xu Yilun Link: https://lore.kernel.org/r/20260518190742.61426-4-sebasjosue84@gmail.com Signed-off-by: Xu Yilun Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/microchip-spi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index 6134cea86ac85..cc8f6d7bb9781 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -116,6 +116,9 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr, } header_size = *(buf + MPF_HEADER_SIZE_OFFSET); + if (!header_size) + return -EINVAL; + if (header_size > count) { info->header_size = header_size; return -EAGAIN; From 693bf91d4db134f9b1c2840c8e287eee3d993bac Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 15 Jun 2026 17:39:12 +0200 Subject: [PATCH 1894/3297] s390/pkey: Check length in PKEY_VERIFYPROTK ioctl commit b3d4ab2d7df9426f7f1d3671d7e2108f2ca6e970 upstream. Explicitly check the buffer length request structure provided by user-space and fail, if it exceeds the buffer size. Cc: stable@vger.kernel.org Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reported-by: Christian Borntraeger Reviewed-by: Harald Freudenberger Reviewed-by: Ingo Franzki Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/pkey_api.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 01549003a9034..162109f1e3dfc 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -335,6 +335,13 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) if (copy_from_user(&kvp, uvp, sizeof(kvp))) return -EFAULT; + if (kvp.protkey.len > sizeof(kvp.protkey.protkey)) { + PKEY_DBF_ERR("%s protkey length %u exceeds protkey buffer size\n", + __func__, kvp.protkey.len); + memzero_explicit(&kvp, sizeof(kvp)); + return -EINVAL; + } + keytype = pkey_aes_bitsize_to_keytype(8 * kvp.protkey.len); if (!keytype) { PKEY_DBF_ERR("%s unknown/unsupported protkey length %u\n", From 433e5e70cdc1edf382d28d08a885b22e2b98b7da Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 17 Jun 2026 19:06:39 +0200 Subject: [PATCH 1895/3297] s390/pkey: Check length in pkey_pckmo handler implementation commit 1ac287e2af9a9112fe271427ef45eceb26bce8b4 upstream. Explicitly check the length of the target buffer in the pkey_pckmo implementation of the key_to_protkey() handler function. The handler function fails, if the generated output data exceeds the length of the provided target buffer. Cc: stable@vger.kernel.org Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reported-by: Christian Borntraeger Reviewed-by: Harald Freudenberger Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/pkey_pckmo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/s390/crypto/pkey_pckmo.c b/drivers/s390/crypto/pkey_pckmo.c index 7eca9f1340bdd..f3078b41ae1a4 100644 --- a/drivers/s390/crypto/pkey_pckmo.c +++ b/drivers/s390/crypto/pkey_pckmo.c @@ -257,6 +257,10 @@ static int pckmo_key2protkey(const u8 *key, u32 keylen, goto out; break; } + if (t->len > *protkeylen) { + rc = -EINVAL; + goto out; + } memcpy(protkey, t->protkey, t->len); *protkeylen = t->len; *protkeytype = t->keytype; From c0806df5cf806545160f3fdac3c888926ceac557 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 26 May 2026 16:56:26 +0200 Subject: [PATCH 1896/3297] mtd: spi-nor: swp: Improve locking user experience commit e1d456b26bf23e30db305a6184e8abd9ab68bbf2 upstream. In the case of the first block being locked (or the few first blocks), if the user want to fully unlock the device it has two possibilities: - either it asks to unlock the entire device, and this works; - or it asks to unlock just the block(s) that are currently locked, which fails. It fails because the conditions "can_be_top" and "can_be_bottom" are true. Indeed, in this case, we unlock everything, so the TB bit does not matter. However in the current implementation, use_top would be true (as this is the favourite option) and lock_len, which in practice should be reduced down to 0, is set to "nor->params->size - (ofs + len)" which is a positive number. This is wrong. An easy way is to simply add an extra condition. In the unlock() path, if we can achieve the same result from both sides, it means we unlock everything and lock_len must simply be 0. A comment is added to clarify that logic. Fixes: 3dd8012a8eeb ("mtd: spi-nor: add TB (Top/Bottom) protect support") Cc: stable@kernel.org Signed-off-by: Miquel Raynal Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/spi-nor/swp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index e67a81dbb6bf6..d5f4bf555cfc6 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -282,8 +282,15 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) /* Prefer top, if both are valid */ use_top = can_be_top; - /* lock_len: length of region that should remain locked */ - if (use_top) + /* + * lock_len: length of region that should remain locked. + * + * When can_be_top and can_be_bottom booleans are true, both adjacent + * regions are unlocked, thus the entire flash can be unlocked. + */ + if (can_be_top && can_be_bottom) + lock_len = 0; + else if (use_top) lock_len = nor->params->size - (ofs + len); else lock_len = ofs; From 0fd20c1905abc90754bfaa60fdd0d91fd3b724cb Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Wed, 27 May 2026 18:05:39 +0900 Subject: [PATCH 1897/3297] mtd: spi-nor: spansion: use die erase for multi-die devices only commit 306e443156b82a2a14a3f33da908c303be45529c upstream. Die erase opcode is supported in multi-die devices only. For single die devices, default chip erase opcode must be used. In s25hx_t_late_init(), die erase opcode is set only when the device is multi-die. Fixes: 461d0babb544 ("mtd: spi-nor: spansion: enable die erase for multi die flashes") Cc: stable@kernel.org Reviewed-by: Pratyush Yadav Reviewed-by: Tudor Ambarus Reviewed-by: Miquel Raynal Signed-off-by: Takahiro Kuwano Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/spi-nor/spansion.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index a0296c8716346..3ba7dcc39032e 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -674,7 +674,9 @@ static int s25hx_t_late_init(struct spi_nor *nor) params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); - params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + if (params->n_dice > 1) + params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + return 0; } From ff7bcc9d71bf4017fe7d0db99a542c46ad3c7662 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 22 May 2026 11:17:39 +0200 Subject: [PATCH 1898/3297] mtd: rawnand: Pause continuous reads at block boundaries commit 8e4531667d718e2e9b193928cf9b2497fa0d01ef upstream. Some chips do not support sequential cached reads past block boundaries, like Winbond. In practice when using UBI, this should very rarely happen, but let's make sure it never happens. Cc: stable@vger.kernel.org Fixes: 003fe4b9545b ("mtd: rawnand: Support for sequential cache reads") Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/nand_base.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a269d24476c42..19b7e22174b94 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1215,32 +1215,32 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, return nand_exec_op(chip, &op); } -static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun) +static unsigned int rawnand_last_page_of_block(unsigned int ppb, unsigned int block) { - /* lun is expected to be very small */ - return (lun * pages_per_lun) + pages_per_lun - 1; + /* block is expected to be very small */ + return (block * ppb) + ppb - 1; } static void rawnand_cap_cont_reads(struct nand_chip *chip) { struct nand_memory_organization *memorg; - unsigned int ppl, first_lun, last_lun; + unsigned int ppb, first_block, last_block; memorg = nanddev_get_memorg(&chip->base); - ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun; - first_lun = chip->cont_read.first_page / ppl; - last_lun = chip->cont_read.last_page / ppl; + ppb = memorg->pages_per_eraseblock; + first_block = chip->cont_read.first_page / ppb; + last_block = chip->cont_read.last_page / ppb; - /* Prevent sequential cache reads across LUN boundaries */ - if (first_lun != last_lun) - chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun); + /* Prevent sequential cache reads across block boundaries */ + if (first_block != last_block) + chip->cont_read.pause_page = rawnand_last_page_of_block(ppb, first_block); else chip->cont_read.pause_page = chip->cont_read.last_page; if (chip->cont_read.first_page == chip->cont_read.pause_page) { chip->cont_read.first_page++; chip->cont_read.pause_page = min(chip->cont_read.last_page, - rawnand_last_page_of_lun(ppl, first_lun + 1)); + rawnand_last_page_of_block(ppb, first_block + 1)); } if (chip->cont_read.first_page >= chip->cont_read.last_page) From 3fac46068fe4cea22ed373432b9173a915e8e60d Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Fri, 22 May 2026 17:28:31 +0100 Subject: [PATCH 1899/3297] openrisc: Fix jump_label smp syncing commit aca063c9024522e4e5b9a9d1927433f6a01785a3 upstream. The original commit 8c30b0018f9d ("openrisc: Add jump label support") copies from arm64 and does not properly consider how icache invalidation on remote cores works in OpenRISC. On OpenRISC remote icaches need to be invalidated otherwise static key's may remain state after updating. Fix SMP cache syncing by: 1. Properly invalidate remote core icaches on SMP systems by using icache_all_inv. The old code uses kick_all_cpus_sync() which runs a no-op IPI function call on remote CPU's which does execute a lot of code and flushes many cache lines in the process, but does not flush all and it's not correct on OpenRISC. 2. For architectures that do not have WRITETHROUGH caches be sure to flush the dcache after patching. To test this I first reproduced the issue using a custom test module [0]. The test confirmed that some icache lines maintained stale static_key code sequences after calling static_branch_enable(). After this patch there are no longer jump_label coherency issues. [0] https://github.com/stffrdhrn/or1k-utils/tree/master/tests/smp_static_key_test Cc: stable@vger.kernel.org # depends on openrisc: Add icache_all_inv Fixes: 8c30b0018f9d ("openrisc: Add jump label support") Signed-off-by: Stafford Horne Signed-off-by: Greg Kroah-Hartman --- arch/openrisc/kernel/jump_label.c | 2 +- arch/openrisc/kernel/patching.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/openrisc/kernel/jump_label.c b/arch/openrisc/kernel/jump_label.c index ab7137c23b46c..9cb63f2d2e2b7 100644 --- a/arch/openrisc/kernel/jump_label.c +++ b/arch/openrisc/kernel/jump_label.c @@ -47,5 +47,5 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry, void arch_jump_label_transform_apply(void) { - kick_all_cpus_sync(); + icache_all_inv(); } diff --git a/arch/openrisc/kernel/patching.c b/arch/openrisc/kernel/patching.c index d186172beb337..5db027b78bc40 100644 --- a/arch/openrisc/kernel/patching.c +++ b/arch/openrisc/kernel/patching.c @@ -49,6 +49,9 @@ static int __patch_insn_write(void *addr, u32 insn) waddr = patch_map(addr, FIX_TEXT_POKE0); ret = copy_to_kernel_nofault(waddr, &insn, OPENRISC_INSN_SIZE); + if (!IS_ENABLED(CONFIG_DCACHE_WRITETHROUGH)) + local_dcache_range_flush((unsigned long)waddr, + (unsigned long)waddr + OPENRISC_INSN_SIZE); local_icache_range_inv((unsigned long)waddr, (unsigned long)waddr + OPENRISC_INSN_SIZE); From 9ac007affa77c3dc3def53760e3d69c350fc9967 Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 18 May 2026 13:45:21 +0200 Subject: [PATCH 1900/3297] mtd: maps: vmu-flash: fix NULL pointer dereference in initialization commit 357e3b8e3a8769ba36eb8ec5e053e4825f1a9329 upstream. The mtd_info contains a struct device, which must be linked to its parent. Without this, the initialization of the MTD fails with a NULL pointer dereference. Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.") Cc: stable@vger.kernel.org Signed-off-by: Florian Fuchs Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/maps/vmu-flash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 53019d313db71..3667faefe6c58 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -547,6 +547,7 @@ static void vmu_queryblocks(struct mapleq *mq) mpart->partition = card->partition; mtd_cur->priv = mpart; mtd_cur->owner = THIS_MODULE; + mtd_cur->dev.parent = &mdev->dev; pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL); if (!pcache) From 0d078152fcab71f7a273b41051affb07f74a028a Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Mon, 13 Apr 2026 23:45:44 +0800 Subject: [PATCH 1901/3297] taskstats: retain dead thread stats in TGID queries commit b3e4fbb04220efc3bc022bcf31b5689d39c6b111 upstream. Patch series "taskstats: fix TGID dead-thread stat retention", v3. This series fixes a taskstats TGID aggregation bug where fields added in the TGID query path were not preserved after thread exit, and adds a kselftest covering the regression. The first patch keeps the cached TGID aggregate used for dead threads in step with the fields already accumulated for live threads, and also fixes the final TGID exit notification emitted when group_dead is true. The second patch adds a kselftest that verifies TGID CPU stats do not regress after a worker thread exits and has been reaped. This patch (of 2): fill_stats_for_tgid() builds TGID stats from two sources: the cached aggregate in signal->stats and a scan of the live threads in the group. However, fill_tgid_exit() only accumulates delay accounting into signal->stats. This means that once a thread exits, TGID queries lose the fields that fill_stats_for_tgid() adds for live threads. This gap was introduced incrementally by two earlier changes that extended fill_stats_for_tgid() but did not make the corresponding update to fill_tgid_exit(): - commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") added ac_etime, ac_utime, and ac_stime to the TGID query path. - commit b663a79c1915 ("taskstats: add context-switch counters") added nvcsw and nivcsw to the TGID query path. As a result, those fields were accounted for live threads in TGID queries, but were dropped from the cached TGID aggregate after thread exit. The final TGID exit notification emitted when group_dead is true also copies that cached aggregate, so it loses the same fields. Factor the per-task TGID accumulation into tgid_stats_add_task() and use it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps the cached aggregate used for dead threads aligned with the live-thread accumulation used by TGID queries. Link: https://lore.kernel.org/cover.1776094300.git.cyyzero16@gmail.com Link: https://lore.kernel.org/abd2a15d33343636ab5ba43d540bcfe508bd66c7.1776094300.git.cyyzero16@gmail.com Fixes: 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") Fixes: b663a79c1915 ("taskstats: add context-switch counters") Signed-off-by: Yiyang Chen Acked-by: Balbir Singh Cc: Dr. Thomas Orgis Cc: Oleg Nesterov Cc: Wang Yaxin Cc: Yang Yang Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- kernel/taskstats.c | 62 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 73bd6a6a78935..2cd0172d05160 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -210,13 +210,39 @@ static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) return 0; } +static void tgid_stats_add_task(struct taskstats *stats, + struct task_struct *tsk, u64 now_ns) +{ + u64 delta, utime, stime; + + /* + * Each accounting subsystem calls its functions here to + * accumulate its per-task stats for tsk, into the per-tgid structure + * + * per-task-foo(stats, tsk); + */ + delayacct_add_tsk(stats, tsk); + + /* calculate task elapsed time in nsec */ + delta = now_ns - tsk->start_time; + /* Convert to micro seconds */ + do_div(delta, NSEC_PER_USEC); + stats->ac_etime += delta; + + task_cputime(tsk, &utime, &stime); + stats->ac_utime += div_u64(utime, NSEC_PER_USEC); + stats->ac_stime += div_u64(stime, NSEC_PER_USEC); + + stats->nvcsw += tsk->nvcsw; + stats->nivcsw += tsk->nivcsw; +} + static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) { struct task_struct *tsk, *first; unsigned long flags; int rc = -ESRCH; - u64 delta, utime, stime; - u64 start_time; + u64 now_ns; /* * Add additional stats from live tasks except zombie thread group @@ -233,30 +259,12 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) else memset(stats, 0, sizeof(*stats)); - start_time = ktime_get_ns(); + now_ns = ktime_get_ns(); for_each_thread(first, tsk) { if (tsk->exit_state) continue; - /* - * Accounting subsystem can call its functions here to - * fill in relevant parts of struct taskstsats as follows - * - * per-task-foo(stats, tsk); - */ - delayacct_add_tsk(stats, tsk); - - /* calculate task elapsed time in nsec */ - delta = start_time - tsk->start_time; - /* Convert to micro seconds */ - do_div(delta, NSEC_PER_USEC); - stats->ac_etime += delta; - task_cputime(tsk, &utime, &stime); - stats->ac_utime += div_u64(utime, NSEC_PER_USEC); - stats->ac_stime += div_u64(stime, NSEC_PER_USEC); - - stats->nvcsw += tsk->nvcsw; - stats->nivcsw += tsk->nivcsw; + tgid_stats_add_task(stats, tsk, now_ns); } unlock_task_sighand(first, &flags); @@ -275,18 +283,14 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) static void fill_tgid_exit(struct task_struct *tsk) { unsigned long flags; + u64 now_ns; spin_lock_irqsave(&tsk->sighand->siglock, flags); if (!tsk->signal->stats) goto ret; - /* - * Each accounting subsystem calls its functions here to - * accumalate its per-task stats for tsk, into the per-tgid structure - * - * per-task-foo(tsk->signal->stats, tsk); - */ - delayacct_add_tsk(tsk->signal->stats, tsk); + now_ns = ktime_get_ns(); + tgid_stats_add_task(tsk->signal->stats, tsk, now_ns); ret: spin_unlock_irqrestore(&tsk->sighand->siglock, flags); return; From 36ca587f55a2c04468a89fd16174bb2f78dc04ec Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Sat, 20 Jun 2026 17:39:16 +0530 Subject: [PATCH 1902/3297] irqchip/crossbar: Use correct index in crossbar_domain_free() commit 043db005a8d6932dc7d217c86307e9af0bc10ddc upstream. crossbar_domain_free() resets the domain data and then uses the nulled out irq_data->hwirq member as index to reset the irq_map[] entry and to write the relevant crossbar register with a safe entry. That means it never frees the correct index and keeps the crossbar register connection to the source interrupt active. If it would not reset the domain data, then this would be even worse as irq_data->hwirq holds the source interrupt number, but both the map and register index need the corresponding GIC SPI number and not the source interrupt number. This might even result in an out of bounds access as the source interrupt number can be higher than the maximal index space. Fix this by using the GIC SPI index from the parent domain's irq_data. Fixes: 783d31863fb82 ("irqchip: crossbar: Convert dra7 crossbar to stacked domains") Signed-off-by: Bhargav Joshi Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260620-irq-crossbar-fix-v2-1-b8e8499f468a@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-crossbar.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index aaf1823ba43fc..da0949e5a582a 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -158,9 +158,14 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, for (i = 0; i < nr_irqs; i++) { struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + /* + * irq_map[] is indexed by GIC SPI number. The parent domain's + * hwirq contains the GIC interrupt number (GIC SPI + + * GIC_IRQ_START). + */ + cb->irq_map[d->parent_data->hwirq - GIC_IRQ_START] = IRQ_FREE; + cb->write(d->parent_data->hwirq - GIC_IRQ_START, cb->safe_map); irq_domain_reset_irq_data(d); - cb->irq_map[d->hwirq] = IRQ_FREE; - cb->write(d->hwirq, cb->safe_map); } raw_spin_unlock(&cb->lock); irq_domain_free_irqs_parent(domain, virq, nr_irqs); From 781f28bd982cf3deee7b084cb9e61cef44bb107e Mon Sep 17 00:00:00 2001 From: Baoli Zhang Date: Tue, 21 Apr 2026 08:50:20 +0800 Subject: [PATCH 1903/3297] tpm: restore timeout for key creation commands commit de59d78e64039baa5fed455ddb905ba8263e7ede upstream. Commit 207696b17f38 ("tpm: use a map for tpm2_calc_ordinal_duration()") inadvertently reduced the timeout for TPM2 key creation commands (`CREATE_PRIMARY`, `CREATE`, `CREATE_LOADED`) from 300 seconds to 30 seconds. This causes intermittent timeout failures, with several failures observed across hundreds of test runs on some Intel platforms using Infineon SLB9670 and SLB9672 TPM modules. Restore the timeout to 300 seconds to avoid spurious failures. Cc: stable@vger.kernel.org # v6.18+ Fixes: 207696b17f38 ("tpm: use a map for tpm2_calc_ordinal_duration()") Co-developed-by: Lili Li Signed-off-by: Lili Li Signed-off-by: Baoli Zhang Link: https://lore.kernel.org/r/20260421005021.13765-1-baoli.zhang@linux.intel.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm2-cmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index fcd46b78c78b5..2f506cdb5f458 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -59,9 +59,9 @@ static const struct { {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000}, {TPM2_CC_GET_CAPABILITY, 750}, {TPM2_CC_NV_READ, 2000}, - {TPM2_CC_CREATE_PRIMARY, 30000}, - {TPM2_CC_CREATE, 30000}, - {TPM2_CC_CREATE_LOADED, 30000}, + {TPM2_CC_CREATE_PRIMARY, 300000}, + {TPM2_CC_CREATE, 300000}, + {TPM2_CC_CREATE_LOADED, 300000}, }; /** From ba33b4f9d3423accd2c91a0b0b0680cd589922f2 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 9 May 2026 21:51:07 +0300 Subject: [PATCH 1904/3297] tpm: tpm_tis_spi: Use wait_woken() in wait_for_tmp_stat() commit c0c9cfb3b75def8bf200a2d4db09015806acfeaf upstream. wait_event_interruptible_timeout() evaluates its condition after setting the current task state to TASK_INTERRUPTIBLE. With CONFIG_DEBUG_ATOMIC_SLEEP this triggers a warning when the IRQ wait path is used: tpm_tis_status() tpm_tis_spi_read_bytes() tpm_tis_spi_transfer_full() spi_bus_lock() mutex_lock() Address this with the following measures: 1. Call wait_tpm_stat_cond() only while tasking is running. 2. Use wait_woken() to wait for changes. Cc: stable@vger.kernel.org # v4.19+ Cc: Linus Walleij Reported-by: Stefan Wahren Closes: https://lore.kernel.org/linux-integrity/6964bec7-3dbb-453b-89ef-9b990217a8b9@gmx.net/ Fixes: 1a339b658d9d ("tpm_tis_spi: Pass the SPI IRQ down to the driver") Reviewed-by: Linus Walleij Tested-by: Stefan Wahren Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm_tis_core.c | 35 ++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 8ea252beef7a4..148c559779002 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -66,8 +66,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, bool check_cancel) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + DEFINE_WAIT_FUNC(wait, woken_wake_function); unsigned long stop; - long rc; u8 status; bool canceled = false; u8 sts_mask; @@ -87,23 +87,30 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, /* process status changes with irq support */ if (sts_mask) { ret = -ETIME; + add_wait_queue(queue, &wait); again: + if (wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, + &canceled)) { + ret = canceled ? -ECANCELED : 0; + goto out; + } + timeout = stop - jiffies; if ((long)timeout <= 0) - return -ETIME; - rc = wait_event_interruptible_timeout(*queue, - wait_for_tpm_stat_cond(chip, sts_mask, check_cancel, - &canceled), - timeout); - if (rc > 0) { - if (canceled) - return -ECANCELED; - ret = 0; - } - if (rc == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; + goto out; + + if (signal_pending(current)) { + if (freezing(current)) { + clear_thread_flag(TIF_SIGPENDING); + goto again; + } + goto out; } + + wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); + goto again; +out: + remove_wait_queue(queue, &wait); } if (ret) From 934d1cd40e2893bf7a041b54f6afd1c008d7a21c Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 31 May 2026 08:44:28 -0400 Subject: [PATCH 1905/3297] tpm: tpm2-sessions: wait for async KPP completion in tpm_buf_append_salt commit 73851a7c43dfa52d2ed9415889b33daf85da0ed9 upstream. tpm_buf_append_salt() in drivers/char/tpm/tpm2-sessions.c calls crypto_kpp_generate_public_key() and crypto_kpp_compute_shared_secret() without installing a completion callback, discards both return values, and immediately frees the kpp_request via kpp_request_free(). When the resolved ecdh-nist-p256 KPP backend is asynchronous (atmel-ecc, HPRE, keembay-ocs), either operation returns -EINPROGRESS and the deferred completion worker dereferences the freed request. The path fires automatically from the hwrng_fillfn kernel thread via tpm_get_random -> tpm2_get_random -> tpm2_start_auth_session -> tpm_buf_append_salt on every entropy poll, without any userland action. Install crypto_req_done as the completion callback, wrap both KPP operations in crypto_wait_req(), and propagate errors to the caller. The wait is a no-op for synchronous backends. Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API") Cc: stable@vger.kernel.org # v6.10+ Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm2-sessions.c | 45 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index 6e53a25f61d49..c4cd58f1f9e95 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -490,15 +490,17 @@ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, sha256_final(&sctx, out); } -static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, - struct tpm2_auth *auth) +static int tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, + struct tpm2_auth *auth) { struct crypto_kpp *kpp; struct kpp_request *req; + DECLARE_CRYPTO_WAIT(wait); struct scatterlist s[2], d[1]; struct ecdh p = {0}; u8 encoded_key[EC_PT_SZ], *x, *y; unsigned int buf_len; + int rc; /* secret is two sized points */ tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2); @@ -521,14 +523,15 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(kpp)) { dev_err(&chip->dev, "crypto ecdh allocation failed\n"); - return; + return PTR_ERR(kpp); } buf_len = crypto_ecdh_key_len(&p); if (sizeof(encoded_key) < buf_len) { dev_err(&chip->dev, "salt buffer too small needs %d\n", buf_len); - goto out; + rc = -EINVAL; + goto err_free_kpp; } crypto_ecdh_encode_key(encoded_key, buf_len, &p); /* this generates a random private key */ @@ -536,11 +539,17 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, /* salt is now the public point of this private key */ req = kpp_request_alloc(kpp, GFP_KERNEL); - if (!req) - goto out; + if (!req) { + rc = -ENOMEM; + goto err_free_kpp; + } + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, s, EC_PT_SZ*2); - crypto_kpp_generate_public_key(req); + rc = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); + if (rc) + goto err_free_req; /* * we're not done: now we have to compute the shared secret * which is our private key multiplied by the tpm_key public @@ -552,8 +561,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, kpp_request_set_input(req, s, EC_PT_SZ*2); sg_init_one(d, auth->salt, EC_PT_SZ); kpp_request_set_output(req, d, EC_PT_SZ); - crypto_kpp_compute_shared_secret(req); - kpp_request_free(req); + rc = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); + if (rc) + goto err_free_req; /* * pass the shared secret through KDFe for salt. Note salt @@ -563,8 +573,16 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, */ tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt); - out: + kpp_request_free(req); crypto_free_kpp(kpp); + return 0; + +err_free_req: + kpp_request_free(req); + +err_free_kpp: + crypto_free_kpp(kpp); + return rc; } /** @@ -1019,7 +1037,12 @@ int tpm2_start_auth_session(struct tpm_chip *chip) tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce)); /* append encrypted salt and squirrel away unencrypted in auth */ - tpm_buf_append_salt(&buf, chip, auth); + rc = tpm_buf_append_salt(&buf, chip, auth); + if (rc) { + tpm2_flush_context(chip, null_key); + tpm_buf_destroy(&buf); + goto out; + } /* session type (HMAC, audit or policy) */ tpm_buf_append_u8(&buf, TPM2_SE_HMAC); From 933654508b2bc36e2c3c086c0365e2403fb2141f Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 3 Jun 2026 09:36:52 +0800 Subject: [PATCH 1906/3297] sunrpc: fix uninitialized xprt_create_args structure commit 17d90b68c3a3d7d7e95b49e1fe9381a723f637a8 upstream. The xprt_create_args structure is allocated on the stack without initialization in rpc_sysfs_xprt_switch_add_xprt_store(). While some fields are manually populated, critical fields like srcaddr, bc_xps, and flags contain uninitialized stack garbage. This can lead to: 1. Kernel panic when xs_setup_xprt() dereferences garbage srcaddr 2. Information leak if srcaddr points to sensitive stack data 3. Unpredictable behavior if flags has random bits set The fix is to zero-initialize the structure to ensure all unused fields are NULL/0, preventing the transport setup code from acting on garbage data. Cc: stable@vger.kernel.org Suggested-by: Jeff Layton Reviewed-by: Jeff Layton Signed-off-by: Hongling Zeng Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index fa9892eec1b44..9dcd5e985e72a 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -326,7 +326,7 @@ static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj, { struct rpc_xprt_switch *xprt_switch = rpc_sysfs_xprt_switch_kobj_get_xprt(kobj); - struct xprt_create xprt_create_args; + struct xprt_create xprt_create_args = {}; struct rpc_xprt *xprt, *new; if (!xprt_switch) From 7926c1e4be86379945fb5f168888ac4d2aaf6c91 Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Wed, 22 Apr 2026 12:11:34 +0530 Subject: [PATCH 1907/3297] dmaengine: tegra: Fix burst size calculation commit 4651df83b6c796daead3447e8fd874322918ee4f upstream. Currently, the Tegra GPC DMA hardware requires the transfer length to be a multiple of the max burst size configured for the channel. When a client requests a transfer where the length is not evenly divisible by the configured max burst size, the DMA hangs with partial burst at the end. Fix this by reducing the burst size to the largest power-of-2 value that evenly divides the transfer length. For example, a 40-byte transfer with a 16-byte max burst will now use an 8-byte burst (40 / 8 = 5 complete bursts) instead of causing a hang. This issue was observed with the PL011 UART driver where TX DMA transfers of arbitrary lengths were stuck. Fixes: ee17028009d4 ("dmaengine: tegra: Add tegra gpcdma driver") Cc: stable@vger.kernel.org Signed-off-by: Kartik Rajput Reviewed-by: Frank Li Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260422064134.1323610-1-kkartik@nvidia.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/tegra186-gpc-dma.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 4d6fe0efa76e4..7df0a745e7b86 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -825,6 +825,13 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc, * len to calculate the optimum burst size */ burst_byte = burst_size ? burst_size * slave_bw : len; + + /* + * Find the largest burst size that evenly divides the transfer length. + * The hardware requires the transfer length to be a multiple of the + * burst size - partial bursts are not supported. + */ + burst_byte = min(burst_byte, 1U << __ffs(len)); burst_mmio_width = burst_byte / 4; if (burst_mmio_width < TEGRA_GPCDMA_MMIOSEQ_BURST_MIN) From ddbc4a8a4fe296f1fa2e59f7d176fc7c773df640 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 21 May 2026 23:21:53 +0900 Subject: [PATCH 1908/3297] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK commit 8ffba0171c6bbce5f093c6dba5a02c0805b31203 upstream. The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA channels, and modifying them requires a read-modify-write sequence. Because this operation is not atomic, concurrent calls to dw_edma_v0_core_start() can introduce race conditions if two channels update these registers simultaneously. Add a spinlock to serialize access to these registers and prevent race conditions. Fixes: 7e4b8a4fbe2c ("dmaengine: Add Synopsys eDMA IP version 0 support") Cc: stable@vger.kernel.org Signed-off-by: Frank Li [den: update dw_edma.lock comment] Link: https://lore.kernel.org/dmaengine/20260109-edma_ll-v2-1-5c0b27b2c664@nxp.com/ Signed-off-by: Koichiro Den Link: https://patch.msgid.link/20260521142153.2957432-5-den@valinux.co.jp Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/dw-edma/dw-edma-core.h | 2 +- drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 71894b9e0b153..cc783e076671a 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -108,7 +108,7 @@ struct dw_edma { struct dw_edma_chan *chan; - raw_spinlock_t lock; /* Only for legacy */ + raw_spinlock_t lock; /* Protect v0 shared registers */ struct dw_edma_chip *chip; diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index b75fdaffad9a4..2850a9df80f54 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; + unsigned long flags; u32 tmp; dw_edma_v0_core_write_chunk(chunk); @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) } } /* Interrupt unmask - done, abort */ + raw_spin_lock_irqsave(&dw->lock, flags); + tmp = GET_RW_32(dw, chan->dir, int_mask); tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)); @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) tmp = GET_RW_32(dw, chan->dir, linked_list_err_en); tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id)); SET_RW_32(dw, chan->dir, linked_list_err_en, tmp); + + raw_spin_unlock_irqrestore(&dw->lock, flags); + /* Channel control */ SET_CH_32(dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); From 1e41ca4a7fba2e680d6950e9511245255fffa46c Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 9 Jun 2026 16:14:19 +0800 Subject: [PATCH 1909/3297] platform/x86: dell-laptop: fix missing cleanups in init error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6e9cab2247e5b243ae2d907ce7c948a8a9c8d61a upstream. dell_init() initializes several resources after dell_setup_rfkill(), including the optional touchpad LED, keyboard backlight LED, battery hook, debugfs directory and dell-laptop notifier. If a later LED or backlight registration fails, the error path only tears down the battery hook and rfkill resources. This leaves the notifier, debugfs directory, keyboard backlight LED and optional touchpad LED registered after dell_init() returns an error. Add the missing cleanup calls before tearing down rfkill. Fixes: 9c656b07997f ("platform/x86: dell-*: Call new led hw_changed API on kbd brightness change") Fixes: 037accfa14b2 ("dell-laptop: Add debugfs support") Fixes: 2d8b90be4f1c ("dell-laptop: support Synaptics/Alps touchpad led") Fixes: 6cff8d60aa0a ("platform: x86: dell-laptop: Add support for keyboard backlight") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260609081419.1995169-1-lihaoxiang@isrc.iscas.ac.cn Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/dell/dell-laptop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 57748c3ea24fc..053f40572bf6e 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -2551,7 +2551,12 @@ static int __init dell_init(void) if (mute_led_registered) led_classdev_unregister(&mute_led_cdev); fail_led: + dell_laptop_unregister_notifier(&dell_laptop_notifier); + debugfs_remove_recursive(dell_laptop_dir); dell_battery_exit(); + kbd_led_exit(); + if (quirks && quirks->touchpad_led) + touchpad_led_exit(); dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); From cea03d67db3a880893360bdbb1b0b8d9b33b1799 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 28 May 2026 13:45:21 -0700 Subject: [PATCH 1910/3297] platform/x86: ISST: Restore SST-PP control to all domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2565a28cdcdcb035e151d285efcba26bccb3726e upstream. The SST-PP control offset is only restored to power domain 0 after resume. During suspend, control values are read and stored for all power domains. Use pd_info->sst_base instead of power_domain_info->sst_base, which only points to power domain 0 base address. Fixes: dc7901b5a156 ("platform/x86: ISST: Store and restore all domains data") Reported-by: Yi Lai Signed-off-by: Srinivas Pandruvada Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260528204521.3531456-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index c8de8688daedc..b11d62c934b37 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1786,7 +1786,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev) memcpy_toio(cp_base + SST_CLOS_ASSOC_0_OFFSET, pd_info->saved_clos_assocs, sizeof(pd_info->saved_clos_assocs)); - writeq(pd_info->saved_pp_control, power_domain_info->sst_base + + writeq(pd_info->saved_pp_control, pd_info->sst_base + pd_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET); } } From d8bc45c4c1a45fd22bf547adfcfc0d128bebed60 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:23 +0200 Subject: [PATCH 1911/3297] platform/x86/amd/pmc: Check for intermediate wakeup in function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3bdd6fc11fbfa8249483f4b716ead51e43e3a0cd upstream. Refactor code introduced by commit 9f5595d5f03f ("pmc: Require at least 2.5 seconds between HW sleep cycles") to allow adding different conditions for that delay in an upcoming change. Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-2-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index cae3fcafd4d7b..2b9e5730170a5 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -598,6 +598,19 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) return rc; } +static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) +{ + /* + * Starting a new HW sleep cycle right after waking from one + * can cause electrical problems triggering the over voltage protection. + * That is avoided by delaying the next suspend a bit, see also + * https://lore.kernel.org/all/20250414162446.3853194-1-superm1@kernel.org/ + */ + struct smu_metrics table; + + return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -632,11 +645,9 @@ static void amd_pmc_s2idle_prepare(void) static void amd_pmc_s2idle_check(void) { struct amd_pmc_dev *pdev = &pmc; - struct smu_metrics table; int rc; - /* Avoid triggering OVP */ - if (!get_metrics_table(pdev, &table) && table.s0i3_last_entry_status) + if (amd_pmc_intermediate_wakeup_need_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ From 27d16a19ae74f547ea99bdaa90fd44957845a296 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:24 +0200 Subject: [PATCH 1912/3297] platform/x86/amd/pmc: Delay suspend for some Lenovo Laptops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9b9e60dd31da054a37d601e9fcabdfd8a2bff354 upstream. Some IdeaPad Slim 3 devices and similar with AMD CPUs have a nonfunctional keyboard and lid switch after s2idle. It helps to delay suspend by 2.5 seconds so the EC has some time to do whatever it needs to get done before suspend - unfortunately at least on my 16ABR8 waking it with a timer (wakealarm) still triggers the issue, but at least normal resume via keypress or lid works fine. On the 14ARP10 wakealarm has been reported to also work fine with this patch. This issue has been reported for many different devices, this patch has been tested with the Zen3-based IdeaPad Slim 3 16ABR8 (82XR) and the Zen3+-based IdeaPad Slim 3 14ARP10 (83K6) and IdeaPad Slim 3 15ARP10 (83MM). Reported-by: Sindre Henriksen Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Tested-by: Sindre Henriksen Suggested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-3-daniel@gibson.sh Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc-quirks.c | 39 +++++++++++++++++++++++ drivers/platform/x86/amd/pmc/pmc.c | 24 +++++++++++++- drivers/platform/x86/amd/pmc/pmc.h | 1 + 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c index 24506e3429430..74ddf1d8289a9 100644 --- a/drivers/platform/x86/amd/pmc/pmc-quirks.c +++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c @@ -18,6 +18,7 @@ struct quirk_entry { u32 s2idle_bug_mmio; bool spurious_8042; + bool need_suspend_delay; }; static struct quirk_entry quirk_s2idle_bug = { @@ -33,6 +34,10 @@ static struct quirk_entry quirk_s2idle_spurious_8042 = { .spurious_8042 = true, }; +static struct quirk_entry quirk_s2idle_need_suspend_delay = { + .need_suspend_delay = true, +}; + static const struct dmi_system_id fwbug_list[] = { { .ident = "L14 Gen2 AMD", @@ -203,6 +208,35 @@ static const struct dmi_system_id fwbug_list[] = { DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), } }, + /* https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ + { + .ident = "Zen3-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + /* + * Note: there are also some Zen2-based 82X* devices that + * need different quirks, they're already handled above + */ + DMI_MATCH(DMI_PRODUCT_NAME, "82X"), + } + }, + { + .ident = "Zen3+-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83K"), + } + }, + { + .ident = "IdeaPad Slim 3 15ARP10 (83MM)", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83MM"), + } + }, /* https://bugzilla.kernel.org/show_bug.cgi?id=221273 */ { .ident = "Thinkpad L14 Gen3", @@ -356,6 +390,11 @@ void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev) amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio); } +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev) +{ + return dev->quirks && dev->quirks->need_suspend_delay; +} + void amd_pmc_quirks_init(struct amd_pmc_dev *dev) { const struct dmi_system_id *dmi_id; diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 2b9e5730170a5..6bafd8661d686 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -611,6 +611,27 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; } +static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) +{ + /* + * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some + * me-time before sleeping or they get uncooperative after waking + * up and don't send events for keyboard and lid switch anymore. + * + * Unfortunately this doesn't entirely fix the problem: It can still + * happen when resuming with a timer (wakealarm), but at least the + * more common usecases (wakeup by opening lid or pressing a key) + * work fine with this workaround. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 + */ + if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + return false; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -647,7 +668,8 @@ static void amd_pmc_s2idle_check(void) struct amd_pmc_dev *pdev = &pmc; int rc; - if (amd_pmc_intermediate_wakeup_need_delay(pdev)) + if (amd_pmc_intermediate_wakeup_need_delay(pdev) || + amd_pmc_want_suspend_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index fe3f53eb59558..f5257e47b8c47 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -147,6 +147,7 @@ enum amd_pmc_def { }; void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev); +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev); void amd_pmc_quirks_init(struct amd_pmc_dev *dev); void amd_mp2_stb_init(struct amd_pmc_dev *dev); void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); From e628d9169f9e110d5c4fc696c83c1958ce73b519 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:25 +0200 Subject: [PATCH 1913/3297] platform/x86/amd/pmc: Add delay_suspend module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 428b9fd2dce50b4dc5cd9ade10b92efcf57ce7aa upstream. Enabling the new delay_suspend module parameter delays suspend for 2.5 seconds which is known to help for some AMD-based Lenovo Laptops that otherwise failed to send/receive events for key presses or the lid switch after s2idle. Apparently the EC needs to do some things in the background before suspend or it gets into a bad state. There are many reports of AMD-based laptops (mostly but not exclusively IdeaPads) about similar issues on the web; this parameter gives affected users an easy way to try out if their issues have the same root cause and to work around them until their specific device is added to the quirks list. The parameter description has a note encouraging users to report their device so it can be added to the quirks list, inspired by a similar request in parameter descriptions of the ideapad-laptop module. The module parameter can be set to "1" to explicitly enable it, "0" to disable it even on devices that are assumed to be affected, or -1 (the default) to enable it if the device is assumed to be affected (according to fwbug_list[]) Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-4-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 6bafd8661d686..2d3d180c15d23 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,11 @@ static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); +static int delay_suspend = -1; +module_param(delay_suspend, int, 0644); +MODULE_PARM_DESC(delay_suspend, + "Delays s2idle by 2.5 seconds to work around buggy ECs, often causing keyboard issues after suspend. 0: don't delay, 1: do delay, -1 (default): let amd_pmc decide. If you need this please report this to: platform-driver-x86@vger.kernel.org"); + static struct amd_pmc_dev pmc; static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) @@ -625,8 +631,23 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ - if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (amd_pmc_quirk_need_suspend_delay(pdev)) { + /* + * delay_suspend=1 force-enables this, otherwise it can be + * disabled with disable_workarounds or delay_suspend=0 + */ + if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + } else if (delay_suspend == 1) { + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; From eafc5aca7156465a901cb7900e6ececc190c07f3 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:26 +0200 Subject: [PATCH 1914/3297] platform/x86/amd/pmc: Don't log during intermediate wakeups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 037f0b03c663a247366673a807834389107995b7 upstream. The ECs in the IdeaPads that need the delay_suspend quirk send lots of messages when charging, which not only causes intermediate wakeups when suspended, but also prevents the device from reaching the deepest suspend state. Because of this amd_pmc_intermediate_wakeup_need_delay() returns false during intermediate wakeups and amd_pmc_want_suspend_delay() is called. So far it always logged its "Delaying suspend by 2.5s ..." messages then, which spams dmesg. This commit makes sure that those messages are only logged once per suspend. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-5-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 39 ++++++++++++++++++++++++------ drivers/platform/x86/amd/pmc/pmc.h | 1 + 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 2d3d180c15d23..7d772ccd17a67 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -619,6 +619,20 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) { + /* + * intermediate_wakeup implies that the machine didn't get to deepest sleep + * state before - otherwise this function isn't called in amd_pmc_s2idle_check() + * because amd_pmc_intermediate_wakeup_need_delay() returns true first. + * On some IdeaPads that happens when charging, because the EC seems + * to send lots of messages then that wake the machine. + * + * But even in that case, the sleep here is necessary (on those IdeaPads), + * otherwise they wake up completely (resume) after a few seconds. + * So this variable is only used to avoid spamming dmesg on each + * intermediate wakeup. + */ + bool intermediate_wakeup = !pdev->is_first_check_after_suspend; + /* * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some * me-time before sleeping or they get uncooperative after waking @@ -637,17 +651,20 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * disabled with disable_workarounds or delay_suspend=0 */ if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); return true; } - dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); } else if (delay_suspend == 1) { - dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", - dmi_get_system_info(DMI_SYS_VENDOR), - dmi_get_system_info(DMI_PRODUCT_NAME), - dmi_get_system_info(DMI_PRODUCT_FAMILY), - dmi_get_system_info(DMI_BOARD_VENDOR), - dmi_get_system_info(DMI_BOARD_NAME)); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; @@ -660,6 +677,9 @@ static void amd_pmc_s2idle_prepare(void) u8 msg; u32 arg = 1; + /* Reset this variable because this is a fresh suspend */ + pdev->is_first_check_after_suspend = true; + /* Reset and Start SMU logging - to monitor the s0i3 stats */ amd_pmc_setup_smu_logging(pdev); @@ -699,6 +719,9 @@ static void amd_pmc_s2idle_check(void) rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); + + /* remember that first check after suspend is done (until next prepare) */ + pdev->is_first_check_after_suspend = false; } static int amd_pmc_dump_data(struct amd_pmc_dev *pdev) diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index f5257e47b8c47..8aa7073ed09f2 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -114,6 +114,7 @@ struct amd_pmc_dev { struct dentry *dbgfs_dir; struct quirk_entry *quirks; bool disable_8042_wakeup; + bool is_first_check_after_suspend; struct amd_mp2_dev *mp2; struct stb_arg stb_arg; }; From fe623f9515bb07bd88c15ad4ca34cfa03682f8e3 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 23 Jun 2026 16:20:31 +0200 Subject: [PATCH 1915/3297] pkey: Move keytype check from pkey api to handler commit 754e9e49b76fd5be339172aa98544182ed3ca75e upstream. The PKEY_VERIFYPROTK ioctl takes data from user-space and verifies the contained protected key. While checking the integrity of the ioctl request structure is the responsibility of the generic pkey_api code, the verification of the contained protected key is the responsibility of the pkey handler. The keytype verification (based on the calculated bitsize of the key) is part of the protected key verification and therefore the responsibility of the pkey handler (which already verifies it). Therefore the keytype verification is removed from the generic pkey_api code. As the calculation of the key bitsize is currently wrong, the removal of the keytype check in pkey_api also removes this wrong calculation. For this reason, the commit is flagged with the Fixes: tag. Cc: stable@kernel.org # 6.12+ Fixes: 8fcc231ce3be ("s390/pkey: Introduce pkey base with handler registry and handler modules") Reviewed-by: Ingo Franzki Reviewed-by: Harald Freudenberger Signed-off-by: Holger Dengler Signed-off-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/pkey_api.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index 162109f1e3dfc..79bd71ceba8e3 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -328,7 +328,6 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) { struct pkey_verifyprotk kvp; struct protaeskeytoken *t; - u32 keytype; u8 *tmpbuf; int rc; @@ -342,14 +341,6 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) return -EINVAL; } - keytype = pkey_aes_bitsize_to_keytype(8 * kvp.protkey.len); - if (!keytype) { - PKEY_DBF_ERR("%s unknown/unsupported protkey length %u\n", - __func__, kvp.protkey.len); - memzero_explicit(&kvp, sizeof(kvp)); - return -EINVAL; - } - /* build a 'protected key token' from the raw protected key */ tmpbuf = kzalloc(sizeof(*t), GFP_KERNEL); if (!tmpbuf) { @@ -359,7 +350,7 @@ static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp) t = (struct protaeskeytoken *)tmpbuf; t->type = TOKTYPE_NON_CCA; t->version = TOKVER_PROTECTED_KEY; - t->keytype = keytype; + t->keytype = kvp.protkey.type; t->len = kvp.protkey.len; memcpy(t->protkey, kvp.protkey.protkey, kvp.protkey.len); From 6cc15183573691761ea0c91447a5952ef9b4ae9e Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Sat, 11 Jul 2026 19:54:02 -0700 Subject: [PATCH 1916/3297] smb: client: use kvzalloc() for megabyte buffer in simple fallocate commit 806c00c23e3ce8eae397a40ced536ef88ae4e012 upstream. Currently in smb3_simple_fallocate_range(), a 1 MB buffer is allocated using kzalloc(). Under heavy memory fragmentation, a contiguous 1 MB block of physical memory (an order-8 allocation) may not be available, causing the allocation to fail. This failure was observed during xfstests generic/013 on a 4GB RAM test machine running fsstress: fsstress: page allocation failure: order:8, mode:0x40dc0(GFP_KERNEL|__GFP_ZERO|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0 Call Trace: dump_stack_lvl+0x5d/0x80 warn_alloc+0x163/0x190 __alloc_pages_slowpath.constprop.0+0x71b/0x12f0 __alloc_frozen_pages_noprof+0x2f6/0x340 alloc_pages_mpol+0xb6/0x170 ___kmalloc_large_node+0xb3/0xd0 __kmalloc_large_noprof+0x1e/0xc0 smb3_simple_falloc.isra.0+0x62b/0x960 cifs_fallocate+0xed/0x180 vfs_fallocate+0x165/0x3c0 __x64_sys_fallocate+0x48/0xa0 do_syscall_64+0xe1/0x640 entry_SYSCALL_64_after_hwframe+0x76/0x7e Node 0 Normal: 3375*4kB ... 7*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB Since this scratch buffer does not require physically contiguous memory, switch the allocation to kvzalloc(). This retains the performance benefits of kmalloc() under normal conditions, while gracefully falling back to virtually contiguous memory when physical allocation fails. Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation") Cc: stable@vger.kernel.org Signed-off-by: Fredric Cover Tested-by: Fredric Cover 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 d4095710948e4..786a7e963f1ff 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3560,7 +3560,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, if (rc) goto out; - buf = kzalloc(1024 * 1024, GFP_KERNEL); + buf = kvzalloc(1024 * 1024, GFP_KERNEL); if (buf == NULL) { rc = -ENOMEM; goto out; @@ -3617,7 +3617,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, out: kfree(out_data); - kfree(buf); + kvfree(buf); return rc; } From 3cd6f93f3d593838d4aa17a55379291c2250dc28 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Thu, 9 Jul 2026 17:05:30 +0200 Subject: [PATCH 1917/3297] ksmbd: fix integer overflow in set_file_allocation_info() commit 1c0ae3df692ea2a4ce992f786346154e75a3f0d5 upstream. set_file_allocation_info() converts the client-supplied FILE_ALLOCATION_INFORMATION::AllocationSize into a 512-byte block count with: alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; AllocationSize is a fully client-controlled __le64 field; the only validation performed by the caller (smb2_set_info_file(), case FILE_ALLOCATION_INFORMATION) is that the fixed buffer is at least sizeof(struct smb2_file_alloc_info) == 8 bytes. The value itself is never range-checked before this arithmetic. When AllocationSize is close to U64_MAX (e.g. 0xffffffffffffffff), "AllocationSize + 511" wraps around mod 2^64 to a small number (0xffffffffffffffff + 511 = 510), so alloc_blks becomes 0. Since any existing regular file has stat.blocks > 0, the function then takes the "shrink" branch and calls: ksmbd_vfs_truncate(work, fp, alloc_blks * 512); /* == 0 */ silently truncating the file to size 0, even though the client asked to grow the allocation to (what looks like) the maximum possible size. The trailing "if (size < alloc_blks * 512) i_size_write(inode, size);" restore is guarded by a comparison that is never true once alloc_blks == 0, so the truncation is not undone. This lets an authenticated SMB client that already holds an open handle with FILE_WRITE_DATA on a file silently truncate that same file to size 0 via a single crafted SET_INFO(FILE_ALLOCATION_INFORMATION) request advertising a near-U64_MAX AllocationSize, even though the request asks to grow the file's allocation rather than shrink it. This is a functional/data-loss bug, not a privilege-boundary violation: the same client could already truncate the file via FILE_END_OF_FILE_INFORMATION or a plain write. Fix it by validating AllocationSize against MAX_LFS_FILESIZE, the same upper bound the VFS itself uses to reject unrepresentable file sizes, before doing the "+511" rounding, and rejecting oversized values with -EINVAL. Bounding AllocationSize to MAX_LFS_FILESIZE - 511 guarantees the "+511" addition cannot wrap, and that the subsequent "alloc_blks * 512" values passed to vfs_fallocate() and ksmbd_vfs_truncate() stay within a representable loff_t as well. No legitimate SMB client asks for an allocation size anywhere near 2^64 bytes, so this only rejects a value that was previously silently misinterpreted as zero. Runtime-verified on a v6.19 KASAN test stand: sending SET_INFO (FILE_ALLOCATION_INFORMATION) with AllocationSize = 0xffffffffffffffff against ksmbd now returns -EINVAL and leaves the target file's size unchanged, where the unpatched kernel truncated it from 4096 to 0 bytes. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Assisted-by: AuditCode-AI:2026.07 Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c49ae51628649..9644b42404ae7 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6347,6 +6347,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, */ loff_t alloc_blks; + u64 alloc_size; struct inode *inode; struct kstat stat; int rc; @@ -6362,7 +6363,19 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (rc) return rc; - alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + /* + * AllocationSize is fully client-controlled (the caller only + * validates the fixed 8-byte buffer length). Reject values that + * would overflow the "round up to 512-byte blocks" conversion + * below instead of silently wrapping it to a tiny block count, + * which would truncate the file to a size the client never + * asked for. + */ + alloc_size = le64_to_cpu(file_alloc_info->AllocationSize); + if (alloc_size > MAX_LFS_FILESIZE - 511) + return -EINVAL; + + alloc_blks = (alloc_size + 511) >> 9; inode = file_inode(fp->filp); if (alloc_blks > stat.blocks) { From 1dcd7565e5909d87b604e8fe8565c9a6d9b01f42 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 29 Jun 2026 21:17:40 +0200 Subject: [PATCH 1918/3297] hwmon: (ltc2992) add missing 'select REGMAP_I2C' to Kconfig commit a35a6f1b20100057c66b7be5a8f6864661c3945c upstream. The Kconfig entry for the LTC2992 sensor doesn't contain a `select REGMAP_I2C` parameter, causing build failures if regmap isn't selected previously during the build process. Fixes: b0bd407e94b0 ("hwmon: (ltc2992) Add support") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Link: https://lore.kernel.org/r/20260629-add-kconfig-deps-v1-2-8104df929b1a@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 2a71b6e834b08..7c37d6e3dc157 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1086,6 +1086,7 @@ config SENSORS_LTC2992 tristate "Linear Technology LTC2992" depends on I2C depends on GPIOLIB + select REGMAP_I2C help If you say yes here you get support for Linear Technology LTC2992 I2C System Monitor. The LTC2992 measures current, voltage, and From 500716a007b2e5c081600bf84978fe5e951c61e1 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 29 Jun 2026 21:17:41 +0200 Subject: [PATCH 1919/3297] hwmon: (max6697) add missing 'select REGMAP_I2C' to Kconfig commit ed576f2f4eef8cbe2c110da503825a8dc4717030 upstream. The Kconfig entry for the MAX6697 sensor doesn't contain a `select REGMAP_I2C` parameter, causing build failures if regmap isn't selected previously during the build process. Fixes: 3a2a8cc3fe24 ("hwmon: (max6697) Convert to use regmap") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Link: https://lore.kernel.org/r/20260629-add-kconfig-deps-v1-3-8104df929b1a@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 7c37d6e3dc157..04808d244c924 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1331,6 +1331,7 @@ config SENSORS_MAX6650 config SENSORS_MAX6697 tristate "Maxim MAX6697 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for MAX6581, MAX6602, MAX6622, MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 From 6d2c973926d0612360693bc559be2ffde836151b Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Mon, 13 Jul 2026 20:11:59 +0200 Subject: [PATCH 1920/3297] i2c: imx: fix locked bus on SMBus block-read of 0 (atomic) commit cb2fc37857693b55909fb77dc2c87cfbc1cdc476 upstream. SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the atomic (polling) path rejects it as -EPROTO. Worse, it returns without a NACK+STOP: the next receive cycle has already started, so the target keeps holding SDA and the bus stays stuck until a power cycle for this i2c controller. Reading I2DR to obtain the count likewise arms the next byte on the count > I2C_SMBUS_BLOCK_MAX path, which also returned -EPROTO directly and left the bus held. Handle both: NACK the in-flight dummy byte (TXAK) and extend msgs->len so the existing last-byte handling emits STOP; the dummy byte is discarded. A count of 0 is a valid empty block read; a count above I2C_SMBUS_BLOCK_MAX is still reported as -EPROTO, but only after the bus has been released. The interrupt-driven path has the same flaw from a later commit and is fixed separately, as it carries a different Fixes: tag and stable range. Fixes: 8e8782c71595 ("i2c: imx: add SMBus block read support") Signed-off-by: Vincent Jardin Cc: # v3.16+ Acked-by: Oleksij Rempel Acked-by: Carlos Song Reviewed-by: Stefan Eichenberger Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-1-073ac9e103a5@free.fr Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 28313d0fad37e..cfd1e63359e75 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1415,6 +1415,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, int i, result; unsigned int temp; int block_data = msgs->flags & I2C_M_RECV_LEN; + int block_err = 0; result = i2c_imx_prepare_read(i2c_imx, msgs, false); if (result) @@ -1436,8 +1437,20 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, */ if ((!i) && block_data) { len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); - if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) - return -EPROTO; + if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + */ + if (len > I2C_SMBUS_BLOCK_MAX) + block_err = -EPROTO; + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + msgs->buf[0] = 0; + msgs->len = 2; + continue; + } dev_dbg(&i2c_imx->adapter.dev, "<%s> read length: 0x%X\n", __func__, len); @@ -1485,7 +1498,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, "<%s> read byte: B%d=0x%X\n", __func__, i, msgs->buf[i]); } - return 0; + return block_err; } static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, From 5d3240f42a667e29262aff76fdebfcdac4980626 Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Mon, 13 Jul 2026 20:12:00 +0200 Subject: [PATCH 1921/3297] i2c: imx: fix locked bus on SMBus block-read of 0 (IRQ) commit 07fd9385f0d87dff4b34f355f68adf701080cb24 upstream. SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the interrupt-driven block-read state machine rejects it as -EPROTO. Worse, it returns without a NACK+STOP: the next receive cycle has already started, so the target keeps holding SDA and the bus stays stuck until a power cycle of this i2c controller. Accept count=0: NACK the in-flight dummy byte (TXAK) and set msg->len to 2 so i2c_imx_isr_read_continue() emits STOP via its normal last-byte path. The dummy byte is discarded; block-read callers only consume buf[0..count-1]. Reading I2DR has likewise already armed the next byte on the count > I2C_SMBUS_BLOCK_MAX error path, so NACK it (TXAK) before aborting with -EPROTO; otherwise the failing transfer's STOP cannot complete and the bus stays held. The atomic path regressed earlier (v3.16) and is fixed separately; this patch covers only the v6.13 state-machine rework. Fixes: 5f5c2d4579ca ("i2c: imx: prevent rescheduling in non dma mode") Signed-off-by: Vincent Jardin Cc: # v6.13+ Acked-by: Oleksij Rempel Acked-by: Carlos Song Reviewed-by: Stefan Eichenberger Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-2-073ac9e103a5@free.fr Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index cfd1e63359e75..d5e6e2eca3b34 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1061,11 +1061,28 @@ static inline enum imx_i2c_state i2c_imx_isr_read_continue(struct imx_i2c_struct static inline void i2c_imx_isr_read_block_data_len(struct imx_i2c_struct *i2c_imx) { u8 len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + unsigned int temp; if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + * So NACK it (TXAK) to not hold the bus. + */ + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + if (len == 0) { + i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = 0; + i2c_imx->msg->len = 2; + return; + } + i2c_imx->isr_result = -EPROTO; i2c_imx->state = IMX_I2C_STATE_FAILED; wake_up(&i2c_imx->queue); + return; } i2c_imx->msg->len += len; i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = len; From 2f3f471a448a5be40e35d55d0eb26775bc762b86 Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Thu, 9 Jul 2026 16:31:29 +0300 Subject: [PATCH 1922/3297] i2c: mediatek: fix WRRD for SoCs without auto_restart option commit deb35336b5bfed5db9231b5348bc1514db930797 upstream. MediaTek mt65xx family SoCs have no auto restart, however, they still support the WRRD mode in the hardware. Because auto_restart is set to 0, the WRRD mode will be never enabled, leading to read errors. Fix this by removing auto_restart check from the WRRD enable path. Fixes: b49218365280 ("i2c: mediatek: fix potential incorrect use of I2C_MASTER_WRRD") Signed-off-by: Roman Vivchar Cc: # v6.18+ Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260709-6572-6595-i2c-v2-1-b2fb8510d1d3@protonmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-mt65xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index aefdbee1f03c0..e30baf200720b 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -1250,7 +1250,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, i2c->auto_restart = i2c->dev_comp->auto_restart; /* checking if we can skip restart and optimize using WRRD mode */ - if (i2c->auto_restart && num == 2) { + if (num == 2) { if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) && msgs[0].addr == msgs[1].addr) { i2c->auto_restart = 0; From e6a395a71f4652261d09b572a03c96052662a056 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Tue, 14 Jul 2026 23:08:08 +0800 Subject: [PATCH 1923/3297] i2c: mlxbf: Fix use-after-free in mlxbf_i2c_init_resource() commit 71356737a7a55c76fee847563e3d33f8e6dc6b6d upstream. If devm_platform_get_and_ioremap_resource() returns an error, mlxbf_i2c_init_resource() frees tmp_res before reading tmp_res->io to get the error code. This results in a use-after-free. Save the error code before freeing tmp_res. Fixes: b5b5b32081cd ("i2c: mlxbf: I2C SMBus driver for Mellanox BlueField SoC") Signed-off-by: Xuanqiang Luo Cc: # v5.10+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260714150808.85045-1-xuanqiang.luo@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-mlxbf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 8345f7e6385df..1049a725e6e16 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -1058,8 +1058,10 @@ static int mlxbf_i2c_init_resource(struct platform_device *pdev, tmp_res->io = devm_platform_get_and_ioremap_resource(pdev, type, &tmp_res->params); if (IS_ERR(tmp_res->io)) { + int ret = PTR_ERR(tmp_res->io); + devm_kfree(dev, tmp_res); - return PTR_ERR(tmp_res->io); + return ret; } tmp_res->type = type; From 8bd84316bbaf357e790e32c6795d791cadff7ab1 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Fri, 10 Jul 2026 15:21:13 +0800 Subject: [PATCH 1924/3297] i2c: spacemit: fix spurious IRQ handling returning IRQ_HANDLED commit 9db20d23aac7916ff49be409a4bfd48fe7cbbfb4 upstream. When the interrupt status register reads zero, the handler should return IRQ_NONE instead of IRQ_HANDLED. What the return value actually feeds into is the spurious interrupt accounting in note_interrupt(): falsely claiming IRQ_HANDLED defeats the "irq XX: nobody cared" detection, so a stuck interrupt source would never be caught. Fixes: 5ea558473fa3 ("i2c: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Pei Xiao Cc: # v6.15+ Reviewed-by: Troy Mitchell Reviewed-by: Mukesh Savaliya Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/ef8b623f45d4e430721e46572c2598d882044aed.1783667875.git.xiaopei01@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 8ef6d5d1927b7..1c58c39b92795 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -425,7 +425,7 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) status = readl(i2c->base + SPACEMIT_ISR); if (!status) - return IRQ_HANDLED; + return IRQ_NONE; i2c->status = status; From bccae122dab8fbed24f9f650486b490c124bc6e8 Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Wed, 8 Apr 2026 16:11:05 +0200 Subject: [PATCH 1925/3297] ice: fix ice_init_link() error return preventing probe commit eb509638686b0f8a98a0dd9c809f6a8db4d73a45 upstream. ice_init_link() can return an error status from ice_update_link_info() or ice_init_phy_user_cfg(), causing probe to fail. An incorrect NVM update procedure can result in link/PHY errors, and the recommended resolution is to update the NVM using the correct procedure. If the driver fails probe due to link errors, the user cannot update the NVM to recover. The link/PHY errors logged are non-fatal: they are already annotated as 'not a fatal error if this fails'. Since none of the errors inside ice_init_link() should prevent probe from completing, convert it to void and remove the error check in the caller. All failures are already logged; callers have no meaningful recovery path for link init errors. Fixes: 5b246e533d01 ("ice: split probe into smaller functions") Cc: stable@vger.kernel.org Signed-off-by: Paul Greenwalt Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_main.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index dc5d821bf3348..b89041743f6fc 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -4871,16 +4871,14 @@ static void ice_init_wakeup(struct ice_pf *pf) device_set_wakeup_enable(ice_pf_to_dev(pf), false); } -static int ice_init_link(struct ice_pf *pf) +static void ice_init_link(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); int err; err = ice_init_link_events(pf->hw.port_info); - if (err) { + if (err) dev_err(dev, "ice_init_link_events failed: %d\n", err); - return err; - } /* not a fatal error if this fails */ err = ice_init_nvm_phy_type(pf->hw.port_info); @@ -4914,8 +4912,6 @@ static int ice_init_link(struct ice_pf *pf) } else { set_bit(ICE_FLAG_NO_MEDIA, pf->flags); } - - return err; } static int ice_init_pf_sw(struct ice_pf *pf) @@ -5058,13 +5054,11 @@ static int ice_init(struct ice_pf *pf) ice_init_wakeup(pf); - err = ice_init_link(pf); - if (err) - goto err_init_link; + ice_init_link(pf); err = ice_send_version(pf); if (err) - goto err_init_link; + goto err_deinit_pf_sw; ice_verify_cacheline_size(pf); @@ -5083,7 +5077,7 @@ static int ice_init(struct ice_pf *pf) return 0; -err_init_link: +err_deinit_pf_sw: ice_deinit_pf_sw(pf); err_init_pf_sw: ice_dealloc_vsis(pf); From 33a1bee413628378fd036a4f2b17ba86b0bc560c Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 25 Jun 2026 19:21:40 +0100 Subject: [PATCH 1926/3297] tcp: defer md5sig_info kfree past RCU grace period in tcp_connect commit b74cd55038905d5e74c1de109ab78a30b2ea0e1f upstream. The md5+ao reconciliation in tcp_connect() (net/ipv4/tcp_output.c) has two symmetric branches: if (needs_md5) { tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { tcp_clear_md5_list(sk); kfree(rcu_replace_pointer(tp->md5sig_info, NULL, ...)); } Both branches free a per-socket auth-info object while the socket is in TCP_SYN_SENT and is already on the inet ehash (inserted by inet_hash_connect() in tcp_v4_connect()). Both branches are reachable by softirq RX-path readers that load the corresponding info pointer via implicit RCU before bh_lock_sock_nested() is taken. The needs_md5 branch is fixed in the prior patch by re-introducing the call_rcu() free in tcp_ao_destroy_sock(): the equivalent per-key loop runs inside tcp_ao_info_free_rcu(), the RCU callback, so by the time it frees each tcp_ao_key all softirq readers that captured the container have already completed rcu_read_unlock(). The needs_ao branch is not symmetric in the same way. The container free can be deferred via kfree_rcu(md5sig, rcu) -- struct tcp_md5sig_info already has the required rcu member (include/net/tcp.h:1999-2002), and the rest of the tree already does this in the tcp_md5sig_info_add() rollback paths (net/ipv4/tcp_ipv4.c:1410, 1436). But the per-key teardown is done by tcp_clear_md5_list() in process context BEFORE the container's RCU grace period: it walks &md5sig->head and frees each tcp_md5sig_key with bare hlist_del + kfree. A concurrent softirq reader in __tcp_md5_do_lookup() / __tcp_md5_do_lookup_exact() (tcp_ipv4.c:1253, 1298) walks the same list via hlist_for_each_entry_rcu() and races with that bare kfree on the keys themselves -- a per-key slab use-after-free of the same class as the TCP-AO bug, on the same race window. Fix this in two halves: 1. Convert the bare kfree() in tcp_connect() to kfree_rcu() so the md5sig_info container joins the rest of the md5sig lifecycle. The local-variable lift is mechanical and required because kfree_rcu() is a macro that expects an lvalue. 2. Make tcp_clear_md5_list() RCU-safe by replacing hlist_del + kfree(key) with hlist_del_rcu + kfree_rcu(key, rcu). struct tcp_md5sig_key already carries the rcu member (include/net/tcp.h:1995) and tcp_md5_do_del() (net/ipv4/tcp_ipv4.c:1456) already uses kfree_rcu, so this restores the lifecycle invariant the rest of the file follows rather than introducing a one-off. The other caller of tcp_clear_md5_list() is tcp_md5_destruct_sock() (net/ipv4/tcp.c:412), which runs from the sock destructor when the socket is already unhashed and unreachable; the extra grace period there is unnecessary but harmless. Making the helper unconditionally RCU-safe is the cleaner contract. The needs_ao branch is not reachable by the userns reproducer used to demonstrate the AO-side splat (the repro installs both keys but ends up in the needs_md5 branch because the connect peer matches the MD5 key, not the AO key); however the symmetric race exists and a maintainer touching this code should not have to think about which branch escapes RCU and which one does not. Fixes: 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") Cc: stable@vger.kernel.org # v6.18+ Suggested-by: Eric Dumazet Signed-off-by: Michael Bommarito Reviewed-by: Dmitry Safonov Reviewed-by: Eric Dumazet [also credits to Qihang, who found that this races with tcp-diag] Reported-by: Qihang Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-2-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_ipv4.c | 4 ++-- net/ipv4/tcp_output.c | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 36206fc6aed2d..f0106c2be1cf2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1505,9 +1505,9 @@ void tcp_clear_md5_list(struct sock *sk) md5sig = rcu_dereference_protected(tp->md5sig_info, 1); hlist_for_each_entry_safe(key, n, &md5sig->head, node) { - hlist_del(&key->node); + hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); - kfree(key); + kfree_rcu(key, rcu); } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 318d548b606e5..11333797e0dbe 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4263,9 +4263,13 @@ int tcp_connect(struct sock *sk) if (needs_md5) { tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { + struct tcp_md5sig_info *md5sig; + tcp_clear_md5_list(sk); - kfree(rcu_replace_pointer(tp->md5sig_info, NULL, - lockdep_sock_is_held(sk))); + md5sig = rcu_replace_pointer(tp->md5sig_info, NULL, + lockdep_sock_is_held(sk)); + if (md5sig) + kfree_rcu(md5sig, rcu); } } #endif From 0ced34b4bbc046cf215a4172548e9a0e1832f00b Mon Sep 17 00:00:00 2001 From: Dmitry Safonov <0x7f454c46@gmail.com> Date: Thu, 25 Jun 2026 19:21:41 +0100 Subject: [PATCH 1927/3297] tcp: Decrement tcp_md5_needed static branch commit 6f6e860e370c9e4e919b92118a25e9e1f82e9180 upstream. In case of early freeing an unwanted TCP-MD5 key on TCP-AO connect(), md5sig_info is freed right away (and set to NULL). Later, at the moment of socket destruction, the static branch counter is not getting decremented. Add a missing decrement for TCP-MD5 static branch. Reported-by: Qihang Fixes: 0aadc73995d0 ("net/tcp: Prevent TCP-MD5 with TCP-AO being set") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-3-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 11333797e0dbe..479be2f242627 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4268,8 +4268,8 @@ int tcp_connect(struct sock *sk) tcp_clear_md5_list(sk); md5sig = rcu_replace_pointer(tp->md5sig_info, NULL, lockdep_sock_is_held(sk)); - if (md5sig) - kfree_rcu(md5sig, rcu); + kfree_rcu(md5sig, rcu); + static_branch_slow_dec_deferred(&tcp_md5_needed); } } #endif From e497fef9ad7e913f52de6f97e818f56915e96164 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 30 Jun 2026 18:54:12 -0400 Subject: [PATCH 1928/3297] ufs: core: tracing: Do not dereference pointers in TP_printk() commit 535fcf4b8a261fbb8cc4f91e4597343c135a90f2 upstream. The trace events in drivers/ufs/core/ufs_trace.h were converted to take a pointer to the hba structure as an argument for the tracepoint and then in TP_printk() the printing of the dev_name from the ring buffer was converted to using the dev dereferenced pointer from the hba saved pointer. This is not allowed as the TP_printk() is executed at the time the trace event is read from /sys/kernel/tracing/trace file. That can happen literally, seconds, minutes, hours, weeks, days, or even months later! There is no guarantee that the hba pointer will still exist by the time it is dereferenced when the "trace" file is read. Instead, save the device name from the hba pointer at the time the tracepoint is called and place it into the ring buffer event. Then the TP_printk() can read the name directly from the ring buffer and remove the possibility that it will read a freed pointer and crash the kernel. This was detected when testing the trace event code that looks for TP_printk() parameters doing illegal derferences[1] [1] https://lore.kernel.org/all/20260630184836.74d477b6@gandalf.local.home/ Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260630185412.283c26c5@gandalf.local.home Fixes: 583e518e71003 ("scsi: ufs: core: Add hba parameter to trace events") Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- drivers/ufs/core/ufs_trace.h | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/ufs/core/ufs_trace.h b/drivers/ufs/core/ufs_trace.h index 584c2b5c6ad99..5e887f87248dd 100644 --- a/drivers/ufs/core/ufs_trace.h +++ b/drivers/ufs/core/ufs_trace.h @@ -90,16 +90,18 @@ TRACE_EVENT(ufshcd_clk_gating, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, state) ), TP_fast_assign( + __assign_str(dev_name); __entry->hba = hba; __entry->state = state; ), TP_printk("%s: gating state changed to %s", - dev_name(__entry->hba->dev), + __get_str(dev_name), __print_symbolic(__entry->state, UFSCHD_CLK_GATING_STATES)) ); @@ -112,6 +114,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) __string(clk, clk) __field(u32, prev_state) @@ -120,6 +123,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); __assign_str(clk); __entry->prev_state = prev_state; @@ -127,7 +131,7 @@ TRACE_EVENT(ufshcd_clk_scaling, ), TP_printk("%s: %s %s from %u to %u Hz", - dev_name(__entry->hba->dev), __get_str(state), __get_str(clk), + __get_str(dev_name), __get_str(state), __get_str(clk), __entry->prev_state, __entry->curr_state) ); @@ -139,16 +143,18 @@ TRACE_EVENT(ufshcd_auto_bkops_state, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); ), TP_printk("%s: auto bkops - %s", - dev_name(__entry->hba->dev), __get_str(state)) + __get_str(dev_name), __get_str(state)) ); DECLARE_EVENT_CLASS(ufshcd_profiling_template, @@ -159,6 +165,7 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(profile_info, profile_info) __field(s64, time_us) __field(int, err) @@ -166,13 +173,14 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(profile_info); __entry->time_us = time_us; __entry->err = err; ), TP_printk("%s: %s: took %lld usecs, err %d", - dev_name(__entry->hba->dev), __get_str(profile_info), + __get_str(dev_name), __get_str(profile_info), __entry->time_us, __entry->err) ); @@ -201,6 +209,7 @@ DECLARE_EVENT_CLASS(ufshcd_template, __field(s64, usecs) __field(int, err) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, dev_state) __field(int, link_state) ), @@ -209,13 +218,14 @@ DECLARE_EVENT_CLASS(ufshcd_template, __entry->usecs = usecs; __entry->err = err; __entry->hba = hba; + __assign_str(dev_name); __entry->dev_state = dev_state; __entry->link_state = link_state; ), TP_printk( "%s: took %lld usecs, dev_state: %s, link_state: %s, err %d", - dev_name(__entry->hba->dev), + __get_str(dev_name), __entry->usecs, __print_symbolic(__entry->dev_state, UFS_PWR_MODES), __print_symbolic(__entry->link_state, UFS_LINK_STATES), @@ -280,6 +290,7 @@ TRACE_EVENT(ufshcd_command, TP_STRUCT__entry( __field(struct scsi_device *, sdev) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(&sdev->sdev_dev)) __field(enum ufs_trace_str_t, str_t) __field(unsigned int, tag) __field(u32, doorbell) @@ -292,6 +303,7 @@ TRACE_EVENT(ufshcd_command, ), TP_fast_assign( + __assign_str(dev_name); __entry->sdev = sdev; __entry->hba = hba; __entry->str_t = str_t; @@ -308,7 +320,7 @@ TRACE_EVENT(ufshcd_command, TP_printk( "%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x (%s), group_id: 0x%x, hwq_id: %d", show_ufs_cmd_trace_str(__entry->str_t), - dev_name(&__entry->sdev->sdev_dev), __entry->tag, + __get_str(dev_name), __entry->tag, __entry->doorbell, __entry->transfer_len, __entry->intr, __entry->lba, (u32)__entry->opcode, str_opcode(__entry->opcode), (u32)__entry->group_id, __entry->hwq_id @@ -323,6 +335,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __field(u32, cmd) __field(u32, arg1) @@ -332,6 +345,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; __entry->cmd = cmd; __entry->arg1 = arg1; @@ -341,7 +355,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_printk( "%s: %s: cmd: 0x%x, arg1: 0x%x, arg2: 0x%x, arg3: 0x%x", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __entry->cmd, __entry->arg1, __entry->arg2, __entry->arg3 ) ); @@ -354,6 +368,7 @@ TRACE_EVENT(ufshcd_upiu, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __array(unsigned char, hdr, 12) __array(unsigned char, tsf, 16) @@ -362,6 +377,7 @@ TRACE_EVENT(ufshcd_upiu, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; memcpy(__entry->hdr, hdr, sizeof(__entry->hdr)); memcpy(__entry->tsf, tsf, sizeof(__entry->tsf)); @@ -370,7 +386,7 @@ TRACE_EVENT(ufshcd_upiu, TP_printk( "%s: %s: HDR:%s, %s:%s", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __print_hex(__entry->hdr, sizeof(__entry->hdr)), show_ufs_cmd_trace_tsf(__entry->tsf_t), __print_hex(__entry->tsf, sizeof(__entry->tsf)) @@ -385,16 +401,18 @@ TRACE_EVENT(ufshcd_exception_event, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(u16, status) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->status = status; ), TP_printk("%s: status 0x%x", - dev_name(__entry->hba->dev), __entry->status + __get_str(dev_name), __entry->status ) ); From 16d3ccdabb8dee9be2cdcd6d3f9a125572ec0454 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Mon, 22 Jun 2026 19:25:41 +0800 Subject: [PATCH 1929/3297] xen/gntdev: fix error handling in ioctl commit 45ca1afe2fd14c04e37227e79d3f8455831d8408 upstream. When gntdev_ioctl_map_grant_ref() fails to copy the operation result back to userspace after successfully adding the mapping to the list, the error path returns -EFAULT without releasing the reference acquired by gntdev_alloc_map(). The mapping remains in priv->maps with a refcount of 1, causing a memory leak and a dangling list entry. Additionally, gntdev_add_map() may modify map->index to avoid overlap with existing mappings. Therefore, the index returned to userspace must be obtained after gntdev_add_map() completes. Fix this by holding the mutex across gntdev_add_map(), retrieving the correct index, and copy_to_user(). If copy_to_user() fails, remove the mapping from the list and release the reference while still holding the lock. Cc: stable@vger.kernel.org Fix these issues by properly handling all error cases. Fixes: 1401c00e59ea ("xen/gntdev: convert priv->lock to a mutex") Fixes: 68b025c813c2 ("xen-gntdev: Add reference counting to maps") Signed-off-by: Wentao Liang Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260622112541.38194-1-vulab@iscas.ac.cn> Signed-off-by: Greg Kroah-Hartman --- drivers/xen/gntdev.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 2c960f187f7c3..1e21b75114e5e 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -676,11 +676,15 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, mutex_lock(&priv->lock); gntdev_add_map(priv, map); op.index = map->index << PAGE_SHIFT; - mutex_unlock(&priv->lock); - if (copy_to_user(u, &op, sizeof(op)) != 0) + if (copy_to_user(u, &op, sizeof(op)) != 0) { + list_del(&map->next); + mutex_unlock(&priv->lock); + gntdev_put_map(priv, map); return -EFAULT; + } + mutex_unlock(&priv->lock); return 0; } From 5b0c4c916f202b8fd13d12afb6af62b385622f81 Mon Sep 17 00:00:00 2001 From: Qianyu Luo Date: Thu, 25 Jun 2026 13:55:08 +0800 Subject: [PATCH 1930/3297] xfrm: nat_keepalive: avoid double free on send error commit 226f4a490d1a938fc838d8f8c46a4eca864c0d78 upstream. nat_keepalive_send() frees the keepalive skb whenever the IPv4 or IPv6 send helper reports an error. That cleanup is only correct before the skb is handed to the output path. Once ip_build_and_send_pkt() or ip6_xmit() takes ownership, the networking stack may already have consumed the skb before returning an error, so freeing it again is unsafe. Handle the pre-handoff failure cases inside nat_keepalive_send_ipv4() and nat_keepalive_send_ipv6(), where the caller still owns the skb, and keep nat_keepalive_send() responsible only for family dispatch and the unsupported-family cleanup path. Fixes: f531d13bdfe3 ("xfrm: support sending NAT keepalives in ESP in UDP states") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Signed-off-by: Qianyu Luo Signed-off-by: Ren Wei Reviewed-by: Eyal Birger Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_nat_keepalive.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c index 1856beee0149b..f50b1f48f2ed9 100644 --- a/net/xfrm/xfrm_nat_keepalive.c +++ b/net/xfrm/xfrm_nat_keepalive.c @@ -55,8 +55,10 @@ static int nat_keepalive_send_ipv4(struct sk_buff *skb, ka->encap_sport, sock_net_uid(net, NULL)); rt = ip_route_output_key(net, &fl4); - if (IS_ERR(rt)) + if (IS_ERR(rt)) { + kfree_skb(skb); return PTR_ERR(rt); + } skb_dst_set(skb, &rt->dst); @@ -101,6 +103,7 @@ static int nat_keepalive_send_ipv6(struct sk_buff *skb, dst = ipv6_stub->ipv6_dst_lookup_flow(net, sk, &fl6, NULL); if (IS_ERR(dst)) { local_unlock_nested_bh(&nat_keepalive_sk_ipv6.bh_lock); + kfree_skb(skb); return PTR_ERR(dst); } @@ -118,7 +121,6 @@ static void nat_keepalive_send(struct nat_keepalive *ka) sizeof(struct ipv6hdr)) + sizeof(struct udphdr); const u8 nat_ka_payload = 0xFF; - int err = -EAFNOSUPPORT; struct sk_buff *skb; struct udphdr *uh; @@ -140,16 +142,17 @@ static void nat_keepalive_send(struct nat_keepalive *ka) switch (ka->family) { case AF_INET: - err = nat_keepalive_send_ipv4(skb, ka); + nat_keepalive_send_ipv4(skb, ka); break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - err = nat_keepalive_send_ipv6(skb, ka, uh); + nat_keepalive_send_ipv6(skb, ka, uh); break; #endif - } - if (err) + default: kfree_skb(skb); + break; + } } struct nat_keepalive_work_ctx { From e0f688ccb20f10fe16c4873ccf808eb3d1d5c96a Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Sun, 7 Jun 2026 16:47:34 +0000 Subject: [PATCH 1931/3297] xfrm: use compat translator only for u64 alignment mismatch commit 355fbcbdc2539cca7890b0d0914d4ce0f985ad74 upstream. The XFRM compat layer (CONFIG_XFRM_USER_COMPAT) translates 32-bit xfrm netlink and setsockopt messages into the native 64-bit layout. It is only needed on architectures where the 32-bit and 64-bit ABIs disagree on u64 alignment, which the kernel encodes as COMPAT_FOR_U64_ALIGNMENT. That symbol is defined only by arch/x86. XFRM_USER_COMPAT depends on it, so the translator can never be built on any other architecture, including arm64, which still provides a 32-bit compat ABI (CONFIG_COMPAT) for AArch32 EL0 userspace. On arm64 the AArch32 EABI already aligns u64 to 8 bytes, identical to the AArch64 ABI, so no translation is required and the native code path is correct for 32-bit tasks. However, xfrm_user_rcv_msg() and xfrm_user_policy() gate on in_compat_syscall() alone and then call xfrm_get_translator(), which returns NULL when no translator is registered. On arm64 that is always the case, so every xfrm netlink message and the XFRM_POLICY setsockopt issued by a 32-bit task returns -EOPNOTSUPP. A 32-bit userspace process on arm64 (and on any other arch with CONFIG_COMPAT but without COMPAT_FOR_U64_ALIGNMENT) therefore cannot configure XFRM state or policy through the XFRM_USER netlink API, and cannot use the XFRM_POLICY setsockopt path, because both fail before reaching the native parser. The translator series replaced the blanket compat rejection with a translator lookup. That made the path usable on x86 when the translator is available, but left architectures that cannot build the translator permanently rejected even when their compat layout already matches the native layout. Let those architectures use the native parser instead. Gate the translator requirement on COMPAT_FOR_U64_ALIGNMENT instead of on in_compat_syscall() alone. Gating on the ABI property rather than on CONFIG_XFRM_USER_COMPAT is deliberate: on x86 with IA32_EMULATION=y but XFRM_USER_COMPAT=n, a 32-bit task must still be rejected rather than routed through the native parser, which would misread genuinely 4-byte-aligned x86-32 messages. COMPAT_FOR_U64_ALIGNMENT is the ABI property that makes the XFRM translator mandatory. Only the receive/input direction needs the guard. The send, dump and notification paths already call the translator as "if (xtr) { ... }" with no error on NULL, so on arches without a translator they no-op and the kernel emits native 64-bit-layout messages, which is what an AArch32 task expects. Tested on Juniper SRX hardware: with the fix, 32-bit IPsec userspace netlink and XFRM_POLICY setsockopt operations that previously failed with -EOPNOTSUPP now succeed; x86 behaviour is unchanged by inspection. Fixes: 5106f4a8acff ("xfrm/compat: Add 32=>64-bit messages translator") Fixes: 96392ee5a13b ("xfrm/compat: Translate 32-bit user_policy from sockptr") Cc: stable@vger.kernel.org Signed-off-by: Sanman Pradhan Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_state.c | 2 +- net/xfrm/xfrm_user.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 7fc6727e75c5a..89daad5db10c9 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2983,7 +2983,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen) if (IS_ERR(data)) return PTR_ERR(data); - if (in_compat_syscall()) { + if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) { struct xfrm_translator *xtr = xfrm_get_translator(); if (!xtr) { diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 817e40d057341..9ad6619888c73 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3463,7 +3463,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (!netlink_net_capable(skb, CAP_NET_ADMIN)) return -EPERM; - if (in_compat_syscall()) { + if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) { struct xfrm_translator *xtr = xfrm_get_translator(); if (!xtr) From 8ca2a19a987a7d1cb4c916ed9723a1c6993b4276 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:41 +0800 Subject: [PATCH 1932/3297] xfrm: xfrm_interface: require CAP_NET_ADMIN in the device netns for changelink commit 095515d89b19b6cc19dfcdc846f97403ed1ebce3 upstream. xfrmi_changelink() operates on at most two netns, dev_net(dev) and the interface link netns xi->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in xi->net can rewrite an interface that lives in xi->net. Gate xfrmi_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: f203b76d7809 ("xfrm: Add virtual xfrm interfaces") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-8-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_interface_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c index 330a05286a56f..688306bf62c5a 100644 --- a/net/xfrm/xfrm_interface_core.c +++ b/net/xfrm/xfrm_interface_core.c @@ -869,6 +869,9 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[], struct net *net = xi->net; struct xfrm_if_parms p = {}; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + xfrmi_netlink_parms(data, &p); if (!p.if_id) { NL_SET_ERR_MSG(extack, "if_id must be non zero"); From 95bdf3950d668f83dd08f864f68da4b8dbed22bb Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 15 Jun 2026 15:02:05 +0300 Subject: [PATCH 1933/3297] tpm: fix event_size output in tpm1_binary_bios_measurements_show commit 1a58f6115bfb34eabcc7de8a3a9745b219179781 upstream. Commit 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements") split the output to write the endian-converted event header first and then the variable-length event data. However, the split was at sizeof(struct tcpa_event) - 1, even though event_data was a zero-length array, and later a flexible array member, both of which already excluded the event data. Therefore, the current code writes the first three bytes of event_size from the endian-converted header and then the last byte from the raw header, which can emit a corrupted event_size on PPC64, where do_endian_conversion() maps to be32_to_cpu(). Split one byte later to write the full endian-converted header first, followed by the variable-length event->event_data. Fixes: 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements") Cc: stable@vger.kernel.org # v5.10+ Signed-off-by: Thorsten Blum Signed-off-by: Jarkko Sakkinen Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/eventlog/tpm1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index e7913b2853d53..0397e3361020a 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -236,12 +236,12 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v) temp_ptr = (char *) &temp_event; - for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++) + for (i = 0; i < sizeof(struct tcpa_event); i++) seq_putc(m, temp_ptr[i]); temp_ptr = (char *) v; - for (i = (sizeof(struct tcpa_event) - 1); + for (i = sizeof(struct tcpa_event); i < (sizeof(struct tcpa_event) + temp_event.event_size); i++) seq_putc(m, temp_ptr[i]); From ada4b9a5087ea7f30dd8e4c6411a4fb6547eb1ed Mon Sep 17 00:00:00 2001 From: Jaewon Yang Date: Mon, 13 Jul 2026 02:11:47 +0900 Subject: [PATCH 1934/3297] tpm: Make the TPM character devices non-seekable commit f20d61c22bcaf172d6790b6500e3838e532e71c8 upstream. The TPM character devices expose a sequential command/response interface, but their open handlers leave FMODE_PREAD and FMODE_PWRITE enabled. After a command leaves a response pending, pread(fd, buf, 16, 0x1400) passes 0x1400 as *off to tpm_common_read(). The transfer length is bounded by response_length, but the offset is used unchecked when forming data_buffer + *off. A sufficiently large offset therefore causes an out-of-bounds heap read through copy_to_user() and, if the copy succeeds, an out-of-bounds zero-write through the following memset(). Positional I/O does not provide coherent semantics for this interface. An arbitrary pread offset cannot represent how much of a response has been consumed sequentially. The write callback always stores a command at the start of data_buffer, while pwrite() does not update file->f_pos and can leave the sequential read cursor stale. Call nonseekable_open() from both open handlers. This removes FMODE_PREAD and FMODE_PWRITE, causing positional reads and writes to fail with -ESPIPE before reaching the TPM callbacks, and explicitly marks the files non-seekable. Normal read() and write() continue to use the existing sequential f_pos cursor, leaving the response state machine unchanged. Tested on Linux 6.12 with KASAN and a swtpm TPM2 device: - sequential partial reads returned the complete response - pread() and preadv() with offset 0x1400 returned -ESPIPE - pwrite() and pwritev() with offset zero returned -ESPIPE - the pending response remained intact after the rejected operations - a subsequent normal command/response cycle completed normally - no KASAN report was produced. Fixes: 9488585b21be ("tpm: add support for partial reads") Link: https://lore.kernel.org/all/20260710090217.191289-1-yong010301@gmail.com/ Cc: stable@vger.kernel.org Signed-off-by: Jaewon Yang Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm-dev.c | 2 +- drivers/char/tpm/tpmrm-dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 97c94b5e93407..9181f155912b8 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -36,7 +36,7 @@ static int tpm_open(struct inode *inode, struct file *file) tpm_common_open(file, chip, priv, NULL); - return 0; + return nonseekable_open(inode, file); out: clear_bit(0, &chip->is_open); diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c index c25df7ea064ee..fd38de3449a57 100644 --- a/drivers/char/tpm/tpmrm-dev.c +++ b/drivers/char/tpm/tpmrm-dev.c @@ -29,7 +29,7 @@ static int tpmrm_open(struct inode *inode, struct file *file) tpm_common_open(file, chip, &priv->priv, &priv->space); - return 0; + return nonseekable_open(inode, file); } static int tpmrm_release(struct inode *inode, struct file *file) From 9b092f9e6b34db11710d74c85f364878fbad41f0 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Mon, 22 Jun 2026 18:33:48 +0800 Subject: [PATCH 1935/3297] time: Fix off-by-one in compat settimeofday() usec validation commit 269f2b43fae692d1f3988c9f888a6301aa537b82 upstream. The compat version of settimeofday() uses '>' instead of '>=' when validating tv_usec against USEC_PER_SEC, allowing the value 1000000 to pass the check. After the subsequent conversion to nanoseconds (tv_nsec *= NSEC_PER_USEC), this results in tv_nsec == NSEC_PER_SEC, which violates the timespec invariant that tv_nsec must be strictly less than NSEC_PER_SEC. The native settimeofday() was already fixed in commit ce4abda5e126 ("time: Fix off-by-one in settimeofday() usec validation"), but the compat counterpart was missed. Fix it by using '>=' to reject tv_usec values outside the valid range [0, USEC_PER_SEC - 1]. Fixes: 5e0fb1b57bea ("y2038: time: avoid timespec usage in settimeofday()") Signed-off-by: Wang Yan Signed-off-by: Thomas Gleixner Acked-by: Arnd Bergmann Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260622103348.120255-1-wangyan01@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- kernel/time/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/time.c b/kernel/time/time.c index 3c1518a7a5265..c0ca090c46704 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -251,7 +251,7 @@ COMPAT_SYSCALL_DEFINE2(settimeofday, struct old_timeval32 __user *, tv, get_user(new_ts.tv_nsec, &tv->tv_usec)) return -EFAULT; - if (new_ts.tv_nsec > USEC_PER_SEC || new_ts.tv_nsec < 0) + if (new_ts.tv_nsec >= USEC_PER_SEC || new_ts.tv_nsec < 0) return -EINVAL; new_ts.tv_nsec *= NSEC_PER_USEC; From d44b828eb551bd59ba9f22457825cc3db3a39fc1 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Tue, 16 Jun 2026 10:12:23 +0900 Subject: [PATCH 1936/3297] spi: uniphier: Fix completion initialization order before devm_request_irq() commit f3ad1c87d8201e54b66bd6072442f0b5d5a308ee upstream. The driver calls devm_request_irq() before initializing the completion used by the interrupt handler. Because the interrupt may occur immediately after devm_request_irq(), the handler may execute before init_completion(). This may result in calling complete() on an uninitialized completion, causing undefined behavior. This has been observed with KASAN. Fix this by initializing the completion before registering the IRQ. Reported-by: Sangyun Kim Reported-by: Kyungwook Boo Fixes: 5ba155a4d4cc ("spi: add SPI controller driver for UniPhier SoC") Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Signed-off-by: Kunihiko Hayashi Reviewed-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260616011223.201357-1-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-uniphier.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 75b7a0d3f8657..2126f46cb14e3 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -659,6 +659,8 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->host = host; priv->is_save_param = false; + init_completion(&priv->xfer_done); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); @@ -686,8 +688,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) goto out_host_put; } - init_completion(&priv->xfer_done); - clk_rate = clk_get_rate(priv->clk); host->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER); From ebe0a55d954fa8da383b6192edb8f763dcb002d5 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Fri, 3 Jul 2026 20:35:46 -0700 Subject: [PATCH 1937/3297] sctp: validate STALE_COOKIE cause length before reading staleness commit 1cd23ca80784223fa2204e16203f754da4e821f8 upstream. When an ERROR chunk with a STALE_COOKIE cause is received in the COOKIE_ECHOED state, sctp_sf_do_5_2_6_stale() reads the 4-byte Measure of Staleness that follows the cause header: err = (struct sctp_errhdr *)(chunk->skb->data); stale = ntohl(*(__be32 *)((u8 *)err + sizeof(*err))); err is the first cause in the chunk, not the STALE_COOKIE cause that caused the dispatch, and nothing guarantees the staleness field is present. sctp_walk_errors() only requires a cause to be as long as the 4-byte header, so for a STALE_COOKIE cause of length 4 the read runs past the cause, and for a minimal ERROR chunk past skb->tail. The value is echoed to the peer in the Cookie Preservative of the reply INIT, leaking uninitialized memory. sctp_sf_cookie_echoed_err() already walks to the STALE_COOKIE cause, so check its length there and pass it to sctp_sf_do_5_2_6_stale(), which reads that cause instead of the first one. A STALE_COOKIE cause too short to hold the staleness field is discarded. The read is reachable by any peer that can drive an association into COOKIE_ECHOED, including an unprivileged process using a raw SCTP socket in a user and network namespace. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Cc: stable@vger.kernel.org Signed-off-by: Weiming Shi Acked-by: Xin Long Link: https://patch.msgid.link/20260704033545.2438373-2-bestswngs@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/sctp/sm_statefuns.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 345395662d90d..abfd72d1f20c6 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -74,7 +74,8 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands); + struct sctp_cmd_seq *commands, + struct sctp_errhdr *err); static enum sctp_disposition sctp_sf_shut_8_4_5( struct net *net, const struct sctp_endpoint *ep, @@ -2524,9 +2525,15 @@ enum sctp_disposition sctp_sf_cookie_echoed_err( * errors. */ sctp_walk_errors(err, chunk->chunk_hdr) { - if (SCTP_ERROR_STALE_COOKIE == err->cause) - return sctp_sf_do_5_2_6_stale(net, ep, asoc, type, - arg, commands); + if (err->cause != SCTP_ERROR_STALE_COOKIE) + continue; + /* The staleness is only meaningful if the cause is long + * enough to hold it; a shorter one is malformed. + */ + if (ntohs(err->length) < sizeof(*err) + sizeof(__be32)) + break; + return sctp_sf_do_5_2_6_stale(net, ep, asoc, type, + arg, commands, err); } /* It is possible to have malformed error causes, and that @@ -2568,13 +2575,13 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) + struct sctp_cmd_seq *commands, + struct sctp_errhdr *err) { int attempts = asoc->init_err_counter + 1; - struct sctp_chunk *chunk = arg, *reply; struct sctp_cookie_preserve_param bht; struct sctp_bind_addr *bp; - struct sctp_errhdr *err; + struct sctp_chunk *reply; u32 stale; if (attempts > asoc->max_init_attempts) { @@ -2585,8 +2592,6 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( return SCTP_DISPOSITION_DELETE_TCB; } - err = (struct sctp_errhdr *)(chunk->skb->data); - /* When calculating the time extension, an implementation * SHOULD use the RTT information measured based on the * previous COOKIE ECHO / ERROR exchange, and should add no From 0ffc032294a29601b1019dba91aa1a930d90df17 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Tue, 7 Jul 2026 11:07:16 -0400 Subject: [PATCH 1938/3297] NFS: Charge unstable writes by request size, not folio size commit 27934d02cbeb8a957dd11c985a579e58d30c5270 upstream. nfs_folio_mark_unstable() and nfs_folio_clear_commit() charge and uncharge NR_WRITEBACK/WB_WRITEBACK by folio_nr_pages(folio) once per *request* added to or removed from a commit list. This is correct only when a folio has a single associated request. When pg_test splits a folio into N sub-folio requests (e.g. pNFS flexfiles striping with a stripe unit smaller than the folio size, or plain wsize-limited splitting), each of the N requests independently charges the whole folio's page count, inflating the accounting by a factor of N per folio. With large folios and small stripe units this reaches multiple orders of magnitude: a 2 MiB folio split into 512 4 KiB requests can charge up to 512x its real size, pushing global dirty+writeback accounting past the system's dirty threshold and forcing every buffered writer on the host into the hard-throttle path, including unrelated in-kernel NFS server threads sharing the box. Charge each request only for the pages it actually covers. Fixes: 0c493b5cf16e ("NFS: Convert buffered writes to use folios") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Coddington Assisted-By: Claude Sonnet 5 Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/internal.h | 12 +++++++----- fs/nfs/pnfs_nfs.c | 2 +- fs/nfs/write.c | 14 ++++++++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 61216b30cb93f..ea239a7e5ef68 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -854,17 +854,19 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) } /* - * Record the page as unstable (an extra writeback period) and mark its - * inode as dirty. + * Record the request's range as unstable (an extra writeback period) and + * mark its inode as dirty. */ -static inline void nfs_folio_mark_unstable(struct folio *folio, +static inline void nfs_folio_mark_unstable(struct nfs_page *req, struct nfs_commit_info *cinfo) { + struct folio *folio = nfs_page_to_folio(req); + if (folio && !cinfo->dreq) { struct inode *inode = folio->mapping->host; - long nr = folio_nr_pages(folio); + long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE); - /* This page is really still in write-back - just that the + /* This range is really still in write-back - just that the * writeback is happening on the server now. */ node_stat_mod_folio(folio, NR_WRITEBACK, nr); diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 37cf4020f3d5d..1e291fd23d46f 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -1199,7 +1199,7 @@ pnfs_layout_mark_request_commit(struct nfs_page *req, nfs_request_add_commit_list_locked(req, list, cinfo); mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); - nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo); + nfs_folio_mark_unstable(req, cinfo); return; out_resched: mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index bf412455e8edf..c68129903411d 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -807,7 +807,7 @@ nfs_request_add_commit_list(struct nfs_page *req, struct nfs_commit_info *cinfo) mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); nfs_request_add_commit_list_locked(req, &cinfo->mds->list, cinfo); mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); - nfs_folio_mark_unstable(nfs_page_to_folio(req), cinfo); + nfs_folio_mark_unstable(req, cinfo); } EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); @@ -866,10 +866,12 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg, nfs_request_add_commit_list(req, cinfo); } -static void nfs_folio_clear_commit(struct folio *folio) +static void nfs_folio_clear_commit(struct nfs_page *req) { + struct folio *folio = nfs_page_to_folio(req); + if (folio) { - long nr = folio_nr_pages(folio); + long nr = DIV_ROUND_UP(req->wb_bytes, PAGE_SIZE); node_stat_mod_folio(folio, NR_WRITEBACK, -nr); wb_stat_mod(&inode_to_bdi(folio->mapping->host)->wb, @@ -890,7 +892,7 @@ static void nfs_clear_request_commit(struct nfs_commit_info *cinfo, nfs_request_remove_commit_list(req, cinfo); } mutex_unlock(&NFS_I(inode)->commit_mutex); - nfs_folio_clear_commit(nfs_page_to_folio(req)); + nfs_folio_clear_commit(req); } } @@ -1735,7 +1737,7 @@ void nfs_retry_commit(struct list_head *page_list, req = nfs_list_entry(page_list->next); nfs_list_remove_request(req); nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx); - nfs_folio_clear_commit(nfs_page_to_folio(req)); + nfs_folio_clear_commit(req); nfs_unlock_and_release_request(req); } } @@ -1807,7 +1809,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); folio = nfs_page_to_folio(req); - nfs_folio_clear_commit(folio); + nfs_folio_clear_commit(req); dprintk("NFS: commit (%s/%llu %d@%lld)", nfs_req_openctx(req)->dentry->d_sb->s_id, From 59cef6abc924a84824b0c3f563a7fe74cd5fc7a4 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 7 Jun 2026 14:10:58 +0800 Subject: [PATCH 1939/3297] nvme-apple: Prevent shared tags across queues on Apple A11 commit 6fe0687245e8406bf26143bd45eb16441bbe5280 upstream. On Apple A11, tags of pending commands must be unique across the admin and IO queues, else the firmware crashes with "duplicate tag error for tag N", with N being the tag. Apply the existing workaround for M1 of reserving two tags for the admin queue to A11. Cc: stable@vger.kernel.org Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support") Reviewed-by: Sven Peter Signed-off-by: Nick Chan Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/host/apple.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index 0a5b3903f9b9c..9cc614227bc70 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -225,7 +225,7 @@ static unsigned int apple_nvme_queue_depth(struct apple_nvme_queue *q) { struct apple_nvme *anv = queue_to_apple_nvme(q); - if (q->is_adminq && anv->hw->has_lsq_nvmmu) + if (q->is_adminq) return APPLE_NVME_AQ_DEPTH; return anv->hw->max_queue_depth; @@ -303,7 +303,7 @@ static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q, memcpy((void *)q->sqes + (q->sq_tail << APPLE_NVME_IOSQES), cmd, sizeof(*cmd)); - if (++q->sq_tail == anv->hw->max_queue_depth) + if (++q->sq_tail == apple_nvme_queue_depth(q)) q->sq_tail = 0; writel(q->sq_tail, q->sq_db); @@ -1139,10 +1139,7 @@ static void apple_nvme_reset_work(struct work_struct *work) } /* Setup the admin queue */ - if (anv->hw->has_lsq_nvmmu) - aqa = APPLE_NVME_AQ_DEPTH - 1; - else - aqa = anv->hw->max_queue_depth - 1; + aqa = APPLE_NVME_AQ_DEPTH - 1; aqa |= aqa << 16; writel(aqa, anv->mmio_nvme + NVME_REG_AQA); writeq(anv->adminq.sq_dma_addr, anv->mmio_nvme + NVME_REG_ASQ); @@ -1324,8 +1321,7 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) * both queues. The admin queue gets the first APPLE_NVME_AQ_DEPTH which * must be marked as reserved in the IO queue. */ - if (anv->hw->has_lsq_nvmmu) - anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; + anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH; anv->tagset.queue_depth = anv->hw->max_queue_depth - 1; anv->tagset.timeout = NVME_IO_TIMEOUT; anv->tagset.numa_node = NUMA_NO_NODE; From 2eaa3ad450141cfcf187bb43cb8335eb336b5f87 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 9 Jun 2026 14:24:31 -0400 Subject: [PATCH 1940/3297] nvmet-auth: reject short AUTH_RECEIVE buffers commit 779575bc35c687697ba69e904f2cd22e60112534 upstream. nvmet_execute_auth_receive() trusts the AUTH_RECEIVE allocation length after checking only that it is nonzero and matches the transfer length. In the SUCCESS1 and FAILURE1/default states, that lets a remote NVMe-oF initiator reach the fixed-size DH-HMAC-CHAP response builders with a kmalloc() buffer shorter than the response, so nvmet_auth_success1() and nvmet_auth_failure1() write past the allocation; both only WARN_ON the short length and then format the message anyway. Impact: A remote NVMe-oF initiator with access to an auth-enabled target can trigger a 16-byte heap out-of-bounds write via a one-byte AUTH_RECEIVE allocation length. Compute the minimum response length for the current DH-HMAC-CHAP step in nvmet_auth_receive_data_len() and report a zero data length when the host-supplied allocation length is shorter, so the existing zero-length check in nvmet_execute_auth_receive() rejects the command before any builder runs. The SUCCESS1 minimum is sizeof(struct nvmf_auth_dhchap_success1_data) plus the HMAC hash length, because the response hash is written into the rval[] flexible-array tail, so the minimum is state dependent rather than a flat sizeof. CHALLENGE keeps its existing variable-length guard in nvmet_auth_challenge(). This is reachable only when in-band DH-HMAC-CHAP authentication is configured on the target. Fixes: db1312dd9548 ("nvmet: implement basic In-Band Authentication") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5-5-xhigh Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Hannes Reinecke Signed-off-by: Michael Bommarito Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/fabrics-cmd-auth.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 4f2f03bdf2b38..2400d70233987 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -494,7 +494,31 @@ static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al) u32 nvmet_auth_receive_data_len(struct nvmet_req *req) { - return le32_to_cpu(req->cmd->auth_receive.al); + struct nvmet_ctrl *ctrl = req->sq->ctrl; + u32 al = le32_to_cpu(req->cmd->auth_receive.al); + u32 min_len; + + /* + * Reject too-short al before kmalloc(al), since the SUCCESS1 and + * FAILURE1/default builders write fixed response headers into it. + */ + switch (req->sq->dhchap_step) { + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE: + return al; + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1: + min_len = sizeof(struct nvmf_auth_dhchap_success1_data); + if (req->sq->dhchap_c2) + min_len += nvme_auth_hmac_hash_len(ctrl->shash_id); + break; + default: + min_len = sizeof(struct nvmf_auth_dhchap_failure_data); + break; + } + + if (al < min_len) + return 0; + + return al; } void nvmet_execute_auth_receive(struct nvmet_req *req) From 2944113ad5fbcdf5d349d857c03d2a44b6de75b8 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 4 Jun 2026 19:36:54 +0000 Subject: [PATCH 1941/3297] nvmet-rdma: handle inline data with a nonzero offset commit 48c0162f647bb47e6084ffbc71b8f213f5e2f4f8 upstream. nvmet_rdma_use_inline_sg() maps the host-controlled inline data offset into the per-command inline scatterlist. The bounds check admits any offset with off + len <= inline_data_size, but the mapping still assumes the data begins in the first inline page: sg->offset = off; sg->length = min_t(int, len, PAGE_SIZE - off); When a port is configured with inline_data_size > PAGE_SIZE (settable up to max(SZ_16K, PAGE_SIZE)), an offset in (PAGE_SIZE, inline_data_size] makes "PAGE_SIZE - off" underflow, so sg->length is set to ~4 GiB and the block backend reads far past the first inline page. num_pages(len) also ignores the offset, so an in-bounds offset whose [off, off+len) span crosses a page boundary under-counts the scatterlist. Map the offset properly: split it into a page index and an in-page offset, start the scatterlist at that page, and size the page count from page_off + len. Because the request scatterlist may now start at inline_sg[page_idx] rather than inline_sg[0], generalize the inline-SGL identity test in nvmet_rdma_release_rsp() to a range test; otherwise the persistent inline scatterlist is mistaken for an allocated one and nvmet_req_free_sgls() frees an inline page (and warns in free_large_kmalloc()). Fixes: 0d5ee2b2ab4f ("nvmet-rdma: support max(16KB, PAGE_SIZE) inline data") Cc: stable@vger.kernel.org Suggested-by: Keith Busch Reported-by: Bryam Vargas Signed-off-by: Bryam Vargas Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/rdma.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index 9307dbc41303c..4b493226d07c1 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -667,7 +667,8 @@ static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp) if (rsp->n_rdma) nvmet_rdma_rw_ctx_destroy(rsp); - if (rsp->req.sg != rsp->cmd->inline_sg) + if (rsp->req.sg < rsp->cmd->inline_sg || + rsp->req.sg >= rsp->cmd->inline_sg + queue->dev->inline_page_count) nvmet_req_free_sgls(&rsp->req); if (unlikely(!list_empty_careful(&queue->rsp_wr_wait_list))) @@ -822,24 +823,25 @@ static void nvmet_rdma_write_data_done(struct ib_cq *cq, struct ib_wc *wc) static void nvmet_rdma_use_inline_sg(struct nvmet_rdma_rsp *rsp, u32 len, u64 off) { - int sg_count = num_pages(len); + u64 page_off = off % PAGE_SIZE; + u64 page_idx = off / PAGE_SIZE; + int sg_count = num_pages(page_off + len); struct scatterlist *sg; int i; - sg = rsp->cmd->inline_sg; + sg = &rsp->cmd->inline_sg[page_idx]; for (i = 0; i < sg_count; i++, sg++) { if (i < sg_count - 1) sg_unmark_end(sg); else sg_mark_end(sg); - sg->offset = off; - sg->length = min_t(int, len, PAGE_SIZE - off); + sg->offset = page_off; + sg->length = min_t(u64, len, PAGE_SIZE - page_off); len -= sg->length; - if (!i) - off = 0; + page_off = 0; } - rsp->req.sg = rsp->cmd->inline_sg; + rsp->req.sg = &rsp->cmd->inline_sg[page_idx]; rsp->req.sg_cnt = sg_count; } From 26355295ce21cb046546085c3a81abe68160a784 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 09:55:05 +0000 Subject: [PATCH 1942/3297] nvmet: fix refcount leak in nvmet_sq_create() commit 34b9a83c50660148bde01cde16451dbe78369749 upstream. In nvmet_sq_create(), a reference on the ctrl is taken via kref_get_unless_zero() before calling nvmet_check_sqid(). If nvmet_check_sqid() fails, the function returns the error directly without releasing the reference, leading to a leak. Fix this by jumping to the "ctrl_put" label, which already performs the necessary nvmet_ctrl_put(ctrl). This ensures the reference is properly released on this error path. Cc: stable@vger.kernel.org Fixes: 1eb380caf527 ("nvmet: Introduce nvmet_sq_create() and nvmet_cq_create()") Signed-off-by: Wentao Liang Signed-off-by: Keith Busch Signed-off-by: Greg Kroah-Hartman --- drivers/nvme/target/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 1567b35f96e37..39f41416d3a7f 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -935,7 +935,7 @@ u16 nvmet_sq_create(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, status = nvmet_check_sqid(ctrl, sqid, true); if (status != NVME_SC_SUCCESS) - return status; + goto ctrl_put; ret = nvmet_sq_init(sq, cq); if (ret) { From 5e4c8e08ce95730c87d6ada0bdbe1131a3c06393 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Tue, 16 Jun 2026 01:17:36 +0800 Subject: [PATCH 1943/3297] netdev-genl: report NAPI thread PID in the caller's pid namespace commit 1f24c0d01db214c9e661915e9972404c96ca73c0 upstream. netdev_nl_napi_fill_one() reports the NAPI kthread PID in NETDEV_A_NAPI_PID using task_pid_nr(), which returns the PID in the initial pid namespace. NETDEV_CMD_NAPI_GET does not have GENL_ADMIN_PERM and the netdev genl family is netnsok, so a caller in a child pid namespace can issue it. That caller then sees the kthread's global PID, even though the kthread is not visible in its pid namespace, where the value should be 0. Translate the PID through the caller's pid namespace, the same way commit 3799c2570982 ("io_uring/fdinfo: translate SqThread PID through caller's pid_ns") did for the io_uring SQPOLL thread. The doit and dumpit paths both run synchronously in the caller's context, so task_active_pid_ns(current) is the caller's pid namespace. Fixes: db4704f4e4df ("netdev-genl: Add PID for the NAPI thread") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Joe Damato Reviewed-by: Samiullah Khawaja Link: https://patch.msgid.link/20260615171736.1709318-1-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/netdev-genl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index 93ea09bd1e7ba..ba35ba5ae7848 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -189,7 +190,8 @@ netdev_nl_napi_fill_one(struct sk_buff *rsp, struct napi_struct *napi, goto nla_put_failure; if (napi->thread) { - pid = task_pid_nr(napi->thread); + pid = task_pid_nr_ns(napi->thread, + task_active_pid_ns(current)); if (nla_put_u32(rsp, NETDEV_A_NAPI_PID, pid)) goto nla_put_failure; } From 765ba1c91823a296447528791b89a6504947fd5c Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 16:41:59 +0000 Subject: [PATCH 1944/3297] can: esd_usb: kill anchored URBs before freeing netdevs commit c43122fef328a70045fe7621c06de6b2b8e19264 upstream. esd_usb_disconnect() frees each CAN netdev with free_candev() inside its per-netdev loop and only calls unlink_all_urbs(dev) afterwards. The per-netdev private data (struct esd_usb_net_priv) is embedded in the net_device allocation returned by alloc_candev(), so once free_candev() has run, dev->nets[i] points to freed memory. unlink_all_urbs() then dereferences the freed dev->nets[i] to kill the per-netdev TX anchor (usb_kill_anchored_urbs(&priv->tx_submitted)), clear active_tx_jobs, and reset priv->tx_contexts[]. Reorder the teardown so the anchored URBs are killed before the netdevs are freed, matching other CAN/USB drivers in the same directory such as ems_usb, usb_8dev and mcba_usb, which unregister, then unlink, then free: unregister the netdevs first (which stops their TX queues), call unlink_all_urbs(dev) once, then free the netdevs. This issue was found by an in-house static analysis tool. Fixes: 96d8e90382dc ("can: Add driver for esd CAN-USB/2 device") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.5 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260709164159.497640-1-fanwu01@zju.edu.cn Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/esd_usb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c index e58f0bfa84a4c..9b712aedc19a7 100644 --- a/drivers/net/can/usb/esd_usb.c +++ b/drivers/net/can/usb/esd_usb.c @@ -1387,10 +1387,13 @@ static void esd_usb_disconnect(struct usb_interface *intf) netdev = dev->nets[i]->netdev; netdev_info(netdev, "unregister\n"); unregister_netdev(netdev); - free_candev(netdev); } } unlink_all_urbs(dev); + for (i = 0; i < dev->net_count; i++) { + if (dev->nets[i]) + free_candev(dev->nets[i]->netdev); + } kfree(dev); } } From b88a511308779c225005d7994b8744561bdbafbc Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:41 +0200 Subject: [PATCH 1945/3297] can: isotp: use unconditional synchronize_rcu() in isotp_release() commit 9b1a02e0d980ac6b0e36a90378f847062f81d7e4 upstream. isotp_notify() unregisters the (RCU) CAN filters via can_rx_unregister() and clears so->bound without waiting for a grace period. isotp_release() uses so->bound to decide whether it needs to call synchronize_rcu() before cancelling so->rxtimer, so when NETDEV_UNREGISTER runs first it skips that synchronize_rcu() and can cancel the timer while an in-flight isotp_rcv() is still executing and about to re-arm it via isotp_send_fc(), leading to a use-after-free timer callback on the freed socket. sakisho-bot remarked a problem with rtnl_lock held in isotp_notify(), therefore make isotp_release() always call synchronize_rcu() before cancelling the timers, regardless of so->bound. This still closes the original race (isotp_notify() clearing so->bound without waiting for in-flight isotp_rcv() callers before isotp_release() cancels the RX timer) without adding any RCU wait to the netdevice notifier path. Fixes: 14a4696bc311 ("can: isotp: isotp_release(): omit unintended hrtimer restart on socket release") Closes: https://lore.kernel.org/linux-can/20260707085210.6B6C01F000E9@smtp.kernel.org/ Reported-by: Nico Yip Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-1-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 12eabfd47d824..4cf1ad2fd7503 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1218,11 +1218,18 @@ static int isotp_release(struct socket *sock) SINGLE_MASK(so->txid), isotp_rcv_echo, sk); dev_put(dev); - synchronize_rcu(); } } } + /* Always wait for a grace period before touching the timers below. + * A concurrent NETDEV_UNREGISTER may have already unregistered our + * filters and cleared so->bound in isotp_notify() without waiting + * for in-flight isotp_rcv() callers to finish, so this call must not + * be skipped just because so->bound is already 0 here. + */ + synchronize_rcu(); + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); From 7bef39ba76eb7307ed22a50329e0f5776dbeda58 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:42 +0200 Subject: [PATCH 1946/3297] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER commit 20bab8b88baac140ca3701116e1d486c7f51e311 upstream. isotp_release() looked up the bound network device via dev_get_by_index() using the stored ifindex. During device unregistration the device is unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier chain runs, so a concurrent isotp_release() could find no device, skip can_rx_unregister() entirely, and still proceed to free the socket. Since isotp_release() had already removed itself from the isotp notifier list at that point, isotp_notify() would never get a chance to clean up either, leaving a stale CAN filter that keeps pointing at the freed socket. Fix this the same way raw.c already does: hold a tracked reference to the bound net_device in the socket (so->dev/so->dev_tracker) from bind() onward instead of re-resolving it from the ifindex, and serialize bind()/release() with rtnl_lock() so that so->dev is always consistent with what the NETDEV_UNREGISTER notifier sees. so->dev stays valid regardless of ifindex-hash unlisting, and is only ever cleared by whichever of isotp_release()/isotp_notify() gets there first, so the filter is always removed exactly once. isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state isn't ISOTP_IDLE yet, so a timer left running by a prior NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks share the same lock_sock() section, so there is no window in which a concurrent isotp_notify() clearing so->bound could be missed. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-2-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 87 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 4cf1ad2fd7503..2faa10478d3e0 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -151,6 +151,8 @@ struct isotp_sock { struct sock sk; int bound; int ifindex; + struct net_device *dev; + netdevice_tracker dev_tracker; canid_t txid; canid_t rxid; ktime_t tx_gap; @@ -965,6 +967,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto err_event_drop; } + /* so->bound is only checked once above - a wakeup may have + * unbound/rebound the socket meanwhile, so re-validate it + */ + if (!so->bound) { + err = -EADDRNOTAVAIL; + goto err_out_drop; + } + /* PDU size > default => try max_pdu_size */ if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) { u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL); @@ -1200,28 +1210,30 @@ static int isotp_release(struct socket *sock) list_del(&so->notifier); spin_unlock(&isotp_notifier_lock); + rtnl_lock(); lock_sock(sk); - /* remove current filters & unregister */ - if (so->bound) { - if (so->ifindex) { - struct net_device *dev; - - dev = dev_get_by_index(net, so->ifindex); - if (dev) { - if (isotp_register_rxid(so)) - can_rx_unregister(net, dev, so->rxid, - SINGLE_MASK(so->rxid), - isotp_rcv, sk); - - can_rx_unregister(net, dev, so->txid, - SINGLE_MASK(so->txid), - isotp_rcv_echo, sk); - dev_put(dev); - } - } + /* remove current filters & unregister + * tracked reference so->dev is taken at bind() time with rtnl_lock + */ + if (so->bound && so->dev) { + if (isotp_register_rxid(so)) + can_rx_unregister(net, so->dev, so->rxid, + SINGLE_MASK(so->rxid), + isotp_rcv, sk); + + can_rx_unregister(net, so->dev, so->txid, + SINGLE_MASK(so->txid), + isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } + so->ifindex = 0; + so->bound = 0; + so->dev = NULL; + + rtnl_unlock(); + /* Always wait for a grace period before touching the timers below. * A concurrent NETDEV_UNREGISTER may have already unregistered our * filters and cleared so->bound in isotp_notify() without waiting @@ -1234,9 +1246,6 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); - so->ifindex = 0; - so->bound = 0; - sock_orphan(sk); sock->sk = NULL; @@ -1291,6 +1300,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (!addr->can_ifindex) return -ENODEV; + rtnl_lock(); lock_sock(sk); if (so->bound) { @@ -1298,6 +1308,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) goto out; } + /* A transmission or reception that outlived a previous binding + * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo + * and RX watchdog timers bound how long this takes. Checked together + * with so->bound in the same lock_sock() section above, so there is + * no window in which a concurrent isotp_notify() could be missed. + */ + if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) { + err = -EAGAIN; + goto out; + } + /* ensure different CAN IDs when the rx_id is to be registered */ if (isotp_register_rxid(so) && rx_id == tx_id) { err = -EADDRNOTAVAIL; @@ -1310,14 +1331,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) goto out; } if (dev->type != ARPHRD_CAN) { - dev_put(dev); err = -ENODEV; - goto out; + goto out_put_dev; } if (READ_ONCE(dev->mtu) < so->ll.mtu) { - dev_put(dev); err = -EINVAL; - goto out; + goto out_put_dev; } if (!(dev->flags & IFF_UP)) notify_enetdown = 1; @@ -1335,16 +1354,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), isotp_rcv_echo, sk, "isotpe", sk); - dev_put(dev); - /* switch to new settings */ so->ifindex = ifindex; so->rxid = rx_id; so->txid = tx_id; so->bound = 1; + /* bind() ok -> hold a reference for so->dev so that isotp_release() + * can safely reach the device later, even if a concurrent + * NETDEV_UNREGISTER has already unlisted it by ifindex. + */ + so->dev = dev; + netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL); + +out_put_dev: + /* remove potential reference from dev_get_by_index() */ + dev_put(dev); out: release_sock(sk); + rtnl_unlock(); if (notify_enetdown) { sk->sk_err = ENETDOWN; @@ -1547,7 +1575,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, if (!net_eq(dev_net(dev), sock_net(sk))) return; - if (so->ifindex != dev->ifindex) + if (so->dev != dev) return; switch (msg) { @@ -1563,10 +1591,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, can_rx_unregister(dev_net(dev), dev, so->txid, SINGLE_MASK(so->txid), isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } so->ifindex = 0; so->bound = 0; + so->dev = NULL; release_sock(sk); sk->sk_err = ENODEV; @@ -1626,6 +1656,7 @@ static int isotp_init(struct sock *sk) so->ifindex = 0; so->bound = 0; + so->dev = NULL; so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS; so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS; From 37beb16e08cae94cc05840c7274225e3b0b38ae7 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:43 +0200 Subject: [PATCH 1947/3297] can: isotp: serialize TX state transitions under so->rx_lock commit cf070fe33bfbd1a4c21236078fadb35dd223a157 upstream. The TX state machine (so->tx.state) is driven from three contexts: sendmsg() claiming and progressing a transfer, the RX path consuming Flow Control/echo frames, and two hrtimers timing out a stalled transfer. Mixing a lock-free cmpxchg() claim in sendmsg() with hrtimer_cancel() calls made under so->rx_lock elsewhere left windows where a frame or timer callback could act on a state that had already moved on, corrupting an unrelated transfer. so->rx_lock now covers the full lifecycle of a TX claim: sendmsg() takes it to check so->tx.state is ISOTP_IDLE, switch it to ISOTP_SENDING, bump so->tx_gen and drain the previous transfer's timers - all as one critical section. isotp_rcv_fc()/isotp_rcv_cf() already run under this lock via isotp_rcv(), and isotp_rcv_echo() now takes it itself, so none of them can ever observe a transfer mid-claim. This also means a transfer can no longer be handed to sendmsg()'s cleanup paths (signal or send error) while another thread is concurrently claiming or finishing it, so those paths can cancel timers and reset the state unconditionally. isotp_release() claims the socket the same way, so a racing sendmsg() sees a consistent ISOTP_SHUTDOWN and skips arming its timer or sending. Only the hrtimer callbacks stay outside so->rx_lock, since they run under so->rx_lock's cancellation elsewhere and taking it themselves would deadlock. so->tx_gen lets them recognize whether the transfer they timed out is still the one currently active, so they don't report an error against a transfer that has since completed or been superseded. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260712-isotp-fixes-v10-3-793a1b1ce17f@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 202 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 42 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 2faa10478d3e0..c21d816a52747 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -157,7 +157,7 @@ struct isotp_sock { canid_t rxid; ktime_t tx_gap; ktime_t lastrxcf_tstamp; - struct hrtimer rxtimer, txtimer, txfrtimer; + struct hrtimer rxtimer, txtimer, txfrtimer, echotimer; struct can_isotp_options opt; struct can_isotp_fc_options rxfc, txfc; struct can_isotp_ll_options ll; @@ -165,6 +165,7 @@ struct isotp_sock { u32 force_tx_stmin; u32 force_rx_stmin; u32 cfecho; /* consecutive frame echo tag */ + u32 tx_gen; /* generation, bumped per new tx transfer */ struct tpcon rx, tx; struct list_head notifier; wait_queue_head_t wait; @@ -372,6 +373,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) hrtimer_cancel(&so->txtimer); + /* isotp_tx_timeout() may have given up on this job while + * hrtimer_cancel() above waited for it to finish; so->rx_lock + * (held by our caller isotp_rcv()) rules out a concurrent claim, + * so a plain recheck is enough here. + */ + if (so->tx.state != ISOTP_WAIT_FC && + so->tx.state != ISOTP_WAIT_FIRST_FC) + return 1; + if ((cf->len < ae + FC_CONTENT_SZ) || ((so->opt.flags & ISOTP_CHECK_PADDING) && check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) { @@ -418,7 +428,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) so->tx.bs = 0; so->tx.state = ISOTP_SENDING; /* send CF frame and enable echo timeout handling */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); isotp_send_cframe(so); break; @@ -571,6 +581,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, hrtimer_cancel(&so->rxtimer); + /* isotp_rx_timer_handler() may have raced us for so->rx.state + * while hrtimer_cancel() above waited for it to finish, already + * reporting ETIMEDOUT and resetting the reception; don't process + * this CF into a reassembly that has already been given up on. + */ + if (so->rx.state != ISOTP_WAIT_DATA) + return 1; + /* CFs are never longer than the FF */ if (cf->len > so->rx.ll_dl) return 1; @@ -860,20 +878,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) struct canfd_frame *cf = (struct canfd_frame *)skb->data; /* only handle my own local echo CF/SF skb's (no FF!) */ - if (skb->sk != sk || so->cfecho != *(u32 *)cf->data) + if (skb->sk != sk) return; + /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock + * (no isotp_rcv() caller here), so take it ourselves + */ + spin_lock(&so->rx_lock); + + /* so->cfecho may since belong to a new transfer; recheck under lock */ + if (so->cfecho != *(u32 *)cf->data) + goto out_unlock; + /* cancel local echo timeout */ - hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); /* local echo skb with consecutive frame has been consumed */ so->cfecho = 0; + /* claiming a transfer also takes so->rx_lock, so a plain recheck + * is enough: so->tx.state can't have flipped to ISOTP_SENDING for + * a new claim while we're still in here + */ + if (so->tx.state != ISOTP_SENDING) + goto out_unlock; + if (so->tx.idx >= so->tx.len) { /* we are done */ so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); - return; + goto out_unlock; } if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { @@ -881,53 +915,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) so->tx.state = ISOTP_WAIT_FC; hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); - return; + goto out_unlock; } /* no gap between data frames needed => use burst mode */ if (!so->tx_gap) { /* enable echo timeout handling */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); isotp_send_cframe(so); - return; + goto out_unlock; } /* start timer to send next consecutive frame with correct delay */ hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT); + +out_unlock: + spin_unlock(&so->rx_lock); } -static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) +/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get + * cancelled under so->rx_lock elsewhere, so this must stay lock-free to + * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting + * a new transfer with an error from the one that just timed out. + */ +static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) { - struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, - txtimer); struct sock *sk = &so->sk; + u32 gen = READ_ONCE(so->tx_gen); + u32 old_state = READ_ONCE(so->tx.state); /* don't handle timeouts in IDLE or SHUTDOWN state */ - if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN) + if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN) + return HRTIMER_NORESTART; + + /* only claim the timeout if the state is still unchanged */ + if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) return HRTIMER_NORESTART; /* we did not get any flow control or echo frame in time */ - /* report 'communication error on send' */ - sk->sk_err = ECOMM; - if (!sock_flag(sk, SOCK_DEAD)) - sk_error_report(sk); + if (READ_ONCE(so->tx_gen) == gen) { + /* report 'communication error on send' */ + sk->sk_err = ECOMM; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); + } - /* reset tx state */ - so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); return HRTIMER_NORESTART; } +/* so->txtimer: fires when a Flow Control frame does not arrive in time */ +static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) +{ + struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, + txtimer); + + return isotp_tx_timeout(so); +} + +/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */ +static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer) +{ + struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, + echotimer); + + return isotp_tx_timeout(so); +} + static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer) { struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, txfrtimer); /* start echo timeout handling and cover below protocol error */ - hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), + hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); /* cfecho should be consumed by isotp_rcv_echo() here */ @@ -947,13 +1011,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0; s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT; + struct hrtimer *tx_hrt = &so->echotimer; + u32 new_state = ISOTP_SENDING; int off; int err; if (!so->bound || so->tx.state == ISOTP_SHUTDOWN) return -EADDRNOTAVAIL; - while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) { + /* claim the socket under so->rx_lock: this serializes the claim + * with the RX path and with sendmsg()'s own error paths below, so + * none of them can ever see a transfer mid-claim + */ + for (;;) { + spin_lock_bh(&so->rx_lock); + if (READ_ONCE(so->tx.state) == ISOTP_IDLE) + break; + spin_unlock_bh(&so->rx_lock); + /* we do not support multiple buffers - for now */ if (msg->msg_flags & MSG_DONTWAIT) return -EAGAIN; @@ -962,11 +1037,23 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return -EADDRNOTAVAIL; /* wait for complete transmission of current pdu */ - err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); + err = wait_event_interruptible(so->wait, + so->tx.state == ISOTP_IDLE); if (err) - goto err_event_drop; + return err; } + /* new transfer: bump so->tx_gen and drain the old one's timers, + * still under the so->rx_lock we just claimed the socket with + */ + WRITE_ONCE(so->tx.state, ISOTP_SENDING); + WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1); + hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); + hrtimer_cancel(&so->txfrtimer); + so->cfecho = 0; + spin_unlock_bh(&so->rx_lock); + /* so->bound is only checked once above - a wakeup may have * unbound/rebound the socket meanwhile, so re-validate it */ @@ -1077,18 +1164,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->cfecho = *(u32 *)cf->data; } else { /* standard flow control check */ - so->tx.state = ISOTP_WAIT_FIRST_FC; + new_state = ISOTP_WAIT_FIRST_FC; /* start timeout for FC */ hrtimer_sec = ISOTP_FC_TIMEOUT; + tx_hrt = &so->txtimer; /* no CF echo tag for isotp_rcv_echo() (FF-mode) */ so->cfecho = 0; } } - hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0), + spin_lock_bh(&so->rx_lock); + if (so->tx.state == ISOTP_SHUTDOWN) { + /* isotp_release() has since taken over and already drained + * our timers - don't send into a socket that's going away + */ + spin_unlock_bh(&so->rx_lock); + kfree_skb(skb); + dev_put(dev); + wake_up_interruptible(&so->wait); + return -EADDRNOTAVAIL; + } + /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ + so->tx.state = new_state; + hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0), HRTIMER_MODE_REL_SOFT); + spin_unlock_bh(&so->rx_lock); /* send the first or only CAN frame */ cf->flags = so->ll.tx_flags; @@ -1101,13 +1203,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) pr_notice_once("can-isotp: %s: can_send_ret %pe\n", __func__, ERR_PTR(err)); + spin_lock_bh(&so->rx_lock); /* no transmission -> no timeout monitoring */ - hrtimer_cancel(&so->txtimer); - - /* reset consecutive frame echo tag */ - so->cfecho = 0; - - goto err_out_drop; + hrtimer_cancel(tx_hrt); + goto err_out_drop_locked; } if (wait_tx_done) { @@ -1123,14 +1222,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return size; +err_out_drop: + /* claimed but nothing sent yet - no timer to cancel */ + spin_lock_bh(&so->rx_lock); + goto err_out_drop_locked; err_event_drop: - /* got signal: force tx state machine to be idle */ - so->tx.state = ISOTP_IDLE; + /* interrupted waiting on our own transfer - drain its timers */ + spin_lock_bh(&so->rx_lock); hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); -err_out_drop: - /* drop this PDU and unlock a potential wait queue */ + hrtimer_cancel(&so->echotimer); +err_out_drop_locked: + /* release the claim; so->rx_lock still held from above */ + so->cfecho = 0; so->tx.state = ISOTP_IDLE; + spin_unlock_bh(&so->rx_lock); wake_up_interruptible(&so->wait); return err; @@ -1192,13 +1298,20 @@ static int isotp_release(struct socket *sock) so = isotp_sk(sk); net = sock_net(sk); - /* wait for complete transmission of current pdu */ - while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 && - cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE) + /* best-effort: wait for a running pdu to finish, but don't block on + * it forever - give up after the first signal + */ + while (so->tx.state != ISOTP_IDLE && + wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0) ; - /* force state machines to be idle also when a signal occurred */ + /* claim the socket under so->rx_lock like sendmsg() does, so its + * claim can't race the forced ISOTP_SHUTDOWN below; force it + * unconditionally, even when a signal cut the wait above short + */ + spin_lock_bh(&so->rx_lock); so->tx.state = ISOTP_SHUTDOWN; + spin_unlock_bh(&so->rx_lock); so->rx.state = ISOTP_IDLE; spin_lock(&isotp_notifier_lock); @@ -1244,6 +1357,7 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); + hrtimer_cancel(&so->echotimer); hrtimer_cancel(&so->rxtimer); sock_orphan(sk); @@ -1683,10 +1797,14 @@ static int isotp_init(struct sock *sk) so->rx.buflen = ARRAY_SIZE(so->rx.sbuf); so->tx.buflen = ARRAY_SIZE(so->tx.sbuf); - hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->echotimer, isotp_echo_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); init_waitqueue_head(&so->wait); spin_lock_init(&so->rx_lock); From cd830e0bc25ee2d38cbfbdbb3cd77c5f53b2b6d5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2026 18:55:23 +0200 Subject: [PATCH 1948/3297] can: bcm: defer rx_op deallocation to workqueue to fix thrtimer UAF commit 68973f9db76144825e4f35dfdc80fb8279eb2d57 upstream. Commit f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()") replaced synchronize_rcu() in bcm_delete_rx_op() with call_rcu() and introduced the RX_NO_AUTOTIMER flag. However, this flag check was omitted for thrtimer in the packet rx fast-path. During BCM RX operation teardown, a concurrent RCU reader (bcm_rx_handler) can race and re-arm thrtimer via bcm_rx_update_and_send() after call_rcu() has been scheduled. Once the RCU grace period elapses, bcm_op is freed. The subsequently firing thrtimer then dereferences the deallocated op, causing a UAF. Adding flag checks to the rx fast-path (bcm_rx_update_and_send) does not fully close the TOCTOU race and introduces latency for every CAN frame. Conversely, calling hrtimer_cancel() directly inside the RCU callback (softirq context) is fatal as hrtimer_cancel() can sleep, triggering a "scheduling while atomic" panic. Resolve this by deferring the timer cancellation and memory free to a dedicated unbound workqueue (bcm_wq). The RCU callback now queues a work item to bcm_wq, which safely cancels both timers and deallocates memory in sleepable process context. A dedicated workqueue is used to prevent system-wide WQ saturation and is cleanly flushed/destroyed on module unload to avoid rmmod page faults. Since the deferred work can now outlive the calling context by an unbounded amount, also take a reference on op->sk when it is assigned and drop it only once the deferred work has cancelled both timers, so a socket can no longer be freed out from under a still-armed timer whose callback (bcm_send_to_user()) dereferences op->sk. Fixes: f1b4e32aca08 ("can: bcm: use call_rcu() instead of costly synchronize_rcu()") Tested-by: Feng Xue Tested-by: Oliver Hartkopp Signed-off-by: Lee Jones Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-1-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 756acfd20c6c2..102d037d4563a 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,8 @@ MODULE_ALIAS("can-proto-2"); #define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex) +static struct workqueue_struct *bcm_wq; + /* * easy access to the first 64 bit of can(fd)_frame payload. cp->data is * 64 bit aligned so the offset has to be multiples of 8 which is ensured @@ -104,6 +107,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset) struct bcm_op { struct list_head list; struct rcu_head rcu; + struct work_struct work; int ifindex; canid_t can_id; u32 flags; @@ -788,9 +792,12 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, return NULL; } -static void bcm_free_op_rcu(struct rcu_head *rcu_head) +static void bcm_free_op_work(struct work_struct *work) { - struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu); + struct bcm_op *op = container_of(work, struct bcm_op, work); + + hrtimer_cancel(&op->timer); + hrtimer_cancel(&op->thrtimer); if ((op->frames) && (op->frames != &op->sframe)) kfree(op->frames); @@ -798,9 +805,23 @@ static void bcm_free_op_rcu(struct rcu_head *rcu_head) if ((op->last_frames) && (op->last_frames != &op->last_sframe)) kfree(op->last_frames); + /* the last possible access to op->timer/op->thrtimer has now + * happened above via hrtimer_cancel() - op->sk is no longer + * needed by any pending timer callback, so drop our reference + */ + sock_put(op->sk); + kfree(op); } +static void bcm_free_op_rcu(struct rcu_head *rcu_head) +{ + struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu); + + INIT_WORK(&op->work, bcm_free_op_work); + queue_work(bcm_wq, &op->work); +} + static void bcm_remove_op(struct bcm_op *op) { hrtimer_cancel(&op->timer); @@ -1055,6 +1076,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; + sock_hold(sk); op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ @@ -1216,6 +1238,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* bcm_can_tx / bcm_tx_timeout_handler needs this */ op->sk = sk; + sock_hold(sk); op->ifindex = ifindex; /* ifindex for timeout events w/o previous frame reception */ @@ -1830,11 +1853,15 @@ static int __init bcm_module_init(void) { int err; + bcm_wq = alloc_workqueue("can-bcm-wq", WQ_UNBOUND, 0); + if (!bcm_wq) + return -ENOMEM; + pr_info("can: broadcast manager protocol\n"); err = register_pernet_subsys(&canbcm_pernet_ops); if (err) - return err; + goto register_pernet_failed; err = register_netdevice_notifier(&canbcm_notifier); if (err) @@ -1852,6 +1879,8 @@ static int __init bcm_module_init(void) unregister_netdevice_notifier(&canbcm_notifier); register_notifier_failed: unregister_pernet_subsys(&canbcm_pernet_ops); +register_pernet_failed: + destroy_workqueue(bcm_wq); return err; } @@ -1860,6 +1889,8 @@ static void __exit bcm_module_exit(void) can_proto_unregister(&bcm_can_proto); unregister_netdevice_notifier(&canbcm_notifier); unregister_pernet_subsys(&canbcm_pernet_ops); + rcu_barrier(); + destroy_workqueue(bcm_wq); } module_init(bcm_module_init); From 35f0ac19efb1a3f6c5e12c00e475a9ec2d9c3a6d Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:24 +0200 Subject: [PATCH 1949/3297] can: bcm: fix lockless bound/ifindex race and silent RX_SETUP failure commit d9b091d9d22fee81ec53fb55d2032951993ceadb upstream. bcm_sendmsg() reads bo->ifindex and checks bo->bound before taking lock_sock(), while bcm_notify(), bcm_connect() and bcm_release() all mutate both fields under that same lock. Because the lockless reads and the locked writes are unordered with respect to each other, a racing bcm_notify() (device unregister) or bcm_connect() (concurrent bind on another thread sharing the socket) can make bcm_sendmsg() observe an inconsistent combination, e.g. a stale bound=1 together with the now-cleared ifindex=0, silently turning a socket bound to a specific CAN interface into one that also matches "any" interface. Keep the lockless bo->bound check purely as a fast-path reject, and move the ifindex read (and a bo->bound re-check) into the locked section, where every writer already serializes. This removes the possibility of observing the two fields torn against each other, rather than trying to fix it with more READ_ONCE()/WRITE_ONCE() pairs on two independently updated fields. Annotate the now-purely-lockless bo->bound accesses consistently across all its write sites. Also fix bcm_rx_setup() silently returning success when the target device disappears concurrently instead of reporting -ENODEV, so a broken RX op is no longer left registered as if it had succeeded. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: Ginger Closes: https://lore.kernel.org/linux-can/CAGp+u1aBK8QVjsvAxM2Ldzep4rEbsP9x_pV3At4g=h1kVEtyhA@mail.gmail.com/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-2-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 65 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 102d037d4563a..984df8764b09d 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1318,6 +1318,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->rx_reg_dev = dev; dev_put(dev); + } else { + /* the requested device is gone - do not + * silently succeed without registering + */ + err = -ENODEV; } } else @@ -1387,12 +1392,13 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); - int ifindex = bo->ifindex; /* default ifindex for this bcm_op */ + int ifindex; struct bcm_msg_head msg_head; int cfsiz; int ret; /* read bytes or error codes as return value */ - if (!bo->bound) + /* Lockless fast-path check for bound socket */ + if (!READ_ONCE(bo->bound)) return -ENOTCONN; /* check for valid message length from userspace */ @@ -1408,17 +1414,38 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if ((size - MHSIZ) % cfsiz) return -EINVAL; + lock_sock(sk); + + /* Re-validate under the socket lock: a concurrent bcm_notify() + * may have unbound this socket (device removal) after the + * lockless fast-path check above. bo->ifindex is only ever + * mutated under lock_sock(), so reading it here - instead of + * before taking the lock - guarantees it can't be observed + * torn against bo->bound. + */ + if (!bo->bound) { + ret = -ENOTCONN; + goto out_release; + } + + /* default ifindex for this bcm_op */ + ifindex = bo->ifindex; + /* check for alternative ifindex for this bcm_op */ if (!ifindex && msg->msg_name) { /* no bound device as default => check msg_name */ DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name); - if (msg->msg_namelen < BCM_MIN_NAMELEN) - return -EINVAL; + if (msg->msg_namelen < BCM_MIN_NAMELEN) { + ret = -EINVAL; + goto out_release; + } - if (addr->can_family != AF_CAN) - return -EINVAL; + if (addr->can_family != AF_CAN) { + ret = -EINVAL; + goto out_release; + } /* ifindex from sendto() */ ifindex = addr->can_ifindex; @@ -1427,20 +1454,21 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) struct net_device *dev; dev = dev_get_by_index(sock_net(sk), ifindex); - if (!dev) - return -ENODEV; + if (!dev) { + ret = -ENODEV; + goto out_release; + } if (dev->type != ARPHRD_CAN) { dev_put(dev); - return -ENODEV; + ret = -ENODEV; + goto out_release; } dev_put(dev); } } - lock_sock(sk); - switch (msg_head.opcode) { case TX_SETUP: @@ -1490,6 +1518,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) break; } +out_release: release_sock(sk); return ret; @@ -1526,7 +1555,12 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, bo->bcm_proc_read = NULL; } #endif - bo->bound = 0; + /* Paired with the lockless fast-path check in + * bcm_sendmsg(); bo->ifindex itself is only ever + * accessed under lock_sock() so it needs no + * annotation. + */ + WRITE_ONCE(bo->bound, 0); bo->ifindex = 0; notify_enodev = 1; } @@ -1667,7 +1701,7 @@ static int bcm_release(struct socket *sock) /* remove device reference */ if (bo->bound) { - bo->bound = 0; + WRITE_ONCE(bo->bound, 0); bo->ifindex = 0; } @@ -1737,7 +1771,10 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, } #endif /* CONFIG_PROC_FS */ - bo->bound = 1; + /* bo->ifindex above is fully assigned before this point; pairs + * with the lockless fast-path check in bcm_sendmsg() + */ + WRITE_ONCE(bo->bound, 1); fail: release_sock(sk); From b06a4a397ac826603f39875cb7c7819a41365196 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:27 +0200 Subject: [PATCH 1950/3297] can: bcm: add missing rcu list annotations and operations commit 7b2c3eabc4dafc062a25e10711154f2107526a78 upstream. sashiko-bot remarked the missing use of list_add_rcu() in bcm_[rx|tx]_setup() to have a proper initialized bcm_op structure when bcm_proc_show() traverses the bcm_op's under rcu_read_lock(). To cover all initial settings of the bcm_op's the list_add_rcu() calls are moved to the end of the setup code. While at it, also fix the mirroring removal side: bcm_release() called bcm_remove_op() - which frees the op via call_rcu() - on ops that were still linked in bo->tx_ops/bo->rx_ops, without list_del_rcu() first. Unlink each op with list_del_rcu() before handing it to bcm_remove_op(), matching the existing pattern in bcm_delete_tx_op()/bcm_delete_rx_op(). Reported-by: sashiko-reviews@lists.linux.dev Closes: https://lore.kernel.org/linux-can/20260610094654.A1FFE1F00893@smtp.kernel.org/ Fixes: dac5e6249159 ("can: bcm: add missing rcu read protection for procfs content") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-5-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 984df8764b09d..a40519638a262 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -260,7 +260,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) (reduction == 100) ? "near " : "", reduction); } - list_for_each_entry(op, &bo->tx_ops, list) { + list_for_each_entry_rcu(op, &bo->tx_ops, list) { seq_printf(m, "tx_op: %03X %s ", op->can_id, bcm_proc_getifname(net, ifname, op->ifindex)); @@ -946,6 +946,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; struct canfd_frame *cf; + bool add_op_to_list = false; unsigned int i; int err; @@ -1087,8 +1088,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the tx_ops */ - list_add(&op->list, &bo->tx_ops); + add_op_to_list = true; } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */ @@ -1110,6 +1110,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->flags |= TX_ANNOUNCE; } + /* add this bcm_op to the list of the tx_ops? */ + if (add_op_to_list) + list_add_rcu(&op->list, &bo->tx_ops); + if (op->flags & TX_ANNOUNCE) bcm_can_tx(op); @@ -1250,9 +1254,6 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_setup(&op->thrtimer, bcm_rx_thr_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - /* add this bcm_op to the list of the rx_ops */ - list_add(&op->list, &bo->rx_ops); - /* call can_rx_register() */ do_rx_register = 1; @@ -1331,10 +1332,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, bcm_rx_handler, op, "bcm", sk); if (err) { /* this bcm rx op is broken -> remove it */ - list_del_rcu(&op->list); bcm_remove_op(op); return err; } + + /* add this bcm_op to the list of the rx_ops */ + list_add_rcu(&op->list, &bo->rx_ops); } return msg_head->nframes * op->cfsiz + MHSIZ; @@ -1664,8 +1667,10 @@ static int bcm_release(struct socket *sock) remove_proc_entry(bo->procname, net->can.bcmproc_dir); #endif /* CONFIG_PROC_FS */ - list_for_each_entry_safe(op, next, &bo->tx_ops, list) + list_for_each_entry_safe(op, next, &bo->tx_ops, list) { + list_del_rcu(&op->list); bcm_remove_op(op); + } list_for_each_entry_safe(op, next, &bo->rx_ops, list) { /* @@ -1696,8 +1701,10 @@ static int bcm_release(struct socket *sock) synchronize_rcu(); - list_for_each_entry_safe(op, next, &bo->rx_ops, list) + list_for_each_entry_safe(op, next, &bo->rx_ops, list) { + list_del_rcu(&op->list); bcm_remove_op(op); + } /* remove device reference */ if (bo->bound) { From 0993dc5fc619c0b25ab1310cb11d65e78351c0fe Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Mon, 22 Jun 2026 23:01:22 +0000 Subject: [PATCH 1951/3297] bpf: Reset register bounds before narrowing retval range in check_mem_access() commit 5e0b273e0a62cc04ec338c7b502797c66c2ed42a upstream. When the BPF verifier processes a context load of an LSM hook return value, it calls __mark_reg_s32_range() to narrow the register to the hook's valid range. However, __mark_reg_s32_range() intersects the new range with the register's existing bounds using max_t()/min_t() rather than replacing them. If the destination register carries stale bounds from a prior instruction (e.g. BPF_MOV64_IMM), the intersection can produce a range narrower than reality. The verifier then believes it knows the register's exact value, while at runtime the actual hook return value is loaded, creating a verifier/runtime mismatch that can be used to bypass BPF memory safety checks. The else branch already calls mark_reg_unknown() to reset register state before any narrowing. Apply the same reset in the is_retval path so stale bounds are cleared before __mark_reg_s32_range() intersects. Fixes: 5d99e198be27 ("bpf, lsm: Add check for BPF LSM return value") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Acked-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260622230123.3695446-2-tristmd@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 99fa175e8ef89..53605f4b28d98 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7672,6 +7672,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn */ if (info.reg_type == SCALAR_VALUE) { if (info.is_retval && get_func_retval_range(env->prog, &range)) { + mark_reg_unknown(env, regs, value_regno); err = __mark_reg_s32_range(env, regs, value_regno, range.minval, range.maxval); if (err) From c4f626ddf2350652ad2f79daf1f10847f3f6eabd Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 26 Jun 2026 17:52:52 +0200 Subject: [PATCH 1952/3297] bpf,fork: wipe ->bpf_storage before bailouts that access it commit 9b51a6155d14389876916726430da30eabb1d4ed upstream. Currently, copy_process() can bail out to free_task() before p->bpf_storage has been initialized, with this call graph (shown here for the !CONFIG_MEMCG case): copy_process dup_task_struct arch_dup_task_struct [copies the entire task_struct, including ->bpf_storage member] [RLIMIT_NPROC check fails] delayed_free_task free_task bpf_task_storage_free rcu_dereference(task->bpf_storage) bpf_local_storage_destroy In this case, the nascent task's ->bpf_storage member that bpf_local_storage_destroy() operates on is a plain copy of the parent's ->bpf_storage pointer, not a real initialized pointer. This leads to badness (kernel hangs, UAF). This is reachable as long as the process calling fork() has been inserted into a task storage map. Cc: stable@kernel.org Fixes: a10787e6d58c ("bpf: Enable task local storage for tracing programs") Signed-off-by: Jann Horn Signed-off-by: Andrii Nakryiko Signed-off-by: Greg Kroah-Hartman --- kernel/fork.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 8b1238d692916..91f1512a1d312 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -961,6 +961,11 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) tsk->mm_cid_active = 0; tsk->migrate_from_cpu = -1; #endif + +#ifdef CONFIG_BPF_SYSCALL + RCU_INIT_POINTER(tsk->bpf_storage, NULL); + tsk->bpf_ctx = NULL; +#endif return tsk; free_stack: @@ -2143,10 +2148,6 @@ __latent_entropy struct task_struct *copy_process( p->sequential_io = 0; p->sequential_io_avg = 0; #endif -#ifdef CONFIG_BPF_SYSCALL - RCU_INIT_POINTER(p->bpf_storage, NULL); - p->bpf_ctx = NULL; -#endif unwind_task_init(p); From 0b6252afcd1965f77c6a6f8a802a2e1381822b98 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 16 Jun 2026 10:30:56 +0200 Subject: [PATCH 1953/3297] bpf: Add missing access_ok call to copy_user_syms commit d5dc200c3a3f217de072af269dd90adddf90e48d upstream. As reported by sashiko we use __get_user without prior access_ok call on the user space pointer. Adding the missing call for the whole pointer array. Plus removing the err check in the error path, because it's not needed and also we can return -ENOMEM directly from the first kvmalloc_array fail path. Cc: stable@vger.kernel.org [1] https://lore.kernel.org/bpf/20260611115503.AC16D1F00893@smtp.kernel.org/ Fixes: 0236fec57a15 ("bpf: Resolve symbols with ftrace_lookup_symbols for kprobe multi link") Reported-by: Sashiko Closes: https://lore.kernel.org/bpf/20260611115503.AC16D1F00893@smtp.kernel.org/ Signed-off-by: Jiri Olsa Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/r/20260616083056.405652-1-jolsa@kernel.org Signed-off-by: Alexei Starovoitov Signed-off-by: Greg Kroah-Hartman --- kernel/trace/bpf_trace.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 88f470b31375a..22d779ae345d0 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2318,9 +2318,12 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 int err = -ENOMEM; unsigned int i; + if (!access_ok(usyms, cnt * sizeof(*usyms))) + return -EFAULT; + syms = kvmalloc_array(cnt, sizeof(*syms), GFP_KERNEL); if (!syms) - goto error; + return -ENOMEM; buf = kvmalloc_array(cnt, KSYM_NAME_LEN, GFP_KERNEL); if (!buf) @@ -2345,10 +2348,8 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 return 0; error: - if (err) { - kvfree(syms); - kvfree(buf); - } + kvfree(syms); + kvfree(buf); return err; } From af382ffca93e52d2c2c1d40f4635e351e296dbab Mon Sep 17 00:00:00 2001 From: Connor Williamson Date: Mon, 15 Jun 2026 13:07:15 +0000 Subject: [PATCH 1954/3297] block: remove redundant GD_NEED_PART_SCAN in add_disk_final() commit 181bb9c9eae4f69fe510a62a42c2932d0314a800 upstream. add_disk_final() sets GD_NEED_PART_SCAN before calling bdev_add(), then calls disk_scan_partitions() which sets the flag itself. The early set is redundant and introduces a race. Between bdev_add() and disk_scan_partitions(), concurrent openers (multipathd, blkid, LVM) see the flag in blkdev_get_whole() and trigger bdev_disk_changed(). When disk_scan_partitions() then runs, it calls bdev_disk_changed() again, dropping the partitions the concurrent opener already created before re-adding them, which can result in transient partition disappearances. The race is observable by inserting an msleep() between bdev_add() and disk_scan_partitions() while running concurrent open() calls during device bind. Without artificial delay, it manifests under scheduling pressure during boot on systems with aggressive device scanners (multipathd, systemd-udevd). Therefore, do not set GD_NEED_PART_SCAN in add_disk_final(). Other GD_NEED_PART_SCAN consumers (blkdev_get_whole(), sd_need_revalidate()) should not be affected as the flag is set internally by disk_scan_partitions(). The retry-on-next-open intention from commit e5cfefa97bcc ("block: fix scan partition for exclusively open device again") should also not be affected as the early return paths in disk_scan_partitions() should be unreachable at device registration time (bd_holder is NULL and open_partitions is zero). Fixes: e5cfefa97bcc ("block: fix scan partition for exclusively open device again") Cc: stable@vger.kernel.org Signed-off-by: Connor Williamson Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260615130715.53693-1-connordw@amazon.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/genhd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index bd3a6841e5b59..d23cb33ea1276 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -407,10 +407,6 @@ static void add_disk_final(struct gendisk *disk) struct device *ddev = disk_to_dev(disk); if (!(disk->flags & GENHD_FL_HIDDEN)) { - /* Make sure the first partition scan will be proceed */ - if (get_capacity(disk) && disk_has_partscan(disk)) - set_bit(GD_NEED_PART_SCAN, &disk->state); - bdev_add(disk->part0, ddev->devt); if (get_capacity(disk)) disk_scan_partitions(disk, BLK_OPEN_READ); From 2977b5fe401c1812640c2abf04514c4856a3647c Mon Sep 17 00:00:00 2001 From: Mike Waychison Date: Wed, 15 Jul 2026 15:29:50 -0400 Subject: [PATCH 1955/3297] block: fix race in blk_time_get_ns() returning 0 commit 691b052139c94ee6640ac39e0b764dd3867897c0 upstream. blk_time_get_ns() populates the per-plug cached timestamp and then returns it by re-reading the field: if (!plug->cur_ktime) { plug->cur_ktime = ktime_get_ns(); current->flags |= PF_BLOCK_TS; } return plug->cur_ktime; This is problematic when the compiler emits the final "return plug->cur_ktime" as a reload from memory, after PF_BLOCK_TS has already been set. Since the cached timestamp is now invalidated from finish_task_switch() (fad156c2af22 "block: invalidate cached plug timestamp after task switch"), a task preempted between setting PF_BLOCK_TS and that reload has plug->cur_ktime zeroed by blk_plug_invalidate_ts() when it is scheduled back in. The reload then returns 0. A 0 handed back here is stored as a start timestamp -- e.g. blk_account_io_start() writes it to rq->start_time_ns -- and later subtracted from "now". blk_account_io_done() then adds (now - 0), i.e. roughly the system uptime, to the per-group nsecs[] counters. On an otherwise idle, healthy device this appears as sudden ~uptime-sized jumps in the diskstats time fields (write_ticks/discard_ticks/time_in_queue). The solution is to be explicit in our reads and writes to this field that is preemption volatile. We also add a barrier() to ensure that any setting of PF_BLOCK_TS is ordered to happen after the cur_ktime update. This issue was discovered using AI-assisted kprobes looking for paths that were leaking zeroed timestamps in a live system, based on the observation that we were sometimes seeing uptime-sized jumps in kernel exported counters. This was flagged by NodeDiskIOSaturation prometheus alerts that started firing on all hosts post 7.1.3 kernel upgrade, due to node-exporter now exporting a nonsensical node_disk_io_time_weighted_seconds_total. Fixes: fad156c2af22 ("block: invalidate cached plug timestamp after task switch") Cc: stable@vger.kernel.org Signed-off-by: Mike Waychison Assisted-by: Claude:claude-opus-4.8 Link: https://patch.msgid.link/20260715192950.2488921-1-mike@waychison.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/block/blk.h b/block/blk.h index 59bd91a4acc4d..2810b4d086e6a 100644 --- a/block/blk.h +++ b/block/blk.h @@ -699,6 +699,7 @@ static inline int req_ref_read(struct request *req) static inline u64 blk_time_get_ns(void) { struct blk_plug *plug = current->plug; + u64 now; if (!plug || !in_task()) return ktime_get_ns(); @@ -707,12 +708,18 @@ static inline u64 blk_time_get_ns(void) * 0 could very well be a valid time, but rather than flag "this is * a valid timestamp" separately, just accept that we'll do an extra * ktime_get_ns() if we just happen to get 0 as the current time. + * + * cur_ktime can be zeroed by pre-emption the moment PF_BLOCK_TS is set. */ - if (!plug->cur_ktime) { - plug->cur_ktime = ktime_get_ns(); + now = READ_ONCE(plug->cur_ktime); + if (!now) { + now = ktime_get_ns(); + WRITE_ONCE(plug->cur_ktime, now); + /* Ensure PF_BLOCK_TS is set after cur_ktime. */ + barrier(); current->flags |= PF_BLOCK_TS; } - return plug->cur_ktime; + return now; } static inline ktime_t blk_time_get(void) From ffd17a393921af784f9b2391d6be8b135162fe9f Mon Sep 17 00:00:00 2001 From: Yitang Yang Date: Tue, 16 Jun 2026 23:51:29 +0800 Subject: [PATCH 1956/3297] block: fix IORING_URING_CMD_REISSUE flags check in blkdev_uring_cmd commit 4f919141be38ea2b1314e3a531b7b998eb64e8bc upstream. blkdev_uring_cmd() checks IORING_URING_CMD_REISSUE to determine whether this is the first issue. However, this flag lives in cmd->flags instead of issue_flags. Coincidentally, IO_URING_F_NONBLOCK shares bit 31 with IORING_URING_CMD_REISSUE. As a result, the SQE read was never performed, bic->len remained zero, and every BLOCK_URING_CMD_DISCARD failed with -EINVAL. Fix it by checking cmd->flags as intended. Cc: stable@vger.kernel.org Fixes: 212ec34e4e72 ("block: only read from sqe on initial invocation of blkdev_uring_cmd") Signed-off-by: Yitang Yang Link: https://patch.msgid.link/20260616155129.406057-1-yi1tang.yang@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/ioctl.c b/block/ioctl.c index 08e4981506cbc..801f904b5af47 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -857,7 +857,7 @@ int blkdev_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) u32 cmd_op = cmd->cmd_op; /* Read what we need from the SQE on the first issue */ - if (!(issue_flags & IORING_URING_CMD_REISSUE)) { + if (!(cmd->flags & IORING_URING_CMD_REISSUE)) { const struct io_uring_sqe *sqe = cmd->sqe; if (unlikely(sqe->ioprio || sqe->__pad1 || sqe->len || From 8a3c44a003176282ee4306b7c96e5a536c6f0707 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 19:57:14 +0800 Subject: [PATCH 1957/3297] net: sparx5: unregister blocking notifier on init failure commit 483be61b4a9a6df3b7cb277e8f189e082dee4cb8 upstream. sparx5_register_notifier_blocks() registers the switchdev blocking notifier before allocating the ordered workqueue. If the workqueue allocation fails, the error path unregisters the switchdev and netdevice notifiers, but leaves the blocking notifier registered. Add a separate error label for the workqueue allocation failure path and unregister the switchdev blocking notifier there. Fixes: d6fce5141929 ("net: sparx5: add switching support") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260623115714.2192074-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c index 0a71abbd3da58..391ce604e6611 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c @@ -765,11 +765,13 @@ int sparx5_register_notifier_blocks(struct sparx5 *s5) sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0); if (!sparx5_owq) { err = -ENOMEM; - goto err_switchdev_blocking_nb; + goto err_alloc_workqueue; } return 0; +err_alloc_workqueue: + unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb); err_switchdev_blocking_nb: unregister_switchdev_notifier(&s5->switchdev_nb); err_switchdev_nb: From ac2136dc4441dc71cc6ebc35b68fcd764931a6dc Mon Sep 17 00:00:00 2001 From: Genjian Zhang Date: Sat, 11 Jul 2026 18:05:26 +0800 Subject: [PATCH 1958/3297] dm thin metadata: fix superblock refcount leak on snapshot shadow failure commit 4b22d0801fadfcae2e106e6ba32e49439c7c7ebf upstream. __reserve_metadata_snap() increments THIN_SUPERBLOCK_LOCATION in the metadata space map before shadowing it. When dm_tm_shadow_block() fails, a reference is leaked in the metadata space map. Fix by adding the missing dm_sm_dec_block(). Signed-off-by: Genjian Zhang Signed-off-by: Mikulas Patocka Fixes: cc8394d86f04 ("dm thin: provide userspace access to pool metadata") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index f90679cfec5bb..8a0890a7428d3 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1353,8 +1353,10 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); r = dm_tm_shadow_block(pmd->tm, THIN_SUPERBLOCK_LOCATION, &sb_validator, ©, &inc); - if (r) + if (r) { + dm_sm_dec_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION); return r; + } BUG_ON(!inc); From 7f76245960a332f39b08cc556e675d1765dc5bbb Mon Sep 17 00:00:00 2001 From: Ming-Hung Tsai Date: Tue, 30 Jun 2026 20:17:44 +0800 Subject: [PATCH 1959/3297] dm thin metadata: fix metadata snapshot consistency on commit failure commit 5bcd4d3058ebaf46ad2e163829d87dd4870c7a45 upstream. __reserve_metadata_snap() and __release_metadata_snap() modify the superblock's held_root directly in the block_manager's buffer. If the subsequent metadata commit fails, the held_root gets flushed to disk through the abort_transaction path, resulting in inconsistent metadata. Reproducer 1: __reserve_metadata_snap() 1. Create a 2 MiB metadata device and make the region after the 14th block inaccessible, to trigger metadata commit failure in the subsequent reserve_metadata_snap operation. The 14th block will be the shadow destination for the index block. dmsetup create tmeta --table "0 112 linear /dev/sdc 0 112 3984 error" 2. Create a 16 MiB thin-pool dmsetup create tdata --table "0 32768 zero" dd if=/dev/zero of=/dev/mapper/tmeta bs=4k count=1 dmsetup create tpool --table "0 32768 thin-pool /dev/mapper/tmeta \ /dev/mapper/tdata 128 0 1 skip_block_zeroing" 3. Take a metadata snapshot to trigger metadata commit failure and transaction abort. However, the held_root is written to disk, breaking metadata consistency. dmsetup message tpool 0 "reserve_metadata_snap" thin_check v1.2.2 result: Bad reference count for metadata block 6. Expected 2, but space map contains 1. Bad reference count for metadata block 7. Expected 2, but space map contains 1. Bad reference count for metadata block 13. Expected 1, but space map contains 0. Reproducer 2: __release_metadata_snap() 1. Create a 2 MiB metadata device and make the region after the 16th block inaccessible, to trigger metadata commit failure in the subsequent release_metadata_snap operation. The 16th block will be the shadow destination for the index block. dmsetup create tmeta --table "0 128 linear /dev/sdc 0 128 3968 error" 2. Create a 16 MiB thin-pool dmsetup create tdata --table "0 32768 zero" dd if=/dev/zero of=/dev/mapper/tmeta bs=4k count=1 dmsetup create tpool --table "0 32768 thin-pool /dev/mapper/tmeta \ /dev/mapper/tdata 128 0 1 skip_block_zeroing" 3. Reserve then release the metadata snapshot, to trigger metadata commit failure and transaction abort. The held_root gets removed from the on-disk superblock, causing inconsistent metadata. dmsetup message tpool 0 "reserve_metadata_snap" dmsetup message tpool 0 "release_metadata_snap" thin_check v1.2.2 result: Bad reference count for metadata block 6. Expected 1, but space map contains 2. Bad reference count for metadata block 7. Expected 1, but space map contains 2. 1 metadata blocks have leaked. Fix by deferring the held_root update to commit time. Additionally, move the existing-snapshot check in __reserve_metadata_snap before the shadow operation to avoid unnecessary work. In __release_metadata_snap, clear pmd->held_root before btree deletion so partial failure leaks blocks rather than leaving a stale reference, and unlock the snapshot block before decrementing its refcount. Fixes: 991d9fa02da0 ("dm: add thin provisioning target") Cc: stable@vger.kernel.org Signed-off-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 63 ++++++++++------------------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 8a0890a7428d3..9224d91fca71f 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -186,6 +186,7 @@ struct dm_pool_metadata { uint32_t time; dm_block_t root; dm_block_t details_root; + dm_block_t held_root; struct list_head thin_devices; uint64_t trans_id; unsigned long flags; @@ -748,6 +749,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd) */ pmd->root = le64_to_cpu(disk_super->data_mapping_root); pmd->details_root = le64_to_cpu(disk_super->device_details_root); + pmd->held_root = le64_to_cpu(disk_super->held_root); __setup_btree_details(pmd); dm_bm_unlock(sblock); @@ -838,6 +840,7 @@ static int __begin_transaction(struct dm_pool_metadata *pmd) pmd->time = le32_to_cpu(disk_super->time); pmd->root = le64_to_cpu(disk_super->data_mapping_root); pmd->details_root = le64_to_cpu(disk_super->device_details_root); + pmd->held_root = le64_to_cpu(disk_super->held_root); pmd->trans_id = le64_to_cpu(disk_super->trans_id); pmd->flags = le32_to_cpu(disk_super->flags); pmd->data_block_size = le32_to_cpu(disk_super->data_block_size); @@ -928,6 +931,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd) disk_super->time = cpu_to_le32(pmd->time); disk_super->data_mapping_root = cpu_to_le64(pmd->root); disk_super->device_details_root = cpu_to_le64(pmd->details_root); + disk_super->held_root = cpu_to_le64(pmd->held_root); disk_super->trans_id = cpu_to_le64(pmd->trans_id); disk_super->flags = cpu_to_le32(pmd->flags); @@ -1333,9 +1337,14 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) { int r, inc; struct thin_disk_superblock *disk_super; - struct dm_block *copy, *sblock; + struct dm_block *copy; dm_block_t held_root; + if (pmd->held_root) { + DMWARN("Pool metadata snapshot already exists: release this before taking another."); + return -EBUSY; + } + /* * We commit to ensure the btree roots which we increment in a * moment are up to date. @@ -1363,14 +1372,6 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) held_root = dm_block_location(copy); disk_super = dm_block_data(copy); - if (le64_to_cpu(disk_super->held_root)) { - DMWARN("Pool metadata snapshot already exists: release this before taking another."); - - dm_tm_dec(pmd->tm, held_root); - dm_tm_unlock(pmd->tm, copy); - return -EBUSY; - } - /* * Wipe the spacemap since we're not publishing this. */ @@ -1386,18 +1387,8 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd) dm_tm_inc(pmd->tm, le64_to_cpu(disk_super->device_details_root)); dm_tm_unlock(pmd->tm, copy); - /* - * Write the held root into the superblock. - */ - r = superblock_lock(pmd, &sblock); - if (r) { - dm_tm_dec(pmd->tm, held_root); - return r; - } + pmd->held_root = held_root; - disk_super = dm_block_data(sblock); - disk_super->held_root = cpu_to_le64(held_root); - dm_bm_unlock(sblock); return 0; } @@ -1417,18 +1408,10 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) { int r; struct thin_disk_superblock *disk_super; - struct dm_block *sblock, *copy; + struct dm_block *copy; dm_block_t held_root; - r = superblock_lock(pmd, &sblock); - if (r) - return r; - - disk_super = dm_block_data(sblock); - held_root = le64_to_cpu(disk_super->held_root); - disk_super->held_root = cpu_to_le64(0); - - dm_bm_unlock(sblock); + held_root = pmd->held_root; if (!held_root) { DMWARN("No pool metadata snapshot found: nothing to release."); @@ -1439,13 +1422,15 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) if (r) return r; + pmd->held_root = 0; + disk_super = dm_block_data(copy); dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root)); dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root)); - dm_sm_dec_block(pmd->metadata_sm, held_root); - dm_tm_unlock(pmd->tm, copy); + dm_sm_dec_block(pmd->metadata_sm, held_root); + return 0; } @@ -1464,19 +1449,7 @@ int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd) static int __get_metadata_snap(struct dm_pool_metadata *pmd, dm_block_t *result) { - int r; - struct thin_disk_superblock *disk_super; - struct dm_block *sblock; - - r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); - if (r) - return r; - - disk_super = dm_block_data(sblock); - *result = le64_to_cpu(disk_super->held_root); - - dm_bm_unlock(sblock); + *result = pmd->held_root; return 0; } From 1fcb5e29dd7a5b85adb9d8b539911741d878e829 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:27:35 +0000 Subject: [PATCH 1960/3297] dm era: fix out-of-bounds memory access for non-zero start sector commit a868196f03c2b19418ae3d2b69e195d668a271e5 upstream. dm-era tracks writes in target-relative blocks, but era_map() calculates the writeset block before applying the target offset. Tables with a non-zero start sector can therefore pass an absolute mapped-device block to metadata_current_marked(). If the absolute block is beyond the current writeset size, writeset_marked() tests past the end of the in-core bitset. KASAN reports this as a vmalloc-out-of-bounds access. Apply the target offset before calculating the era block so writeset lookups use the target-relative block number. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Ming-Hung Tsai Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Fixes: eec40579d848 ("dm: add era target") Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-era-target.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 5cc71dbc7e039..883d1549cfc0e 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1231,6 +1231,7 @@ static dm_block_t get_block(struct era *era, struct bio *bio) static void remap_to_origin(struct era *era, struct bio *bio) { bio_set_dev(bio, era->origin_dev->bdev); + bio->bi_iter.bi_sector = dm_target_offset(era->ti, bio->bi_iter.bi_sector); } /* @@ -1562,7 +1563,7 @@ static void era_dtr(struct dm_target *ti) static int era_map(struct dm_target *ti, struct bio *bio) { struct era *era = ti->private; - dm_block_t block = get_block(era, bio); + dm_block_t block; /* * All bios get remapped to the origin device. We do this now, but @@ -1570,6 +1571,7 @@ static int era_map(struct dm_target *ti, struct bio *bio) * block is marked in this era. */ remap_to_origin(era, bio); + block = get_block(era, bio); /* * REQ_PREFLUSH bios carry no data, so we're not interested in them. From 021dab70eb37dd34e9a2742667b7ef522fb00208 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:32:49 +0200 Subject: [PATCH 1961/3297] dm-bufio: fix wrong count calculation in dm_bufio_issue_discard commit 422f1d4f141eaa3a6e4199ceec86cc6b9bf26570 upstream. block_to_sector converts a block number to a sector number and adds c->start to the result. It is inappropriate to use this function for converting the number of blocks to a number to sectors because c->start would be incorrectly added to the result. Luckily, the only target that uses dm_bufio_issue_discard is dm-ebs, which sets c->start to 0, so this bug is latent. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: 6fbeb0048e6b ("dm bufio: implement discard") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-bufio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index f41f649c01d4e..b7cbe58740947 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -2215,7 +2215,9 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c struct dm_io_region io_reg = { .bdev = c->bdev, .sector = block_to_sector(c, block), - .count = block_to_sector(c, count), + .count = likely(c->sectors_per_block_bits >= 0) ? + count << c->sectors_per_block_bits : + count * (c->block_size >> SECTOR_SHIFT), }; if (WARN_ON_ONCE(dm_bufio_in_request())) From d61c12573ed9768690fdcb2bc38846a1bcb01358 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:31:47 +0200 Subject: [PATCH 1962/3297] dm-ioctl: fix a possible overflow in list_version_get_info commit 76c6f845dc0c614304a6e6ee619b552f97cf24b3 upstream. sizeof(tt->version) is 12 bytes, but the code writes 16 bytes into the output buffer - info->vers->version[0], info->vers->version[1], info->vers->version[2] and info->vers->next. This can cause buffer overflow. Fix this buffer overflow by replacing "sizeof(tt->version)" with "sizeof(struct dm_target_versions)". Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 128dd1a939254..006f60650249b 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -683,7 +683,7 @@ static void list_version_get_info(struct target_type *tt, void *param) struct vers_iter *info = param; /* Check space - it might have changed since the first iteration */ - if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) { + if ((char *)info->vers + sizeof(struct dm_target_versions) + strlen(tt->name) + 1 > info->end) { info->flags = DM_BUFFER_FULL_FLAG; return; } From e0b0163a65758ec3a2361ae1cea407898c27f21e Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Thu, 2 Jul 2026 21:43:39 -0400 Subject: [PATCH 1963/3297] dm-log: fix a bitset_size overflow on 32bit machines commit 9743132a41f4d9d0e54c5f2adcb821b04796bab1 upstream. Commit c20e36b7631d ("dm log: fix out-of-bounds write due to region_count overflow") made sure that region_count could fit in an unsigned int. But the bitmap memory isn't allocated based on region_count. It uses bitset_size (a size_t variable). The first step of calculating bitset_size is to set it to region_count, rounded up to a multiple of BITS_PER_LONG. If region_size is less than BITS_PER_LONG smaller than UINT_MAX, it will get rounded up to 2^32. On a 32bit architecture, this will make bitset_size wrap around to 0 and fail, despite region_count being valid. Since bitset_size gets divided by 8, it can hold any valid region_count. It just needs a special case to handle the rollover. If it is 0, the value rolled over, and bitset size should be set to the number of bytes needed to hold 2^32 bits. Signed-off-by: Benjamin Marzinski Signed-off-by: Mikulas Patocka Fixes: c20e36b7631d ("dm log: fix out-of-bounds write due to region_count overflow") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 4a1369b8f44a0..c8a1f9b6385fc 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -425,6 +425,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, */ bitset_size = dm_round_up(region_count, BITS_PER_LONG); bitset_size >>= BYTE_SHIFT; + /* Handle dm_round_up rollover on 32-bit systems */ + if (!bitset_size) + bitset_size = 1UL << (BITS_PER_LONG - BYTE_SHIFT); lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits); From 1247615aadb74c89c1b2f01a6de7df9dab92ecb3 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 29 Jun 2026 15:47:40 +0000 Subject: [PATCH 1964/3297] dm-pcache: reject option groups without values commit d9c631e3fbd44246a2be781d26cfacbb9b8ec127 upstream. The pcache target parses optional arguments as name/value pairs. A table that advertises one optional argument and supplies only a recognized option name, for example "cache_mode", reaches parse_cache_opts() with argc == 1. The parser consumes the name, decrements argc to zero, then calls dm_shift_arg() again for the value. dm_shift_arg() returns NULL when no arguments remain, and the following strcmp() dereferences that NULL pointer. Check that each recognized option has a value before consuming it. This keeps valid "cache_mode writeback" and "data_crc true/false" tables unchanged while making malformed tables fail during target construction with a precise missing-value error. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Reviewed-by: Zheng Gu Signed-off-by: Mikulas Patocka Fixes: 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-pcache/dm_pcache.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c index e5f5936fa6f08..87bcc1c515a6f 100644 --- a/drivers/md/dm-pcache/dm_pcache.c +++ b/drivers/md/dm-pcache/dm_pcache.c @@ -168,6 +168,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as, argc--; if (!strcmp(arg, "cache_mode")) { + if (!argc) { + *error = "Missing value for cache_mode"; + return -EINVAL; + } arg = dm_shift_arg(as); if (!strcmp(arg, "writeback")) { opts->cache_mode = PCACHE_CACHE_MODE_WRITEBACK; @@ -177,6 +181,10 @@ static int parse_cache_opts(struct dm_pcache *pcache, struct dm_arg_set *as, } argc--; } else if (!strcmp(arg, "data_crc")) { + if (!argc) { + *error = "Missing value for data_crc"; + return -EINVAL; + } arg = dm_shift_arg(as); if (!strcmp(arg, "true")) { opts->data_crc = true; From 461d36b5ddafc4b851ddb54486f8170fa281aebe Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:37:15 +0200 Subject: [PATCH 1965/3297] dm-stats: fix dm_jiffies_to_msec64 commit 386df1a57b631c456d14f857cb0c0c2e11c16bef upstream. There were wrong calculations in dm_jiffies_to_msec64 that produced incorrect output when HZ was different from 1000. This commit fixes them. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: fd2ed4d25270 ("dm: add statistics support") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-stats.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 1e5d988f44da6..c944c001f1e2d 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -842,10 +842,10 @@ static unsigned long long dm_jiffies_to_msec64(struct dm_stat *s, unsigned long result = jiffies_to_msecs(j & 0x3fffff); if (j >= 1 << 22) { mult = jiffies_to_msecs(1 << 22); - result += (unsigned long long)mult * (unsigned long long)jiffies_to_msecs((j >> 22) & 0x3fffff); + result += (unsigned long long)mult * ((j >> 22) & 0x3fffff); } if (j >= 1ULL << 44) - result += (unsigned long long)mult * (unsigned long long)mult * (unsigned long long)jiffies_to_msecs(j >> 44); + result += (unsigned long long)mult * (unsigned long long)(1 << 22) * (j >> 44); return result; } From ee458c3c18343ea18ea8eda357581366b8304598 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:35:49 +0200 Subject: [PATCH 1966/3297] dm-stats: fix merge accounting commit 1917eb2db750ecbdf710f79a8042eaa545a063c7 upstream. There were wrong parentheses when setting stats_aux->merged, so that merging was never properly accounted. This commit fixes it. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: fd2ed4d25270 ("dm: add statistics support") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-stats.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index c944c001f1e2d..dbff4922e0682 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -692,10 +692,8 @@ void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw, */ last = raw_cpu_ptr(stats->last); stats_aux->merged = - (bi_sector == (READ_ONCE(last->last_sector) && - ((bi_rw == WRITE) == - (READ_ONCE(last->last_rw) == WRITE)) - )); + bi_sector == READ_ONCE(last->last_sector) && + (bi_rw == WRITE) == (READ_ONCE(last->last_rw) == WRITE); WRITE_ONCE(last->last_sector, end_sector); WRITE_ONCE(last->last_rw, bi_rw); } else From 92e3c93d60be1f2425738cd66dbadac7d6bc0cd1 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:29:11 +0200 Subject: [PATCH 1967/3297] dm_early_create: fix freeing used table on dm_resume failure commit 366665416f20527ff7cad548a32d1ddf23195740 upstream. If dm_resume fails, the kernel attempts to free table with dm_table_destroy, but the table was already instantiated with dm_swap_table. This commit skips the call to dm_table_destroy in this case. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 6bbc923dfcf5 ("dm: add support to directly boot to a mapped device") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 006f60650249b..f8d048ece9822 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -2341,7 +2341,7 @@ int __init dm_early_create(struct dm_ioctl *dmi, /* resume device */ r = dm_resume(md); if (r) - goto err_destroy_table; + goto err_hash_remove; DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name); dm_put(md); From 0c4e9bb1d4101030f55c869f18bd3ece39a77bbb Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:22:47 +0200 Subject: [PATCH 1968/3297] dm-integrity: fix leaking uninitialized kernel memory commit 7bb03b2b01b814a9fc14afbfc2cbb2cca5b34750 upstream. If hash size is less than device's tuple size, dm-integrity is supposed to zero the remaining space. There was a bug in the code that zeroing didn't work. This commit fixes it. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: fb0987682c62 ("dm-integrity: introduce the Inline mode") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-integrity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index a9c0157bf42fe..8c0e8ed663a28 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -2605,7 +2605,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) struct bio_vec bv = bio_iter_iovec(bio, dio->bio_details.bi_iter); const char *mem = integrity_kmap(ic, bv.bv_page); if (ic->tag_size < ic->tuple_size) - memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tuple_size); + memset(dio->integrity_payload + pos + ic->tag_size, 0, ic->tuple_size - ic->tag_size); integrity_sector_checksum(ic, &dio->ahash_req, dio->bio_details.bi_iter.bi_sector, mem, bv.bv_offset, dio->integrity_payload + pos); integrity_kunmap(ic, mem); pos += ic->tuple_size; From aa5113e7155f4ea81d2c0303ab5cb272d4b32627 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:25:39 +0200 Subject: [PATCH 1969/3297] dm-integrity: fix a bug if the bio is out of limits commit 5a266764fadaff8b5c1fe37a186ebf9b09cb953e upstream. If dm_integrity_check_limits fails, the code would exit with DM_MAPIO_KILL. However, the range would be already locked at this point, and it wouldn't be unlocked, resulting in a deadlock. Let's move the limit check up, so that when it exits, no resources are leaked. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: fb0987682c62 ("dm-integrity: introduce the Inline mode") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-integrity.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 8c0e8ed663a28..4bb53ff5d3696 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -2522,6 +2522,9 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) if (unlikely((bio->bi_opf & REQ_PREFLUSH) != 0)) return DM_MAPIO_REMAPPED; + if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio))) + return DM_MAPIO_KILL; + retry: if (!dio->integrity_payload) { unsigned digest_size, extra_size; @@ -2586,10 +2589,6 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map) dio->bio_details.bi_iter = bio->bi_iter; - if (unlikely(!dm_integrity_check_limits(ic, bio->bi_iter.bi_sector, bio))) { - return DM_MAPIO_KILL; - } - bio->bi_iter.bi_sector += ic->start + SB_SECTORS; bip = bio_integrity_alloc(bio, GFP_NOIO, 1); From 5dfd8042635278613da3b88553e25ade2103cd58 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:24:09 +0200 Subject: [PATCH 1970/3297] dm-integrity: don't increment hash_offset twice commit edf025f083854f80032b73a1aad69a3c90db236f upstream. hash_offset is already incremented in the loop "for (i = 0; i < to_copy; i++, ts--)". Do not increment it again. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 84597a44a9d8 ("dm-integrity: dm integrity: add optional discard support") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-integrity.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 4bb53ff5d3696..cd602b39481d6 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -1479,9 +1479,6 @@ static int dm_integrity_rw_tag(struct dm_integrity_c *ic, unsigned char *tag, se *metadata_offset = 0; } - if (unlikely(!is_power_of_2(ic->tag_size))) - hash_offset = (hash_offset + to_copy) % ic->tag_size; - total_size -= to_copy; } while (unlikely(total_size)); From 2a0858cba1dab49df2a6b14acfcb4bc70a50f720 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:33:16 +0200 Subject: [PATCH 1971/3297] dm-verity: avoid double increment of &use_bh_wq_enabled commit 72e9ec2fe32b00994f41719cf77423fca67d48b2 upstream. verity_parse_opt_args is called twice, first with the only_modifier_opts, first with only_modifier_opts == true and then with only_modifier_opts == false. Thus, the static branch &use_bh_wq_enabled was incremented twice and the destructor verity_dtr would only decrement it once. Fix tihs bug by only incrementing it on the first call, on the second call, when v->use_bh_wq is true, do nothing. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Cc: stable@vger.kernel.org Fixes: df326e7a0699 ("dm verity: allow optional args to alter primary args handling") Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-target.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index a5cd55b2ba02f..076b34cc7f130 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -1194,6 +1194,8 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, continue; } else if (!strcasecmp(arg_name, DM_VERITY_OPT_TASKLET_VERIFY)) { + if (v->use_bh_wq) + continue; v->use_bh_wq = true; static_branch_inc(&use_bh_wq_enabled); continue; From f15eaa3801f2f9207dff156f1ab3e7436ce52bb1 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:35:06 +0200 Subject: [PATCH 1972/3297] dm-verity: fix a possible NULL pointer dereference commit e72b793ae440f6900fb17a4b8518c707b5cd3e17 upstream. Fix a possible NULL pointer dereference dm_verity_loadpin_is_bdev_trusted if the device has no table. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Fixes: b6c1c5745ccc ("dm: Add verity helpers for LoadPin") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-loadpin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-loadpin.c b/drivers/md/dm-verity-loadpin.c index 0666699b68581..9a64f575ae5f6 100644 --- a/drivers/md/dm-verity-loadpin.c +++ b/drivers/md/dm-verity-loadpin.c @@ -70,7 +70,7 @@ bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev) table = dm_get_live_table(md, &srcu_idx); - if (table->num_targets != 1) + if (!table || table->num_targets != 1) goto out; ti = dm_table_get_target(table, 0); From c8d743bb0e98ac9bbb3d5a84c3ded3cd54b357b0 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:36:01 +0200 Subject: [PATCH 1973/3297] dm-verity: increase sprintf buffer size commit 88dd117c92a142253fb7a17e791773902b3babc6 upstream. The prefix "DM_VERITY_ERR_BLOCK_NR" is 22 chars. Add '=', one digit for type, ',', up to 20 digits for a u64 block number, and a NUL terminator: that's 46 bytes. The buffer is 42 bytes. For block numbers >= 16 decimal digits (devices larger than ~16 EB with 4K blocks), snprintf silently truncates the uevent environment variable. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 65ff5b7ddf05 ("dm verity: add error handling modes for corrupted blocks") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 076b34cc7f130..0bf8305fe1216 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -25,7 +25,7 @@ #define DM_MSG_PREFIX "verity" -#define DM_VERITY_ENV_LENGTH 42 +#define DM_VERITY_ENV_LENGTH 46 #define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR" #define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144 From 089e05b644d5aa786c21af467c80b24d691becb1 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:37:38 +0200 Subject: [PATCH 1974/3297] dm-verity: make error counter atomic commit 8ec4d9c5a5cf4b61fc087f871465b1f79b393325 upstream. The error counter "v->corrupted_errs" was not atomic, thus it could be subject to race conditions. The call to dm_audit_log_target("max-corrupted-errors") may be skipped due to the races. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4.6 Fixes: 65ff5b7ddf05 ("dm verity: add error handling modes for corrupted blocks") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-verity-target.c | 12 +++++++----- drivers/md/dm-verity.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 0bf8305fe1216..40c0664fa0342 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -165,14 +165,16 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, char *envp[] = { verity_env, NULL }; const char *type_str = ""; struct mapped_device *md = dm_table_get_md(v->ti->table); + int ce; /* Corruption should be visible in device status in all modes */ v->hash_failed = true; - if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS) - goto out; - - v->corrupted_errs++; + ce = atomic_read(&v->corrupted_errs); + do { + if (ce >= DM_VERITY_MAX_CORRUPTED_ERRS) + goto out; + } while (!atomic_try_cmpxchg(&v->corrupted_errs, &ce, ce + 1)); switch (type) { case DM_VERITY_BLOCK_TYPE_DATA: @@ -188,7 +190,7 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name, type_str, block); - if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS) { + if (ce + 1 == DM_VERITY_MAX_CORRUPTED_ERRS) { DMERR("%s: reached maximum errors", v->data_dev->name); dm_audit_log_target(DM_MSG_PREFIX, "max-corrupted-errors", v->ti, 0); } diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 6d141abd965c7..2ed44b60042bc 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -62,7 +62,7 @@ struct dm_verity { unsigned int digest_size; /* digest size for the current hash algorithm */ enum verity_mode mode; /* mode for handling verification errors */ enum verity_mode error_mode;/* mode for handling I/O errors */ - unsigned int corrupted_errs;/* Number of errors for corrupted blocks */ + atomic_t corrupted_errs;/* Number of errors for corrupted blocks */ struct workqueue_struct *verify_wq; From 7aa8f3dba53422465dbe1be8dbb7240304462bb2 Mon Sep 17 00:00:00 2001 From: Baineng Shou Date: Mon, 29 Jun 2026 11:13:46 +0800 Subject: [PATCH 1975/3297] dma-fence: Make dma_fence_dedup_array() robust against 0-count input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 77a9298741f8f9e8b963c977f5582ab21c6d3427 upstream. dma_fence_dedup_array() returns 1 when called with num_fences == 0: the for-loop body never executes, j stays at 0, and the final `return ++j` yields 1. This contradicts both the kernel-doc ("Return: Number of unique fences remaining in the array") and the natural expectation that 0 input gives 0 output. The caller __dma_fence_unwrap_merge() bails out via the `if (count == 0 || count == 1)` fast path and so is save. But amdgpu_userq_wait_*() could reach the dedup call with a zero local count and dereference an uninitialized fence slot in the array. Make the contract match the documentation by returning 0 early. This also skips an unnecessary sort() call on an empty array. Cc: stable@vger.kernel.org Signed-off-by: Baineng Shou Reviewed-by: Christian König Fixes: 575ec9b0c2f1 ("dma-fence: Add helper to sort and deduplicate dma_fence arrays") Signed-off-by: Christian König Link: https://lore.kernel.org/r/20260629031346.3875683-1-shoubaineng@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-fence-unwrap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c index a495d8a6c2e34..6ca607a9af032 100644 --- a/drivers/dma-buf/dma-fence-unwrap.c +++ b/drivers/dma-buf/dma-fence-unwrap.c @@ -97,6 +97,9 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences) { int i, j; + if (!num_fences) + return 0; + sort(fences, num_fences, sizeof(*fences), fence_cmp, NULL); /* From 4e370b5289624f46a356dcc94f9f87025eaa6036 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 25 Jun 2026 19:32:39 +0800 Subject: [PATCH 1976/3297] accel/amdxdna: Fix use-after-free in amdxdna_gem_dmabuf_mmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 63bbf9ac5dde2ba85e7b39d0a0b7d540e6252ba4 upstream. When vm_insert_pages() fails, the error path calls vma->vm_ops->close(vma) which internally calls drm_gem_vm_close() → drm_gem_object_put(), releasing the GEM object reference acquired at the start of the function. However, the close_vma label then falls through to put_obj, which calls drm_gem_object_put() a second time on the same object. If the first put releases the last reference, the object is freed and the second put accesses freed memory, causing a use-after-free. Fix by returning directly from close_vma instead of falling through to put_obj, since the close handler already performs all necessary cleanup including the object put. Cc: stable@vger.kernel.org Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Signed-off-by: Wentao Liang Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260625113239.49764-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/accel/amdxdna/amdxdna_gem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 084b22b774193..a719dc6aab339 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -374,6 +374,7 @@ static int amdxdna_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struc close_vma: vma->vm_ops->close(vma); + return ret; put_obj: drm_gem_object_put(gobj); return ret; From 257321a1c036da417f5d9c47b95c7e58f62bf263 Mon Sep 17 00:00:00 2001 From: Jhonraushan Date: Wed, 15 Jul 2026 13:12:06 +0530 Subject: [PATCH 1977/3297] accel/ivpu: Reject firmware log with size smaller than header commit ddb44baed257560f192b145ed36cf8c0a412de47 upstream. fw_log_from_bo() validates the tracing buffer header_size and that the log fits within the BO, but never checks that log->size is at least log->header_size. fw_log_print_buffer() then computes: u32 data_size = log->size - log->header_size; which underflows to a near-U32_MAX value when firmware reports a log whose size is smaller than its header. That huge data_size defeats the log_start/log_end bounds clamps added by commit dd1311bcf0e6 ("accel/ivpu: Add bounds checks for firmware log indices"), so fw_log_print_lines() reads far past the small real data region of the BO. A size of 0 also makes fw_log_from_bo() advance the offset by 0, causing the callers to loop forever on the same header. Reject logs whose size is smaller than the header (which also rejects size == 0). Fixes: d4e4257afa6e ("accel/ivpu: Add firmware tracing support") Cc: stable@vger.kernel.org Signed-off-by: Jhonraushan Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260715074206.867712-1-raushan.jhon@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/accel/ivpu/ivpu_fw_log.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c index 275baf844b562..716467aa31567 100644 --- a/drivers/accel/ivpu/ivpu_fw_log.c +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -43,6 +43,10 @@ static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *off ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size); return -EINVAL; } + if (log->size < log->header_size) { + ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); + return -EINVAL; + } if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) { ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); return -EINVAL; From d495b403d5b357dfe506b963bd7a78ecd5c7b667 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 00:00:28 +0800 Subject: [PATCH 1978/3297] scsi: hpsa: Fix DMA mapping leak on IOACCEL2 reset path commit e166bafc483e927150cb9b5f286c9191ea0df84e upstream. If phys_disk->in_reset is set, the function returns directly without undoing the resources acquired for the command. Add the missing error cleanup by unmapping the IOACCEL2 SG chain block when needed, unmapping the SCSI command, and dropping the outstanding IOACCEL command count before returning. Fixes: c5dfd106414f ("scsi: hpsa: correct device resets") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Acked-by: Don Brace Link: https://patch.msgid.link/20260622160028.1240496-1-haoxiang_li2024@163.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/hpsa.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 3654b12c5d5a7..23bbe6ce0225a 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -5020,6 +5020,10 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, if (phys_disk->in_reset) { cmd->result = DID_RESET << 16; + atomic_dec(&phys_disk->ioaccel_cmds_out); + scsi_dma_unmap(cmd); + if (use_sg > h->ioaccel_maxsg) + hpsa_unmap_ioaccel2_sg_chain_block(h, cp); return -1; } From ed08497977820e002a62cb114440f365cc7a087f Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 7 Jul 2026 12:23:02 +0530 Subject: [PATCH 1979/3297] scsi: lpfc: Fix memory leak in lpfc_sli4_driver_resource_setup() commit 1bd28625e25be549ee7c47532e7c3ef91c682410 upstream. The memory allocated for mboxq using mempool_alloc() is not freed in some of the early exit error paths. Fix that by moving the mempool_free() call to an earlier point after last use. Fixes: d79c9e9d4b3d ("scsi: lpfc: Support dynamic unbounded SGL lists on G7 hardware.") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Justin Tee Link: https://patch.msgid.link/20260707065304.949135-1-nihaal@cse.iitm.ac.in Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index adc0beaf5468f..8a64d2639d126 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -8194,6 +8194,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) mempool_free(mboxq, phba->mbox_mem_pool); goto out_free_bsmbx; } + mempool_free(mboxq, phba->mbox_mem_pool); /* * 1 for cmd, 1 for rsp, NVME adds an extra one @@ -8316,8 +8317,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_sg_dma_buf; } - mempool_free(mboxq, phba->mbox_mem_pool); - /* Verify OAS is supported */ lpfc_sli4_oas_verify(phba); From d0a8a6660d58e4a9f191c01e07d4db61f8bcf264 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 11:08:45 +0800 Subject: [PATCH 1980/3297] scsi: sg: Report request-table problems when any status is set commit 1d3a742afeb761eaead774691bde1ced699e9a5d upstream. SG_GET_REQUEST_TABLE reports per-request diagnostic state through sg_req_info::problem. The field is meant to indicate whether there is an error to report for a completed request. sg_fill_request_table() currently combines masked_status, host_status and driver_status with bitwise AND. This only reports a problem when all three status fields are non-zero at the same time. A normal target check condition, for example, has masked_status set while host_status and driver_status may both be zero, so the request is incorrectly reported as clean. Use the same condition as sg_new_read(), which sets SG_INFO_CHECK when any of the three status fields is non-zero. Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/54B60C19F7DB8889+20260707030845.970018-1-raoxu@uniontech.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 21c3962ee1cb5..238f5360ca239 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -862,10 +862,9 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) if (val >= SG_MAX_QUEUE) break; rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; + rinfo[val].problem = srp->header.masked_status || + srp->header.host_status || + srp->header.driver_status; if (srp->done) rinfo[val].duration = srp->header.duration; From 255fb7b0cdc947b1c01929c7f133281342a3a6b5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:30:46 -0400 Subject: [PATCH 1981/3297] scsi: xen: scsiback: Free the command tag on the TMR submit-failure path commit 66aefc277ebb796ec285d550305535dc3fc0179f upstream. scsiback_device_action() obtains a command tag in scsiback_get_pend_req() and submits a task-management request with target_submit_tmr(). When target_submit_tmr() fails it returns < 0 and scsiback jumps to the err: label, which sends a response but frees nothing, leaking the tag. Impact: a pvSCSI guest can leak the command tags of a LUN's session, stopping the LUN, by issuing VSCSIIF_ACT_SCSI_ABORT or RESET requests whenever target_submit_tmr() fails. transport_generic_free_cmd() cannot be used here. By the time target_submit_tmr() returns an error it has already run __target_init_cmd() (so se_cmd->cmd_kref is one, not zero), and on its target_get_sess_cmd() error path it has freed se_cmd->se_tmr_req via core_tmr_release_req() while leaving SCF_SCSI_TMR_CDB set and the pointer dangling. Letting the command release run target_free_cmd_mem() would then double-free se_tmr_req. Use the same helper, which returns just the tag, on this path too. Fixes: 2dbcdf33dbf6 ("xen-scsiback: Convert to percpu_ida tag allocation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Link: https://patch.msgid.link/20260611123046.2323342-3-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/xen/xen-scsiback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 7d5117e5efe0e..bca8b5f16e064 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -640,7 +640,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req, return; err: - scsiback_do_resp_with_sense(NULL, err, 0, pending_req); + scsiback_resp_and_free(pending_req, err); } /* From f1516c56ac540da1769f264c3cfefe4499548a5d Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:30:45 -0400 Subject: [PATCH 1982/3297] scsi: xen: scsiback: Free unsubmitted command instead of double-putting it commit ca978f8a93d4d36841839bf2847d29b88c2591d6 upstream. scsiback_get_pend_req() obtains a command tag and returns a vscsibk_pend whose embedded se_cmd has only been memset to 0, so its cmd_kref is 0; the se_cmd is initialised (kref_init() via target_init_cmd()) only later, in scsiback_cmd_exec(), on the successful VSCSIIF_ACT_SCSI_CDB path. The two error paths in scsiback_do_cmd_fn() taken before the command is submitted -- a failed scsiback_gnttab_data_map() and an unknown ring_req.act -- call transport_generic_free_cmd(&pending_req->se_cmd, 0), which kref_put()s a refcount of 0. That underflows it ("refcount_t: underflow; use-after-free") and, as the release function is not run, leaks the command tag. Impact: a pvSCSI guest can leak every command tag of a LUN's session, stopping the LUN, by submitting requests with a bad grant reference or an unknown request type; under panic_on_warn the refcount underflow panics the host. Add a helper that just returns the tag with target_free_tag() and sends the error response. It frees the tag while the v2p reference still pins the session, and snapshots the response fields beforehand because freeing the tag can let another ring reuse the pending_req slot. Fixes: 2dbcdf33dbf6 ("xen-scsiback: Convert to percpu_ida tag allocation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Link: https://patch.msgid.link/20260611123046.2323342-2-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/xen/xen-scsiback.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index bca8b5f16e064..82a757d7fe26b 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -612,6 +612,25 @@ static void scsiback_disconnect(struct vscsibk_info *info) xenbus_unmap_ring_vfree(info->dev, info->ring.sring); } +/* + * Send the error response for a request that did not reach the target core + * and return its tag. Free the tag before the response drops the v2p + * reference that keeps the session alive, and snapshot what the response + * needs since returning the tag can let the slot be reused. + */ +static void scsiback_resp_and_free(struct vscsibk_pend *pending_req, + int32_t result) +{ + struct vscsibk_info *info = pending_req->info; + struct v2p_entry *v2p = pending_req->v2p; + struct se_session *se_sess = v2p->tpg->tpg_nexus->tvn_se_sess; + u16 rqid = pending_req->rqid; + + target_free_tag(se_sess, &pending_req->se_cmd); + scsiback_send_response(info, NULL, result, 0, rqid); + kref_put(&v2p->kref, scsiback_free_translation_entry); +} + static void scsiback_device_action(struct vscsibk_pend *pending_req, enum tcm_tmreq_table act, int tag) { @@ -793,9 +812,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, case VSCSIIF_ACT_SCSI_CDB: if (scsiback_gnttab_data_map(&ring_req, pending_req)) { scsiback_fast_flush_area(pending_req); - scsiback_do_resp_with_sense(NULL, - DID_ERROR << 16, 0, pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, + DID_ERROR << 16); } else { scsiback_cmd_exec(pending_req); } @@ -809,9 +827,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, break; default: pr_err_ratelimited("invalid request\n"); - scsiback_do_resp_with_sense(NULL, DID_ERROR << 16, 0, - pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, DID_ERROR << 16); break; } From 004ccd2d3b4ac36a300e05e01df152e5c02a5a82 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 11 Jun 2026 13:42:26 -0500 Subject: [PATCH 1983/3297] scsi: target: Bound PR-OUT TransportID parsing to the received buffer commit d04a179085c262c9ed577d0a4cbc6482ff1fd9a3 upstream. core_scsi3_decode_spec_i_port() and core_scsi3_emulate_register_and_move() hand the raw PERSISTENT RESERVE OUT parameter buffer to target_parse_pr_out_transport_id() without telling it how many bytes are valid. For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() locates the ",i,0x" ISID separator with an unbounded strstr() (and on the error path prints the name with a further unbounded "%s"). An initiator can submit a TransportID whose iSCSI name contains neither a ",i,0x" substring nor a NUL terminator, filling the parameter list to its end, so the scan runs off the end of the buffer. When the parameter list spans more than one page the buffer is a multi-page vmap (transport_kmap_data_sg()), so the over-read walks into the trailing vmalloc guard page and oopses (KASAN: vmalloc-out-of-bounds in strstr). It is reachable by any fabric that delivers a PR OUT to a device exported through an iSCSI TPG, including a guest via vhost-scsi. Pass the number of received bytes down to the parser and validate the iSCSI TransportID's own self-described length (ADDITIONAL LENGTH + 4) once, up front: reject it if it is below the spc4r17 minimum or larger than the received buffer, then bound the separator search, the ISID walk and the name copy by that length. This is the length check the callers already perform after the parse (core_scsi3_decode_spec_i_port() compares tid_len against tpdl, core_scsi3_emulate_register_and_move() validates it against data_length), moved ahead of the scan. Also drop the unbounded "%s" of the unterminated name. Add per-format explicit name-length checks before copying into i_str, rather than silently truncating with min_t: for FORMAT CODE 00b reject if the descriptor body (tid_len - 4 bytes) cannot fit in i_str[TRANSPORT_IQN_LEN]; for FORMAT CODE 01b reject if the name portion (from &buf[4] up to the separator) cannot fit. Both checks make the bounds intent explicit at each format branch. While here, also reject a FORMAT CODE 01b TransportID whose ",i,0x" separator sits at the very end of the descriptor: that leaves an empty ISID and points the returned port nexus pointer at buf + tid_len, one past the descriptor, which the registration code (__core_scsi3_locate_pr_reg(), __core_scsi3_alloc_registration()) then dereferences as the ISID string -- the same over-read of the parameter buffer for a malformed descriptor. Fixes: c66ac9db8d4a ("[SCSI] target: Add LIO target core v4.0.0-rc6") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: John Garry Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260611-b4-disp-9f20739e-v6-1-f6630e2aae44@proton.me Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_fabric_lib.c | 89 +++++++++++++++++++------ drivers/target/target_core_internal.h | 3 +- drivers/target/target_core_pr.c | 4 +- 3 files changed, 73 insertions(+), 23 deletions(-) diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index ec7bc6e302289..3af9a26cdefc4 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -289,13 +289,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str) static bool iscsi_parse_pr_out_transport_id( struct se_portal_group *se_tpg, char *buf, + u32 buf_len, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) { char *p; + u32 tid_len; int i; - u8 format_code = (buf[0] & 0xc0); + u8 format_code; + + /* + * The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL + * LENGTH) must be present before any of it can be parsed. + */ + if (buf_len < 4) + return false; + + format_code = buf[0] & 0xc0; /* * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6: * @@ -315,15 +326,17 @@ static bool iscsi_parse_pr_out_transport_id( return false; } /* - * If the caller wants the TransportID Length, we set that value for the - * entire iSCSI Tarnsport ID now. + * Reconstruct the self-described TransportID length from the ADDITIONAL + * LENGTH field plus the 4-byte header. Reject it if it is below the + * spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least + * 20) or if it runs past the bytes actually received, so that every + * access below stays inside the TransportID. */ - if (out_tid_len) { - /* The shift works thanks to integer promotion rules */ - *out_tid_len = get_unaligned_be16(&buf[2]); - /* Add four bytes for iSCSI Transport ID header */ - *out_tid_len += 4; - } + tid_len = get_unaligned_be16(&buf[2]) + 4; + if (tid_len < 24 || tid_len > buf_len) + return false; + if (out_tid_len) + *out_tid_len = tid_len; /* * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator @@ -331,16 +344,32 @@ static bool iscsi_parse_pr_out_transport_id( * format. */ if (format_code == 0x40) { - p = strstr(&buf[4], ",i,0x"); + p = strnstr(&buf[4], ",i,0x", tid_len - 4); if (!p) { - pr_err("Unable to locate \",i,0x\" separator" - " for Initiator port identifier: %s\n", - &buf[4]); + pr_err("Unable to locate \",i,0x\" separator in iSCSI TransportID\n"); + return false; + } + /* + * The iSCSI name runs from &buf[4] up to the separator; reject it + * if it cannot fit in i_str[TRANSPORT_IQN_LEN]. + */ + if (p - &buf[4] >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); return false; } *p = '\0'; /* Terminate iSCSI Name */ p += 5; /* Skip over ",i,0x" separator */ + /* + * The ISID must follow the separator. A ",i,0x" sitting at the + * very end of the TransportID leaves no ISID and would point the + * port nexus at buf + tid_len, i.e. past the descriptor, which + * the registration code then reads as the ISID string. + */ + if (p >= buf + tid_len) { + pr_err("Missing ISID in iSCSI Initiator port TransportID\n"); + return false; + } *port_nexus_ptr = p; /* * Go ahead and do the lower case conversion of the received @@ -348,7 +377,7 @@ static bool iscsi_parse_pr_out_transport_id( * for comparison against the running iSCSI session's ISID from * iscsi_target.c:lio_sess_get_initiator_sid() */ - for (i = 0; i < 12; i++) { + for (i = 0; i < 12 && p < buf + tid_len; i++) { /* * The first ISCSI INITIATOR SESSION ID field byte * containing an ASCII null character terminates the @@ -366,10 +395,22 @@ static bool iscsi_parse_pr_out_transport_id( *p = tolower(*p); p++; } - } else + strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + } else { *port_nexus_ptr = NULL; - - strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + /* + * FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The + * declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN]. + * (For 01b the same tid_len bound would be over-restrictive: the + * descriptor also carries the separator and ISID, so a legal + * <=223-byte name gives tid_len up to 244.) + */ + if (tid_len - 4 >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); + return false; + } + strscpy(i_str, &buf[4], tid_len - 4); + } return true; } @@ -419,8 +460,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, } bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str) { + /* + * The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI + * format is variable and bounds itself against buf_len below. + */ + if (tpg->proto_id != SCSI_PROTOCOL_ISCSI && buf_len < 24) + return false; + switch (tpg->proto_id) { case SCSI_PROTOCOL_SAS: /* @@ -439,8 +488,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, sbp_parse_pr_out_transport_id(buf, i_str); break; case SCSI_PROTOCOL_ISCSI: - return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len, - port_nexus_ptr, i_str); + return iscsi_parse_pr_out_transport_id(tpg, buf, buf_len, + out_tid_len, port_nexus_ptr, i_str); default: pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id); return false; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 20aab1f505655..32d0da505bafa 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, struct t10_pr_registration *pr_reg, int *format_code, unsigned char *buf); bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str); + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str); /* target_core_hba.c */ struct se_hba *core_alloc_hba(const char *, u32, u32); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 83e172c92238d..d088099a0a1f8 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port( iport_ptr = NULL; tid_found = target_parse_pr_out_transport_id(tmp_tpg, - ptr, &tid_len, &iport_ptr, i_str); + ptr, tpdl, &tid_len, &iport_ptr, i_str); if (!tid_found) continue; /* @@ -3281,7 +3281,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } tid_found = target_parse_pr_out_transport_id(dest_se_tpg, - &buf[24], &tmp_tid_len, &iport_ptr, initiator_str); + &buf[24], tid_len, &tmp_tid_len, &iport_ptr, initiator_str); if (!tid_found) { pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate" " initiator_str from Transport ID\n"); From cb7bdae7fba404852ade34b0c1445fbaf3e54fbb Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Wed, 10 Jun 2026 04:22:48 +0000 Subject: [PATCH 1984/3297] scsi: target: core: Fix iSCSI ISID use-after-free in REGISTER AND MOVE commit fda6a1f3c3d7047b5ce5654487649c2daa738bfc upstream. core_scsi3_emulate_pro_register_and_move() maps the PERSISTENT RESERVE OUT parameter list with transport_kmap_data_sg() and parses the destination TransportID with target_parse_pr_out_transport_id(). For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() returns the ISID in iport_ptr as a raw pointer into that mapped buffer. The function then unmaps the buffer with transport_kunmap_data_sg() before dereferencing iport_ptr in strcmp(), __core_scsi3_locate_pr_reg() and core_scsi3_alloc_registration(). When the parameter list spans more than one page (PARAMETER LIST LENGTH > 4096), transport_kmap_data_sg() uses vmap() and transport_kunmap_data_sg() does vunmap(), so the kernel virtual address backing iport_ptr is torn down and every subsequent dereference is a use-after-free read of the unmapped region. Keep the parameter list mapped until iport_ptr is no longer needed: drop the early transport_kunmap_data_sg() and unmap once on the success path, right before returning. The error paths already unmap through the existing "if (buf) transport_kunmap_data_sg(cmd)" at the out: label, which now runs on every post-map error exit because buf is no longer cleared early. Only reads of the mapping happen while spinlocks are held; the map and unmap calls remain outside any lock. The sibling caller core_scsi3_decode_spec_i_port() already uses the buffer before unmapping it and is left unchanged. Fixes: 4949314c7283 ("target: Allow control CDBs with data > 1 page") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: John Garry Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260610042245.35473-1-hexlabsecurity@proton.me Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_pr.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index d088099a0a1f8..f18085d8345f5 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -3289,9 +3289,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } - transport_kunmap_data_sg(cmd); - buf = NULL; - pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s" " %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ? "port" : "device", initiator_str, (iport_ptr != NULL) ? @@ -3528,6 +3525,11 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl); core_scsi3_put_pr_reg(dest_pr_reg); + /* + * iport_ptr aliases the PR-OUT parameter list mapped above, so the + * buffer is unmapped only here on success (and at out: on error). + */ + transport_kunmap_data_sg(cmd); return 0; out: if (buf) From 9b871369cbb4532f6715e044d6b9c4ceb036e5b6 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 13:30:37 +0800 Subject: [PATCH 1985/3297] scsi: elx: efct: Fix refcount leak in efct_hw_io_abort() commit 2c007acf7b31c39c08ce4959451ad00b19be4c1f upstream. When efct_hw_reqtag_alloc() fails in efct_hw_io_abort(), the error path returns -ENOSPC without releasing the reference obtained via kref_get_unless_zero() earlier in the function. All other error paths correctly drop the reference. This causes a permanent reference leak on the io_to_abort object. Additionally, the abort_in_progress flag is left set to true on this path, which means future abort attempts for the same I/O will immediately return -EINPROGRESS even though the abort was never submitted, effectively blocking recovery. Fix this by adding the missing kref_put() call and reset abort_in_progress to false, matching the cleanup done in the efct_hw_wq_write() failure path below. Cc: stable@vger.kernel.org Fixes: 63de51327a64 ("scsi: elx: efct: Hardware I/O and SGL initialization") Signed-off-by: WenTao Liang Reviewed-by: Daniel Wagner Link: https://patch.msgid.link/20260611053037.63756-1-vulab@iscas.ac.cn Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/elx/efct/efct_hw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c index 5b079b8b7a082..f41b7f1e4416b 100644 --- a/drivers/scsi/elx/efct/efct_hw.c +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -1998,6 +1998,8 @@ efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort, wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort); if (!wqcb) { efc_log_err(hw->os, "can't allocate request tag\n"); + io_to_abort->abort_in_progress = false; + kref_put(&io_to_abort->ref, io_to_abort->release); return -ENOSPC; } From df87532e9212238509f23effd0ca39d9c3062d21 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 15:58:44 +0800 Subject: [PATCH 1986/3297] scsi: elx: efct: Fix I/O leak on unsupported additional CDB commit 9cb2d5291dbfe7bed565ead3337047dee9ed1064 upstream. efct_dispatch_fcp_cmd() allocates an efct_io before dispatching an unsolicited FCP command. If the command has an unsupported additional CDB, the function returns -EIO before handing the IO to the SCSI layer. Free the allocated IO before returning from this error path. Fixes: f45ae6aac0a0 ("scsi: elx: efct: Unsolicited FC frame processing routines") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Daniel Wagner Link: https://patch.msgid.link/20260622075844.832871-1-haoxiang_li2024@163.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/elx/efct/efct_unsol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/elx/efct/efct_unsol.c b/drivers/scsi/elx/efct/efct_unsol.c index e6addab66a603..6a871a59c9096 100644 --- a/drivers/scsi/elx/efct/efct_unsol.c +++ b/drivers/scsi/elx/efct/efct_unsol.c @@ -385,6 +385,7 @@ efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq) if (cmnd->fc_flags & FCP_CFL_LEN_MASK) { efc_log_err(efct, "Additional CDB not supported\n"); + efct_scsi_io_free(io); return -EIO; } /* From 6aacc18004b1a915ccb8a829d30279a37988066e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:24:47 -0700 Subject: [PATCH 1987/3297] Input: ims-pcu - fix use-after-free and double-free in disconnect commit 462a999917755a3bf77448dfd64307963cf0a9f0 upstream. ims_pcu_disconnect() only intended to perform cleanup when the primary (control) interface is unbound. However, it currently relies on the interface class to distinguish between control and data interfaces. A malicious device could present a data interface with the same class as the control interface, leading to premature cleanup and potential use-after-free or double-free. Switch to verifying that the interface being disconnected is indeed the control interface. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 22d34644254dd..7f51e1c622fe0 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -2051,7 +2051,6 @@ static int ims_pcu_probe(struct usb_interface *intf, static void ims_pcu_disconnect(struct usb_interface *intf) { struct ims_pcu *pcu = usb_get_intfdata(intf); - struct usb_host_interface *alt = intf->cur_altsetting; usb_set_intfdata(intf, NULL); @@ -2059,7 +2058,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf) * See if we are dealing with control or data interface. The cleanup * happens when we unbind primary (control) interface. */ - if (alt->desc.bInterfaceClass != USB_CLASS_COMM) + if (intf != pcu->ctrl_intf) return; ims_pcu_stop_io(pcu); From 87e2f89dea078572fb9e13864cec2b1bd8e89b71 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 31 May 2026 21:42:35 -0700 Subject: [PATCH 1988/3297] Input: ims-pcu - only expose sysfs attributes on control interface commit 001428ea4d2c371107cb984108e266adf99f1f1e upstream. When the driver was converted to use the driver core to instantiate device attributes (via .dev_groups in the usb_driver structure), the attributes started appearing on all interfaces bound to the driver. Since the ims-pcu driver manually claims the secondary data interface during probe, the driver core automatically creates the sysfs attributes for that interface as well. However, the driver only supports these attributes on the primary control interface. Data interfaces lack the necessary descriptors and internal state to handle these requests, and accessing them can lead to unexpected behavior or crashes. Fix this by updating the is_visible() callbacks for both the main and OFN attribute groups to verify that the interface being accessed is indeed the control interface. Fixes: 204d18a7a0c6 ("Input: ims-pcu - use driver core to instantiate device attributes") Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Cc: stable@vger.kernel.org Link: https://patch.msgid.link/ahp23lj4_vXIeUBl@google.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 7f51e1c622fe0..31b5103c91ea1 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1229,6 +1230,9 @@ static umode_t ims_pcu_is_attr_visible(struct kobject *kobj, struct ims_pcu *pcu = usb_get_intfdata(intf); umode_t mode = attr->mode; + if (intf != pcu->ctrl_intf) + return 0; + if (pcu->bootloader_mode) { if (attr != &dev_attr_update_firmware_status.attr && attr != &dev_attr_update_firmware.attr && @@ -1468,6 +1472,9 @@ static umode_t ims_pcu_ofn_is_attr_visible(struct kobject *kobj, struct ims_pcu *pcu = usb_get_intfdata(intf); umode_t mode = attr->mode; + if (intf != pcu->ctrl_intf) + return SYSFS_GROUP_INVISIBLE; + /* * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor. */ From 6329d1af316a4b154465d008cf74e075282938ad Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:22:55 -0700 Subject: [PATCH 1989/3297] Input: ims-pcu - release data interface on disconnect commit 441c510a649c8ddce38aa0311334ed8bb546b36c upstream. During probe the driver claims the data interface, but it never releases it. Release it in disconnect to avoid leaving it permanently claimed. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 31b5103c91ea1..3f1a4610fbd55 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -2076,6 +2076,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf) ims_pcu_destroy_application_mode(pcu); ims_pcu_buffers_free(pcu); + usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf); kfree(pcu); } From c8d3d83f2eaaf7659de76e8d44e8fc88ee346042 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:26 -0700 Subject: [PATCH 1990/3297] Input: ims-pcu - validate control endpoint type commit baf56975806534268e24acf9a8abb1c447ce11e9 upstream. The driver currently assumes that the first endpoint of the control interface is an interrupt IN endpoint without verifying it. A malicious device could provide a different endpoint type, which would then be passed to usb_fill_int_urb(), potentially leading to kernel warnings or undefined behavior. Verify that the control endpoint is an interrupt IN endpoint. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 3f1a4610fbd55..89e4667a67edd 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1689,6 +1689,12 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc return -ENODEV; pcu->ep_ctrl = &alt->endpoint[0].desc; + if (!usb_endpoint_is_int_in(pcu->ep_ctrl)) { + dev_err(pcu->dev, + "Control endpoint is not INTERRUPT IN\n"); + return -EINVAL; + } + pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl); pcu->data_intf = usb_ifnum_to_if(pcu->udev, From f28c5cabb2df09ad1fcfb7cdb2b3ed2f690d81f0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:40:54 -0700 Subject: [PATCH 1991/3297] Input: ims-pcu - add response length checks commit 48c9d92fd4ee3a8f5d2cb46c802a0eff8e67c79c upstream. The driver processes response data from device buffers without verifying that the device actually sent enough data. This can lead to out-of-bounds reads or processing stale data. Add checks for the expected response length before accessing the buffers. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 87 +++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 89e4667a67edd..fc77a5d1e79c7 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -407,7 +407,16 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) static void ims_pcu_report_events(struct ims_pcu *pcu) { - u32 data = get_unaligned_be32(&pcu->read_buf[3]); + u32 data; + + /* 6-axis setting (1 byte) + button data + checksum */ + if (pcu->read_pos < IMS_PCU_DATA_OFFSET + 1 + sizeof(data) + 1) { + dev_warn(pcu->dev, "Short buttons report: %d bytes\n", + pcu->read_pos); + return; + } + + data = get_unaligned_be32(&pcu->read_buf[IMS_PCU_DATA_OFFSET + 1]); ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK); if (pcu->gamepad) @@ -667,11 +676,19 @@ static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu, return error; } - if (expected_response && pcu->cmd_buf[2] != expected_response) { - dev_err(pcu->dev, - "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", - pcu->cmd_buf[2], expected_response); - return -EINVAL; + if (expected_response) { + if (pcu->cmd_buf_len < 3) { + dev_err(pcu->dev, "Short response from bootloader: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + + if (pcu->cmd_buf[2] != expected_response) { + dev_err(pcu->dev, + "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", + pcu->cmd_buf[2], expected_response); + return -EINVAL; + } } return 0; @@ -699,6 +716,12 @@ static int ims_pcu_get_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + IMS_PCU_SET_INFO_SIZE + 1) { + dev_err(pcu->dev, "Short GET_INFO response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + memcpy(pcu->part_number, &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], sizeof(pcu->part_number)); @@ -796,6 +819,12 @@ static int ims_pcu_verify_block(struct ims_pcu *pcu, return error; } + if (pcu->cmd_buf_len < IMS_PCU_BL_DATA_OFFSET + sizeof(*fragment) + len + 1) { + dev_err(pcu->dev, "Short READ_APP response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET]; if (get_unaligned_le32(&fragment->addr) != addr || fragment->len != len) { @@ -990,6 +1019,10 @@ ims_pcu_backlight_get_brightness(struct led_classdev *cdev) error); /* Assume the LED is OFF */ brightness = LED_OFF; + } else if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short GET_BRIGHTNESS response: %d bytes\n", + pcu->cmd_buf_len); + brightness = LED_OFF; } else { brightness = get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); @@ -1266,6 +1299,12 @@ static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data) if (error) return error; + if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short OFN_GET_CONFIG response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); if (result < 0) return -EIO; @@ -1286,6 +1325,12 @@ static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data) if (error) return error; + if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) { + dev_err(pcu->dev, "Short OFN_SET_CONFIG response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); if (result < 0) return -EIO; @@ -1824,6 +1869,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) { + dev_err(pcu->dev, "Short GET_FW_VERSION response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->fw_version, sizeof(pcu->fw_version), "%02d%02d%02d%02d.%c%c", pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], @@ -1836,6 +1887,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) { + dev_err(pcu->dev, "Short GET_BL_VERSION response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->bl_version, sizeof(pcu->bl_version), "%02d%02d%02d%02d.%c%c", pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], @@ -1848,6 +1905,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) { + dev_err(pcu->dev, "Short RESET_REASON response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + snprintf(pcu->reset_reason, sizeof(pcu->reset_reason), "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); @@ -1874,6 +1937,12 @@ static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) { + dev_err(pcu->dev, "Short GET_DEVICE_ID response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + *device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET]; dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id); @@ -1965,6 +2034,12 @@ static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu) return error; } + if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 15 + 4 + 1) { + dev_err(pcu->dev, "Short QUERY_DEVICE response: %d bytes\n", + pcu->cmd_buf_len); + return -EIO; + } + pcu->fw_start_addr = get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]); pcu->fw_end_addr = From 05ac85da121980dba89de0966c1c47e7489dc4b7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:30:44 -0700 Subject: [PATCH 1992/3297] Input: ims-pcu - fix DMA mapping violation in line setup commit 8adf4289d945e8990e4336436a97da71d21d2cae upstream. In ims_pcu_line_setup(), the driver uses pcu->cmd_buf as a transfer buffer for usb_control_msg(). However, pcu->cmd_buf is embedded in the struct ims_pcu allocation, which violates DMA mapping rules regarding cacheline alignment. Use a heap-allocated buffer for the line coding data instead. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index fc77a5d1e79c7..e334cf44f72e1 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1821,11 +1821,16 @@ static void ims_pcu_stop_io(struct ims_pcu *pcu) static int ims_pcu_line_setup(struct ims_pcu *pcu) { struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting; - struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf; + struct usb_cdc_line_coding *line __free(kfree) = + kmalloc(sizeof(*line), GFP_KERNEL); int error; - memset(line, 0, sizeof(*line)); + if (!line) + return -ENOMEM; + line->dwDTERate = cpu_to_le32(57600); + line->bCharFormat = USB_CDC_1_STOP_BITS; + line->bParityType = USB_CDC_NO_PARITY; line->bDataBits = 8; error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0), From 99c428d7ef644d3e394f3072f905040c16dab18d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:25:31 -0700 Subject: [PATCH 1993/3297] Input: ims-pcu - fix firmware leak in async update commit d48795b5cd6828d36b707e8d62fc9e5c90e004ab upstream. The firmware object was not being released if validation failed. Use __free(firmware) to ensure the firmware is always released. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index e334cf44f72e1..6f2e1f6d9f1d4 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -944,9 +944,10 @@ static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu, return retval; } -static void ims_pcu_process_async_firmware(const struct firmware *fw, +static void ims_pcu_process_async_firmware(const struct firmware *_fw, void *context) { + const struct firmware *fw __free(firmware) = _fw; struct ims_pcu *pcu = context; int error; @@ -966,8 +967,6 @@ static void ims_pcu_process_async_firmware(const struct firmware *fw, scoped_guard(mutex, &pcu->cmd_mutex) ims_pcu_handle_firmware_update(pcu, fw); - release_firmware(fw); - out: complete(&pcu->async_firmware_done); } From 9c964fc9507aeab74376ba9f892cf84ad6950dfe Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:30:21 -0700 Subject: [PATCH 1994/3297] Input: ims-pcu - fix out-of-bounds read in ims_pcu_irq() debug logging commit 403b0a6970b1084bb27907c0f8225801fdd0fe1d upstream. The debug logging in ims_pcu_irq() unconditionally prints data from pcu->urb_in_buf. However, if the interrupt fired for pcu->urb_ctrl, the actual data resides in pcu->urb_ctrl_buf. If urb->actual_length for the control URB exceeds pcu->max_in_size, this leads to an out-of-bounds read. Fix this by printing from the correct buffer associated with the URB. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro 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 6f2e1f6d9f1d4..4d8b02dfc6db7 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1559,7 +1559,7 @@ static void ims_pcu_irq(struct urb *urb) } dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__, - urb->actual_length, urb->actual_length, pcu->urb_in_buf); + urb->actual_length, urb->actual_length, urb->transfer_buffer); if (urb == pcu->urb_in) ims_pcu_process_data(pcu, urb); From 383934c249a9817d242d02d30bfbc8defbf0d533 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 21:42:39 -0700 Subject: [PATCH 1995/3297] Input: ims-pcu - fix potential infinite loop in CDC union descriptor parsing commit d4579af29e67ca8722db0a1194227f8015c8981d upstream. The driver parses CDC union descriptors in ims_pcu_get_cdc_union_desc() by iterating through the extra descriptor data. However, it does not verify that the bLength of each descriptor is at least 2. A malicious device could provide a descriptor with bLength = 0, leading to an infinite loop in the driver. Add a check to ensure bLength is at least 2 before proceeding with parsing. Fixes: 628329d52474 (Input: add IMS Passenger Control Unit driver) Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 4d8b02dfc6db7..887cac93c8000 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1687,8 +1687,9 @@ ims_pcu_get_cdc_union_desc(struct usb_interface *intf) while (buflen >= sizeof(*union_desc)) { union_desc = (struct usb_cdc_union_desc *)buf; - if (union_desc->bLength > buflen) { - dev_err(&intf->dev, "Too large descriptor\n"); + if (union_desc->bLength < 2 || union_desc->bLength > buflen) { + dev_err(&intf->dev, "Invalid descriptor length: %d\n", + union_desc->bLength); return NULL; } From f516cba88bf952d847e5c96d0e87f17eaae7ee6f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:04 -0700 Subject: [PATCH 1996/3297] Input: ims-pcu - fix race condition in reset_device sysfs callback commit 411b8c4b274737c3bf08e1e025801161603cfffc upstream. The ims_pcu_reset_device() sysfs callback calls ims_pcu_execute_command() without acquiring pcu->cmd_mutex. This can lead to data races and corruption of the shared command buffer if triggered concurrently with other commands. Acquire pcu->cmd_mutex before calling ims_pcu_execute_command(). Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 887cac93c8000..0d39af455ddcd 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1166,6 +1166,8 @@ static ssize_t ims_pcu_reset_device(struct device *dev, dev_info(pcu->dev, "Attempting to reset device\n"); + guard(mutex)(&pcu->cmd_mutex); + error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1); if (error) { dev_info(pcu->dev, From 0e8115a7ed9a99ff9495615a575a6c0f43566d10 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:25:11 -0700 Subject: [PATCH 1997/3297] Input: ims-pcu - fix type confusion in CDC union descriptor parsing commit ca459e237bc49567649c56bc72e4c602fb92fd67 upstream. The driver currently trusts the bMasterInterface0 from the CDC union descriptor without verifying that it matches the interface being probed. This could lead to the driver overwriting the private data of another interface. Validate that the control interface found in the descriptor is indeed the one we are probing. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro 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 0d39af455ddcd..94113e14bf599 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1727,7 +1727,7 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev, union_desc->bMasterInterface0); - if (!pcu->ctrl_intf) + if (pcu->ctrl_intf != intf) return -EINVAL; alt = pcu->ctrl_intf->cur_altsetting; From ed3cc4218070d6b98bf5fb456dccae424fd38c4f Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Sun, 28 Jun 2026 00:30:59 +0200 Subject: [PATCH 1998/3297] net/mlx5e: macsec: fix use-after-free of metadata_dst on RX SC delete commit de74d8fd10291763d97b218f09adcc7513c975e4 upstream. When an offloaded MACsec RX SC is deleted, macsec_del_rxsc_ctx() freed the per-SC metadata_dst with metadata_dst_free(), which kfree()s the object unconditionally and ignores the dst reference count. The RX datapath in mlx5e_macsec_offload_handle_rx_skb() looks up the SC under rcu_read_lock() via xa_load(), takes a reference with dst_hold() and attaches the dst to the skb with skb_dst_set(). A reader that already obtained the rx_sc pointer can race with the delete path and operate on freed memory. Fix the owner side by dropping the reference with dst_release() instead of freeing unconditionally, and convert the RX datapath to dst_hold_safe() so a reader racing the SC delete cannot attach a dst whose last reference was just dropped; only attach it when a reference was actually taken. mlx5e_macsec_add_rxsc() also published sc_xarray_element via xa_alloc() before rx_sc->md_dst was allocated and initialised, so a datapath reader that looked the SC up by fs_id could observe rx_sc with md_dst still NULL or, on weakly-ordered architectures, a non-NULL md_dst pointer whose contents were not yet visible. NULL-check the xa_load() result and md_dst on the datapath, and reorder add_rxsc() so the xa_alloc() publish happens only after md_dst is fully initialised; the xarray RCU publish then pairs with the rcu_read_lock()/xa_load() in the datapath. Note: macsec_del_rxsc_ctx() also kfree()s rx_sc->sc_xarray_element without an RCU grace period while the same datapath reads it under rcu_read_lock(); that is a separate pre-existing issue left to a follow-up patch. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: b7c9400cbc48 ("net/mlx5e: Implement MACsec Rx data path using MACsec skb_metadata_dst") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260627223059.29917-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../mellanox/mlx5/core/en_accel/macsec.c | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c index 528b04d4de416..a7255b42043b1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c @@ -713,34 +713,43 @@ static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx) } sc_xarray_element->rx_sc = rx_sc; - err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element, - XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL); - if (err) { - if (err == -EBUSY) - netdev_err(ctx->netdev, - "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n", - MLX5_MACEC_RX_FS_ID_MAX); - goto destroy_sc_xarray_elemenet; - } rx_sc->md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL); if (!rx_sc->md_dst) { err = -ENOMEM; - goto erase_xa_alloc; + goto destroy_sc_xarray_elemenet; } rx_sc->sci = ctx_rx_sc->sci; rx_sc->active = ctx_rx_sc->active; - list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list); - rx_sc->sc_xarray_element = sc_xarray_element; rx_sc->md_dst->u.macsec_info.sci = rx_sc->sci; + + /* + * Publish the fully-initialised SC last: xa_alloc() makes + * sc_xarray_element->rx_sc (and rx_sc->md_dst) reachable from the RX + * datapath via xa_load(). Doing it only after md_dst is allocated and + * initialised pairs with the rcu_read_lock()/xa_load() in + * mlx5e_macsec_offload_handle_rx_skb(), so a reader can never observe + * a non-NULL md_dst with uninitialised contents. + */ + err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element, + XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL); + if (err) { + if (err == -EBUSY) + netdev_err(ctx->netdev, + "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n", + MLX5_MACEC_RX_FS_ID_MAX); + goto destroy_md_dst; + } + + list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list); mutex_unlock(&macsec->lock); return 0; -erase_xa_alloc: - xa_erase(&macsec->sc_xarray, sc_xarray_element->fs_id); +destroy_md_dst: + dst_release(&rx_sc->md_dst->dst); destroy_sc_xarray_elemenet: kfree(sc_xarray_element); destroy_rx_sc: @@ -828,7 +837,7 @@ static void macsec_del_rxsc_ctx(struct mlx5e_macsec *macsec, struct mlx5e_macsec */ list_del_rcu(&rx_sc->rx_sc_list_element); xa_erase(&macsec->sc_xarray, rx_sc->sc_xarray_element->fs_id); - metadata_dst_free(rx_sc->md_dst); + dst_release(&rx_sc->md_dst->dst); kfree(rx_sc->sc_xarray_element); kfree_rcu_mightsleep(rx_sc); } @@ -1698,10 +1707,10 @@ void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, rcu_read_lock(); sc_xarray_element = xa_load(&macsec->sc_xarray, fs_id); - rx_sc = sc_xarray_element->rx_sc; - if (rx_sc) { - dst_hold(&rx_sc->md_dst->dst); - skb_dst_set(skb, &rx_sc->md_dst->dst); + rx_sc = sc_xarray_element ? sc_xarray_element->rx_sc : NULL; + if (rx_sc && rx_sc->md_dst) { + if (dst_hold_safe(&rx_sc->md_dst->dst)) + skb_dst_set(skb, &rx_sc->md_dst->dst); } rcu_read_unlock(); From b33ac2d39953efb12f598c0dae242c5f644ea669 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 7 Jul 2026 12:59:11 -0400 Subject: [PATCH 1999/3297] tracing/user_events: Fix use-after-free in user_event_mm_dup() commit 50fd6dd755c6e48a38af2fa4621167eea56829c2 upstream. user_event_mm_dup() walks the parent mm's enabler list locklessly under rcu_read_lock() during fork() (from copy_process()); it does not take event_mutex: rcu_read_lock(); list_for_each_entry_rcu(enabler, &old_mm->enablers, mm_enablers_link) enabler->event = user_event_get(orig->event); user_event_enabler_destroy() removes an enabler from that list with list_del_rcu() and then, without waiting for a grace period, drops the enabler's user_event reference with user_event_put() and frees the enabler with kfree(). A reader that loaded the enabler before the list_del_rcu() can still be walking it, which leads to two use-after-frees: - kfree(enabler) frees the enabler while that reader dereferences enabler->event. - user_event_put() may drop the last reference to the user_event, which is then freed (via delayed_destroy_user_event() on a work queue), while the same reader does user_event_get(orig->event) on it. Both are reachable by an unprivileged task that can open user_events_data: one multithreaded process that registers an enabler and then concurrently unregisters it and calls fork() triggers the race. KASAN reports a slab-use-after-free in user_event_mm_dup() during clone(), with a "refcount_t: addition on 0" warning when the user_event is freed. The enabler use-after-free was found first; the user_event one was reported by XIAO WU, and the earlier enabler-only fix did not address it. Defer both the user_event_put() and the kfree(enabler) to a work item queued with queue_rcu_work(), so they run only after an RCU grace period, once all readers walking the enabler list have finished. The put must run in process context because user_event_put() takes event_mutex on the last reference, so a work queue is used rather than call_rcu(). The now-unlocked put lets the locked argument of user_event_enabler_destroy() be removed; all callers are updated. Fixes: 7235759084a4 ("tracing/user_events: Use remote writes for event enablement") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260707165912.2560537-2-michael.bommarito@gmail.com Reported-by: XIAO WU Closes: https://lore.kernel.org/all/tencent_89647CE40DC452B891C65C94D1B271DE8E07@qq.com/ Suggested-by: Beau Belgrave Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_events_user.c | 39 ++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index c428dafe74960..8e839370f713e 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -109,6 +109,9 @@ struct user_event_enabler { /* Track enable bit, flags, etc. Aligned for bitops. */ unsigned long values; + + /* Defer the event put and enabler free past an RCU grace period. */ + struct rcu_work put_rwork; }; /* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */ @@ -396,17 +399,39 @@ static struct user_event_group *user_event_group_create(void) return NULL; }; -static void user_event_enabler_destroy(struct user_event_enabler *enabler, - bool locked) +static void delayed_user_event_enabler_put(struct work_struct *work) { - list_del_rcu(&enabler->mm_enablers_link); + struct user_event_enabler *enabler = container_of(to_rcu_work(work), + struct user_event_enabler, put_rwork); /* No longer tracking the event via the enabler */ - user_event_put(enabler->event, locked); + user_event_put(enabler->event, false); + /* Run from queue_rcu_work(), the RCU grace period has elapsed */ kfree(enabler); } +static void user_event_enabler_destroy(struct user_event_enabler *enabler) +{ + list_del_rcu(&enabler->mm_enablers_link); + + /* + * The enabler is removed from an RCU-traversed list + * (user_event_mm_dup() walks mm->enablers under rcu_read_lock() only), + * and readers there dereference enabler->event and take a new ref on + * it. Both the put of that event reference and the free of the enabler + * therefore have to wait for a grace period so no reader can be looking + * at the enabler or racing the last put of its event. + * + * The put itself must not run in RCU context: when it drops the last + * reference user_event_put() takes event_mutex, which cannot be taken + * from a softirq/RCU callback. Defer both to a work item scheduled + * after a grace period via queue_rcu_work(). + */ + INIT_RCU_WORK(&enabler->put_rwork, delayed_user_event_enabler_put); + queue_rcu_work(system_percpu_wq, &enabler->put_rwork); +} + static int user_event_mm_fault_in(struct user_event_mm *mm, unsigned long uaddr, int attempt) { @@ -464,7 +489,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work) /* User asked for enabler to be removed during fault */ if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) { - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); goto out; } @@ -764,7 +789,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm) struct user_event_enabler *enabler, *next; list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link) - user_event_enabler_destroy(enabler, false); + user_event_enabler_destroy(enabler); mmdrop(mm->mm); kfree(mm); @@ -2655,7 +2680,7 @@ static long user_events_ioctl_unreg(unsigned long uarg) flags |= enabler->values & ENABLE_VAL_COMPAT_MASK; if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler))) - user_event_enabler_destroy(enabler, true); + user_event_enabler_destroy(enabler); /* Removed at least one */ ret = 0; From fcff712d0e3d183843ec3916470ee6cc3455baad Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 22 Jun 2026 15:53:38 +0800 Subject: [PATCH 2000/3297] wifi: libertas_tf: fix use-after-free in lbtf_free_adapter() commit aa6dcd5c8dd9ba1d7d0f60093bcda41c0d6d438d upstream. lbtf_free_adapter() calls timer_delete(&priv->command_timer), which does not wait for a running command_timer_fn() callback. lbtf_free_adapter() runs on the teardown path right before ieee80211_free_hw() frees priv, both in lbtf_remove_card() and in the probe error path. command_timer is armed by mod_timer() in lbtf_cmd() whenever a firmware command is sent. command_timer_fn() dereferences priv. If a command times out as the device is removed, command_timer_fn() runs concurrently with teardown and dereferences priv after it has been freed. This is the same use-after-free that commit 03cc8f90d053 ("wifi: libertas: fix use-after-free in lbs_free_adapter()") fixed in the sibling libertas driver. The libertas_tf variant has the identical pattern and was left unchanged. Use timer_delete_sync() so any in-flight callback completes before priv is freed. Fixes: 06b16ae53192 ("libertas_tf: main.c, data paths and mac80211 handlers") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/178211481807.2212567.8773346114561900100@maoyixie.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/libertas_tf/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index fb20fe31cd363..42be6fa22f9c6 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -174,7 +174,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv) { lbtf_deb_enter(LBTF_DEB_MAIN); lbtf_free_cmd_buffer(priv); - timer_delete(&priv->command_timer); + timer_delete_sync(&priv->command_timer); lbtf_deb_leave(LBTF_DEB_MAIN); } From 633cadbc0b8323f5cc140a285d2432089dbb534e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 19 Jun 2026 14:52:08 +0200 Subject: [PATCH 2001/3297] locking/rt: Fix the incorrect RCU protection in rt_spin_unlock() commit 89038cc87d80c77e7aa6f42a64b2573b74af339f upstream. rt_spin_unlock() releases the RCU protection before unlocking the lock. That opens the door for the following UAF scenario: T1 T2 spin_lock(&p->lock); rcu_read_lock(); invalidate(p); p = rcu_dereference(ptr); rcu_assign_pointer(ptr, NULL); if (!p) return; spin_unlock(&p->lock); spin_lock(&p->lock) lock(&lock->lock); rcu_read_lock(); kfree_rcu(p); rcu_read_unlock(); .... spin_unlock(&p->lock) rcu_read_unlock(); // Ends grace period rcu_do_batch() kfree(p); UAF -> rt_mutex_cmpxchg_release(&lock->lock...) Regular spinlocks keep preemption disabled accross the unlock operation, which provides full RCU protection, but the RT substitution fails to resemble that. Same applies for the rwlock substitution. Move the rcu_read_unlock() invocation past the unlock operations to match the non-RT semantics. This makes it asymmetric vs. rt_xxx_lock(), but that's harmless as the caller needs to hold RCU read lock across the lock operation. The migrate_enable() call stays before the unlock operation because there is no per CPU operation in the unlock path which would require migration to be kept disabled. Fixes: 0f383b6dc96e ("locking/spinlock: Provide RT variant") Reported-by: syzbot+000c800a02097aaa10ed@syzkaller.appspotmail.com Decoded-by: Jann Horn Signed-off-by: Thomas Gleixner Reviewed-by: Sebastian Andrzej Siewior Acked-by: Al Viro Cc: stable@vger.kernel.org Link: https://patch.msgid.link/87jyrud75z.ffs@fw13 Signed-off-by: Greg Kroah-Hartman --- kernel/locking/spinlock_rt.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/kernel/locking/spinlock_rt.c b/kernel/locking/spinlock_rt.c index db1e11b45de67..1d5e1b3c60bfa 100644 --- a/kernel/locking/spinlock_rt.c +++ b/kernel/locking/spinlock_rt.c @@ -79,10 +79,27 @@ void __sched rt_spin_unlock(spinlock_t *lock) __releases(RCU) { spin_release(&lock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); if (unlikely(!rt_mutex_cmpxchg_release(&lock->lock, current, NULL))) rt_mutex_slowunlock(&lock->lock); + + /* + * This must be last to prevent the following UAF: + * + * T1 T2 + * spin_lock(&p->lock); rcu_read_lock(); + * invalidate(p); p = rcu_dereference(ptr); + * rcu_assign_pointer(ptr, NULL); if (!p) return; + * spin_unlock(&p->lock); spin_lock(&p->lock); + * kfree_rcu(p); rcu_read_unlock(); + * .... + * spin_unlock(&p->lock) + * rcu_read_unlock(); // Ends grace period + * rcu_do_batch() + * kfree(p); + * UAF -> rt_mutex_cmpxchg_release(&p->lock.lock...) + */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_spin_unlock); @@ -262,17 +279,21 @@ void __sched rt_read_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); rwbase_read_unlock(&rwlock->rwbase, TASK_RTLOCK_WAIT); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_read_unlock); void __sched rt_write_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); - rcu_read_unlock(); migrate_enable(); rwbase_write_unlock(&rwlock->rwbase); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_write_unlock); From ea6a188ee805e841a0e242f14d15953e0dfa74bb Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 16 Jun 2026 19:20:17 +0800 Subject: [PATCH 2002/3297] posix-cpu-timers: Use u64 multiplication in update_rlimit_cpu() commit 26aff38fefb1d6cd87e22525f41cc8f1aa61b24f upstream. update_rlimit_cpu() converts the RLIMIT_CPU value to nanoseconds with u64 nsecs = rlim_new * NSEC_PER_SEC; On 32-bit kernels both rlim_new (unsigned long) and NSEC_PER_SEC (1000000000L) are 32-bit, so the multiplication is performed in unsigned long and truncated for rlim_new > 4 seconds before being widened to u64. The same file already casts to u64 for the matching computation in check_process_timers(): u64 softns = (u64)soft * NSEC_PER_SEC; As a result, the truncated value is installed into the CPUCLOCK_PROF expiry cache (nextevt), causing the process CPU timer to be programmed to fire prematurely for any RLIMIT_CPU soft limit >= 5 seconds. The actual SIGXCPU/SIGKILL decision in check_process_timers() already casts to u64 and is therefore correct, so limit enforcement is not broken; only the expiry-cache programming is wrong. Apply the same cast here so both paths convert rlim_cur identically. 64-bit kernels are unaffected. Fixes: 858cf3a8c599 ("timers/itimer: Convert internal cputime_t units to nsec") Signed-off-by: Zhan Xusheng Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260616112017.1681372-1-zhanxusheng@xiaomi.com Signed-off-by: Greg Kroah-Hartman --- kernel/time/posix-cpu-timers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index d6ccd9fbf5b96..99affd9e228ec 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -41,7 +41,7 @@ void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit) */ int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new) { - u64 nsecs = rlim_new * NSEC_PER_SEC; + u64 nsecs = (u64)rlim_new * NSEC_PER_SEC; unsigned long irq_fl; if (!lock_task_sighand(task, &irq_fl)) From afd147e59b32a00dc505f88fe2922bf07c82bb81 Mon Sep 17 00:00:00 2001 From: Cao Ruichuang Date: Tue, 7 Apr 2026 18:26:13 +0800 Subject: [PATCH 2003/3297] selftests/ftrace: Drop invalid top-level local in test_ownership commit 808c447df2fe234eb7d9e08ecf53159d291c104c upstream. test_ownership.tc is sourced by ftracetest under /bin/sh. The script currently declares mount_point with local at file scope, which makes /bin/sh abort with "local: not in a function" before the test can reach the eventfs ownership checks. Replace the top-level local declaration with a normal shell variable so kernels that support the gid= tracefs mount option can run the test at all. Link: https://lore.kernel.org/r/20260407102613.81419-1-create0818@163.com Fixes: 8b55572e51805 ("tracing/selftests: Add tracefs mount options test") Signed-off-by: Cao Ruichuang Reviewed-by: Steven Rostedt (Google) Acked-by: Masami Hiramatsu (Google) Cc: stable@vger.kernel.org Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc b/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc index e71cc3ad0bdf9..6d00d3c0f493a 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/test_ownership.tc @@ -6,7 +6,7 @@ original_group=`stat -c "%g" .` original_owner=`stat -c "%u" .` -local mount_point=$(get_mount_point) +mount_point=$(get_mount_point) mount_options=$(get_mnt_options "$mount_point") From f77117530fc3f5932ffb107182a6dd34006be9b6 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 16:37:17 +0000 Subject: [PATCH 2004/3297] cpu: hotplug: Preserve per instance callback errors commit 673db10729fb121ea1b16fe57791a0cb9eac1eb5 upstream. cpuhp_invoke_callback() unwinds earlier callbacks for the same hotplug state when one instance fails. The rollback path currently reuses ret, so a successful rollback can hide the original error and make the failed transition look successful. Keep the rollback result separate from the original error. Fixes: 724a86881d03 ("smp/hotplug: Callback vs state-machine consistency") Signed-off-by: Bradley Morgan Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260619163719.12103-1-include@grrlz.net Signed-off-by: Greg Kroah-Hartman --- kernel/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index 15000c7abc659..63d7433e832c8 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -175,7 +175,7 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, struct cpuhp_step *step = cpuhp_get_step(state); int (*cbm)(unsigned int cpu, struct hlist_node *node); int (*cb)(unsigned int cpu); - int ret, cnt; + int ret, cnt, rollback_ret; if (st->fail == state) { st->fail = CPUHP_INVALID; @@ -239,12 +239,12 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, break; trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); - ret = cbm(cpu, node); - trace_cpuhp_exit(cpu, st->state, state, ret); + rollback_ret = cbm(cpu, node); + trace_cpuhp_exit(cpu, st->state, state, rollback_ret); /* * Rollback must not fail, */ - WARN_ON_ONCE(ret); + WARN_ON_ONCE(rollback_ret); } return ret; } From 6cb15b81ff545840048fb0e1a6e827d560dbf367 Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Fri, 19 Jun 2026 16:37:18 +0000 Subject: [PATCH 2005/3297] cpu: hotplug: Bound hotplug states sysfs output commit 86f436567f2516a0083b210bedc933544826a2c3 upstream. states_show() adds CPU hotplug state names into a single sysfs buffer using sprintf(). With enough registered states, this can write past the end of the PAGE_SIZE buffer. Use sysfs_emit_at() so output is bounded. Fixes: 98f8cdce1db5 ("cpu/hotplug: Add sysfs state interface") Signed-off-by: Bradley Morgan Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260619163719.12103-2-include@grrlz.net Signed-off-by: Greg Kroah-Hartman --- kernel/cpu.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index 63d7433e832c8..27ad827689e48 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -2865,21 +2865,17 @@ static const struct attribute_group cpuhp_cpu_attr_group = { .name = "hotplug", }; -static ssize_t states_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t states_show(struct device *dev, struct device_attribute *attr, char *buf) { - ssize_t cur, res = 0; + ssize_t res = 0; int i; mutex_lock(&cpuhp_state_mutex); for (i = CPUHP_OFFLINE; i <= CPUHP_ONLINE; i++) { struct cpuhp_step *sp = cpuhp_get_step(i); - if (sp->name) { - cur = sprintf(buf, "%3d: %s\n", i, sp->name); - buf += cur; - res += cur; - } + if (sp->name) + res += sysfs_emit_at(buf, res, "%3d: %s\n", i, sp->name); } mutex_unlock(&cpuhp_state_mutex); return res; From 0630f2c3c16c08271f550bee7c260dcde95fc8d1 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:10 +0200 Subject: [PATCH 2006/3297] gpio: mt7621: more robust management of IRQ domain teardown commit 839738536adabae1a7e98ed3fc332ce9cc991d27 upstream. The driver uses devm_gpiochip_add_data() to register the GPIO chips which means the devres subsystem will unregister them only after the function 'mt7621_gpio_remove()' returns. During the window between domain destruction and devres unregistering the GPIO chips, the chips are still fully active. If a consumer or userspace invokes gpiod_to_irq() during this window, 'mt7621_gpio_to_irq()' can dereference the already-freed irq domain pointer. Thus, manage the IRQ domain teardown using 'devm_add_action_or_reset()' to guarantee it is destroyed strictly after the GPIO chips are removed. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a46f2e5720f5 ("gpio: mt7621: fix interrupt banks mapping on gpio chips") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-3-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mt7621.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index a814885ccd5d1..5e973f8c33329 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -270,9 +270,9 @@ static const struct irq_chip mt7621_irq_chip = { }; static void -mt7621_gpio_remove(struct platform_device *pdev) +mt7621_gpio_remove(void *data) { - struct mtk *priv = platform_get_drvdata(pdev); + struct mtk *priv = data; int offset, virq; if (priv->gpio_irq > 0) @@ -473,14 +473,14 @@ mediatek_gpio_probe(struct platform_device *pdev) if (mtk->gpio_irq > 0) { ret = mt7621_gpio_irq_setup(pdev, mtk); if (ret) - goto fail; + return ret; } - return 0; + ret = devm_add_action_or_reset(dev, mt7621_gpio_remove, mtk); + if (ret) + return ret; -fail: - mt7621_gpio_remove(pdev); - return ret; + return 0; } static const struct of_device_id mediatek_gpio_match[] = { @@ -491,7 +491,6 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match); static struct platform_driver mediatek_gpio_driver = { .probe = mediatek_gpio_probe, - .remove = mt7621_gpio_remove, .driver = { .name = "mt7621_gpio", .of_match_table = mediatek_gpio_match, From ac761e66708d51dac35c4c7f1891ea991dc788f0 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 23:24:39 +0800 Subject: [PATCH 2007/3297] gpio: tegra: do not call pinctrl for GPIO direction commit d3e91a95b2b0fc6336dbf3ec90d831a1654d2720 upstream. tegra_gpio_direction_input() and tegra_gpio_direction_output() already program the GPIO controller direction registers directly. The additional pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl operation, because the Tegra pinmux ops provide GPIO request/free handling but no gpio_set_direction hook. The extra call still enters the pinctrl core and takes pctldev->mutex. Shared GPIO users can call the direction path while holding their per-line spinlock, so this otherwise redundant pinctrl direction call can sleep in an atomic context. This was found by our static analysis tool and then confirmed by manual review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the Tegra pinctrl ops. The reviewed path has a default non-sleeping struct gpio_chip while the direction callback still enters the pinctrl mutex path. A directed runtime validation kept the same non-sleeping chip registration and drove: gpio_shared_proxy_direction_output() gpiod_direction_output_raw_commit() tegra_gpio_direction_output() pinctrl_gpio_direction_output() Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output() on the stack. Do not mark the whole chip as can_sleep to paper over this: can_sleep describes whether get()/set() may sleep, and Tegra value access is MMIO. Remove the redundant pinctrl direction calls and keep pinctrl involvement in the existing request/free path. Fixes: 11da90541283 ("gpio: tegra: Fix offset of pinctrl calls") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619152439.1239561-1-runyu.xiao@seu.edu.cn Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-tegra.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index b14052fe64ac6..a5994f2ee4316 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -172,18 +172,11 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_input(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl input direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_direction_output(struct gpio_chip *chip, @@ -191,19 +184,12 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - int ret; tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); tegra_gpio_enable(tgi, offset); - ret = pinctrl_gpio_direction_output(chip, offset); - if (ret < 0) - dev_err(tgi->dev, - "Failed to set pinctrl output direction of GPIO %d: %d", - chip->base + offset, ret); - - return ret; + return 0; } static int tegra_gpio_get_direction(struct gpio_chip *chip, From b6e040b5143cc2d378f7490b560e39d56afe09aa Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:11 +0200 Subject: [PATCH 2008/3297] gpio: mt7621: be sure IRQ domain is created before exposing GPIO chips commit 0e024f58291dfcb28d98c512002e1a80fad69798 upstream. Function 'mediatek_gpio_bank_probe()' registers three GPIO chips using 'devm_gpiochip_add_data()'. At this point, the chips become live and visible to consumers. However, the IRQ domain isn't allocated and set up until 'mt7621_gpio_irq_setup()' is called after the GPIO chips setup finishes. If a consumer requests a GPIO IRQ concurrently 'mt7621_gpio_to_irq()' can be called and pass a NULL irq domain pointer irq_create_mapping(), that can corrupt the mappings or cause a crash. Fix this possible problem seting up irq domain before GPIO chips setup is performed. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a46f2e5720f5 ("gpio: mt7621: fix interrupt banks mapping on gpio chips") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-4-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mt7621.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 5e973f8c33329..2f695d8016909 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -464,12 +464,6 @@ mediatek_gpio_probe(struct platform_device *pdev) mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT; platform_set_drvdata(pdev, mtk); - for (i = 0; i < MTK_BANK_CNT; i++) { - ret = mediatek_gpio_bank_probe(dev, i); - if (ret) - return ret; - } - if (mtk->gpio_irq > 0) { ret = mt7621_gpio_irq_setup(pdev, mtk); if (ret) @@ -480,6 +474,12 @@ mediatek_gpio_probe(struct platform_device *pdev) if (ret) return ret; + for (i = 0; i < MTK_BANK_CNT; i++) { + ret = mediatek_gpio_bank_probe(dev, i); + if (ret) + return ret; + } + return 0; } From 4e16bc75c750213eac24e5494be2753d3e7ea3df Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Mon, 29 Jun 2026 16:07:02 +0200 Subject: [PATCH 2009/3297] gpio-f7188x: Add support for NCT6126D version B commit 9a6c0b6ea12746d50cf53d59a7e05fd83f974bda upstream. The Nuvoton NCT6126D Super-I/O is available in two hardware revisions. According to the manufacturer datasheet revision 2.4, version A reports chip ID 0xD283, while version B reports chip ID 0xD284. The driver currently only recognizes only the version A ID. Version B only contains hardware fixes unrelated to the GPIO functionality, so it can be supported by simply adding its chip ID without any other driver changes. Fixes: 3002b8642f01 ("gpio-f7188x: fix chip name and pin count on Nuvoton chip") Cc: stable@vger.kernel.org Signed-off-by: Paul Louvel Link: https://patch.msgid.link/20260629-gpio-f7188x-nct6126d-version-b-v1-1-a06226c02a2d@bootlin.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-f7188x.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 4d5b927ad70fd..fb007b9787290 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -48,7 +48,8 @@ /* * Nuvoton devices. */ -#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */ +#define SIO_NCT6126D_VER_A_ID 0xD283 /* NCT6126D version A chipset ID */ +#define SIO_NCT6126D_VER_B_ID 0xD284 /* NCT6126D version B chipset ID */ #define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */ @@ -564,7 +565,8 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) case SIO_F81865_ID: sio->type = f81865; break; - case SIO_NCT6126D_ID: + case SIO_NCT6126D_VER_A_ID: + case SIO_NCT6126D_VER_B_ID: sio->device = SIO_LD_GPIO_NUVOTON; sio->type = nct6126d; break; From d3b9026ef78da3018a7d2c5a9c9d611de6d45c47 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Fri, 26 Jun 2026 08:01:09 +0200 Subject: [PATCH 2010/3297] gpio: mt7621: avoid corruption of shared interrupt trigger state commit 1781172526d1092323af443fa03f00e6de560401 upstream. The bank-shared fields like 'rising' and 'falling' are modified using non-atomic read-modify-write operations. Since every gpio chip instance represents an entire bank of 32 pins, if 'mediatek_gpio_irq_type()' is called concurrently for different IRQs on the same bank a possible overwrite of each other's configuration is possible. Thus, protect this state with 'gpio_generic_lock_irqsave' lock in the same way it is handled in irp_chip 'mediatek_gpio_irq_mask()' and 'mediatek_gpio_irq_unmask()' callbacks. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: 4ba9c3afda41 ("gpio: mt7621: Add a driver for MT7621") Signed-off-by: Sergio Paracuellos Link: https://patch.msgid.link/20260626060112.2498324-2-sergio.paracuellos@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mt7621.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 2f695d8016909..1b0b5247d3c9a 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -187,6 +187,8 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) struct mtk_gc *rg = gpiochip_get_data(gc); u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg)); + guard(gpio_generic_lock_irqsave)(&rg->chip); + if (type == IRQ_TYPE_PROBE) { if ((rg->rising | rg->falling | rg->hlevel | rg->llevel) & mask) From 5d7bd8790309b9e344da099117f449a9e86eeb6a Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sat, 4 Jul 2026 10:40:54 +0200 Subject: [PATCH 2011/3297] gpios: palmas: add .get_direction() op commit db4a79713ed8e252d5e4edf6eaaa80948b6855a2 upstream. Accessing debug/gpio is quite noisy without a get_direction() implementation. To calm that down add an implementation. Fixes: 3d50a2785271 ("gpio: palmas: Add support for Palmas GPIO") Cc: stable@vger.kernel.org Reviewed-by: Linus Walleij Signed-off-by: Andreas Kemnade Link: https://patch.msgid.link/20260704-palmas-getdirection-v2-1-2fd85fee3832@kemnade.info Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-palmas.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index e377f6dd4ccf1..e64ee04877185 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -116,6 +116,24 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset) return ret; } +static int palmas_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct palmas_gpio *pg = gpiochip_get_data(gc); + struct palmas *palmas = pg->palmas; + unsigned int val; + unsigned int reg; + int ret; + int gpio16 = (offset/8); + + offset %= 8; + reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; + ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val); + if (ret) + return ret; + + return (val & BIT(offset)) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct palmas_gpio *pg = gpiochip_get_data(gc); @@ -165,6 +183,7 @@ static int palmas_gpio_probe(struct platform_device *pdev) palmas_gpio->gpio_chip.can_sleep = true; palmas_gpio->gpio_chip.direction_input = palmas_gpio_input; palmas_gpio->gpio_chip.direction_output = palmas_gpio_output; + palmas_gpio->gpio_chip.get_direction = palmas_gpio_get_direction; palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq; palmas_gpio->gpio_chip.set = palmas_gpio_set; palmas_gpio->gpio_chip.get = palmas_gpio_get; From 3118e97dae533fb45964b87bbed1801afcff7c65 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 15:08:17 +0800 Subject: [PATCH 2012/3297] net: sit: require CAP_NET_ADMIN in the device netns for changelink commit 27ccb68e7cccead5d8c611665a45d23032d468b3 upstream. ipip6_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ipip6_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. sit was the one tunnel type not covered by the recent series that added this check to the other changelink() handlers. Fixes: 5e6700b3bf98 ("sit: add support of x-netns") Link: https://lore.kernel.org/netdev/20260612085941.3158249-1-maoyixie.tju@gmail.com/ Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Nicolas Dichtel Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260618070817.3378283-1-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/sit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 6a833ee665e9b..c03df1682a639 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1610,6 +1610,9 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], __u32 fwmark = t->fwmark; int err; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + if (dev == sitn->fb_tunnel_dev) return -EINVAL; From 121c5f31c3fb70d4a23e8a084f5cb8b3ec63be8d Mon Sep 17 00:00:00 2001 From: Philippe Schenker Date: Thu, 18 Jun 2026 11:30:24 +0200 Subject: [PATCH 2013/3297] net: ethernet: ti: icssg: guard PA stat lookups commit 27b9daba50609335db6ca81e4cccf50ded21ec76 upstream. icssg_ndo_get_stats64() unconditionally calls emac_get_stat_by_name() with FW PA stat names regardless of whether the PA stats block is present on the hardware. emac_get_stat_by_name() already guards the PA stats lookup with `if (emac->prueth->pa_stats)`; when that pointer is NULL the lookup falls through to netdev_err() and returns -EINVAL. Because ndo_get_stats64 is polled regularly by the networking stack this produces thousands of log entries of the form: icssg-prueth icssg1-eth end0: Invalid stats FW_RX_ERROR A secondary consequence is that the int(-EINVAL) return value is implicitly widened to a near-ULLONG_MAX unsigned value when accumulated into the __u64 fields of rtnl_link_stats64, silently corrupting the rx_errors, rx_dropped and tx_dropped counters reported by `ip -s link`. Every other PA-aware code path in the driver is already guarded with the same `if (emac->prueth->pa_stats)` check. Apply the same guard here. Fixes: 0d15a26b247d ("net: ti: icssg-prueth: Add ICSSG FW Stats") Signed-off-by: Philippe Schenker Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman Cc: danishanwar@ti.com Cc: rogerq@kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260618093037.3448858-1-dev@pschenker.ch Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/icssg/icssg_common.c | 49 +++++++++++--------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index 57e5f1c88f509..93bc054e3fba3 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -1315,28 +1315,35 @@ void icssg_ndo_get_stats64(struct net_device *ndev, stats->rx_over_errors = emac_get_stat_by_name(emac, "rx_over_errors"); stats->multicast = emac_get_stat_by_name(emac, "rx_multicast_frames"); - stats->rx_errors = ndev->stats.rx_errors + - emac_get_stat_by_name(emac, "FW_RX_ERROR") + - emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") + - emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") + - emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") + - emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN"); - stats->rx_dropped = ndev->stats.rx_dropped + - emac_get_stat_by_name(emac, "FW_DROPPED_PKT") + - emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") + - emac_get_stat_by_name(emac, "FW_INF_SAV") + - emac_get_stat_by_name(emac, "FW_INF_SA_DL") + - emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") + - emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") + - emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER"); + stats->rx_errors = ndev->stats.rx_errors; + stats->rx_dropped = ndev->stats.rx_dropped; stats->tx_errors = ndev->stats.tx_errors; - stats->tx_dropped = ndev->stats.tx_dropped + - emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") + - emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") + - emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") + - emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF"); + stats->tx_dropped = ndev->stats.tx_dropped; + + if (!emac->prueth->pa_stats) + return; + + stats->rx_errors += + emac_get_stat_by_name(emac, "FW_RX_ERROR") + + emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") + + emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") + + emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") + + emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN"); + stats->rx_dropped += + emac_get_stat_by_name(emac, "FW_DROPPED_PKT") + + emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") + + emac_get_stat_by_name(emac, "FW_INF_SAV") + + emac_get_stat_by_name(emac, "FW_INF_SA_DL") + + emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") + + emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") + + emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER"); + stats->tx_dropped += + emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") + + emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") + + emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") + + emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF"); } EXPORT_SYMBOL_GPL(icssg_ndo_get_stats64); From e89b8829693e65217d587dceeaad4826c96c731b Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Sun, 21 Jun 2026 11:17:14 +0800 Subject: [PATCH 2014/3297] net: wwan: t7xx: destroy DMA pool on CLDMA late init failure commit 2bd6f26d4ce1e87de4d736b1e8896daf3acf1c0e upstream. t7xx_cldma_late_init() creates md_ctrl->gpd_dmapool before initializing the TX and RX rings. If any ring initialization fails, the error path frees the already initialized rings but leaves the DMA pool allocated. Destroy md_ctrl->gpd_dmapool on the late-init failure path to avoid leaking the DMA pool. Fixes: 39d439047f1d ("net: wwan: t7xx: Add control DMA interface") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260621031714.3605022-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wwan/t7xx/t7xx_hif_cldma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c index 97163e1e5783e..f7aa9398f5cd2 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c +++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c @@ -1066,6 +1066,9 @@ static int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl) while (i--) t7xx_cldma_ring_free(md_ctrl, &md_ctrl->tx_ring[i], DMA_TO_DEVICE); + dma_pool_destroy(md_ctrl->gpd_dmapool); + md_ctrl->gpd_dmapool = NULL; + return ret; } From 3f85fcd520aa703824b1958f35169925ddae2558 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 12:30:15 +0800 Subject: [PATCH 2015/3297] net: ixp4xx_hss: fix duplicate HDLC netdev allocation commit db818b0e8af7bac16860116a19c341a63d6677b4 upstream. ixp4xx_hss_probe() allocates two HDLC netdevs. The first one is stored in ndev, initialized, and registered with register_hdlc_device(). The second one is stored in port->netdev and later used by the remove path for unregister_hdlc_device() and free_netdev(). This means that the registered netdev is not the same object that is unregistered and freed on remove. It also leaks the first allocation if the second alloc_hdlcdev() call fails, and the first allocation is not checked before ndev is used. Older code allocated the HDLC netdev only once and stored the same object in both the local variable and port->netdev. The buggy conversion split this into two alloc_hdlcdev() calls. A later rename changed the local variable name to ndev, but the underlying mismatch remained. Fix this by allocating the HDLC netdev only once and assigning the same object to port->netdev. Fixes: 99ebe65eb9c0 ("net: ixp4xx_hss: move out assignment in if condition") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260622043015.643637-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wan/ixp4xx_hss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 720c5dc889ea3..7f4645ff90aa9 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -1487,11 +1487,11 @@ static int ixp4xx_hss_probe(struct platform_device *pdev) "unable to get CLK internal GPIO\n"); ndev = alloc_hdlcdev(port); - port->netdev = alloc_hdlcdev(port); - if (!port->netdev) { + if (!ndev) { err = -ENOMEM; goto err_plat; } + port->netdev = ndev; SET_NETDEV_DEV(ndev, &pdev->dev); hdlc = dev_to_hdlc(ndev); From b3d835407846134b0d54637c0281b39bebef831d Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 14 Jun 2026 01:42:39 +0800 Subject: [PATCH 2016/3297] net/sched: act_ct: preserve tc_skb_cb across defragmentation commit 9092e15defbe6c7bc241c306093ca9d358a578e7 upstream. tcf_ct_handle_fragments() calls nf_ct_handle_fragments() without saving and restoring skb->cb. The defrag helper clears IPCB/IP6CB, which aliases the tc_skb_cb/qdisc_skb_cb control buffer. Fragmented traffic through act_ct therefore loses qdisc metadata such as pkt_segs and can trigger WARN_ON_ONCE() in qdisc_pkt_segs() when panic_on_warn is enabled. Save and restore the full tc_skb_cb around nf_ct_handle_fragments(), matching the pattern used by ovs_ct_handle_fragments(). Fixes: ec624fe740b4 ("net/sched: Extend qdisc control block with tc control block") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Link: https://patch.msgid.link/510c51217fd7aaf29c6dc298bab8d643fe229b1c.1781358692.git.xizh2024@lzu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/act_ct.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 9bf27c02ea45d..27d9a7181a0d3 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -842,11 +842,11 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, u8 family, u16 zone, bool *defrag) { enum ip_conntrack_info ctinfo; + struct tc_skb_cb cb; struct nf_conn *ct; int err = 0; bool frag; u8 proto; - u16 mru; /* Previously seen (loopback)? Ignore. */ ct = nf_ct_get(skb, &ctinfo); @@ -860,12 +860,13 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, if (err || !frag) return err; - err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &mru); + cb = *tc_skb_cb(skb); + err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru); if (err) return err; *defrag = true; - tc_skb_cb(skb)->mru = mru; + *tc_skb_cb(skb) = cb; return 0; } From ab625256882e0e25ff6d72cc276424d8c02b21fc Mon Sep 17 00:00:00 2001 From: Ross Porter Date: Wed, 17 Jun 2026 18:10:39 +1200 Subject: [PATCH 2017/3297] selftests: net: fix file owner for broadcast_ether_dst test commit b8613e9792002add3bf77122868cc06ce142e953 upstream. Ensure the output file is always owned by root (even if tcpdump was compiled with `--with-user`), by passing the `-Z root` argument when invoking it. Cc: stable@vger.kernel.org Reported-by: Edoardo Canepa Closes: https://bugs.launchpad.net/ubuntu-kernel-tests/+bug/2129815 Fixes: bf59028ea8d4 ("selftests: net: add test for destination in broadcast packets") Suggested-by: Edoardo Canepa Tested-by: Ross Porter Signed-off-by: Ross Porter Link: https://patch.msgid.link/20260617061039.79717-2-ross.porter@canonical.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/broadcast_ether_dst.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/broadcast_ether_dst.sh b/tools/testing/selftests/net/broadcast_ether_dst.sh index 334a7eca8a803..cc571f6074297 100755 --- a/tools/testing/selftests/net/broadcast_ether_dst.sh +++ b/tools/testing/selftests/net/broadcast_ether_dst.sh @@ -44,7 +44,7 @@ test_broadcast_ether_dst() { # tcpdump will exit after receiving a single packet # timeout will kill tcpdump if it is still running after 2s timeout 2s ip netns exec "${CLIENT_NS}" \ - tcpdump -i link0 -c 1 -w "${CAPFILE}" icmp &> "${OUTPUT}" & + tcpdump -i link0 -c 1 -w "${CAPFILE}" -Z root icmp &> "${OUTPUT}" & pid=$! slowwait 1 grep -qs "listening" "${OUTPUT}" From 6596baf8041100a9d1647beb5ea8ea4496ce058f Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 16 Jun 2026 22:24:24 +0800 Subject: [PATCH 2018/3297] net: ena: clean up XDP TX queues when regular TX setup fails commit 1bd6676254b4ab6acd44b662b5e92822c036463a upstream. create_queues_with_size_backoff() creates XDP TX queues before setting up the regular TX path. If the subsequent allocation or creation of regular TX queues fails, the error handling paths omit the teardown of the XDP TX queues, leading to a resource leak. Fix this by explicitly destroying the XDP TX queue subset at the two missing failure points. 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-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have an ENA device to test with, no runtime testing was able to be performed. Fixes: 548c4940b9f1 ("net: ena: Implement XDP_TX action") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Reviewed-by: Arthur Kiyanovski Tested-by: Arthur Kiyanovski Link: https://patch.msgid.link/20260616142424.4005130-1-dawei.feng@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/amazon/ena/ena_netdev.c | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 92d149d4f0917..5d05020a6d058 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -752,6 +752,18 @@ static void ena_destroy_all_tx_queues(struct ena_adapter *adapter) } } +static void ena_destroy_xdp_tx_queues(struct ena_adapter *adapter) +{ + u16 ena_qid; + int i; + + for (i = adapter->xdp_first_ring; + i < adapter->xdp_first_ring + adapter->xdp_num_queues; i++) { + ena_qid = ENA_IO_TXQ_IDX(i); + ena_com_destroy_io_queue(adapter->ena_dev, ena_qid); + } +} + static void ena_destroy_all_rx_queues(struct ena_adapter *adapter) { u16 ena_qid; @@ -2078,14 +2090,21 @@ static int create_queues_with_size_backoff(struct ena_adapter *adapter) rc = ena_setup_tx_resources_in_range(adapter, 0, adapter->num_io_queues); - if (rc) + if (rc) { + ena_destroy_xdp_tx_queues(adapter); + ena_free_all_io_tx_resources_in_range(adapter, + adapter->xdp_first_ring, + adapter->xdp_num_queues); goto err_setup_tx; + } rc = ena_create_io_tx_queues_in_range(adapter, 0, adapter->num_io_queues); - if (rc) + if (rc) { + ena_destroy_xdp_tx_queues(adapter); goto err_create_tx_queues; + } rc = ena_setup_all_rx_resources(adapter); if (rc) From 0b2f9c908f930ec4be17d389723f9a202d6a883c Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:40 +0800 Subject: [PATCH 2019/3297] net: ip6_vti: require CAP_NET_ADMIN in the device netns for changelink commit e2ac3b242c37dff323a964962e43854f4b1a2b79 upstream. vti6_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate vti6_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 61220ab34948 ("vti6: Enable namespace changing") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-7-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_vti.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index d871cab6938d3..ab94b3a4ba9c3 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -1046,6 +1046,9 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], struct __ip6_tnl_parm p; struct vti6_net *ip6n; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + ip6n = net_generic(net, vti6_net_id); if (dev == ip6n->fb_tnl_dev) return -EINVAL; From 9571af2eec8023af9a1671b7f2cd4ab400011724 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:37 +0800 Subject: [PATCH 2020/3297] net: ip_vti: require CAP_NET_ADMIN in the device netns for changelink commit 95cceadbfd52d7239bd730afdda0655287d77425 upstream. vti_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate vti_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 895de9a3488a ("vti4: Enable namespace changing") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-4-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_vti.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 95b6bb78fcd27..3b80929994a00 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -596,6 +596,9 @@ static int vti_changelink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm_kern p; __u32 fwmark = t->fwmark; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + vti_netlink_parms(data, &p, &fwmark); return ip_tunnel_changelink(dev, tb, &p, fwmark); } From 68cadc3698c7de88966d306d12ec9c6217da228a Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:36 +0800 Subject: [PATCH 2021/3297] net: ipip: require CAP_NET_ADMIN in the device netns for changelink commit 8211a26324667980a463c069469a818e71207e02 upstream. ipip_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ipip_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 6c742e714d8c ("ipip: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-3-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ipip.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 3e03af073a1cc..94e974c201f0a 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -469,6 +469,9 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[], bool collect_md; __u32 fwmark = t->fwmark; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + if (ip_tunnel_netlink_encap_parms(data, &ipencap)) { int err = ip_tunnel_encap_setup(t, &ipencap); From 03d8843b143ebbbfaf48511922abc6e886575a61 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:39 +0800 Subject: [PATCH 2022/3297] net: ip6_gre: require CAP_NET_ADMIN in the device netns for changelink commit f00a50876d2818bd6dc86fa98b3ef360884c53c8 upstream. ip6gre_changelink() and ip6erspan_changelink() operate on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate both ops on rtnl_dev_link_net_capable() at their top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 690afc165bb3 ("net: ip6_gre: fix moving ip6gre between namespaces") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-6-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_gre.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 6dea7caf5a777..75961c4ebcdd2 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -2046,6 +2046,9 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[], struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); struct __ip6_tnl_parm p; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) return PTR_ERR(t); @@ -2265,6 +2268,9 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[], struct __ip6_tnl_parm p; struct ip6gre_net *ign; + if (!rtnl_dev_link_net_capable(dev, t->net)) + return -EPERM; + ign = net_generic(t->net, ip6gre_net_id); t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) From 234cd54fc500f69db43e37de38603da617fbbeea Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Fri, 12 Jun 2026 16:59:38 +0800 Subject: [PATCH 2023/3297] net: ip6_tunnel: require CAP_NET_ADMIN in the device netns for changelink commit 2496fa0b7d180b3ad356b514e7ff93bb14e6140a upstream. ip6_tnl_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net. Gate ip6_tnl_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed. Reported-by: Xiao Liang Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ Fixes: 0bd8762824e7 ("ip6tnl: add x-netns support") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260612085941.3158249-5-maoyixie.tju@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 9e2449db0bdf2..cbc984a32a29c 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -2068,6 +2068,9 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[], struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); struct ip_tunnel_encap ipencap; + if (!rtnl_dev_link_net_capable(dev, net)) + return -EPERM; + if (dev == ip6n->fb_tnl_dev) { if (ip_tunnel_netlink_encap_parms(data, &ipencap)) { /* iproute2 always sets TUNNEL_ENCAP_FLAG_CSUM6, so From 19c148cb82d11bb7cc7d86038a94e608a297e63c Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 19:43:16 +0800 Subject: [PATCH 2024/3297] octeontx2-af: Free BPID bitmap on setup failure commit 36323f54cd323122a1be89ab2c316a6e55a94e30 upstream. nix_setup_bpids() allocates bp->bpids with rvu_alloc_bitmap(), which uses a plain kcalloc(). If any of the following devm_kcalloc() allocations for the BPID mapping arrays fails, the function returns without freeing the bitmap. Free the BPID bitmap before returning from those error paths. Fixes: d6212d2e41a0 ("octeontx2-af: Create BPIDs free pool") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260623114316.2182271-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 3563cfc679049..7de06eb8284ac 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -527,19 +527,24 @@ static int nix_setup_bpids(struct rvu *rvu, struct nix_hw *hw, int blkaddr) bp->fn_map = devm_kcalloc(rvu->dev, bp->bpids.max, sizeof(u16), GFP_KERNEL); if (!bp->fn_map) - return -ENOMEM; + goto free_bpids; bp->intf_map = devm_kcalloc(rvu->dev, bp->bpids.max, sizeof(u8), GFP_KERNEL); if (!bp->intf_map) - return -ENOMEM; + goto free_bpids; bp->ref_cnt = devm_kcalloc(rvu->dev, bp->bpids.max, sizeof(u8), GFP_KERNEL); if (!bp->ref_cnt) - return -ENOMEM; + goto free_bpids; return 0; + +free_bpids: + rvu_free_bitmap(&bp->bpids); + bp->bpids.bmap = NULL; + return -ENOMEM; } void rvu_nix_flr_free_bpids(struct rvu *rvu, u16 pcifunc) From 1905ebabe638c946aced00c4bb664da26cac56d5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 10:16:39 -0400 Subject: [PATCH 2025/3297] ieee802154: admin-gate legacy LLSEC dump operations commit 9c1e0b6d49471a712511d23fc9d06901561135e8 upstream. In net/ieee802154/netlink.c, the legacy IEEE802154_NL family ops table builds the LLSEC dump entries (LLSEC_LIST_KEY, LLSEC_LIST_DEV, LLSEC_LIST_DEVKEY, LLSEC_LIST_SECLEVEL) with IEEE802154_DUMP() which sets no .flags, so generic netlink runs them ungated. The modern nl802154 family admin-gates the equivalent reads via NL802154_CMD_GET_SEC_KEY and friends with .flags = GENL_ADMIN_PERM. Any local uid that can open AF_NETLINK / NETLINK_GENERIC can resolve the "802.15.4 MAC" family and dump LLSEC_LIST_KEY on any wpan netdev that has an LLSEC key installed; the dump handler writes the raw 16-byte AES-128 key bytes (IEEE802154_ATTR_LLSEC_KEY_BYTES, copied verbatim from struct ieee802154_llsec_key.key) into the reply. Recovering the AES key compromises 802.15.4 LLSEC link confidentiality and authenticity, since LLSEC uses CCM* and the same key authenticates and encrypts frames. Impact: any local uid with no capabilities can read the raw 16-byte AES-128 LLSEC key from the kernel keytable on any wpan netdev that has an administrator-installed LLSEC key, by issuing an LLSEC_LIST_KEY dump on the legacy IEEE802154_NL generic-netlink family. Introduce IEEE802154_DUMP_PRIV() mirroring IEEE802154_DUMP() but setting .flags = GENL_ADMIN_PERM, and use it for the four LLSEC dump entries. LIST_PHY and LIST_IFACE retain IEEE802154_DUMP() because the modern nl802154 family exposes their equivalents to unprivileged readers by design (NL802154_CMD_GET_WPAN_PHY and NL802154_CMD_GET_INTERFACE carry "can be retrieved by unprivileged users" annotations). Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/20260520141640.1149513-2-michael.bommarito@gmail.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- net/ieee802154/ieee802154.h | 8 ++++++++ net/ieee802154/netlink.c | 16 ++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index c5d91f78301ad..fd9778f705503 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -23,6 +23,14 @@ void ieee802154_nl_exit(void); .dumpit = _dump, \ } +#define IEEE802154_DUMP_PRIV(_cmd, _func, _dump) \ + { \ + .cmd = _cmd, \ + .doit = _func, \ + .dumpit = _dump, \ + .flags = GENL_ADMIN_PERM, \ + } + struct genl_info; struct sk_buff *ieee802154_nl_create(int flags, u8 req); diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 7d2de4ee6992b..9c9fd14d0ca8b 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -98,20 +98,20 @@ static const struct genl_small_ops ieee802154_ops[] = { IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams), IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams), IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL, - ieee802154_llsec_dump_keys), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL, + ieee802154_llsec_dump_keys), IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL, - ieee802154_llsec_dump_devs), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL, + ieee802154_llsec_dump_devs), IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL, - ieee802154_llsec_dump_devkeys), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL, + ieee802154_llsec_dump_devkeys), IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), - IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, - ieee802154_llsec_dump_seclevels), + IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, + ieee802154_llsec_dump_seclevels), IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, ieee802154_llsec_add_seclevel), IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, From 314f21c9dd0dc9fb986a585b75927fc2b5a7ccf3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 10:16:40 -0400 Subject: [PATCH 2026/3297] ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation commit a6bfdfcc6711d1d5a92e98644359dedc67c0c858 upstream. The LLSEC ADD/DEL doit handlers under the legacy IEEE802154_NL family consume IEEE802154_ATTR_LLSEC_KEY_BYTES and IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, both declared in net/ieee802154/nl_policy.c as bare length entries with no .type (defaulting to NLA_UNSPEC). Generic netlink strict validation rejects all NLA_UNSPEC attributes via validate_nla(), so every LLSEC_ADD_KEY, LLSEC_DEL_KEY, LLSEC_ADD_DEV, LLSEC_DEL_DEV, LLSEC_ADD_DEVKEY, LLSEC_DEL_DEVKEY, LLSEC_ADD_SECLEVEL, and LLSEC_DEL_SECLEVEL request fails at the dispatcher with "Unsupported attribute" before reaching the handler. The doit path has been silently dead since strict validation became the default for genl families that do not opt out. The dump path is unaffected because dump requests carry no LLSEC attributes to validate, which is why the LLSEC_LIST_KEY read remained reachable (patch 1/2). Introduce IEEE802154_OP_RELAXED() mirroring IEEE802154_OP() but with .validate = GENL_DONT_VALIDATE_STRICT, and use it for the eight legacy LLSEC mutate ops so admin-driven LLSEC configuration via the legacy interface works again. Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/20260520141640.1149513-3-michael.bommarito@gmail.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- net/ieee802154/ieee802154.h | 9 +++++++++ net/ieee802154/netlink.c | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index fd9778f705503..e765adc4b88f2 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -16,6 +16,15 @@ void ieee802154_nl_exit(void); .flags = GENL_ADMIN_PERM, \ } +#define IEEE802154_OP_RELAXED(_cmd, _func) \ + { \ + .cmd = _cmd, \ + .doit = _func, \ + .dumpit = NULL, \ + .flags = GENL_ADMIN_PERM, \ + .validate = GENL_DONT_VALIDATE_STRICT,\ + } + #define IEEE802154_DUMP(_cmd, _func, _dump) \ { \ .cmd = _cmd, \ diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 9c9fd14d0ca8b..cacad21347eca 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -100,22 +100,22 @@ static const struct genl_small_ops ieee802154_ops[] = { IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL, ieee802154_llsec_dump_keys), - IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), - IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL, ieee802154_llsec_dump_devs), - IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), - IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL, ieee802154_llsec_dump_devkeys), - IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), - IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, ieee802154_llsec_dump_seclevels), - IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, - ieee802154_llsec_add_seclevel), - IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, - ieee802154_llsec_del_seclevel), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_SECLEVEL, + ieee802154_llsec_add_seclevel), + IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_SECLEVEL, + ieee802154_llsec_del_seclevel), }; static const struct genl_multicast_group ieee802154_mcgrps[] = { From cb5cca1d2a908ddd5e357971de0f2009617b8d6a Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Tue, 21 Apr 2026 13:02:59 +0530 Subject: [PATCH 2027/3297] ieee802154: ca8210: fix cas_ctl leak on spi_async failure commit e09390e439bd7cca30dd10893b1f64802961667a upstream. ca8210_spi_transfer() allocates cas_ctl with kzalloc_obj(GFP_ATOMIC) and relies entirely on the SPI completion callback ca8210_spi_transfer_complete() to free it. The spi_async() API only invokes the completion callback on successful submission. On failure it returns a negative error code without ever queuing the callback, which leaves cas_ctl and its embedded spi_message and spi_transfer orphaned. Every kfree(cas_ctl) in the driver is inside the completion callback, so there is no other reclamation path. ca8210_spi_transfer() is called from ca8210_spi_exchange(), the interrupt handler ca8210_interrupt_handler(), and from the retry path inside the completion callback itself. The exchange and interrupt handler paths loop on -EBUSY, so under sustained SPI bus contention every retry iteration leaks a fresh cas_ctl (~600 bytes per occurrence). Fix it by freeing cas_ctl on the spi_async() error path. While here, correct the misleading error string: the function calls spi_async(), not spi_sync(). Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") Cc: stable@vger.kernel.org Signed-off-by: Shitalkumar Gandhi Reviewed-by: Miquel Raynal Link: https://lore.kernel.org/20260421073259.2259783-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- drivers/net/ieee802154/ca8210.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index ebc4f1b18e7bc..b272fa00e5410 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -920,9 +920,10 @@ static int ca8210_spi_transfer( if (status < 0) { dev_crit( &spi->dev, - "status %d from spi_sync in write\n", + "status %d from spi_async in write\n", status ); + kfree(cas_ctl); } return status; From 2059c28bd725beded01277cdf1f67be33e714323 Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Wed, 20 May 2026 16:27:50 +0530 Subject: [PATCH 2028/3297] ieee802154: ca8210: fix pointer truncation in kfifo on 64-bit commit 6d7f7bcf225b2d566176bf6229dbd1252940cb3c upstream. ca8210_test_int_driver_write() and ca8210_test_int_user_read() exchange a kmalloc'd buffer pointer through a struct kfifo, but pass a literal '4' as the byte count to kfifo_in()/kfifo_out(). This is correct on 32-bit (pointer = 4 bytes), but on 64-bit only the low 4 bytes of the 8-byte pointer are written into the FIFO. The reader then reads back 4 bytes into an 8-byte local pointer variable, leaving the upper 4 bytes uninitialized stack data. The first dereference of the reconstructed pointer (fifo_buffer[1]) accesses an arbitrary kernel address and generally results in an oops. Use sizeof(fifo_buffer) so the byte count matches pointer width on every architecture. The driver has no architecture restriction in Kconfig, so any 64-bit build with CONFIG_IEEE802154_CA8210_DEBUGFS=y is exposed. Issue has been latent since the driver was added in 2017 because it is most commonly deployed on 32-bit MCUs. Found via a custom Coccinelle semantic patch hunting for short-byte kfifo I/O on byte-mode kfifos used to shuttle pointers. Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") Cc: stable@vger.kernel.org Signed-off-by: Shitalkumar Gandhi Reviewed-by: Simon Horman Link: https://lore.kernel.org/20260520105750.30144-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Stefan Schmidt Signed-off-by: Greg Kroah-Hartman --- drivers/net/ieee802154/ca8210.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index b272fa00e5410..9e3695e4afabb 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -595,7 +595,7 @@ static int ca8210_test_int_driver_write( fifo_buffer = kmemdup(buf, len, GFP_KERNEL); if (!fifo_buffer) return -ENOMEM; - kfifo_in(&test->up_fifo, &fifo_buffer, 4); + kfifo_in(&test->up_fifo, &fifo_buffer, sizeof(fifo_buffer)); wake_up_interruptible(&priv->test.readq); return 0; @@ -2527,6 +2527,7 @@ static ssize_t ca8210_test_int_user_read( struct ca8210_priv *priv = filp->private_data; unsigned char *fifo_buffer; unsigned long bytes_not_copied; + unsigned int copied; if (filp->f_flags & O_NONBLOCK) { /* Non-blocking mode */ @@ -2540,7 +2541,8 @@ static ssize_t ca8210_test_int_user_read( ); } - if (kfifo_out(&priv->test.up_fifo, &fifo_buffer, 4) != 4) { + copied = kfifo_out(&priv->test.up_fifo, &fifo_buffer, sizeof(fifo_buffer)); + if (copied != sizeof(fifo_buffer)) { dev_err( &priv->spi->dev, "test_interface: Wrong number of elements popped from upstream fifo\n" From 35267819b25074084130b6a7be18bbaf44d3ae74 Mon Sep 17 00:00:00 2001 From: Ankit Garg Date: Tue, 16 Jun 2026 18:32:08 -0700 Subject: [PATCH 2029/3297] gve: fix header buffer corruption with header-split and HW-GRO commit d676c9a73bdcd8237425dbb826f2bd1a25c36e40 upstream. The DQO RX datapath programs a per-buffer-queue-descriptor header_buf_addr at post time and reads the split header back at completion time. Both the post and the read currently index the header buffer by queue position rather than by the buffer's identity: - post (gve_rx_post_buffers_dqo): header_buf_addr is computed from bufq->tail - read (gve_rx_dqo): the header is read from desc_idx (the completion queue head index) This relies on the buffer-queue index and the completion-queue index being equal for the start of every packet, i.e. on the device consuming posted buffers and returning completions in the exact same order. That assumption does not hold once HW-GRO is enabled with multiple flows: coalesced segments are accepted and completed in an order that may differ from the order buffers were posted, and segments from different flows may interleave. That results in two problems: 1. Wrong header slot on read. Because the read offset is derived from the completion index (desc_idx) while the device wrote the header to the address programmed for the buffer's buf_id, the driver can copy a header belonging to a different packet. This shows up as throughput drop (about 30% drop and large numbers of TCP retransmissions) with header-split and HW-GRO both enabled and many streams. 2. Header buffer reused while still owned by the device. The driver advances bufq->head by one per completion and re-posts buffers based on that. Arrival of N RX completions only guarantees that at least N RX buffer descriptors have been read by the device. It does not guarantee that the device has relinquished the ownership of all the buffers corresponding to those N descriptors. With out-of-order completions (e.g. the completion for a packet copied into buffer N arrives before the completion for a packet copied into buffer N-1), the driver can re-post and overwrite a header buffer that the device is still going to write into, corrupting the header of a packet whose completion has not yet been processed. Fix both issues by indexing the header buffer by buf_id on both the post and read paths. Reading from buf_id's slot is therefore always correct regardless of completion ordering (fixes problem 1). Indexing by buf_id also ties each header slot to the lifetime of its buffer state. A buffer state is only returned to the free/recycle lists when its own completion (buf_id) is processed, so its header slot can only be re-posted after the device is done with it. This makes header slot reuse safe under out-of-order completions (fixes problem 2). Allocate (gve_rx_alloc_hdr_bufs) and free (gve_rx_free_hdr_bufs) the header buffers based on num_buf_states to match the buf_id indexing. Cc: stable@vger.kernel.org Fixes: 5e37d8254e7f ("gve: Add header split data path") Signed-off-by: Ankit Garg Reviewed-by: Praveen Kaligineedi Reviewed-by: Jordan Rhee Reviewed-by: Harshitha Ramamurthy Signed-off-by: Joshua Washington Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260617013208.3781453-1-joshwash@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/google/gve/gve_rx_dqo.c | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 1aff3bbb8cfcf..6a0430a93ed75 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -21,11 +21,13 @@ static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx) { struct device *hdev = &priv->pdev->dev; - int buf_count = rx->dqo.bufq.mask + 1; if (rx->dqo.hdr_bufs.data) { - dma_free_coherent(hdev, priv->header_buf_size * buf_count, - rx->dqo.hdr_bufs.data, rx->dqo.hdr_bufs.addr); + size_t size = + (size_t)priv->header_buf_size * rx->dqo.num_buf_states; + + dma_free_coherent(hdev, size, rx->dqo.hdr_bufs.data, + rx->dqo.hdr_bufs.addr); rx->dqo.hdr_bufs.data = NULL; } } @@ -250,7 +252,7 @@ int gve_rx_alloc_ring_dqo(struct gve_priv *priv, /* Allocate header buffers for header-split */ if (cfg->enable_header_split) - if (gve_rx_alloc_hdr_bufs(priv, rx, buffer_queue_slots)) + if (gve_rx_alloc_hdr_bufs(priv, rx, rx->dqo.num_buf_states)) goto err; /* Allocate RX completion queue */ @@ -379,10 +381,13 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) break; } - if (rx->dqo.hdr_bufs.data) + if (rx->dqo.hdr_bufs.data) { + u16 buf_id = le16_to_cpu(desc->buf_id); + desc->header_buf_addr = cpu_to_le64(rx->dqo.hdr_bufs.addr + - priv->header_buf_size * bufq->tail); + (size_t)priv->header_buf_size * buf_id); + } bufq->tail = (bufq->tail + 1) & bufq->mask; complq->num_free_slots--; @@ -799,10 +804,13 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, int unsplit = 0; if (hdr_len && !hbo) { - rx->ctx.skb_head = gve_rx_copy_data(priv->dev, napi, - rx->dqo.hdr_bufs.data + - desc_idx * priv->header_buf_size, - hdr_len); + size_t offset = + (size_t)buffer_id * priv->header_buf_size; + + rx->ctx.skb_head = + gve_rx_copy_data(priv->dev, napi, + rx->dqo.hdr_bufs.data + offset, + hdr_len); if (unlikely(!rx->ctx.skb_head)) goto error; rx->ctx.skb_tail = rx->ctx.skb_head; From 55da782eb4545d9baf2cb030121f3adbc8e7f25d Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 27 Jun 2026 00:02:10 +0200 Subject: [PATCH 2030/3297] platform/x86/amd/pmc: Avoid logging "(null)" for DMI values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a0738abd042f7406edd2175a819cf2e66388ed97 upstream. dmi_get_system_info(...) can return NULL. Using that as %s arguments of dev_info() would log "(null)" (as part of a message like '... System Vendor: "(null)", Product Name: "(null)" ...'), which may be confusing for users. Use Elvis operator to print "(Unknown)" instead. Fixes: 428b9fd2dce5 ("platform/x86/amd/pmc: Add delay_suspend module parameter") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606251540.Nr2BtaNu-lkp@intel.com/ Suggested-by: Ilpo Järvinen Signed-off-by: Daniel Gibson Link: https://patch.msgid.link/20260626220210.1761783-2-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/amd/pmc/pmc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 7d772ccd17a67..2cd77a44c7e7c 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -660,11 +660,11 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) } else if (delay_suspend == 1) { if (!intermediate_wakeup) dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", - dmi_get_system_info(DMI_SYS_VENDOR), - dmi_get_system_info(DMI_PRODUCT_NAME), - dmi_get_system_info(DMI_PRODUCT_FAMILY), - dmi_get_system_info(DMI_BOARD_VENDOR), - dmi_get_system_info(DMI_BOARD_NAME)); + dmi_get_system_info(DMI_SYS_VENDOR) ?: "(Unknown)", + dmi_get_system_info(DMI_PRODUCT_NAME) ?: "(Unknown)", + dmi_get_system_info(DMI_PRODUCT_FAMILY) ?: "(Unknown)", + dmi_get_system_info(DMI_BOARD_VENDOR) ?: "(Unknown)", + dmi_get_system_info(DMI_BOARD_NAME) ?: "(Unknown)"); return true; } return false; From 6cec36c795c03a4c660205a992fbefa10eeb9e78 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 30 Jun 2026 11:09:22 -0400 Subject: [PATCH 2031/3297] net/sched: sch_teql: move rcu_read_lock()/spin_lock() from _bh variants commit 6301f6a34ed86fe6f3b7b3211ea069f3677fc559 upstream. This is a followup based on sashiko comments [1] on commit e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Use plain rcu_read_lock()/spin_lock() in teql_master_xmit() instead of the _bh variants, since ndo_start_xmit is already invoked with BH disabled by the core stack and the _bh primitives can warn in_hardirq() when xmit is reached through netpoll or a softirq xmit path with hard IRQs disabled. Moves rcu_read_lock() after restart: label + adds rcu_read_unlock() before goto restart (fixes the unbounded RCU hold across retries) [1] https://sashiko.dev/#/patchset/20260628111229.669751-1-jhs%40mojatatu.com Signed-off-by: Jamal Hadi Salim Fixes: e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Link: https://patch.msgid.link/20260630150922.238714-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_teql.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 21dc1043547e4..4d463ba5ce725 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -311,14 +311,14 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - rcu_read_lock_bh(); - - start = rcu_dereference_bh(master->slaves); - restart: nores = 0; busy = 0; + rcu_read_lock(); + + start = rcu_dereference(master->slaves); + q = start; if (!q) goto drop; @@ -345,17 +345,17 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); + spin_unlock(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -364,37 +364,38 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) busy = 1; break; case 1: - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); - rcu_read_unlock_bh(); + spin_unlock(&master->slaves_lock); + rcu_read_unlock(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); + } while ((q = rcu_dereference(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; + rcu_read_unlock(); goto restart; } if (busy) { netif_stop_queue(dev); - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; - rcu_read_unlock_bh(); + rcu_read_unlock(); dev_kfree_skb(skb); return NETDEV_TX_OK; } From d0cc2c74060be4559a1fae1428f40802fb2eba6c Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Tue, 30 Jun 2026 19:22:21 +0000 Subject: [PATCH 2032/3297] drm/xe/userptr: Stub notifier_lock helpers when DRM_GPUSVM=n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f5ef65adf81da3dbce4e692e48c1754c0bb95da0 upstream. When CONFIG_DRM_GPUSVM=n (e.g. um-allyesconfig), the only caller of xe_pt_svm_userptr_notifier_lock() is compiled out, triggering: drivers/gpu/drm/xe/xe_pt.c:1418:13: warning: 'xe_pt_svm_userptr_notifier_lock' defined but not used [-Wunused-function] The helpers cannot simply be removed in this case: the matching xe_pt_svm_userptr_notifier_unlock() is also referenced from xe_pt_update_ops_run(), which lives outside any DRM_GPUSVM ifdef and is gated only at runtime by pt_update_ops->needs_svm_lock. The symbol must exist in all builds. Provide empty static inline stubs for !DRM_GPUSVM, matching the pattern used by xe_svm_notifier_lock()/_unlock() in xe_svm.h. Fixes: dca6e08c923a ("drm/xe/userptr: Hold notifier_lock for write on inject test path") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606302210.QqcLbOEN-lkp@intel.com/ Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260630192221.2998168-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 3359422bf0a1140e96d783a19a397686e580a3ca) Signed-off-by: Thomas Hellström Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_pt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 6b75794e76b12..1f0262adc3ae3 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1388,6 +1388,7 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +#if IS_ENABLED(CONFIG_DRM_GPUSVM) /* * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() * and the matching late release in xe_pt_update_ops_run(). Read mode by @@ -1414,6 +1415,10 @@ static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) xe_svm_notifier_unlock(vm); #endif } +#else +static inline void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) { } +static inline void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) { } +#endif #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT From da7f17c2d5bb444101003e37ea323a58d889eacb Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 20 Jul 2026 11:03:29 +0800 Subject: [PATCH 2033/3297] KVM: TDX: Account all non-transient page allocations for per-TD structures [ Upstream commit a8b2924676ec42d76597f849baba31acc3ba1bfc ] Account all non-transient allocations associated with a single TD (or its vCPUs), as KVM's ABI is that allocations that are active for the lifetime of a VM are accounted. Leave temporary allocations, i.e. allocations that are freed within a single function/ioctl, unaccounted, to again align with KVM's existing behavior, e.g. see commit dd103407ca31 ("KVM: X86: Remove unnecessary GFP_KERNEL_ACCOUNT for temporary variables"). Fixes: 8d032b683c29 ("KVM: TDX: create/destroy VM structure") Fixes: a50f673f25e0 ("KVM: TDX: Do TDX specific vcpu initialization") Cc: stable@vger.kernel.org Reviewed-by: Kai Huang Reviewed-by: Rick Edgecombe Link: https://patch.msgid.link/20260129011517.3545883-4-seanjc@google.com Signed-off-by: Sean Christopherson [ Binbin: Resolved conflicts in __tdx_td_init() due to lacking commits eac90a5ba0aa and 69050f8d6d07, retain nr_configured_hkid increment and kcalloc() call. ] Signed-off-by: Binbin Wu Signed-off-by: Sasha Levin --- arch/x86/kvm/vmx/tdx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index d9ffc16dfefe6..7534397defa29 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -2474,7 +2474,7 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, atomic_inc(&nr_configured_hkid); - tdr_page = alloc_page(GFP_KERNEL); + tdr_page = alloc_page(GFP_KERNEL_ACCOUNT); if (!tdr_page) goto free_hkid; @@ -2482,12 +2482,12 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, /* TDVPS = TDVPR(4K page) + TDCX(multiple 4K pages), -1 for TDVPR. */ kvm_tdx->td.tdcx_nr_pages = tdx_sysinfo->td_ctrl.tdvps_base_size / PAGE_SIZE - 1; tdcs_pages = kcalloc(kvm_tdx->td.tdcs_nr_pages, sizeof(*kvm_tdx->td.tdcs_pages), - GFP_KERNEL); + GFP_KERNEL_ACCOUNT); if (!tdcs_pages) goto free_tdr; for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { - tdcs_pages[i] = alloc_page(GFP_KERNEL); + tdcs_pages[i] = alloc_page(GFP_KERNEL_ACCOUNT); if (!tdcs_pages[i]) goto free_tdcs; } @@ -2937,7 +2937,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) int ret, i; u64 err; - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_KERNEL_ACCOUNT); if (!page) return -ENOMEM; tdx->vp.tdvpr_page = page; @@ -2950,14 +2950,14 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) tdx->vp.tdvpr_pa = page_to_phys(tdx->vp.tdvpr_page); tdx->vp.tdcx_pages = kcalloc(kvm_tdx->td.tdcx_nr_pages, sizeof(*tdx->vp.tdcx_pages), - GFP_KERNEL); + GFP_KERNEL_ACCOUNT); if (!tdx->vp.tdcx_pages) { ret = -ENOMEM; goto free_tdvpr; } for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_KERNEL_ACCOUNT); if (!page) { ret = -ENOMEM; goto free_tdcx; From 4b73889941b95f82c7386f64de9c979bd3b7b8d8 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Mon, 23 Feb 2026 11:07:17 -0800 Subject: [PATCH 2034/3297] selftests/bpf: Add simple strscpy() implementation commit 63c49efc987afefc6b9bb7de083eb8748e0b1789 upstream. Replace bpf_strlcpy() in bpf_util.h with a sized_strscpy(), which is a simplified sized_strscpy() from the kernel (lib/string.c [1]). It: * takes a count (destination size) parameter * guarantees NULL-termination * returns the number of characters copied or -E2BIG Re-define strscpy macro similar to in-kernel implementation [2]: allow the count parameter to be optional. Add #ifdef-s to tools/include/linux/args.h, as they may be defined in other system headers (for example, __CONCAT in sys/cdefs.h). Fixup the single existing bpf_strlcpy() call in cgroup_helpers.c [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/lib/string.c?h=v6.19#n113 [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/string.h?h=v6.19#n91 Signed-off-by: Ihor Solodrai Link: https://lore.kernel.org/r/20260223190736.649171-2-ihor.solodrai@linux.dev Signed-off-by: Alexei Starovoitov Cc: Shung-Hsi Yu Signed-off-by: Greg Kroah-Hartman --- tools/include/linux/args.h | 4 ++ tools/testing/selftests/bpf/bpf_util.h | 45 ++++++++++++++------ tools/testing/selftests/bpf/cgroup_helpers.c | 2 +- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/tools/include/linux/args.h b/tools/include/linux/args.h index 2e8e65d975c77..14b268f2389ac 100644 --- a/tools/include/linux/args.h +++ b/tools/include/linux/args.h @@ -22,7 +22,11 @@ #define COUNT_ARGS(X...) __COUNT_ARGS(, ##X, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) /* Concatenate two parameters, but allow them to be expanded beforehand. */ +#ifndef __CONCAT #define __CONCAT(a, b) a ## b +#endif +#ifndef CONCATENATE #define CONCATENATE(a, b) __CONCAT(a, b) +#endif #endif /* _LINUX_ARGS_H */ diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h index 4bc2d25f33e18..6cb56501a505d 100644 --- a/tools/testing/selftests/bpf/bpf_util.h +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -8,6 +8,7 @@ #include #include #include /* libbpf_num_possible_cpus */ +#include static inline unsigned int bpf_num_possible_cpus(void) { @@ -21,25 +22,43 @@ static inline unsigned int bpf_num_possible_cpus(void) return possible_cpus; } -/* Copy up to sz - 1 bytes from zero-terminated src string and ensure that dst - * is zero-terminated string no matter what (unless sz == 0, in which case - * it's a no-op). It's conceptually close to FreeBSD's strlcpy(), but differs - * in what is returned. Given this is internal helper, it's trivial to extend - * this, when necessary. Use this instead of strncpy inside libbpf source code. +/* + * Simplified strscpy() implementation. The kernel one is in lib/string.c */ -static inline void bpf_strlcpy(char *dst, const char *src, size_t sz) +static inline ssize_t sized_strscpy(char *dest, const char *src, size_t count) { - size_t i; + long res = 0; - if (sz == 0) - return; + if (count == 0) + return -E2BIG; - sz--; - for (i = 0; i < sz && src[i]; i++) - dst[i] = src[i]; - dst[i] = '\0'; + while (count > 1) { + char c; + + c = src[res]; + dest[res] = c; + if (!c) + return res; + res++; + count--; + } + + /* Force NUL-termination. */ + dest[res] = '\0'; + + /* Return E2BIG if the source didn't stop */ + return src[res] ? -E2BIG : res; } +#define __strscpy0(dst, src, ...) \ + sized_strscpy(dst, src, sizeof(dst)) +#define __strscpy1(dst, src, size) \ + sized_strscpy(dst, src, size) + +#undef strscpy /* Redefine the placeholder from tools/include/linux/string.h */ +#define strscpy(dst, src, ...) \ + CONCATENATE(__strscpy, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__) + #define __bpf_percpu_val_align __attribute__((__aligned__(8))) #define BPF_DECLARE_PERCPU(type, name) \ diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c index 20cede4db3cee..45cd0b479fe35 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.c +++ b/tools/testing/selftests/bpf/cgroup_helpers.c @@ -86,7 +86,7 @@ static int __enable_controllers(const char *cgroup_path, const char *controllers enable[len] = 0; close(fd); } else { - bpf_strlcpy(enable, controllers, sizeof(enable)); + strscpy(enable, controllers); } snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path); From 612cda6630f2e6a054ac69fbfef9411261d92fef Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 4 Jun 2026 10:56:31 +0100 Subject: [PATCH 2035/3297] pwm: rzg2l-gpt: Fix period_ticks type from u32 to u64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2b40d72de9354a76f5e3bb71230a4210eaa92849 upstream. period_ticks is used to store PWM period values that can exceed the 32-bit range, so change its type from u32 to u64 to prevent overflow. Cc: stable@kernel.org Fixes: 061f087f5d0b ("pwm: Add support for RZ/G2L GPT") Signed-off-by: Biju Das Link: https://patch.msgid.link/20260604095647.108654-2-biju.das.jz@bp.renesas.com Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/pwm/pwm-rzg2l-gpt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index 8534a2a9cf750..dfa1d11a48a82 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -81,7 +81,7 @@ struct rzg2l_gpt_chip { void __iomem *mmio; struct mutex lock; /* lock to protect shared channel resources */ unsigned long rate_khz; - u32 period_ticks[RZG2L_MAX_HW_CHANNELS]; + u64 period_ticks[RZG2L_MAX_HW_CHANNELS]; u32 channel_request_count[RZG2L_MAX_HW_CHANNELS]; u32 channel_enable_count[RZG2L_MAX_HW_CHANNELS]; }; From 20ac8131f8c90608e99b96221ec6313b9bf3907e Mon Sep 17 00:00:00 2001 From: Xuewen Wang Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 2036/3297] LoongArch: Fix nr passing in set_direct_map_valid_noflush() commit 70378a710598432f13509bdc16a1c0f06b3ecb53 upstream. set_direct_map_valid_noflush() incorrectly passes 1 to __set_memory() instead of nr. This causes only the first page's attr to be updated when nr > 1. Other architectures all pass nr correctly. Cc: stable@vger.kernel.org Fixes: 0c6378a71574 ("arch: introduce set_direct_map_valid_noflush()") Signed-off-by: Xuewen Wang Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/mm/pageattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/mm/pageattr.c b/arch/loongarch/mm/pageattr.c index f5e910b68229d..614ccc7afccbe 100644 --- a/arch/loongarch/mm/pageattr.c +++ b/arch/loongarch/mm/pageattr.c @@ -234,5 +234,5 @@ int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid) clear = __pgprot(_PAGE_PRESENT | _PAGE_VALID); } - return __set_memory(addr, 1, set, clear); + return __set_memory(addr, nr, set, clear); } From a65f49b6f7ece756394f8f0e85570020e7fd0e35 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Thu, 25 Jun 2026 13:03:49 +0800 Subject: [PATCH 2037/3297] LoongArch: Fix missing dirty page tracking in {pte,pmd}_wrprotect() commit 018e9828eb523c638fa3d9bdf0fd4956b74555b2 upstream. When hardware page table walker (PTW) is enabled on LoongArch, the CPU may set _PAGE_DIRTY directly in the page table entry during a write TLB miss, without going through the software TLB store handler. The software TLB store handler (tlbex.S:254) sets both _PAGE_DIRTY and_PAGE_MODIFIED together: ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) Since hardware PTW only sets _PAGE_DIRTY, the software-only bit, i.e. _PAGE_MODIFIED is left unchanged. This creates a window where a PTE has _PAGE_DIRTY set (hardware knows the page is dirty) but _PAGE_MODIFIED clear (software is unaware). When fork()/clone() triggers copy-on-write, __copy_present_ptes() calls pte_wrprotect(), which unconditionally clears both the _PAGE_WRITE and _PAGE_DIRTY bits: pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_DIRTY); Since _PAGE_MODIFIED was never set, the dirtiness information is lost completely. Subsequently, when memory pressure triggers page reclaim, page_mkclean() / try_to_unmap() sees the page as clean (i.e. pte_dirty() returns false) and the page may be freed without writeback, causing data corruption. Fix this by propagating the _PAGE_DIRTY bit to the _PAGE_MODIFIED bit in both pte_wrprotect() and pmd_wrprotect() before clearing writeable bits: if (pte_val(pte) & _PAGE_DIRTY) pte_val(pte) |= _PAGE_MODIFIED; The pmd_wrprotect() fix handles the CONFIG_TRANSPARENT_HUGEPAGE case, where pmd entries need the same treatment. This ensures the software dirty tracking bit (checked by pte_dirty() and pmd_dirty(), which read both the _PAGE_DIRTY and _PAGE_MODIFIED bits) is preserved across fork COW write-protection. The issue was found by the LTP madvise09 test case, which exercises page reclaim after "madvise(MADV_FREE), write and fork" operation sequence on private anonymous mappings. Cc: stable@vger.kernel.org Fixes: 09cfefb7fa70 ("LoongArch: Add memory management") Co-developed-by: Tianyang Zhang Signed-off-by: Tianyang Zhang Signed-off-by: Hongchen Zhang Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/include/asm/pgtable.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 9a7029285fd51..f51fa0ecb366a 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -392,6 +392,8 @@ static inline pte_t pte_mkwrite_novma(pte_t pte) static inline pte_t pte_wrprotect(pte_t pte) { + if (pte_val(pte) & _PAGE_DIRTY) + pte_val(pte) |= _PAGE_MODIFIED; pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_DIRTY); return pte; } @@ -498,6 +500,8 @@ static inline pmd_t pmd_mkwrite_novma(pmd_t pmd) static inline pmd_t pmd_wrprotect(pmd_t pmd) { + if (pmd_val(pmd) & _PAGE_DIRTY) + pmd_val(pmd) |= _PAGE_MODIFIED; pmd_val(pmd) &= ~(_PAGE_WRITE | _PAGE_DIRTY); return pmd; } From ddbb6e3dc9bb4743de686aa1598c31e745cee76b Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 21 May 2026 14:06:27 +0100 Subject: [PATCH 2038/3297] ipmi: Fix user refcount underflow in event delivery commit 6aa9e61c46465d231e9beddf56af7effd71be682 upstream. ipmi_alloc_recv_msg(user) takes the temporary user reference owned by the receive message, and ipmi_free_recv_msg() drops it again. If event delivery fails after allocating receive messages for earlier users, handle_read_event_rsp() rolls those messages back with ipmi_free_recv_msg(). That rollback path still drops user->refcount explicitly after freeing each message. The extra put can free a user that remains linked on intf->users, so later event delivery may dereference a freed user or trip refcount_t's addition-on-zero warning when ipmi_alloc_recv_msg() tries to acquire another reference. Remove the stale explicit put and the now-dead user assignment. Keep the list_del() and ipmi_free_recv_msg() calls; they are the required rollback operations. Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Matt Fleming Message-ID: <20260521130628.3641050-1-matt@readmodwrite.com> Signed-off-by: Corey Minyard Signed-off-by: Greg Kroah-Hartman --- drivers/char/ipmi/ipmi_msghandler.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 17f7b3b551d5c..9f0f2ef6c58c5 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4475,10 +4475,8 @@ static int handle_read_event_rsp(struct ipmi_smi *intf, mutex_unlock(&intf->users_mutex); list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { - user = recv_msg->user; list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); - kref_put(&user->refcount, free_ipmi_user); } /* * We couldn't allocate memory for the From a66d45e0ce6d73cd79962d422388e61bfaf0cb50 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 12 Jun 2026 16:11:39 +0200 Subject: [PATCH 2039/3297] espintcp: use sk_msg_free_partial to fix partial send commit 007800408002d871f5699bdb944f985896730b8f upstream. sk_msg_free_partial() ensures consistency of the skmsg at every iteration, without having to manually handle uncharges and offsets. This simplifies the code, and fixes some bugs in skmsg accounting when we don't send the full contents. Cc: stable@vger.kernel.org Fixes: e27cca96cd68 ("xfrm: add espintcp (RFC 8229)") Reported-by: Aaron Esau Reported-by: Yiming Qian Signed-off-by: Sabrina Dubroca Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/espintcp.c | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c index 542098283783d..dd7a7dc027855 100644 --- a/net/xfrm/espintcp.c +++ b/net/xfrm/espintcp.c @@ -215,43 +215,23 @@ static int espintcp_sendskmsg_locked(struct sock *sk, struct sk_msg *skmsg = &emsg->skmsg; bool more = flags & MSG_MORE; struct scatterlist *sg; - int done = 0; int ret; - sg = &skmsg->sg.data[skmsg->sg.start]; do { struct bio_vec bvec; - size_t size = sg->length - emsg->offset; - int offset = sg->offset + emsg->offset; - struct page *p; - - emsg->offset = 0; + sg = &skmsg->sg.data[skmsg->sg.start]; if (sg_is_last(sg) && !more) msghdr.msg_flags &= ~MSG_MORE; - p = sg_page(sg); -retry: - bvec_set_page(&bvec, p, size, offset); - iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, size); - ret = tcp_sendmsg_locked(sk, &msghdr, size); - if (ret < 0) { - emsg->offset = offset - sg->offset; - skmsg->sg.start += done; + bvec_set_page(&bvec, sg_page(sg), sg->length, sg->offset); + iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, sg->length); + ret = tcp_sendmsg_locked(sk, &msghdr, sg->length); + if (ret < 0) return ret; - } - - if (ret != size) { - offset += ret; - size -= ret; - goto retry; - } - done++; - put_page(p); - sk_mem_uncharge(sk, sg->length); - sg = sg_next(sg); - } while (sg); + sk_msg_free_partial(sk, skmsg, ret); + } while (skmsg->sg.size); memset(emsg, 0, sizeof(*emsg)); From f5c5065963024390ddad51bd455d1adc710de575 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 3 Jun 2026 12:06:34 +0000 Subject: [PATCH 2040/3297] ipmi: fix refcount leak in i_ipmi_request() commit a3f3859cecacb64f18fd446271ece9a3b3f2d4de upstream. When a caller provides a `supplied_recv` message to i_ipmi_request(), the function increments the user's `nr_msgs` reference count. If an error occurs later, the out_err cleanup path only frees the recv_msg if the function allocated it itself (i.e., !supplied_recv). In the supplied_recv case the cleanup is skipped, leaving the reference count elevated. The caller ipmi_request_supply_msgs() does not release the supplied_recv on error, so the reference is permanently leaked. Fix this by explicitly reverting the reference count operations when a supplied recv_msg with a valid user pointer is present in the error path: decrement nr_msgs and drop the user's kref. Cc: stable@vger.kernel.org Fixes: b52da4054ee0 ("ipmi: Rework user message limit handling") Signed-off-by: Wentao Liang Message-ID: <20260603120634.3758747-1-vulab@iscas.ac.cn> Signed-off-by: Corey Minyard Signed-off-by: Greg Kroah-Hartman --- drivers/char/ipmi/ipmi_msghandler.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 9f0f2ef6c58c5..51f76d755f987 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2349,6 +2349,10 @@ static int i_ipmi_request(struct ipmi_user *user, if (smi_msg == NULL) { if (!supplied_recv) ipmi_free_recv_msg(recv_msg); + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } return -ENOMEM; } } @@ -2422,6 +2426,10 @@ static int i_ipmi_request(struct ipmi_user *user, ipmi_free_smi_msg(smi_msg); if (!supplied_recv) ipmi_free_recv_msg(recv_msg); + else if (recv_msg->user) { + atomic_dec(&recv_msg->user->nr_msgs); + kref_put(&recv_msg->user->refcount, free_ipmi_user); + } } return rv; } From 6c98ccdb9a0967e04a7b1866eb0d97c0c8c0e403 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Sat, 20 Jun 2026 11:53:50 +0530 Subject: [PATCH 2041/3297] bnx2x: fix potential memory leak in bnx2x_alloc_mem_bp() commit a986fde914d88af47eb78fd29c5d1af7952c3500 upstream. If the allocation of fp[i].tpa_info fails, the error path will not free the struct bnx2x_fastpath allocated earlier, as it is not linked to the bp structure yet. Fix that by linking it immediately after allocation. Cc: stable@vger.kernel.org Fixes: 15192a8cf8a8 ("bnx2x: Split the FP structure") Signed-off-by: Abdun Nihaal Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260620062402.89549-1-nihaal@cse.iitm.ac.in Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index e59530357e2c3..445e80a681fe6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4750,6 +4750,7 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) fp = kcalloc(bp->fp_array_size, sizeof(*fp), GFP_KERNEL); if (!fp) goto alloc_err; + bp->fp = fp; for (i = 0; i < bp->fp_array_size; i++) { fp[i].tpa_info = kcalloc(ETH_MAX_AGGREGATION_QUEUES_E1H_E2, @@ -4758,8 +4759,6 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp) goto alloc_err; } - bp->fp = fp; - /* allocate sp objs */ bp->sp_objs = kcalloc(bp->fp_array_size, sizeof(struct bnx2x_sp_objs), GFP_KERNEL); From 6e21d1253ef1322718984b9c45f561d1f8a3823b Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 20:25:55 +0100 Subject: [PATCH 2042/3297] rtc: renesas-rtca3: Fix PIE clear polling condition in alarm setup error path commit 7e342d87aa8e6b831cf6d21ca41b1f7e032d0fcf upstream. In rtca3_set_alarm(), the setup_failed path attempts to disable the Periodic Interrupt Enable (PIE) bit and wait until it is cleared. However, the polling condition passed to readb_poll_timeout_atomic() uses an incorrect expression: !(tmp & ~RTCA3_RCR1_PIE) As ~RTCA3_RCR1_PIE evaluates to a mask of all bits except PIE, the condition effectively waits for all non-PIE bits to become zero, which is unrelated to the intended operation and is unlikely to ever be true. This causes the poll to time out unnecessarily. Fix the condition to check for the PIE bit itself being cleared: !(tmp & RTCA3_RCR1_PIE) This correctly waits until PIE is deasserted after being cleared. Fixes: d4488377609e3 ("rtc: renesas-rtca3: Add driver for RTCA-3 available on Renesas RZ/G3S SoC") Cc: stable@vger.kernel.org Signed-off-by: Lad Prabhakar Reviewed-by: Claudiu Beznea Tested-by: Claudiu Beznea # on RZ/G3S Link: https://patch.msgid.link/20260602192559.1791344-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Alexandre Belloni Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-renesas-rtca3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index ab816bdf0d776..47b4827979344 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -455,7 +455,7 @@ static int rtca3_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) * specified timeout for setup. */ writeb(rcr1 & ~RTCA3_RCR1_PIE, priv->base + RTCA3_RCR1); - readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & ~RTCA3_RCR1_PIE), + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & RTCA3_RCR1_PIE), 10, RTCA3_DEFAULT_TIMEOUT_US); atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); } From 8dc6c7e8c96787ce49289d6efc5ff79330c01177 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 13 May 2026 18:55:55 +0100 Subject: [PATCH 2043/3297] rtc: mpfs: fix counter upload completion condition commit 9792ff8afa9017fe14f436f3ef3cd75f41f9f145 upstream. The condition that needs to be checked for upload completion is the UPLOAD bit in the completion register going low. The original iterations of this driver used a do-while and this was converted to a read_poll_timeout() during upstreaming without the condition being inverted as it should have been. I suspect that this went unnoticed until now because a) the first read was done when the bit was still set, immediately completing the read_poll_timeout() and b) because the RTC doesn't hold time when power is removed from the SoC reducing its utility (I for one keep it disabled). If my first suspicion was true when the driver was upstreamed, it's not true any longer though, hence the detection of the problem. Fixes: 0b31d703598dc ("rtc: Add driver for Microchip PolarFire SoC") CC: stable@vger.kernel.org Signed-off-by: Conor Dooley Tested-by: Valentina Fernandez Link: https://patch.msgid.link/20260513-panhandle-ashy-70c6abf84d59@spud Signed-off-by: Alexandre Belloni Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c index 6aa3eae575d2a..ece6de4a6adbd 100644 --- a/drivers/rtc/rtc-mpfs.c +++ b/drivers/rtc/rtc-mpfs.c @@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm) ctrl |= CONTROL_UPLOAD_BIT; writel(ctrl, rtcdev->base + CONTROL_REG); - ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US, + ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US, false, rtcdev->base + CONTROL_REG); if (ret) { dev_err(dev, "timed out uploading time to rtc"); From c6c990f7208c9ad08b6e2ac448f3741dd9cd3fc1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:47:31 +0800 Subject: [PATCH 2044/3297] hwmon: (w83627hf) remove VID sysfs files on error and remove commit 5264b389c4e02dec214a46c400eb3ab867a7749a upstream. w83627hf_probe() creates cpu0_vid and vrm with device_create_file() when VID information is available. The error path and remove callback only remove the common and optional attribute groups. Those groups do not contain cpu0_vid or vrm, so the files can remain after a later probe failure or after device removal while their callbacks still expect live driver data. Remove the standalone VID sysfs files from both the probe error path and the remove callback. Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260615064732.48113-1-pengpeng@iscas.ac.cn Cc: stable@vger.kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/w83627hf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 95115d7b863e3..bb993bb09f401 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -1823,6 +1823,8 @@ static int w83627hf_probe(struct platform_device *pdev) return 0; error: + device_remove_file(dev, &dev_attr_vrm); + device_remove_file(dev, &dev_attr_cpu0_vid); sysfs_remove_group(&dev->kobj, &w83627hf_group); sysfs_remove_group(&dev->kobj, &w83627hf_group_opt); return err; @@ -1834,6 +1836,8 @@ static void w83627hf_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); + device_remove_file(&pdev->dev, &dev_attr_vrm); + device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt); } From 664480021f6a4ce97e3778739af3a834a66e82e0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:48:06 +0800 Subject: [PATCH 2045/3297] hwmon: (w83793) remove vrm sysfs file on probe failure commit 77b983757280c69b0290811669ff1d31022e5f1d upstream. w83793_probe() creates the vrm sysfs file after creating the VID files when VID support is present. The normal remove path deletes vrm, but the probe error path only removes the sensor, SDA, VID, fan, PWM and temperature files. A later probe failure can therefore leave vrm behind after the driver data has been freed. Remove vrm in the probe error path next to the VID files, matching the normal remove path. Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260615064806.51139-1-pengpeng@iscas.ac.cn Cc: stable@vger.kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/w83793.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 67728f60333fe..ec03745328a6d 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1917,6 +1917,7 @@ static int w83793_probe(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) device_remove_file(dev, &w83793_vid[i].dev_attr); + device_remove_file(dev, &dev_attr_vrm); for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++) device_remove_file(dev, &w83793_left_fan[i].dev_attr); From 715cce38424fb2488ef2c5e6e6ffbfe1b74a9e67 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 24 Jun 2026 14:40:13 +0800 Subject: [PATCH 2046/3297] net: liquidio: fix BAR resource leak on PF number failure commit c63ee62a3c4ac1a1542f4c1a4b87e2f41df5a496 upstream. If cn23xx_get_pf_num() fails, the function returns without unmapping either BAR. Unmap both BARs before returning from the error path. Found by manual code review. Fixes: 0c45d7fe12c7 ("liquidio: fix use of pf in pass-through mode in a virtual machine") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Larysa Zaremba Link: https://patch.msgid.link/20260624064013.2809570-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../cavium/liquidio/cn23xx_pf_device.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 75f22f74774cb..06b4424e778ed 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1163,18 +1163,14 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) if (octeon_map_pci_barx(oct, 1, MAX_BAR1_IOREMAP_SIZE)) { dev_err(&oct->pci_dev->dev, "%s CN23XX BAR1 map failed\n", __func__); - octeon_unmap_pci_barx(oct, 0); - return 1; + goto err_unmap_bar0; } if (cn23xx_get_pf_num(oct) != 0) - return 1; + goto err_unmap_bar1; - if (cn23xx_sriov_config(oct)) { - octeon_unmap_pci_barx(oct, 0); - octeon_unmap_pci_barx(oct, 1); - return 1; - } + if (cn23xx_sriov_config(oct)) + goto err_unmap_bar1; octeon_write_csr64(oct, CN23XX_SLI_MAC_CREDIT_CNT, 0x3F802080802080ULL); @@ -1205,6 +1201,12 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) oct->coproc_clock_rate = 1000000ULL * cn23xx_coprocessor_clock(oct); return 0; + +err_unmap_bar1: + octeon_unmap_pci_barx(oct, 1); +err_unmap_bar0: + octeon_unmap_pci_barx(oct, 0); + return 1; } EXPORT_SYMBOL_GPL(setup_cn23xx_octeon_pf_device); From f0aad157576da199c146c1bb266442befa7912ca Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 09:59:38 +0800 Subject: [PATCH 2047/3297] hwmon: (occ) unregister sysfs devices outside occ lock commit e31408734332b8cc611342cdaaab6ba492180156 upstream. occ_active(false) and occ_shutdown() unregister sysfs-backed devices while occ->lock is held. hwmon_device_unregister() and sysfs_remove_group() can wait for active sysfs callbacks to drain, and those callbacks can enter the OCC update path and try to take occ->lock again. That gives the unregister paths the lock ordering occ->lock -> sysfs callback drain, while a callback has the opposite edge sysfs callback -> occ->lock. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the real unregister and callback carrier: occ_shutdown() hwmon_device_unregister() occ_show_temp_1() occ_update_response() Lockdep reported the circular dependency with occ_shutdown() already holding the OCC mutex and hwmon_device_unregister() waiting on the sysfs side: WARNING: possible circular locking dependency detected ... (sysfs_lock) ... at: hwmon_device_unregister+0x12/0x30 [vuln_msv] ... (&test_occ.lock) ... at: occ_shutdown.constprop.0+0xe/0x40 [vuln_msv] occ_update_response.isra.0+0xb/0x20 [vuln_msv] occ_show_temp_1.constprop.0.isra.0+0x23/0x40 [vuln_msv] *** DEADLOCK *** Serialize hwmon registration and removal with a separate hwmon_lock. Under that lock, detach occ->hwmon and update occ->active while occ->lock is held so concurrent OCC state changes still see a stable state, then drop occ->lock before calling hwmon_device_unregister(). Remove the driver sysfs group before taking occ->lock in occ_shutdown(), so draining the driver attributes cannot wait while the OCC mutex is held. Also make OCC update callbacks return -ENODEV after deactivation, so callbacks that already passed sysfs active protection do not poll the hardware after teardown has detached the hwmon device. Fixes: 849b0156d996 ("hwmon: (occ) Delay hwmon registration until user request") Fixes: ac6888ac5a11 ("hwmon: (occ) Lock mutex in shutdown to prevent race with occ_active") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://lore.kernel.org/r/20260619015938.494464-1-runyu.xiao@seu.edu.cn Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/occ/common.c | 34 ++++++++++++++++++++++++++++------ drivers/hwmon/occ/common.h | 1 + 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 42cc6068bb08b..e18e80e832fd3 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -214,6 +214,11 @@ int occ_update_response(struct occ *occ) if (rc) return rc; + if (!occ->active) { + rc = -ENODEV; + goto unlock; + } + /* limit the maximum rate of polling the OCC */ if (time_after(jiffies, occ->next_update)) { rc = occ_poll(occ); @@ -222,6 +227,7 @@ int occ_update_response(struct occ *occ) rc = occ->last_error; } +unlock: mutex_unlock(&occ->lock); return rc; } @@ -1105,11 +1111,16 @@ static void occ_parse_poll_response(struct occ *occ) int occ_active(struct occ *occ, bool active) { - int rc = mutex_lock_interruptible(&occ->lock); + struct device *hwmon = NULL; + int rc = mutex_lock_interruptible(&occ->hwmon_lock); if (rc) return rc; + rc = mutex_lock_interruptible(&occ->lock); + if (rc) + goto unlock_hwmon; + if (active) { if (occ->active) { rc = -EALREADY; @@ -1154,14 +1165,17 @@ int occ_active(struct occ *occ, bool active) goto unlock; } - if (occ->hwmon) - hwmon_device_unregister(occ->hwmon); + hwmon = occ->hwmon; occ->active = false; occ->hwmon = NULL; } unlock: mutex_unlock(&occ->lock); + if (hwmon) + hwmon_device_unregister(hwmon); +unlock_hwmon: + mutex_unlock(&occ->hwmon_lock); return rc; } @@ -1170,6 +1184,7 @@ int occ_setup(struct occ *occ) int rc; mutex_init(&occ->lock); + mutex_init(&occ->hwmon_lock); occ->groups[0] = &occ->group; rc = occ_setup_sysfs(occ); @@ -1190,15 +1205,22 @@ EXPORT_SYMBOL_GPL(occ_setup); void occ_shutdown(struct occ *occ) { - mutex_lock(&occ->lock); + struct device *hwmon; occ_shutdown_sysfs(occ); - if (occ->hwmon) - hwmon_device_unregister(occ->hwmon); + mutex_lock(&occ->hwmon_lock); + mutex_lock(&occ->lock); + + hwmon = occ->hwmon; + occ->active = false; occ->hwmon = NULL; mutex_unlock(&occ->lock); + + if (hwmon) + hwmon_device_unregister(hwmon); + mutex_unlock(&occ->hwmon_lock); } EXPORT_SYMBOL_GPL(occ_shutdown); diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h index 7ac4b2febce65..82f600093c7fa 100644 --- a/drivers/hwmon/occ/common.h +++ b/drivers/hwmon/occ/common.h @@ -101,6 +101,7 @@ struct occ { unsigned long next_update; struct mutex lock; /* lock OCC access */ + struct mutex hwmon_lock; /* serialize hwmon registration/removal */ struct device *hwmon; struct occ_attribute *attrs; From ac58088d70b8acdf9cf25db87ecff8c55660d63d Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Thu, 25 Jun 2026 08:48:34 +0800 Subject: [PATCH 2048/3297] fsl/fman: Free init resources on KeyGen failure in fman_init() commit d288efa2b94abc2e45a061fceb156b4f4e5b37be upstream. fman_muram_alloc() allocates initialization resources before initializing the KeyGen block. If keygen_init() fails, the function returns -EINVAL directly and leaves those resources allocated. Free the initialization resources before returning from the KeyGen failure path. Fixes: 7472f4f281d0 ("fsl/fman: enable FMan Keygen") Cc: stable@kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Pavan Chebbi Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260625004834.3394389-1-haoxiang_li2024@163.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/freescale/fman/fman.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 11887458f050c..b96d4e03f1695 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -1995,8 +1995,10 @@ static int fman_init(struct fman *fman) /* Init KeyGen */ fman->keygen = keygen_init(fman->kg_regs); - if (!fman->keygen) + if (!fman->keygen) { + free_init_resources(fman); return -EINVAL; + } err = enable(fman, cfg); if (err != 0) From 99a6f37b113c46815deb160c5012073563679ef4 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Fri, 26 Jun 2026 18:32:18 +0200 Subject: [PATCH 2049/3297] net: lan743x: Initialize eth_syslock spinlock before use commit 39139b1c1c2b614096519b526112c726adb12ff0 upstream. lan743x_hardware_init() calls pci11x1x_strap_get_status() during the PCI11x1x probe sequence. That helper acquires the Ethernet subsystem hardware lock via lan743x_hs_syslock_acquire(), which relies on adapter->eth_syslock_spinlock to serialize access. The spinlock is currently initialized only after the strap status is read. With CONFIG_DEBUG_SPINLOCK enabled, taking the zeroed initialized spinlock can trip the spinlock debug check. Fix by initializing adapter->eth_syslock_spinlock before reading the strap status so the probe path never attempts to lock an uninitialized spinlock. Fixes: 46b777ad9a8c ("net: lan743x: Add support to SGMII 1G and 2.5G") Cc: stable@vger.kernel.org # v6.0+ Signed-off-by: Andrea Righi Reviewed-by: David Thompson Reviewed-by: Thangaraj Samynathan Link: https://patch.msgid.link/20260626163218.3591486-1-arighi@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/microchip/lan743x_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index ab2f3c92b3fa3..441bc7a789f0f 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -3536,8 +3536,8 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter, adapter->max_tx_channels = PCI11X1X_MAX_TX_CHANNELS; adapter->used_tx_channels = PCI11X1X_USED_TX_CHANNELS; adapter->max_vector_count = PCI11X1X_MAX_VECTOR_COUNT; - pci11x1x_strap_get_status(adapter); spin_lock_init(&adapter->eth_syslock_spinlock); + pci11x1x_strap_get_status(adapter); mutex_init(&adapter->sgmii_rw_lock); pci11x1x_set_rfe_rd_fifo_threshold(adapter); sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); From 5889064919a1e5c0a9469c54895000414fc46944 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 04:51:20 -0500 Subject: [PATCH 2050/3297] net/sched: sch_multiq: Replace direct dequeue call with peek and qdisc_dequeue_peeked commit 54f6b0c843e228d499eb4b6bbb89df68cad9ad5d upstream. multiq_dequeue() takes a packet from a band's child with a direct ->dequeue() call after multiq_peek() peeked it. When the child is non-work-conserving the peek stashes the skb in the child's gso_skb, so the direct dequeue returns a different skb and orphans the stash, desyncing the child's qlen/backlog. With a qfq child reached through a peeking parent (e.g. tbf) this re-enters the child on an emptied list and dereferences NULL, panicking the kernel from softirq on ordinary egress. Take the packet through qdisc_dequeue_peeked(), as sch_prio already does and as sch_red and sch_sfb were just fixed to do. The helper is a no-op when the child has no stash, so a work-conserving child is unaffected. Fixes: 77be155cba4e ("pkt_sched: Add peek emulation for non-work-conserving qdiscs.") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260625-b4-disp-31bcb279-v1-2-85c40b83c529@proton.me Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_multiq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 06e03f5cd7ce1..e50a37b7cec1a 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -103,7 +103,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch) if (!netif_xmit_stopped( netdev_get_tx_queue(qdisc_dev(sch), q->curband))) { qdisc = q->queues[q->curband]; - skb = qdisc->dequeue(qdisc); + skb = qdisc_dequeue_peeked(qdisc); if (skb) { qdisc_bstats_update(sch, skb); sch->q.qlen--; From e2b7ee61989f2d39df6c2cc06f9db1aea69bdb09 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 04:51:19 -0500 Subject: [PATCH 2051/3297] net/sched: sch_taprio: Replace direct dequeue call with peek and qdisc_dequeue_peeked commit e056e1dfcddca877dd46d704e8ec9860cfc9ec44 upstream. When taprio's software path peeks a non-work-conserving child qdisc, the child stashes the peeked skb in its gso_skb; taprio_dequeue_from_txq() then takes the packet with a direct child ->dequeue() call, which ignores that stash, orphans the peeked skb and desyncs the child's qlen/backlog. With a qfq child this re-enters the child on an emptied list and dereferences NULL, panicking the kernel from softirq on ordinary egress. Take the packet through qdisc_dequeue_peeked(), as sch_red and sch_sfb now do. The helper returns the child's stashed skb first and is a no-op when there is none, so a work-conserving child is unaffected and the gated path now consumes the skb whose length was charged to the budget. Fixes: 5a781ccbd19e ("tc: Add support for configuring the taprio scheduler") Cc: stable@vger.kernel.org Cc: Vladimir Oltean Signed-off-by: Bryam Vargas Reviewed-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260625-b4-disp-31bcb279-v1-1-85c40b83c529@proton.me Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_taprio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index b3481cafa6eca..99a711d0479c2 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -748,7 +748,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq, return NULL; skip_peek_checks: - skb = child->ops->dequeue(child); + skb = qdisc_dequeue_peeked(child); if (unlikely(!skb)) return NULL; From 6c52226072a3c61337b1eec1799bb987748884fa Mon Sep 17 00:00:00 2001 From: David Lee Date: Wed, 1 Jul 2026 11:44:28 +0000 Subject: [PATCH 2052/3297] fhandle: reject detached mounts in capable_wrt_mount() commit 6c732471740bc2ac9b0946134f9f551dc75f4369 upstream. The recent fhandle RCU fix moved the mount namespace capability check into capable_wrt_mount(), so a non-NULL mnt_namespace survives the ns_capable() dereference. The helper still assumes the later READ_ONCE(mount->mnt_ns) must be non-NULL because may_decode_fh() checked is_mounted() first. That assumption is not stable. A detached mount from open_tree(..., OPEN_TREE_CLONE) can be dissolved on fput while open_by_handle_at() is between those checks, and umount_tree() can clear mount->mnt_ns. If the helper observes NULL, it dereferences mnt_ns->user_ns and panics. Return false when the RCU read observes a detached mount. This keeps the relaxed permission path conservative: a mount no longer attached to a namespace cannot authorize open_by_handle_at() access. Fixes: 620c266f3949 ("fhandle: relax open_by_handle_at() permission checks") Cc: stable@vger.kernel.org Signed-off-by: David Lee Assisted-by: LLM Link: https://patch.msgid.link/20260701114438.24431-1-david.lee@trailofbits.com Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/fhandle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fhandle.c b/fs/fhandle.c index a56dd02ddbbd6..831dc3b69225b 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -297,7 +297,7 @@ static bool capable_wrt_mount(struct mount *mount) */ guard(rcu)(); mnt_ns = READ_ONCE(mount->mnt_ns); - return ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN); + return mnt_ns && ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN); } static inline int may_decode_fh(struct handle_to_path_ctx *ctx, From b4427ee3667c57065976d382257e073d40d1caf7 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Mon, 29 Jun 2026 21:17:39 +0200 Subject: [PATCH 2053/3297] hwmon: (max1619) add missing 'select REGMAP' to Kconfig commit 943a749bdffdd2132fab9240db890e07d93e1fcf upstream. The Kconfig entry for the MAX1619 sensor doesn't contain a `select REGMAP` parameter, causing build failures if regmap isn't selected previously during the build process. Fixes: f8016132ce49 ("hwmon: (max1619) Convert to use regmap") Cc: stable@vger.kernel.org Signed-off-by: Joshua Crofts Link: https://lore.kernel.org/r/20260629-add-kconfig-deps-v1-1-8104df929b1a@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 04808d244c924..c8a7e45e8006c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1213,6 +1213,7 @@ config SENSORS_MAX16065 config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" depends on I2C + select REGMAP help If you say yes here you get support for MAX1619 sensor chip. From 2c76c01a505c133b6d5fc1f7815c57de63087da3 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 25 Jun 2026 08:34:47 +0900 Subject: [PATCH 2054/3297] tracing/probes: Fix double addition of offset for @+FOFFSET commit 9a667b7750dda88cbf1cca96a53a2163b2ee71f7 upstream. Since commit 533059281ee5 ("tracing: probeevent: Introduce new argument fetching code") wrongly use @offset local variable during the parsing, the offset value is added twice when dereferencing. Reset the @offset after setting it in FETCH_OP_FOFFS. Link: https://lore.kernel.org/all/178217905962.643090.1978577464942171332.stgit@devnote2/ Fixes: 533059281ee5 ("tracing: probeevent: Introduce new argument fetching code") Signed-off-by: Masami Hiramatsu (Google) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_probe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index a7a73eb6f011e..2333e5446c9fa 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -1248,6 +1248,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = FETCH_OP_FOFFS; code->immediate = (unsigned long)offset; // imm64? + offset = 0; } else { /* uprobes don't support symbols */ if (!(ctx->flags & TPARG_FL_KERNEL)) { From 82fc886e244c76fadf05ef1958aaf8815478ccde Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 19 Jun 2026 04:38:20 -0500 Subject: [PATCH 2055/3297] orangefs: keep the readdir entry size 64-bit in fill_from_part() commit 18227a6bc98bd0ba96ed3ce9d5b28776a5a28dfc upstream. fill_from_part() computes the size of a directory entry in size_t but stores it in a __u32. An entry length near U32_MAX wraps it to a small value, bypasses the bounds check, and is then used to index the entry, reading far past the directory part -- an out-of-bounds read that oopses the kernel. Compute the size as a u64 so it cannot truncate; the bounds check then rejects the entry. The trailer is supplied by the userspace client. Fixes: 480e3e532e31 ("orangefs: support very large directories") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260619-b4-disp-50d2bd59-v1-1-ce332969b4a2@proton.me Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/orangefs/dir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/orangefs/dir.c b/fs/orangefs/dir.c index 6d1fbeca9d817..53acc281fcd96 100644 --- a/fs/orangefs/dir.c +++ b/fs/orangefs/dir.c @@ -190,7 +190,8 @@ static int fill_from_part(struct orangefs_dir_part *part, { const int offset = sizeof(struct orangefs_readdir_response_s); struct orangefs_khandle *khandle; - __u32 *len, padlen; + __u32 *len; + u64 padlen; loff_t i; char *s; i = ctx->pos & ~PART_MASK; @@ -214,8 +215,8 @@ static int fill_from_part(struct orangefs_dir_part *part, * len is the size of the string itself. padlen is the * total size of the encoded string. */ - padlen = (sizeof *len + *len + 1) + - (8 - (sizeof *len + *len + 1)%8)%8; + padlen = (u64)sizeof *len + *len + 1; + padlen += (8 - padlen % 8) % 8; if (part->len < i + padlen + sizeof *khandle) goto next; s = (void *)part + offset + i + sizeof *len; From 1dce4f4bb3c1c02080b1a45bdd2abb2913a6642a Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Mon, 29 Jun 2026 14:40:49 +0800 Subject: [PATCH 2056/3297] net/mlx5: HWS, fix matcher leak on resize target setup failure commit bb09d0e64ecaa0aa0f7d1133a1696ed74dead295 upstream. hws_bwc_matcher_move() allocates a replacement matcher before setting it as the resize target. If mlx5hws_matcher_resize_set_target() fails, the replacement matcher is not attached anywhere and is leaked. Fix the leak by destroying the replacement matcher before returning from the resize-target failure path. 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.1. An x86_64 allyesconfig build showed no new warnings. As we do not have a mlx5 HWS-capable device to test with, no runtime testing was able to be performed. Fixes: 2111bb970c78 ("net/mlx5: HWS, added backward-compatible API handling") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Reviewed-by: Yevgeny Kliteynik Acked-by: Tariq Toukan Link: https://patch.msgid.link/20260629064049.3852759-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c index 6ef0c4be27e17..564ae98316bc9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c @@ -205,6 +205,7 @@ static int hws_bwc_matcher_move(struct mlx5hws_bwc_matcher *bwc_matcher) ret = mlx5hws_matcher_resize_set_target(old_matcher, new_matcher); if (ret) { mlx5hws_err(ctx, "Rehash error: failed setting resize target\n"); + mlx5hws_matcher_destroy(new_matcher); return ret; } From 1286a4156333753cae99b3f2da3c4456a08eae2c Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 25 Jun 2026 22:18:37 +0800 Subject: [PATCH 2057/3297] ata: pata_pxa: Fix DMA channel leak on probe error commit fcaf242e7fc406e78f444a35441e3b58f5e28781 upstream. When dmaengine_slave_config() fails, the DMA channel acquired by dma_request_chan() is not released before returning the error, leaking the channel reference. Fix by adding dma_release_channel() in the error path. The ata_host_activate() error path already correctly releases the DMA channel. Cc: stable@vger.kernel.org Fixes: 88622d80af82 ("ata: pata_pxa: dmaengine conversion") Signed-off-by: Wentao Liang Reviewed-by: Niklas Cassel Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/pata_pxa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index 03dbaf4a13a75..9f63bdfb8576e 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -286,6 +286,7 @@ static int pxa_ata_probe(struct platform_device *pdev) ret = dmaengine_slave_config(data->dma_chan, &config); if (ret < 0) { dev_err(&pdev->dev, "dma configuration failed: %d\n", ret); + dma_release_channel(data->dma_chan); return ret; } From 07f5eb6d268a37bd9e131079489655cd599182e0 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 25 Jun 2026 14:17:28 +0800 Subject: [PATCH 2058/3297] net: wwan: iosm: bound device offsets in the MUX downlink decoder commit 526b8ef54668780c8f69e0211c342763d5dcbad1 upstream. mux_dl_adb_decode() walks a chain of aggregated datagram tables using offsets and lengths taken from the modem. first_table_index, next_table_index, table_length, datagram_index and datagram_length are all device supplied le values. Only first_table_index was checked, and only for being non zero. The decoder then formed adth = block + adth_index and read the table header and the datagram entries with no bound against the received skb. A modem that reports an index or a length past the downlink buffer makes the decoder read out of bounds. The buffer is IPC_MEM_MAX_DL_MUX_LITE_BUF_SIZE and skb->len is at most that, so skb->len is the real limit, but none of these in band offsets were checked against it. The table chain is also followed with no forward progress check. The loop takes the next table from adth->next_table_index and stops only when that reaches zero. A modem can stage two tables that point at each other, so the loop never ends. It runs in softirq and clones the skb on every pass. Validate every device offset and length against skb->len before use. The block header must fit. Each table header, on entry and after every next_table_index, must lie inside the skb. The datagram table must fit. Each datagram index and length must stay inside the skb. The header padding must not exceed the datagram length so the receive length does not wrap. Require each next_table_index to move forward so the chain cannot cycle. This was reproduced under KASAN as a slab out of bounds read on a normal downlink receive once the iosm net device is up. Fixes: 1f52d7b62285 ("net: wwan: iosm: Enable M.2 7360 WWAN card support") Suggested-by: Loic Poulain Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Simon Horman Reviewed-by: Loic Poulain Link: https://patch.msgid.link/178236824878.3259367.5389624724479864947@maoyixie.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wwan/iosm/iosm_ipc_mux_codec.c | 40 ++++++++++++++++------ 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c index bff46f7ca59f3..0bbd41263cc2f 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c +++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c @@ -553,19 +553,21 @@ static int mux_dl_process_dg(struct iosm_mux *ipc_mux, struct mux_adbh *adbh, u32 packet_offset, i, rc, dg_len; for (i = 0; i < nr_of_dg; i++, dg++) { - if (le32_to_cpu(dg->datagram_index) - < sizeof(struct mux_adbh)) + u32 dg_index = le32_to_cpu(dg->datagram_index); + + dg_len = le16_to_cpu(dg->datagram_length); + + if (dg_index < sizeof(struct mux_adbh)) goto dg_error; - /* Is the packet inside of the ADB */ - if (le32_to_cpu(dg->datagram_index) >= - le32_to_cpu(adbh->block_length)) { + /* Is the packet inside of the ADB and the received skb ? */ + if (dg_index >= le32_to_cpu(adbh->block_length) || + dg_index >= skb->len || + dg_len > skb->len - dg_index || + dl_head_pad_len >= dg_len) { goto dg_error; } else { - packet_offset = - le32_to_cpu(dg->datagram_index) + - dl_head_pad_len; - dg_len = le16_to_cpu(dg->datagram_length); + packet_offset = dg_index + dl_head_pad_len; /* Pass the packet to the netif layer. */ rc = ipc_mux_net_receive(ipc_mux, if_id, ipc_mux->wwan, packet_offset, @@ -589,12 +591,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, struct mux_adbh *adbh; struct mux_adth *adth; int nr_of_dg, if_id; - u32 adth_index; + u32 adth_index, prev_index = 0; u8 *block; block = skb->data; adbh = (struct mux_adbh *)block; + /* The block header itself must fit in the received skb. */ + if (skb->len < sizeof(struct mux_adbh)) + goto adb_decode_err; + /* Process the aggregated datagram tables. */ adth_index = le32_to_cpu(adbh->first_table_index); @@ -606,6 +612,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, /* Loop through mixed session tables. */ while (adth_index) { + /* The table header must lie within the received skb, and the + * chain must move forward so a modem cannot make the loop + * cycle between two tables. + */ + if (adth_index <= prev_index || + adth_index < sizeof(struct mux_adbh) || + adth_index > skb->len - sizeof(struct mux_adth)) + goto adb_decode_err; + prev_index = adth_index; + /* Get the reference to the table header. */ adth = (struct mux_adth *)(block + adth_index); @@ -629,6 +645,10 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, if (le16_to_cpu(adth->table_length) < sizeof(struct mux_adth)) goto adb_decode_err; + /* The whole datagram table must fit in the received skb. */ + if (le16_to_cpu(adth->table_length) > skb->len - adth_index) + goto adb_decode_err; + /* Calculate the number of datagrams. */ nr_of_dg = (le16_to_cpu(adth->table_length) - sizeof(struct mux_adth)) / From d8d4fa0c4f818e30b6f6737bdd989b7e2b511cae Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Fri, 19 Jun 2026 21:27:46 +0900 Subject: [PATCH 2059/3297] hwmon: (asus_atk0110) Check package count before accessing element commit e2735b39f044bad7bf2017aef248935525bc0b97 upstream. atk_ec_present() walks the management group package returned by the GGRP ACPI method and, for each sub-package, reads its first element: id = &obj->package.elements[0]; if (id->type != ACPI_TYPE_INTEGER) without checking that the sub-package is non-empty. ACPICA allocates the element array with exactly package.count entries, so for a sub-package with a zero count this reads past the allocation. The sibling function atk_debugfs_ggrp_open() performs the same access but skips empty packages with a package.count check first. Add the same check to atk_ec_present() so a malformed firmware package cannot trigger an out-of-bounds read. Fixes: 9e6eba610c2e ("hwmon: (asus_atk0110) Enable the EC") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://lore.kernel.org/r/20260619122746.721981-1-sammiee5311@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/asus_atk0110.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index c80350e499e97..94ff698488fd1 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -1037,6 +1037,9 @@ static int atk_ec_present(struct atk_data *data) if (obj->type != ACPI_TYPE_PACKAGE) continue; + if (!obj->package.count) + continue; + id = &obj->package.elements[0]; if (id->type != ACPI_TYPE_INTEGER) continue; From 2faf0198168d2017cb528a79f76c560fda3b6e94 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 30 Jun 2026 21:40:03 +0200 Subject: [PATCH 2060/3297] riscv: probes: save original sp in rethook trampoline commit bc7b086a45521a986a49045907f017e3e46c763e upstream. Reading a word from the stack in a kretprobe crashes a risc-v kernel. $ cd /sys/kernel/tracing/ $ echo 'r n_tty_write $stack0' > dynamic_events $ echo 1 > events/kprobes/enable Unable to handle kernel paging request at virtual address 0000000200000128 ... [] regs_get_kernel_stack_nth+0x26/0x38 [] process_fetch_insn+0x3ee/0x760 [] kretprobe_trace_func+0x116/0x1f0 [] kretprobe_dispatcher+0x4a/0x58 [] kretprobe_rethook_handler+0x5e/0x90 [] rethook_trampoline_handler+0x70/0x108 [] arch_rethook_trampoline_callback+0x12/0x1c [] arch_rethook_trampoline+0x48/0x94 [] tty_write+0x1a/0x30 In regs_get_kernel_stack_nth, regs->sp contains an arbitrary value. arch_rethook_trampoline saves the registers from the probed function in a struct pt_regs. sp is not saved. Instead, sp is decremented for arch_rethook_trampoline's local stack. Fix this crash and save the original sp along with the other registers. Use a0 as a temporary register, it is overwritten anyway. Cc: stable@vger.kernel.org Fixes: c22b0bcb1dd02 ("riscv: Add kprobes supported") Signed-off-by: Martin Kaiser Acked-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260630194010.1824039-1-martin@kaiser.cx [pjw@kernel.org: added Fixes tag; cc'ed stable] Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/probes/rethook_trampoline.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/probes/rethook_trampoline.S b/arch/riscv/kernel/probes/rethook_trampoline.S index f2cd83d9b0f00..c3aa8d8cf5aff 100644 --- a/arch/riscv/kernel/probes/rethook_trampoline.S +++ b/arch/riscv/kernel/probes/rethook_trampoline.S @@ -41,6 +41,9 @@ REG_S x29, PT_T4(sp) REG_S x30, PT_T5(sp) REG_S x31, PT_T6(sp) + /* save original sp */ + addi a0, sp, PT_SIZE_ON_STACK + REG_S a0, PT_SP(sp) .endm .macro restore_all_base_regs From 018d7ad26cb8bdfe9842f2ca68a42b0bfdcc82fe Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Mon, 22 Jun 2026 11:30:42 -0400 Subject: [PATCH 2061/3297] mm/compaction: handle free_pages_prepare() properly in compaction_free() commit 7da7d599b8a83271c464adfd5ef160202b470570 upstream. free_pages_prepare() can fail but compaction_free() does not handle the failure case. Failed pages should not be added back to cc->freepages for future use, since they can be either PageHWPoison or free_page_is_bad() and might cause data corruption. Link: https://lore.kernel.org/20260622-handle_free_pages_prepare_in_compaction_free-v1-1-fcf3b14abcf7@nvidia.com Fixes: 733aea0b3a7b ("mm/compaction: add support for >0 order folio memory compaction.") Signed-off-by: Zi Yan Reviewed-by: Vlastimil Babka (SUSE) Acked-by: Johannes Weiner Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Cc: Brendan Jackman Cc: Jiaqi Yan Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/compaction.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mm/compaction.c b/mm/compaction.c index 1e8f8eca318c6..6769a9d6f16a8 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1858,15 +1858,14 @@ static void compaction_free(struct folio *dst, unsigned long data) int order = folio_order(dst); struct page *page = &dst->page; - if (folio_put_testzero(dst)) { - free_pages_prepare(page, order); + if (folio_put_testzero(dst) && free_pages_prepare(page, order)) { list_add(&dst->lru, &cc->freepages[order]); cc->nr_freepages += 1 << order; } cc->nr_migratepages += 1 << order; /* - * someone else has referenced the page, we cannot take it back to our - * free list. + * someone else has referenced the page or free_pages_prepare() fails, + * we cannot take it back to our free list. */ } From a5a367756926de1c21a013ea5ed7fc2219f4cb4f Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 15:37:44 +0800 Subject: [PATCH 2062/3297] irqchip/irq-riscv-imsic-early: Fix fwnode leak on state setup failure commit 1358126fbed104e5657955d3ba029b283687ba02 upstream. imsic_early_acpi_init() allocates a firmware node before setting up the IMSIC state. If imsic_setup_state() fails, the function returns without freeing the allocated fwnode. Free the fwnode and clear the global pointer on this error path, matching the cleanup already done when imsic_early_probe() fails. [ tglx: Use a common cleanup path instead of copying code around ] Fixes: fbe826b1c106 ("irqchip/riscv-imsic: Add ACPI support") Signed-off-by: Haoxiang Li Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260623073744.2009137-1-haoxiang_li2024@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-riscv-imsic-early.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index 12526b125bab0..2849d99e11083 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -277,16 +277,13 @@ static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, rc = imsic_setup_state(imsic_acpi_fwnode, imsic); if (rc) { pr_err("%pfwP: failed to setup state (error %d)\n", imsic_acpi_fwnode, rc); - return rc; + goto cleanup; } /* Do early setup of IMSIC state and IPIs */ rc = imsic_early_probe(imsic_acpi_fwnode); - if (rc) { - irq_domain_free_fwnode(imsic_acpi_fwnode); - imsic_acpi_fwnode = NULL; - return rc; - } + if (rc) + goto cleanup; rc = imsic_platform_acpi_probe(imsic_acpi_fwnode); @@ -305,8 +302,12 @@ static int __init imsic_early_acpi_init(union acpi_subtable_headers *header, * DT where IPI works but MSI probe fails for some reason. */ return 0; -} +cleanup: + irq_domain_free_fwnode(imsic_acpi_fwnode); + imsic_acpi_fwnode = NULL; + return rc; +} IRQCHIP_ACPI_DECLARE(riscv_imsic, ACPI_MADT_TYPE_IMSIC, NULL, 1, imsic_early_acpi_init); #endif From f0745496f7c171271cafd9457df3b914a483ddeb Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 23 Jun 2026 19:44:06 +0200 Subject: [PATCH 2063/3297] s390/monwriter: Reject buffer reuse with different data length commit 2995ccec260caa9e85b3301a4aba1e66ed80ad74 upstream. When data buffers are reused, e.g. for interval sample records, the first record determines the data length, and the size of the buffer for user copy. Current monwriter code does not check if the data length was changed for subsequent records, which also would never happen for valid user programs. However, a malicious user could change the data length, resulting in out of bounds user copy to the kernel buffer, and memory corruption. By default, the monwriter misc device is created with root-only permissions, so practical impact is typically low. Fix this by checking for changed data length and rejecting such records. Cc: stable@vger.kernel.org Signed-off-by: Gerald Schaefer Reviewed-by: Christian Borntraeger Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/char/monwriter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 0fab1f025a94d..dab717ae40aa9 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -123,6 +123,9 @@ static int monwrite_new_hdr(struct mon_private *monpriv) kfree(monbuf->data); kfree(monbuf); monbuf = NULL; + } else if (monbuf->hdr.datalen != monhdr->datalen) { + /* Data with buffer reuse must not change its length */ + return -EINVAL; } } else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) { if (mon_buf_count >= mon_max_bufs) From 77caf2d6eba7cb94a7ecd7b369a5974fd7d7c054 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 1 Jul 2026 18:42:22 +0200 Subject: [PATCH 2064/3297] mac802154: remove interfaces with RCU list deletion commit 539dfcf69105d8d3d4d677b71de6e5ede2e6dfa0 upstream. Queue wake, stop, and disable paths walk local->interfaces under RCU. The bulk hardware teardown path removes entries with list_del(), so an asynchronous transmit completion can follow a poisoned list node in ieee802154_wake_queue(). Use list_del_rcu() as in the single-interface removal path. The following unregister_netdevice() waits for in-flight RCU readers before freeing the netdevice, so no separate grace-period wait is needed. Fixes: 592dfbfc72f5 ("mac820154: move interface unregistration into iface") Reported-by: syzbot+36256deb69a588e9290e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=36256deb69a588e9290e Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260701164222.9094-1-alhouseenyousef@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/mac802154/iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 000be60d95803..b823720630e72 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -703,7 +703,7 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local) mutex_lock(&local->iflist_mtx); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { - list_del(&sdata->list); + list_del_rcu(&sdata->list); unregister_netdevice(sdata->dev); } From 5df30f05db96552903680a17f858d250dfd9e86e Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:16:25 +0800 Subject: [PATCH 2065/3297] octeontx2-pf: fix SQB pointer leak on init failure commit 62e7df6d042aeebd5efb581074e28865c04477be upstream. otx2_init_hw_resources() initializes SQ aura and pool resources before several later setup steps. On failure, err_free_sq_ptrs only frees SQB pages, leaving the per-SQ sqb_ptrs arrays behind. Use otx2_free_sq_res() for the SQ unwind path and let it free sqb_ptrs even when sq->sqe has not been allocated yet. 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.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an OcteonTX2 PF device and the corresponding AF mailbox setup to test with, no runtime testing was able to be performed. Fixes: caa2da34fd25 ("octeontx2-pf: Initialize and config queues") Cc: stable@vger.kernel.org Reviewed-by: Ratheesh Kannoth Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260630071625.349996-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 21bb47e9ba61c..ae7bded777688 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1569,15 +1569,15 @@ static void otx2_free_sq_res(struct otx2_nic *pf) otx2_sq_free_sqbs(pf); for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) { sq = &qset->sq[qidx]; - /* Skip freeing Qos queues if they are not initialized */ - if (!sq->sqe) - continue; - qmem_free(pf->dev, sq->sqe); - qmem_free(pf->dev, sq->sqe_ring); - qmem_free(pf->dev, sq->cpt_resp); - qmem_free(pf->dev, sq->tso_hdrs); - qmem_free(pf->dev, sq->timestamps); - kfree(sq->sg); + /* sq->sqe is not initialized for unused QoS queues */ + if (sq->sqe) { + qmem_free(pf->dev, sq->sqe); + qmem_free(pf->dev, sq->sqe_ring); + qmem_free(pf->dev, sq->cpt_resp); + qmem_free(pf->dev, sq->tso_hdrs); + qmem_free(pf->dev, sq->timestamps); + kfree(sq->sg); + } kfree(sq->sqb_ptrs); } } @@ -1712,13 +1712,12 @@ int otx2_init_hw_resources(struct otx2_nic *pf) return err; err_free_nix_queues: - otx2_free_sq_res(pf); otx2_free_cq_res(pf); otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); err_free_txsch: otx2_txschq_stop(pf); err_free_sq_ptrs: - otx2_sq_free_sqbs(pf); + otx2_free_sq_res(pf); err_free_rq_ptrs: otx2_free_aura_ptr(pf, AURA_NIX_RQ); otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); From 685fb410d90e54fb18cbf43b7c25fe4afd14fb84 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Tue, 30 Jun 2026 09:51:57 -0700 Subject: [PATCH 2066/3297] selftests: net: make busywait timeout clock portable commit dd6a23bac306b7aa322e0aaccb60c6e32a198fb3 upstream. loopy_wait() expects millisecond timestamps. However, Ubuntu Resolute can use uutils date, where `date -u +%s%3N` returns seconds plus full nanoseconds instead of a 3-digit millisecond field. This makes busywait expire too early and can make vlan_bridge_binding.sh read a stale operstate. Fixes: 25ae948b4478 ("selftests/net: add lib.sh") Cc: stable@vger.kernel.org # 6.8+ Link: https://github.com/uutils/coreutils/issues/11658 Signed-off-by: Nirmoy Das Link: https://patch.msgid.link/20260630165157.3814871-1-nirmoyd@nvidia.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/lib.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index d0306b27fe95e..e182e4f3b64d5 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -70,12 +70,33 @@ ksft_exit_status_merge() $ksft_xfail $ksft_pass $ksft_skip $ksft_fail } +timestamp_ms() +{ + local now + local seconds + local nanoseconds + + now=$(date -u +%s:%N) || return + seconds=${now%:*} + nanoseconds=${now#*:} + + if [[ $nanoseconds =~ ^[0-9]+$ ]]; then + nanoseconds=${nanoseconds:0:9} + else + nanoseconds=0 + fi + + echo $((seconds * 1000 + 10#$nanoseconds / 1000000)) +} + loopy_wait() { local sleep_cmd=$1; shift local timeout_ms=$1; shift + local start_time + local current_time - local start_time="$(date -u +%s%3N)" + start_time=$(timestamp_ms) || return while true do local out @@ -84,7 +105,7 @@ loopy_wait() return 0 fi - local current_time="$(date -u +%s%3N)" + current_time=$(timestamp_ms) || return if ((current_time - start_time > timeout_ms)); then echo -n "$out" return 1 From 61a7ff4a62003b55a15022567486741b3bd83914 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Tue, 30 Jun 2026 15:48:56 -0400 Subject: [PATCH 2067/3297] llc: fix SAP refcount leak in llc_ui_autobind() commit 660667cd406648bbaffbd5c0d897c2263a852f11 upstream. llc_ui_autobind() opens a SAP after choosing a dynamic LSAP. llc_sap_open() returns a reference owned by the caller, and llc_sap_add_socket() takes a second reference for the socket's membership in the SAP hash tables. llc_ui_bind() drops the caller's reference after adding the socket, but llc_ui_autobind() keeps it. When the socket is closed, llc_sap_remove_socket() releases only the socket reference, leaving the SAP on llc_sap_list with sk_count == 0. This is user-visible because repeated autobind and close cycles can consume all dynamic SAP values and make later autobinds fail with -EUSERS. Drop the caller's reference after a successful autobind, matching llc_ui_bind()'s ownership model. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260630194856.1036497-1-shuangpeng.kernel@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/llc/af_llc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 5958a80fe14cf..1863cacc63a87 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -317,6 +317,7 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) /* assign new connection to its SAP */ llc_sap_add_socket(sap, sk); sock_reset_flag(sk, SOCK_ZAPPED); + llc_sap_put(sap); rc = 0; out: dev_put(dev); From 247d055504dcc852e539b9f7f30d19f9741474bf Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:24 +0800 Subject: [PATCH 2068/3297] ipvs: use parsed transport offset in SCTP state lookup commit 2f75c0faa3361b28e36cc0512b3299e163e25789 upstream. set_sctp_state() reads the SCTP chunk header again in order to drive the IPVS SCTP state table. For IPv6 it computes the offset with sizeof(struct ipv6hdr), while the surrounding IPVS code uses iph.len from ip_vs_fill_iph_skb(), where ipv6_find_hdr() has already skipped extension headers and found the real transport header. This makes the state machine read from the wrong offset for IPv6 SCTP packets that carry extension headers. For example, an INIT packet with an 8-byte destination options header can be scheduled correctly by sctp_conn_schedule(), but set_sctp_state() reads the first byte of the SCTP verification tag as a DATA chunk type. The connection then moves from NONE to ESTABLISHED instead of INIT1, gets the longer established timeout, and updates the active/inactive destination counters incorrectly. This happens even though the SCTP handshake has not completed. Use the parsed transport offset passed down from ip_vs_set_state() for the SCTP chunk-header lookup. For IPv4 and IPv6 packets without extension headers this preserves the existing offset. Fixes: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/netdev/20260705123040.35755-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 394367b7b3887..c67317be17dfa 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -372,20 +372,15 @@ static const char *sctp_state_name(int state) static inline void set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, - int direction, const struct sk_buff *skb) + int direction, const struct sk_buff *skb, + unsigned int iph_len) { struct sctp_chunkhdr _sctpch, *sch; unsigned char chunk_type; int event, next_state; - int ihl, cofs; - -#ifdef CONFIG_IP_VS_IPV6 - ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - ihl = ip_hdrlen(skb); -#endif + int cofs; - cofs = ihl + sizeof(struct sctphdr); + cofs = iph_len + sizeof(struct sctphdr); sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch); if (sch == NULL) return; @@ -472,7 +467,7 @@ sctp_state_transition(struct ip_vs_conn *cp, int direction, unsigned int iph_len) { spin_lock_bh(&cp->lock); - set_sctp_state(pd, cp, direction, skb); + set_sctp_state(pd, cp, direction, skb, iph_len); spin_unlock_bh(&cp->lock); } From 83fb4c2c5344f02eac929f66de3c9d1adfcde04c Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 19:28:36 +0800 Subject: [PATCH 2069/3297] ipvs: reset full ip_vs_seq structs in ip_vs_conn_new commit 2975324d164c552b028632f107b567302863b7f6 upstream. Commit 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") changed ip_vs_conn_new() to allocate an ip_vs_conn object with kmem_cache_alloc(). The function then initializes many fields explicitly, but only resets in_seq.delta and out_seq.delta in the two struct ip_vs_seq members. That leaves init_seq and previous_delta uninitialized. This is normally harmless while the corresponding IP_VS_CONN_F_IN_SEQ or IP_VS_CONN_F_OUT_SEQ flag is clear. For connections learned from a sync message, however, ip_vs_proc_conn() preserves those flags from IP_VS_CONN_F_BACKUP_MASK and passes opt=NULL when the message omits IPVS_OPT_SEQ_DATA. In that case the new connection can be hashed with SEQ flags set but with the rest of in_seq/out_seq still containing stale slab data. When a packet for such a connection is later handled by an IPVS application helper, vs_fix_seq() and vs_fix_ack_seq() use previous_delta and init_seq to rewrite TCP sequence numbers. A malformed sync message can therefore make forwarded packets carry stale slab bytes in their TCP seq/ack numbers, and can also corrupt the forwarded TCP flow. Reset both struct ip_vs_seq members completely before publishing the connection. This matches the existing "reset struct ip_vs_seq" comment and keeps the sequence-adjustment gates inactive unless valid sequence data is installed later. Fixes: 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 37ebb0cb62b8b..bc8ff5f15702b 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -996,8 +996,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, cp->app = NULL; cp->app_data = NULL; /* reset struct ip_vs_seq */ - cp->in_seq.delta = 0; - cp->out_seq.delta = 0; + memset(&cp->in_seq, 0, sizeof(cp->in_seq)); + memset(&cp->out_seq, 0, sizeof(cp->out_seq)); atomic_inc(&ipvs->conn_count); if (flags & IP_VS_CONN_F_NO_CPORT) From 2cf10d042562283ff4ae97c02d0993d4f1b5ea29 Mon Sep 17 00:00:00 2001 From: Daehyeon Ko <4ncienth@gmail.com> Date: Fri, 3 Jul 2026 17:36:33 +0900 Subject: [PATCH 2070/3297] macsec: don't read an unset MAC header in macsec_encrypt() commit f5089008f90c0a7c5520dff3934e0af00adf322d upstream. macsec_encrypt() reads the Ethernet header via eth_hdr(skb) (skb->head + skb->mac_header) to memmove() the 12 source/destination MAC bytes forward and make room for the SecTAG. On the AF_PACKET SOCK_RAW + PACKET_QDISC_BYPASS transmit path the skb reaches the macsec ndo_start_xmit() with the MAC header unset, so eth_hdr(skb) resolves to skb->head + (u16)~0 and the read is out of bounds: a 12-byte heap over-read that is also emitted on the wire as the frame's outer source/destination MAC. KASAN reports a slab-out-of-bounds read in macsec_start_xmit() on 6.0; on current mainline a CONFIG_DEBUG_NET build flags it as an unset mac header in skb_mac_header(). On the TX path the L2 header is at skb->data, so use skb_eth_hdr(), added by commit 96cc4b69581d ("macvlan: do not assume mac_header is set in macvlan_broadcast()") for exactly this purpose. Fixes: c09440f7dcb3 ("macsec: introduce IEEE 802.1AE driver") Cc: stable@vger.kernel.org Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260703083634.2035145-1-4ncienth@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/macsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index ee1b565efca4a..5b19afde065ff 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -642,7 +642,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, } unprotected_len = skb->len; - eth = eth_hdr(skb); + eth = skb_eth_hdr(skb); sci_present = macsec_send_sci(secy); hh = skb_push(skb, macsec_extra_len(sci_present)); memmove(hh, eth, 2 * ETH_ALEN); From b2f426a9a22886071966432ede8fceafffe12b8c Mon Sep 17 00:00:00 2001 From: Dust Li Date: Tue, 7 Jul 2026 15:43:18 +0800 Subject: [PATCH 2071/3297] dibs: loopback: validate offset and size in move_data() commit 78237e3c0720fcc6eb9b87e90fd70f63eeca886f upstream. The loopback move_data() performs a memcpy into the registered DMB without checking whether offset + size exceeds the DMB length. Unlike real ISM hardware, which enforces memory region bounds natively, the software loopback has no such protection. A peer-supplied out-of-bounds offset or oversized write would result in an OOB write past the allocated kernel buffer. Add an explicit bounds check before the memcpy to reject such requests with -EINVAL. Fixes: f7a22071dbf3 ("net/smc: implement DMB-related operations of loopback-ism") Cc: stable@vger.kernel.org Reported-by: Federico Kirschbaum Signed-off-by: Dust Li Reported-by: Baul Lee Link: https://patch.msgid.link/20260707074318.1448662-1-dust.li@linux.alibaba.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/dibs/dibs_loopback.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index aa029e29c6b2c..8e165bdedb33b 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -254,6 +254,11 @@ static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok, read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } + if ((u64)offset + size > rmb_node->len) { + read_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; + } + memcpy((char *)rmb_node->cpu_addr + offset, data, size); sba_idx = rmb_node->sba_idx; read_unlock_bh(&ldev->dmb_ht_lock); From 26b131b2d5b55a81ef6182769d28105a870c0eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Thu, 2 Jul 2026 17:37:02 +0200 Subject: [PATCH 2072/3297] net: macb: drop in-flight Tx SKBs on close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 27f575836cfebbf872dec020428742b10650a955 upstream. The MACB driver has since forever leaked the outgoing SKBs that have not yet been marked as completed. They live in queue->tx_skb which gets freed without remorse nor checking. macb_free_consistent() gets called in a few codepaths, but only close will trigger the added expressions. In macb_open() and macb_alloc_consistent() failure cases, queues' tx_skb just got allocated and are empty. Fixes: 89e5785fc8a6 ("[PATCH] Atmel MACB ethernet driver") Cc: stable@vger.kernel.org Reviewed-by: Nicolai Buchwitz Signed-off-by: Théo Lebrun Link: https://patch.msgid.link/20260702-macb-drop-tx-v4-1-1c833eebdbc8@bootlin.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/cadence/macb_main.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 000796ebb6e36..a6540ef210448 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2589,8 +2589,25 @@ static void macb_free_consistent(struct macb *bp) dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - kfree(queue->tx_skb); - queue->tx_skb = NULL; + if (queue->tx_skb) { + unsigned int dropped = 0, tail; + + for (tail = queue->tx_tail; tail != queue->tx_head; + tail++) { + if (macb_tx_skb(queue, tail)->skb) + dropped++; + macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0); + } + + queue->stats.tx_dropped += dropped; + bp->dev->stats.tx_dropped += dropped; + + kfree(queue->tx_skb); + queue->tx_skb = NULL; + } + + queue->tx_head = 0; + queue->tx_tail = 0; queue->tx_ring = NULL; queue->rx_ring = NULL; } From 5f5783c7806fc2b9e9e777cfa0b85a2203e48b2b Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:01 +0800 Subject: [PATCH 2073/3297] arm64: smp: Fix hot-unplug tearing by forcing unregistration commit 18a4e5cf633fad5c40ac9d936c51bf38db68796d upstream. Sashiko review pointed out the following issue[1]. Commit eba4675008a6 ("arm64: arch_register_cpu() variant to check if an ACPI handle is now available.") introduced architectural safety blocks inside arch_unregister_cpu(). If a hot-unplug operation is determined to be a physical hardware removal (where _STA evaluates to !ACPI_STA_DEVICE_PRESENT), or if firmware evaluation fails, it aborts the unregistration transaction early to protect unreadied arm64 infrastructure. However, returning early from arch_unregister_cpu() causes a catastrophic state tearing because the generic ACPI layer (acpi_processor_post_eject()) unconditionally continues its cleanup flow. This leaves the stale sysfs device leaked in the memory, deadlocking any subsequent hot-add attempts on the same CPU. Fix it by simplifying arch_unregister_cpu() to always proceed with the unregistration, as a pr_err_once() warning is sufficient to make it more visible for currently not supported physical CPU removal. Also remove the redundant NULL check on acpi_handle as it cannot be NULL when calling arch_unregister_cpu(). Cc: Catalin Marinas Cc: Jonathan Cameron Cc: James Morse Cc: stable@vger.kernel.org Link: https://sashiko.dev/#/patchset/20260520022023.126670-1-ruanjinjie@huawei.com [1] Fixes: eba4675008a6 ("arm64: arch_register_cpu() variant to check if an ACPI handle is now available.") Suggested-by: Catalin Marinas Signed-off-by: Jinjie Ruan Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/smp.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 68cea3a4a35ca..2686f94b9953c 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -535,23 +535,13 @@ void arch_unregister_cpu(int cpu) { acpi_handle acpi_handle = acpi_get_processor_handle(cpu); struct cpu *c = &per_cpu(cpu_devices, cpu); - acpi_status status; unsigned long long sta; - - if (!acpi_handle) { - pr_err_once("Removing a CPU without associated ACPI handle\n"); - return; - } + acpi_status status; status = acpi_evaluate_integer(acpi_handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status)) - return; - - /* For now do not allow anything that looks like physical CPU HP */ - if (cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) { + if (!ACPI_FAILURE(status) && + cpu_present(cpu) && !(sta & ACPI_STA_DEVICE_PRESENT)) pr_err_once("Changing CPU present bit is not supported\n"); - return; - } unregister_cpu(c); } From ccdf1770a4ba27e31599d24ad970d77a371c7912 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:02 +0800 Subject: [PATCH 2074/3297] cpu/hotplug: Fix NULL kobject warning in cpuhp_smt_enable() commit f9a82544c7174851f5c7524622f5966dcafd3a47 upstream. On arm64, when booting with `maxcpus` greater than the number of present CPUs (e.g., QEMU -smp cpus=4,maxcpus=8), some CPUs are marked as 'present' but have not yet been registered via register_cpu(). Consequently, the per-cpu device objects for these CPUs are not yet initialized. In cpuhp_smt_enable(), the code iterates over all present CPUs. Calling _cpu_up() for these unregistered CPUs eventually leads to sysfs_create_group() being called with a NULL kobject (or a kobject without a directory), triggering the following warning in fs/sysfs/group.c: WARNING: fs/sysfs/group.c:137 at internal_create_group+0x41c/0x4bc, CPU#2: sh/181 [...] Call trace: internal_create_group+0x41c/0x4bc (P) sysfs_create_group+0x18/0x24 topology_add_dev+0x1c/0x28 cpuhp_invoke_callback+0x104/0x20c __cpuhp_invoke_callback_range+0x94/0x11c _cpu_up+0x200/0x37c When booting with ACPI, arm64 smp_prepare_cpus() currently sets all enumerated CPUs as "present" regardless of their status in the MADT. This causes issues with SMT hotplug control. For instance, with QEMU's "-smp 4,maxcpus=8" configuration, the MADT GICC entries are populated as follows: 1. The first four CPUs: `Enabled` set but `Online Capable` not set. 2. The remaining four CPUs: `Online Capable` set but `Enabled` not set to support potential hot-plugging. Fix this by: 1. When booting with ACPI, checking the ACPI_MADT_ENABLED flag in the GICC entry before calling set_cpu_present() during SMP initialization. 2. Properly managing the present mask in acpi_map_cpu() and acpi_unmap_cpu() to support actual CPU hotplug events, This aligns with other architectures like x86 and LoongArch. 3. Update the arm64 CPU hotplug documentation to no longer state that all online-capable vCPUs are marked as present by the kernel at boot time. This ensures that only physically available or explicitly enabled CPUs are in the present mask, keeping the SMT control logic consistent with the actual hardware state. Cc: Catalin Marinas Cc: Jonathan Cameron Cc: James Morse Cc: Yicong Yang Cc: stable@vger.kernel.org Link: https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#gic-cpu-interface-gicc-structure Fixes: eed4583bcf9a ("arm64: Kconfig: Enable HOTPLUG_SMT") Reviewed-by: Catalin Marinas Suggested-by: Catalin Marinas Signed-off-by: Jinjie Ruan Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- Documentation/arch/arm64/cpu-hotplug.rst | 28 ++++++++++++++---------- arch/arm64/kernel/acpi.c | 2 ++ arch/arm64/kernel/smp.c | 12 +++++++++- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Documentation/arch/arm64/cpu-hotplug.rst b/Documentation/arch/arm64/cpu-hotplug.rst index 8fb438bf77816..7c3379b704aaf 100644 --- a/Documentation/arch/arm64/cpu-hotplug.rst +++ b/Documentation/arch/arm64/cpu-hotplug.rst @@ -47,11 +47,12 @@ ever have can be described at boot. There are no power-domain considerations as such devices are emulated. CPU Hotplug on virtual systems is supported. It is distinct from physical -CPU Hotplug as all resources are described as ``present``, but CPUs may be -marked as disabled by firmware. Only the CPU's online/offline behaviour is -influenced by firmware. An example is where a virtual machine boots with a -single CPU, and additional CPUs are added once a cloud orchestrator deploys -the workload. +CPU Hotplug as all vCPU resources are statically described in the firmware +configuration tables (e.g. MADT), meaning their maximum possible count is +known at boot. However, vCPUs that are not enabled at boot are not marked +as ``present`` by the kernel until they are hotplugged. An example is where +a virtual machine boots with a single CPU, and additional CPUs are added +once a cloud orchestrator deploys the workload. For a virtual machine, the VMM (e.g. Qemu) plays the part of firmware. @@ -60,16 +61,19 @@ brought online. Firmware can enforce its policy via PSCI's return codes. e.g. ``DENIED``. The ACPI tables must describe all the resources of the virtual machine. CPUs -that firmware wishes to disable either from boot (or later) should not be -``enabled`` in the MADT GICC structures, but should have the ``online capable`` -bit set, to indicate they can be enabled later. The boot CPU must be marked as -``enabled``. The 'always on' GICR structure must be used to describe the -redistributors. +that are hot-pluggable must have the ``online capable`` bit set and the +``enabled`` bit cleared in the MADT GICC structures to indicate they can be +enabled later. The boot CPU must be marked as ``enabled`` with its +``online capable`` bit cleared. The 'always on' GICR structure must be used +to describe the redistributors. CPUs described as ``online capable`` but not ``enabled`` can be set to enabled by the DSDT's Processor object's _STA method. On virtual systems the _STA method -must always report the CPU as ``present``. Changes to the firmware policy can -be notified to the OS via device-check or eject-request. +must always set the ``ACPI_STA_DEVICE_PRESENT`` bit, while toggling the +``ACPI_STA_DEVICE_ENABLED`` bit to reflect its plug status. The kernel will +then dynamically mark the vCPU as ``present`` within the OS when the +``ACPI_STA_DEVICE_ENABLED`` bit becomes set during hot-add. Changes to the +firmware policy can be notified to the OS via device-check or eject-request. CPUs described as ``enabled`` in the static table, should not have their _STA modified dynamically by firmware. Soft-restart features such as kexec will diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index b285e174f4f51..b8d75f053f45c 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -448,12 +448,14 @@ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 apci_id, return *pcpu; } + set_cpu_present(*pcpu, true); return 0; } EXPORT_SYMBOL(acpi_map_cpu); int acpi_unmap_cpu(int cpu) { + set_cpu_present(cpu, false); return 0; } EXPORT_SYMBOL(acpi_unmap_cpu); diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 2686f94b9953c..92b57fc87ee96 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -556,6 +556,11 @@ struct acpi_madt_generic_interrupt *acpi_cpu_get_madt_gicc(int cpu) } EXPORT_SYMBOL_GPL(acpi_cpu_get_madt_gicc); +static bool acpi_cpu_is_present(int cpu) +{ + return acpi_cpu_get_madt_gicc(cpu)->flags & ACPI_MADT_ENABLED; +} + /* * acpi_map_gic_cpu_interface - parse processor MADT entry * @@ -660,6 +665,10 @@ static void __init acpi_parse_and_init_cpus(void) early_map_cpu_to_node(i, acpi_numa_get_nid(i)); } #else +static bool acpi_cpu_is_present(int cpu) +{ + return false; +} #define acpi_parse_and_init_cpus(...) do { } while (0) #endif @@ -798,7 +807,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (err) continue; - set_cpu_present(cpu, true); + if (acpi_disabled || acpi_cpu_is_present(cpu)) + set_cpu_present(cpu, true); numa_store_cpu_info(cpu); } } From 1155a9d0a2e0d708e4168f70a0b437324d4ed211 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 6 Jul 2026 15:46:21 -0700 Subject: [PATCH 2075/3297] fs/resctrl: Free mon_data structures on rdt_get_tree() failure commit ca0676ae2e1a23ac3e858224fc630b99f9e216ea upstream. If mkdir_mondata_all() or a subsequent call in rdt_get_tree() fails, the mon_data structures allocated by mon_get_kn_priv() are leaked. Add mon_put_kn_priv() to the out_mongrp error path to free the mon_data structures. Fixes: 2a6566038544 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits") Closes: https://lore.kernel.org/lkml/5d38c1fb-8f91-472b-8897-24b2f50c772b@intel.com/ Reported-by: Reinette Chatre Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Chen Yu Reviewed-by: Ben Horgan Cc: Link: https://patch.msgid.link/433623b7e3316ffd52323255d1aa4f156ad97cb1.1783377598.git.reinette.chatre@intel.com Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/rdtgroup.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 0320360cd7a6e..8f7bd8e086a63 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -73,6 +73,8 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx); static void rdtgroup_destroy_root(void); +static void mon_put_kn_priv(void); + struct dentry *debugfs_resctrl; /* @@ -2807,6 +2809,7 @@ static int rdt_get_tree(struct fs_context *fc) kernfs_remove(kn_mondata); out_mongrp: if (resctrl_arch_mon_capable()) { + mon_put_kn_priv(); rdtgroup_unassign_cntrs(&rdtgroup_default); kernfs_remove(kn_mongrp); } From 52007bfdce5310e8c8a29849bfbfb188a1e50ca0 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Mon, 6 Jul 2026 15:46:23 -0700 Subject: [PATCH 2076/3297] fs/resctrl: Fix double-add of pseudo-locked region's RMID to free list commit b9f089723aee892efc77c349ae47a6b452b293c4 upstream. A pseudo-locked group's RMID is freed when it is created. On unmount rmdir_all_sub() unconditionally frees all RMID of all groups, resulting in a double-free of the pseudo-locked group's RMID. The consequence of this is that the original free results in the pseudo-locked group's RMID being added to the rmid_free_lru linked list and the second free then attempts to add the same RMID entry to the rmid_free_lru again. Do not double-free a pseudo-locked group's RMID. Fixes: e0bdfe8e36f3 ("x86/intel_rdt: Support creation/removal of pseudo-locked region") Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/551432dd7e624a862b8e58314c38aaba0afff3e9.1783377598.git.reinette.chatre@intel.com Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/rdtgroup.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 8f7bd8e086a63..7cfb0086451c5 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -2986,10 +2986,6 @@ static void rmdir_all_sub(void) if (rdtgrp == &rdtgroup_default) continue; - if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || - rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) - rdtgroup_pseudo_lock_remove(rdtgrp); - /* * Give any CPUs back to the default group. We cannot copy * cpu_online_mask because a CPU might have executed the @@ -3000,7 +2996,13 @@ static void rmdir_all_sub(void) rdtgroup_unassign_cntrs(rdtgrp); - free_rmid(rdtgrp->closid, rdtgrp->mon.rmid); + if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || + rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) { + rdtgroup_pseudo_lock_remove(rdtgrp); + } else { + /* Pseudo-locked group's RMID is freed during setup. */ + free_rmid(rdtgrp->closid, rdtgrp->mon.rmid); + } kernfs_remove(rdtgrp->kn); list_del(&rdtgrp->rdtgroup_list); From 7a9a69641b68aca95f5fb001b05b87c4544cb580 Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:53 -0700 Subject: [PATCH 2077/3297] ata: libata-core: Skip HPA resize for locked drives commit 917d0a4b95ea7ba01ed6296fb808f752d5d81107 upstream. Skip HPA resize in ata_hpa_resize() if the drive is security locked. If the drive is locked, the command to read the native max address fails with -EACCES, which currently causes the sticky quirk ATA_QUIRK_BROKEN_HPA to be set on the device. Setting this sticky quirk causes subsequent revalidations (after the drive is unlocked) to bypass HPA checks, preventing the unlocked drive from exposing its full native capacity without a reboot or device removal. Cc: stable@vger.kernel.org Signed-off-by: Terrence Adams Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 501330e91f9d3..6bada38dcadc8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1324,7 +1324,7 @@ static int ata_hpa_resize(struct ata_device *dev) /* do we need to do it? */ if ((dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) || !ata_id_has_lba(dev->id) || !ata_id_hpa_enabled(dev->id) || - (dev->quirks & ATA_QUIRK_BROKEN_HPA)) + (dev->quirks & ATA_QUIRK_BROKEN_HPA) || ata_id_is_locked(dev->id)) return 0; /* read native max address */ From 91ec52dd2a5d9f791ea8fe2c83c8859cd64b8180 Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:54 -0700 Subject: [PATCH 2078/3297] ata: libata-core: Allow capacity transition to zero for locked drives commit 793bf193b18e9bff6c4280268bbffd16a5b533e5 upstream. Commit 91842ed844a0 ("ata: libata-core: Set capacity to zero for a security locked drive") introduced setting the device capacity (n_sectors) to zero in ata_dev_configure() if the drive is security locked. However, during runtime revalidation, ata_dev_revalidate() compares the new capacity (now 0) with the old capacity (>0) and detects a mismatch. Since it does not consider the locked status, it returns -ENODEV. This revalidation failure can occur when doing a reset of the PHY (e.g. hard reset) for a controller that has I/Os in flight. The timed out I/Os trigger the SCSI Error Handling (EH) path, which in turn invokes libata device revalidation. If the drive is locked at runtime (e.g. it lost power during reset and relocked), revalidation sees the capacity transition to zero and fails, eventually disabling the device. Fix this by allowing the capacity transition to zero in ata_dev_revalidate() if the drive is reported as security locked by ata_id_is_locked(). Fixes: 91842ed844a0 ("ata: libata-core: Set capacity to zero for a security locked drive") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Terrence Adams Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 6bada38dcadc8..22ce6accbc5bb 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3963,7 +3963,7 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, /* verify n_sectors hasn't changed */ if (dev->class != ATA_DEV_ATA || !n_sectors || - dev->n_sectors == n_sectors) + dev->n_sectors == n_sectors || ata_id_is_locked(dev->id)) return 0; /* n_sectors has changed */ From 38cc4867540ae8beedfe41a1a1a6ed37052c77d6 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 9 Jul 2026 22:28:37 -0400 Subject: [PATCH 2079/3297] drbd: reject data replies with an out-of-range payload size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bd910a7660d280595ef94cb6d193951d855d330f upstream. recv_dless_read() receives a P_DATA_REPLY from a peer into the bio of an outstanding read request. The peer-supplied payload length reaches it as the signed int data_size, and two peer-controlled inputs can make it negative. With a negotiated data-integrity-alg the digest length is subtracted first, so a reply whose payload is smaller than the digest underflows data_size. With no integrity algorithm (the default) data_size is assigned from the unsigned h95/h100 wire length and drbdd() never bounds it for a payload-carrying command, so a length above INT_MAX casts it negative; this path needs no non-default feature. The bio receive loop then computes expect = min_t(int, data_size, bv_len), which is negative, and drbd_recv_all_warn(mapped, expect) receives with a size_t of SIZE_MAX into the first mapped page. The sibling receive path read_in_block() is not affected: it uses an unsigned size and rejects it against DRBD_MAX_BIO_SIZE before receiving. Reject a data reply whose size is negative after the optional digest subtraction, covering both triggers. Impact: a malicious or man-in-the-middle DRBD peer copies attacker-chosen bytes past a bio page in the receiver, corrupting kernel memory. A node that reads from its peer (a diskless node, or read-balancing to the peer) is exposed in the default configuration; data-integrity-alg is not required. Fixes: b411b3637fa7 ("The DRBD driver") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5-5-xhigh Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Christoph Böhmwalder Link: https://patch.msgid.link/20260710022837.3738461-1-michael.bommarito@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/drbd/drbd_receiver.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index caaf2781136d0..acac73b75c7cb 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1810,6 +1810,11 @@ static int recv_dless_read(struct drbd_peer_device *peer_device, struct drbd_req data_size -= digest_size; } + if (data_size < 0) { + drbd_err(peer_device, "Invalid data reply size\n"); + return -EIO; + } + /* optimistically update recv_cnt. if receiving fails below, * we disconnect anyways, and counters will be reset. */ peer_device->device->recv_cnt += data_size>>9; From eadd0c2c76aee6a738f17f251434c77db3c606c8 Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Mon, 6 Jul 2026 11:27:07 +1200 Subject: [PATCH 2080/3297] riscv: Prevent NULL pointer dereference in machine_kexec_prepare() commit 81bbcff0c053c4f5c711c31a9b72fc492bd96c3f upstream. A NULL pointer dereference issue is noticed in riscv's machine_kexec_prepare(), where image->segment[i].buf might be NULL and copied unchecked. The NULL buf comes from ima_add_kexec_buffer(), where kbuf is added by kexec_add_buffer(), but kbuf.buffer is NULL, then it is copied without a check in machine_kexec_prepare(): kexec_file_load -> kimage_file_alloc_init() -> kimage_file_prepare_segments() -> ima_add_kexec_buffer() -> kexec_add_buffer() -> machine_kexec_prepare() -> memcpy() Address this by adding a check before the data copy attempt. Fixes: b7fb4d78a6ad ("RISC-V: use memcpy for kexec_file mode") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/kexec/CAO7dBbVftLUhd2qrh7hmijTB3PEPfZAhykCGqEfrPoOcSrrj-w@mail.gmail.com/ Acked-by: Baoquan He Acked-by: Pratyush Yadav Reviewed-by: Nutty Liu Signed-off-by: Tao Liu Link: https://patch.msgid.link/20260705232706.30265-2-ltao@redhat.com Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/machine_kexec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c index 2306ce3e5f229..738df176ff6f1 100644 --- a/arch/riscv/kernel/machine_kexec.c +++ b/arch/riscv/kernel/machine_kexec.c @@ -41,6 +41,9 @@ machine_kexec_prepare(struct kimage *image) if (image->segment[i].memsz <= sizeof(fdt)) continue; + if (!image->segment[i].buf) + continue; + if (image->file_mode) memcpy(&fdt, image->segment[i].buf, sizeof(fdt)); else if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt))) From fad36954b29592ce463254179c3043697678481f Mon Sep 17 00:00:00 2001 From: Crystal Wood Date: Mon, 8 Jun 2026 23:54:30 -0500 Subject: [PATCH 2081/3297] tracing/osnoise: Call synchronize_rcu() when unregistering commit fe58f457ad8d0a2bef4e053cfecca4b5cd266b1a upstream. This ensures that any RCU readers traversing the instance list have finished, before releasing the reference on the tracer that the instance points to. Cc: stable@vger.kernel.org Fixes: a6ed2aee54644 ("tracing: Switch to kvfree_rcu() API") Link: https://patch.msgid.link/20260609045430.1589786-1-crwood@redhat.com Suggested-by: Steven Rostedt Signed-off-by: Crystal Wood Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_osnoise.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c index b93ab382d833c..1bf9ed1ae7f84 100644 --- a/kernel/trace/trace_osnoise.c +++ b/kernel/trace/trace_osnoise.c @@ -160,7 +160,9 @@ static void osnoise_unregister_instance(struct trace_array *tr) if (!found) return; - kvfree_rcu_mightsleep(inst); + /* Do a full sync to ensure that tr remains valid, not just inst */ + synchronize_rcu(); + kvfree(inst); } /* From c6b4d454865a81ceea1422243aaaedc363b6f713 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Jun 2026 16:31:24 +0200 Subject: [PATCH 2082/3297] s390/diag: Add missing array_index_nospec() call to memtop_get_page_count() commit b7577fe4c47a31ca7c99714c53244a44af03cdfe upstream. 'level' is user space controlled and used to read from an array. Add the missing array_index_nospec() call to prevent speculative execution. Cc: stable@vger.kernel.org Fixes: 0d30871739ab ("s390/diag: Add memory topology information via diag310") Signed-off-by: Heiko Carstens Reviewed-by: Mete Durlu Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/diag/diag310.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/s390/kernel/diag/diag310.c b/arch/s390/kernel/diag/diag310.c index f411562aa7f6b..c68ec9d28513d 100644 --- a/arch/s390/kernel/diag/diag310.c +++ b/arch/s390/kernel/diag/diag310.c @@ -190,17 +190,18 @@ static int memtop_get_stride_len(unsigned long *res) static int memtop_get_page_count(unsigned long *res, unsigned long level) { static unsigned long memtop_pages[DIAG310_LEVELMAX]; - unsigned long pages; + unsigned long pages, idx; int rc; if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN) return -EINVAL; - pages = READ_ONCE(memtop_pages[level - 1]); + idx = array_index_nospec(level - 1, ARRAY_SIZE(memtop_pages)); + pages = READ_ONCE(memtop_pages[idx]); if (!pages) { rc = diag310_get_memtop_size(&pages, level); if (rc) return rc; - WRITE_ONCE(memtop_pages[level - 1], pages); + WRITE_ONCE(memtop_pages[idx], pages); } *res = pages; return 0; From 3dfec7490f3a26708b7a139d1bde7b6f8414e78f Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 30 Jun 2026 19:32:14 +0200 Subject: [PATCH 2083/3297] s390/mm: Fix type mismatch in get_align_mask(). commit ec84aad4c3594307d103af563991b4415ac5c8ab upstream. Commit 86f48f922ba79 ("s390/mmap: disable mmap alignment when randomize_va_space = 0") introduced get_align_mask() with return type of 'int', while the target field 'info.align_mask' in struct vm_unmapped_area_info is 'unsigned long'. With currently used masks, this should not cause truncation issues, but fix it and return 'unsigned long' to avoid future problems. Fixes: 86f48f922ba79 ("s390/mmap: disable mmap alignment when randomize_va_space = 0") Cc: stable@vger.kernel.org # v6.9+ Signed-off-by: Gerald Schaefer Reviewed-by: Sven Schnelle Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- arch/s390/mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c index 197c1d9497a74..53c5ba2c963e1 100644 --- a/arch/s390/mm/mmap.c +++ b/arch/s390/mm/mmap.c @@ -65,7 +65,7 @@ static inline unsigned long mmap_base(unsigned long rnd, return PAGE_ALIGN(STACK_TOP - gap - rnd); } -static int get_align_mask(struct file *filp, unsigned long flags) +static unsigned long get_align_mask(struct file *filp, unsigned long flags) { if (filp && is_file_hugepages(filp)) return huge_page_mask_align(filp); From 8131a91fe2dea7b1316cf5557ab13627f545b406 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Tue, 7 Jul 2026 16:23:48 +0800 Subject: [PATCH 2084/3297] selftests/rseq: Fix a building error for riscv arch commit a2ac823d8a228e392bc6a79ea63b550ab84a55b2 upstream. RISC-V rseq selftests include asm/fence.h from tools/arch/riscv, but the rseq Makefile only adds tools/include in the CFLAGS, this results in the building failure both for native and cross build: In file included from rseq.h:131, from rseq.c:37: rseq-riscv.h:11:10: fatal error: asm/fence.h: No such file or directory To fix it, add the matching tools/arch/$(ARCH)/include path in the CFLAGS and derive ARCH from SUBARCH for standalone native builds where ARCH is not set. Fixes: c92786e179e0 ("KVM: riscv: selftests: Use the existing RISCV_FENCE macro in `rseq-riscv.h`") Cc: stable@vger.kernel.org Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260707082348.36896-1-hui.wang@canonical.com Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/rseq/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile index 0d0a5fae59547..ee4325874bae3 100644 --- a/tools/testing/selftests/rseq/Makefile +++ b/tools/testing/selftests/rseq/Makefile @@ -5,9 +5,13 @@ CLANG_FLAGS += -no-integrated-as endif top_srcdir = ../../../.. +include $(top_srcdir)/scripts/subarch.include +ARCH ?= $(SUBARCH) +LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \ - $(CLANG_FLAGS) -I$(top_srcdir)/tools/include + $(CLANG_FLAGS) -I$(top_srcdir)/tools/include \ + -I$(LINUX_TOOL_ARCH_INCLUDE) LDLIBS += -lpthread -ldl # Own dependencies because we only want to build against 1st prerequisite, but From c844b7d9a9586de15dd28c06da5cc7f6ab28787d Mon Sep 17 00:00:00 2001 From: Farhad Alemi Date: Mon, 6 Jul 2026 10:20:23 +0200 Subject: [PATCH 2085/3297] cgroup/cpuset: rebind mm mempolicy to effective_mems, not mems_allowed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b983c56426383e4a06fa5970c4e33cee879b1482 upstream. Creating a child cpuset where cpuset.mems is never set leads to a div/0 when a VMA mempolicy with MPOL_F_RELATIVE_NODES rebinds in response to a CPU hotplug event. Reproduction steps: 1) Create a cgroup w/ cpuset controls (do not set cpuset.mems) 2) Move the task into the child cpuset 3) Create a VMA mempolicy for that task with MPOL_F_RELATIVE_NODES 4) unplug and hotplug a cpu echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online 5) mempolicy rebind does a div/0 in mpol_relative_nodemask on the call to __nodes_fold() The cpuset code passes (cs->mems_allowed) which is not guaranteed to have nodes to the rebind routine. Use cs->effective_mems instead, which is guaranteed to have a non-empty nodemask once we reach that code path. Link: https://lore.kernel.org/all/CA+0ovCiEz6SP_sn3kN4Tb+_oC=eHMXy_Ffj=usV3wREdQrUtww@mail.gmail.com/ Fixes: ae1c802382f7 ("cpuset: apply cs->effective_{cpus,mems}") Closes: https://lore.kernel.org/linux-mm/CA+0ovCgxbZkXa+OU8w3s84R3KNPNxxRfmsNR-udh+afQBbGNmw@mail.gmail.com/ Suggested-by: Gregory Price Suggested-by: Waiman Long Acked-by: Waiman Long Signed-off-by: Farhad Alemi Cc: Andrew Morton Cc: Alistair Popple Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Matthew Brost Cc: Rakie Kim Cc: Rasmus Villemoes Cc: Zi Yan Cc: Tejun Heo Cc: Ridong Chen Cc: Johannes Weiner Cc: "Michal Koutný" Cc: [ david: add a comment, slightly rephrase description ] Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- kernel/cgroup/cpuset.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 5057cf44342b5..2386090351cd3 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2774,7 +2774,12 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs) migrate = is_memory_migrate(cs); - mpol_rebind_mm(mm, &cs->mems_allowed); + /* + * For v1 we can have empty effective_mems, but we cannot + * attach any tasks (see cpuset_can_attach_check()). For v2, + * effective_mems is guaranteed to not be empty. + */ + mpol_rebind_mm(mm, &cs->effective_mems); if (migrate) cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems); else From 9a0464fcfae4f35874eddfbc3925cc1661dacd65 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:10 +0800 Subject: [PATCH 2086/3297] pmdomain: imx: Fix i.MX8MP power notifier commit 72422525f641f68bed6ca3389d29ee3f41fdea33 upstream. Using imx8mm_vpu_power_notifier() for i.MX8MP is wrong, as it ungates the VPU clocks to provide the ADB clock, which is necessary on i.MX8MM, but on i.MX8MP there is a separate gate (bit 3) for the NoC. So add imx8mp_vpu_power_notifier() for i.MX8MP. Fixes: a1a5f15f7f6cb ("soc: imx: imx8m-blk-ctrl: add i.MX8MP VPU blk ctrl") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Peng Fan Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/imx/imx8m-blk-ctrl.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 19e992d2ee3b8..e13a47eeed75d 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -514,9 +514,34 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] }, }; +static int imx8mp_vpu_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action == GENPD_NOTIFY_ON) { + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + + udelay(5); + + /* set "fuse" bits to enable the VPUs */ + regmap_set_bits(bc->regmap, 0x8, 0xffffffff); + regmap_set_bits(bc->regmap, 0xc, 0xffffffff); + regmap_set_bits(bc->regmap, 0x10, 0xffffffff); + regmap_set_bits(bc->regmap, 0x14, 0xffffffff); + } + + return NOTIFY_OK; +} + static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = { .max_reg = 0x18, - .power_notifier_fn = imx8mm_vpu_power_notifier, + .power_notifier_fn = imx8mp_vpu_power_notifier, .domains = imx8mp_vpu_blk_ctl_domain_data, .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data), }; From 4ff3960f3527189bc115a927ed3561c758f562f1 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:11 +0800 Subject: [PATCH 2087/3297] pmdomain: imx: Fix i.MX8MP VC8000E power up sequence commit 25e252bcf1593b420b12a7231d9dd64b885a2ae2 upstream. Per errata[1]: ERR050531: VPU_NOC power down handshake may hang during VC8000E/VPUMIX power up/down cycling. Description: VC8000E reset de-assertion edge and AXI clock may have a timing issue. Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is de-asserted by HW) Add a bool variable is_errata_err050531 in 'struct imx8m_blk_ctrl_domain_data' to represent whether the workaround is needed. If is_errata_err050531 is true, first clear the clk before powering up gpc, then enable the clk after powering up gpc. [1] https://www.nxp.com/webapp/Download?colCode=IMX8MP_1P33A Fixes: a1a5f15f7f6cb ("soc: imx: imx8m-blk-ctrl: add i.MX8MP VPU blk ctrl") Cc: stable@vger.kernel.org Signed-off-by: Peng Fan Reviewed-by: Frank Li Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/imx/imx8m-blk-ctrl.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index e13a47eeed75d..99d100e1d923f 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -54,6 +54,15 @@ struct imx8m_blk_ctrl_domain_data { * register. */ u32 mipi_phy_rst_mask; + + /* + * VC8000E reset de-assertion edge and AXI clock may have a timing issue. + * Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off + * both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to + * VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is + * de-asserted by HW) + */ + bool is_errata_err050531; }; #define DOMAIN_MAX_CLKS 4 @@ -108,7 +117,11 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) dev_err(bc->dev, "failed to enable clocks\n"); goto bus_put; } - regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + if (data->is_errata_err050531) + regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + else + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); /* power up upstream GPC domain */ ret = pm_runtime_get_sync(domain->power_dev); @@ -117,6 +130,9 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) goto clk_disable; } + if (data->is_errata_err050531) + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + /* wait for reset to propagate */ udelay(5); @@ -511,6 +527,7 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] .clk_mask = BIT(2), .path_names = (const char *[]){"vc8000e"}, .num_paths = 1, + .is_errata_err050531 = true, }, }; From ba481c0b537607b238e64c3e92b4d39d6ea1b790 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 10 Jul 2026 10:16:42 +0200 Subject: [PATCH 2088/3297] selftests/landlock: Skip scoped_signal subtest with MSG_OOB if not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5ab1dc6d110db6bee167a32fd94c53ea0e7ad6d2 upstream. MSG_OOB might be disabled in the kernel for unix sockets (by not selecting CONFIG_AF_UNIX_OOB), and in this case the related tests of the scoped_signal_test are currently failing. Add a runtime probe using socketpair() to detect MSG_OOB support and skip the test gracefully if it is unavailable. Signed-off-by: Thomas Huth Link: https://patch.msgid.link/20260710081642.405916-1-thuth@redhat.com Cc: stable@vger.kernel.org Fixes: f34e9ce5f479 ("selftests/landlock: Test signal created by out-of-bound message") Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- .../selftests/landlock/scoped_signal_test.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index f24f2c28f62e5..01d1c8810af30 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -400,6 +400,24 @@ static int setup_signal_handler(int signal) return sigaction(SIGURG, &sa, NULL); } +/* + * MSG_OOB might be disabled in the kernel via the CONFIG_AF_UNIX_OOB + * switch, so this function can be used for probing for its availability. + */ +static bool has_af_unix_oob(void) +{ + bool available = false; + int sp[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0) { + available = (send(sp[0], ".", 1, MSG_OOB) == 1); + close(sp[0]); + close(sp[1]); + } + + return available; +} + /* clang-format off */ FIXTURE(fown) {}; /* clang-format on */ @@ -462,6 +480,9 @@ TEST_F(fown, sigurg_socket) int pipe_parent[2], pipe_child[2]; pid_t child; + if (!has_af_unix_oob()) + SKIP(return, "CONFIG_AF_UNIX_OOB / MSG_OOB not available"); + memset(&server_address, 0, sizeof(server_address)); set_unix_address(&server_address, 0); From d94160a5d1ac3936a5c09676e35336aaef6444c1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 9 Jul 2026 18:43:40 +0200 Subject: [PATCH 2089/3297] selftests/landlock: Fix screwed up pointers in the scoped_signal_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d793186aa3bb833c878ae6826c87e62c843afaa3 upstream. The scoped_signal_test uses pthread_join(..., (void **)&ret)) in a couple of places, i.e. the return value of the thread is stored in the shape of a "void *" into the memory location of &ret. Pointers are 64-bit on modern computers, but the ret variable is declared as a simple "enum thread_return" which is only 32 bits. So the pthread_join() will overflow the ret variable by 4 byte. The problem is very visible on big endian systems like s390x where the test is failing: The least significant byte that carries the return code of the thread is not written into the ret variable here, but somewhere else in the stack frame, so the comparison for the right return code is failing here. Fix it by getting rid of the enum and defining the THREAD_* constants and "ret" variables as proper "void *" pointers. This way we can also get rid of some ugly (void *) castings in a couple of spots. Signed-off-by: Thomas Huth Link: https://patch.msgid.link/20260709164340.339656-1-thuth@redhat.com Cc: stable@vger.kernel.org Fixes: c8994965013e ("selftests/landlock: Test signal scoping for threads") [mic: Add clang-format markups] Signed-off-by: Mickaël Salaün Signed-off-by: Greg Kroah-Hartman --- .../selftests/landlock/scoped_signal_test.c | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c index 01d1c8810af30..2d37d0c06c069 100644 --- a/tools/testing/selftests/landlock/scoped_signal_test.c +++ b/tools/testing/selftests/landlock/scoped_signal_test.c @@ -249,12 +249,12 @@ TEST_F(scoped_domains, check_access_signal) _metadata->exit_code = KSFT_FAIL; } -enum thread_return { - THREAD_INVALID = 0, - THREAD_SUCCESS = 1, - THREAD_ERROR = 2, - THREAD_TEST_FAILED = 3, -}; +/* clang-format off */ +#define THREAD_INVALID ((void *)0) +#define THREAD_SUCCESS ((void *)1) +#define THREAD_ERROR ((void *)2) +#define THREAD_TEST_FAILED ((void *)3) +/* clang-format on */ static void *thread_sync(void *arg) { @@ -262,15 +262,15 @@ static void *thread_sync(void *arg) char buf; if (read(pipe_read, &buf, 1) != 1) - return (void *)THREAD_ERROR; + return THREAD_ERROR; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } TEST(signal_scoping_thread_before) { pthread_t no_sandbox_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int thread_pipe[2]; drop_caps(_metadata); @@ -285,7 +285,7 @@ TEST(signal_scoping_thread_before) EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0)); EXPECT_EQ(1, write(thread_pipe[1], ".", 1)); - EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); EXPECT_EQ(0, close(thread_pipe[0])); @@ -295,7 +295,7 @@ TEST(signal_scoping_thread_before) TEST(signal_scoping_thread_after) { pthread_t scoped_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int thread_pipe[2]; drop_caps(_metadata); @@ -310,7 +310,7 @@ TEST(signal_scoping_thread_after) EXPECT_EQ(0, pthread_kill(scoped_thread, 0)); EXPECT_EQ(1, write(thread_pipe[1], ".", 1)); - EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(scoped_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); EXPECT_EQ(0, close(thread_pipe[0])); @@ -327,20 +327,20 @@ void *thread_setuid(void *ptr) char buf; if (read(arg->pipe_read, &buf, 1) != 1) - return (void *)THREAD_ERROR; + return THREAD_ERROR; /* libc's setuid() should update all thread's credentials. */ if (getuid() != arg->new_uid) - return (void *)THREAD_TEST_FAILED; + return THREAD_TEST_FAILED; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } TEST(signal_scoping_thread_setuid) { struct thread_setuid_args arg; pthread_t no_sandbox_thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int pipe_parent[2]; int prev_uid; @@ -367,7 +367,7 @@ TEST(signal_scoping_thread_setuid) EXPECT_EQ(arg.new_uid, getuid()); EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); - EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret)); + EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret)); EXPECT_EQ(THREAD_SUCCESS, ret); clear_cap(_metadata, CAP_SETUID); @@ -688,20 +688,20 @@ static void *thread_setown_scoped(void *arg) ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); if (ruleset_fd < 0) - return (void *)THREAD_ERROR; + return THREAD_ERROR; if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) || landlock_restrict_self(ruleset_fd, 0)) { close(ruleset_fd); - return (void *)THREAD_ERROR; + return THREAD_ERROR; } close(ruleset_fd); /* Makes this process group own the SIGIO source. */ if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) || fcntl(fd, F_SETFL, O_ASYNC)) - return (void *)THREAD_ERROR; + return THREAD_ERROR; - return (void *)THREAD_SUCCESS; + return THREAD_SUCCESS; } /* @@ -723,7 +723,7 @@ TEST(sigio_to_pgid_self) { int trigger[2]; pthread_t thread; - enum thread_return ret = THREAD_INVALID; + void *ret = THREAD_INVALID; int i; drop_caps(_metadata); @@ -743,7 +743,7 @@ TEST(sigio_to_pgid_self) */ ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped, &trigger[0])); - ASSERT_EQ(0, pthread_join(thread, (void **)&ret)); + ASSERT_EQ(0, pthread_join(thread, &ret)); ASSERT_EQ(THREAD_SUCCESS, ret); /* Fans SIGURG out to the process group. */ From afa0db5322c5f26c565d8c0a7c8f68244c3d5f9b Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:14 +0800 Subject: [PATCH 2090/3297] mmc: sdhci-esdhc-imx: restore pinctrl before restoring ios timing on resume commit 1db87818bde3d2295613660879378b43a70d31f8 upstream. SDIO devices such as WiFi may keep power during suspend, so the MMC core skips full card re-initialization on resume and directly restores the host controller's ios timing to match the card. For DDR mode, pm_runtime_force_resume() sets DDR_EN before the pin configuration is restored from sleep state. This is related to the SoC IP integration: switching pinctrl setting (changing alt from GPIO to USDHC) impacts the internal loopback path. If pinctrl configures the pad to GPIO function, once DDR_EN is set, the DLL delay will be fixed based on the GPIO function loopback path. When the pinctrl is later changed to USDHC function, the internal loopback path changes, making the original fixed sample point no longer suitable for the current loopback path. This causes persistent read CRC errors on subsequent data transfers. SD/eMMC running in DDR mode are unaffected as they are fully re-initialized from legacy timing after resume. Fix this by restoring the pinctrl state based on current timing mode using esdhc_change_pinstate() before pm_runtime_force_resume(). This ensures the correct pin configuration (e.g., 100/200MHz for UHS modes) is applied before DDR_EN is set. Only restore for non-wakeup devices since wakeup devices kept their active pin state during suspend. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a7a5df673b0f6..27d450cf0f990 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2083,6 +2083,12 @@ static int sdhci_esdhc_resume(struct device *dev) struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); int ret; + if (!device_may_wakeup(dev)) { + ret = esdhc_change_pinstate(host, host->timing); + if (ret) + dev_warn(dev, "Failed to restore pinctrl state\n"); + } + pm_runtime_force_resume(dev); ret = mmc_gpio_set_cd_wake(host->mmc, false); From 9b8df4da2cf79317ceaa0500fb96601b0d720cf5 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 14 Jun 2026 16:23:56 +0200 Subject: [PATCH 2091/3297] powerpc/pseries: fix memory leak on krealloc failure in papr_init commit bd83c98b988d2c560531084e296dbfb530aff829 upstream. When krealloc() fails, free the original esi_buf before returning to avoid a memory leak. Fixes: 3c14b73454cf ("powerpc/pseries: Interface to represent PAPR firmware attributes") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260614142356.658212-2-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/papr_platform_attributes.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr_platform_attributes.c b/arch/powerpc/platforms/pseries/papr_platform_attributes.c index eea2041b270b5..f63e2de9cccab 100644 --- a/arch/powerpc/platforms/pseries/papr_platform_attributes.c +++ b/arch/powerpc/platforms/pseries/papr_platform_attributes.c @@ -271,11 +271,9 @@ static int __init papr_init(void) esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs); temp_esi_buf = krealloc(esi_buf, esi_buf_size, GFP_KERNEL); - if (temp_esi_buf) - esi_buf = temp_esi_buf; - else - return -ENOMEM; - + if (!temp_esi_buf) + goto out_free_esi_buf; + esi_buf = temp_esi_buf; goto retry; } From 262da8b6ea03d01ee7ed01ad309e4c89941f6b14 Mon Sep 17 00:00:00 2001 From: Zhiping Zhang Date: Thu, 2 Jul 2026 15:24:58 -0700 Subject: [PATCH 2092/3297] net/mlx5: free mlx5_st_idx_data on final dealloc commit df6134b527a88b3e65ba6ae5073664af091d5fd2 upstream. Workloads that repeatedly allocate and release mkeys carrying TPH steering-tag hints (e.g. churning RDMA MRs) leak one struct mlx5_st_idx_data per cycle; kmemleak flags it as unreferenced and the kmalloc slab grows over time. When the last reference to an ST table entry is dropped, mlx5_st_dealloc_index() removed the entry from idx_xa but the backing mlx5_st_idx_data allocation was never freed. Free idx_data after the xa_erase() so the lifetime of the bookkeeping struct matches the lifetime of the ST entry it tracks. Cc: stable@vger.kernel.org Fixes: 888a7776f4fb ("net/mlx5: Add support for device steering tag") Reviewed-by: Michael Gur Signed-off-by: Zhiping Zhang Reviewed-by: Leon Romanovsky Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260702222507.1234467-1-zhipingz@meta.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/lib/st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c index 47fe215f66bf0..a60e8e0f74ca2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c @@ -154,6 +154,7 @@ int mlx5_st_dealloc_index(struct mlx5_core_dev *dev, u16 st_index) if (refcount_dec_and_test(&idx_data->usecount)) { xa_erase(&st->idx_xa, st_index); + kfree(idx_data); /* We leave PCI config space as was before, no mkey will refer to it */ } From 816559409e340acaa5c9d868291dab30d8c80263 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 15:31:04 +0800 Subject: [PATCH 2093/3297] wifi: rt2x00: avoid full teardown before work setup in probe commit 536fb3d739d75a03cb318c0c6fe799425cfea501 upstream. rt2x00lib_probe_dev() uses the full rt2x00lib_remove_dev() teardown for all probe failures. However, drv_data allocation and workqueue allocation can fail before intf_work, autowakeup_work and sleep_work have been initialized. Do not enter the full remove path until the probe has reached the point where those work items are set up. Return directly for drv_data allocation failure, and use a small early cleanup path for workqueue allocation failure. This issue was found by our static analysis tool and then confirmed by manual review of rt2x00lib_probe_dev() and rt2x00lib_remove_dev(). The early probe exits should not call a common teardown path that assumes the later work setup has already completed. A QEMU PoC forced alloc_ordered_workqueue() to fail before the work initializers are reached. The resulting fail path entered rt2x00lib_remove_dev(), and DEBUG_OBJECTS reported invalid work drains with rt2x00lib_probe_dev() and rt2x00lib_remove_dev() in the stack. Fixes: 1ebbc48520a0 ("rt2x00: Introduce concept of driver data in struct rt2x00_dev.") Fixes: 0439f5367c8d ("rt2x00: Move TX/RX work into dedicated workqueue") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260619073104.1809161-1-runyu.xiao@seu.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index f8a6f9c968a1e..847b64e586f62 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1382,7 +1382,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) GFP_KERNEL); if (!rt2x00dev->drv_data) { retval = -ENOMEM; - goto exit; + return retval; } } @@ -1416,7 +1416,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); if (!rt2x00dev->workqueue) { retval = -ENOMEM; - goto exit; + goto exit_free_drv_data; } INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); @@ -1488,6 +1488,14 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) exit: rt2x00lib_remove_dev(rt2x00dev); + return retval; + +exit_free_drv_data: + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + kfree(rt2x00dev->drv_data); + rt2x00dev->drv_data = NULL; + return retval; } EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); From 65446b85595a4bec4400889e02021487ce92076a Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Wed, 10 Jun 2026 12:00:18 -0300 Subject: [PATCH 2094/3297] wifi: mwifiex: fix roaming to different channel in host_mlme mode commit a707e4127c0f893c7a7703500ab56297a5bd2d51 upstream. When host MLME is enabled, mwifiex_cfg80211_authenticate() transmits the authentication frame on a remain-on-channel (ROC) reservation so that the frame is sent on the target BSS's channel. The ROC is only configured when priv->auth_flag is zero. priv->auth_flag is set to HOST_MLME_AUTH_PENDING when the auth frame is queued and advances to HOST_MLME_AUTH_DONE once authentication completes. It is only cleared back to zero on a disconnect, deauth or timeout path; nothing clears it when an association succeeds. It therefore stays at HOST_MLME_AUTH_DONE for the whole connected session. When the station later roams to a BSS on a different channel, the next authentication finds auth_flag != 0, skips the ROC setup, and the auth frame is transmitted on the currently-associated channel instead of the target's channel. Authentication times out on the new AP and the device stays connected to the original AP. Gate the ROC setup on HOST_MLME_AUTH_PENDING instead of on auth_flag being completely clear. This re-arms the remain-on-channel for every new authentication attempt, while still suppressing a redundant ROC during the multi-frame SAE exchange, where auth_flag stays PENDING between the commit and confirm frames. This change was tested in 3 different devices: Verdin AM62 (IW412 SD-UART) - (16.92.21.p142) Verdin iMX8MM (W8997 SD-SD) - (16.68.1.p197) Verdin iMX8MP (W8997 SD-UART) - (16.92.21.p137) There following loop tests were performed: 1) force roaming between two AP's, one 5GHz and one 2.4GHz, same SSID. Use wpa_cli to trigger the roaming behavior, sleep 2s between iterations. 2) force a disconnection to AP 1 and a connection to AP 2, test scan. Use wpa_cli to trigger the connection changes, sleep 2s between iterations. Each test ran in each device for at least 3 hours. Fixes: 36995892c271 ("wifi: mwifiex: add host mlme for client mode") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Rafael Beims Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260610150021.1018611-1-rafael@beims.me Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index be23a29e7de09..ce569d35d0513 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4326,7 +4326,7 @@ mwifiex_cfg80211_authenticate(struct wiphy *wiphy, return -EOPNOTSUPP; } - if (!priv->auth_flag) { + if (!(priv->auth_flag & HOST_MLME_AUTH_PENDING)) { ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, req->bss->channel, AUTH_TX_DEFAULT_WAIT_TIME); From bdc0b8bfdc142439d6708b072e8159eb515ee7b0 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Mon, 6 Jul 2026 22:35:07 +0800 Subject: [PATCH 2095/3297] wifi: mac80211: fix memory leak in ieee80211_register_hw() commit 95fc02722edde02946d0d475221f2b2054d3d8ba upstream. If kmemdup() fails while copying supported band structures, the error path jumps to fail_rate. This skips rate_control_deinitialize() and leaks the initialized local->rate_ctrl. Fix this by adding a fail_band label that shares the rate-control cleanup path before falling through to the remaining teardown. 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-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have a suitable mac80211 device/driver combination to test with, no runtime testing was able to be performed. Fixes: 09b4a4faf9d0 ("mac80211: introduce capability flags for VHT EXT NSS support") Cc: stable@vger.kernel.org Reviewed-by: Zilin Guan Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260706143507.146131-1-dawei.feng@seu.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index eefa6f7e899bd..655a8aec77386 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1575,7 +1575,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) sband = kmemdup(sband, sizeof(*sband), GFP_KERNEL); if (!sband) { result = -ENOMEM; - goto fail_rate; + goto fail_band; } wiphy_dbg(hw->wiphy, "copying sband (band %d) due to VHT EXT NSS BW flag\n", @@ -1642,6 +1642,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) #endif wiphy_unregister(local->hw.wiphy); fail_wiphy_register: + fail_band: rtnl_lock(); rate_control_deinitialize(local); ieee80211_remove_interfaces(local); From 55b26abb1fa1ec406b3ad11b43c49c7624257565 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 21:13:13 +0800 Subject: [PATCH 2096/3297] wifi: brcmfmac: cyw: fix heap overflow on a short auth frame commit 240c8d2c717b3f8153e7e877b22a82518d78dbdc upstream. brcmf_notify_auth_frame_rx() takes the frame length from the firmware event and copies the frame body with the management header offset subtracted: u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); ... memcpy(&mgmt_frame->u, frame, mgmt_frame_len - offsetof(struct ieee80211_mgmt, u)); The only length check is e->datalen >= sizeof(*rxframe), so mgmt_frame_len can be anything from 0 up. offsetof(struct ieee80211_mgmt, u) is 24. When mgmt_frame_len is below that, the subtraction wraps as an unsigned value to a huge length. The memcpy then runs far past the kzalloc'd buffer. A malicious or malfunctioning AP can make the frame short during the external SAE auth exchange, so this is a remotely triggered heap overflow. Reject frames shorter than the management header offset before the copy. Fixes: 66f909308a7c ("wifi: brcmfmac: cyw: support external SAE authentication in station mode") Link: https://lore.kernel.org/r/178214417708.2368577.16740907093694208834@maoyixie.com Cc: stable@vger.kernel.org Co-developed-by: Kaixuan Li Signed-off-by: Kaixuan Li Signed-off-by: Maoyi Xie Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260627131313.3878893-1-maoyixie.tju@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c index 4f0ea4347840b..b7472e19dd608 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c @@ -294,6 +294,12 @@ brcmf_notify_auth_frame_rx(struct brcmf_if *ifp, return -EINVAL; } + if (mgmt_frame_len < offsetof(struct ieee80211_mgmt, u)) { + bphy_err(drvr, "Event %s (%d) frame too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + wdev = &ifp->vif->wdev; WARN_ON(!wdev); From 688bd4c6144d23be6ba935c4b089ee3ba64e9839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 1 Jul 2026 11:21:22 +0200 Subject: [PATCH 2097/3297] riscv: vdso: Do not use LTO for the vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ad6dcfa023762e37962f77ee48e752b7570e9440 upstream. With LTO enabled the compiler assumes that the vDSO functions are not used and optimizes them away completely. Currently this happens to __vdso_clock_getres(), __vdso_clock_gettime(), __vdso_getrandom(), __vdso_gettimeofday() and __vdso_riscv_hwprobe(). Disable LTO for the vDSO, as these functions are hand-optimized anyways. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606301855.WvkSC4kD-lkp@intel.com/ Fixes: 021d23428bdb ("RISC-V: build: Allow LTO to be selected") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260701-riscv-vdso-lto-v1-1-89db0cd82077@linutronix.de Signed-off-by: Paul Walmsley Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kernel/vdso/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 9ebb5e590f93a..340bbd945591e 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -49,9 +49,9 @@ CPPFLAGS_vdso.lds += -DHAS_VGETTIMEOFDAY endif # Disable -pg to prevent insert call site -CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) +CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) # Force dependency $(obj)/vdso.o: $(obj)/vdso.so From 6926d13865aaa1548b8ba1f57f56e5df78e10774 Mon Sep 17 00:00:00 2001 From: Abhishek Ojha Date: Wed, 15 Jul 2026 13:04:08 -0400 Subject: [PATCH 2098/3297] regulator: ltc3676: Fix incorrect IRQSTAT bit offsets commit 50dce2e2f84b56d8b4b406d97a1543709e8a87f5 upstream. The LTC3676_IRQSTAT_* bit definitions do not match the IRQSTAT (Interrupt Request Status) register layout documented in Table 15 of the LTC3676/LTC3676-1 datasheet: bit 0 - Pushbutton Status Active bit 1 - Hard Reset Occurred bit 2 - PGOOD Timeout Occurred bit 3 - Undervoltage Warning bit 4 - Undervoltage Standby (Fault) Occurred bit 5 - Overtemperature Warning bit 6 - Overtemperature Standby (Fault) Occurred bit 7 - Reserved The driver instead defines these starting at bit 3, one bit higher than the datasheet specifies, which causes ltc3676_regulator_isr() to check the wrong status bits and misreport (or miss) PGOOD timeout, undervoltage and thermal warning/fault conditions. Fix the bit offsets to match the datasheet. Fixes: 37b918a034fe ("regulator: Add LTC3676 support") Cc: stable@vger.kernel.org Signed-off-by: Abhishek Ojha Link: https://patch.msgid.link/20260715170408.295552-1-Abhishek.ojha@savoirfairelinux.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/ltc3676.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c index 73d511eb1c1dc..768cdce0d4ecf 100644 --- a/drivers/regulator/ltc3676.c +++ b/drivers/regulator/ltc3676.c @@ -45,11 +45,11 @@ #define LTC3676_DVBxA_REF_SELECT BIT(5) #define LTC3676_DVBxB_PGOOD_MASK BIT(5) -#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3) -#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4) -#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5) -#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6) -#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7) +#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(2) +#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(3) +#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(4) +#define LTC3676_IRQSTAT_THERMAL_WARN BIT(5) +#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(6) enum ltc3676_reg { LTC3676_SW1, From dbd14f736be02cfe73049bd801af89becd1a0749 Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Mon, 6 Jul 2026 09:44:10 +0000 Subject: [PATCH 2099/3297] net: openvswitch: reject oversized nested action attrs commit 3f1f755366687d051174739fb99f7d560202f60b upstream. Open vSwitch stores generated flow actions as nlattrs, whose nla_len field is u16. Commit a1e64addf3ff ("net: openvswitch: remove misbehaving actions length check") allowed the total sw_flow_actions stream to grow beyond 64 KiB, which is valid, but also removed the last guard preventing a generated nested action attribute from exceeding U16_MAX. An oversized generated container can thus be closed with a truncated nla_len. A later dump or teardown then walks a structurally different stream than the one that was validated. In particular, an oversized nested CLONE/CT action may cause subsequent bytes in the generated stream to be interpreted as independent actions. Keep the larger total-action-stream behavior, but make nested action close reject generated containers that do not fit in nla_len, and return the error through all callers. For recursive SAMPLE, CLONE, DEC_TTL, and CHECK_PKT_LEN builders, trim resource-owning action-list tails in reverse construction order before discarding failed wrappers, so resources copied into the rejected tails are released before the wrappers are removed. Most failed outer wrappers are discarded by truncating actions_len after child resources have been released. CHECK_PKT_LEN also trims its parent after branch resources are gone. SET/TUNNEL close failures unwind their known tun_dst ownership directly, and SET_TO_MASKED has no external ownership and truncates on close failure. Fixes: a1e64addf3ff ("net: openvswitch: remove misbehaving actions length check") Cc: stable@vger.kernel.org Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix Signed-off-by: Asim Viladi Oglu Manizada Reviewed-by: Eelco Chaudron Reviewed-by: Aaron Conole Reviewed-by: Ilya Maximets Link: https://patch.msgid.link/20260706094336.38639-1-manizada@pm.me Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/openvswitch/flow_netlink.c | 201 +++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 44 deletions(-) diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 2dc4a6c2aecec..ed85279ba0248 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2496,13 +2496,56 @@ static inline int add_nested_action_start(struct sw_flow_actions **sfa, return used; } -static inline void add_nested_action_end(struct sw_flow_actions *sfa, - int st_offset) +static inline int add_nested_action_end(struct sw_flow_actions *sfa, + int st_offset) { - struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + - st_offset); + struct nlattr *a; + u32 attr_len; + + if (WARN_ON_ONCE(st_offset < 0 || + (u32)st_offset > sfa->actions_len)) + return -EINVAL; + + attr_len = sfa->actions_len - (u32)st_offset; + if (WARN_ON_ONCE(attr_len < NLA_HDRLEN)) + return -EINVAL; - a->nla_len = sfa->actions_len - st_offset; + if (attr_len > U16_MAX) + return -EMSGSIZE; + + a = (struct nlattr *)((u8 *)sfa->actions + st_offset); + a->nla_len = attr_len; + return 0; +} + +/* Free the generated action-list tail at @start and truncate it. + * If @nested, @start points to its containing nlattr header. + */ +static void ovs_nla_trim(struct sw_flow_actions *sfa, int start, bool nested) +{ + const struct nlattr *actions; + u32 len; + + if (start < 0) + return; + + if (WARN_ON_ONCE((u32)start > sfa->actions_len)) + return; + + actions = (const struct nlattr *)((u8 *)sfa->actions + start); + len = sfa->actions_len - (u32)start; + + if (nested) { + if (len < NLA_HDRLEN) + goto out; + + actions = (const struct nlattr *)((u8 *)actions + NLA_HDRLEN); + len -= NLA_HDRLEN; + } + + ovs_nla_free_nested_actions(actions, len); +out: + sfa->actions_len = start; } static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, @@ -2522,6 +2565,7 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; const struct nlattr *probability, *actions; const struct nlattr *a; + int actions_start; int rem, start, err; struct sample_arg arg; @@ -2565,18 +2609,27 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg), log); if (err) - return err; + goto err; + actions_start = (*sfa)->actions_len; err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, actions_start, false); +err: + (*sfa)->actions_len = start; + return err; } static int validate_and_copy_dec_ttl(struct net *net, @@ -2624,18 +2677,31 @@ static int validate_and_copy_dec_ttl(struct net *net, return start; action_start = add_nested_action_start(sfa, OVS_DEC_TTL_ATTR_ACTION, log); - if (action_start < 0) - return action_start; + if (action_start < 0) { + err = action_start; + goto err; + } err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; + + err = add_nested_action_end(*sfa, action_start); + if (err) + goto err_free; - add_nested_action_end(*sfa, action_start); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, action_start, true); +err: + (*sfa)->actions_len = start; + return err; } static int validate_and_copy_clone(struct net *net, @@ -2646,6 +2712,7 @@ static int validate_and_copy_clone(struct net *net, u32 mpls_label_count, bool log, bool last, u32 depth) { + int actions_start; int start, err; u32 exec; @@ -2661,17 +2728,26 @@ static int validate_and_copy_clone(struct net *net, err = ovs_nla_add_action(sfa, OVS_CLONE_ATTR_EXEC, &exec, sizeof(exec), log); if (err) - return err; + goto err; + actions_start = (*sfa)->actions_len; err = __ovs_nla_copy_actions(net, attr, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, actions_start, false); +err: + (*sfa)->actions_len = start; + return err; } void ovs_match_init(struct sw_flow_match *match, @@ -2763,20 +2839,20 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, tun_dst = metadata_dst_alloc(key.tun_opts_len, METADATA_IP_TUNNEL, GFP_KERNEL); - if (!tun_dst) - return -ENOMEM; + if (!tun_dst) { + err = -ENOMEM; + goto err; + } err = dst_cache_init(&tun_dst->u.tun_info.dst_cache, GFP_KERNEL); - if (err) { - dst_release((struct dst_entry *)tun_dst); - return err; - } + if (err) + goto err_free_tun_dst; a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL, sizeof(*ovs_tun), log); if (IS_ERR(a)) { - dst_release((struct dst_entry *)tun_dst); - return PTR_ERR(a); + err = PTR_ERR(a); + goto err_free_tun_dst; } ovs_tun = nla_data(a); @@ -2797,8 +2873,16 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, ip_tunnel_info_opts_set(tun_info, TUN_METADATA_OPTS(&key, key.tun_opts_len), key.tun_opts_len, dst_opt_type); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (WARN_ON_ONCE(err)) + goto err_free_tun_dst; + + return 0; +err_free_tun_dst: + dst_release((struct dst_entry *)tun_dst); +err: + (*sfa)->actions_len = start; return err; } @@ -2971,7 +3055,7 @@ static int validate_set(const struct nlattr *a, /* Convert non-masked non-tunnel set actions to masked set actions. */ if (!masked && key_type != OVS_KEY_ATTR_TUNNEL) { - int start, len = key_len * 2; + int err, start, len = key_len * 2; struct nlattr *at; *skip_copy = true; @@ -2983,8 +3067,11 @@ static int validate_set(const struct nlattr *a, return start; at = __add_action(sfa, key_type, NULL, len, log); - if (IS_ERR(at)) - return PTR_ERR(at); + if (IS_ERR(at)) { + err = PTR_ERR(at); + (*sfa)->actions_len = start; + return err; + } memcpy(nla_data(at), nla_data(ovs_key), key_len); /* Key. */ memset(nla_data(at) + key_len, 0xff, key_len); /* Mask. */ @@ -2994,7 +3081,11 @@ static int validate_set(const struct nlattr *a, mask->ipv6_label &= htonl(0x000FFFFF); } - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (WARN_ON_ONCE(err)) { + (*sfa)->actions_len = start; + return err; + } } return 0; @@ -3040,7 +3131,8 @@ static int validate_and_copy_check_pkt_len(struct net *net, const struct nlattr *acts_if_greater, *acts_if_lesser_eq; struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]; struct check_pkt_len_arg arg; - int nested_acts_start; + int greater_acts_start = -1; + int lesser_acts_start = -1; int start, err; err = nla_parse_deprecated_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX, @@ -3075,37 +3167,58 @@ static int validate_and_copy_check_pkt_len(struct net *net, err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg, sizeof(arg), log); if (err) - return err; + goto err_free; - nested_acts_start = add_nested_action_start(sfa, - OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log); - if (nested_acts_start < 0) - return nested_acts_start; + lesser_acts_start = + add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, + log); + if (lesser_acts_start < 0) { + err = lesser_acts_start; + goto err_free; + } err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; - add_nested_action_end(*sfa, nested_acts_start); + err = add_nested_action_end(*sfa, lesser_acts_start); + if (err) + goto err_free; - nested_acts_start = add_nested_action_start(sfa, - OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log); - if (nested_acts_start < 0) - return nested_acts_start; + greater_acts_start = + add_nested_action_start(sfa, + OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, + log); + if (greater_acts_start < 0) { + err = greater_acts_start; + goto err_free; + } err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa, eth_type, vlan_tci, mpls_label_count, log, depth + 1); if (err) - return err; + goto err_free; + + err = add_nested_action_end(*sfa, greater_acts_start); + if (err) + goto err_free; - add_nested_action_end(*sfa, nested_acts_start); - add_nested_action_end(*sfa, start); + err = add_nested_action_end(*sfa, start); + if (err) + goto err_free; return 0; + +err_free: + ovs_nla_trim(*sfa, greater_acts_start, true); + ovs_nla_trim(*sfa, lesser_acts_start, true); + ovs_nla_trim(*sfa, start, false); + return err; } static int validate_psample(const struct nlattr *attr) From 6744ab60dfac55d1df5733960aad5be300984301 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Fri, 10 Jul 2026 23:10:03 +0545 Subject: [PATCH 2100/3297] Bluetooth: btrtl: validate firmware patch bounds commit 609c5b04a28dc1b0f3af6a7bc93055135b2d2059 upstream. rtlbt_parse_firmware() copies patch_length - 4 bytes before appending the firmware version. A malformed firmware patch shorter than the version field can make this subtraction underflow and turn the copy into an oversized read and write during Bluetooth setup. The existing patch_offset + patch_length check can also wrap on 32-bit architectures. Validate the patch length and range without arithmetic overflow before allocating or copying the patch. Fixes: db33c77dddc2 ("Bluetooth: btrtl: Create separate module for Realtek BT driver") Cc: stable@vger.kernel.org Signed-off-by: Laxman Acharya Padhya Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btrtl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 52794db2739bf..a62eae7276d64 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -786,8 +786,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, } BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i); - min_size = patch_offset + patch_length; - if (btrtl_dev->fw_len < min_size) + if (patch_length < sizeof(epatch_info->fw_version) || + patch_offset > btrtl_dev->fw_len || + patch_length > btrtl_dev->fw_len - patch_offset) return -EINVAL; /* Copy the firmware into a new buffer and write the version at From fd701fc0d065260ad2e5c2ff1c830afb73012590 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Sun, 12 Jul 2026 21:03:43 +0800 Subject: [PATCH 2101/3297] llc: fix SAP refcount leak when creating incoming sockets commit 2c72eb6286347d05a885412fb076993bd5286b53 upstream. llc_sap_add_socket() takes a SAP reference for each socket added to a SAP, and llc_sap_remove_socket() releases it. llc_create_incoming_sock() takes an additional SAP reference after adding the child socket. This extra reference was balanced by an explicit llc_sap_put() in llc_ui_release() until commit 3100aa9d74db ("llc: fix SAP reference counting w.r.t. socket handling") removed that put. The corresponding hold in the accept path was left behind. When such a child socket is removed, only the reference taken by llc_sap_add_socket() is released. The extra reference keeps the SAP alive after its last socket is removed. Remove the obsolete hold. Fixes: 3100aa9d74db ("llc: fix SAP reference counting w.r.t. socket handling") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Link: https://patch.msgid.link/20260712130343.518797-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/llc/llc_conn.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 5c0ac243b248f..b4d732baafb89 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -773,7 +773,6 @@ static struct sock *llc_create_incoming_sock(struct sock *sk, newllc->dev = dev; dev_hold(dev); llc_sap_add_socket(llc->sap, newsk); - llc_sap_hold(llc->sap); out: return newsk; } From a75d2b5249e385e9b2cb23ffca945bb0e5e33ebf Mon Sep 17 00:00:00 2001 From: James Raphael Tiovalen Date: Sun, 5 Jul 2026 19:36:29 +0800 Subject: [PATCH 2102/3297] macsec: fix promiscuity refcount leak in macsec_dev_open() commit 7410d11460eb90d6c9281162ccc6a128534d897d upstream. When a MACsec interface with IFF_PROMISC set is brought up on top of a device that has hardware offload enabled, macsec_dev_open() first calls dev_set_promiscuity(real_dev, 1) and then propagates the open to the offload device. If that propagation fails, the error path jumps to the clear_allmulti label, which only reverts allmulti and the unicast address. The promiscuity taken on the lower device is never dropped, so real_dev is left permanently stuck in promiscuous mode. Its promiscuity count can no longer be balanced from software. Add a clear_promisc label that drops the promiscuity reference and route the two offload failure paths to it. The dev_set_promiscuity() failure itself still jumps to clear_allmulti, since on that failure the count was not incremented. Fixes: 3cf3227a21d1 ("net: macsec: hardware offloading infrastructure") Cc: stable@vger.kernel.org Signed-off-by: James Raphael Tiovalen Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260705113629.187490-1-jamestiotio@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/macsec.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 5b19afde065ff..e20538eda2001 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3609,19 +3609,22 @@ static int macsec_dev_open(struct net_device *dev) ops = macsec_get_ops(netdev_priv(dev), &ctx); if (!ops) { err = -EOPNOTSUPP; - goto clear_allmulti; + goto clear_promisc; } ctx.secy = &macsec->secy; err = macsec_offload(ops->mdo_dev_open, &ctx); if (err) - goto clear_allmulti; + goto clear_promisc; } if (netif_carrier_ok(real_dev)) netif_carrier_on(dev); return 0; +clear_promisc: + if (dev->flags & IFF_PROMISC) + dev_set_promiscuity(real_dev, -1); clear_allmulti: if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(real_dev, -1); From 39151f0708c84221e94cdd6aa070aba5d7cb1c01 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 2 Jul 2026 16:27:45 +0800 Subject: [PATCH 2103/3297] memstick: ms_block: reject a card that reports too many blocks commit 718178f524b98bc920d74bc771aed823c8b81425 upstream. msb_ftl_initialize() computes the zone count from the card block count with no bound: msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; ... for (i = 0; i < msb->zone_count; i++) msb->free_block_count[i] = MS_BLOCKS_IN_ZONE; msb->block_count is a card value. msb_read_boot_blocks() reads number_of_blocks from the card boot page and byte swaps it. free_block_count is a fixed int[MS_MAX_ZONES]. MS_MAX_ZONES is 16, so the valid indices are 0 to 15. The init loop above indexes it by zone_count. msb_mark_block_used() and msb_mark_block_unused() index it by pba / MS_BLOCKS_IN_ZONE, for pba up to block_count - 1. A card may report up to 65535 blocks. A block_count above 8192 (MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) lets the pba index reach 16. That writes past free_block_count[] and corrupts struct msb_data. A larger count runs the init loop past the end too. A real Memory Stick has at most 16 zones. So it has at most 8192 blocks. msb_ftl_initialize() now rejects a card that reports more than MS_MAX_ZONES * MS_BLOCKS_IN_ZONE blocks. Fixes: 0ab30494bc4f ("memstick: add support for legacy memorysticks") Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/memstick/core/ms_block.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index 1af157ce0a637..e3657efd045e4 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -1339,6 +1339,10 @@ static int msb_ftl_initialize(struct msb_data *msb) return 0; msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; + if (msb->block_count > MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) { + pr_err("Too many blocks: %d\n", msb->block_count); + return -EINVAL; + } msb->logical_block_count = msb->zone_count * 496 - 2; msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL); From d4ec18f48ce78a3bf7c999ac908691007375fe99 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 8 Jul 2026 21:03:15 +0300 Subject: [PATCH 2104/3297] ipvs: fix more places with wrong ipv6 transport offsets commit b3fe4cbd583895987935a9bdad01c8f9d3a02310 upstream. Sashiko reports for more incorrect IPv6 transport offsets. The app code for TCP was assuming IPv4 network header even after the ipvsh argument was provided. This can cause problems with apps over IPv6. As for the only official app in the kernel tree (FTP) this problem is harmless because we use Netfilter to mangle the FTP ports and we do not adjust the TCP seq numbers. Also, provide correct offset of the ICMPV6 header in ip_vs_out_icmp_v6() for correct checksum checks when the IPv6 packet has extension headers. Fixes: d12e12299a69 ("ipvs: add ipv6 support to ftp") Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") Cc: stable@vger.kernel.org Link: https://sashiko.dev/#/patchset/20260706101624.69471-1-zhaoyz24%40mails.tsinghua.edu.cn Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_app.c | 10 ++++------ net/netfilter/ipvs/ip_vs_core.c | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index fdacbc3c15bef..f9132b359f0c6 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -362,14 +362,13 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, struct ip_vs_iphdr *ipvsh) { int diff; - const unsigned int tcp_offset = ip_hdrlen(skb); struct tcphdr *th; __u32 seq; - if (skb_ensure_writable(skb, tcp_offset + sizeof(*th))) + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset); + th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); /* * Remember seq number in case this pkt gets resized @@ -439,14 +438,13 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, struct ip_vs_iphdr *ipvsh) { int diff; - const unsigned int tcp_offset = ip_hdrlen(skb); struct tcphdr *th; __u32 seq; - if (skb_ensure_writable(skb, tcp_offset + sizeof(*th))) + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset); + th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); /* * Remember seq number in case this pkt gets resized diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 594c7b24007d6..90619453cb6f0 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1041,8 +1041,7 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, snet.in6 = ciph.saddr.in6; offset = ciph.len; return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, - pp, offset, sizeof(struct ipv6hdr), - hooknum); + pp, offset, ipvsh->len, hooknum); } #endif From 3fb7edd2018bb1ad0a68157383d9b9dac33dd645 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 8 Jul 2026 16:21:30 +0200 Subject: [PATCH 2105/3297] ipvs: reload ip header after head reallocation commit a2f57827bf7c695b8c72dc4511cae8e86582369d upstream. __ip_vs_get_out_rt() calls skb_ensure_writable() which may reallocate skb->head. Fixes: 8d8e20e2d7bb ("ipvs: Decrement ttl") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Acked-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_xmit.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index fc254d1f59de9..f564b4197cee9 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -733,13 +733,11 @@ int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) { - struct iphdr *iph = ip_hdr(skb); - - if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, iph->daddr, + if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, ip_hdr(skb)->daddr, IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0) goto tx_error; - ip_send_check(iph); + ip_send_check(ip_hdr(skb)); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; From 82753ac86cb3d641b8634a61335fd0f04a61cc1a Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 17 Jun 2026 11:16:27 +0800 Subject: [PATCH 2106/3297] reset: sunxi: fix memory region leak on ioremap failure commit 1a8c89f8c112c75e84ff9a140f969e372aed0c9a upstream. In sunxi_reset_init(), when ioremap() fails, the memory region obtained via request_mem_region() is not released, leading to a resource leak. Add an err_mem_region label to properly release the memory region before freeing the data structure. Fixes: 8f1ae77f4666 ("reset: Add Allwinner SoCs Reset Controller Driver") Cc: stable@vger.kernel.org Signed-off-by: Zhao Dongdong Reviewed-by: Philipp Zabel Acked-by: Jernej Skrabec Signed-off-by: Philipp Zabel Signed-off-by: Greg Kroah-Hartman --- drivers/reset/reset-sunxi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index e752594b6971c..8e01589ab7afb 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -44,7 +44,7 @@ static int sunxi_reset_init(struct device_node *np) data->membase = ioremap(res.start, size); if (!data->membase) { ret = -ENOMEM; - goto err_alloc; + goto err_mem_region; } spin_lock_init(&data->lock); @@ -57,6 +57,8 @@ static int sunxi_reset_init(struct device_node *np) return reset_controller_register(&data->rcdev); +err_mem_region: + release_mem_region(res.start, size); err_alloc: kfree(data); return ret; From 3c1e92f75e11a11492b8cb901fceeb9f16ae6415 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Mon, 1 Jun 2026 15:50:00 +0800 Subject: [PATCH 2107/3297] powerpc/spufs: fix out-of-bounds access in spufs_mem_mmap_access() commit 47b87f469a35b5ffc81c16eee6b13a9b6c8d55c6 upstream. spufs_mem_mmap_access() computes the local store offset as address - vma->vm_start, but bounds-checks it against vma->vm_end instead of the local store size. On 64-bit, offset is always well below vma->vm_end, so the clamp never fires and len stays unbounded against the LS_SIZE buffer returned by ctx->ops->get_ls(). Reject offsets at or beyond LS_SIZE and clamp len to the remaining space, mirroring the guard already used by spufs_mem_mmap_fault() and spufs_ps_fault(). Fixes: a352894d0705 ("spufs: use new vm_ops->access to allow local state access from gdb") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/SYBPR01MB7881EE775E8B51C09F5A29E7AF152@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/cell/spufs/file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index ce839783c0df6..8b7320b3a6e40 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -268,10 +268,12 @@ static int spufs_mem_mmap_access(struct vm_area_struct *vma, if (write && !(vma->vm_flags & VM_WRITE)) return -EACCES; + if (offset >= LS_SIZE) + return -EFAULT; if (spu_acquire(ctx)) return -EINTR; - if ((offset + len) > vma->vm_end) - len = vma->vm_end - offset; + if ((offset + len) > LS_SIZE) + len = LS_SIZE - offset; local_store = ctx->ops->get_ls(ctx); if (write) memcpy_toio(local_store + offset, buf, len); From 584657c5fc58d7a840623a2fa06331c9661dd0f1 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Wed, 1 Jul 2026 17:33:27 +0800 Subject: [PATCH 2108/3297] wifi: cfg80211: validate EHT MLE before MLD ID read commit 74e27cd1d98b546fdb276008a83708d062339661 upstream. cfg80211_gen_new_ie() copies ML probe response elements from the parent frame when the parent EHT multi-link element has an MLD ID matching the nontransmitted BSSID index. The code only checked that the extension element had more than one byte before calling ieee80211_mle_get_mld_id(). That helper assumes a BASIC MLE with enough common info and documents that callers must first use ieee80211_mle_type_ok(). Attack chain: malicious AP sends a short EHT MLE in an MBSSID beacon. cfg80211_inform_bss_frame_data() stores the copied IE buffer. cfg80211_parse_mbssid_data() builds the nontransmitted BSS IE. cfg80211_gen_new_ie() sees the EHT MLE in the parent frame. ieee80211_mle_get_mld_id() then reads past the IE boundary. Validate the MLE type and size before reading the MLD ID. This matches the contract required by the MLE helper and rejects the short element before any internal MLE fields are accessed. Cc: stable@vger.kernel.org Fixes: 61dcfa8c2a8f ("wifi: cfg80211: copy multi-link element from the multi-link probe request's frame body to the generated elements") Signed-off-by: Haofeng Li Link: https://patch.msgid.link/20260701093327.2680709-1-lihaofeng@kylinos.cn Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/scan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 199c63de01457..2f2104bb0d52f 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -326,8 +326,11 @@ cfg80211_gen_new_ie(const u8 *ie, size_t ielen, /* For ML probe response, match the MLE in the frame body with * MLD id being 'bssid_index' */ - if (parent->id == WLAN_EID_EXTENSION && parent->datalen > 1 && + if (parent->id == WLAN_EID_EXTENSION && parent->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK && + ieee80211_mle_type_ok(parent->data + 1, + IEEE80211_ML_CONTROL_TYPE_BASIC, + parent->datalen - 1) && bssid_index == ieee80211_mle_get_mld_id(parent->data + 1)) { if (!cfg80211_copy_elem_with_frags(parent, ie, ielen, From 90576bd6921a91eb038bffbb4b9467c2dc26aa1d Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 01:35:07 +0800 Subject: [PATCH 2109/3297] wifi: ieee80211: validate MLE common info length commit 293baeae9b2434a3e432629d7720b5603db2d77e upstream. ieee80211_mle_common_size() uses the first common-info octet as the common information length for all known MLE types. However, ieee80211_mle_size_ok() only validates that octet for Basic, Probe Request, and TDLS MLEs. Reconfiguration MLEs also skipped the length octet when calculating the minimum common size, and Priority Access MLEs skipped validation of the advertised common information length. Account for the Reconfiguration common-info length octet and validate the advertised common information length for all known MLE types. Keep unknown-type handling unchanged. Fixes: 0f48b8b88aa9 ("wifi: ieee80211: add definitions for multi-link element") Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260611173506.36838-2-enderaoelyther@gmail.com [remove now misleading comment] Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- include/linux/ieee80211-eht.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index cd9abf5193966..d386a6c189c1b 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -833,7 +833,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) const struct ieee80211_multi_link_elem *mle = (const void *)data; u8 fixed = sizeof(*mle); u8 common = 0; - bool check_common_len = false; + u8 common_len; u16 control; if (!data || len < fixed) @@ -844,7 +844,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) switch (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: common += sizeof(struct ieee80211_mle_basic_common_info); - check_common_len = true; if (control & IEEE80211_MLC_BASIC_PRES_LINK_ID) common += 1; if (control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) @@ -864,9 +863,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) common += sizeof(struct ieee80211_mle_preq_common_info); if (control & IEEE80211_MLC_PREQ_PRES_MLD_ID) common += 1; - check_common_len = true; break; case IEEE80211_ML_CONTROL_TYPE_RECONF: + common += 1; if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR) common += ETH_ALEN; if (control & IEEE80211_MLC_RECONF_PRES_EML_CAPA) @@ -878,7 +877,6 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) break; case IEEE80211_ML_CONTROL_TYPE_TDLS: common += sizeof(struct ieee80211_mle_tdls_common_info); - check_common_len = true; break; case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: common = ETH_ALEN + 1; @@ -891,11 +889,9 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) if (len < fixed + common) return false; - if (!check_common_len) - return true; + common_len = mle->variable[0]; - /* if present, common length is the first octet there */ - return mle->variable[0] >= common; + return common_len >= common && common_len <= len - fixed; } /** From b8df3a993f690cd639a158ad6ad6cfc5da709f49 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Sat, 27 Jun 2026 00:58:30 +0800 Subject: [PATCH 2110/3297] wifi: mac80211: free ack status frame on TX header build failure commit 2c51457d930f723e5f2903af90f5847f7df53f42 upstream. ieee80211_build_hdr() stores an ACK status frame before it has finished all validation and header construction. If a later error path is taken, the transmit skb is freed but the stored ACK status frame remains in local->ack_status_frames. This can happen for control port frames when the requested MLO link ID does not match the link selected for a non-MLO station. Repeated failures can fill the ACK status IDR and leave pending ACK frames until hardware teardown. Remove any stored ACK status frame before returning an error after it has been inserted into the IDR. Fixes: a729cff8ad51 ("mac80211: implement wifi TX status") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Link: https://patch.msgid.link/9de0423da840e92084915b8f92e66a421245c4b8.1782462409.git.roxy520tt@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/tx.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 98f0a275b60c3..c125871adb62b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2578,6 +2578,18 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, return info_id; } +static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id) +{ + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_remove(&local->ack_status_frames, info_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + kfree_skb(ack_skb); +} + /** * ieee80211_build_hdr - build 802.11 header in the given frame * @sdata: virtual interface to build the header for @@ -2934,7 +2946,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) { ieee80211_free_txskb(&local->hw, skb); skb = NULL; - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto free; } } @@ -3002,6 +3015,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, return skb; free: + if (info_id) + ieee80211_remove_ack_skb(local, info_id); kfree_skb(skb); return ERR_PTR(ret); } From a59cfa165aee3e29d06145041c0ebe46a51de604 Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Fri, 12 Jun 2026 09:25:46 -0300 Subject: [PATCH 2111/3297] wifi: mwifiex: fix permanently busy scans after multiple roam iterations commit d78a407bad6f500884a8606aea1a5a9207be4030 upstream. In order for the firmware to sleep, the driver has to confirm a previously received sleep request. The normal sequence of evets goes like this: EVENT_SLEEP -> adapter->ps_state = PS_STATE_PRE_SLEEP -> sleep-confirm -> SLEEP -> EVENT_AWAKE -> AWAKE. Before sending the sleep-confirm command, the driver must make sure there are no commands either running or waiting to be completed. mwifiex_ret_802_11_associate() unconditionally sets ps_state = PS_STATE_AWAKE when it processes the association command response, outside of the normal powersave management flow. If EVENT_SLEEP arrives while the association command is in flight, ps_state is PRE_SLEEP when the association command response is parsed, and the forced AWAKE overwrites it. The deferred sleep-confirm is never sent. A subsequent scan_start command is correctly acknowledged, but the firmware doesn't generate scan_result events. The scan request never finishes, and additional requests from userspace fail with -EBUSY. After testing on both IW412 and W8997, I could only trigger the bug on the IW412 and observed the firmwares behave differently. On the IW412 the firmware still sends EVENT_SLEEP while the authentication / association process is ongoing. A W8997 under the same conditions seems to suppress power-save for the duration of the association, so PRE_SLEEP never coincided with the association response even after extended periods of testing using the loops described below (>12hours). On the IW412, the delay between commands that triggers an EVENT_SLEEP was empirically determined to be ~20ms. This delay can naturally occur when the driver is outputting debugging information (debug_mask = 0x00000037), in which situation the busy scans issue is repeatable while running "test 1)" as described below. If the delay between commands is less than ~20ms, the firmware stays awake and the issue was not reproducible running the same test. The host_mlme=false path also behaves differently. In this case, the entire authentication / association transaction is executed by one command (HostCmd_CMD_802_11_ASSOCIATE), and the firmware doesn't emit EVENT_SLEEP while the command is running. Remove the assignment so the ps_state is only manipulated in the paths that are related to powersave event handling and on the main workqueue for correct sleep confirmation. The following loop tests were performed (with debugging output enabled): 1) force roaming between two AP's, one 5GHz and one 2.4GHz, same SSID. Use wpa_cli to trigger the roaming behavior, sleep 2s between iterations. 2) force a disconnection to AP 1 and a connection to AP 2, test scan. Use wpa_cli to trigger the connection changes, sleep 2s between iterations. Each test ran in each device for at least 3 hours. Fixes: 5e6e3a92b9a4 ("wireless: mwifiex: initial commit for Marvell mwifiex driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Rafael Beims Reviewed-by: Jeff Chen Link: https://patch.msgid.link/20260612122547.1586872-2-rafael@beims.me Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/mwifiex/join.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index 5a1a0287c1d58..b48f7febaf03f 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -736,7 +736,6 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, /* Send a Media Connected event, according to the Spec */ priv->media_connected = true; - priv->adapter->ps_state = PS_STATE_AWAKE; priv->adapter->pps_uapsd_mode = false; priv->adapter->tx_lock_flag = false; From ac2d9f6b4f90519ba7ead7baba04e8074bcb3975 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:43:50 +0800 Subject: [PATCH 2112/3297] mtd: onenand: samsung: report DMA completion timeouts commit d03a19bd6c7f86b99ca8fb61a6ec2345cee1d9d6 upstream. The S5PC110 OneNAND DMA helpers have bounded waits for transfer completion. The polling helper falls out of its timeout loop and returns success, and the IRQ helper ignores wait_for_completion_timeout(). Return -ETIMEDOUT when the DMA transfer-done bit or completion does not arrive before the timeout so callers can treat the buffer transfer as failed. Fixes: e23abf4b7743 ("mtd: OneNAND: S5PC110: Implement DMA interrupt method") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/onenand/onenand_samsung.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c index 6d6aa709a21f8..b7b7758ce4d86 100644 --- a/drivers/mtd/nand/onenand/onenand_samsung.c +++ b/drivers/mtd/nand/onenand/onenand_samsung.c @@ -554,6 +554,9 @@ static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int di } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) && time_before(jiffies, timeout)); + if (!(status & S5PC110_DMA_TRANS_STATUS_TD)) + return -ETIMEDOUT; + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); return 0; @@ -608,7 +611,9 @@ static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int dir writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); - wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); + if (!wait_for_completion_timeout(&onenand->complete, + msecs_to_jiffies(20))) + return -ETIMEDOUT; return 0; } From 04ebd3766861f219325852c9598e0f0281cb3636 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:40:52 +0800 Subject: [PATCH 2113/3297] mtd: mchp23k256: use SPI match data for chip caps commit d322e40f4edf92bf0ca329e5aa4ae1c0316feb38 upstream. The driver stores chip capacity information in both the OF match table and the SPI id table. Probe currently uses of_device_get_match_data(), so a non-OF SPI modalias match falls back to mchp23k256_caps even when the SPI id table selected a different part. Use spi_get_device_match_data() so SPI id-table driver_data is consumed when OF match data is absent. This keeps the existing default fallback while avoiding the wrong MTD geometry for id-table-only matches. Fixes: 4379075a870b ("mtd: mchp23k256: Add support for mchp23lcv1024") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/devices/mchp23k256.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c index cef5f9677d39e..66e058df4c328 100644 --- a/drivers/mtd/devices/mchp23k256.c +++ b/drivers/mtd/devices/mchp23k256.c @@ -188,7 +188,7 @@ static int mchp23k256_probe(struct spi_device *spi) data = dev_get_platdata(&spi->dev); - flash->caps = of_device_get_match_data(&spi->dev); + flash->caps = spi_get_device_match_data(spi); if (!flash->caps) flash->caps = &mchp23k256_caps; From bf9848a22a8e50d39d5e8d871581f0a8110f16b3 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:23:19 +0800 Subject: [PATCH 2114/3297] mmc: vub300: defer reset until cmd_mutex is unlocked commit ee5fb641c4ccac8406c668d3e947eb20ce44f233 upstream. vub300_cmndwork_thread() holds cmd_mutex while it sends a command and waits for the command response. If the response wait times out, __vub300_command_response() kills the command URBs and then synchronously resets the USB device through usb_reset_device(). That reset path re-enters the driver through vub300_pre_reset(), which also takes cmd_mutex. The worker therefore tries to acquire the same mutex recursively while it is still holding it from the command path. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the real worker and timeout/reset carrier: vub300_cmndwork_thread() __vub300_command_response() usb_lock_device_for_reset() usb_reset_device() vub300_pre_reset() Lockdep reported the same-task recursive acquisition on cmd_mutex: WARNING: possible recursive locking detected ... (&test_vub300.cmd_mutex) ... at: usb_reset_device... [vuln_msv] ... (&test_vub300.cmd_mutex) ... at: vub300_cmndwork_thread+0x12/0x20 [vuln_msv] Workqueue: vub300_cmd_wq vub300_cmndwork_thread [vuln_msv] *** DEADLOCK *** Return a flag from __vub300_command_response() when the timeout path needs a device reset, then perform the reset after vub300_cmndwork_thread() has cleared the in-flight command state and dropped cmd_mutex. The reset is still attempted before mmc_request_done(), preserving the existing request completion ordering while avoiding the recursive lock. Fixes: 88095e7b473a ("mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/vub300.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 3c9df27f9fa74..93c4e145009e0 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -1586,7 +1586,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300, return linear_length; } -static void __vub300_command_response(struct vub300_mmc_host *vub300, +static bool __vub300_command_response(struct vub300_mmc_host *vub300, struct mmc_command *cmd, struct mmc_data *data, int data_length) { @@ -1598,17 +1598,11 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300, msecs_to_jiffies(msec_timeout)); if (respretval == 0) { /* TIMED OUT */ /* we don't know which of "out" and "res" if any failed */ - int result; vub300->usb_timed_out = 1; usb_kill_urb(vub300->command_out_urb); usb_kill_urb(vub300->command_res_urb); cmd->error = -ETIMEDOUT; - result = usb_lock_device_for_reset(vub300->udev, - vub300->interface); - if (result == 0) { - result = usb_reset_device(vub300->udev); - usb_unlock_device(vub300->udev); - } + return true; } else if (respretval < 0) { /* we don't know which of "out" and "res" if any failed */ usb_kill_urb(vub300->command_out_urb); @@ -1704,6 +1698,8 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300, } else { cmd->error = -EINVAL; } + + return false; } static void construct_request_response(struct vub300_mmc_host *vub300, @@ -1749,6 +1745,7 @@ static void vub300_cmndwork_thread(struct work_struct *work) struct mmc_request *req = vub300->req; struct mmc_command *cmd = vub300->cmd; struct mmc_data *data = vub300->data; + bool reset_device; int data_length; mutex_lock(&vub300->cmd_mutex); init_completion(&vub300->command_complete); @@ -1771,7 +1768,8 @@ static void vub300_cmndwork_thread(struct work_struct *work) data_length = __command_read_data(vub300, cmd, data); else data_length = __command_write_data(vub300, cmd, data); - __vub300_command_response(vub300, cmd, data, data_length); + reset_device = __vub300_command_response(vub300, cmd, + data, data_length); vub300->req = NULL; vub300->cmd = NULL; vub300->data = NULL; @@ -1779,6 +1777,16 @@ static void vub300_cmndwork_thread(struct work_struct *work) if (cmd->error == -ENOMEDIUM) check_vub300_port_status(vub300); mutex_unlock(&vub300->cmd_mutex); + if (reset_device) { + int result; + + result = usb_lock_device_for_reset(vub300->udev, + vub300->interface); + if (result == 0) { + result = usb_reset_device(vub300->udev); + usb_unlock_device(vub300->udev); + } + } mmc_request_done(vub300->mmc, req); kref_put(&vub300->kref, vub300_delete); return; From de2bc884d887047732918050d03fd6f1d6b3f6fd Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:42:33 +0800 Subject: [PATCH 2115/3297] mtd: rawnand: fsl_ifc: return errors for failed page reads commit f9a13e05a327080c3a1c8165adf9e678fb68fef2 upstream. fsl_ifc_run_command() logs controller timeout and other non-OPC completion states in ctrl->nand_stat. fsl_ifc_read_page() then only increments the ECC failure counter for non-OPC status and still returns max_bitflips, which can be zero. Return -ETIMEDOUT when the command did not complete at all and -EIO for other non-OPC read completions so the NAND core does not treat a failed page read as a clean page. Fixes: 82771882d960 ("NAND Machine support for Integrated Flash Controller") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 7be95d0be2482..d8ddaa8a35fae 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -683,8 +683,15 @@ static int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf, return check_erased_page(chip, buf); } - if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + if (!ctrl->nand_stat) { mtd->ecc_stats.failed++; + return -ETIMEDOUT; + } + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) { + mtd->ecc_stats.failed++; + return -EIO; + } return nctrl->max_bitflips; } From 4b5de4007e5bf8edfb2947a3a76e8b328dc05969 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:37:59 +0800 Subject: [PATCH 2116/3297] mtd: rawnand: lpc32xx_mlc: fail DMA transfers on timeout commit dbf590b662695b16fbf5917ef129697be4410ea9 upstream. lpc32xx_xmit_dma() starts a DMA transfer and waits up to one second for its completion, but it ignores the wait result and returns success after unmapping the buffer. A timed out read can therefore return success with incomplete data, and a timed out write can continue the NAND operation without proof that the DMA payload reached the controller. Terminate the DMA channel on timeout, unmap the scatterlist through the existing cleanup path, and return -ETIMEDOUT to the NAND read/write callers. Initialize the shared cleanup-path result before using it for dmaengine_prep_slave_sg() failures. Fixes: 70f7cb78ec53 ("mtd: add LPC32xx MLC NAND driver") Cc: stable@vger.kernel.org Reviewed-by: Vladimir Zapolskiy Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 19b13ae536d48..8f6a89d9ba83f 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -396,6 +396,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, struct lpc32xx_nand_host *host = nand_get_controller_data(chip); struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + unsigned long time_left; int res; sg_init_one(&host->sgl, mem, len); @@ -410,6 +411,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, flags); if (!desc) { dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); + res = -ENXIO; goto out1; } @@ -420,7 +422,13 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, dmaengine_submit(desc); dma_async_issue_pending(host->dma_chan); - wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000)); + time_left = wait_for_completion_timeout(&host->comp_dma, + msecs_to_jiffies(1000)); + if (!time_left) { + dmaengine_terminate_sync(host->dma_chan); + res = -ETIMEDOUT; + goto out1; + } dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); @@ -428,7 +436,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len, out1: dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); - return -ENXIO; + return res; } static int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf, From cf7258f57d18026b8f77c0e80ff1805e9caf7250 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:39:43 +0800 Subject: [PATCH 2117/3297] mtd: rawnand: lpc32xx_slc: fail DMA transfer on completion timeout commit 17a8ce84964f243c8f89dc7353ac7e8d3137bc74 upstream. lpc32xx_xmit_dma() waits for the DMA completion callback but ignores wait_for_completion_timeout(). A timed out DMA transfer is therefore unmapped and reported as successful to the NAND read/write path. Return -ETIMEDOUT when the completion wait expires. Terminate the DMA channel before unmapping the scatterlist so the timed out transfer cannot continue to access the buffer after the error is returned. Fixes: 2944a44da09e ("mtd: add LPC32xx SLC NAND driver") Cc: stable@vger.kernel.org Reviewed-by: Vladimir Zapolskiy Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/raw/lpc32xx_slc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index fea3705a21386..db9d1d5289cf9 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -430,6 +430,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, struct dma_async_tx_descriptor *desc; int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; int res; + unsigned long time_left; host->dma_slave_config.direction = dir; host->dma_slave_config.src_addr = dma; @@ -467,12 +468,19 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, dmaengine_submit(desc); dma_async_issue_pending(host->dma_chan); - wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); + time_left = wait_for_completion_timeout(&host->comp, + msecs_to_jiffies(1000)); + if (!time_left) { + dmaengine_terminate_sync(host->dma_chan); + res = -ETIMEDOUT; + } else { + res = 0; + } dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); - return 0; + return res; out1: dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, DMA_BIDIRECTIONAL); From 8d94498cc4453282e6ef981ef863ba5f643b1fba Mon Sep 17 00:00:00 2001 From: Ao Sun Date: Mon, 6 Jul 2026 11:43:00 +0000 Subject: [PATCH 2118/3297] mmc: block: fix RPMB device unregister ordering commit 86152fef52cac15cd662ed3bfc7604fbfef378f0 upstream. Since commit 7852028a35f0 ("mmc: block: register RPMB partition with the RPMB subsystem"), each mmc RPMB partition is represented by two device objects: - the mmc-owned device (`rpmb->dev`, backing the legacy /dev/mmcblkXrpmb char device) and - the rpmb-core device (`rdev`, backing /dev/rpmbN). The child RPMB device holds a reference to its parent, so the parent's release callback cannot be invoked if the child device is still registered. Remove rpmb_dev_unregister() from the parent release handler and unregister the child RPMB device in the remove path before tearing down the parent device. Also delete the extra blank line between mmc_blk_remove_rpmb_part() and {. Fixes: 7852028a35f0 ("mmc: block: register RPMB partition with the RPMB subsystem") Cc: stable@vger.kernel.org Signed-off-by: Jiazi Li Signed-off-by: Ao Sun Reviewed-by: Avri Altman Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/block.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 6113bb927946d..e8e45b161a398 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2784,7 +2784,6 @@ static void mmc_blk_rpmb_device_release(struct device *dev) { struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); - rpmb_dev_unregister(rpmb->rdev); mmc_blk_put(rpmb->md); ida_free(&mmc_rpmb_ida, rpmb->id); kfree(rpmb); @@ -2999,8 +2998,8 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, } static void mmc_blk_remove_rpmb_part(struct mmc_rpmb_data *rpmb) - { + rpmb_dev_unregister(rpmb->rdev); cdev_device_del(&rpmb->chrdev, &rpmb->dev); put_device(&rpmb->dev); } From c02237966c199df2303fe6808ee1735bb299f503 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Mon, 1 Jun 2026 17:49:01 +0300 Subject: [PATCH 2119/3297] mmc: sdhci-of-dwcmshc: check bus clock enable result in the probe() method commit 521f39ca93cc43ce1b3eae8d44201f8f55dd9151 upstream. In the driver's probe() method, clk_disable_unprepare() for the bus clock is called on the error path even if the prior clk_prepare_enable() call has failed (and the same thing happens in the remove() method as well) -- that would cause the prepare/enable counter imbalance. Also, the same problem can happen in the driver's suspend() method; note that the resume() method does check the clk_prepare_enable()'s result -- let's be consistent and do that in probe() method as well. BTW, I don't know for sure what does the bus clock control -- if it affects the register accesses, the driver will likely cause (e.g. on ARM) a kernel oops if it fails to prepare/enable the bus clock in the probe() method... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: e438cf49b305 ("mmc: sdhci-of-dwcmshc: add SDHCI OF Synopsys DWC MSHC driver") Fixes: bccce2ec7790 ("mmc: sdhci-of-dwcmshc: add suspend/resume support") Signed-off-by: Sergey Shtylyov Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-of-dwcmshc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 5b7ffc359414a..d7ad11a85bff1 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -1421,13 +1421,16 @@ static int dwcmshc_probe(struct platform_device *pdev) return err; priv->bus_clk = devm_clk_get(dev, "bus"); - if (!IS_ERR(priv->bus_clk)) - clk_prepare_enable(priv->bus_clk); + if (!IS_ERR(priv->bus_clk)) { + err = clk_prepare_enable(priv->bus_clk); + if (err) + goto err_clk; + } } err = mmc_of_parse(host->mmc); if (err) - goto err_clk; + goto err_bus_clk; sdhci_get_of_property(pdev); @@ -1441,7 +1444,7 @@ static int dwcmshc_probe(struct platform_device *pdev) if (pltfm_data->init) { err = pltfm_data->init(&pdev->dev, host, priv); if (err) - goto err_clk; + goto err_bus_clk; } #ifdef CONFIG_ACPI @@ -1487,9 +1490,10 @@ static int dwcmshc_probe(struct platform_device *pdev) err_rpm: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); +err_bus_clk: + clk_disable_unprepare(priv->bus_clk); err_clk: clk_disable_unprepare(pltfm_host->clk); - clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); return err; } From eefcd3ca245c1056c43c8a5a3cc1ec475e1d08cd Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:11 +0800 Subject: [PATCH 2120/3297] mmc: sdhci-esdhc-imx: remove unnecessary mmc_card_wake_sdio_irq check for tuning save/restore commit 899160e2774d9952e9f2770b38f701ff1906c0b2 upstream. The tuning save/restore during system PM is conditioned on mmc_card_wake_sdio_irq(), but this check is unrelated to whether tuning values need to be preserved. The actual requirement is that the card keeps power during suspend and the controller is a uSDHC. SDIO devices using out-of-band GPIO wakeup maintain power during suspend but do not set the SDIO IRQ wake flag. In this case the tuning delay values are not saved/restored. Remove the unnecessary mmc_card_wake_sdio_irq() condition from both the suspend save and resume restore paths. Fixes: c63d25cdc59a ("mmc: sdhci-esdhc-imx: Save tuning value when card stays powered in suspend") Acked-by: Adrian Hunter Reviewed-by: Frank Li Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 27d450cf0f990..1f89c34246b4f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2041,8 +2041,7 @@ static int sdhci_esdhc_suspend(struct device *dev) * to save the tuning delay value just in case the usdhc * lost power during system PM. */ - if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && - esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_save(host); if (device_may_wakeup(dev)) { @@ -2107,8 +2106,7 @@ static int sdhci_esdhc_resume(struct device *dev) * restore the saved tuning delay value for the device which keep * power during system PM. */ - if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && - esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_restore(host); pm_runtime_put_autosuspend(dev); From 52990f6b575265d678717bad9178e59bcc5d2299 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:12 +0800 Subject: [PATCH 2121/3297] mmc: sdhci-esdhc-imx: restore DLL override for DDR modes on resume commit 2439becd91bad6883b135044f85f83a0538b96a6 upstream. sdhci_esdhc_imx_hwinit() unconditionally clears ESDHC_DLL_CTRL by writing zero. For SDIO devices that keep power during system suspend and operate in DDR mode, the card remains in DDR timing while the host DLL override configuration is lost. Extract the DLL override setup from esdhc_set_uhs_signaling() into a helper esdhc_set_dll_override(), and call it on the resume path when the card kept power and is using a DDR timing mode. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 39 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1f89c34246b4f..4a65c89a10b8f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1338,6 +1338,23 @@ static int esdhc_change_pinstate(struct sdhci_host *host, return pinctrl_select_state(imx_data->pinctrl, pinctrl); } +static void esdhc_set_dll_override(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + struct esdhc_platform_data *boarddata = &imx_data->boarddata; + u32 v; + + if (!boarddata->delay_line) + return; + + v = boarddata->delay_line << ESDHC_DLL_OVERRIDE_VAL_SHIFT | + (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); + if (is_imx53_esdhc(imx_data)) + v <<= 1; + writel(v, host->ioaddr + ESDHC_DLL_CTRL); +} + /* * For HS400 eMMC, there is a data_strobe line. This signal is generated * by the device and used for data output and CRC status response output @@ -1393,7 +1410,6 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) u32 m; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); - struct esdhc_platform_data *boarddata = &imx_data->boarddata; /* disable ddr mode and disable HS400 mode */ m = readl(host->ioaddr + ESDHC_MIX_CTRL); @@ -1414,15 +1430,7 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) m |= ESDHC_MIX_CTRL_DDREN; writel(m, host->ioaddr + ESDHC_MIX_CTRL); imx_data->is_ddr = 1; - if (boarddata->delay_line) { - u32 v; - v = boarddata->delay_line << - ESDHC_DLL_OVERRIDE_VAL_SHIFT | - (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); - if (is_imx53_esdhc(imx_data)) - v <<= 1; - writel(v, host->ioaddr + ESDHC_DLL_CTRL); - } + esdhc_set_dll_override(host); break; case MMC_TIMING_MMC_HS400: m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN; @@ -2106,9 +2114,18 @@ static int sdhci_esdhc_resume(struct device *dev) * restore the saved tuning delay value for the device which keep * power during system PM. */ - if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) + if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) { sdhc_esdhc_tuning_restore(host); + /* + * Restore DLL override for DDR modes. hwinit unconditionally + * clears ESDHC_DLL_CTRL, but the card is still in DDR mode. + */ + if (host->timing == MMC_TIMING_UHS_DDR50 || + host->timing == MMC_TIMING_MMC_DDR52) + esdhc_set_dll_override(host); + } + pm_runtime_put_autosuspend(dev); return ret; From aa276aa6cbfbc5622bf974cf9be1d579ce4a5fab Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:13 +0800 Subject: [PATCH 2122/3297] mmc: sdhci-esdhc-imx: fix esdhc_change_pinstate() to allow default state restore commit 5adc14cd4b905629d5b9163b3a416dcab24c7ce2 upstream. esdhc_change_pinstate() checks for pins_100mhz and pins_200mhz at the top of the function and returns -EINVAL if either is not defined. This prevents the default case from ever being reached, which means devices with a sleep pinctrl state but without high-speed pin states (100mhz/ 200mhz) can never restore their default pin configuration. Move the IS_ERR checks for pins_100mhz and pins_200mhz into their respective switch cases. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 4a65c89a10b8f..e685b203e1c27 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1315,19 +1315,21 @@ static int esdhc_change_pinstate(struct sdhci_host *host, dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs); - if (IS_ERR(imx_data->pinctrl) || - IS_ERR(imx_data->pins_100mhz) || - IS_ERR(imx_data->pins_200mhz)) + if (IS_ERR(imx_data->pinctrl)) return -EINVAL; switch (uhs) { case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_DDR50: + if (IS_ERR(imx_data->pins_100mhz)) + return -EINVAL; pinctrl = imx_data->pins_100mhz; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: case MMC_TIMING_MMC_HS400: + if (IS_ERR(imx_data->pins_200mhz)) + return -EINVAL; pinctrl = imx_data->pins_200mhz; break; default: From 4f96903e2fd228aa96013372402d650f6dbe5cb3 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:15 +0800 Subject: [PATCH 2123/3297] mmc: sdhci-esdhc-imx: disable irq during suspend to fix unhandled interrupt commit 9d87eaf985cef9581b6ed99b461b38e8cd666480 upstream. When using WIFI out-of-band wakeup, an "irq xxx: nobody cared" warning occurs. This happens because the usdhc interrupt is not disabled during system suspend when device_may_wakeup() returns false. The sequence of events leading to this issue: 1. System enters suspend without disabling usdhc interrupt (because device_may_wakeup() returns false for usdhc device) 2. WIFI out-of-band wakeup triggers system resume via GPIO interrupt 3. WIFI sends a Card interrupt before usdhc has fully resumed 4. usdhc is still in runtime suspend state and cannot handle the interrupt properly 5. The unhandled interrupt triggers "nobody cared" warning Fix this by unconditionally disabling the usdhc interrupt during suspend and re-enabling it during resume, regardless of the wakeup capability. This ensures no interrupts are processed during the suspend/resume transition. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index e685b203e1c27..08a7d70dd5620 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2054,9 +2054,10 @@ static int sdhci_esdhc_suspend(struct device *dev) if (mmc_card_keep_power(host->mmc) && esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_save(host); + /* The irqs of imx are not shared. It is safe to disable */ + disable_irq(host->irq); + if (device_may_wakeup(dev)) { - /* The irqs of imx are not shared. It is safe to disable */ - disable_irq(host->irq); ret = sdhci_enable_irq_wakeups(host); if (!ret) dev_warn(dev, "Failed to enable irq wakeup\n"); @@ -2107,10 +2108,10 @@ static int sdhci_esdhc_resume(struct device *dev) /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); - if (host->irq_wake_enabled) { + if (host->irq_wake_enabled) sdhci_disable_irq_wakeups(host); - enable_irq(host->irq); - } + + enable_irq(host->irq); /* * restore the saved tuning delay value for the device which keep From 5b8f11cbe8ad5726b2b63ff1574d5cffa4df18c2 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:16 +0800 Subject: [PATCH 2124/3297] mmc: sdhci-esdhc-imx: use pm_runtime_resume_and_get() in suspend commit 8da5930144712412d85e7f868693d96ec5c2018c upstream. Replace pm_runtime_get_sync() with pm_runtime_resume_and_get() to simplify error handling. pm_runtime_resume_and_get() automatically drops the usage counter on failure, avoiding the need for a separate pm_runtime_put_noidle() call. If it fails, the device is unclocked and accessing hardware registers would cause a kernel panic, so return the error immediately. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 08a7d70dd5620..41f362e81d5e7 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2038,7 +2038,9 @@ static int sdhci_esdhc_suspend(struct device *dev) * 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really * invoke its ->runtime_resume callback (needs_force_resume = 1). */ - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) && (host->tuning_mode != SDHCI_TUNING_MODE_1)) { From 174dc8103ab7b124ff976d06406f8c09c97f49dc Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:17 +0800 Subject: [PATCH 2125/3297] mmc: sdhci-esdhc-imx: make non-fatal errors non-blocking in suspend commit 6aa00a43bbd3d994558a55586351757cebbff236 upstream. Make pinctrl_pm_select_sleep_state() and mmc_gpio_set_cd_wake() failures non-fatal in the suspend path. These failures only mean slightly higher power consumption or missing CD wakeup capability, but should not block system suspend. Also change the function to always return 0 on the success path instead of propagating non-fatal warning return values. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 41f362e81d5e7..7c3691ae5d311 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2060,8 +2060,7 @@ static int sdhci_esdhc_suspend(struct device *dev) disable_irq(host->irq); if (device_may_wakeup(dev)) { - ret = sdhci_enable_irq_wakeups(host); - if (!ret) + if (!sdhci_enable_irq_wakeups(host)) dev_warn(dev, "Failed to enable irq wakeup\n"); } else { /* @@ -2072,12 +2071,12 @@ static int sdhci_esdhc_suspend(struct device *dev) * other function like GPIO function to save power in PM, * which finally block the SDIO wakeup function. */ - ret = pinctrl_pm_select_sleep_state(dev); - if (ret) - return ret; + if (pinctrl_pm_select_sleep_state(dev)) + dev_warn(dev, "Failed to select sleep pinctrl state\n"); } - ret = mmc_gpio_set_cd_wake(host->mmc, true); + if (mmc_gpio_set_cd_wake(host->mmc, true)) + dev_warn(dev, "Failed to enable cd wake\n"); /* * Make sure invoke runtime_suspend to gate off clock. @@ -2085,7 +2084,7 @@ static int sdhci_esdhc_suspend(struct device *dev) */ pm_runtime_force_suspend(dev); - return ret; + return 0; } static int sdhci_esdhc_resume(struct device *dev) From f84c0bae0e8dd08a88fa2495687acc9f5c638222 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:18 +0800 Subject: [PATCH 2126/3297] mmc: sdhci-esdhc-imx: fix resume error handling commit e27c946b589c53520409a0956b33d52ef7a0898f upstream. Check pm_runtime_force_resume() return value in resume. If it fails (clock enable failure), return immediately since accessing hardware registers on an unclocked device would cause a kernel panic. The early return intentionally skips enable_irq() and sdhci_disable_irq_wakeups() because the IRQ handler reads SDHCI_INT_STATUS, which would also fault without clocks. The PM runtime usage counter leak only affects this already-broken device instance and is an acceptable tradeoff to preserve system stability. Remove the return value check for mmc_gpio_set_cd_wake(host->mmc, false) since disable_irq_wake() called internally always returns 0. Also return 0 explicitly on the success path instead of propagating stale return values. Fixes: 676a83855614 ("mmc: host: sdhci-esdhc-imx: refactor the system PM logic") Acked-by: Adrian Hunter Reviewed-by: Frank Li Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 7c3691ae5d311..aa2a1eeedffa7 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -2100,12 +2100,12 @@ static int sdhci_esdhc_resume(struct device *dev) dev_warn(dev, "Failed to restore pinctrl state\n"); } - pm_runtime_force_resume(dev); - - ret = mmc_gpio_set_cd_wake(host->mmc, false); + ret = pm_runtime_force_resume(dev); if (ret) return ret; + mmc_gpio_set_cd_wake(host->mmc, false); + /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); @@ -2132,7 +2132,7 @@ static int sdhci_esdhc_resume(struct device *dev) pm_runtime_put_autosuspend(dev); - return ret; + return 0; } static int sdhci_esdhc_runtime_suspend(struct device *dev) From 83f29da85dc9d9a32fe62cd1055e8e6705e6bf80 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 31 May 2026 12:17:35 -0700 Subject: [PATCH 2127/3297] crypto: xilinx-trng - Remove crypto_rng interface commit 32b4d29280ed2a991dc196d5845b892acedc63b8 upstream. Implementing the crypto_rng interface has no purpose, as it isn't used in practice. It's being removed from other drivers too. Just remove it. This leaves hwrng, which is actually used. Tagging with 'Cc stable' due to the bugs that this removes: - xtrng_trng_generate() sometimes returned success even when it didn't fill in all the bytes. - It was possible for xtrng_trng_generate() and xtrng_hwrng_trng_read() to run concurrently and interfere with each other, as the locking code in xtrng_hwrng_trng_read() was broken. Fixes: 8979744aca80 ("crypto: xilinx - Add TRNG driver for Versal") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/Kconfig | 1 - drivers/crypto/xilinx/xilinx-trng.c | 75 +---------------------------- 2 files changed, 1 insertion(+), 75 deletions(-) diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index e121d6929cf19..8c4a2e4ca8fa7 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -728,7 +728,6 @@ config CRYPTO_DEV_TEGRA config CRYPTO_DEV_XILINX_TRNG tristate "Support for Xilinx True Random Generator" depends on ZYNQMP_FIRMWARE || COMPILE_TEST - select CRYPTO_RNG select HW_RANDOM help Xilinx Versal SoC driver provides kernel-side support for True Random Number diff --git a/drivers/crypto/xilinx/xilinx-trng.c b/drivers/crypto/xilinx/xilinx-trng.c index 4e4700d68127d..3b193471cc94c 100644 --- a/drivers/crypto/xilinx/xilinx-trng.c +++ b/drivers/crypto/xilinx/xilinx-trng.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -15,12 +14,10 @@ #include #include #include -#include #include #include #include #include -#include #include /* TRNG Registers Offsets */ @@ -59,16 +56,9 @@ struct xilinx_rng { void __iomem *rng_base; struct device *dev; - struct mutex lock; /* Protect access to TRNG device */ struct hwrng trng; }; -struct xilinx_rng_ctx { - struct xilinx_rng *rng; -}; - -static struct xilinx_rng *xilinx_rng_dev; - static void xtrng_readwrite32(void __iomem *addr, u32 mask, u8 value) { u32 val; @@ -235,47 +225,6 @@ static int xtrng_random_bytes_generate(struct xilinx_rng *rng, u8 *rand_buf_ptr, return nbytes; } -static int xtrng_trng_generate(struct crypto_rng *tfm, const u8 *src, u32 slen, - u8 *dst, u32 dlen) -{ - struct xilinx_rng_ctx *ctx = crypto_rng_ctx(tfm); - int ret; - - mutex_lock(&ctx->rng->lock); - ret = xtrng_random_bytes_generate(ctx->rng, dst, dlen, true); - mutex_unlock(&ctx->rng->lock); - - return ret < 0 ? ret : 0; -} - -static int xtrng_trng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen) -{ - return 0; -} - -static int xtrng_trng_init(struct crypto_tfm *rtfm) -{ - struct xilinx_rng_ctx *ctx = crypto_tfm_ctx(rtfm); - - ctx->rng = xilinx_rng_dev; - - return 0; -} - -static struct rng_alg xtrng_trng_alg = { - .generate = xtrng_trng_generate, - .seed = xtrng_trng_seed, - .seedsize = 0, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "xilinx-trng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct xilinx_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = xtrng_trng_init, - }, -}; - static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) { u8 buf[TRNG_SEC_STRENGTH_BYTES]; @@ -283,12 +232,6 @@ static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bo int ret = -EINVAL, i = 0; rng = container_of(hwrng, struct xilinx_rng, trng); - /* Return in case wait not set and lock not available. */ - if (!mutex_trylock(&rng->lock) && !wait) - return 0; - else if (!mutex_is_locked(&rng->lock) && wait) - mutex_lock(&rng->lock); - while (i < max) { ret = xtrng_random_bytes_generate(rng, buf, TRNG_SEC_STRENGTH_BYTES, wait); if (ret < 0) @@ -297,8 +240,6 @@ static int xtrng_hwrng_trng_read(struct hwrng *hwrng, void *data, size_t max, bo memcpy(data + i, buf, min_t(int, ret, (max - i))); i += min_t(int, ret, (max - i)); } - mutex_unlock(&rng->lock); - return ret; } @@ -344,26 +285,14 @@ static int xtrng_probe(struct platform_device *pdev) return ret; } - xilinx_rng_dev = rng; - mutex_init(&rng->lock); - ret = crypto_register_rng(&xtrng_trng_alg); - if (ret) { - dev_err(&pdev->dev, "Crypto Random device registration failed: %d\n", ret); - return ret; - } ret = xtrng_hwrng_register(&rng->trng); if (ret) { dev_err(&pdev->dev, "HWRNG device registration failed: %d\n", ret); - goto crypto_rng_free; + return ret; } platform_set_drvdata(pdev, rng); return 0; - -crypto_rng_free: - crypto_unregister_rng(&xtrng_trng_alg); - - return ret; } static void xtrng_remove(struct platform_device *pdev) @@ -373,13 +302,11 @@ static void xtrng_remove(struct platform_device *pdev) rng = platform_get_drvdata(pdev); xtrng_hwrng_unregister(&rng->trng); - crypto_unregister_rng(&xtrng_trng_alg); xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET, zero, TRNG_NUM_INIT_REGS); xtrng_write_multiple_registers(rng->rng_base + TRNG_PER_STRNG_OFFSET, zero, TRNG_NUM_INIT_REGS); xtrng_hold_reset(rng->rng_base); - xilinx_rng_dev = NULL; } static const struct of_device_id xtrng_of_match[] = { From 3b2628f7682aea8d9ce09ad4b9a3bd144b451eaa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jul 2026 09:32:35 -0400 Subject: [PATCH 2128/3297] ACPI: NFIT: core: Fix acpi_nfit_init() error cleanup [ Upstream commit 38bf27511ef41bffebd157ec3eba41fc89ba59cd ] If acpi_nfit_init() fails after adding the acpi_desc object to the acpi_descs list, that object is never removed from that list because the acpi_nfit_shutdown() devm action is not added for the NFIT device in that case. Next, the acpi_nfit_init() failure causes acpi_nfit_probe() to fail, the acpi_desc object is freed, and a dangling pointer is left behind in the acpi_descs. Any subsequent ACPI Machine Check Exception will trigger nfit_handle_mce() which iterates over acpi_descs and so a use-after-free will occur. Moreover, if acpi_nfit_probe() returns 0 after installing a notify handler for the NFIT device and without allocating the acpi_desc object and setting the NFIT device's driver data pointer, the acpi_desc object will be allocated by acpi_nfit_update_notify() and acpi_nfit_init() will be called to initialize it. Regardless of whether or not acpi_nfit_init() fails in that case, the acpi_nfit_shutdown() devm action is not added for the NFIT device and acpi_desc is never removed from the acpi_descs list. If the acpi_desc object is freed subsequently on driver removal, any subsequent ACPI MCE will lead to a use-after-free like in the previous case. To address the first issue mentioned above, make acpi_nfit_probe() call acpi_nfit_shutdown() directly on acpi_nfit_init() failures and to address the other one, add a remove callback to the driver and make it call acpi_nfit_shutdown(). Also, since it is now possible to pass NULL to acpi_nfit_shutdown() or the acpi_desc object passed to it may not have been initialized, add checks against NULL for acpi_desc and its nvdimm_bus field to that function and make acpi_nfit_unregister() clear the latter after unregistering the NVDIMM bus. Fixes: a61fe6f7902e ("nfit, tools/testing/nvdimm: unify common init for acpi_nfit_desc") Fixes: fbabd829fe76 ("acpi, nfit: fix module unload vs workqueue shutdown race") Signed-off-by: Rafael J. Wysocki Cc: All applicable Reviewed-by: Dave Jiang Link: https://patch.msgid.link/1963615.tdWV9SEqCh@rafael.j.wysocki Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/nfit/core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 2eb9989372ca5..a27d1e63725a6 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3061,6 +3061,8 @@ static void acpi_nfit_unregister(void *data) struct acpi_nfit_desc *acpi_desc = data; nvdimm_bus_unregister(acpi_desc->nvdimm_bus); + /* The nvdimm_bus object may have been freed, so clear the pointer. */ + acpi_desc->nvdimm_bus = NULL; } int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz) @@ -3301,7 +3303,10 @@ static void acpi_nfit_remove_notify_handler(void *data) void acpi_nfit_shutdown(void *data) { struct acpi_nfit_desc *acpi_desc = data; - struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus); + struct device *bus_dev; + + if (!acpi_desc || !acpi_desc->nvdimm_bus) + return; /* * Destruct under acpi_desc_lock so that nfit_handle_mce does not @@ -3316,6 +3321,7 @@ void acpi_nfit_shutdown(void *data) mutex_unlock(&acpi_desc->init_mutex); cancel_delayed_work_sync(&acpi_desc->dwork); + bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus); /* * Bounce the nvdimm bus lock to make sure any in-flight * acpi_nfit_ars_rescan() submissions have had a chance to @@ -3393,9 +3399,14 @@ static int acpi_nfit_add(struct acpi_device *adev) sz - sizeof(struct acpi_table_nfit)); if (rc) - return rc; + acpi_nfit_shutdown(acpi_desc); - return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc); + return rc; +} + +static void acpi_nfit_remove(struct acpi_device *adev) +{ + acpi_nfit_shutdown(dev_get_drvdata(&adev->dev)); } static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle) @@ -3482,6 +3493,7 @@ static struct acpi_driver acpi_nfit_driver = { .ids = acpi_nfit_ids, .ops = { .add = acpi_nfit_add, + .remove = acpi_nfit_remove, }, }; From 34f4d0e4e5065237d15651df759a99e81b7f9f51 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jul 2026 11:47:06 -0400 Subject: [PATCH 2129/3297] ACPI: driver: Check ACPI_COMPANION() against NULL during probe [ Upstream commit e4865a56d013e86e46ea6acea15bb6eae01898ff ] Since 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(), platform drivers that rely on the existence of a device's ACPI companion object should verify its presence. Accordingly, add requisite ACPI_COMPANION() or ACPI_HANDLE() checks against NULL to 13 platform drivers handling core ACPI devices. Also change the value returned by the ACPI thermal zone driver when the device's ACPI companion is not present to -ENODEV for consistency with the other drivers. Signed-off-by: Rafael J. Wysocki Reviewed-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/4516068.ejJDZkT8p0@rafael.j.wysocki Cc: 7.0+ # 7.0+ Stable-dep-of: 18a00ed0e718 ("ACPI: NFIT: core: Fix possible deadlock and missing notifications") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/ac.c | 6 +++++- drivers/acpi/acpi_pad.c | 6 +++++- drivers/acpi/acpi_tad.c | 6 +++++- drivers/acpi/pfr_telemetry.c | 6 +++++- drivers/acpi/pfr_update.c | 6 +++++- drivers/acpi/thermal.c | 2 +- 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 1f69be8f51a29..8738c42a3ae40 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -203,11 +203,15 @@ static const struct dmi_system_id ac_dmi_table[] __initconst = { static int acpi_ac_probe(struct platform_device *pdev) { - struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct power_supply_config psy_cfg = {}; + struct acpi_device *adev; struct acpi_ac *ac; int result; + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return -ENODEV; + ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL); if (!ac) return -ENOMEM; diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index c9a0bcaba2e4c..dea7b2b4e5445 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -426,9 +426,13 @@ static void acpi_pad_notify(acpi_handle handle, u32 event, static int acpi_pad_probe(struct platform_device *pdev) { - struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + struct acpi_device *adev; acpi_status status; + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return -ENODEV; + strscpy(acpi_device_name(adev), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); strscpy(acpi_device_class(adev), ACPI_PROCESSOR_AGGREGATOR_CLASS); diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 33418dd6768a1..fcebfa7d507f5 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -588,12 +588,16 @@ static void acpi_tad_remove(struct platform_device *pdev) static int acpi_tad_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - acpi_handle handle = ACPI_HANDLE(dev); struct acpi_tad_driver_data *dd; + acpi_handle handle; acpi_status status; unsigned long long caps; int ret; + handle = ACPI_HANDLE(dev); + if (!handle) + return -ENODEV; + ret = acpi_install_cmos_rtc_space_handler(handle); if (ret < 0) { dev_info(dev, "Unable to install space handler\n"); diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c index 32bdf8cbe8f23..2387376832a1b 100644 --- a/drivers/acpi/pfr_telemetry.c +++ b/drivers/acpi/pfr_telemetry.c @@ -360,10 +360,14 @@ static void pfrt_log_put_idx(void *data) static int acpi_pfrt_log_probe(struct platform_device *pdev) { - acpi_handle handle = ACPI_HANDLE(&pdev->dev); struct pfrt_log_device *pfrt_log_dev; + acpi_handle handle; int ret; + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + if (!acpi_has_method(handle, "_DSM")) { dev_dbg(&pdev->dev, "Missing _DSM\n"); return -ENODEV; diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c index 11b1c28280052..6283105bb0e8b 100644 --- a/drivers/acpi/pfr_update.c +++ b/drivers/acpi/pfr_update.c @@ -538,10 +538,14 @@ static void pfru_put_idx(void *data) static int acpi_pfru_probe(struct platform_device *pdev) { - acpi_handle handle = ACPI_HANDLE(&pdev->dev); struct pfru_device *pfru_dev; + acpi_handle handle; int ret; + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + if (!acpi_has_method(handle, "_DSM")) { dev_dbg(&pdev->dev, "Missing _DSM\n"); return -ENODEV; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 8537395b417b0..e5e970daca9e5 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -789,7 +789,7 @@ static int acpi_thermal_add(struct acpi_device *device) int i; if (!device) - return -EINVAL; + return -ENODEV; tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL); if (!tz) From d57d2aae87b23978ac71c058c51650bd4a3aef2b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jul 2026 11:47:07 -0400 Subject: [PATCH 2130/3297] ACPI: bus: Introduce devm_acpi_install_notify_handler() [ Upstream commit ca70ce555a1eb8edf5fb1d5575f1fe19c9ae17f4 ] Introduce devm_acpi_install_notify_handler() for installing an ACPI notify handler managed by devres that will be removed automatically on driver detach. It installs the notify handler on the device object in the ACPI namespace that corresponds to the owner device's ACPI companion, if present (an error is returned if the owner device doesn't have an ACPI companion). Currently, there is no way to manually remove the notify handler installed by it because none of its users brought on subsequently will need to do that. Signed-off-by: Rafael J. Wysocki [ rjw: Kerneldoc comment refinement ] Link: https://patch.msgid.link/2268031.irdbgypaU6@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki Stable-dep-of: 18a00ed0e718 ("ACPI: NFIT: core: Fix possible deadlock and missing notifications") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/bus.c | 66 +++++++++++++++++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 2 ++ 2 files changed, 68 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a984ccd4a2a0c..c2ea10e6a2664 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -622,6 +622,72 @@ void acpi_dev_remove_notify_handler(struct acpi_device *adev, } EXPORT_SYMBOL_GPL(acpi_dev_remove_notify_handler); +struct acpi_notify_handler_devres { + acpi_notify_handler handler; + u32 handler_type; +}; + +static void devm_acpi_notify_handler_release(struct device *dev, void *res) +{ + struct acpi_notify_handler_devres *dr = res; + + acpi_dev_remove_notify_handler(ACPI_COMPANION(dev), dr->handler_type, + dr->handler); +} + +/** + * devm_acpi_install_notify_handler - Install an ACPI notify handler for a + * managed device + * @dev: Device to install a notify handler for + * @handler_type: Type of the notify handler + * @handler: Handler function to install + * @context: Data passed back to the handler function + * + * This function performs the same function as acpi_dev_install_notify_handler() + * called for the ACPI companion of @dev with the same @handler_type, @handler, + * and @context arguments, but the ACPI notify handler installed by it will be + * automatically removed on driver detach. + * + * Callers should ensure that all resources used by @handler have been allocated + * prior to invoking this function, in which case those resources should be + * devres-managed so that they won't be released before the notify handler + * removal. Otherwise, special synchronization between @handler and the + * management of those resources is required. + * + * When the request fails, an error message is printed. Don't add extra error + * messages at the call sites. + * + * Return: 0 on success or a negative error number. + */ +int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type, + acpi_notify_handler handler, void *context) +{ + struct acpi_notify_handler_devres *dr; + struct acpi_device *adev; + int ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return dev_err_probe(dev, -ENODEV, "No ACPI companion in %s()\n", __func__); + + dr = devres_alloc(devm_acpi_notify_handler_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = acpi_dev_install_notify_handler(adev, handler_type, handler, context); + if (ret) { + devres_free(dr); + return dev_err_probe(dev, ret, "Failed to install an ACPI notify handler\n"); + } + + dr->handler = handler; + dr->handler_type = handler_type; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_acpi_install_notify_handler); + /* Handle events targeting \_SB device (at present only graceful shutdown) */ #define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81 diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index aad1a95e6863d..9a8a796a870d1 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -625,6 +625,8 @@ int acpi_dev_install_notify_handler(struct acpi_device *adev, void acpi_dev_remove_notify_handler(struct acpi_device *adev, u32 handler_type, acpi_notify_handler handler); +int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type, + acpi_notify_handler handler, void *context); extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32); extern int register_acpi_notifier(struct notifier_block *); extern int unregister_acpi_notifier(struct notifier_block *); From cf5f93228e7aba5d0a36f276d702060348ee73f3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jul 2026 11:47:08 -0400 Subject: [PATCH 2131/3297] ACPI: NFIT: core: Use devm_acpi_install_notify_handler() [ Upstream commit 198541ad53c0d0d891fedea4098f9953a0f566c0 ] Now that devm_acpi_install_notify_handler() is available, use it in acpi_nfit_probe() instead of a custom devm action removing an ACPI notify handler installed via acpi_dev_install_notify_handler(). Also drop the explicit ACPI_COMPANION() check against NULL that is not necessary any more becuase devm_acpi_install_notify_handler() carries out an equivalent check internally and use ACPI_HANDLE() to retrieve the platform device's ACPI handle. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3048737.e9J7NaK4W3@rafael.j.wysocki Stable-dep-of: 18a00ed0e718 ("ACPI: NFIT: core: Fix possible deadlock and missing notifications") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/nfit/core.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index a27d1e63725a6..0a43b6cfa12a7 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3285,19 +3285,11 @@ static void acpi_nfit_put_table(void *table) static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *adev = data; - - device_lock(&adev->dev); - __acpi_nfit_notify(&adev->dev, handle, event); - device_unlock(&adev->dev); -} - -static void acpi_nfit_remove_notify_handler(void *data) -{ - struct acpi_device *adev = data; + struct device *dev = data; - acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acpi_nfit_notify); + device_lock(dev); + __acpi_nfit_notify(dev, handle, event); + device_unlock(dev); } void acpi_nfit_shutdown(void *data) @@ -3344,13 +3336,8 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_size sz; int rc = 0; - rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acpi_nfit_notify, adev); - if (rc) - return rc; - - rc = devm_add_action_or_reset(dev, acpi_nfit_remove_notify_handler, - adev); + rc = devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY, + acpi_nfit_notify, dev); if (rc) return rc; @@ -3381,7 +3368,7 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_desc->acpi_header = *tbl; /* Evaluate _FIT and override with that if present */ - status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf); + status = acpi_evaluate_object(ACPI_HANDLE(dev), "_FIT", NULL, &buf); if (ACPI_SUCCESS(status) && buf.length > 0) { union acpi_object *obj = buf.pointer; From 7f680924c5c2b4ea44979005ba4faec0a72c04d7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jul 2026 11:47:10 -0400 Subject: [PATCH 2132/3297] ACPI: NFIT: core: Fix possible deadlock and missing notifications [ Upstream commit 18a00ed0e718473f5c3fcfa49df46c944575e60c ] After commit 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before getting NFIT table"), ACPI NFIT driver removal may deadlock if an ACPI notify on the NFIT device is triggered concurrently. A similar deadlock may occur if an ACPI notify on the NFIT device is triggered during a failing driver probe. The deadlock is possible because acpi_dev_remove_notify_handler() calls acpi_os_wait_events_complete() after removing the notify handler and the driver core invokes it under the NFIT platform device lock which is also acquired by acpi_nfit_notify(). Thus acpi_os_wait_events_complete() may be waiting for acpi_nfit_notify() to complete, but the latter may not be able to acquire the device lock which is being held by the driver core while the former is being executed. Moreover, after commit 03667e146f81 ("ACPI: NFIT: core: Convert the driver to a platform one"), there are no sysfs notifications regarding NVDIMM devices because __acpi_nvdimm_notify() always bails out after checking the driver data pointer of the device's parent. That parent is the ACPI companion of the platform device used for driver binding, so its driver data pointer is always NULL after the commit in question which was overlooked by it. A remedy for the deadlock is to use a special separate lock for ACPI notify synchronization with driver probe and removal instead of the device lock of the NFIT device, while a remedy for the second issue is to populate the driver data pointer of the NFIT device's ACPI companion when the driver is ready to operate, so do both these things. However, since the new lock is not held across the entire teardown and acpi_nfit_notify() should do nothing when teardown is in progress, make it check the driver data pointer of the NFIT device's ACPI companion, in analogy with the existing check in __acpi_nvdimm_notify(), and bail out if that pointer is NULL. Fixes: 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before getting NFIT table") Fixes: 03667e146f81 ("ACPI: NFIT: core: Convert the driver to a platform one") Signed-off-by: Rafael J. Wysocki Cc: All applicable # 9995e4404ea4: ACPI: NFIT: core: Eliminate redundant local variable Reviewed-by: Dave Jiang Link: https://patch.msgid.link/3420096.aeNJFYEL58@rafael.j.wysocki Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/bus.c | 6 +++- drivers/acpi/nfit/core.c | 62 +++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c2ea10e6a2664..222a6bc616448 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -623,6 +623,7 @@ void acpi_dev_remove_notify_handler(struct acpi_device *adev, EXPORT_SYMBOL_GPL(acpi_dev_remove_notify_handler); struct acpi_notify_handler_devres { + struct acpi_device *adev; acpi_notify_handler handler; u32 handler_type; }; @@ -631,7 +632,7 @@ static void devm_acpi_notify_handler_release(struct device *dev, void *res) { struct acpi_notify_handler_devres *dr = res; - acpi_dev_remove_notify_handler(ACPI_COMPANION(dev), dr->handler_type, + acpi_dev_remove_notify_handler(dr->adev, dr->handler_type, dr->handler); } @@ -667,6 +668,8 @@ int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type, int ret; adev = ACPI_COMPANION(dev); + if (!adev && dev->bus == &acpi_bus_type) + adev = to_acpi_device(dev); if (!adev) return dev_err_probe(dev, -ENODEV, "No ACPI companion in %s()\n", __func__); @@ -680,6 +683,7 @@ int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type, return dev_err_probe(dev, ret, "Failed to install an ACPI notify handler\n"); } + dr->adev = adev; dr->handler = handler; dr->handler_type = handler_type; devres_add(dev, dr); diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 0a43b6cfa12a7..633eb625f037f 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -55,6 +55,8 @@ MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods"); LIST_HEAD(acpi_descs); DEFINE_MUTEX(acpi_desc_lock); +DEFINE_MUTEX(acpi_notify_lock); + static struct workqueue_struct *nfit_wq; struct nfit_table_prev { @@ -1702,9 +1704,15 @@ static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data) struct acpi_device *adev = data; struct device *dev = &adev->dev; - device_lock(dev->parent); - __acpi_nvdimm_notify(dev, event); - device_unlock(dev->parent); + /* + * Locking is needed here for synchronization with driver probe and + * removal and the parent NFIT device's ACPI driver data pointer is + * NULL when teardown is in progress. + */ + guard(mutex)(&acpi_notify_lock); + + if (acpi_driver_data(to_acpi_device(dev->parent))) + __acpi_nvdimm_notify(dev, event); } static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method) @@ -3150,11 +3158,10 @@ EXPORT_SYMBOL_GPL(acpi_nfit_init); static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc) { struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); - struct device *dev = acpi_desc->dev; - /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */ - device_lock(dev); - device_unlock(dev); + /* Bounce the notify lock to flush acpi_nfit_add / acpi_nfit_notify */ + mutex_lock(&acpi_notify_lock); + mutex_unlock(&acpi_notify_lock); /* Bounce the init_mutex to complete initial registration */ mutex_lock(&acpi_desc->init_mutex); @@ -3286,10 +3293,17 @@ static void acpi_nfit_put_table(void *table) static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data) { struct device *dev = data; + struct acpi_device *adev = to_acpi_device(dev); - device_lock(dev); - __acpi_nfit_notify(dev, handle, event); - device_unlock(dev); + /* + * Locking is needed here for synchronization with driver probe and + * removal and the ACPI driver data pointer is NULL when teardown + * is in progress. + */ + guard(mutex)(&acpi_notify_lock); + + if (acpi_driver_data(adev)) + __acpi_nfit_notify(dev, handle, event); } void acpi_nfit_shutdown(void *data) @@ -3336,6 +3350,12 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_size sz; int rc = 0; + /* + * Prevent acpi_nfit_notify() from progressing until the probe is + * complete in case there is a concurrent event to process. + */ + guard(mutex)(&acpi_notify_lock); + rc = devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY, acpi_nfit_notify, dev); if (rc) @@ -3351,6 +3371,11 @@ static int acpi_nfit_add(struct acpi_device *adev) * data in the format of a series of NFIT Structures. */ dev_dbg(dev, "failed to find NFIT at startup\n"); + /* + * Let acpi_nfit_update_notify() run in case it will need to + * allocate the acpi_desc object. + */ + adev->driver_data = dev; return 0; } @@ -3368,7 +3393,7 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_desc->acpi_header = *tbl; /* Evaluate _FIT and override with that if present */ - status = acpi_evaluate_object(ACPI_HANDLE(dev), "_FIT", NULL, &buf); + status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf); if (ACPI_SUCCESS(status) && buf.length > 0) { union acpi_object *obj = buf.pointer; @@ -3385,14 +3410,25 @@ static int acpi_nfit_add(struct acpi_device *adev) + sizeof(struct acpi_table_nfit), sz - sizeof(struct acpi_table_nfit)); - if (rc) + if (rc) { acpi_nfit_shutdown(acpi_desc); + return rc; + } - return rc; + /* + * Let notify handlers operate (the actual value of the ACPI driver + * data pointer does not matter here so long as it is not NULL). + */ + adev->driver_data = dev; + return 0; } static void acpi_nfit_remove(struct acpi_device *adev) { + guard(mutex)(&acpi_notify_lock); + + /* Make notify handlers bail out early going forward. */ + adev->driver_data = NULL; acpi_nfit_shutdown(dev_get_drvdata(&adev->dev)); } From 2745574697ee49bd667d88ef66cf5c619a78705a Mon Sep 17 00:00:00 2001 From: Zhang Lixu Date: Mon, 13 Jul 2026 13:57:33 -0400 Subject: [PATCH 2133/3297] iio: hid-sensor-rotation: Fix stale or zero output when reading raw values [ Upstream commit 3ce8d099e0afc5a7da75a2007a67f67c4f5a4af1 ] When reading the raw quaternion attribute (in_rot_quaternion_raw), the driver currently returns either all zeros (if the sensor was never enabled) or stale data (if the sensor was previously enabled) because it reads from the internal buffer without explicitly requesting a new sample from the sensor. To fix this, power up the sensor, call sensor_hub_input_attr_read_values() to issue a synchronous GET_REPORT and receive the full quaternion data directly into a local buffer, then decode the four components. Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support") Signed-off-by: Zhang Lixu Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/iio/orientation/hid-sensor-rotation.c | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 5a5e6e4fbe34b..83a0b0283605b 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -74,6 +74,13 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, long mask) { struct dev_rot_state *rot_state = iio_priv(indio_dev); + struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev; + struct hid_sensor_hub_attribute_info *info = &rot_state->quaternion; + u32 usage_id = HID_USAGE_SENSOR_ORIENT_QUATERNION; + union { + s16 val16[4]; + s32 val32[4]; + } raw_buf; int ret_type; int i; @@ -83,8 +90,37 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: if (size >= 4) { - for (i = 0; i < 4; ++i) - vals[i] = rot_state->scan.sampled_vals[i]; + if (info->size <= 0 || info->size > sizeof(raw_buf)) + return -EINVAL; + + hid_sensor_power_state(&rot_state->common_attributes, true); + + ret_type = sensor_hub_input_attr_read_values(hsdev, + hsdev->usage, + usage_id, + info->report_id, + SENSOR_HUB_SYNC, + info->size, + (u8 *)&raw_buf); + + hid_sensor_power_state(&rot_state->common_attributes, false); + + if (ret_type < 0) + return ret_type; + + switch (info->size) { + case sizeof(raw_buf.val16): + for (i = 0; i < ARRAY_SIZE(raw_buf.val16); i++) + vals[i] = raw_buf.val16[i]; + break; + case sizeof(raw_buf.val32): + for (i = 0; i < ARRAY_SIZE(raw_buf.val32); i++) + vals[i] = raw_buf.val32[i]; + break; + default: + return -EINVAL; + } + ret_type = IIO_VAL_INT_MULTIPLE; *val_len = 4; } else From ad5c5bdb0f5809dfadf53734948b0b4671e85546 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 13 Jul 2026 22:09:13 -0400 Subject: [PATCH 2134/3297] ALSA: scarlett2: Allow selecting config_set by firmware version [ Upstream commit 732a6397a526c025cd29c3c9309b0db6a2c08837 ] The Scarlett 2i2 Gen 4 firmware 2417 moved the direct monitor gain parameters, so we now need to allow each device to list multiple scarlett2_config_set entries, one per applicable firmware version range, and pick the matching one at probe time. No functional change yet: each device gets a single config_sets entry whose from_firmware_version matches the existing min_firmware_version (0 where none was set). This both prepares for selection and lets a follow-up commit remove the now-redundant min_firmware_version field. scarlett2_count_io() depends on the resolved config_set so it moves out of scarlett2_init_private() into snd_scarlett2_controls_create() after the firmware version has been read. Signed-off-by: Geoffrey D. Bennett Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/ae1695b4c4825f365b4c86b22174035f742807e3.1777151532.git.g@b4.vu Stable-dep-of: 3ca15754b561 ("ALSA: scarlett2: Update offsets for 2i2 Gen 4 firmware 2417") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett2.c | 143 ++++++++++++++++++++++++++++++------ 1 file changed, 121 insertions(+), 22 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index a31f255593d93..fedf0c656b995 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -612,6 +612,20 @@ struct scarlett2_config_set { const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT]; }; +/* Map firmware versions to config sets per-device. + * + * Each device lists one or more entries, sorted in ascending order of + * from_firmware_version. At probe time the running firmware version + * is looked up against this list and the last entry whose + * from_firmware_version is <= the running version is selected. + * + * The list is terminated by a sentinel entry with config_set == NULL. + */ +struct scarlett2_config_set_entry { + u16 from_firmware_version; + const struct scarlett2_config_set *config_set; +}; + /* Input gain TLV dB ranges */ static const DECLARE_TLV_DB_MINMAX( @@ -1100,8 +1114,8 @@ struct scarlett2_meter_entry { }; struct scarlett2_device_info { - /* which set of configuration parameters the device uses */ - const struct scarlett2_config_set *config_set; + /* which sets of configuration parameters the device uses */ + const struct scarlett2_config_set_entry *config_sets; /* minimum firmware version required */ u16 min_firmware_version; @@ -1343,7 +1357,10 @@ struct scarlett2_data { /*** Model-specific data ***/ static const struct scarlett2_device_info s6i6_gen2_info = { - .config_set = &scarlett2_config_set_gen2a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2a }, + { } + }, .level_input_count = 2, .pad_input_count = 2, @@ -1393,7 +1410,10 @@ static const struct scarlett2_device_info s6i6_gen2_info = { }; static const struct scarlett2_device_info s18i8_gen2_info = { - .config_set = &scarlett2_config_set_gen2a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2a }, + { } + }, .level_input_count = 2, .pad_input_count = 4, @@ -1446,7 +1466,10 @@ static const struct scarlett2_device_info s18i8_gen2_info = { }; static const struct scarlett2_device_info s18i20_gen2_info = { - .config_set = &scarlett2_config_set_gen2b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen2b }, + { } + }, .line_out_descrs = { "Monitor L", @@ -1503,7 +1526,10 @@ static const struct scarlett2_device_info s18i20_gen2_info = { }; static const struct scarlett2_device_info solo_gen3_info = { - .config_set = &scarlett2_config_set_gen3a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3a }, + { } + }, .level_input_count = 1, .level_input_first = 1, .air_input_count = 1, @@ -1513,7 +1539,10 @@ static const struct scarlett2_device_info solo_gen3_info = { }; static const struct scarlett2_device_info s2i2_gen3_info = { - .config_set = &scarlett2_config_set_gen3a, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3a }, + { } + }, .level_input_count = 2, .air_input_count = 2, .phantom_count = 1, @@ -1522,7 +1551,10 @@ static const struct scarlett2_device_info s2i2_gen3_info = { }; static const struct scarlett2_device_info s4i4_gen3_info = { - .config_set = &scarlett2_config_set_gen3b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3b }, + { } + }, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -1571,7 +1603,10 @@ static const struct scarlett2_device_info s4i4_gen3_info = { }; static const struct scarlett2_device_info s8i6_gen3_info = { - .config_set = &scarlett2_config_set_gen3b, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3b }, + { } + }, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, @@ -1637,7 +1672,10 @@ static const char * const scarlett2_spdif_s18i8_gen3_texts[] = { }; static const struct scarlett2_device_info s18i8_gen3_info = { - .config_set = &scarlett2_config_set_gen3c, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3c }, + { } + }, .has_speaker_switching = 1, .level_input_count = 2, .pad_input_count = 4, @@ -1729,7 +1767,10 @@ static const char * const scarlett2_spdif_s18i20_gen3_texts[] = { }; static const struct scarlett2_device_info s18i20_gen3_info = { - .config_set = &scarlett2_config_set_gen3c, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_gen3c }, + { } + }, .has_speaker_switching = 1, .has_talkback = 1, .level_input_count = 2, @@ -1803,7 +1844,10 @@ static const struct scarlett2_device_info s18i20_gen3_info = { }; static const struct scarlett2_device_info vocaster_one_info = { - .config_set = &scarlett2_config_set_vocaster, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 1769, &scarlett2_config_set_vocaster }, + { } + }, .min_firmware_version = 1769, .has_devmap = 1, @@ -1847,7 +1891,10 @@ static const struct scarlett2_device_info vocaster_one_info = { }; static const struct scarlett2_device_info vocaster_two_info = { - .config_set = &scarlett2_config_set_vocaster, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 1769, &scarlett2_config_set_vocaster }, + { } + }, .min_firmware_version = 1769, .has_devmap = 1, @@ -1892,7 +1939,10 @@ static const struct scarlett2_device_info vocaster_two_info = { }; static const struct scarlett2_device_info solo_gen4_info = { - .config_set = &scarlett2_config_set_gen4_solo, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2115, &scarlett2_config_set_gen4_solo }, + { } + }, .min_firmware_version = 2115, .has_devmap = 1, @@ -1947,7 +1997,10 @@ static const struct scarlett2_device_info solo_gen4_info = { }; static const struct scarlett2_device_info s2i2_gen4_info = { - .config_set = &scarlett2_config_set_gen4_2i2, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2115, &scarlett2_config_set_gen4_2i2 }, + { } + }, .min_firmware_version = 2115, .has_devmap = 1, @@ -2002,7 +2055,10 @@ static const struct scarlett2_device_info s2i2_gen4_info = { }; static const struct scarlett2_device_info s4i4_gen4_info = { - .config_set = &scarlett2_config_set_gen4_4i4, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 2089, &scarlett2_config_set_gen4_4i4 }, + { } + }, .min_firmware_version = 2089, .has_devmap = 1, @@ -2051,7 +2107,10 @@ static const struct scarlett2_device_info s4i4_gen4_info = { }; static const struct scarlett2_device_info clarett_2pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 2, @@ -2107,7 +2166,10 @@ static const char * const scarlett2_spdif_clarett_texts[] = { }; static const struct scarlett2_device_info clarett_4pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 4, @@ -2163,7 +2225,10 @@ static const struct scarlett2_device_info clarett_4pre_info = { }; static const struct scarlett2_device_info clarett_8pre_info = { - .config_set = &scarlett2_config_set_clarett, + .config_sets = (const struct scarlett2_config_set_entry[]) { + { 0, &scarlett2_config_set_clarett }, + { } + }, .level_input_count = 2, .air_input_count = 8, @@ -8211,10 +8276,32 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) /*** Initialisation ***/ +/* Select the config_set matching the running firmware version. + * + * The device info's config_sets array is ordered by ascending + * from_firmware_version; pick the last entry whose version is <= the + * running firmware version. If the running firmware is older than the + * first entry's from_firmware_version (i.e. older than the driver's + * minimum supported version for this device), the first entry's + * config_set is selected anyway so firmware updates can still be done + * (requires only the ACK handler), but the usual mixer controls + * aren't created. + */ +static void scarlett2_resolve_config_set(struct scarlett2_data *private) +{ + const struct scarlett2_config_set_entry *entry = + private->info->config_sets; + + private->config_set = entry->config_set; + for (entry++; entry->config_set; entry++) + if (entry->from_firmware_version <= private->firmware_version) + private->config_set = entry->config_set; +} + static void scarlett2_count_io(struct scarlett2_data *private) { const struct scarlett2_device_info *info = private->info; - const struct scarlett2_config_set *config_set = info->config_set; + const struct scarlett2_config_set *config_set = private->config_set; const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int port_type, srcs = 0, dsts = 0, i; @@ -8311,9 +8398,14 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, mixer->private_suspend = scarlett2_private_suspend; private->info = entry->info; - private->config_set = entry->info->config_set; + + /* Set config_set to the first entry's config_set so the + * notify handler has a valid pointer while USB init runs; it + * is re-resolved once the firmware version has been read. + */ + private->config_set = entry->info->config_sets[0].config_set; + private->series_name = entry->series_name; - scarlett2_count_io(private); private->scarlett2_seq = 0; private->mixer = mixer; @@ -8717,6 +8809,13 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Now that the firmware version is known, pick the matching + * config_set + */ + scarlett2_resolve_config_set(private); + + scarlett2_count_io(private); + /* Get the upgrade & settings flash segment numbers */ err = scarlett2_get_flash_segment_nums(mixer); if (err < 0) From 1ed7ff33cfc8cf55185dd5355e708790b1144d7b Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 13 Jul 2026 22:09:14 -0400 Subject: [PATCH 2135/3297] ALSA: scarlett2: Update offsets for 2i2 Gen 4 firmware 2417 [ Upstream commit 3ca15754b561483aa7a1bce51677d6389f8ff5bb ] Firmware 2417 for the Scarlett 4th Gen 2i2 moved the direct monitor gain parameters, so add a second config_set with the shifted offset and select it for firmware versions >= 2417. Fixes: 4e809a299677 ("ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4") Cc: stable@vger.kernel.org # ALSA: scarlett2: Allow selecting config_set by firmware version Cc: stable@vger.kernel.org # ALSA: scarlett2: Fold min_firmware_version into config_sets Cc: stable@vger.kernel.org Signed-off-by: Geoffrey D. Bennett Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/ad0fc5a131e76eb656a24e0e198382f7134068fe.1777151532.git.g@b4.vu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett2.c | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index fedf0c656b995..6ac2fad37f04a 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -937,6 +937,63 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { } }; +/* 2i2 Gen 4, firmware version 2417 and above + * + * Firmware 2417 shifted DIRECT_MONITOR_GAIN by 4 bytes; all other + * offsets are unchanged from scarlett2_config_set_gen4_2i2. + */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2_2417 = { + .notifications = scarlett4_2i2_notifications, + .param_buf_addr = 0xfc, + .input_gain_tlv = db_scale_gen4_gain, + .autogain_status_texts = scarlett2_autogain_status_gen4, + .items = { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x49, .size = 8, .activate = 4 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR] = { + .offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { + .offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { + .offset = 0x137, .size = 8 }, + + [SCARLETT2_CONFIG_AG_MEAN_TARGET] = { + .offset = 0x131, .size = 8, .activate = 29, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AG_PEAK_TARGET] = { + .offset = 0x132, .size = 8, .activate = 30, .pbuf = 1 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x48, .size = 8, .activate = 11, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_INPUT_GAIN] = { + .offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1, + .mute = 1 }, + + [SCARLETT2_CONFIG_SAFE_SWITCH] = { + .offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { + .offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 }, + + [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { + .offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { + .offset = 0x2a4, .size = 16, .activate = 36 } + } +}; + /* 4i4 Gen 4 */ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { .notifications = scarlett4_4i4_notifications, @@ -1999,6 +2056,7 @@ static const struct scarlett2_device_info solo_gen4_info = { static const struct scarlett2_device_info s2i2_gen4_info = { .config_sets = (const struct scarlett2_config_set_entry[]) { { 2115, &scarlett2_config_set_gen4_2i2 }, + { 2417, &scarlett2_config_set_gen4_2i2_2417 }, { } }, .min_firmware_version = 2115, From 6438d0707087ec9d2bc4e1d72d983d22dee521f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 14 Jul 2026 00:05:38 -0400 Subject: [PATCH 2136/3297] firmware_loader: Add cancel helper for async requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b9bdd68b8b979c7e9de58b2e7d21e1d7d932c755 ] request_firmware_nowait() keeps the callback module pinned and holds a device reference until the firmware work completes. Callers still have no way to cancel or synchronize the queued callback before tearing down their driver-private state. Track scheduled async firmware work in an internal list and add request_firmware_nowait_cancel(). The helper cancels work matching the device, callback context and callback function. It cancels work that has not started yet and waits for an already-running callback to return. If the request has already completed, it is a no-op. Keep the existing request_firmware_nowait() lifetime model manual. A devres-managed variant can be layered on top separately if needed. Signed-off-by: Cássio Gabriel Reviewed-by: Takashi Iwai Acked-by: Danilo Krummrich Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-1-e7c4bf930dc8@gmail.com Stable-dep-of: 5367e2ad14f0 ("ALSA: hda/tas2781: Cancel async firmware request at unbind") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/main.c | 68 +++++++++++++++++++++++++++-- include/linux/firmware.h | 10 +++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index d4552101cbea2..0f3e1779d1e87 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -1141,6 +1141,7 @@ EXPORT_SYMBOL(release_firmware); /* Async support */ struct firmware_work { struct work_struct work; + struct list_head list; struct module *module; const char *name; struct device *device; @@ -1149,6 +1150,17 @@ struct firmware_work { u32 opt_flags; }; +static LIST_HEAD(firmware_work_list); +static DEFINE_SPINLOCK(firmware_work_lock); + +static void firmware_work_free(struct firmware_work *fw_work) +{ + put_device(fw_work->device); /* taken in request_firmware_nowait() */ + module_put(fw_work->module); + kfree_const(fw_work->name); + kfree(fw_work); +} + static void request_firmware_work_func(struct work_struct *work) { struct firmware_work *fw_work; @@ -1159,11 +1171,15 @@ static void request_firmware_work_func(struct work_struct *work) _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, fw_work->opt_flags); fw_work->cont(fw, fw_work->context); - put_device(fw_work->device); /* taken in request_firmware_nowait() */ - module_put(fw_work->module); - kfree_const(fw_work->name); - kfree(fw_work); + spin_lock_irq(&firmware_work_lock); + if (!list_empty(&fw_work->list)) { + list_del_init(&fw_work->list); + spin_unlock_irq(&firmware_work_lock); + firmware_work_free(fw_work); + return; + } + spin_unlock_irq(&firmware_work_lock); } @@ -1173,6 +1189,7 @@ static int _request_firmware_nowait( void (*cont)(const struct firmware *fw, void *context), bool nowarn) { struct firmware_work *fw_work; + unsigned long flags; fw_work = kzalloc(sizeof(struct firmware_work), gfp); if (!fw_work) @@ -1205,7 +1222,12 @@ static int _request_firmware_nowait( get_device(fw_work->device); INIT_WORK(&fw_work->work, request_firmware_work_func); + + spin_lock_irqsave(&firmware_work_lock, flags); + list_add_tail(&fw_work->list, &firmware_work_list); schedule_work(&fw_work->work); + spin_unlock_irqrestore(&firmware_work_lock, flags); + return 0; } @@ -1268,6 +1290,44 @@ int firmware_request_nowait_nowarn( } EXPORT_SYMBOL_GPL(firmware_request_nowait_nowarn); +/** + * request_firmware_nowait_cancel() - cancel an async firmware request + * @device: device for which the firmware is being loaded + * @context: context passed to request_firmware_nowait() + * @cont: callback passed to request_firmware_nowait() + * + * Cancel a pending request_firmware_nowait() request for @device, @context + * and @cont. If the associated work has already started, this function waits + * until the callback has returned. If the callback has already completed, this + * function does nothing. + * + * This function may sleep. + */ +void request_firmware_nowait_cancel(struct device *device, void *context, + void (*cont)(const struct firmware *fw, + void *context)) +{ + struct firmware_work *fw_work = NULL; + struct firmware_work *tmp; + + spin_lock_irq(&firmware_work_lock); + list_for_each_entry_reverse(tmp, &firmware_work_list, list) { + if (tmp->device == device && tmp->context == context && + tmp->cont == cont) { + fw_work = tmp; + list_del_init(&fw_work->list); + break; + } + } + spin_unlock_irq(&firmware_work_lock); + + if (!fw_work) + return; + cancel_work_sync(&fw_work->work); + firmware_work_free(fw_work); +} +EXPORT_SYMBOL_GPL(request_firmware_nowait_cancel); + #ifdef CONFIG_FW_CACHE static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index aae1b85ffc10e..0fa3b027f02f1 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -110,6 +110,9 @@ int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)); +void request_firmware_nowait_cancel(struct device *device, void *context, + void (*cont)(const struct firmware *fw, + void *context)); int request_firmware_direct(const struct firmware **fw, const char *name, struct device *device); int request_firmware_into_buf(const struct firmware **firmware_p, @@ -157,6 +160,13 @@ static inline int request_firmware_nowait( return -EINVAL; } +static inline void request_firmware_nowait_cancel(struct device *device, + void *context, + void (*cont)(const struct firmware *fw, + void *context)) +{ +} + static inline void release_firmware(const struct firmware *fw) { } From f8272331da877eec8fe8e89a1e98e6a710e12490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 14 Jul 2026 00:05:39 -0400 Subject: [PATCH 2137/3297] ALSA: hda/tas2781: Cancel async firmware request at unbind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5367e2ad14f0ae9350a7aaf2e77c87de39a43ae9 ] TAS2781 HDA I2C and SPI queue RCA firmware loading from component bind with request_firmware_nowait(). The firmware loader keeps the callback module pinned and holds a device reference, but the callback still uses driver-private HDA state. Component unbind removes controls and DSP state immediately. Later device removal tears down the TAS2781 private data, including codec_lock. If the async firmware callback runs after unbind has started, it can operate on state that is being torn down. Cancel or synchronize the async firmware request before removing controls and DSP state. A queued callback is cancelled, and an already-running callback is allowed to finish before unbind continues. Fixes: 5be27f1e3ec9 ("ALSA: hda/tas2781: Add tas2781 HDA driver") Fixes: bb5f86ea50ff ("ALSA: hda/tas2781: Add tas2781 hda SPI driver") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Reviewed-by: Takashi Iwai Acked-by: Danilo Krummrich Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-2-e7c4bf930dc8@gmail.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 3 +++ sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 624a822341bb7..ebfc08b5fb283 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -616,6 +616,9 @@ static void tas2781_hda_unbind(struct device *dev, comp->playback_hook = NULL; } + request_firmware_nowait_cancel(tas_hda->priv->dev, tas_hda->priv, + tasdev_fw_ready); + tas2781_hda_remove_controls(tas_hda); tasdevice_config_info_remove(tas_hda->priv); diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index a64f2c899d50c..ab2a2472d7bdc 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -753,6 +753,9 @@ static void tas2781_hda_unbind(struct device *dev, struct device *master, comp->playback_hook = NULL; } + request_firmware_nowait_cancel(tas_priv->dev, tas_priv, + tasdev_fw_ready); + tas2781_hda_remove_controls(tas_hda); tasdevice_config_info_remove(tas_priv); From 7ed120b1a007bace57c461805519d70e1af44e59 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 14 Jul 2026 09:27:08 -0400 Subject: [PATCH 2138/3297] vfio/mlx5: Fix racy bitfields and tighten struct layout [ Upstream commit f2365a63b02ddea32e7db78b742c2503ec7b81f1 ] Bitfield operations are not atomic, they use a read-modify-write pattern, therefore we should be careful not to pack bitfields that can be concurrently updated into the same storage unit. This split takes a binary approach: flags that are only modified pre/post open/close remain bitfields, flags modified from user action, including actions that reach across to another device (ex. reset) use dedicated storage units. Note mlx5_vhca_page_tracker.status is relocated to fill the alignment hole this split exposes. Bitfield justifications: migrate_cap: written only in mlx5vf_cmd_set_migratable() at probe chunk_mode: written only in mlx5vf_cmd_set_migratable() at probe mig_state_cap: written only in mlx5vf_cmd_set_migratable() at probe Dedicated storage units: mdev_detach: written in the VF attach/detach event notifier mlx5fv_vf_event() at runtime log_active: written in mlx5vf_start_page_tracker()/ mlx5vf_stop_page_tracker() during runtime dirty tracking deferred_reset: written in mlx5vf_state_mutex_unlock()/ mlx5vf_pci_aer_reset_done() during runtime reset handling is_err: set by tracker error handling and dirty-log polling at runtime object_changed: set by tracker event handling and cleared by dirty-log polling at runtime Fixes: 61a2f1460fd0 ("vfio/mlx5: Manage the VF attach/detach callback from the PF") Fixes: 79c3cf279926 ("vfio/mlx5: Init QP based resources for dirty tracking") Fixes: f886473071d6 ("vfio/mlx5: Add support for tracker object change event") Cc: Yishai Hadas Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Alex Williamson Reviewed-by: Kevin Tian Link: https://lore.kernel.org/r/20260615191241.688297-5-alex.williamson@nvidia.com Signed-off-by: Alex Williamson Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/vfio/pci/mlx5/cmd.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/vfio/pci/mlx5/cmd.h b/drivers/vfio/pci/mlx5/cmd.h index d7821b5ca7729..82831e0483c09 100644 --- a/drivers/vfio/pci/mlx5/cmd.h +++ b/drivers/vfio/pci/mlx5/cmd.h @@ -155,25 +155,28 @@ struct mlx5_vhca_qp { struct mlx5_vhca_page_tracker { u32 id; u32 pdn; - u8 is_err:1; - u8 object_changed:1; + /* Flags modified at runtime - dedicated storage unit */ + u8 is_err; + u8 object_changed; + int status; struct mlx5_uars_page *uar; struct mlx5_vhca_cq cq; struct mlx5_vhca_qp *host_qp; struct mlx5_vhca_qp *fw_qp; struct mlx5_nb nb; - int status; }; struct mlx5vf_pci_core_device { struct vfio_pci_core_device core_device; int vf_id; u16 vhca_id; + /* Flags only modified on setup/release - bitfield ok */ u8 migrate_cap:1; - u8 deferred_reset:1; - u8 mdev_detach:1; - u8 log_active:1; u8 chunk_mode:1; + /* Flags modified at runtime - dedicated storage unit */ + u8 mdev_detach; + u8 log_active; + u8 deferred_reset; struct completion tracker_comp; /* protect migration state */ struct mutex state_mutex; From 79ac87bb1a4c861e5c7c40b11dcc3d4a92ec6790 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 14 Jul 2026 09:43:06 -0400 Subject: [PATCH 2139/3297] binder: Use LIST_HEAD() to initialize on stack list head [ Upstream commit 91f818b184b44b105b1c92859ea8d2d6f47912a9 ] Use LIST_HEAD to initialize on stack list head. No intentional functional impact. Change generated with below coccinelle script: @@ identifier name; @@ - struct list_head name; + LIST_HEAD(name); ... when != name - INIT_LIST_HEAD(&name); Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260519055623.13142-1-jszhang@kernel.org Signed-off-by: Greg Kroah-Hartman Stable-dep-of: b34826e55aad ("binder: cache secctx size before release zeroes it") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 558164f0918fb..1a6719abf67e3 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3097,12 +3097,10 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); ktime_t t_start_time = ktime_get(); struct lsm_context lsmctx = { }; - struct list_head sgc_head; - struct list_head pf_head; + LIST_HEAD(sgc_head); + LIST_HEAD(pf_head); const void __user *user_buffer = (const void __user *) (uintptr_t)tr->data.ptr.buffer; - INIT_LIST_HEAD(&sgc_head); - INIT_LIST_HEAD(&pf_head); e = binder_transaction_log_add(&binder_transaction_log); e->debug_id = t_debug_id; From 1228926e1e4d605cc74d9e675558982a6f2cf446 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Tue, 14 Jul 2026 09:43:07 -0400 Subject: [PATCH 2140/3297] binder: cache secctx size before release zeroes it [ Upstream commit b34826e55aad3520ec813f1f367c11b24b29dc9f ] binder_transaction() bounds the scatter-gather buffer area with sg_buf_end_offset and subtracts the aligned LSM context size because the secctx is written at the tail of that area. The subtraction reads lsmctx.len, but that field has already been cleared by the time the line runs: security_secid_to_secctx(secid, &lsmctx) /* lsmctx.len set */ lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64)) extra_buffers_size += lsmctx_aligned_size ... security_release_secctx(&lsmctx) /* memset zeroes len */ ... sg_buf_end_offset = sg_buf_offset + extra_buffers_size - ALIGN(lsmctx.len, sizeof(u64)) /* ALIGN(0,8) */ security_release_secctx() does memset(cp, 0, sizeof(*cp)), so lsmctx.len reads back as 0 and the subtraction contributes nothing, leaving sg_buf_end_offset too large by the aligned secctx size on every transaction to a txn_security_ctx node. Each BINDER_TYPE_PTR object then derives buf_left = sg_buf_end_offset - sg_buf_offset as the sole upper bound on its copy, so the inflated end offset lets the copy run into the bytes that already hold the secctx. The aligned size must therefore be cached before release rather than re-read from the now-cleared field. Fix by caching it in lsmctx_aligned_size at function scope when it is first computed and subtracting lsmctx_aligned_size instead of re-reading lsmctx.len after release. Reuse the same value for the earlier buf_offset computation. Fixes: 6fba89813ccf ("lsm: ensure the correct LSM context releaser") Cc: stable Assisted-by: kres:claude-opus-4-8 Signed-off-by: Chris Mason Reviewed-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260603174506.1957278-1-clm@meta.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 1a6719abf67e3..81f1d92b2701a 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3097,6 +3097,7 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); ktime_t t_start_time = ktime_get(); struct lsm_context lsmctx = { }; + size_t lsmctx_aligned_size = 0; LIST_HEAD(sgc_head); LIST_HEAD(pf_head); const void __user *user_buffer = (const void __user *) @@ -3363,7 +3364,6 @@ static void binder_transaction(struct binder_proc *proc, if (target_node && target_node->txn_security_ctx) { u32 secid; - size_t added_size; security_cred_getsecid(proc->cred, &secid); ret = security_secid_to_secctx(secid, &lsmctx); @@ -3375,9 +3375,9 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_get_secctx_failed; } - added_size = ALIGN(lsmctx.len, sizeof(u64)); - extra_buffers_size += added_size; - if (extra_buffers_size < added_size) { + lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64)); + extra_buffers_size += lsmctx_aligned_size; + if (extra_buffers_size < lsmctx_aligned_size) { binder_txn_error("%d:%d integer overflow of extra_buffers_size\n", thread->pid, proc->pid); return_error = BR_FAILED_REPLY; @@ -3414,7 +3414,7 @@ static void binder_transaction(struct binder_proc *proc, size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) + ALIGN(tr->offsets_size, sizeof(void *)) + ALIGN(extra_buffers_size, sizeof(void *)) - - ALIGN(lsmctx.len, sizeof(u64)); + lsmctx_aligned_size; t->security_ctx = t->buffer->user_data + buf_offset; err = binder_alloc_copy_to_buffer(&target_proc->alloc, @@ -3469,7 +3469,7 @@ static void binder_transaction(struct binder_proc *proc, off_end_offset = off_start_offset + tr->offsets_size; sg_buf_offset = ALIGN(off_end_offset, sizeof(void *)); sg_buf_end_offset = sg_buf_offset + extra_buffers_size - - ALIGN(lsmctx.len, sizeof(u64)); + lsmctx_aligned_size; off_min = 0; for (buffer_offset = off_start_offset; buffer_offset < off_end_offset; buffer_offset += sizeof(binder_size_t)) { From e53d54b92f0c286e2644da70f4d542577749a720 Mon Sep 17 00:00:00 2001 From: Franz Schnyder Date: Tue, 14 Jul 2026 13:49:20 -0400 Subject: [PATCH 2141/3297] PCI: imx6: Fix reference clock source selection for i.MX95 [ Upstream commit 88cc4cbe08bba27bb58888d25d336774aa0ccab1 ] In the PCIe PHY init for the i.MX95, the reference clock source selection uses a conditional instead of always passing the mask. This currently breaks functionality if the internal refclk is used. To fix this issue, always pass IMX95_PCIE_REF_USE_PAD as the mask and clear bit if external refclk is not used. This essentially swaps the parameters. Fixes: d8574ce57d76 ("PCI: imx6: Add external reference clock input mode support") Signed-off-by: Franz Schnyder Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Acked-by: Richard Zhu Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260325093118.684142-1-fra.schnyder@gmail.com Stable-dep-of: 0c26b1c34d12 ("PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pci-imx6.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index d7de9790fe9fd..bf25fbbcb35bf 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -150,6 +150,7 @@ struct imx_lut_data { struct imx_pcie { struct dw_pcie *pci; struct gpio_desc *reset_gpiod; + bool enable_ext_refclk; struct clk_bulk_data *clks; int num_clks; struct regmap *iomuxc_gpr; @@ -244,6 +245,8 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { + bool ext = imx_pcie->enable_ext_refclk; + /* * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready * Through Beacon or PERST# De-assertion @@ -262,13 +265,12 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, - IMX95_PCIE_PHY_GEN_CTRL, - IMX95_PCIE_REF_USE_PAD, 0); - regmap_update_bits(imx_pcie->iomuxc_gpr, - IMX95_PCIE_SS_RW_REG_0, + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, + ext ? IMX95_PCIE_REF_USE_PAD : 0); + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, - IMX95_PCIE_REF_CLKEN); + ext ? 0 : IMX95_PCIE_REF_CLKEN); return 0; } From 0dfad346c293ea3cbca122676ceb493706da69e5 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Tue, 14 Jul 2026 13:49:21 -0400 Subject: [PATCH 2142/3297] PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95 [ Upstream commit 0c26b1c34d12d4debfb5363cc0be6cdf68e87ba2 ] According to the i.MX95 PCIe PHY Databook, the ref_use_pad signal in the Common Block Signals section selects the reference clock source connected to the PHY pads. Per the specification, any change to this input must be followed by a PHY reset assertion to take effect. Move the REF_USE_PAD configuration before the PHY reset toggle to comply with the required initialization sequence. Fixes: 47f54a902dcd ("PCI: imx6: Toggle the core reset for i.MX95 PCIe") Signed-off-by: Richard Zhu [mani: renamed the callback and helper to match the usecase] Signed-off-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260518072715.3166514-2-hongxing.zhu@nxp.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/controller/dwc/pci-imx6.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index bf25fbbcb35bf..de09cc2a63112 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -135,6 +135,7 @@ struct imx_pcie_drvdata { const u32 mode_off[IMX_PCIE_MAX_INSTANCES]; const u32 mode_mask[IMX_PCIE_MAX_INSTANCES]; const struct pci_epc_features *epc_features; + int (*select_ref_clk_src)(struct imx_pcie *pcie); int (*init_phy)(struct imx_pcie *pcie); int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable); int (*core_reset)(struct imx_pcie *pcie, bool assert); @@ -243,6 +244,24 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; } +static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie) +{ + bool ext = imx_pcie->enable_ext_refclk; + + /* + * Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is + * used to select reference clock connected to a pair of pads. + * + * Any change in this input must be followed by phy_reset assertion. + */ + + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, + ext ? IMX95_PCIE_REF_USE_PAD : 0); + + return 0; +} + static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { bool ext = imx_pcie->enable_ext_refclk; @@ -265,9 +284,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, - IMX95_PCIE_REF_USE_PAD, - ext ? IMX95_PCIE_REF_USE_PAD : 0); regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, ext ? 0 : IMX95_PCIE_REF_CLKEN); @@ -1232,6 +1248,9 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) pp->bridge->disable_device = imx_pcie_disable_device; } + if (imx_pcie->drvdata->select_ref_clk_src) + imx_pcie->drvdata->select_ref_clk_src(imx_pcie); + imx_pcie_assert_core_reset(imx_pcie); if (imx_pcie->drvdata->init_phy) @@ -1932,6 +1951,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, .core_reset = imx95_pcie_core_reset, .init_phy = imx95_pcie_init_phy, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .enable_ref_clk = imx95_pcie_enable_ref_clk, }, @@ -1986,6 +2006,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .ltssm_mask = IMX95_PCIE_LTSSM_EN, .mode_off[0] = IMX95_PE0_GEN_CTRL_1, .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .init_phy = imx95_pcie_init_phy, .core_reset = imx95_pcie_core_reset, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, From 0d1c263e6fd731f3032b2e0e1bd150cd6021d918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 14 Jul 2026 16:02:29 -0400 Subject: [PATCH 2143/3297] PCI/IOV: Adjust ->barsz[] when changing BAR size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4687b3315a3f76647746f5b7f92684cf1045b085 ] pci_rebar_set_size() adjusts BAR size for both normal and IOV BARs. The struct pci_sriov keeps a cached copy of BAR size in ->barsz[] which is not adjusted by pci_rebar_set_size() but by pci_iov_resource_set_size(). pci_iov_resource_set_size() is called also from pci_resize_resource_set_size(). The current arrangement is problematic once BAR resize algorithm starts to roll back changes properly in case of a failure. The normal resource fitting algorithm rolls back resource size using the struct pci_dev_resource easily but also calling pci_resize_resource_set_size() or pci_iov_resource_set_size() to roll back BAR size would be an extra burden, whereas combining ->barsz[] update with pci_rebar_set_size() naturally rolls back it when restoring the old BAR size on a different layer of the BAR resize operation. Thus, rework pci_rebar_set_size() to also update ->barsz[]. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Alex Bennée # AVA, AMD GPU Link: https://patch.msgid.link/20251113162628.5946-3-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/iov.c | 15 ++++----------- drivers/pci/pci.c | 4 ++++ drivers/pci/pci.h | 5 ++--- drivers/pci/setup-res.c | 10 ++++------ 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 8bda4e202f56d..277a239e26e6c 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -158,8 +158,7 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) return dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)]; } -void pci_iov_resource_set_size(struct pci_dev *dev, int resno, - resource_size_t size) +void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size) { if (!pci_resource_is_iov(resno)) { pci_warn(dev, "%s is not an IOV resource\n", @@ -167,7 +166,8 @@ void pci_iov_resource_set_size(struct pci_dev *dev, int resno, return; } - dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)] = size; + resno = pci_resource_num_to_vf_bar(resno); + dev->sriov->barsz[resno] = pci_rebar_size_to_bytes(size); } bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev) @@ -1345,7 +1345,6 @@ EXPORT_SYMBOL_GPL(pci_sriov_configure_simple); int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size) { u32 sizes; - int ret; if (!pci_resource_is_iov(resno)) return -EINVAL; @@ -1360,13 +1359,7 @@ int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size) if (!(sizes & BIT(size))) return -EINVAL; - ret = pci_rebar_set_size(dev, resno, size); - if (ret) - return ret; - - pci_iov_resource_set_size(dev, resno, pci_rebar_size_to_bytes(size)); - - return 0; + return pci_rebar_set_size(dev, resno, size); } EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6420b371aaf07..6db59bd452d9a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3797,6 +3797,10 @@ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); + + if (pci_resource_is_iov(bar)) + pci_iov_resource_set_size(pdev, bar, size); + return 0; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5510c103be2d3..549f60d219864 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -826,8 +826,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno); resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno); void pci_restore_iov_state(struct pci_dev *dev); int pci_iov_bus_range(struct pci_bus *bus); -void pci_iov_resource_set_size(struct pci_dev *dev, int resno, - resource_size_t size); +void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size); bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev); static inline u16 pci_iov_vf_rebar_cap(struct pci_dev *dev) { @@ -869,7 +868,7 @@ static inline int pci_iov_bus_range(struct pci_bus *bus) return 0; } static inline void pci_iov_resource_set_size(struct pci_dev *dev, int resno, - resource_size_t size) { } + int size) { } static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev) { return false; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 60da129d714f9..53338eb45f038 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -452,12 +452,10 @@ static void pci_resize_resource_set_size(struct pci_dev *dev, int resno, resource_size_t res_size = pci_rebar_size_to_bytes(size); struct resource *res = pci_resource_n(dev, resno); - if (!pci_resource_is_iov(resno)) { - resource_set_size(res, res_size); - } else { - resource_set_size(res, res_size * pci_sriov_get_totalvfs(dev)); - pci_iov_resource_set_size(dev, resno, res_size); - } + if (pci_resource_is_iov(resno)) + res_size *= pci_sriov_get_totalvfs(dev); + + resource_set_size(res, res_size); } int pci_resize_resource(struct pci_dev *dev, int resno, int size) From dbb1d8507dd923f87cafa71fe575f8d9c458b248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 14 Jul 2026 16:02:30 -0400 Subject: [PATCH 2144/3297] PCI: Change pci_dev variable from 'bridge' to 'dev' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 34c702ea0497e092a487eacdf28a037f43e3e39f ] Upcoming fix to BAR resize will store also device BAR resource in the saved list. Change the pci_dev variable in the loop from 'bridge' to 'dev' as the former would be misleading with non-bridges in the list. This is in a separate change to reduce churn in the upcoming BAR resize fix. While it appears that the logic in the loop doing pci_setup_bridge() is altered as 'bridge' variable is no longer updated, a bridge should never appear more than once in the saved list so the check can only match to the first entry. As such, the code with two distinct pci_dev variables better represents the intention of the check compared with the old code where bridge variable was reused for a different purpose. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Alex Bennée # AVA, AMD GPU Reviewed-by: Alex Bennée Link: https://patch.msgid.link/20251113162628.5946-4-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/setup-bus.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 522e25d725bad..ae72a34b09c56 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2431,12 +2431,13 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) } list_for_each_entry(dev_res, &saved, list) { + struct pci_dev *dev = dev_res->dev; + /* Skip the bridge we just assigned resources for */ - if (bridge == dev_res->dev) + if (bridge == dev) continue; - bridge = dev_res->dev; - pci_setup_bridge(bridge->subordinate); + pci_setup_bridge(dev->subordinate); } free_list(&saved); @@ -2452,19 +2453,19 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) /* Revert to the old configuration */ list_for_each_entry(dev_res, &saved, list) { struct resource *res = dev_res->res; + struct pci_dev *dev = dev_res->dev; - bridge = dev_res->dev; - i = pci_resource_num(bridge, res); + i = pci_resource_num(dev, res); if (res->parent) { release_child_resources(res); - pci_release_resource(bridge, i); + pci_release_resource(dev, i); } restore_dev_resource(dev_res); - pci_claim_resource(bridge, i); - pci_setup_bridge(bridge->subordinate); + pci_claim_resource(dev, i); + pci_setup_bridge(dev->subordinate); } free_list(&saved); up_read(&pci_bus_sem); From 534f20cdddc311444ff4398f202df283ac6d5a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 14 Jul 2026 16:02:31 -0400 Subject: [PATCH 2145/3297] PCI: Try BAR resize even when no window was released MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 121d3e9e4b217f2f23715901fb15c6a3ca2bab46 ] Usually, resizing BARs requires releasing bridge windows in order to resize it to fit a larger BAR into the window. This is not always the case, however, FW could have made the window large enough to accommodate larger BAR as is, or the user might prefer to shrink a BAR to make more space for another Resizable BAR. Thus, replace the check that requires that at least one bridge window was released with a check that simply ensures bridge is not NULL. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Alex Bennée # AVA, AMD GPU Link: https://patch.msgid.link/20251113162628.5946-5-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/setup-bus.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ae72a34b09c56..55f3ac3f3eb1c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2376,7 +2376,7 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) { unsigned long type = res->flags; struct pci_dev_resource *dev_res; - struct pci_dev *bridge; + struct pci_dev *bridge = NULL; LIST_HEAD(saved); LIST_HEAD(added); LIST_HEAD(failed); @@ -2411,10 +2411,8 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) bus = bus->parent; } - if (list_empty(&saved)) { - up_read(&pci_bus_sem); + if (!bridge) return -ENOENT; - } __pci_bus_size_bridges(bridge->subordinate, &added); __pci_bridge_assign_resources(bridge, &added, &failed); From c18646165f21f7afccb92a9fe1ecc64d31cc403d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 14 Jul 2026 16:02:32 -0400 Subject: [PATCH 2146/3297] PCI: Free saved list without holding pci_bus_sem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1d8a0506f69895b7cfd9d5c4546761c508231a8a ] Freeing the saved list does not require holding pci_bus_sem, so the critical section can be made shorter. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Alex Bennée # AVA, AMD GPU Link: https://patch.msgid.link/20251113162628.5946-6-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 55f3ac3f3eb1c..5985ee441c7e2 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2465,8 +2465,8 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) pci_claim_resource(dev, i); pci_setup_bridge(dev->subordinate); } - free_list(&saved); up_read(&pci_bus_sem); + free_list(&saved); return ret; } From 4b5322f0002aac02a0f9907ac2358811330d982c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 14 Jul 2026 16:02:33 -0400 Subject: [PATCH 2147/3297] PCI: Fix restoring BARs on BAR resize rollback path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 337b1b566db087347194e4543ddfdfa5645275cc ] BAR resize operation is implemented in the pci_resize_resource() and pbus_reassign_bridge_resources() functions. pci_resize_resource() can be called either from __resource_resize_store() from sysfs or directly by the driver for the Endpoint Device. The pci_resize_resource() requires that caller has released the device resources that share the bridge window with the BAR to be resized as otherwise the bridge window is pinned in place and cannot be changed. pbus_reassign_bridge_resources() rolls back resources if the resize operation fails, but rollback is performed only for the bridge windows. Because releasing the device resources are done by the caller of the BAR resize interface, these functions performing the BAR resize do not have access to the device resources as they were before the resize. pbus_reassign_bridge_resources() could try __pci_bridge_assign_resources() after rolling back the bridge windows as they were, however, it will not guarantee the resource are assigned due to differences in how FW and the kernel assign the resources (alignment of the start address and tail). To perform rollback robustly, the BAR resize interface has to be altered to also release the device resources that share the bridge window with the BAR to be resized. Also, remove restoring from the entries failed list as saved list should now contain both the bridge windows and device resources so the extra restore is duplicated work. Some drivers (currently only amdgpu) want to prevent releasing some resources. Add exclude_bars param to pci_resize_resource() and make amdgpu pass its register BAR (BAR 2 or 5), which should never be released during resize operation. Normally 64-bit prefetchable resources do not share a bridge window with the 32-bit only register BAR, but there are various fallbacks in the resource assignment logic which may make the resources share the bridge window in rare cases. This change (together with the driver side changes) is to counter the resource releases that had to be done to prevent resource tree corruption in the ("PCI: Release assigned resource before restoring them") change. As such, it likely restores functionality in cases where device resources were released to avoid resource tree conflicts which appeared to be "working" when such conflicts were not correctly detected by the kernel. Reported-by: Simon Richter Link: https://lore.kernel.org/linux-pci/f9a8c975-f5d3-4dd2-988e-4371a1433a60@hogyros.de/ Reported-by: Alex Bennée Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/ Signed-off-by: Ilpo Järvinen [bhelgaas: squash amdgpu BAR selection from https: //lore.kernel.org/r/20251114103053.13778-1-ilpo.jarvinen@linux.intel.com] Signed-off-by: Bjorn Helgaas Tested-by: Alex Bennée # AVA, AMD GPU Reviewed-by: Christian König Link: https://patch.msgid.link/20251113162628.5946-7-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 4 +- drivers/gpu/drm/i915/gt/intel_region_lmem.c | 2 +- drivers/gpu/drm/xe/xe_vram.c | 2 +- drivers/pci/pci-sysfs.c | 17 +--- drivers/pci/pci.h | 4 +- drivers/pci/setup-bus.c | 100 +++++++++++++++----- drivers/pci/setup-res.c | 23 ++--- include/linux/pci.h | 3 +- 8 files changed, 93 insertions(+), 62 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index c2608d91bcd24..d0cb300cd382b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1736,7 +1736,9 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device *adev) pci_release_resource(adev->pdev, 0); - r = pci_resize_resource(adev->pdev, 0, rbar_size); + r = pci_resize_resource(adev->pdev, 0, rbar_size, + (adev->asic_type >= CHIP_BONAIRE) ? 1 << 5 + : 1 << 2); if (r == -ENOSPC) dev_info(adev->dev, "Not enough PCI address space for a large BAR."); diff --git a/drivers/gpu/drm/i915/gt/intel_region_lmem.c b/drivers/gpu/drm/i915/gt/intel_region_lmem.c index 51bb27e10a4f8..7699e8fcf5edf 100644 --- a/drivers/gpu/drm/i915/gt/intel_region_lmem.c +++ b/drivers/gpu/drm/i915/gt/intel_region_lmem.c @@ -37,7 +37,7 @@ _resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size) _release_bars(pdev); - ret = pci_resize_resource(pdev, resno, bar_size); + ret = pci_resize_resource(pdev, resno, bar_size, 0); if (ret) { drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n", resno, 1 << bar_size, ERR_PTR(ret)); diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c index 652df7a5f4f65..a4076f56a3c98 100644 --- a/drivers/gpu/drm/xe/xe_vram.c +++ b/drivers/gpu/drm/xe/xe_vram.c @@ -56,7 +56,7 @@ static void resize_bar(struct xe_device *xe, int resno, resource_size_t size) release_bars(pdev); - ret = pci_resize_resource(pdev, resno, bar_size); + ret = pci_resize_resource(pdev, resno, bar_size, 0); if (ret) { drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n", resno, 1 << bar_size, ERR_PTR(ret)); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d36b46849fee2..edb9bc34c0c58 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1571,18 +1571,13 @@ static ssize_t __resource_resize_store(struct device *dev, int n, { struct pci_dev *pdev = to_pci_dev(dev); struct pci_bus *bus = pdev->bus; - struct resource *b_win, *res; unsigned long size; - int ret, i; + int ret; u16 cmd; if (kstrtoul(buf, 0, &size) < 0) return -EINVAL; - b_win = pbus_select_window(bus, pci_resource_n(pdev, n)); - if (!b_win) - return -EINVAL; - device_lock(dev); if (dev->driver || pci_num_vf(pdev)) { ret = -EBUSY; @@ -1604,15 +1599,7 @@ static ssize_t __resource_resize_store(struct device *dev, int n, pci_remove_resource_files(pdev); - pci_dev_for_each_resource(pdev, res, i) { - if (i >= PCI_BRIDGE_RESOURCES) - break; - - if (b_win == pbus_select_window(bus, res)) - pci_release_resource(pdev, i); - } - - ret = pci_resize_resource(pdev, n, size); + ret = pci_resize_resource(pdev, n, size, 0); pci_assign_unassigned_bus_resources(bus); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 549f60d219864..5336a4b5b4567 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -439,8 +439,10 @@ enum pci_bar_type { struct device *pci_get_host_bridge_device(struct pci_dev *dev); void pci_put_host_bridge_device(struct device *dev); +void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size); +int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size, + int exclude_bars); unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); -int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res); int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); int pci_configure_extended_tags(struct pci_dev *dev, void *ign); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 5985ee441c7e2..03eadc81dc7d3 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2372,18 +2372,16 @@ EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); * release it when possible. If the bridge window contains assigned * resources, it cannot be released. */ -int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) +static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res, + struct list_head *saved) { unsigned long type = res->flags; struct pci_dev_resource *dev_res; struct pci_dev *bridge = NULL; - LIST_HEAD(saved); LIST_HEAD(added); LIST_HEAD(failed); unsigned int i; - int ret; - - down_read(&pci_bus_sem); + int ret = 0; while (!pci_is_root_bus(bus)) { bridge = bus->self; @@ -2395,9 +2393,9 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) /* Ignore BARs which are still in use */ if (!res->child) { - ret = add_to_list(&saved, bridge, res, 0, 0); + ret = add_to_list(saved, bridge, res, 0, 0); if (ret) - goto cleanup; + return ret; pci_release_resource(bridge, i); } else { @@ -2420,34 +2418,78 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) free_list(&added); if (!list_empty(&failed)) { - if (pci_required_resource_failed(&failed, type)) { + if (pci_required_resource_failed(&failed, type)) ret = -ENOSPC; - goto cleanup; - } - /* Only resources with unrelated types failed (again) */ free_list(&failed); + if (ret) + return ret; + + /* Only resources with unrelated types failed (again) */ } - list_for_each_entry(dev_res, &saved, list) { + list_for_each_entry(dev_res, saved, list) { struct pci_dev *dev = dev_res->dev; /* Skip the bridge we just assigned resources for */ if (bridge == dev) continue; + if (!dev->subordinate) + continue; + pci_setup_bridge(dev->subordinate); } - free_list(&saved); - up_read(&pci_bus_sem); return 0; +} -cleanup: - /* Restore size and flags */ - list_for_each_entry(dev_res, &failed, list) - restore_dev_resource(dev_res); - free_list(&failed); +int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size, + int exclude_bars) +{ + struct resource *res = pci_resource_n(pdev, resno); + struct pci_dev_resource *dev_res; + struct pci_bus *bus = pdev->bus; + struct resource *b_win, *r; + LIST_HEAD(saved); + unsigned int i; + int ret = 0; + + b_win = pbus_select_window(bus, res); + if (!b_win) + return -EINVAL; + + pci_dev_for_each_resource(pdev, r, i) { + if (i >= PCI_BRIDGE_RESOURCES) + break; + + if (exclude_bars & BIT(i)) + continue; + if (b_win != pbus_select_window(bus, r)) + continue; + + ret = add_to_list(&saved, pdev, r, 0, 0); + if (ret) + goto restore; + pci_release_resource(pdev, i); + } + + pci_resize_resource_set_size(pdev, resno, size); + + if (!bus->self) + goto out; + + down_read(&pci_bus_sem); + ret = pbus_reassign_bridge_resources(bus, res, &saved); + if (ret) + goto restore; + +out: + up_read(&pci_bus_sem); + free_list(&saved); + return ret; + +restore: /* Revert to the old configuration */ list_for_each_entry(dev_res, &saved, list) { struct resource *res = dev_res->res; @@ -2462,13 +2504,21 @@ int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) restore_dev_resource(dev_res); - pci_claim_resource(dev, i); - pci_setup_bridge(dev->subordinate); - } - up_read(&pci_bus_sem); - free_list(&saved); + ret = pci_claim_resource(dev, i); + if (ret) + continue; - return ret; + if (i < PCI_BRIDGE_RESOURCES) { + const char *res_name = pci_resource_name(dev, i); + + pci_update_resource(dev, i); + pci_info(dev, "%s %pR: old value restored\n", + res_name, res); + } + if (dev->subordinate) + pci_setup_bridge(dev->subordinate); + } + goto out; } void pci_assign_unassigned_bus_resources(struct pci_bus *bus) diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 53338eb45f038..25a64358feea9 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -446,8 +446,7 @@ static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev, return cmd & PCI_COMMAND_MEMORY; } -static void pci_resize_resource_set_size(struct pci_dev *dev, int resno, - int size) +void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size) { resource_size_t res_size = pci_rebar_size_to_bytes(size); struct resource *res = pci_resource_n(dev, resno); @@ -458,9 +457,9 @@ static void pci_resize_resource_set_size(struct pci_dev *dev, int resno, resource_set_size(res, res_size); } -int pci_resize_resource(struct pci_dev *dev, int resno, int size) +int pci_resize_resource(struct pci_dev *dev, int resno, int size, + int exclude_bars) { - struct resource *res = pci_resource_n(dev, resno); struct pci_host_bridge *host; int old, ret; u32 sizes; @@ -470,10 +469,6 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size) if (host->preserve_config) return -ENOTSUPP; - /* Make sure the resource isn't assigned before resizing it. */ - if (!(res->flags & IORESOURCE_UNSET)) - return -EBUSY; - if (pci_resize_is_memory_decoding_enabled(dev, resno)) return -EBUSY; @@ -492,19 +487,13 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size) if (ret) return ret; - pci_resize_resource_set_size(dev, resno, size); - - /* Check if the new config works by trying to assign everything. */ - if (dev->bus->self) { - ret = pbus_reassign_bridge_resources(dev->bus, res); - if (ret) - goto error_resize; - } + ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); + if (ret) + goto error_resize; return 0; error_resize: pci_rebar_set_size(dev, resno, old); - pci_resize_resource_set_size(dev, resno, old); return ret; } EXPORT_SYMBOL(pci_resize_resource); diff --git a/include/linux/pci.h b/include/linux/pci.h index 89f5a4290b6e2..505bf6e009b18 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1425,7 +1425,8 @@ static inline int pci_rebar_bytes_to_size(u64 bytes) } u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar); -int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); +int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size, + int exclude_bars); int pci_select_bars(struct pci_dev *dev, unsigned long flags); bool pci_device_is_present(struct pci_dev *pdev); void pci_ignore_hotplug(struct pci_dev *dev); From 2242c75b632864325fd22699e418d51f6b8ed87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 14 Jul 2026 16:02:34 -0400 Subject: [PATCH 2148/3297] PCI: Add kerneldoc for pci_resize_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d787018e2dfdc4c1331538e7a8717690d1b7c9b3 ] As pci_resize_resource() is meant to be used also outside of PCI core, document the interface with kerneldoc. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Alex Bennée # AVA, AMD GPU Link: https://patch.msgid.link/20251113162628.5946-8-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/setup-res.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 25a64358feea9..92a5af115cd63 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -457,6 +457,26 @@ void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size) resource_set_size(res, res_size); } +/** + * pci_resize_resource - reconfigure a Resizable BAR and resources + * @dev: the PCI device + * @resno: index of the BAR to be resized + * @size: new size as defined in the spec (0=1MB, 31=128TB) + * @exclude_bars: a mask of BARs that should not be released + * + * Reconfigure @resno to @size and re-run resource assignment algorithm + * with the new size. + * + * Prior to resize, release @dev resources that share a bridge window with + * @resno. This unpins the bridge window resource to allow changing it. + * + * The caller may prevent releasing a particular BAR by providing + * @exclude_bars mask, but this may result in the resize operation failing + * due to insufficient space. + * + * Return: 0 on success, or negative on error. In case of an error, the + * resources are restored to their original places. + */ int pci_resize_resource(struct pci_dev *dev, int resno, int size, int exclude_bars) { From f33837a75447329f716e0a3d886da1a2a956fe23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 14 Jul 2026 16:02:35 -0400 Subject: [PATCH 2149/3297] PCI: Move Resizable BAR code to rebar.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9f71938cd77f32a448f40a288e409eca60e55486 ] For lack of a better place to put it, Resizable BAR code has been placed inside pci.c and setup-res.c that do not use it for anything. Upcoming changes are going to add more Resizable BAR related functions, increasing the code size. As pci.c is huge as is, move the Resizable BAR related code and the BAR resize code from setup-res.c to rebar.c. Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Reviewed-by: Christian König Link: https://patch.msgid.link/20251113180053.27944-2-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/pci/pci.rst | 3 + drivers/pci/Makefile | 2 +- drivers/pci/pci.c | 149 ---------------- drivers/pci/pci.h | 1 + drivers/pci/rebar.c | 247 +++++++++++++++++++++++++++ drivers/pci/setup-res.c | 85 --------- 6 files changed, 252 insertions(+), 235 deletions(-) create mode 100644 drivers/pci/rebar.c diff --git a/Documentation/driver-api/pci/pci.rst b/Documentation/driver-api/pci/pci.rst index 59d86e8271986..99a1bbaaec5d0 100644 --- a/Documentation/driver-api/pci/pci.rst +++ b/Documentation/driver-api/pci/pci.rst @@ -37,6 +37,9 @@ PCI Support Library .. kernel-doc:: drivers/pci/slot.c :export: +.. kernel-doc:: drivers/pci/rebar.c + :export: + .. kernel-doc:: drivers/pci/rom.c :export: diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 67647f1880fb8..f3c81c892786e 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \ remove.o pci.o pci-driver.o search.o \ - rom.o setup-res.o irq.o vpd.o \ + rebar.o rom.o setup-res.o irq.o vpd.o \ setup-bus.o vc.o mmap.o devres.o obj-$(CONFIG_PCI) += msi/ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6db59bd452d9a..439045ab69319 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1823,32 +1823,6 @@ static void pci_restore_config_space(struct pci_dev *pdev) } } -static void pci_restore_rebar_state(struct pci_dev *pdev) -{ - unsigned int pos, nbars, i; - u32 ctrl; - - pos = pdev->rebar_cap; - if (!pos) - return; - - pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); - - for (i = 0; i < nbars; i++, pos += 8) { - struct resource *res; - int bar_idx, size; - - pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; - res = pci_resource_n(pdev, bar_idx); - size = pci_rebar_bytes_to_size(resource_size(res)); - ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); - pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); - } -} - /** * pci_restore_state - Restore the saved state of a PCI device * @dev: PCI device that we're dealing with @@ -3681,129 +3655,6 @@ void pci_acs_init(struct pci_dev *dev) dev->acs_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); } -void pci_rebar_init(struct pci_dev *pdev) -{ - pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); -} - -/** - * pci_rebar_find_pos - find position of resize ctrl reg for BAR - * @pdev: PCI device - * @bar: BAR to find - * - * Helper to find the position of the ctrl register for a BAR. - * Returns -ENOTSUPP if resizable BARs are not supported at all. - * Returns -ENOENT if no ctrl register for the BAR could be found. - */ -static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) -{ - unsigned int pos, nbars, i; - u32 ctrl; - - if (pci_resource_is_iov(bar)) { - pos = pci_iov_vf_rebar_cap(pdev); - bar = pci_resource_num_to_vf_bar(bar); - } else { - pos = pdev->rebar_cap; - } - - if (!pos) - return -ENOTSUPP; - - pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); - - for (i = 0; i < nbars; i++, pos += 8) { - int bar_idx; - - pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl); - if (bar_idx == bar) - return pos; - } - - return -ENOENT; -} - -/** - * pci_rebar_get_possible_sizes - get possible sizes for BAR - * @pdev: PCI device - * @bar: BAR to query - * - * Get the possible sizes of a resizable BAR as bitmask defined in the spec - * (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable. - */ -u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar) -{ - int pos; - u32 cap; - - pos = pci_rebar_find_pos(pdev, bar); - if (pos < 0) - return 0; - - pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap); - cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap); - - /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */ - if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f && - bar == 0 && cap == 0x700) - return 0x3f00; - - return cap; -} -EXPORT_SYMBOL(pci_rebar_get_possible_sizes); - -/** - * pci_rebar_get_current_size - get the current size of a BAR - * @pdev: PCI device - * @bar: BAR to set size to - * - * Read the size of a BAR from the resizable BAR config. - * Returns size if found or negative error code. - */ -int pci_rebar_get_current_size(struct pci_dev *pdev, int bar) -{ - int pos; - u32 ctrl; - - pos = pci_rebar_find_pos(pdev, bar); - if (pos < 0) - return pos; - - pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl); -} - -/** - * pci_rebar_set_size - set a new size for a BAR - * @pdev: PCI device - * @bar: BAR to set size to - * @size: new size as defined in the spec (0=1MB, 31=128TB) - * - * Set the new size of a BAR as defined in the spec. - * Returns zero if resizing was successful, error code otherwise. - */ -int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) -{ - int pos; - u32 ctrl; - - pos = pci_rebar_find_pos(pdev, bar); - if (pos < 0) - return pos; - - pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); - pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); - - if (pci_resource_is_iov(bar)) - pci_iov_resource_set_size(pdev, bar, size); - - return 0; -} - /** * pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5336a4b5b4567..8acded5921509 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1042,6 +1042,7 @@ static inline int acpi_get_rc_resources(struct device *dev, const char *hid, #endif void pci_rebar_init(struct pci_dev *pdev); +void pci_restore_rebar_state(struct pci_dev *pdev); int pci_rebar_get_current_size(struct pci_dev *pdev, int bar); int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size); static inline u64 pci_rebar_size_to_bytes(int size) diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c new file mode 100644 index 0000000000000..f6ed7e4893a77 --- /dev/null +++ b/drivers/pci/rebar.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI Resizable BAR Extended Capability handling. + */ + +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +void pci_rebar_init(struct pci_dev *pdev) +{ + pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); +} + +/** + * pci_rebar_find_pos - find position of resize ctrl reg for BAR + * @pdev: PCI device + * @bar: BAR to find + * + * Helper to find the position of the ctrl register for a BAR. + * Returns -ENOTSUPP if resizable BARs are not supported at all. + * Returns -ENOENT if no ctrl register for the BAR could be found. + */ +static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) +{ + unsigned int pos, nbars, i; + u32 ctrl; + + if (pci_resource_is_iov(bar)) { + pos = pci_iov_vf_rebar_cap(pdev); + bar = pci_resource_num_to_vf_bar(bar); + } else { + pos = pdev->rebar_cap; + } + + if (!pos) + return -ENOTSUPP; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); + + for (i = 0; i < nbars; i++, pos += 8) { + int bar_idx; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl); + if (bar_idx == bar) + return pos; + } + + return -ENOENT; +} + +/** + * pci_rebar_get_possible_sizes - get possible sizes for BAR + * @pdev: PCI device + * @bar: BAR to query + * + * Get the possible sizes of a resizable BAR as bitmask defined in the spec + * (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable. + */ +u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar) +{ + int pos; + u32 cap; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return 0; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap); + cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap); + + /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */ + if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f && + bar == 0 && cap == 0x700) + return 0x3f00; + + return cap; +} +EXPORT_SYMBOL(pci_rebar_get_possible_sizes); + +/** + * pci_rebar_get_current_size - get the current size of a BAR + * @pdev: PCI device + * @bar: BAR to set size to + * + * Read the size of a BAR from the resizable BAR config. + * Returns size if found or negative error code. + */ +int pci_rebar_get_current_size(struct pci_dev *pdev, int bar) +{ + int pos; + u32 ctrl; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return pos; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl); +} + +/** + * pci_rebar_set_size - set a new size for a BAR + * @pdev: PCI device + * @bar: BAR to set size to + * @size: new size as defined in the spec (0=1MB, 31=128TB) + * + * Set the new size of a BAR as defined in the spec. + * Returns zero if resizing was successful, error code otherwise. + */ +int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) +{ + int pos; + u32 ctrl; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return pos; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; + ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); + pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); + + if (pci_resource_is_iov(bar)) + pci_iov_resource_set_size(pdev, bar, size); + + return 0; +} + +void pci_restore_rebar_state(struct pci_dev *pdev) +{ + unsigned int pos, nbars, i; + u32 ctrl; + + pos = pdev->rebar_cap; + if (!pos) + return; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); + + for (i = 0; i < nbars; i++, pos += 8) { + struct resource *res; + int bar_idx, size; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; + res = pci_resource_n(pdev, bar_idx); + size = pci_rebar_bytes_to_size(resource_size(res)); + ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; + ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); + pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); + } +} + +static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev, + int resno) +{ + u16 cmd; + + if (pci_resource_is_iov(resno)) + return pci_iov_is_memory_decoding_enabled(dev); + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + + return cmd & PCI_COMMAND_MEMORY; +} + +void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size) +{ + resource_size_t res_size = pci_rebar_size_to_bytes(size); + struct resource *res = pci_resource_n(dev, resno); + + if (pci_resource_is_iov(resno)) + res_size *= pci_sriov_get_totalvfs(dev); + + resource_set_size(res, res_size); +} + +/** + * pci_resize_resource - reconfigure a Resizable BAR and resources + * @dev: the PCI device + * @resno: index of the BAR to be resized + * @size: new size as defined in the spec (0=1MB, 31=128TB) + * @exclude_bars: a mask of BARs that should not be released + * + * Reconfigure @resno to @size and re-run resource assignment algorithm + * with the new size. + * + * Prior to resize, release @dev resources that share a bridge window with + * @resno. This unpins the bridge window resource to allow changing it. + * + * The caller may prevent releasing a particular BAR by providing + * @exclude_bars mask, but this may result in the resize operation failing + * due to insufficient space. + * + * Return: 0 on success, or negative on error. In case of an error, the + * resources are restored to their original places. + */ +int pci_resize_resource(struct pci_dev *dev, int resno, int size, + int exclude_bars) +{ + struct pci_host_bridge *host; + int old, ret; + u32 sizes; + + /* Check if we must preserve the firmware's resource assignment */ + host = pci_find_host_bridge(dev->bus); + if (host->preserve_config) + return -ENOTSUPP; + + if (pci_resize_is_memory_decoding_enabled(dev, resno)) + return -EBUSY; + + sizes = pci_rebar_get_possible_sizes(dev, resno); + if (!sizes) + return -ENOTSUPP; + + if (!(sizes & BIT(size))) + return -EINVAL; + + old = pci_rebar_get_current_size(dev, resno); + if (old < 0) + return old; + + ret = pci_rebar_set_size(dev, resno, size); + if (ret) + return ret; + + ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); + if (ret) + goto error_resize; + return 0; + +error_resize: + pci_rebar_set_size(dev, resno, old); + return ret; +} +EXPORT_SYMBOL(pci_resize_resource); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 92a5af115cd63..1769ba960197e 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -433,91 +433,6 @@ int pci_release_resource(struct pci_dev *dev, int resno) } EXPORT_SYMBOL(pci_release_resource); -static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev, - int resno) -{ - u16 cmd; - - if (pci_resource_is_iov(resno)) - return pci_iov_is_memory_decoding_enabled(dev); - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - - return cmd & PCI_COMMAND_MEMORY; -} - -void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size) -{ - resource_size_t res_size = pci_rebar_size_to_bytes(size); - struct resource *res = pci_resource_n(dev, resno); - - if (pci_resource_is_iov(resno)) - res_size *= pci_sriov_get_totalvfs(dev); - - resource_set_size(res, res_size); -} - -/** - * pci_resize_resource - reconfigure a Resizable BAR and resources - * @dev: the PCI device - * @resno: index of the BAR to be resized - * @size: new size as defined in the spec (0=1MB, 31=128TB) - * @exclude_bars: a mask of BARs that should not be released - * - * Reconfigure @resno to @size and re-run resource assignment algorithm - * with the new size. - * - * Prior to resize, release @dev resources that share a bridge window with - * @resno. This unpins the bridge window resource to allow changing it. - * - * The caller may prevent releasing a particular BAR by providing - * @exclude_bars mask, but this may result in the resize operation failing - * due to insufficient space. - * - * Return: 0 on success, or negative on error. In case of an error, the - * resources are restored to their original places. - */ -int pci_resize_resource(struct pci_dev *dev, int resno, int size, - int exclude_bars) -{ - struct pci_host_bridge *host; - int old, ret; - u32 sizes; - - /* Check if we must preserve the firmware's resource assignment */ - host = pci_find_host_bridge(dev->bus); - if (host->preserve_config) - return -ENOTSUPP; - - if (pci_resize_is_memory_decoding_enabled(dev, resno)) - return -EBUSY; - - sizes = pci_rebar_get_possible_sizes(dev, resno); - if (!sizes) - return -ENOTSUPP; - - if (!(sizes & BIT(size))) - return -EINVAL; - - old = pci_rebar_get_current_size(dev, resno); - if (old < 0) - return old; - - ret = pci_rebar_set_size(dev, resno, size); - if (ret) - return ret; - - ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); - if (ret) - goto error_resize; - return 0; - -error_resize: - pci_rebar_set_size(dev, resno, old); - return ret; -} -EXPORT_SYMBOL(pci_resize_resource); - int pci_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; From 73cc54326de459192d4bfd332cd78eb7cde1e7ee Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Tue, 14 Jul 2026 16:02:36 -0400 Subject: [PATCH 2150/3297] PCI: Skip Resizable BAR restore on read error [ Upstream commit ee7471fe968d210939be9046089a924cd23c8c3b ] pci_restore_rebar_state() uses the Resizable BAR Control register to decide how many BARs to restore (nbars) and which BAR each iteration addresses (bar_idx). When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7, past the spec's valid ranges for both fields. pci_resource_n() then returns an unrelated resource slot, whose size is used to derive a nonsensical value written back to the Resizable BAR Control register. Bail out if any Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless. Fixes: d3252ace0bc6 ("PCI: Restore resized BAR state on resume") Signed-off-by: Marco Nenciarini Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/666cac19b5daa0ab0e0ab64454e76b4d24465dbd.1776429882.git.mnencia@kcore.it Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/rebar.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c index f6ed7e4893a77..22853cf538fc1 100644 --- a/drivers/pci/rebar.c +++ b/drivers/pci/rebar.c @@ -145,6 +145,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev) return; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { @@ -152,6 +155,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev) int bar_idx, size; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; res = pci_resource_n(pdev, bar_idx); size = pci_rebar_bytes_to_size(resource_size(res)); From 48323ebaeeeedadd64ee8ffee65b2e6b094acf32 Mon Sep 17 00:00:00 2001 From: William Hansen-Baird Date: Tue, 14 Jul 2026 20:07:34 -0400 Subject: [PATCH 2151/3297] staging: rtl8723bs: core: move constants to right side in comparison [ Upstream commit cf0f2680c30d4b438a5e84b73dc70be405936b54 ] Move constants to right side in if-statement conditions. Signed-off-by: William Hansen-Baird Link: https://patch.msgid.link/20251224100329.762141-3-william.hansen.baird@gmail.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_security.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 9ecd00f309461..1095f56b8eb90 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -1012,7 +1012,7 @@ static int rtw_get_cipher_info(struct wlan_network *pnetwork) pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12); if (pbuf && (wpa_ielen > 0)) { - if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) { + if (rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x; @@ -1022,7 +1022,7 @@ static int rtw_get_cipher_info(struct wlan_network *pnetwork) pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12); if (pbuf && (wpa_ielen > 0)) { - if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) { + if (rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x; diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index cef628f1352a3..5406f60dd31c6 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -1495,7 +1495,7 @@ void rtw_sec_restore_wep_key(struct adapter *adapter) struct security_priv *securitypriv = &(adapter->securitypriv); signed int keyid; - if ((_WEP40_ == securitypriv->dot11PrivacyAlgrthm) || (_WEP104_ == securitypriv->dot11PrivacyAlgrthm)) { + if ((securitypriv->dot11PrivacyAlgrthm == _WEP40_) || (securitypriv->dot11PrivacyAlgrthm == _WEP104_)) { for (keyid = 0; keyid < 4; keyid++) { if (securitypriv->key_mask & BIT(keyid)) { if (keyid == securitypriv->dot11PrivacyKeyIndex) From 5d76bc296bb5821f89ec2b3a78c2a36e5a436cde Mon Sep 17 00:00:00 2001 From: Nikolay Kulikov Date: Tue, 14 Jul 2026 20:07:35 -0400 Subject: [PATCH 2152/3297] staging: rtl8723bs: fix spaces around binary operators [ Upstream commit 0c9d1b56f9af0762a5be5118e1bb962f23784dc4 ] Add missing spaces and fix line length to comply with kernel coding style. Signed-off-by: Nikolay Kulikov Reviewed-by: Ethan Tidmore Link: https://patch.msgid.link/20260221172751.52329-1-nikolayof23@gmail.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8723bs/core/rtw_ieee80211.c | 78 ++++++++++--------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 1095f56b8eb90..0a8247d9207b9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -456,10 +456,10 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwis return _FAIL; } - if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) || - (memcmp(wpa_ie+2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) { + if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) || + (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) || + (memcmp(wpa_ie + 2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) return _FAIL; - } pos = wpa_ie; @@ -519,7 +519,7 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwi return _FAIL; } - if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2))) + if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2))) return _FAIL; pos = rsn_ie; @@ -587,18 +587,18 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len) while (cnt < in_len) { authmode = in_ie[cnt]; - /* if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY) */ - if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && (!memcmp(&in_ie[cnt+6], wapi_oui1, 4) || - !memcmp(&in_ie[cnt+6], wapi_oui2, 4))) { + if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && + (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) || + !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) { if (wapi_ie) - memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt+1]+2); + memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); if (wapi_len) - *wapi_len = in_ie[cnt+1]+2; + *wapi_len = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt+1]+2; /* get next */ + cnt += in_ie[cnt + 1] + 2; /* get next */ } else { - cnt += in_ie[cnt+1]+2; /* get next */ + cnt += in_ie[cnt + 1] + 2; /* get next */ } } @@ -622,9 +622,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie while (cnt < in_len) { authmode = in_ie[cnt]; - if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) { + if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && + (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { if (wpa_ie) - memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2); + memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); *wpa_len = in_ie[cnt + 1] + 2; cnt += in_ie[cnt + 1] + 2; /* get next */ @@ -633,10 +634,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie if (rsn_ie) memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); - *rsn_len = in_ie[cnt+1]+2; - cnt += in_ie[cnt+1]+2; /* get next */ + *rsn_len = in_ie[cnt + 1] + 2; + cnt += in_ie[cnt + 1] + 2; /* get next */ } else { - cnt += in_ie[cnt+1]+2; /* get next */ + cnt += in_ie[cnt + 1] + 2; /* get next */ } } } @@ -668,20 +669,20 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) while (cnt < in_len) { eid = in_ie[cnt]; - if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], wps_oui, 4))) { + if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { wpsie_ptr = &in_ie[cnt]; if (wps_ie) - memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2); + memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); if (wps_ielen) - *wps_ielen = in_ie[cnt+1]+2; + *wps_ielen = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt+1]+2; + cnt += in_ie[cnt + 1] + 2; break; } - cnt += in_ie[cnt+1]+2; /* goto next */ + cnt += in_ie[cnt + 1] + 2; /* goto next */ } return wpsie_ptr; @@ -759,12 +760,12 @@ u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 if (attr_ptr && attr_len) { if (buf_content) - memcpy(buf_content, attr_ptr+4, attr_len-4); + memcpy(buf_content, attr_ptr + 4, attr_len - 4); if (len_content) - *len_content = attr_len-4; + *len_content = attr_len - 4; - return attr_ptr+4; + return attr_ptr + 4; } return NULL; @@ -1009,20 +1010,25 @@ static int rtw_get_cipher_info(struct wlan_network *pnetwork) int group_cipher = 0, pairwise_cipher = 0, is8021x = 0; int ret = _FAIL; - pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12); + pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], + &wpa_ielen, + pnetwork->network.ie_length - 12); if (pbuf && (wpa_ielen > 0)) { - if (rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { + if (rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher, + &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x; ret = _SUCCESS; } } else { - pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12); + pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, + pnetwork->network.ie_length - 12); if (pbuf && (wpa_ielen > 0)) { - if (rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { + if (rtw_parse_wpa2_ie(pbuf, wpa_ielen + 2, &group_cipher, + &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x; @@ -1091,21 +1097,21 @@ u16 rtw_mcs_rate(u8 bw_40MHz, u8 short_GI, unsigned char *MCS_rate) u16 max_rate = 0; if (MCS_rate[0] & BIT(7)) - max_rate = (bw_40MHz) ? ((short_GI)?1500:1350):((short_GI)?722:650); + max_rate = (bw_40MHz) ? ((short_GI) ? 1500 : 1350) : ((short_GI) ? 722 : 650); else if (MCS_rate[0] & BIT(6)) - max_rate = (bw_40MHz) ? ((short_GI)?1350:1215):((short_GI)?650:585); + max_rate = (bw_40MHz) ? ((short_GI) ? 1350 : 1215) : ((short_GI) ? 650 : 585); else if (MCS_rate[0] & BIT(5)) - max_rate = (bw_40MHz) ? ((short_GI)?1200:1080):((short_GI)?578:520); + max_rate = (bw_40MHz) ? ((short_GI) ? 1200 : 1080) : ((short_GI) ? 578 : 520); else if (MCS_rate[0] & BIT(4)) - max_rate = (bw_40MHz) ? ((short_GI)?900:810):((short_GI)?433:390); + max_rate = (bw_40MHz) ? ((short_GI) ? 900 : 810) : ((short_GI) ? 433 : 390); else if (MCS_rate[0] & BIT(3)) - max_rate = (bw_40MHz) ? ((short_GI)?600:540):((short_GI)?289:260); + max_rate = (bw_40MHz) ? ((short_GI) ? 600 : 540) : ((short_GI) ? 289 : 260); else if (MCS_rate[0] & BIT(2)) - max_rate = (bw_40MHz) ? ((short_GI)?450:405):((short_GI)?217:195); + max_rate = (bw_40MHz) ? ((short_GI) ? 450 : 405) : ((short_GI) ? 217 : 195); else if (MCS_rate[0] & BIT(1)) - max_rate = (bw_40MHz) ? ((short_GI)?300:270):((short_GI)?144:130); + max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130); else if (MCS_rate[0] & BIT(0)) - max_rate = (bw_40MHz) ? ((short_GI)?150:135):((short_GI)?72:65); + max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65); return max_rate; } From 4b51ee8a40fe47864197d73cc02b191de7a6b072 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Tue, 14 Jul 2026 20:07:36 -0400 Subject: [PATCH 2153/3297] staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr() [ Upstream commit 1463ca3ec6601cbb097d8d87dbf5dcf1cb86a344 ] Three IE/attribute parsing functions have missing bounds checks. rtw_get_sec_ie() and rtw_get_wapi_ie() iterate over a raw IE buffer without verifying that the header bytes (tag + length) are within the remaining buffer before reading them. Additionally, rtw_get_sec_ie() compares the 4-byte WPA OUI at cnt+2 without checking that at least 6 bytes remain, and rtw_get_wapi_ie() compares a 4-byte WAPI OUI at cnt+6 without checking that at least 10 bytes remain. rtw_get_wps_attr() reads wps_ie[0] and wps_ie+2 unconditionally at entry, before verifying that wps_ielen is large enough to contain the 6-byte WPS IE header (element_id + length + 4-byte OUI). Inside the attribute loop, get_unaligned_be16() is called on attr_ptr and attr_ptr+2 without checking that 4 bytes remain in the buffer. Add a cnt+2 bounds check before each loop body in rtw_get_sec_ie() and rtw_get_wapi_ie(), guard each multi-byte comparison with a minimum IE length requirement, add a wps_ielen < 6 early return in rtw_get_wps_attr(), and add a 4-byte bounds check in its inner loop. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260522004531.1038924-8-hossu.alexandru@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 0a8247d9207b9..01ab7fb5e5bc6 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -585,9 +585,14 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len) cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); while (cnt < in_len) { + if (cnt + 2 > in_len) + break; + if (cnt + 2 + in_ie[cnt + 1] > in_len) + break; authmode = in_ie[cnt]; if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && + in_ie[cnt + 1] >= 8 && (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) || !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) { if (wapi_ie) @@ -620,9 +625,14 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); while (cnt < in_len) { + if (cnt + 2 > in_len) + break; + if (cnt + 2 + in_ie[cnt + 1] > in_len) + break; authmode = in_ie[cnt]; if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && + in_ie[cnt + 1] >= 4 && (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { if (wpa_ie) memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); @@ -707,6 +717,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att if (len_attr) *len_attr = 0; + if (wps_ielen < 6) + return attr_ptr; + if ((wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC) || (memcmp(wps_ie + 2, wps_oui, 4))) { return attr_ptr; @@ -717,6 +730,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att while (attr_ptr - wps_ie < wps_ielen) { /* 4 = 2(Attribute ID) + 2(Length) */ + if (attr_ptr + 4 > wps_ie + wps_ielen) + break; u16 attr_id = get_unaligned_be16(attr_ptr); u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); u16 attr_len = attr_data_len + 4; From 49cd5ac6de8de39a14ead609bb552d372d5602cd Mon Sep 17 00:00:00 2001 From: Giovanni Cabiddu Date: Tue, 14 Jul 2026 20:47:11 -0400 Subject: [PATCH 2154/3297] crypto: qat - fix VF2PF work teardown race in adf_disable_sriov() [ Upstream commit 277281c10c63791067d24d421f7c43a15faa9096 ] The VF2PF interrupt handler queues PF-side response work that stores a raw pointer to per-VF state (struct adf_accel_vf_info). Currently, adf_disable_sriov() destroys per-VF mutexes and frees vf_info without stopping new VF2PF work or waiting for in-flight workers to complete. A concurrently scheduled or already queued worker can then dereference freed memory. This manifests as a use-after-free when KASAN is enabled: BUG: KASAN: null-ptr-deref in mutex_lock+0x76/0xe0 Write of size 8 at addr 0000000000000260 by task kworker/24:2/... Workqueue: qat_pf2vf_resp_wq adf_iov_send_resp [intel_qat] Call Trace: kasan_report+0x119/0x140 mutex_lock+0x76/0xe0 adf_gen4_pfvf_send+0xd4/0x1f0 [intel_qat] adf_recv_and_handle_vf2pf_msg+0x290/0x360 [intel_qat] adf_iov_send_resp+0x8c/0xe0 [intel_qat] process_one_work+0x6ac/0xfd0 worker_thread+0x4dd/0xd30 kthread+0x326/0x410 ret_from_fork+0x33b/0x670 Add a PF-local flag, vf2pf_disabled, that gates work queueing, worker processing, and interrupt re-enabling during teardown. Set this flag atomically with the hardware interrupt mask inside adf_disable_all_vf2pf_interrupts(). After masking, synchronize the AE cluster MSI-X interrupt and flush the PF response workqueue before tearing down per-VF locks and state so all in-flight work completes before vf_info is destroyed. Introduce adf_enable_all_vf2pf_interrupts() to clear the flag and unmask all VF2PF interrupts under the same lock when SR-IOV is re-enabled. This ensures the software flag and hardware state transition atomically on both the enable and disable paths. Cc: stable@vger.kernel.org Fixes: ed8ccaef52fa ("crypto: qat - Add support for SRIOV") Signed-off-by: Giovanni Cabiddu Reviewed-by: Ahsan Atta Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../intel/qat/qat_common/adf_accel_devices.h | 2 + .../intel/qat/qat_common/adf_common_drv.h | 2 + drivers/crypto/intel/qat/qat_common/adf_isr.c | 39 +++++++++++++++++++ .../crypto/intel/qat/qat_common/adf_sriov.c | 20 +++++++++- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index 9fe3239f01143..41f6db98cd007 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -472,6 +472,8 @@ struct adf_accel_dev { struct { /* protects VF2PF interrupts access */ spinlock_t vf2pf_ints_lock; + /* prevents VF2PF handling from racing with VF state teardown */ + bool vf2pf_disabled; /* vf_info is non-zero when SR-IOV is init'ed */ struct adf_accel_vf_info *vf_info; } pf; diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index 63e1f1c8358e2..9d65b13ed048d 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -118,6 +118,7 @@ void qat_comp_alg_callback(void *resp); int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_isr_resource_free(struct adf_accel_dev *accel_dev); +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev); int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev); @@ -191,6 +192,7 @@ int adf_sriov_configure(struct pci_dev *pdev, int numvfs); void adf_disable_sriov(struct adf_accel_dev *accel_dev); void adf_reenable_sriov(struct adf_accel_dev *accel_dev); void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask); +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs); void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_vf2pf_msg(struct adf_accel_dev *accel_dev, u32 vf_nr); diff --git a/drivers/crypto/intel/qat/qat_common/adf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_isr.c index 12e5656136610..7683f04adc582 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_isr.c +++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c @@ -62,6 +62,23 @@ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + if (!READ_ONCE(accel_dev->pf.vf2pf_disabled)) + GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); + spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); +} + +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs) +{ + void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev); + unsigned long flags; + u32 vf_mask; + + vf_mask = BIT_ULL(num_vfs) - 1; + if (!vf_mask) + return; + + spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, false); GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -72,6 +89,7 @@ void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true); GET_PFVF_OPS(accel_dev)->disable_all_vf2pf_interrupts(pmisc_addr); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -174,6 +192,27 @@ static irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr) return IRQ_NONE; } +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev) +{ + struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + u32 num_entries = pci_dev_info->msix_entries.num_entries; + struct adf_irq *irqs = pci_dev_info->msix_entries.irqs; + u32 irq_idx; + int irq; + + if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !irqs) + return; + + irq_idx = num_entries > 1 ? hw_data->num_banks : 0; + if (irq_idx >= num_entries || !irqs[irq_idx].enabled) + return; + + irq = pci_irq_vector(pci_dev_info->pci_dev, hw_data->num_banks); + if (irq > 0) + synchronize_irq(irq); +} + static void adf_free_irqs(struct adf_accel_dev *accel_dev) { struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c index e9f825368b7ea..ba1eb368fd65c 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c @@ -26,6 +26,9 @@ static void adf_iov_send_resp(struct work_struct *work) u32 vf_nr = vf_info->vf_nr; bool ret; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + goto out; + mutex_lock(&vf_info->pfvf_mig_lock); ret = adf_recv_and_handle_vf2pf_msg(accel_dev, vf_nr); if (ret) @@ -33,13 +36,18 @@ static void adf_iov_send_resp(struct work_struct *work) adf_enable_vf2pf_interrupts(accel_dev, 1 << vf_nr); mutex_unlock(&vf_info->pfvf_mig_lock); +out: kfree(pf2vf_resp); } void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) { + struct adf_accel_dev *accel_dev = vf_info->accel_dev; struct adf_pf2vf_resp *pf2vf_resp; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + return; + pf2vf_resp = kzalloc(sizeof(*pf2vf_resp), GFP_ATOMIC); if (!pf2vf_resp) return; @@ -49,6 +57,12 @@ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) queue_work(pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work); } +static void adf_flush_pf2vf_resp_wq(void) +{ + if (pf2vf_resp_wq) + flush_workqueue(pf2vf_resp_wq); +} + static int adf_enable_sriov(struct adf_accel_dev *accel_dev) { struct pci_dev *pdev = accel_to_pci_dev(accel_dev); @@ -75,7 +89,7 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) hw_data->configure_iov_threads(accel_dev, true); /* Enable VF to PF interrupts for all VFs */ - adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1); + adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs); /* Do not enable SR-IOV if already enabled */ if (pci_num_vf(pdev)) @@ -259,8 +273,10 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev) if (!test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) pci_disable_sriov(accel_to_pci_dev(accel_dev)); - /* Disable VF to PF interrupts */ + /* Block VF2PF work and disable VF to PF interrupts */ adf_disable_all_vf2pf_interrupts(accel_dev); + adf_isr_sync_ae_cluster(accel_dev); + adf_flush_pf2vf_resp_wq(); /* Clear Valid bits in AE Thread to PCIe Function Mapping */ if (hw_data->configure_iov_threads) From 4278953ff0cd42364499da5e378882523ad27d42 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 15 Jul 2026 07:40:27 -0400 Subject: [PATCH 2155/3297] media: nxp: imx8-isi: use devm_pm_runtime_enable() to simplify code [ Upstream commit 078161dd44d6f848a62a473206d69025607736ec ] Use devm_pm_runtime_enable() to simplify code. Change to use dev_err_probe() because previous goto change to return. No functional change. Signed-off-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260116-cam_cleanup-v4-2-29ce01640443@nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Stable-dep-of: b670bf89824e ("media: nxp: imx8-isi: Fix use-after-free on remove") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../media/platform/nxp/imx8-isi/imx8-isi-core.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index adc8d9960bf0d..da73a8cee0540 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -488,13 +488,14 @@ static int mxc_isi_probe(struct platform_device *pdev) dma_size = isi->pdata->has_36bit_dma ? 36 : 32; dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size)); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = mxc_isi_crossbar_init(isi); - if (ret) { - dev_err(dev, "Failed to initialize crossbar: %d\n", ret); - goto err_pm; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize crossbar\n"); for (i = 0; i < isi->pdata->num_channels; ++i) { ret = mxc_isi_pipe_init(isi, i); @@ -517,8 +518,7 @@ static int mxc_isi_probe(struct platform_device *pdev) err_xbar: mxc_isi_crossbar_cleanup(&isi->crossbar); -err_pm: - pm_runtime_disable(isi->dev); + return ret; } @@ -537,8 +537,6 @@ static void mxc_isi_remove(struct platform_device *pdev) mxc_isi_crossbar_cleanup(&isi->crossbar); mxc_isi_v4l2_cleanup(isi); - - pm_runtime_disable(isi->dev); } static const struct of_device_id mxc_isi_of_match[] = { From ef382a6baf0a95cf199fdf6bba2fd08e58b0a249 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Wed, 15 Jul 2026 07:40:28 -0400 Subject: [PATCH 2156/3297] media: nxp: imx8-isi: Fix use-after-free on remove [ Upstream commit b670bf89824ede5d07d20bb9bfbafb754846081d ] KASAN reports a slab-use-after-free in __media_entity_remove_link() during rmmod of imx8_isi: BUG: KASAN: slab-use-after-free in __media_entity_remove_link+0x608/0x650 Read of size 2 at addr ffff0000d47cb02a by task rmmod/724 Call trace: __media_entity_remove_link+0x608/0x650 __media_entity_remove_links+0x78/0x144 __media_device_unregister_entity+0x150/0x280 media_device_unregister_entity+0x48/0x68 v4l2_device_unregister_subdev+0x158/0x300 v4l2_async_unbind_subdev_one+0x22c/0x358 v4l2_async_nf_unbind_all_subdevs+0xfc/0x1c0 v4l2_async_nf_unregister+0x5c/0x14c mxc_isi_remove+0x124/0x2a0 [imx8_isi] Allocated by task 249: __kmalloc_noprof+0x27c/0x690 mxc_isi_crossbar_init+0x22c/0x560 [imx8_isi] Freed by task 724: kfree+0x1e4/0x5b0 mxc_isi_crossbar_cleanup+0x34/0x80 [imx8_isi] mxc_isi_remove+0x11c/0x2a0 [imx8_isi] The problem is that mxc_isi_remove() calls mxc_isi_crossbar_cleanup() before mxc_isi_v4l2_cleanup(). The crossbar cleanup frees the media entity pads, but the subsequent v4l2 cleanup still tries to remove media links that reference those pads. Fix this by calling mxc_isi_v4l2_cleanup() before mxc_isi_crossbar_cleanup() to ensure all media entities are properly unregistered while the pads are still valid. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-2-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index da73a8cee0540..60fed2fe200dc 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -535,8 +535,8 @@ static void mxc_isi_remove(struct platform_device *pdev) mxc_isi_pipe_cleanup(pipe); } - mxc_isi_crossbar_cleanup(&isi->crossbar); mxc_isi_v4l2_cleanup(isi); + mxc_isi_crossbar_cleanup(&isi->crossbar); } static const struct of_device_id mxc_isi_of_match[] = { From 6d0eeebe22ba7d1ceebd824bc519432efab0419d Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 15 Jul 2026 17:13:37 -0400 Subject: [PATCH 2157/3297] Bluetooth: 6lowpan: fix cyclic locking warning on netdev unregister [ Upstream commit 9707a015fe8f3ba8ec7c270f3b2b8efb38823d6b ] 6lowpan.c has theoretically conflicting lock orderings, which lockdep complains about: a) rtnl_lock > hdev->workqueue from 6lowpan.c:delete_netdev -> rtnl_lock -> device_del -> put_device(parent) -> hci_release_dev -> destroy_workqueue b) hdev->workqueue > l2cap_conn->lock > chan->lock > rtnl_lock from hci_rx_work -> 6lowpan.c:chan_ready_cb -> lowpan_register_netdev, ifup -> rtnl_lock Actual deadlock appears not possible, as hci_rx_work is disabled and l2cap_conn flushed already on hdev unregister. Hence, do minimal thing to make lockdep happy by breaking chain a) by holding hdev refcount until after netdev put in 6lowpan.c. Fixes the lockdep complaint: WARNING: possible circular locking dependency detected. kworker/0:1/11 is trying to acquire lock: ffff8880023b3940 ((wq_completion)hci0#2){+.+.}-{0:0}, at: touch_wq_lockdep_map+0x8b/0x130 but task is already holding lock: ffffffff95e4f9c0 (rtnl_mutex){+.+.}-{4:4}, at: lowpan_unregister_netdev+0xd/0x30 Workqueue: events delete_netdev Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/6lowpan.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 35512e92c215e..33e0a17f42b07 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -760,13 +760,33 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) return chan; } +static void unregister_dev(struct lowpan_btle_dev *dev) +{ + struct hci_dev *hdev = READ_ONCE(dev->hdev); + + /* If netdev holds last reference to hci_dev (its parent device), this + * leads to theoretical cyclic locking on lowpan_unregister_netdev: + * + * rtnl_lock -> put_device(parent) -> hci_release_dev -> + * destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata -> + * chan_ready_cb -> ifup -> rtnl_lock + * + * However, hci_rx_work is disabled in hci_unregister_dev, so this + * should not occur. Make lockdep happy by postponing hdev release after + * netdev put. + */ + hci_dev_hold(hdev); + lowpan_unregister_netdev(dev->netdev); + hci_dev_put(hdev); +} + static void delete_netdev(struct work_struct *work) { struct lowpan_btle_dev *entry = container_of(work, struct lowpan_btle_dev, delete_netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); /* The entry pointer is deleted by the netdev destructor. */ } @@ -1249,6 +1269,7 @@ static void disconnect_devices(void) break; new_dev->netdev = entry->netdev; + new_dev->hdev = entry->hdev; INIT_LIST_HEAD(&new_dev->list); list_add_rcu(&new_dev->list, &devices); @@ -1260,7 +1281,7 @@ static void disconnect_devices(void) ifdown(entry->netdev); BT_DBG("Unregistering netdev %s %p", entry->netdev->name, entry->netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); kfree(entry); } } From 36da806f7fbaee56ad9e81859deec203f9728700 Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Wed, 15 Jul 2026 17:13:38 -0400 Subject: [PATCH 2158/3297] Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() [ Upstream commit 6fef032af0092ed5ccb767239a9ac1bc38c08a40 ] l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after release_sock(parent). Once the parent lock is dropped the newly enqueued child socket sk is reachable via the accept queue, so another task can accept and free it before the callback dereferences sk, resulting in a use-after-free. Rework the ->new_connection() op so the core, rather than the callback, owns the child channel's lifetime. The op now receives a pre-allocated new_chan and returns an errno instead of allocating and returning a channel. l2cap_new_connection() allocates the child channel and links it into the conn list via __l2cap_chan_add() before invoking the callback, so the conn-list reference keeps the channel alive once release_sock(parent) exposes the socket to other tasks. Channel configuration that was duplicated in l2cap_sock_init() and the various new_connection callbacks is consolidated into l2cap_chan_set_defaults(), which now inherits from the parent channel when one is supplied. Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c") Cc: stable@kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/l2cap.h | 10 ++-- net/bluetooth/6lowpan.c | 18 +----- net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++----- net/bluetooth/l2cap_sock.c | 103 ++++++++++++++++------------------ net/bluetooth/smp.c | 27 ++------- 5 files changed, 127 insertions(+), 109 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6578da0048fb4..265afda2e13bc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -620,7 +620,8 @@ struct l2cap_chan { struct l2cap_ops { char *name; - struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); + int (*new_connection)(struct l2cap_chan *chan, + struct l2cap_chan *new_chan); int (*recv) (struct l2cap_chan * chan, struct sk_buff *skb); void (*teardown) (struct l2cap_chan *chan, int err); @@ -885,9 +886,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) return (seq + 1) % (chan->tx_win_max + 1); } -static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan) +static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - return NULL; + return -EOPNOTSUPP; } static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb) @@ -964,7 +966,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); -void l2cap_chan_set_defaults(struct l2cap_chan *chan); +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 33e0a17f42b07..e5e84941c6521 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void) if (!chan) return NULL; - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; chan->mode = L2CAP_MODE_LE_FLOWCTL; @@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) ifup(dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) -{ - struct l2cap_chan *chan; - - chan = chan_create(); - if (!chan) - return NULL; - - chan->ops = pchan->ops; - - BT_DBG("chan %p pchan %p", chan, pchan); - - return chan; -} - static void unregister_dev(struct lowpan_btle_dev *dev) { struct hci_dev *hdev = READ_ONCE(dev->hdev); @@ -889,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) static const struct l2cap_ops bt_6lowpan_chan_ops = { .name = "L2CAP 6LoWPAN channel", - .new_connection = chan_new_conn_cb, .recv = chan_recv_cb, .close = chan_close_cb, .state_change = chan_state_change_cb, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0289d418ac70a..e04948fb6832e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -525,7 +525,10 @@ void l2cap_chan_put(struct l2cap_chan *c) } EXPORT_SYMBOL_GPL(l2cap_chan_put); -void l2cap_chan_set_defaults(struct l2cap_chan *chan) +/* Initialise @chan with default values, inheriting from the parent channel + * @pchan when it is given. + */ +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan) { chan->fcs = L2CAP_FCS_CRC16; chan->max_tx = L2CAP_DEFAULT_MAX_TX; @@ -539,6 +542,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + if (pchan) { + BT_DBG("chan %p pchan %p", chan, pchan); + + chan->chan_type = pchan->chan_type; + chan->imtu = pchan->imtu; + chan->omtu = pchan->omtu; + chan->mode = pchan->mode; + chan->fcs = pchan->fcs; + chan->max_tx = pchan->max_tx; + chan->tx_win = pchan->tx_win; + chan->tx_win_max = pchan->tx_win_max; + chan->sec_level = pchan->sec_level; + chan->conf_state = pchan->conf_state; + chan->flags = pchan->flags; + chan->tx_credits = pchan->tx_credits; + chan->rx_credits = pchan->rx_credits; + + if (chan->chan_type == L2CAP_CHAN_FIXED) { + chan->scid = pchan->scid; + chan->dcid = pchan->scid; + } + + return; + } + chan->conf_state = 0; set_bit(CONF_NOT_COMPLETE, &chan->conf_state); @@ -4027,6 +4055,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, return 0; } +/* Allocate and initialise a channel for an incoming connection. + * + * The channel inherits its configuration from @pchan and is linked into @conn + * before ->new_connection() runs, so the conn list reference keeps it alive if + * the callback exposes it (e.g. via the socket accept queue) before this + * returns. The l2cap_chan_create() reference is taken over by the subsystem on + * success and dropped here on failure. + */ +static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn, + struct l2cap_chan *pchan) +{ + struct l2cap_chan *chan; + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + l2cap_chan_set_defaults(chan, pchan); + chan->ops = pchan->ops; + + __l2cap_chan_add(conn, chan); + + if (pchan->ops->new_connection && + pchan->ops->new_connection(pchan, chan) < 0) { + l2cap_chan_del(chan, 0); + l2cap_chan_put(chan); + return NULL; + } + + return chan; +} + static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data, u8 rsp_code) { @@ -4073,7 +4133,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, goto response; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) goto response; @@ -4091,8 +4151,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, chan->psm = psm; chan->dcid = scid; - __l2cap_chan_add(conn, chan); - dcid = chan->scid; __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); @@ -4984,7 +5042,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, goto response_unlock; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; goto response_unlock; @@ -4999,8 +5057,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits)); dcid = chan->scid; @@ -5208,7 +5264,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, continue; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; continue; @@ -5223,8 +5279,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_ecred_init(chan, __le16_to_cpu(req->credits)); /* Init response */ @@ -7502,14 +7556,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) goto next; l2cap_chan_lock(pchan); - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (chan) { bacpy(&chan->src, &hcon->src); bacpy(&chan->dst, &hcon->dst); chan->src_type = bdaddr_src_type(hcon); chan->dst_type = dst_type; - - __l2cap_chan_add(conn, chan); } l2cap_chan_unlock(pchan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index f2fa3130fd41b..1a916c0f40ccf 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -45,7 +45,8 @@ static struct bt_sock_list l2cap_sk_list = { static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern); + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan); static void l2cap_sock_cleanup_listen(struct sock *parent); bool l2cap_is_socket(struct socket *sock) @@ -1256,6 +1257,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, return err; } +/* Release the sock's ref on chan and clear the pointer so that the ref is + * dropped exactly once even if both l2cap_sock_kill() and + * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other + * task from dereferencing the now-dead sock pointer. + */ +static void l2cap_sock_put_chan(struct sock *sk) +{ + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + + if (!chan) + return; + + chan->data = NULL; + l2cap_pi(sk)->chan = NULL; + l2cap_chan_put(chan); +} + /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket, with l2cap channel lock. */ @@ -1266,13 +1284,9 @@ static void l2cap_sock_kill(struct sock *sk) BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); - /* Sock is dead, so set chan data to NULL, avoid other task use invalid - * sock pointer. - */ - l2cap_pi(sk)->chan->data = NULL; - /* Kill poor orphan */ + l2cap_sock_put_chan(sk); - l2cap_chan_put(l2cap_pi(sk)->chan); + /* Kill poor orphan */ sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } @@ -1515,12 +1529,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) } } -static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) +static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { struct sock *sk, *parent = chan->data; if (!parent) - return NULL; + return -EINVAL; lock_sock(parent); @@ -1528,25 +1543,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); release_sock(parent); - return NULL; + return -ENOBUFS; } sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC, 0); + GFP_ATOMIC, 0, new_chan); if (!sk) { release_sock(parent); - return NULL; - } + return -ENOMEM; + } bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); l2cap_sock_init(sk, parent); + /* The conn list reference taken by l2cap_new_connection() keeps new_chan + * alive once release_sock() lets another task free this socket. + */ bt_accept_enqueue(parent, sk, false); release_sock(parent); - return l2cap_pi(sk)->chan; + return 0; } static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) @@ -1844,10 +1862,7 @@ static void l2cap_sock_destruct(struct sock *sk) BT_DBG("sk %p", sk); - if (l2cap_pi(sk)->chan) { - l2cap_pi(sk)->chan->data = NULL; - l2cap_chan_put(l2cap_pi(sk)->chan); - } + l2cap_sock_put_chan(sk); list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) { kfree_skb(rx_busy->skb); @@ -1880,30 +1895,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) BT_DBG("sk %p", sk); if (parent) { - struct l2cap_chan *pchan = l2cap_pi(parent)->chan; - sk->sk_type = parent->sk_type; bt_sk(sk)->flags = bt_sk(parent)->flags; - chan->chan_type = pchan->chan_type; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->conf_state = pchan->conf_state; - chan->mode = pchan->mode; - chan->fcs = pchan->fcs; - chan->max_tx = pchan->max_tx; - chan->tx_win = pchan->tx_win; - chan->tx_win_max = pchan->tx_win_max; - chan->sec_level = pchan->sec_level; - chan->flags = pchan->flags; - chan->tx_credits = pchan->tx_credits; - chan->rx_credits = pchan->rx_credits; - - if (chan->chan_type == L2CAP_CHAN_FIXED) { - chan->scid = pchan->scid; - chan->dcid = pchan->scid; - } - + /* Channel configuration is inherited from the parent by + * l2cap_new_connection(). + */ security_sk_clone(parent, sk); } else { switch (sk->sk_type) { @@ -1929,7 +1926,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->mode = L2CAP_MODE_BASIC; } - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); } /* Default config options */ @@ -1948,10 +1945,10 @@ static struct proto l2cap_proto = { }; static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern) + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan) { struct sock *sk; - struct l2cap_chan *chan; sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern); if (!sk) @@ -1962,16 +1959,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy); - chan = l2cap_chan_create(); - if (!chan) { - sk_free(sk); - if (sock) - sock->sk = NULL; - return NULL; - } - - l2cap_chan_hold(chan); - + /* The sock takes ownership of the caller's reference on chan. */ l2cap_pi(sk)->chan = chan; return sk; @@ -1981,6 +1969,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; + struct l2cap_chan *chan; BT_DBG("sock %p", sock); @@ -1995,10 +1984,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, sock->ops = &l2cap_sock_ops; - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern); - if (!sk) + chan = l2cap_chan_create(); + if (!chan) return -ENOMEM; + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan); + if (!sk) { + l2cap_chan_put(chan); + return -ENOMEM; + } + l2cap_sock_init(sk, NULL); bt_sock_link(&l2cap_sk_list, sk); return 0; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index abf3ab7479ff2..1172df9b7004d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3234,34 +3234,19 @@ static const struct l2cap_ops smp_chan_ops = { .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; -static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan) +static inline int smp_new_conn_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - struct l2cap_chan *chan; - - BT_DBG("pchan %p", pchan); - - chan = l2cap_chan_create(); - if (!chan) - return NULL; - - chan->chan_type = pchan->chan_type; - chan->ops = &smp_chan_ops; - chan->scid = pchan->scid; - chan->dcid = chan->scid; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->mode = pchan->mode; + new_chan->ops = &smp_chan_ops; /* Other L2CAP channels may request SMP routines in order to * change the security level. This means that the SMP channel * lock must be considered in its own category to avoid lockdep * warnings. */ - atomic_set(&chan->nesting, L2CAP_NESTING_SMP); - - BT_DBG("created chan %p", chan); + atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP); - return chan; + return 0; } static const struct l2cap_ops smp_root_chan_ops = { @@ -3332,7 +3317,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) l2cap_add_scid(chan, cid); - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); if (cid == L2CAP_CID_SMP) { u8 bdaddr_type; From ff943e1f3d31fbc5d7bf502dd1735729ac145b85 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Jul 2026 06:18:17 -0400 Subject: [PATCH 2159/3297] cifs: SMB1 split: Rename cifstransport.c [ Upstream commit b09eab52b307df58a36f34d047378053a2e13e13 ] Rename cifstransport.c to smb1transport.c in order to give consistent names SMB1-specific files. Signed-off-by: David Howells cc: Steve French cc: Paulo Alcantara cc: Enzo Matsumiya cc: linux-cifs@vger.kernel.org cc: linux-fsdevel@vger.kernel.org cc: linux-kernel@vger.kernel.org Acked-by: Enzo Matsumiya Signed-off-by: Steve French Stable-dep-of: c6394bcaf254 ("ksmbd: use opener credentials for FSCTL mutations") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/Makefile | 5 ++++- fs/smb/client/{cifstransport.c => smb1transport.c} | 0 2 files changed, 4 insertions(+), 1 deletion(-) rename fs/smb/client/{cifstransport.c => smb1transport.c} (100%) diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile index 4c97b31a25c28..9754b4776df84 100644 --- a/fs/smb/client/Makefile +++ b/fs/smb/client/Makefile @@ -32,6 +32,9 @@ cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o -cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o cifstransport.o +cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += \ + cifssmb.o \ + smb1ops.o \ + smb1transport.o cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o compress/lz77.o diff --git a/fs/smb/client/cifstransport.c b/fs/smb/client/smb1transport.c similarity index 100% rename from fs/smb/client/cifstransport.c rename to fs/smb/client/smb1transport.c From 90a93fb3230c9d9dc4b36342a1c942567b8423ea Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Jul 2026 06:18:18 -0400 Subject: [PATCH 2160/3297] cifs: SMB1 split: Add some #includes [ Upstream commit a7c7f35bcf0943ba85ab14bafbfbcbc3a30ad402 ] Add some #includes to make sure things continue to compile as splitting occurs. Signed-off-by: David Howells cc: Steve French cc: Paulo Alcantara cc: Enzo Matsumiya cc: linux-cifs@vger.kernel.org cc: linux-fsdevel@vger.kernel.org cc: linux-kernel@vger.kernel.org Acked-by: Enzo Matsumiya Signed-off-by: Steve French Stable-dep-of: c6394bcaf254 ("ksmbd: use opener credentials for FSCTL mutations") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/cifssmb.c | 5 +++-- fs/smb/client/smb1transport.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index bfc9b1ea76fac..7694ca283fdc3 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -26,11 +26,12 @@ #include #include #include +#include "cifsglob.h" +#include "cifsproto.h" +#include "../common/smbfsctl.h" #include "cifspdu.h" #include "cifsfs.h" -#include "cifsglob.h" #include "cifsacl.h" -#include "cifsproto.h" #include "cifs_unicode.h" #include "cifs_debug.h" #include "fscache.h" diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c index e98b95eff8c98..518f764dedcce 100644 --- a/fs/smb/client/smb1transport.c +++ b/fs/smb/client/smb1transport.c @@ -22,13 +22,13 @@ #include #include #include -#include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" -#include "cifs_debug.h" +#include "cifspdu.h" #include "smb2proto.h" #include "smbdirect.h" #include "compress.h" +#include "cifs_debug.h" /* Max number of iovectors we can use off the stack when sending requests. */ #define CIFS_MAX_IOV_SIZE 8 From e205f3e7e8c31a47cd11efb6cf663a527177e432 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 16 Jul 2026 06:18:19 -0400 Subject: [PATCH 2161/3297] ksmbd: use opener credentials for FSCTL mutations [ Upstream commit c6394bcaf254c5baf9aff43376020be5db6d3316 ] SET_SPARSE, SET_ZERO_DATA and SET_COMPRESSION operate on an open SMB handle but call VFS xattr, fallocate or fileattr helpers with the current ksmbd worker credentials. Those helpers can revalidate inode permissions, ownership and LSM policy independently of the SMB handle access mask. Run each operation with the credentials captured in the target file when the handle was opened. Keep credential handling local to these single-file FSCTLs rather than applying session credentials to the complete IOCTL handler, which also contains handle-less and multi-handle operations. Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 3 +++ fs/smb/server/vfs.c | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 9644b42404ae7..2471582469898 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8273,6 +8273,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, if (fp->f_ci->m_fattr != old_fattr && test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + const struct cred *saved_cred; struct xattr_dos_attrib da; ret = ksmbd_vfs_get_dos_attrib_xattr(idmap, @@ -8281,9 +8282,11 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, goto out; da.attr = le32_to_cpu(fp->f_ci->m_fattr); + saved_cred = override_creds(fp->filp->f_cred); ret = ksmbd_vfs_set_dos_attrib_xattr(idmap, &fp->filp->f_path, &da, true); + revert_creds(saved_cred); if (ret) fp->f_ci->m_fattr = old_fattr; } diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index babdecc5a01ff..12652117f3890 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -995,15 +995,21 @@ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t off, loff_t len) { + const struct cred *saved_cred; + int err; + smb_break_all_levII_oplock(work, fp, 1); + saved_cred = override_creds(fp->filp->f_cred); if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) - return vfs_fallocate(fp->filp, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - off, len); - - return vfs_fallocate(fp->filp, - FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, - off, len); + err = vfs_fallocate(fp->filp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + off, len); + else + err = vfs_fallocate(fp->filp, + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, + off, len); + revert_creds(saved_cred); + return err; } int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, From c7c884a1305aa4540eb7942a50bd356b34120e1f Mon Sep 17 00:00:00 2001 From: Davide Ornaghi Date: Thu, 16 Jul 2026 06:18:49 -0400 Subject: [PATCH 2162/3297] ksmbd: fix path resolution in ksmbd_vfs_kern_path_create [ Upstream commit 1c8951963d8ed357f70f59e0ad4ddce2199d2016 ] The SMB2 open lookup is rooted at the share with LOOKUP_BENEATH, but the create/mkdir/hardlink sink is not: ksmbd_vfs_kern_path_create() builds an absolute path with convert_to_unix_name() and resolves it from AT_FDCWD via start_creating_path(), so a ".." component is walked from the real filesystem root and escapes the export. An authenticated client races a missing path component so the rooted open lookup returns -ENOENT (taking the create branch) while the same component is present (a directory) when the create walk runs; the create then resolves ".." out of the share. Root the create walk at the share like the lookup and rename paths already are: resolve the parent with vfs_path_parent_lookup(..., LOOKUP_BENEATH, &share_conf->vfs_path) and create the final component with start_creating_noperm(). convert_to_unix_name() then has no callers and is removed. Fixes: 265fd1991c1d ("ksmbd: use LOOKUP_BENEATH to prevent the out of share access") Cc: stable@vger.kernel.org Signed-off-by: Davide Ornaghi Acked-by: Namjae Jeon Signed-off-by: Steve French [ replaced start_creating_noperm() and CLASS(filename_kernel) with open-coded getname_kernel()/inode_lock_nested()/lookup_one_qstr_excl() equivalents absent in 6.18 ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/misc.c | 33 --------------------------------- fs/smb/server/misc.h | 1 - fs/smb/server/vfs.c | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 40 deletions(-) diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c index cb2a11ffb23fe..53ddcae31f719 100644 --- a/fs/smb/server/misc.c +++ b/fs/smb/server/misc.c @@ -278,39 +278,6 @@ char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename) return ksmbd_casefold_sharename(um, name); } -/** - * convert_to_unix_name() - convert windows name to unix format - * @share: ksmbd_share_config pointer - * @name: file name that is relative to share - * - * Return: converted name on success, otherwise NULL - */ -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) -{ - int no_slash = 0, name_len, path_len; - char *new_name; - - if (name[0] == '/') - name++; - - path_len = share->path_sz; - name_len = strlen(name); - new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP); - if (!new_name) - return new_name; - - memcpy(new_name, share->path, path_len); - if (new_name[path_len - 1] != '/') { - new_name[path_len] = '/'; - no_slash = 1; - } - - memcpy(new_name + path_len + no_slash, name, name_len); - path_len += name_len + no_slash; - new_name[path_len] = 0x00; - return new_name; -} - char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, const struct nls_table *local_nls, int *conv_len) diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h index 1facfcd21200f..53282a6d2436c 100644 --- a/fs/smb/server/misc.h +++ b/fs/smb/server/misc.h @@ -22,7 +22,6 @@ void ksmbd_strip_last_slash(char *path); void ksmbd_conv_path_to_windows(char *path); char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name); char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); #define KSMBD_DIR_INFO_ALIGNMENT 8 struct ksmbd_dir_info; diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 12652117f3890..764eca75291ce 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1338,15 +1338,41 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, unsigned int flags, struct path *path) { - char *abs_name; + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + struct filename *filename __free(putname) = NULL; + struct qstr last; struct dentry *dent; + int err, type; + + /* resolve the name beneath the share root so ".." cannot escape */ + filename = getname_kernel(name); + if (IS_ERR(filename)) + return ERR_CAST(filename); + + err = vfs_path_parent_lookup(filename, flags | LOOKUP_BENEATH, + path, &last, &type, + &share_conf->vfs_path); + if (err) + return ERR_PTR(err); + + if (unlikely(type != LAST_NORM)) { + path_put(path); + return ERR_PTR(-EINVAL); + } - abs_name = convert_to_unix_name(work->tcon->share_conf, name); - if (!abs_name) - return ERR_PTR(-ENOMEM); + err = mnt_want_write(path->mnt); + if (err) { + path_put(path); + return ERR_PTR(err); + } - dent = start_creating_path(AT_FDCWD, abs_name, path, flags); - kfree(abs_name); + inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); + dent = lookup_one_qstr_excl(&last, path->dentry, LOOKUP_CREATE); + if (IS_ERR(dent)) { + inode_unlock(path->dentry->d_inode); + mnt_drop_write(path->mnt); + path_put(path); + } return dent; } From 6a37bc484f12e02c850461272ef41e0a20592dcb Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Thu, 16 Jul 2026 07:21:20 -0400 Subject: [PATCH 2163/3297] ksmbd: centralize ksmbd_conn final release to plug transport leak [ Upstream commit b1f1e80620deb49daf63c2e677046599b693dc1f ] ksmbd_conn_free() is one of four sites that can observe the last refcount drop of a struct ksmbd_conn. The other three fs/smb/server/connection.c ksmbd_conn_r_count_dec() fs/smb/server/oplock.c __free_opinfo() fs/smb/server/vfs_cache.c session_fd_check() end the conn with a bare kfree(), skipping ida_destroy(&conn->async_ida) and conn->transport->ops->free_transport(conn->transport). Whenever one of them is the last putter, the embedded async_ida and the entire transport struct leak -- for TCP, that is also the struct socket and the kvec iov. __free_opinfo() being a final putter is not theoretical. opinfo_put() queues the callback via call_rcu(&opinfo->rcu, free_opinfo_rcu), so ksmbd_server_terminate_conn() can deposit N opinfo releases in RCU and have ksmbd_conn_free() run in the handler thread before any of them fire. ksmbd_conn_free() then observes refcnt > 0 and short-circuits; the last RCU-delivered __free_opinfo() falls onto its bare kfree(conn) branch and the transport is lost. A/B validation in a QEMU/virtme guest, mounting //127.0.0.1/testshare: each iteration holds 8 files open via sleep processes, force-closes TCP with "ss -K sport = :445", kills the holders, lazy-umounts; repeated 10 times, then ksmbd shutdown and kmemleak scan. state conn_alloc conn_free tcp_free opi_rcu kmemleak ---------- ---------- --------- -------- ------- -------- pre-patch 20 20 10 160 7 with patch 20 20 20 160 0 Pre-patch conn_free=20 with tcp_free=10 directly demonstrates the bare-kfree paths skipping transport cleanup; kmemleak backtraces point into struct tcp_transport / iov. With this patch tcp_free matches conn_free at 20/20 and kmemleak is clean. Move the per-struct final release into __ksmbd_conn_release_work() and route the three bare-kfree final-put sites through a new ksmbd_conn_put(). Those sites now pair ida_destroy() and free_transport() with kfree(conn) regardless of which holder happens to release the last reference. stop_sessions() only triggers the transport shutdown and does not itself drop the last conn reference, so it is unaffected. The centralized release reaches sock_release() -> tcp_close() -> lock_sock_nested() (might_sleep) from every final putter, including __free_opinfo() invoked from an RCU softirq callback, which trips CONFIG_DEBUG_ATOMIC_SLEEP. Defer the release to a dedicated ksmbd_conn_wq workqueue so ksmbd_conn_put() is safe from any non-sleeping context. Make ksmbd_file own a strong connection reference while fp->conn is non-NULL so durable-preserve and final-close paths cannot dereference a stale connection. ksmbd_open_fd() and ksmbd_reopen_durable_fd() take the reference via ksmbd_conn_get() (the latter also reorders the fp->conn / fp->tcon assignments before __open_id() so the published fp is never observed with fp->conn == NULL); session_fd_check() and __ksmbd_close_fd() drop it via ksmbd_conn_put(). With that invariant, session_fd_check() can take a local conn pointer once and use it across the m_op_list and lock_list iterations even though op->conn puts may otherwise drop the last reference. At module exit the workqueue is flushed and destroyed after rcu_barrier(), so any release queued by a trailing RCU callback is drained before the inode hash and module text go away. Fixes: ee426bfb9d09 ("ksmbd: add refcnt to ksmbd_conn struct") Signed-off-by: DaeMyung Kang Acked-by: Namjae Jeon Signed-off-by: Steve French Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/connection.c | 101 +++++++++++++++++++++++++++++++------ fs/smb/server/connection.h | 6 +++ fs/smb/server/oplock.c | 7 +-- fs/smb/server/server.c | 12 +++++ fs/smb/server/vfs_cache.c | 60 ++++++++++++++++++---- 5 files changed, 156 insertions(+), 30 deletions(-) diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index a03132adcc1e5..9274953eb064c 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -22,6 +22,81 @@ static struct ksmbd_conn_ops default_conn_ops; DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS); DECLARE_RWSEM(conn_list_lock); +static struct workqueue_struct *ksmbd_conn_wq; + +int ksmbd_conn_wq_init(void) +{ + ksmbd_conn_wq = alloc_workqueue("ksmbd-conn-release", + WQ_UNBOUND | WQ_MEM_RECLAIM, 0); + if (!ksmbd_conn_wq) + return -ENOMEM; + return 0; +} + +void ksmbd_conn_wq_destroy(void) +{ + if (ksmbd_conn_wq) { + destroy_workqueue(ksmbd_conn_wq); + ksmbd_conn_wq = NULL; + } +} + +/* + * __ksmbd_conn_release_work() - perform the final, once-per-struct cleanup + * of a ksmbd_conn whose refcount has just dropped to zero. + * + * This is the common release path used by ksmbd_conn_put() for the embedded + * state that outlives the connection thread: async_ida and the attached + * transport (which owns the socket and iov for TCP). Called from a workqueue + * so that sleep-allowed teardown (sock_release -> tcp_close -> + * lock_sock_nested) never runs from an RCU softirq callback (free_opinfo_rcu) + * or any other non-sleeping putter context. + */ +static void __ksmbd_conn_release_work(struct work_struct *work) +{ + struct ksmbd_conn *conn = + container_of(work, struct ksmbd_conn, release_work); + + ida_destroy(&conn->async_ida); + conn->transport->ops->free_transport(conn->transport); + kfree(conn); +} + +/** + * ksmbd_conn_get() - take a reference on @conn and return it. + * + * Returns @conn unchanged so callers can write + * "fp->conn = ksmbd_conn_get(work->conn);" in one expression. Returns NULL + * if @conn is NULL. + */ +struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn) +{ + if (!conn) + return NULL; + + atomic_inc(&conn->refcnt); + return conn; +} + +/** + * ksmbd_conn_put() - drop a reference and, if it was the last, queue the + * release onto ksmbd_conn_wq so it runs from process context. + * + * Callable from any context including RCU softirq callbacks and non-sleeping + * locks; the actual release is deferred to the workqueue. ksmbd_conn_wq is + * created in ksmbd_server_init() before any conn can be allocated and is + * destroyed in ksmbd_server_exit() after rcu_barrier(), so it is always + * non-NULL while a conn reference is held. + */ +void ksmbd_conn_put(struct ksmbd_conn *conn) +{ + if (!conn) + return; + + if (atomic_dec_and_test(&conn->refcnt)) + queue_work(ksmbd_conn_wq, &conn->release_work); +} + /** * ksmbd_conn_free() - free resources of the connection instance * @@ -36,23 +111,19 @@ void ksmbd_conn_free(struct ksmbd_conn *conn) hash_del(&conn->hlist); up_write(&conn_list_lock); + /* + * request_buf / preauth_info / mechToken are only ever accessed by the + * connection handler thread that owns @conn. ksmbd_conn_free() is + * called from the transport free_transport() path when that thread is + * exiting, so it is safe to release them unconditionally even when + * ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file + * holders only retain the conn pointer, not these per-thread buffers). + */ xa_destroy(&conn->sessions); kvfree(conn->request_buf); kfree(conn->preauth_info); kfree(conn->mechToken); - if (atomic_dec_and_test(&conn->refcnt)) { - /* - * async_ida is embedded in struct ksmbd_conn, so pair - * ida_destroy() with the final kfree() rather than with - * the unconditional field teardown above. This keeps - * the IDA valid for the entire lifetime of the struct, - * even while other refcount holders (oplock / vfs - * durable handles) still reference the connection. - */ - ida_destroy(&conn->async_ida); - conn->transport->ops->free_transport(conn->transport); - kfree(conn); - } + ksmbd_conn_put(conn); } /** @@ -79,6 +150,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) conn->um = ERR_PTR(-EOPNOTSUPP); if (IS_ERR(conn->um)) conn->um = NULL; + INIT_WORK(&conn->release_work, __ksmbd_conn_release_work); atomic_set(&conn->req_running, 0); atomic_set(&conn->r_count, 0); atomic_set(&conn->refcnt, 1); @@ -458,8 +530,7 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn) if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) wake_up(&conn->r_count_q); - if (atomic_dec_and_test(&conn->refcnt)) - kfree(conn); + ksmbd_conn_put(conn); } int ksmbd_conn_transport_init(void) diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 50d1e5a54f33c..8d985568a82fc 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "smb_common.h" #include "ksmbd_work.h" @@ -119,6 +120,7 @@ struct ksmbd_conn { bool binding; atomic_t refcnt; bool is_aapl; + struct work_struct release_work; }; struct ksmbd_conn_ops { @@ -164,6 +166,10 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id); struct ksmbd_conn *ksmbd_conn_alloc(void); void ksmbd_conn_free(struct ksmbd_conn *conn); +struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn); +void ksmbd_conn_put(struct ksmbd_conn *conn); +int ksmbd_conn_wq_init(void); +void ksmbd_conn_wq_destroy(void); bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); int ksmbd_conn_write(struct ksmbd_work *work); int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index fc2ee00cfd810..a019c4496ae43 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -30,7 +30,6 @@ static DEFINE_RWLOCK(lease_list_lock); static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, u64 id, __u16 Tid) { - struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct oplock_info *opinfo; @@ -39,7 +38,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, return NULL; opinfo->sess = sess; - opinfo->conn = conn; + opinfo->conn = ksmbd_conn_get(work->conn); opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->op_state = OPLOCK_STATE_NONE; opinfo->pending_break = 0; @@ -50,7 +49,6 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, init_waitqueue_head(&opinfo->oplock_brk); atomic_set(&opinfo->refcount, 1); atomic_set(&opinfo->breaking_cnt, 0); - atomic_inc(&opinfo->conn->refcnt); return opinfo; } @@ -132,8 +130,7 @@ static void __free_opinfo(struct oplock_info *opinfo) { if (opinfo->is_lease) free_lease(opinfo); - if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt)) - kfree(opinfo->conn); + ksmbd_conn_put(opinfo->conn); kfree(opinfo); } diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index ef7a24e31f506..f36a3325a57d6 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -588,8 +588,14 @@ static int __init ksmbd_server_init(void) if (ret) goto err_crypto_destroy; + ret = ksmbd_conn_wq_init(); + if (ret) + goto err_workqueue_destroy; + return 0; +err_workqueue_destroy: + ksmbd_workqueue_destroy(); err_crypto_destroy: ksmbd_crypto_destroy(); err_release_inode_hash: @@ -615,6 +621,12 @@ static void __exit ksmbd_server_exit(void) { ksmbd_server_shutdown(); rcu_barrier(); + /* + * ksmbd_conn_put() defers the final release onto ksmbd_conn_wq, + * so drain it after rcu_barrier() has fired any pending RCU + * callbacks that may have queued a release. + */ + ksmbd_conn_wq_destroy(); ksmbd_release_inode_hash(); } diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index fad426fe3d14d..6ebc7a90072d9 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -402,6 +402,17 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) kfree(smb_lock); } + /* + * Drop fp's strong reference on conn (taken in ksmbd_open_fd() / + * ksmbd_reopen_durable_fd()). Durable fps that reached the + * scavenger have already had fp->conn cleared by session_fd_check(), + * in which case there is nothing to drop here. + */ + if (fp->conn) { + ksmbd_conn_put(fp->conn); + fp->conn = NULL; + } + if (ksmbd_stream_fd(fp)) kfree(fp->stream.name); kfree(fp->owner.name); @@ -694,7 +705,14 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) atomic_set(&fp->refcount, 1); fp->filp = filp; - fp->conn = work->conn; + /* + * fp owns a strong reference on fp->conn for as long as fp->conn is + * non-NULL, so session_fd_check() and __ksmbd_close_fd() never + * dereference a dangling pointer. Paired with ksmbd_conn_put() in + * session_fd_check() (durable preserve), in __ksmbd_close_fd() + * (final close), and on the error paths below. + */ + fp->conn = ksmbd_conn_get(work->conn); fp->tcon = work->tcon; fp->volatile_id = KSMBD_NO_FID; fp->persistent_id = KSMBD_NO_FID; @@ -716,6 +734,8 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) return fp; err_out: + /* fp->conn was set and refcounted before every branch here. */ + ksmbd_conn_put(fp->conn); kmem_cache_free(filp_cache, fp); return ERR_PTR(ret); } @@ -1043,25 +1063,32 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, if (!is_reconnectable(fp)) return false; + if (WARN_ON_ONCE(!fp->conn)) + return false; + if (ksmbd_vfs_copy_durable_owner(fp, user)) return false; + /* + * fp owns a strong reference on fp->conn (taken in ksmbd_open_fd() + * / ksmbd_reopen_durable_fd()), so conn stays valid for the whole + * body of this function regardless of any op->conn puts below. + */ conn = fp->conn; ci = fp->f_ci; down_write(&ci->m_lock); list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { if (op->conn != conn) continue; - if (op->conn && atomic_dec_and_test(&op->conn->refcnt)) - kfree(op->conn); + ksmbd_conn_put(op->conn); op->conn = NULL; } up_write(&ci->m_lock); list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&fp->conn->llist_lock); + spin_lock(&conn->llist_lock); list_del_init(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); + spin_unlock(&conn->llist_lock); } fp->conn = NULL; @@ -1072,6 +1099,8 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, fp->durable_scavenger_timeout = jiffies_to_msecs(jiffies) + fp->durable_timeout; + /* Drop fp's own reference on conn. */ + ksmbd_conn_put(conn); return true; } @@ -1158,15 +1187,27 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) old_f_state = fp->f_state; fp->f_state = FP_NEW; + + /* + * Initialize fp's connection binding before publishing fp into the + * session's file table. If __open_id() is ordered first, a + * concurrent teardown that iterates the table can observe a valid + * volatile_id with fp->conn == NULL and preserve a + * partially-initialized fp. fp owns a strong reference on the new + * conn (see ksmbd_open_fd()); undo it on __open_id() failure. + */ + fp->conn = ksmbd_conn_get(conn); + fp->tcon = work->tcon; + __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); if (!has_file_id(fp->volatile_id)) { + fp->conn = NULL; + fp->tcon = NULL; + ksmbd_conn_put(conn); fp->f_state = old_f_state; return -EBADF; } - fp->conn = conn; - fp->tcon = work->tcon; - list_for_each_entry(smb_lock, &fp->lock_list, flist) { spin_lock(&conn->llist_lock); list_add_tail(&smb_lock->clist, &conn->lock_list); @@ -1178,8 +1219,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { if (op->conn) continue; - op->conn = fp->conn; - atomic_inc(&op->conn->refcnt); + op->conn = ksmbd_conn_get(fp->conn); } up_write(&ci->m_lock); From 427faaa52b0b399940c1a88065a5c310d10dad15 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 16 Jul 2026 07:21:21 -0400 Subject: [PATCH 2164/3297] ksmbd: track the connection owning a byte-range lock [ Upstream commit c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb ] SMB2_LOCK adds each granted byte-range lock to both the file lock list and the lock list of the connection which handled the request. The final close and durable handle paths, however, remove the connection list entry while holding fp->conn->llist_lock. With SMB3 multichannel, the connection handling the LOCK request can be different from the connection which opened the file. The entry can therefore be removed under a different spinlock from the one protecting the list it belongs to. A concurrent traversal can then access freed struct ksmbd_lock and struct file_lock objects. Record the connection owning each lock's clist entry and hold a reference to it while the entry is linked. Use that connection and its llist_lock for unlock, rollback, close, and durable preserve. Durable reconnect assigns the new connection as the owner when publishing the locks again. Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") Cc: stable@vger.kernel.org Reported-by: Musaab Khan Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 10 ++++++++-- fs/smb/server/vfs_cache.c | 23 +++++++++++++++++------ fs/smb/server/vfs_cache.h | 1 + 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 2471582469898..0addf2d4ece7f 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7660,9 +7660,11 @@ int smb2_lock(struct ksmbd_work *work) nolock = 0; list_del(&cmp_lock->flist); list_del(&cmp_lock->clist); + cmp_lock->conn = NULL; spin_unlock(&conn->llist_lock); up_read(&conn_list_lock); + ksmbd_conn_put(conn); locks_free_lock(cmp_lock->fl); kfree(cmp_lock); goto out_check_cl; @@ -7797,6 +7799,7 @@ int smb2_lock(struct ksmbd_work *work) goto out2; } else if (!rc) { list_add(&smb_lock->llist, &rollback_list); + smb_lock->conn = ksmbd_conn_get(work->conn); spin_lock(&work->conn->llist_lock); list_add_tail(&smb_lock->clist, &work->conn->lock_list); @@ -7851,11 +7854,14 @@ int smb2_lock(struct ksmbd_work *work) } list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); + conn = smb_lock->conn; + spin_lock(&conn->llist_lock); if (!list_empty(&smb_lock->flist)) list_del(&smb_lock->flist); list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); locks_free_lock(smb_lock->fl); if (rlock) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 6ebc7a90072d9..25c216909cc4d 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -391,10 +391,14 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) * there are not accesses to fp->lock_list. */ list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - if (!list_empty(&smb_lock->clist) && fp->conn) { - spin_lock(&fp->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); + struct ksmbd_conn *conn = smb_lock->conn; + + if (conn) { + spin_lock(&conn->llist_lock); + list_del_init(&smb_lock->clist); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); } list_del(&smb_lock->flist); @@ -1086,9 +1090,15 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, up_write(&ci->m_lock); list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&conn->llist_lock); + struct ksmbd_conn *lock_conn = smb_lock->conn; + + if (!lock_conn) + continue; + spin_lock(&lock_conn->llist_lock); list_del_init(&smb_lock->clist); - spin_unlock(&conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&lock_conn->llist_lock); + ksmbd_conn_put(lock_conn); } fp->conn = NULL; @@ -1209,6 +1219,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } list_for_each_entry(smb_lock, &fp->lock_list, flist) { + smb_lock->conn = ksmbd_conn_get(conn); spin_lock(&conn->llist_lock); list_add_tail(&smb_lock->clist, &conn->lock_list); spin_unlock(&conn->llist_lock); diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 6fcb7791f275f..10f4172ec8017 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -32,6 +32,7 @@ struct ksmbd_session; struct ksmbd_lock { struct file_lock *fl; + struct ksmbd_conn *conn; struct list_head clist; struct list_head flist; struct list_head llist; From bd27d9504d20005435722250c8c9066e339584e8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 16 Jul 2026 07:21:44 -0400 Subject: [PATCH 2165/3297] ksmbd: Use SHA-512 library for SMB3.1.1 preauth hash [ Upstream commit e009cb1e3093977c5b96a1c6697a7dc9332222d5 ] Convert ksmbd_gen_preauth_integrity_hash() to use the SHA-512 library instead of a "sha512" crypto_shash. This is simpler and faster. With the library there's no need to allocate memory, no need to handle errors, and the SHA-512 code is accessed directly without inefficient indirect calls and other unnecessary API overhead. Signed-off-by: Eric Biggers Acked-by: Namjae Jeon Signed-off-by: Steve French Stable-dep-of: 954d196bebb2 ("ksmbd: validate NTLMv2 response before updating session key") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/Kconfig | 2 +- fs/smb/server/auth.c | 41 +++++++------------------------------- fs/smb/server/crypto_ctx.c | 8 -------- fs/smb/server/crypto_ctx.h | 4 ---- fs/smb/server/server.c | 1 - 5 files changed, 8 insertions(+), 48 deletions(-) diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig index 6200c71298f66..4cb496a43375b 100644 --- a/fs/smb/server/Kconfig +++ b/fs/smb/server/Kconfig @@ -13,10 +13,10 @@ config SMB_SERVER select CRYPTO_LIB_ARC4 select CRYPTO_LIB_DES select CRYPTO_LIB_SHA256 + select CRYPTO_LIB_SHA512 select CRYPTO_LIB_UTILS select CRYPTO_SHA256 select CRYPTO_CMAC - select CRYPTO_SHA512 select CRYPTO_AEAD2 select CRYPTO_CCM select CRYPTO_GCM diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index b63f32c692bab..0231c08efb5cf 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -918,48 +919,20 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash) { - int rc; struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; int msg_size = get_rfc1002_len(buf); - struct ksmbd_crypto_ctx *ctx = NULL; + struct sha512_ctx sha_ctx; if (conn->preauth_info->Preauth_HashId != SMB2_PREAUTH_INTEGRITY_SHA512) return -EINVAL; - ctx = ksmbd_crypto_ctx_find_sha512(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha512\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA512(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; + sha512_init(&sha_ctx); + sha512_update(&sha_ctx, pi_hash, 64); + sha512_update(&sha_ctx, all_bytes_msg, msg_size); + sha512_final(&sha_ctx, pi_hash); + return 0; } static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c index 80bd68c8635ea..1de41b10528bc 100644 --- a/fs/smb/server/crypto_ctx.c +++ b/fs/smb/server/crypto_ctx.c @@ -75,9 +75,6 @@ static struct shash_desc *alloc_shash_desc(int id) case CRYPTO_SHASH_CMACAES: tfm = crypto_alloc_shash("cmac(aes)", 0, 0); break; - case CRYPTO_SHASH_SHA512: - tfm = crypto_alloc_shash("sha512", 0, 0); - break; default: return NULL; } @@ -195,11 +192,6 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); } -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); -} - static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) { struct ksmbd_crypto_ctx *ctx; diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h index ac64801d52d38..4e8954acd8b8b 100644 --- a/fs/smb/server/crypto_ctx.h +++ b/fs/smb/server/crypto_ctx.h @@ -13,7 +13,6 @@ enum { CRYPTO_SHASH_HMACMD5 = 0, CRYPTO_SHASH_HMACSHA256, CRYPTO_SHASH_CMACAES, - CRYPTO_SHASH_SHA512, CRYPTO_SHASH_MAX, }; @@ -38,13 +37,11 @@ struct ksmbd_crypto_ctx { #define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) #define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) #define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) -#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) #define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) #define CRYPTO_HMACSHA256_TFM(c)\ ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) -#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) #define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) #define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) @@ -53,7 +50,6 @@ void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); void ksmbd_crypto_destroy(void); diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index f36a3325a57d6..19363f80e07da 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -640,7 +640,6 @@ MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); MODULE_SOFTDEP("pre: cmac"); MODULE_SOFTDEP("pre: sha256"); -MODULE_SOFTDEP("pre: sha512"); MODULE_SOFTDEP("pre: aead2"); MODULE_SOFTDEP("pre: ccm"); MODULE_SOFTDEP("pre: gcm"); From 51c5f7e84cfed4e6f7496009e58b66f4ffddadb3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 16 Jul 2026 07:21:45 -0400 Subject: [PATCH 2166/3297] ksmbd: Use HMAC-SHA256 library for message signing and key generation [ Upstream commit 924067ef183bd17f39d790752190f99982524598 ] Convert ksmbd_sign_smb2_pdu() and generate_key() to use the HMAC-SHA256 library instead of a "hmac(sha256)" crypto_shash. This is simpler and faster. With the library there's no need to allocate memory, no need to handle errors, and the HMAC-SHA256 code is accessed directly without inefficient indirect calls and other unnecessary API overhead. Signed-off-by: Eric Biggers Acked-by: Namjae Jeon Signed-off-by: Steve French Stable-dep-of: 954d196bebb2 ("ksmbd: validate NTLMv2 response before updating session key") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/Kconfig | 1 - fs/smb/server/auth.c | 176 ++++++++----------------------------- fs/smb/server/auth.h | 10 +-- fs/smb/server/crypto_ctx.c | 8 -- fs/smb/server/crypto_ctx.h | 5 -- fs/smb/server/server.c | 1 - fs/smb/server/smb2pdu.c | 25 ++---- fs/smb/server/smb_common.h | 2 +- 8 files changed, 52 insertions(+), 176 deletions(-) diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig index 4cb496a43375b..e2eae2b9a21be 100644 --- a/fs/smb/server/Kconfig +++ b/fs/smb/server/Kconfig @@ -15,7 +15,6 @@ config SMB_SERVER select CRYPTO_LIB_SHA256 select CRYPTO_LIB_SHA512 select CRYPTO_LIB_UTILS - select CRYPTO_SHA256 select CRYPTO_CMAC select CRYPTO_AEAD2 select CRYPTO_CCM diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 0231c08efb5cf..01384a845a9b2 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -592,46 +592,16 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, * @sig: signature value generated for client request packet * */ -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) +void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) { - struct ksmbd_crypto_ctx *ctx; - int rc, i; - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } + struct hmac_sha256_ctx ctx; + int i; - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; + hmac_sha256_init_usingrawkey(&ctx, key, SMB2_NTLMV2_SESSKEY_SIZE); + for (i = 0; i < n_vec; i++) + hmac_sha256_update(&ctx, iov[i].iov_base, iov[i].iov_len); + hmac_sha256_final(&ctx, sig); } /** @@ -691,98 +661,39 @@ struct derivation { bool binding; }; -static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct kvec label, struct kvec context, __u8 *key, - unsigned int key_size) +static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct kvec label, struct kvec context, __u8 *key, + unsigned int key_size) { unsigned char zero = 0x0; __u8 i[4] = {0, 0, 0, 1}; __u8 L128[4] = {0, 0, 0, 128}; __u8 L256[4] = {0, 0, 1, 0}; - int rc; unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct ksmbd_crypto_ctx *ctx; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } + struct hmac_sha256_ctx ctx; - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto smb3signkey_ret; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - label.iov_base, - label.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with label\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); - if (rc) { - ksmbd_debug(AUTH, "could not update with zero\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - context.iov_base, - context.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with context\n"); - goto smb3signkey_ret; - } + hmac_sha256_init_usingrawkey(&ctx, sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + hmac_sha256_update(&ctx, i, 4); + hmac_sha256_update(&ctx, label.iov_base, label.iov_len); + hmac_sha256_update(&ctx, &zero, 1); + hmac_sha256_update(&ctx, context.iov_base, context.iov_len); if (key_size == SMB3_ENC_DEC_KEY_SIZE && (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); + hmac_sha256_update(&ctx, L256, 4); else - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with L\n"); - goto smb3signkey_ret; - } + hmac_sha256_update(&ctx, L128, 4); - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", - rc); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - ksmbd_release_crypto_ctx(ctx); - return rc; + hmac_sha256_final(&ctx, prfhash); + memcpy(key, prfhash, key_size); } static int generate_smb3signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn, const struct derivation *signing) { - int rc; struct channel *chann; char *key; @@ -795,10 +706,8 @@ static int generate_smb3signingkey(struct ksmbd_session *sess, else key = sess->smb3signingkey; - rc = generate_key(conn, sess, signing->label, signing->context, key, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; + generate_key(conn, sess, signing->label, signing->context, key, + SMB3_SIGN_KEY_SIZE); if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); @@ -850,32 +759,25 @@ struct derivation_twin { struct derivation decryption; }; -static int generate_smb3encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess, - const struct derivation_twin *ptwin) +static void generate_smb3encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + const struct derivation_twin *ptwin) { - int rc; + generate_key(conn, sess, ptwin->encryption.label, + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); - rc = generate_key(conn, sess, ptwin->encryption.label, - ptwin->encryption.context, sess->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - - rc = generate_key(conn, sess, ptwin->decryption.label, - ptwin->decryption.context, - sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; + generate_key(conn, sess, ptwin->decryption.label, + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); ksmbd_debug(AUTH, "generated SMB3 encryption/decryption keys\n"); ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); - return 0; } -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) +void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) { struct derivation_twin twin; struct derivation *d; @@ -892,11 +794,11 @@ int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, d->context.iov_base = "ServerIn "; d->context.iov_len = 10; - return generate_smb3encryptionkey(conn, sess, &twin); + generate_smb3encryptionkey(conn, sess, &twin); } -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) +void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) { struct derivation_twin twin; struct derivation *d; @@ -913,7 +815,7 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, d->context.iov_base = sess->Preauth_HashValue; d->context.iov_len = 64; - return generate_smb3encryptionkey(conn, sess, &twin); + generate_smb3encryptionkey(conn, sess, &twin); } int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h index 6879a1bd1b91f..6d351d61b0e57 100644 --- a/fs/smb/server/auth.h +++ b/fs/smb/server/auth.h @@ -52,18 +52,18 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, struct ksmbd_conn *conn); int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, int in_len, char *out_blob, int *out_len); -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig); +void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig); int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn); int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn); -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, +void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, struct ksmbd_session *sess); +void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash); #endif diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c index 1de41b10528bc..a5ec22ba8ae37 100644 --- a/fs/smb/server/crypto_ctx.c +++ b/fs/smb/server/crypto_ctx.c @@ -69,9 +69,6 @@ static struct shash_desc *alloc_shash_desc(int id) case CRYPTO_SHASH_HMACMD5: tfm = crypto_alloc_shash("hmac(md5)", 0, 0); break; - case CRYPTO_SHASH_HMACSHA256: - tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); - break; case CRYPTO_SHASH_CMACAES: tfm = crypto_alloc_shash("cmac(aes)", 0, 0); break; @@ -182,11 +179,6 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); } -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); -} - struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) { return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h index 4e8954acd8b8b..c571d65a16f3e 100644 --- a/fs/smb/server/crypto_ctx.h +++ b/fs/smb/server/crypto_ctx.h @@ -11,7 +11,6 @@ enum { CRYPTO_SHASH_HMACMD5 = 0, - CRYPTO_SHASH_HMACSHA256, CRYPTO_SHASH_CMACAES, CRYPTO_SHASH_MAX, }; @@ -35,12 +34,9 @@ struct ksmbd_crypto_ctx { }; #define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) -#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) #define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) #define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) -#define CRYPTO_HMACSHA256_TFM(c)\ - ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) #define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) @@ -48,7 +44,6 @@ struct ksmbd_crypto_ctx { void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 19363f80e07da..6b90c36acf09c 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -639,7 +639,6 @@ MODULE_SOFTDEP("pre: md5"); MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); MODULE_SOFTDEP("pre: cmac"); -MODULE_SOFTDEP("pre: sha256"); MODULE_SOFTDEP("pre: aead2"); MODULE_SOFTDEP("pre: ccm"); MODULE_SOFTDEP("pre: gcm"); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 0addf2d4ece7f..a69bc75c85ad0 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1552,12 +1552,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, if (smb3_encryption_negotiated(conn) && !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - rc = conn->ops->generate_encryptionkey(conn, sess); - if (rc) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } + conn->ops->generate_encryptionkey(conn, sess); sess->enc = true; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; @@ -1656,12 +1651,7 @@ static int krb5_authenticate(struct ksmbd_work *work, if (smb3_encryption_negotiated(conn) && !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - retval = conn->ops->generate_encryptionkey(conn, sess); - if (retval) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } + conn->ops->generate_encryptionkey(conn, sess); sess->enc = true; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; @@ -9077,9 +9067,8 @@ int smb2_check_sign_req(struct ksmbd_work *work) iov[0].iov_base = (char *)&hdr->ProtocolId; iov[0].iov_len = len; - if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, - signature)) - return 0; + ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, + signature); if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) { pr_err("bad smb2 signature\n"); @@ -9112,9 +9101,9 @@ void smb2_set_sign_rsp(struct ksmbd_work *work) iov = &work->iov[work->iov_idx]; } - if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, - signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); + ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature); + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); } /** diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h index dac783b46545d..e3be583e803da 100644 --- a/fs/smb/server/smb_common.h +++ b/fs/smb/server/smb_common.h @@ -373,7 +373,7 @@ struct smb_version_ops { int (*check_sign_req)(struct ksmbd_work *work); void (*set_sign_rsp)(struct ksmbd_work *work); int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); - int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); + void (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); bool (*is_transform_hdr)(void *buf); int (*decrypt_req)(struct ksmbd_work *work); int (*encrypt_resp)(struct ksmbd_work *work); From 74c2f0ffb81c8f3f5e3e1b8d3649d3cf5753341d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 16 Jul 2026 07:21:46 -0400 Subject: [PATCH 2167/3297] ksmbd: Use HMAC-MD5 library for NTLMv2 [ Upstream commit 3a597e6e9701eb7af04864ffdc0a6a91bc8c6496 ] For the HMAC-MD5 computations in NTLMv2, use the HMAC-MD5 library instead of a "hmac(md5)" crypto_shash. This is simpler and faster. With the library there's no need to allocate memory, no need to handle errors, and the HMAC-MD5 code is accessed directly without inefficient indirect calls and other unnecessary API overhead. To preserve the existing behavior of NTLMv2 support being disabled when the kernel is booted with "fips=1", make ksmbd_auth_ntlmv2() check fips_enabled itself. Previously it relied on the error from crypto_alloc_shash("hmac(md5)") being bubbled up. I don't know for sure that this is actually needed, but this preserves the existing behavior. Signed-off-by: Eric Biggers Acked-by: Namjae Jeon Signed-off-by: Steve French Stable-dep-of: 954d196bebb2 ("ksmbd: validate NTLMv2 response before updating session key") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/Kconfig | 3 +- fs/smb/server/auth.c | 173 ++++++------------------------------- fs/smb/server/crypto_ctx.c | 8 -- fs/smb/server/crypto_ctx.h | 6 +- fs/smb/server/server.c | 2 - 5 files changed, 27 insertions(+), 165 deletions(-) diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig index e2eae2b9a21be..12594879cb640 100644 --- a/fs/smb/server/Kconfig +++ b/fs/smb/server/Kconfig @@ -7,11 +7,10 @@ config SMB_SERVER select NLS_UTF8 select NLS_UCS2_UTILS select CRYPTO - select CRYPTO_MD5 - select CRYPTO_HMAC select CRYPTO_ECB select CRYPTO_LIB_ARC4 select CRYPTO_LIB_DES + select CRYPTO_LIB_MD5 select CRYPTO_LIB_SHA256 select CRYPTO_LIB_SHA512 select CRYPTO_LIB_UTILS diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 01384a845a9b2..35e9cb63abcd7 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -71,85 +72,16 @@ void ksmbd_copy_gss_neg_header(void *buf) memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); } -/** - * ksmbd_gen_sess_key() - function to generate session key - * @sess: session of connection - * @hash: source hash value to be used for find session key - * @hmac: source hmac value to be used for finding session key - * - */ -static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, - char *hmac) -{ - struct ksmbd_crypto_ctx *ctx; - int rc; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); - goto out; - } - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), - hmac, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); - goto out; - } - -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, char *ntlmv2_hash, char *dname) { int ret, len, conv_len; wchar_t *domain = NULL; __le16 *uniname = NULL; - struct ksmbd_crypto_ctx *ctx; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); - return -ENOMEM; - } + struct hmac_md5_ctx ctx; - ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - user_passkey(sess->user), + hmac_md5_init_usingrawkey(&ctx, user_passkey(sess->user), CIFS_ENCPWD_SIZE); - if (ret) { - ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); - goto out; - } - - ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (ret) { - ksmbd_debug(AUTH, "could not init hmacmd5\n"); - goto out; - } /* convert user_name to unicode */ len = strlen(user_name(sess->user)); @@ -167,13 +99,7 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, } UniStrupr(uniname); - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)uniname, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with user\n"); - goto out; - } + hmac_md5_update(&ctx, (const u8 *)uniname, UNICODE_LEN(conv_len)); /* Convert domain name or conn name to unicode and uppercase */ len = strlen(dname); @@ -190,21 +116,12 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, goto out; } - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)domain, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with domain\n"); - goto out; - } - - ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); - if (ret) - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + hmac_md5_update(&ctx, (const u8 *)domain, UNICODE_LEN(conv_len)); + hmac_md5_final(&ctx, ntlmv2_hash); + ret = 0; out: kfree(uniname); kfree(domain); - ksmbd_release_crypto_ctx(ctx); return ret; } @@ -225,74 +142,34 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - struct ksmbd_crypto_ctx *ctx = NULL; - char *construct = NULL; - int rc, len; - - rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); - if (rc) { - ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); - goto out; - } - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - ntlmv2_hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "Could not init hmacmd5\n"); - goto out; - } + struct hmac_md5_ctx ctx; + int rc; - len = CIFS_CRYPTO_KEY_SIZE + blen; - construct = kzalloc(len, KSMBD_DEFAULT_GFP); - if (!construct) { - rc = -ENOMEM; - goto out; + if (fips_enabled) { + ksmbd_debug(AUTH, "NTLMv2 support is disabled due to FIPS\n"); + return -EOPNOTSUPP; } - memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); - memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); + rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); if (rc) { - ksmbd_debug(AUTH, "Could not update with response\n"); - goto out; + ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); + return rc; } - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); - goto out; - } - ksmbd_release_crypto_ctx(ctx); - ctx = NULL; + hmac_md5_init_usingrawkey(&ctx, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + hmac_md5_update(&ctx, cryptkey, CIFS_CRYPTO_KEY_SIZE); + hmac_md5_update(&ctx, (const u8 *)&ntlmv2->blob_signature, blen); + hmac_md5_final(&ctx, ntlmv2_rsp); - rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate sess key\n"); - goto out; - } + /* Generate the session key */ + hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE, + ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE, + sess->sess_key); if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE)) - rc = -EINVAL; -out: - if (ctx) - ksmbd_release_crypto_ctx(ctx); - kfree(construct); - return rc; + return -EINVAL; + return 0; } /** diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c index a5ec22ba8ae37..fe29d186baf68 100644 --- a/fs/smb/server/crypto_ctx.c +++ b/fs/smb/server/crypto_ctx.c @@ -66,9 +66,6 @@ static struct shash_desc *alloc_shash_desc(int id) struct shash_desc *shash; switch (id) { - case CRYPTO_SHASH_HMACMD5: - tfm = crypto_alloc_shash("hmac(md5)", 0, 0); - break; case CRYPTO_SHASH_CMACAES: tfm = crypto_alloc_shash("cmac(aes)", 0, 0); break; @@ -174,11 +171,6 @@ static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) return NULL; } -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); -} - struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) { return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h index c571d65a16f3e..b9476ed520ae4 100644 --- a/fs/smb/server/crypto_ctx.h +++ b/fs/smb/server/crypto_ctx.h @@ -10,8 +10,7 @@ #include enum { - CRYPTO_SHASH_HMACMD5 = 0, - CRYPTO_SHASH_CMACAES, + CRYPTO_SHASH_CMACAES = 0, CRYPTO_SHASH_MAX, }; @@ -33,17 +32,14 @@ struct ksmbd_crypto_ctx { struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; }; -#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) #define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) -#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) #define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) #define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 6b90c36acf09c..b78126bb23711 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -634,8 +634,6 @@ MODULE_AUTHOR("Namjae Jeon "); MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_LICENSE("GPL"); MODULE_SOFTDEP("pre: ecb"); -MODULE_SOFTDEP("pre: hmac"); -MODULE_SOFTDEP("pre: md5"); MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); MODULE_SOFTDEP("pre: cmac"); From b56400364aed5c34d6e1a0b493081290a5328a9c Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Thu, 16 Jul 2026 07:21:47 -0400 Subject: [PATCH 2168/3297] ksmbd: validate NTLMv2 response before updating session key [ Upstream commit 954d196bebb2b50151cb96454c72dc113b2af1ac ] ksmbd_auth_ntlmv2() derives the NTLMv2 session key into sess->sess_key before it verifies the NTLMv2 response. ksmbd_decode_ntlmssp_auth_blob() then continues into KEY_XCH even when ksmbd_auth_ntlmv2() failed. With SMB3 multichannel binding, the failed authentication operates on an existing session and the session setup error path does not expire binding sessions. A client can send a binding session setup with a bad NT proof and KEY_XCH and still modify sess->sess_key before STATUS_LOGON_FAILURE is returned. Relevant path: smb2_sess_setup() -> conn->binding = true -> ntlm_authenticate() -> session_user() -> ksmbd_decode_ntlmssp_auth_blob() -> ksmbd_auth_ntlmv2() -> calc_ntlmv2_hash() -> hmac_md5_usingrawkey(..., sess->sess_key) -> crypto_memneq() returns mismatch -> KEY_XCH arc4_crypt(..., sess->sess_key, ...) -> out_err without expiring the binding session Derive the base session key into a local buffer and copy it to sess->sess_key only after the proof matches. Return immediately on authentication failure so KEY_XCH is only processed after successful authentication. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Fixes: f9929ef6a2a5 ("ksmbd: add support for key exchange") Cc: stable@vger.kernel.org Signed-off-by: Haofeng Li Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/auth.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 35e9cb63abcd7..a778275207fe7 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -142,6 +142,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; struct hmac_md5_ctx ctx; int rc; @@ -164,12 +165,21 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, /* Generate the session key */ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE, - sess->sess_key); + sess_key); if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp, - CIFS_HMAC_MD5_HASH_SIZE)) - return -EINVAL; - return 0; + CIFS_HMAC_MD5_HASH_SIZE)) { + rc = -EINVAL; + goto out; + } + + memcpy(sess->sess_key, sess_key, sizeof(sess_key)); + rc = 0; +out: + memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash)); + memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp)); + memzero_explicit(sess_key, sizeof(sess_key)); + return rc; } /** @@ -226,6 +236,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, nt_len - CIFS_ENCPWD_SIZE, domain_name, conn->ntlmssp.cryptkey); kfree(domain_name); + if (ret) + return ret; /* The recovered secondary session key */ if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { From 903d78e5aca7792fb4169c7ca83f403056823d53 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 16 Jul 2026 12:45:13 -0400 Subject: [PATCH 2169/3297] proc: rename proc_setattr to proc_nochmod_setattr [ Upstream commit 690005b0b1e6b567c88b7790e6d90d4d6c9e09cc ] What is currently proc_setattr is a special version added after the more general procfs ->seattr in commit 6d76fa58b050 ("Don't allow chmod() on the /proc// files"). Give it a name that reflects that to free the proc_setattr name and better describe what is doing. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260325063711.3298685-5-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/proc/base.c | 22 +++++++++++----------- fs/proc/fd.c | 6 +++--- fs/proc/internal.h | 4 ++-- fs/proc/namespaces.c | 4 ++-- fs/proc/proc_net.c | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index a314fd9af17c0..998c6cce77c19 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -721,7 +721,7 @@ static bool proc_fd_access_allowed(struct inode *inode) return allowed; } -int proc_setattr(struct mnt_idmap *idmap, struct dentry *dentry, +int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { int error; @@ -794,7 +794,7 @@ static int proc_pid_permission(struct mnt_idmap *idmap, static const struct inode_operations proc_def_inode_operations = { - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static int proc_single_show(struct seq_file *m, void *v) @@ -1866,7 +1866,7 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int b const struct inode_operations proc_pid_link_inode_operations = { .readlink = proc_pid_readlink, .get_link = proc_pid_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; @@ -2319,7 +2319,7 @@ proc_map_files_get_link(struct dentry *dentry, static const struct inode_operations proc_map_files_link_inode_operations = { .readlink = proc_pid_readlink, .get_link = proc_map_files_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry * @@ -2398,7 +2398,7 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, static const struct inode_operations proc_map_files_inode_operations = { .lookup = proc_map_files_lookup, .permission = proc_fd_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static int @@ -2885,7 +2885,7 @@ static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \ static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \ .lookup = proc_##LSM##_attr_dir_lookup, \ .getattr = pid_getattr, \ - .setattr = proc_setattr, \ + .setattr = proc_nochmod_setattr, \ } #ifdef CONFIG_SECURITY_SMACK @@ -2944,7 +2944,7 @@ static struct dentry *proc_attr_dir_lookup(struct inode *dir, static const struct inode_operations proc_attr_dir_inode_operations = { .lookup = proc_attr_dir_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; #endif @@ -3460,7 +3460,7 @@ static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *de static const struct inode_operations proc_tgid_base_inode_operations = { .lookup = proc_tgid_base_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_pid_permission, }; @@ -3659,7 +3659,7 @@ static int proc_tid_comm_permission(struct mnt_idmap *idmap, } static const struct inode_operations proc_tid_comm_inode_operations = { - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_tid_comm_permission, }; @@ -3788,7 +3788,7 @@ static const struct file_operations proc_tid_base_operations = { static const struct inode_operations proc_tid_base_inode_operations = { .lookup = proc_tid_base_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_task_instantiate(struct dentry *dentry, @@ -4001,7 +4001,7 @@ static loff_t proc_dir_llseek(struct file *file, loff_t offset, int whence) static const struct inode_operations proc_task_inode_operations = { .lookup = proc_task_lookup, .getattr = proc_task_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_pid_permission, }; diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 9eeccff49b2ab..8a48ff4a19ecb 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -102,7 +102,7 @@ static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode, static const struct inode_operations proc_fdinfo_file_inode_operations = { .permission = proc_fdinfo_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static const struct file_operations proc_fdinfo_file_operations = { @@ -361,7 +361,7 @@ const struct inode_operations proc_fd_inode_operations = { .lookup = proc_lookupfd, .permission = proc_fd_permission, .getattr = proc_fd_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry, @@ -402,7 +402,7 @@ static int proc_fdinfo_iterate(struct file *file, struct dir_context *ctx) const struct inode_operations proc_fdinfo_inode_operations = { .lookup = proc_lookupfdinfo, .permission = proc_fdinfo_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; const struct file_operations proc_fdinfo_operations = { diff --git a/fs/proc/internal.h b/fs/proc/internal.h index d1598576506c1..2807306004e5e 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -257,8 +257,8 @@ extern int proc_pid_statm(struct seq_file *, struct pid_namespace *, extern const struct dentry_operations pid_dentry_operations; extern int pid_getattr(struct mnt_idmap *, const struct path *, struct kstat *, u32, unsigned int); -extern int proc_setattr(struct mnt_idmap *, struct dentry *, - struct iattr *); +int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); extern void proc_pid_evict_inode(struct proc_inode *); extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *, umode_t); extern void pid_update_inode(struct task_struct *, struct inode *); diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index ea2b597fd92cd..39f4169f669f6 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -92,7 +92,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl static const struct inode_operations proc_ns_link_inode_operations = { .readlink = proc_ns_readlink, .get_link = proc_ns_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_ns_instantiate(struct dentry *dentry, @@ -178,5 +178,5 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, const struct inode_operations proc_ns_dir_inode_operations = { .lookup = proc_ns_dir_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 52f0b75cbce2c..184cddeb8215c 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -322,7 +322,7 @@ static int proc_tgid_net_getattr(struct mnt_idmap *idmap, const struct inode_operations proc_net_inode_operations = { .lookup = proc_tgid_net_lookup, .getattr = proc_tgid_net_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx) From 497c6bae5167428596575f20af6613ff5671f383 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Thu, 16 Jul 2026 12:45:14 -0400 Subject: [PATCH 2170/3297] proc: protect ptrace_may_access() with exec_update_lock (FD links) [ Upstream commit 6255da28d4bb5349fe18e84cb043ccd394eba75d ] proc_pid_get_link() and proc_pid_readlink() currently look up the task from the pid once, then do the ptrace access check on that task, then look up the task from the pid a second time to do the actual access. That's racy in several ways. To fix it, pass the task to the ->proc_get_link() handler, and instead of proc_fd_access_allowed(), introduce a new helper call_proc_get_link() that looks up and locks the task, does the access check, and calls ->proc_get_link(). Fixes: 778c1144771f ("[PATCH] proc: Use sane permission checks on the /proc//fd/ symlinks") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-2-5c3d20e0ac33@google.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/proc/base.c | 119 ++++++++++++++++++--------------------------- fs/proc/fd.c | 27 +++++----- fs/proc/internal.h | 2 +- 3 files changed, 59 insertions(+), 89 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 998c6cce77c19..9e1c4c7fd6913 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -218,33 +218,24 @@ static int get_task_root(struct task_struct *task, struct path *root) return result; } -static int proc_cwd_link(struct dentry *dentry, struct path *path) +static int proc_cwd_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { - struct task_struct *task = get_proc_task(d_inode(dentry)); int result = -ENOENT; - if (task) { - task_lock(task); - if (task->fs) { - get_fs_pwd(task->fs, path); - result = 0; - } - task_unlock(task); - put_task_struct(task); + task_lock(task); + if (task->fs) { + get_fs_pwd(task->fs, path); + result = 0; } + task_unlock(task); return result; } -static int proc_root_link(struct dentry *dentry, struct path *path) +static int proc_root_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { - struct task_struct *task = get_proc_task(d_inode(dentry)); - int result = -ENOENT; - - if (task) { - result = get_task_root(task, path); - put_task_struct(task); - } - return result; + return get_task_root(task, path); } /* @@ -704,23 +695,6 @@ static int proc_pid_syscall(struct seq_file *m, struct pid_namespace *ns, /* Here the fs part begins */ /************************************************************************/ -/* permission checks */ -static bool proc_fd_access_allowed(struct inode *inode) -{ - struct task_struct *task; - bool allowed = false; - /* Allow access to a task's file descriptors if it is us or we - * may use ptrace attach to the process and find out that - * information. - */ - task = get_proc_task(inode); - if (task) { - allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); - put_task_struct(task); - } - return allowed; -} - int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { @@ -1777,16 +1751,12 @@ static const struct file_operations proc_pid_set_comm_operations = { .release = single_release, }; -static int proc_exe_link(struct dentry *dentry, struct path *exe_path) +static int proc_exe_link(struct dentry *dentry, struct path *exe_path, + struct task_struct *task) { - struct task_struct *task; struct file *exe_file; - task = get_proc_task(d_inode(dentry)); - if (!task) - return -ENOENT; exe_file = get_task_exe_file(task); - put_task_struct(task); if (exe_file) { *exe_path = exe_file->f_path; path_get(&exe_file->f_path); @@ -1796,26 +1766,42 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) return -ENOENT; } +static int call_proc_get_link(struct dentry *dentry, struct inode *inode, struct path *path_out) +{ + struct task_struct *task; + int ret; + + task = get_proc_task(inode); + if (!task) + return -ENOENT; + ret = down_read_killable(&task->signal->exec_update_lock); + if (ret) + goto out_put_task; + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { + ret = -EACCES; + goto out; + } + ret = PROC_I(inode)->op.proc_get_link(dentry, path_out, task); + +out: + up_read(&task->signal->exec_update_lock); +out_put_task: + put_task_struct(task); + return ret; +} + static const char *proc_pid_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { struct path path; - int error = -EACCES; + int error; if (!dentry) return ERR_PTR(-ECHILD); - - /* Are we allowed to snoop on the tasks file descriptors? */ - if (!proc_fd_access_allowed(inode)) - goto out; - - error = PROC_I(inode)->op.proc_get_link(dentry, &path); - if (error) - goto out; - - error = nd_jump_link(&path); -out: + error = call_proc_get_link(dentry, inode, &path); + if (!error) + error = nd_jump_link(&path); return ERR_PTR(error); } @@ -1849,17 +1835,11 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int b struct inode *inode = d_inode(dentry); struct path path; - /* Are we allowed to snoop on the tasks file descriptors? */ - if (!proc_fd_access_allowed(inode)) - goto out; - - error = PROC_I(inode)->op.proc_get_link(dentry, &path); - if (error) - goto out; - - error = do_proc_readlink(&path, buffer, buflen); - path_put(&path); -out: + error = call_proc_get_link(dentry, inode, &path); + if (!error) { + error = do_proc_readlink(&path, buffer, buflen); + path_put(&path); + } return error; } @@ -2250,21 +2230,16 @@ static const struct dentry_operations tid_map_files_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int map_files_get_link(struct dentry *dentry, struct path *path) +static int map_files_get_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { unsigned long vm_start, vm_end; struct vm_area_struct *vma; - struct task_struct *task; struct mm_struct *mm; int rc; rc = -ENOENT; - task = get_proc_task(d_inode(dentry)); - if (!task) - goto out; - mm = get_task_mm(task); - put_task_struct(task); if (!mm) goto out; diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 8a48ff4a19ecb..5d6dac7fd1c8e 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -171,24 +171,19 @@ static const struct dentry_operations tid_fd_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int proc_fd_link(struct dentry *dentry, struct path *path) +static int proc_fd_link(struct dentry *dentry, struct path *path, + struct task_struct *task) { - struct task_struct *task; int ret = -ENOENT; - - task = get_proc_task(d_inode(dentry)); - if (task) { - unsigned int fd = proc_fd(d_inode(dentry)); - struct file *fd_file; - - fd_file = fget_task(task, fd); - if (fd_file) { - *path = fd_file->f_path; - path_get(&fd_file->f_path); - ret = 0; - fput(fd_file); - } - put_task_struct(task); + unsigned int fd = proc_fd(d_inode(dentry)); + struct file *fd_file; + + fd_file = fget_task(task, fd); + if (fd_file) { + *path = fd_file->f_path; + path_get(&fd_file->f_path); + ret = 0; + fput(fd_file); } return ret; diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 2807306004e5e..77c347d3b6ad2 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -107,7 +107,7 @@ extern struct kmem_cache *proc_dir_entry_cache; void pde_free(struct proc_dir_entry *pde); union proc_op { - int (*proc_get_link)(struct dentry *, struct path *); + int (*proc_get_link)(struct dentry *, struct path *, struct task_struct *); int (*proc_show)(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task); From c897fd63762e0189c08795c7423def5291c3d0fa Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 17 Jul 2026 07:18:32 -0400 Subject: [PATCH 2171/3297] seqlock: Introduce scoped_seqlock_read() [ Upstream commit cc39f3872c0865bef992b713338df369554fa9e0 ] The read_seqbegin/need_seqretry/done_seqretry API is cumbersome and error prone. With the new helper the "typical" code like int seq, nextseq; unsigned long flags; nextseq = 0; do { seq = nextseq; flags = read_seqbegin_or_lock_irqsave(&seqlock, &seq); // read-side critical section nextseq = 1; } while (need_seqretry(&seqlock, seq)); done_seqretry_irqrestore(&seqlock, seq, flags); can be rewritten as scoped_seqlock_read (&seqlock, ss_lock_irqsave) { // read-side critical section } Original idea by Oleg Nesterov; with contributions from Linus. Originally-by: Oleg Nesterov Signed-off-by: Peter Zijlstra (Intel) Stable-dep-of: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/seqlock.h | 111 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 5ce48eab7a2ad..b7bcc4111e908 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -1209,4 +1209,115 @@ done_seqretry_irqrestore(seqlock_t *lock, int seq, unsigned long flags) if (seq & 1) read_sequnlock_excl_irqrestore(lock, flags); } + +enum ss_state { + ss_done = 0, + ss_lock, + ss_lock_irqsave, + ss_lockless, +}; + +struct ss_tmp { + enum ss_state state; + unsigned long data; + spinlock_t *lock; + spinlock_t *lock_irqsave; +}; + +static inline void __scoped_seqlock_cleanup(struct ss_tmp *sst) +{ + if (sst->lock) + spin_unlock(sst->lock); + if (sst->lock_irqsave) + spin_unlock_irqrestore(sst->lock_irqsave, sst->data); +} + +extern void __scoped_seqlock_invalid_target(void); + +#if defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000 +/* + * For some reason some GCC-8 architectures (nios2, alpha) have trouble + * determining that the ss_done state is impossible in __scoped_seqlock_next() + * below. + */ +static inline void __scoped_seqlock_bug(void) { } +#else +/* + * Canary for compiler optimization -- if the compiler doesn't realize this is + * an impossible state, it very likely generates sub-optimal code here. + */ +extern void __scoped_seqlock_bug(void); +#endif + +static inline void +__scoped_seqlock_next(struct ss_tmp *sst, seqlock_t *lock, enum ss_state target) +{ + switch (sst->state) { + case ss_done: + __scoped_seqlock_bug(); + return; + + case ss_lock: + case ss_lock_irqsave: + sst->state = ss_done; + return; + + case ss_lockless: + if (!read_seqretry(lock, sst->data)) { + sst->state = ss_done; + return; + } + break; + } + + switch (target) { + case ss_done: + __scoped_seqlock_invalid_target(); + return; + + case ss_lock: + sst->lock = &lock->lock; + spin_lock(sst->lock); + sst->state = ss_lock; + return; + + case ss_lock_irqsave: + sst->lock_irqsave = &lock->lock; + spin_lock_irqsave(sst->lock_irqsave, sst->data); + sst->state = ss_lock_irqsave; + return; + + case ss_lockless: + sst->data = read_seqbegin(lock); + return; + } +} + +#define __scoped_seqlock_read(_seqlock, _target, _s) \ + for (struct ss_tmp _s __cleanup(__scoped_seqlock_cleanup) = \ + { .state = ss_lockless, .data = read_seqbegin(_seqlock) }; \ + _s.state != ss_done; \ + __scoped_seqlock_next(&_s, _seqlock, _target)) + +/** + * scoped_seqlock_read (lock, ss_state) - execute the read side critical + * section without manual sequence + * counter handling or calls to other + * helpers + * @lock: pointer to seqlock_t protecting the data + * @ss_state: one of {ss_lock, ss_lock_irqsave, ss_lockless} indicating + * the type of critical read section + * + * Example: + * + * scoped_seqlock_read (&lock, ss_lock) { + * // read-side critical section + * } + * + * Starts with a lockess pass first. If it fails, restarts the critical + * section with the lock held. + */ +#define scoped_seqlock_read(_seqlock, _target) \ + __scoped_seqlock_read(_seqlock, _target, __UNIQUE_ID(seqlock)) + #endif /* __LINUX_SEQLOCK_H */ From 07bf18dc63f78026166898e8ed0ce6209ded84ed Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 17 Jul 2026 07:18:33 -0400 Subject: [PATCH 2172/3297] seqlock: Change do_task_stat() to use scoped_seqlock_read() [ Upstream commit b76f72bea2c601afec81829ea427fc0d20f83216 ] To simplify the code and make it more readable. [peterz: change to new interface] Signed-off-by: Oleg Nesterov Signed-off-by: Peter Zijlstra (Intel) Stable-dep-of: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/proc/array.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 038d4b57127fe..4602a38f85d82 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -481,7 +481,6 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, unsigned long flags; int exit_code = task->exit_code; struct signal_struct *sig = task->signal; - unsigned int seq = 1; state = *get_task_state(task); vsize = eip = esp = 0; @@ -538,10 +537,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, if (permitted && (!whole || num_threads < 2)) wchan = !task_is_running(task); - do { - seq++; /* 2 on the 1st/lockless path, otherwise odd */ - flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq); - + scoped_seqlock_read (&sig->stats_lock, ss_lock_irqsave) { cmin_flt = sig->cmin_flt; cmaj_flt = sig->cmaj_flt; cutime = sig->cutime; @@ -563,8 +559,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, } rcu_read_unlock(); } - } while (need_seqretry(&sig->stats_lock, seq)); - done_seqretry_irqrestore(&sig->stats_lock, seq, flags); + } if (whole) { thread_group_cputime_adjusted(task, &utime, &stime); From f9b4b03ccc9c69bf7f7298d4559906ebea7143b3 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 17 Jul 2026 07:18:34 -0400 Subject: [PATCH 2173/3297] proc: protect ptrace_may_access() with exec_update_lock (part 1) [ Upstream commit 6650527444dadc63d84aa939d14ecba4fadb2f69 ] Fix the easy cases where procfs currently calls ptrace_may_access() without exec_update_lock protection, where the fix is to simply add the extra lock or use mm_access(): - do_task_stat(): grab exec_update_lock - proc_pid_wchan(): grab exec_update_lock - proc_map_files_lookup(): use mm_access() instead of get_task_mm() - proc_map_files_readdir(): use mm_access() instead of get_task_mm() - proc_ns_get_link(): grab exec_update_lock - proc_ns_readlink(): grab exec_update_lock Fixes: f83ce3e6b02d ("proc: avoid information leaks to non-privileged processes") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-1-5c3d20e0ac33@google.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/proc/array.c | 6 ++++++ fs/proc/base.c | 41 ++++++++++++++++++++++------------------- fs/proc/namespaces.c | 12 ++++++++++++ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 4602a38f85d82..38f96af7b521b 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -481,6 +481,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, unsigned long flags; int exit_code = task->exit_code; struct signal_struct *sig = task->signal; + int ret; + + ret = down_read_killable(&task->signal->exec_update_lock); + if (ret) + return ret; state = *get_task_state(task); vsize = eip = esp = 0; @@ -656,6 +661,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, seq_puts(m, " 0"); seq_putc(m, '\n'); + up_read(&task->signal->exec_update_lock); if (mm) mmput(mm); return 0; diff --git a/fs/proc/base.c b/fs/proc/base.c index 9e1c4c7fd6913..c8d783f3b2ac6 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -414,18 +414,24 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns, { unsigned long wchan; char symname[KSYM_NAME_LEN]; + int err; + err = down_read_killable(&task->signal->exec_update_lock); + if (err) + return err; if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) goto print0; wchan = get_wchan(task); if (wchan && !lookup_symbol_name(wchan, symname)) { seq_puts(m, symname); + up_read(&task->signal->exec_update_lock); return 0; } print0: seq_putc(m, '0'); + up_read(&task->signal->exec_update_lock); return 0; } #endif /* CONFIG_KALLSYMS */ @@ -2335,17 +2341,15 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, if (!task) goto out; - result = ERR_PTR(-EACCES); - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) - goto out_put_task; - result = ERR_PTR(-ENOENT); if (dname_to_vma_addr(dentry, &vm_start, &vm_end)) goto out_put_task; - mm = get_task_mm(task); - if (!mm) + mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); + if (IS_ERR(mm)) { + result = ERR_CAST(mm); goto out_put_task; + } result = ERR_PTR(-EINTR); if (mmap_read_lock_killable(mm)) @@ -2395,23 +2399,22 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) if (!task) goto out; - ret = -EACCES; - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) - goto out_put_task; - ret = 0; if (!dir_emit_dots(file, ctx)) goto out_put_task; - mm = get_task_mm(task); - if (!mm) + mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); + if (IS_ERR(mm)) { + ret = PTR_ERR(mm); + /* if the task has no mm, the directory should just be empty */ + if (ret == -ESRCH) + ret = 0; goto out_put_task; + } ret = mmap_read_lock_killable(mm); - if (ret) { - mmput(mm); - goto out_put_task; - } + if (ret) + goto out_put_mm; nr_files = 0; @@ -2437,8 +2440,7 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) if (!p) { ret = -ENOMEM; mmap_read_unlock(mm); - mmput(mm); - goto out_put_task; + goto out_put_mm; } p->start = vma->vm_start; @@ -2446,7 +2448,6 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) p->mode = vma->vm_file->f_mode; } mmap_read_unlock(mm); - mmput(mm); for (i = 0; i < nr_files; i++) { char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */ @@ -2463,6 +2464,8 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx) ctx->pos++; } +out_put_mm: + mmput(mm); out_put_task: put_task_struct(task); out: diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 39f4169f669f6..2f46f13967445 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -55,6 +55,10 @@ static const char *proc_ns_get_link(struct dentry *dentry, if (!task) return ERR_PTR(-EACCES); + error = down_read_killable(&task->signal->exec_update_lock); + if (error) + goto out_put_task; + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) goto out; @@ -64,6 +68,8 @@ static const char *proc_ns_get_link(struct dentry *dentry, error = nd_jump_link(&ns_path); out: + up_read(&task->signal->exec_update_lock); +out_put_task: put_task_struct(task); return ERR_PTR(error); } @@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl if (!task) return res; + res = down_read_killable(&task->signal->exec_update_lock); + if (res) + goto out_put_task; + if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { res = ns_get_name(name, sizeof(name), task, ns_ops); if (res >= 0) res = readlink_copy(buffer, buflen, name, strlen(name)); } + up_read(&task->signal->exec_update_lock); +out_put_task: put_task_struct(task); return res; } From 7676ea09beb5d2c861db2b9d4bd5fffef600f836 Mon Sep 17 00:00:00 2001 From: Viacheslav Dubeyko Date: Fri, 17 Jul 2026 15:55:15 -0400 Subject: [PATCH 2174/3297] hfs/hfsplus: prevent getting negative values of offset/length [ Upstream commit 00c14a09a70e10ae18eb3707d0059291425c04bd ] The syzbot reported KASAN out-of-bounds issue in hfs_bnode_move(): [ 45.588165][ T9821] hfs: dst 14, src 65536, len -65536 [ 45.588895][ T9821] ================================================================== [ 45.590114][ T9821] BUG: KASAN: out-of-bounds in hfs_bnode_move+0xfd/0x140 [ 45.591127][ T9821] Read of size 18446744073709486080 at addr ffff888035935400 by task repro/9821 [ 45.592207][ T9821] [ 45.592420][ T9821] CPU: 0 UID: 0 PID: 9821 Comm: repro Not tainted 6.16.0-rc7-dirty #42 PREEMPT(full) [ 45.592428][ T9821] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 45.592431][ T9821] Call Trace: [ 45.592434][ T9821] [ 45.592437][ T9821] dump_stack_lvl+0x1c1/0x2a0 [ 45.592446][ T9821] ? __virt_addr_valid+0x1c8/0x5c0 [ 45.592454][ T9821] ? __pfx_dump_stack_lvl+0x10/0x10 [ 45.592461][ T9821] ? rcu_is_watching+0x15/0xb0 [ 45.592469][ T9821] ? lock_release+0x4b/0x3e0 [ 45.592476][ T9821] ? __virt_addr_valid+0x1c8/0x5c0 [ 45.592483][ T9821] ? __virt_addr_valid+0x4a5/0x5c0 [ 45.592491][ T9821] print_report+0x17e/0x7c0 [ 45.592497][ T9821] ? __virt_addr_valid+0x1c8/0x5c0 [ 45.592504][ T9821] ? __virt_addr_valid+0x4a5/0x5c0 [ 45.592511][ T9821] ? __phys_addr+0xd3/0x180 [ 45.592519][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592526][ T9821] kasan_report+0x147/0x180 [ 45.592531][ T9821] ? _printk+0xcf/0x120 [ 45.592537][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592544][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592552][ T9821] kasan_check_range+0x2b0/0x2c0 [ 45.592557][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592565][ T9821] __asan_memmove+0x29/0x70 [ 45.592572][ T9821] hfs_bnode_move+0xfd/0x140 [ 45.592580][ T9821] hfs_brec_remove+0x473/0x560 [ 45.592589][ T9821] hfs_cat_move+0x6fb/0x960 [ 45.592598][ T9821] ? __pfx_hfs_cat_move+0x10/0x10 [ 45.592607][ T9821] ? seqcount_lockdep_reader_access+0x122/0x1c0 [ 45.592614][ T9821] ? lockdep_hardirqs_on+0x9c/0x150 [ 45.592631][ T9821] ? __lock_acquire+0xaec/0xd80 [ 45.592641][ T9821] hfs_rename+0x1dc/0x2d0 [ 45.592649][ T9821] ? __pfx_hfs_rename+0x10/0x10 [ 45.592657][ T9821] vfs_rename+0xac6/0xed0 [ 45.592664][ T9821] ? __pfx_vfs_rename+0x10/0x10 [ 45.592670][ T9821] ? d_alloc+0x144/0x190 [ 45.592677][ T9821] ? bpf_lsm_path_rename+0x9/0x20 [ 45.592683][ T9821] ? security_path_rename+0x17d/0x490 [ 45.592691][ T9821] do_renameat2+0x890/0xc50 [ 45.592699][ T9821] ? __pfx_do_renameat2+0x10/0x10 [ 45.592707][ T9821] ? getname_flags+0x1e5/0x540 [ 45.592714][ T9821] __x64_sys_rename+0x82/0x90 [ 45.592720][ T9821] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 45.592725][ T9821] do_syscall_64+0xf3/0x3a0 [ 45.592741][ T9821] ? exc_page_fault+0x9f/0xf0 [ 45.592748][ T9821] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 45.592754][ T9821] RIP: 0033:0x7f7f73fe3fc9 [ 45.592760][ T9821] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 48 [ 45.592765][ T9821] RSP: 002b:00007ffc7e116cf8 EFLAGS: 00000283 ORIG_RAX: 0000000000000052 [ 45.592772][ T9821] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f7f73fe3fc9 [ 45.592776][ T9821] RDX: 0000200000000871 RSI: 0000200000000780 RDI: 00002000000003c0 [ 45.592781][ T9821] RBP: 00007ffc7e116d00 R08: 0000000000000000 R09: 00007ffc7e116d30 [ 45.592784][ T9821] R10: fffffffffffffff0 R11: 0000000000000283 R12: 00005557e81f8250 [ 45.592788][ T9821] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 [ 45.592795][ T9821] [ 45.592797][ T9821] [ 45.619721][ T9821] The buggy address belongs to the physical page: [ 45.620300][ T9821] page: refcount:1 mapcount:1 mapping:0000000000000000 index:0x559a88174 pfn:0x35935 [ 45.621150][ T9821] memcg:ffff88810a1d5b00 [ 45.621531][ T9821] anon flags: 0xfff60000020838(uptodate|dirty|lru|owner_2|swapbacked|node=0|zone=1|lastcpupid=0x7ff) [ 45.622496][ T9821] raw: 00fff60000020838 ffffea0000d64d88 ffff888021753e10 ffff888029da0771 [ 45.623260][ T9821] raw: 0000000559a88174 0000000000000000 0000000100000000 ffff88810a1d5b00 [ 45.624030][ T9821] page dumped because: kasan: bad access detected [ 45.624602][ T9821] page_owner tracks the page as allocated [ 45.625115][ T9821] page last allocated via order 0, migratetype Movable, gfp_mask 0x140dca(GFP_HIGHUSER_MOVABLE|__GFP_ZERO0 [ 45.626685][ T9821] post_alloc_hook+0x240/0x2a0 [ 45.627127][ T9821] get_page_from_freelist+0x2101/0x21e0 [ 45.627628][ T9821] __alloc_frozen_pages_noprof+0x274/0x380 [ 45.628154][ T9821] alloc_pages_mpol+0x241/0x4b0 [ 45.628593][ T9821] vma_alloc_folio_noprof+0xe4/0x210 [ 45.629066][ T9821] folio_prealloc+0x30/0x180 [ 45.629487][ T9821] __handle_mm_fault+0x34bd/0x5640 [ 45.629957][ T9821] handle_mm_fault+0x40e/0x8e0 [ 45.630392][ T9821] do_user_addr_fault+0xa81/0x1390 [ 45.630862][ T9821] exc_page_fault+0x76/0xf0 [ 45.631273][ T9821] asm_exc_page_fault+0x26/0x30 [ 45.631712][ T9821] page last free pid 5269 tgid 5269 stack trace: [ 45.632281][ T9821] free_unref_folios+0xc73/0x14c0 [ 45.632740][ T9821] folios_put_refs+0x55b/0x640 [ 45.633177][ T9821] free_pages_and_swap_cache+0x26d/0x510 [ 45.633685][ T9821] tlb_flush_mmu+0x3a0/0x680 [ 45.634105][ T9821] tlb_finish_mmu+0xd4/0x200 [ 45.634525][ T9821] exit_mmap+0x44c/0xb70 [ 45.634914][ T9821] __mmput+0x118/0x420 [ 45.635286][ T9821] exit_mm+0x1da/0x2c0 [ 45.635659][ T9821] do_exit+0x652/0x2330 [ 45.636039][ T9821] do_group_exit+0x21c/0x2d0 [ 45.636457][ T9821] __x64_sys_exit_group+0x3f/0x40 [ 45.636915][ T9821] x64_sys_call+0x21ba/0x21c0 [ 45.637342][ T9821] do_syscall_64+0xf3/0x3a0 [ 45.637756][ T9821] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 45.638290][ T9821] page has been migrated, last migrate reason: numa_misplaced [ 45.638956][ T9821] [ 45.639173][ T9821] Memory state around the buggy address: [ 45.639677][ T9821] ffff888035935300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.640397][ T9821] ffff888035935380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.641117][ T9821] >ffff888035935400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.641837][ T9821] ^ [ 45.642207][ T9821] ffff888035935480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.642929][ T9821] ffff888035935500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.643650][ T9821] ================================================================== This commit [1] fixes the issue if an offset inside of b-tree node or length of the request is bigger than b-tree node. However, this fix is still not ready for negative values of the offset or length. Moreover, negative values of the offset or length doesn't make sense for b-tree's operations. Because we could try to access the memory address outside of the beginning of memory page's addresses range. Also, using of negative values make logic very complicated, unpredictable, and we could access the wrong item(s) in the b-tree node. This patch changes b-tree interface by means of converting signed integer arguments of offset and length on u32 type. Such conversion has goal to prevent of using negative values unintentionally or by mistake in b-tree operations. [1] 'commit a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()")' Signed-off-by: Viacheslav Dubeyko cc: John Paul Adrian Glaubitz cc: Yangtao Li cc: linux-fsdevel@vger.kernel.org Link: https://lore.kernel.org/r/20251002200020.2578311-1-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko Stable-dep-of: 966cb76fb285 ("hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/hfs/bfind.c | 2 +- fs/hfs/bnode.c | 52 ++++++++++++------------ fs/hfs/brec.c | 2 +- fs/hfs/btree.c | 2 +- fs/hfs/btree.h | 71 +++++++++++++++++---------------- fs/hfs/hfs_fs.h | 88 ++++++++++++++++++++++++----------------- fs/hfs/inode.c | 3 +- fs/hfsplus/bfind.c | 2 +- fs/hfsplus/bnode.c | 60 ++++++++++++++-------------- fs/hfsplus/brec.c | 2 +- fs/hfsplus/btree.c | 2 +- fs/hfsplus/hfsplus_fs.h | 38 +++++++++--------- 12 files changed, 171 insertions(+), 153 deletions(-) diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c index c2f840c49e60b..d56e47bdc517d 100644 --- a/fs/hfs/bfind.c +++ b/fs/hfs/bfind.c @@ -167,7 +167,7 @@ int hfs_brec_find(struct hfs_find_data *fd) return res; } -int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) +int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len) { int res; diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index c5ebff5f68875..4742760730ab0 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -16,14 +16,14 @@ #include "btree.h" static inline -bool is_bnode_offset_valid(struct hfs_bnode *node, int off) +bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off) { bool is_valid = off < node->tree->node_size; if (!is_valid) { pr_err("requested invalid offset: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d\n", + "node_size %u, offset %u\n", node->this, node->type, node->height, node->tree->node_size, off); } @@ -32,7 +32,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off) } static inline -int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len) +u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) { unsigned int node_size; @@ -42,12 +42,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len) node_size = node->tree->node_size; if ((off + len) > node_size) { - int new_len = (int)node_size - off; + u32 new_len = node_size - off; pr_err("requested length has been corrected: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, " - "requested_len %d, corrected_len %d\n", + "node_size %u, offset %u, " + "requested_len %u, corrected_len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len, new_len); @@ -57,12 +57,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len) return len; } -void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) +void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) { struct page *page; - int pagenum; - int bytes_read; - int bytes_to_read; + u32 pagenum; + u32 bytes_read; + u32 bytes_to_read; memset(buf, 0, len); @@ -72,7 +72,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) if (len == 0) { pr_err("requested zero length: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, len %d\n", + "node_size %u, offset %u, len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len); return; @@ -88,7 +88,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) if (pagenum >= node->tree->pages_per_bnode) break; page = node->page[pagenum]; - bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off); + bytes_to_read = min_t(u32, len - bytes_read, PAGE_SIZE - off); memcpy_from_page(buf + bytes_read, page, off, bytes_to_read); @@ -97,7 +97,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) } } -u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) +u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off) { __be16 data; // optimize later... @@ -105,7 +105,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) return be16_to_cpu(data); } -u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) +u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off) { u8 data; // optimize later... @@ -113,10 +113,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) return data; } -void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) +void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off) { struct hfs_btree *tree; - int key_len; + u32 key_len; tree = node->tree; if (node->type == HFS_NODE_LEAF || @@ -127,14 +127,14 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) if (key_len > sizeof(hfs_btree_key) || key_len < 1) { memset(key, 0, sizeof(hfs_btree_key)); - pr_err("hfs: Invalid key length: %d\n", key_len); + pr_err("hfs: Invalid key length: %u\n", key_len); return; } hfs_bnode_read(node, key, off, key_len); } -void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) +void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len) { struct page *page; @@ -144,7 +144,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) if (len == 0) { pr_err("requested zero length: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, len %d\n", + "node_size %u, offset %u, len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len); return; @@ -159,20 +159,20 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) set_page_dirty(page); } -void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data) +void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data) { __be16 v = cpu_to_be16(data); // optimize later... hfs_bnode_write(node, &v, off, 2); } -void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data) +void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data) { // optimize later... hfs_bnode_write(node, &data, off, 1); } -void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) +void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len) { struct page *page; @@ -182,7 +182,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) if (len == 0) { pr_err("requested zero length: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, len %d\n", + "node_size %u, offset %u, len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len); return; @@ -197,8 +197,8 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) set_page_dirty(page); } -void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, - struct hfs_bnode *src_node, int src, int len) +void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, + struct hfs_bnode *src_node, u32 src, u32 len) { struct page *src_page, *dst_page; @@ -218,7 +218,7 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, set_page_dirty(dst_page); } -void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) +void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len) { struct page *page; void *ptr; diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c index e49a141c87e51..5a2f740ddefd3 100644 --- a/fs/hfs/brec.c +++ b/fs/hfs/brec.c @@ -62,7 +62,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) return retval; } -int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) +int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len) { struct hfs_btree *tree; struct hfs_bnode *node, *new_node; diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index 22e62fe7448bf..b8be01809b631 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -259,7 +259,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx) } /* Make sure @tree has enough space for the @rsvd_nodes */ -int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes) +int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes) { struct inode *inode = tree->inode; u32 count; diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h index 0e6baee932453..97f88035b2248 100644 --- a/fs/hfs/btree.h +++ b/fs/hfs/btree.h @@ -86,48 +86,49 @@ struct hfs_find_data { /* btree.c */ -extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp); -extern void hfs_btree_close(struct hfs_btree *); -extern void hfs_btree_write(struct hfs_btree *); -extern int hfs_bmap_reserve(struct hfs_btree *, int); -extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *); +extern struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, + btree_keycmp keycmp); +extern void hfs_btree_close(struct hfs_btree *tree); +extern void hfs_btree_write(struct hfs_btree *tree); +extern int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes); +extern struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree); extern void hfs_bmap_free(struct hfs_bnode *node); /* bnode.c */ -extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int); -extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int); -extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int); -extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int); -extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int); -extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16); -extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8); -extern void hfs_bnode_clear(struct hfs_bnode *, int, int); -extern void hfs_bnode_copy(struct hfs_bnode *, int, - struct hfs_bnode *, int, int); -extern void hfs_bnode_move(struct hfs_bnode *, int, int, int); -extern void hfs_bnode_dump(struct hfs_bnode *); -extern void hfs_bnode_unlink(struct hfs_bnode *); -extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32); -extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32); -extern void hfs_bnode_unhash(struct hfs_bnode *); -extern void hfs_bnode_free(struct hfs_bnode *); -extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32); -extern void hfs_bnode_get(struct hfs_bnode *); -extern void hfs_bnode_put(struct hfs_bnode *); +extern void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len); +extern u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off); +extern u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off); +extern void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off); +extern void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len); +extern void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data); +extern void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data); +extern void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len); +extern void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, + struct hfs_bnode *src_node, u32 src, u32 len); +extern void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len); +extern void hfs_bnode_dump(struct hfs_bnode *node); +extern void hfs_bnode_unlink(struct hfs_bnode *node); +extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid); +extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num); +extern void hfs_bnode_unhash(struct hfs_bnode *node); +extern void hfs_bnode_free(struct hfs_bnode *node); +extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num); +extern void hfs_bnode_get(struct hfs_bnode *node); +extern void hfs_bnode_put(struct hfs_bnode *node); /* brec.c */ -extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *); -extern u16 hfs_brec_keylen(struct hfs_bnode *, u16); -extern int hfs_brec_insert(struct hfs_find_data *, void *, int); -extern int hfs_brec_remove(struct hfs_find_data *); +extern u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off); +extern u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec); +extern int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len); +extern int hfs_brec_remove(struct hfs_find_data *fd); /* bfind.c */ -extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); -extern void hfs_find_exit(struct hfs_find_data *); -extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *); -extern int hfs_brec_find(struct hfs_find_data *); -extern int hfs_brec_read(struct hfs_find_data *, void *, int); -extern int hfs_brec_goto(struct hfs_find_data *, int); +extern int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd); +extern void hfs_find_exit(struct hfs_find_data *fd); +extern int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd); +extern int hfs_brec_find(struct hfs_find_data *fd); +extern int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len); +extern int hfs_brec_goto(struct hfs_find_data *fd, int cnt); struct hfs_bnode_desc { diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index fff149af89da3..38854df4c1b42 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -140,74 +140,90 @@ struct hfs_sb_info { #define HFS_FLG_ALT_MDB_DIRTY 2 /* bitmap.c */ -extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *); -extern int hfs_clear_vbm_bits(struct super_block *, u16, u16); +extern u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits); +extern int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count); /* catalog.c */ -extern int hfs_cat_keycmp(const btree_key *, const btree_key *); +extern int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2); struct hfs_find_data; -extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *); -extern int hfs_cat_create(u32, struct inode *, const struct qstr *, struct inode *); -extern int hfs_cat_delete(u32, struct inode *, const struct qstr *); -extern int hfs_cat_move(u32, struct inode *, const struct qstr *, - struct inode *, const struct qstr *); -extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, const struct qstr *); +extern int hfs_cat_find_brec(struct super_block *sb, u32 cnid, + struct hfs_find_data *fd); +extern int hfs_cat_create(u32 cnid, struct inode *dir, + const struct qstr *str, struct inode *inode); +extern int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str); +extern int hfs_cat_move(u32 cnid, struct inode *src_dir, + const struct qstr *src_name, + struct inode *dst_dir, + const struct qstr *dst_name); +extern void hfs_cat_build_key(struct super_block *sb, btree_key *key, + u32 parent, const struct qstr *name); /* dir.c */ extern const struct file_operations hfs_dir_operations; extern const struct inode_operations hfs_dir_inode_operations; /* extent.c */ -extern int hfs_ext_keycmp(const btree_key *, const btree_key *); +extern int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2); extern u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off); -extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int); -extern int hfs_ext_write_extent(struct inode *); -extern int hfs_extend_file(struct inode *); -extern void hfs_file_truncate(struct inode *); +extern int hfs_free_fork(struct super_block *sb, + struct hfs_cat_file *file, int type); +extern int hfs_ext_write_extent(struct inode *inode); +extern int hfs_extend_file(struct inode *inode); +extern void hfs_file_truncate(struct inode *inode); -extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int); +extern int hfs_get_block(struct inode *inode, sector_t block, + struct buffer_head *bh_result, int create); /* inode.c */ extern const struct address_space_operations hfs_aops; extern const struct address_space_operations hfs_btree_aops; int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping, - loff_t pos, unsigned len, struct folio **foliop, void **fsdata); -extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t); -extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *); -extern int hfs_write_inode(struct inode *, struct writeback_control *); -extern int hfs_inode_setattr(struct mnt_idmap *, struct dentry *, - struct iattr *); + loff_t pos, unsigned int len, struct folio **foliop, + void **fsdata); +extern struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, + umode_t mode); +extern void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext, + __be32 *log_size, __be32 *phys_size); +extern int hfs_write_inode(struct inode *inode, struct writeback_control *wbc); +extern int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext, - __be32 log_size, __be32 phys_size, u32 clump_size); -extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *); -extern void hfs_evict_inode(struct inode *); -extern void hfs_delete_inode(struct inode *); + __be32 __log_size, __be32 phys_size, + u32 clump_size); +extern struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key, + hfs_cat_rec *rec); +extern void hfs_evict_inode(struct inode *inode); +extern void hfs_delete_inode(struct inode *inode); /* attr.c */ extern const struct xattr_handler * const hfs_xattr_handlers[]; /* mdb.c */ -extern int hfs_mdb_get(struct super_block *); -extern void hfs_mdb_commit(struct super_block *); -extern void hfs_mdb_close(struct super_block *); -extern void hfs_mdb_put(struct super_block *); +extern int hfs_mdb_get(struct super_block *sb); +extern void hfs_mdb_commit(struct super_block *sb); +extern void hfs_mdb_close(struct super_block *sb); +extern void hfs_mdb_put(struct super_block *sb); /* part_tbl.c */ -extern int hfs_part_find(struct super_block *, sector_t *, sector_t *); +extern int hfs_part_find(struct super_block *sb, + sector_t *part_start, sector_t *part_size); /* string.c */ extern const struct dentry_operations hfs_dentry_operations; -extern int hfs_hash_dentry(const struct dentry *, struct qstr *); -extern int hfs_strcmp(const unsigned char *, unsigned int, - const unsigned char *, unsigned int); +extern int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this); +extern int hfs_strcmp(const unsigned char *s1, unsigned int len1, + const unsigned char *s2, unsigned int len2); extern int hfs_compare_dentry(const struct dentry *dentry, - unsigned int len, const char *str, const struct qstr *name); + unsigned int len, const char *str, + const struct qstr *name); /* trans.c */ -extern void hfs_asc2mac(struct super_block *, struct hfs_name *, const struct qstr *); -extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *); +extern void hfs_asc2mac(struct super_block *sb, + struct hfs_name *out, const struct qstr *in); +extern int hfs_mac2asc(struct super_block *sb, + char *out, const struct hfs_name *in); /* super.c */ extern void hfs_mark_mdb_dirty(struct super_block *sb); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 9cd449913dc82..cd43eff72d13d 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -45,7 +45,8 @@ static void hfs_write_failed(struct address_space *mapping, loff_t to) } int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping, - loff_t pos, unsigned len, struct folio **foliop, void **fsdata) + loff_t pos, unsigned int len, struct folio **foliop, + void **fsdata) { int ret; diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c index 9e3c19138ae47..9a55fa6d52942 100644 --- a/fs/hfsplus/bfind.c +++ b/fs/hfsplus/bfind.c @@ -210,7 +210,7 @@ int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare) return res; } -int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) +int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len) { int res; diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index 76dea3bc5fffe..c8cbabd63986a 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -20,10 +20,10 @@ /* Copy a specified range of bytes from the raw data of a node */ -void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) +void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len) { struct page **pagep; - int l; + u32 l; memset(buf, 0, len); @@ -33,7 +33,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) if (len == 0) { pr_err("requested zero length: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, len %d\n", + "node_size %u, offset %u, len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len); return; @@ -45,17 +45,17 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len) pagep = node->page + (off >> PAGE_SHIFT); off &= ~PAGE_MASK; - l = min_t(int, len, PAGE_SIZE - off); + l = min_t(u32, len, PAGE_SIZE - off); memcpy_from_page(buf, *pagep, off, l); while ((len -= l) != 0) { buf += l; - l = min_t(int, len, PAGE_SIZE); + l = min_t(u32, len, PAGE_SIZE); memcpy_from_page(buf, *++pagep, 0, l); } } -u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) +u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off) { __be16 data; /* TODO: optimize later... */ @@ -63,7 +63,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off) return be16_to_cpu(data); } -u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) +u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off) { u8 data; /* TODO: optimize later... */ @@ -71,10 +71,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off) return data; } -void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) +void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off) { struct hfs_btree *tree; - int key_len; + u32 key_len; tree = node->tree; if (node->type == HFS_NODE_LEAF || @@ -86,17 +86,17 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off) if (key_len > sizeof(hfsplus_btree_key) || key_len < 1) { memset(key, 0, sizeof(hfsplus_btree_key)); - pr_err("hfsplus: Invalid key length: %d\n", key_len); + pr_err("hfsplus: Invalid key length: %u\n", key_len); return; } hfs_bnode_read(node, key, off, key_len); } -void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) +void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len) { struct page **pagep; - int l; + u32 l; if (!is_bnode_offset_valid(node, off)) return; @@ -104,7 +104,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) if (len == 0) { pr_err("requested zero length: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, len %d\n", + "node_size %u, offset %u, len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len); return; @@ -116,29 +116,29 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len) pagep = node->page + (off >> PAGE_SHIFT); off &= ~PAGE_MASK; - l = min_t(int, len, PAGE_SIZE - off); + l = min_t(u32, len, PAGE_SIZE - off); memcpy_to_page(*pagep, off, buf, l); set_page_dirty(*pagep); while ((len -= l) != 0) { buf += l; - l = min_t(int, len, PAGE_SIZE); + l = min_t(u32, len, PAGE_SIZE); memcpy_to_page(*++pagep, 0, buf, l); set_page_dirty(*pagep); } } -void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data) +void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data) { __be16 v = cpu_to_be16(data); /* TODO: optimize later... */ hfs_bnode_write(node, &v, off, 2); } -void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) +void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len) { struct page **pagep; - int l; + u32 l; if (!is_bnode_offset_valid(node, off)) return; @@ -146,7 +146,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) if (len == 0) { pr_err("requested zero length: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, len %d\n", + "node_size %u, offset %u, len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len); return; @@ -158,22 +158,22 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len) pagep = node->page + (off >> PAGE_SHIFT); off &= ~PAGE_MASK; - l = min_t(int, len, PAGE_SIZE - off); + l = min_t(u32, len, PAGE_SIZE - off); memzero_page(*pagep, off, l); set_page_dirty(*pagep); while ((len -= l) != 0) { - l = min_t(int, len, PAGE_SIZE); + l = min_t(u32, len, PAGE_SIZE); memzero_page(*++pagep, 0, l); set_page_dirty(*pagep); } } -void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, - struct hfs_bnode *src_node, int src, int len) +void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, + struct hfs_bnode *src_node, u32 src, u32 len) { struct page **src_page, **dst_page; - int l; + u32 l; hfs_dbg("dst %u, src %u, len %u\n", dst, src, len); if (!len) @@ -190,12 +190,12 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, dst &= ~PAGE_MASK; if (src == dst) { - l = min_t(int, len, PAGE_SIZE - src); + l = min_t(u32, len, PAGE_SIZE - src); memcpy_page(*dst_page, src, *src_page, src, l); set_page_dirty(*dst_page); while ((len -= l) != 0) { - l = min_t(int, len, PAGE_SIZE); + l = min_t(u32, len, PAGE_SIZE); memcpy_page(*++dst_page, 0, *++src_page, 0, l); set_page_dirty(*dst_page); } @@ -227,11 +227,11 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, } } -void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) +void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len) { struct page **src_page, **dst_page; void *src_ptr, *dst_ptr; - int l; + u32 l; hfs_dbg("dst %u, src %u, len %u\n", dst, src, len); if (!len) @@ -301,7 +301,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) dst &= ~PAGE_MASK; if (src == dst) { - l = min_t(int, len, PAGE_SIZE - src); + l = min_t(u32, len, PAGE_SIZE - src); dst_ptr = kmap_local_page(*dst_page) + src; src_ptr = kmap_local_page(*src_page) + src; @@ -311,7 +311,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len) kunmap_local(dst_ptr); while ((len -= l) != 0) { - l = min_t(int, len, PAGE_SIZE); + l = min_t(u32, len, PAGE_SIZE); dst_ptr = kmap_local_page(*++dst_page); src_ptr = kmap_local_page(*++src_page); memmove(dst_ptr, src_ptr, l); diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c index b4645102feecd..6796c1a80e997 100644 --- a/fs/hfsplus/brec.c +++ b/fs/hfsplus/brec.c @@ -60,7 +60,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) return retval; } -int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) +int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len) { struct hfs_btree *tree; struct hfs_bnode *node, *new_node; diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 7cc5aea145720..229f25dc7c49c 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -344,7 +344,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx) } /* Make sure @tree has enough space for the @rsvd_nodes */ -int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes) +int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes) { struct inode *inode = tree->inode; struct hfsplus_inode_info *hip = HFSPLUS_I(inode); diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index d0eb2a4e06b44..d2cc0f9f3d513 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -357,21 +357,21 @@ u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, u64 sectors, struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id); void hfs_btree_close(struct hfs_btree *tree); int hfs_btree_write(struct hfs_btree *tree); -int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes); +int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes); struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree); void hfs_bmap_free(struct hfs_bnode *node); /* bnode.c */ -void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len); -u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off); -u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off); -void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off); -void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len); -void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data); -void hfs_bnode_clear(struct hfs_bnode *node, int off, int len); -void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst, - struct hfs_bnode *src_node, int src, int len); -void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len); +void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len); +u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off); +u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off); +void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off); +void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len); +void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data); +void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len); +void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst, + struct hfs_bnode *src_node, u32 src, u32 len); +void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len); void hfs_bnode_dump(struct hfs_bnode *node); void hfs_bnode_unlink(struct hfs_bnode *node); struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid); @@ -386,7 +386,7 @@ bool hfs_bnode_need_zeroout(struct hfs_btree *tree); /* brec.c */ u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off); u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec); -int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len); +int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len); int hfs_brec_remove(struct hfs_find_data *fd); /* bfind.c */ @@ -399,7 +399,7 @@ int hfs_find_rec_by_key(struct hfs_bnode *bnode, struct hfs_find_data *fd, int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd, search_strategy_t rec_found); int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare); -int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len); +int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len); int hfs_brec_goto(struct hfs_find_data *fd, int cnt); /* catalog.c */ @@ -560,14 +560,14 @@ hfsplus_btree_lock_class(struct hfs_btree *tree) } static inline -bool is_bnode_offset_valid(struct hfs_bnode *node, int off) +bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off) { bool is_valid = off < node->tree->node_size; if (!is_valid) { pr_err("requested invalid offset: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d\n", + "node_size %u, offset %u\n", node->this, node->type, node->height, node->tree->node_size, off); } @@ -576,7 +576,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off) } static inline -int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len) +u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) { unsigned int node_size; @@ -586,12 +586,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len) node_size = node->tree->node_size; if ((off + len) > node_size) { - int new_len = (int)node_size - off; + u32 new_len = node_size - off; pr_err("requested length has been corrected: " "NODE: id %u, type %#x, height %u, " - "node_size %u, offset %d, " - "requested_len %d, corrected_len %d\n", + "node_size %u, offset %u, " + "requested_len %u, corrected_len %u\n", node->this, node->type, node->height, node->tree->node_size, off, len, new_len); From 607217f7ad419b53926f71e3f75001813bbc08ad Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Fri, 17 Jul 2026 15:55:16 -0400 Subject: [PATCH 2175/3297] hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length [ Upstream commit 966cb76fb2857a4242cab6ea2ea17acf818a3da7 ] check_and_correct_requested_length() compares (off + len) against node_size using u32 arithmetic. When the caller passes a large len value (e.g. from an underflowed subtraction in hfs_brec_remove()), off + len can wrap past 2^32 and produce a small result, causing the bounds check to pass when it should fail. For example, with off=14 and len=0xFFFFFFF2 (underflowed from data_off - keyoffset - size in hfs_brec_remove), off + len wraps to 6, which is less than a typical node_size of 512, so the check passes and the subsequent memmove reads ~4GB past the node buffer. Fix this by widening the addition to u64 before comparing against node_size. This prevents the u32 wrap while keeping the logic straightforward. Reported-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=6df204b70bf3261691c5 Tested-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com Reported-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e76bf3d19b85350571ac Tested-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com Fixes: a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Reviewed-by: Viacheslav Dubeyko Signed-off-by: Viacheslav Dubeyko Link: https://lore.kernel.org/r/20260505111300.3592757-2-tristmd@gmail.com Signed-off-by: Viacheslav Dubeyko Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/hfs/bnode.c | 2 +- fs/hfsplus/hfsplus_fs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 4742760730ab0..25cef62fbba6d 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) node_size = node->tree->node_size; - if ((off + len) > node_size) { + if ((u64)off + len > node_size) { u32 new_len = node_size - off; pr_err("requested length has been corrected: " diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index d2cc0f9f3d513..2da2bd52d200e 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -585,7 +585,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len) node_size = node->tree->node_size; - if ((off + len) > node_size) { + if ((u64)off + len > node_size) { u32 new_len = node_size - off; pr_err("requested length has been corrected: " From 60eed44674295423cd28a2255c38cea80b8cc256 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 18 Jul 2026 07:20:31 -0400 Subject: [PATCH 2176/3297] xfs: initialize iomap->flags earlier in xfs_bmbt_to_iomap [ Upstream commit 327e58826eb72f8bae9419cf1a4e722b57c85694 ] Otherwise we lose the IOMAP_IOEND_BOUNDARY assingment for writes to the first block in a realtime group, and could cause incorrect merges for such writes. Fixes: b91afef72471 ("xfs: don't merge ioends across RTGs") Cc: # v6.13 Signed-off-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_iomap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 490e12cb99be9..dcea927e0d3ab 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -113,6 +113,7 @@ xfs_bmbt_to_iomap( return xfs_alert_fsblock_zero(ip, imap); } + iomap->flags = iomap_flags; if (imap->br_startblock == HOLESTARTBLOCK) { iomap->addr = IOMAP_NULL_ADDR; iomap->type = IOMAP_HOLE; @@ -147,7 +148,6 @@ xfs_bmbt_to_iomap( iomap->dax_dev = target->bt_daxdev; else iomap->bdev = target->bt_bdev; - iomap->flags = iomap_flags; /* * If the inode is dirty for datasync purposes, let iomap know so it From dccb3c557879de05ac2b7c853149fc0010aedd71 Mon Sep 17 00:00:00 2001 From: Mykyta Yatsenko Date: Sat, 18 Jul 2026 07:44:32 -0400 Subject: [PATCH 2177/3297] bpf: Consistently use reg_state() for register access in the verifier [ Upstream commit 7af3339948601e188d93d7e03326aeb8fcbd6bea ] Replace the pattern of declaring a local regs array from cur_regs() and then indexing into it with the more concise reg_state() helper. This simplifies the code by eliminating intermediate variables and makes register access more consistent throughout the verifier. Signed-off-by: Mykyta Yatsenko Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20260113134826.2214860-1-mykyta.yatsenko5@gmail.com Signed-off-by: Alexei Starovoitov Stable-dep-of: 53040a81ae57 ("bpf: Keep dynamic inner array lookups nullable") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 53605f4b28d98..754f34b26319b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5690,8 +5690,8 @@ static int check_stack_write(struct bpf_verifier_env *env, static int check_map_access_type(struct bpf_verifier_env *env, u32 regno, int off, int size, enum bpf_access_type type) { - struct bpf_reg_state *regs = cur_regs(env); - struct bpf_map *map = regs[regno].map_ptr; + struct bpf_reg_state *reg = reg_state(env, regno); + struct bpf_map *map = reg->map_ptr; u32 cap = bpf_map_flags_to_cap(map); if (type == BPF_WRITE && !(cap & BPF_MAP_CAN_WRITE)) { @@ -6195,8 +6195,7 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, int size, bool zero_size_allowed) { - struct bpf_reg_state *regs = cur_regs(env); - struct bpf_reg_state *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); int err; /* We may have added a variable offset to the packet pointer; but any @@ -6283,8 +6282,7 @@ static int check_sock_access(struct bpf_verifier_env *env, int insn_idx, u32 regno, int off, int size, enum bpf_access_type t) { - struct bpf_reg_state *regs = cur_regs(env); - struct bpf_reg_state *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); struct bpf_insn_access_aux info = {}; bool valid; @@ -7469,8 +7467,7 @@ static int check_stack_access_within_bounds( int regno, int off, int access_size, enum bpf_access_type type) { - struct bpf_reg_state *regs = cur_regs(env); - struct bpf_reg_state *reg = regs + regno; + struct bpf_reg_state *reg = reg_state(env, regno); struct bpf_func_state *state = func(env, reg); s64 min_off, max_off; int err; @@ -8414,7 +8411,7 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, int flags) { bool is_lock = flags & PROCESS_SPIN_LOCK, is_res_lock = flags & PROCESS_RES_LOCK; const char *lock_str = is_res_lock ? "bpf_res_spin" : "bpf_spin"; - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); struct bpf_verifier_state *cur = env->cur_state; bool is_const = tnum_is_const(reg->var_off); bool is_irq = flags & PROCESS_LOCK_IRQ; @@ -8530,7 +8527,7 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, int flags) static int check_map_field_pointer(struct bpf_verifier_env *env, u32 regno, enum btf_field_type field_type) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); bool is_const = tnum_is_const(reg->var_off); struct bpf_map *map = reg->map_ptr; u64 val = reg->var_off.value; @@ -8577,7 +8574,7 @@ static int check_map_field_pointer(struct bpf_verifier_env *env, u32 regno, static int process_timer_func(struct bpf_verifier_env *env, int regno, struct bpf_call_arg_meta *meta) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); struct bpf_map *map = reg->map_ptr; int err; @@ -8601,7 +8598,7 @@ static int process_timer_func(struct bpf_verifier_env *env, int regno, static int process_wq_func(struct bpf_verifier_env *env, int regno, struct bpf_kfunc_call_arg_meta *meta) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); struct bpf_map *map = reg->map_ptr; int err; @@ -8622,7 +8619,7 @@ static int process_wq_func(struct bpf_verifier_env *env, int regno, static int process_task_work_func(struct bpf_verifier_env *env, int regno, struct bpf_kfunc_call_arg_meta *meta) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); struct bpf_map *map = reg->map_ptr; int err; @@ -8642,7 +8639,7 @@ static int process_task_work_func(struct bpf_verifier_env *env, int regno, static int process_kptr_func(struct bpf_verifier_env *env, int regno, struct bpf_call_arg_meta *meta) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); struct btf_field *kptr_field; struct bpf_map *map_ptr; struct btf_record *rec; @@ -8715,7 +8712,7 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno, static int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx, enum bpf_arg_type arg_type, int clone_ref_obj_id) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); int err; if (reg->type != PTR_TO_STACK && reg->type != CONST_PTR_TO_DYNPTR) { @@ -8835,7 +8832,7 @@ static bool is_kfunc_arg_iter(struct bpf_kfunc_call_arg_meta *meta, int arg_idx, static int process_iter_arg(struct bpf_verifier_env *env, int regno, int insn_idx, struct bpf_kfunc_call_arg_meta *meta) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); const struct btf_type *t; int spi, err, i, nr_slots, btf_id; @@ -9307,7 +9304,7 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno, const u32 *arg_btf_id, struct bpf_call_arg_meta *meta) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); enum bpf_reg_type expected, type = reg->type; const struct bpf_reg_types *compatible; int i, j; @@ -9720,7 +9717,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, int insn_idx) { u32 regno = BPF_REG_1 + arg; - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); enum bpf_arg_type arg_type = fn->arg_type[arg]; enum bpf_reg_type type = reg->type; u32 *arg_btf_id = NULL; @@ -11250,7 +11247,7 @@ record_func_key(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, int func_id, int insn_idx) { struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx]; - struct bpf_reg_state *regs = cur_regs(env), *reg; + struct bpf_reg_state *reg; struct bpf_map *map = meta->map_ptr; u64 val, max; int err; @@ -11262,7 +11259,7 @@ record_func_key(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, return -EINVAL; } - reg = ®s[BPF_REG_3]; + reg = reg_state(env, BPF_REG_3); val = reg->var_off.value; max = map->max_entries; @@ -11408,8 +11405,7 @@ static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env) static bool loop_flag_is_zero(struct bpf_verifier_env *env) { - struct bpf_reg_state *regs = cur_regs(env); - struct bpf_reg_state *reg = ®s[BPF_REG_4]; + struct bpf_reg_state *reg = reg_state(env, BPF_REG_4); bool reg_is_null = register_is_null(reg); if (reg_is_null) @@ -12680,7 +12676,7 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env, static int process_irq_flag(struct bpf_verifier_env *env, int regno, struct bpf_kfunc_call_arg_meta *meta) { - struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; + struct bpf_reg_state *reg = reg_state(env, regno); int err, kfunc_class = IRQ_NATIVE_KFUNC; bool irq_save; From c447be8d88c309f65c412c23fc09612967121fcc Mon Sep 17 00:00:00 2001 From: Mykyta Yatsenko Date: Sat, 18 Jul 2026 07:44:33 -0400 Subject: [PATCH 2178/3297] bpf: Introduce struct bpf_map_desc in verifier [ Upstream commit 98c4fd2963cb2bc5eae22b5365e6bbf3a5cd0f14 ] Introduce struct bpf_map_desc to hold bpf_map pointer and map uid. Use this struct in both bpf_call_arg_meta and bpf_kfunc_call_arg_meta instead of having different representations: - bpf_call_arg_meta had separate map_ptr and map_uid fields - bpf_kfunc_call_arg_meta had an anonymous inline struct This unifies the map fields layout across both metadata structures, making the code more consistent and preparing for further refactoring of map field pointer validation. Acked-by: Eduard Zingerman Signed-off-by: Mykyta Yatsenko Link: https://lore.kernel.org/r/20260130-verif_special_fields-v2-1-2c59e637da7d@meta.com Signed-off-by: Alexei Starovoitov Stable-dep-of: 53040a81ae57 ("bpf: Keep dynamic inner array lookups nullable") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 79 ++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 754f34b26319b..9cc6f3f4abce9 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -274,8 +274,13 @@ static bool bpf_pseudo_kfunc_call(const struct bpf_insn *insn) insn->src_reg == BPF_PSEUDO_KFUNC_CALL; } +struct bpf_map_desc { + struct bpf_map *ptr; + int uid; +}; + struct bpf_call_arg_meta { - struct bpf_map *map_ptr; + struct bpf_map_desc map; bool raw_mode; bool pkt_access; u8 release_regno; @@ -285,7 +290,6 @@ struct bpf_call_arg_meta { u64 msize_max_value; int ref_obj_id; int dynptr_id; - int map_uid; int func_id; struct btf *btf; u32 btf_id; @@ -345,10 +349,7 @@ struct bpf_kfunc_call_arg_meta { u8 spi; u8 frameno; } iter; - struct { - struct bpf_map *ptr; - int uid; - } map; + struct bpf_map_desc map; u64 mem_size; }; @@ -8582,7 +8583,7 @@ static int process_timer_func(struct bpf_verifier_env *env, int regno, if (err) return err; - if (meta->map_ptr) { + if (meta->map.ptr) { verifier_bug(env, "Two map pointers in a timer helper"); return -EFAULT; } @@ -8590,8 +8591,8 @@ static int process_timer_func(struct bpf_verifier_env *env, int regno, verbose(env, "bpf_timer cannot be used for PREEMPT_RT.\n"); return -EOPNOTSUPP; } - meta->map_uid = reg->map_uid; - meta->map_ptr = map; + meta->map.uid = reg->map_uid; + meta->map.ptr = map; return 0; } @@ -8655,7 +8656,7 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno, return -EINVAL; } rec = map_ptr->record; - meta->map_ptr = map_ptr; + meta->map.ptr = map_ptr; } if (!tnum_is_const(reg->var_off)) { @@ -9162,13 +9163,13 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env, const struct bpf_call_arg_meta *meta, enum bpf_arg_type *arg_type) { - if (!meta->map_ptr) { + if (!meta->map.ptr) { /* kernel subsystem misconfigured verifier */ verifier_bug(env, "invalid map_ptr to access map->type"); return -EFAULT; } - switch (meta->map_ptr->map_type) { + switch (meta->map.ptr->map_type) { case BPF_MAP_TYPE_SOCKMAP: case BPF_MAP_TYPE_SOCKHASH: if (*arg_type == ARG_PTR_TO_MAP_VALUE) { @@ -9817,7 +9818,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, switch (base_type(arg_type)) { case ARG_CONST_MAP_PTR: /* bpf_map_xxx(map_ptr) call: remember that map_ptr */ - if (meta->map_ptr) { + if (meta->map.ptr) { /* Use map_uid (which is unique id of inner map) to reject: * inner_map1 = bpf_map_lookup_elem(outer_map, key1) * inner_map2 = bpf_map_lookup_elem(outer_map, key2) @@ -9830,23 +9831,23 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, * * Comparing map_ptr is enough to distinguish normal and outer maps. */ - if (meta->map_ptr != reg->map_ptr || - meta->map_uid != reg->map_uid) { + if (meta->map.ptr != reg->map_ptr || + meta->map.uid != reg->map_uid) { verbose(env, "timer pointer in R1 map_uid=%d doesn't match map pointer in R2 map_uid=%d\n", - meta->map_uid, reg->map_uid); + meta->map.uid, reg->map_uid); return -EINVAL; } } - meta->map_ptr = reg->map_ptr; - meta->map_uid = reg->map_uid; + meta->map.ptr = reg->map_ptr; + meta->map.uid = reg->map_uid; break; case ARG_PTR_TO_MAP_KEY: /* bpf_map_xxx(..., map_ptr, ..., key) call: * check that [key, key + map->key_size) are within * stack limits and initialized */ - if (!meta->map_ptr) { + if (!meta->map.ptr) { /* in function declaration map_ptr must come before * map_key, so that it's verified and known before * we have to check map_key here. Otherwise it means @@ -9855,11 +9856,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, verifier_bug(env, "invalid map_ptr to access map->key"); return -EFAULT; } - key_size = meta->map_ptr->key_size; + key_size = meta->map.ptr->key_size; err = check_helper_mem_access(env, regno, key_size, BPF_READ, false, NULL); if (err) return err; - if (can_elide_value_nullness(meta->map_ptr->map_type)) { + if (can_elide_value_nullness(meta->map.ptr->map_type)) { err = get_constant_map_key(env, reg, key_size, &meta->const_map_key); if (err < 0) { meta->const_map_key = -1; @@ -9877,13 +9878,13 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, /* bpf_map_xxx(..., map_ptr, ..., value) call: * check [value, value + map->value_size) validity */ - if (!meta->map_ptr) { + if (!meta->map.ptr) { /* kernel subsystem misconfigured verifier */ verifier_bug(env, "invalid map_ptr to access map->value"); return -EFAULT; } meta->raw_mode = arg_type & MEM_UNINIT; - err = check_helper_mem_access(env, regno, meta->map_ptr->value_size, + err = check_helper_mem_access(env, regno, meta->map.ptr->value_size, arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, false, meta); break; @@ -11201,7 +11202,7 @@ record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, int func_id, int insn_idx) { struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx]; - struct bpf_map *map = meta->map_ptr; + struct bpf_map *map = meta->map.ptr; if (func_id != BPF_FUNC_tail_call && func_id != BPF_FUNC_map_lookup_elem && @@ -11234,11 +11235,11 @@ record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, } if (!aux->map_ptr_state.map_ptr) - bpf_map_ptr_store(aux, meta->map_ptr, - !meta->map_ptr->bypass_spec_v1, false); - else if (aux->map_ptr_state.map_ptr != meta->map_ptr) - bpf_map_ptr_store(aux, meta->map_ptr, - !meta->map_ptr->bypass_spec_v1, true); + bpf_map_ptr_store(aux, meta->map.ptr, + !meta->map.ptr->bypass_spec_v1, false); + else if (aux->map_ptr_state.map_ptr != meta->map.ptr) + bpf_map_ptr_store(aux, meta->map.ptr, + !meta->map.ptr->bypass_spec_v1, true); return 0; } @@ -11248,7 +11249,7 @@ record_func_key(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, { struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx]; struct bpf_reg_state *reg; - struct bpf_map *map = meta->map_ptr; + struct bpf_map *map = meta->map.ptr; u64 val, max; int err; @@ -11814,22 +11815,22 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn * can check 'value_size' boundary of memory access * to map element returned from bpf_map_lookup_elem() */ - if (meta.map_ptr == NULL) { + if (meta.map.ptr == NULL) { verifier_bug(env, "unexpected null map_ptr"); return -EFAULT; } if (func_id == BPF_FUNC_map_lookup_elem && - can_elide_value_nullness(meta.map_ptr->map_type) && + can_elide_value_nullness(meta.map.ptr->map_type) && meta.const_map_key >= 0 && - meta.const_map_key < meta.map_ptr->max_entries) + meta.const_map_key < meta.map.ptr->max_entries) ret_flag &= ~PTR_MAYBE_NULL; - regs[BPF_REG_0].map_ptr = meta.map_ptr; - regs[BPF_REG_0].map_uid = meta.map_uid; + regs[BPF_REG_0].map_ptr = meta.map.ptr; + regs[BPF_REG_0].map_uid = meta.map.uid; regs[BPF_REG_0].type = PTR_TO_MAP_VALUE | ret_flag; if (!type_may_be_null(ret_flag) && - btf_record_has_field(meta.map_ptr->record, BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK)) { + btf_record_has_field(meta.map.ptr->record, BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK)) { regs[BPF_REG_0].id = ++env->id_gen; } break; @@ -11932,7 +11933,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn if (type_may_be_null(regs[BPF_REG_0].type)) regs[BPF_REG_0].id = ++env->id_gen; - if (helper_multiple_ref_obj_use(func_id, meta.map_ptr)) { + if (helper_multiple_ref_obj_use(func_id, meta.map.ptr)) { verifier_bug(env, "func %s#%d sets ref_obj_id more than once", func_id_name(func_id), func_id); return -EFAULT; @@ -11944,7 +11945,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn if (is_ptr_cast_function(func_id) || is_dynptr_ref_function(func_id)) { /* For release_reference() */ regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id; - } else if (is_acquire_function(func_id, meta.map_ptr)) { + } else if (is_acquire_function(func_id, meta.map.ptr)) { int id = acquire_reference(env, insn_idx); if (id < 0) @@ -11959,7 +11960,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn if (err) return err; - err = check_map_func_compatibility(env, meta.map_ptr, func_id); + err = check_map_func_compatibility(env, meta.map.ptr, func_id); if (err) return err; From 0b92ad64d6e4bde85e6b9888404f9a7a2b65d269 Mon Sep 17 00:00:00 2001 From: Nuoqi Gui Date: Sat, 18 Jul 2026 07:44:34 -0400 Subject: [PATCH 2179/3297] bpf: Keep dynamic inner array lookups nullable [ Upstream commit 53040a81ae57cdca8af8ac36fe4e661730cf7c6b ] An ARRAY_OF_MAPS can use an array created with BPF_F_INNER_MAP as its inner map template. A concrete inner array with a different max_entries value can then replace the template. After a successful outer map lookup, the verifier represents the resulting map pointer using the inner map template. Const-key lookup nullness elision consequently uses the template max_entries even though the runtime helper uses the concrete inner map max_entries. Do not elide lookup result nullness for maps marked with BPF_F_INNER_MAP, because the template max_entries does not prove that the key is in bounds for the concrete runtime map. Fixes: d2102f2f5d75 ("bpf: verifier: Support eliding map lookup nullness") Signed-off-by: Nuoqi Gui Acked-by: Eduard Zingerman Acked-by: Jiri Olsa Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/20260607-f01-v2-v2-1-da48453146e8@mails.tsinghua.edu.cn Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9cc6f3f4abce9..d1c51d812f3b6 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9710,7 +9710,7 @@ static int get_constant_map_key(struct bpf_verifier_env *env, return 0; } -static bool can_elide_value_nullness(enum bpf_map_type type); +static bool can_elide_value_nullness(const struct bpf_map *map); static int check_func_arg(struct bpf_verifier_env *env, u32 arg, struct bpf_call_arg_meta *meta, @@ -9860,7 +9860,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, err = check_helper_mem_access(env, regno, key_size, BPF_READ, false, NULL); if (err) return err; - if (can_elide_value_nullness(meta->map.ptr->map_type)) { + if (can_elide_value_nullness(meta->map.ptr)) { err = get_constant_map_key(env, reg, key_size, &meta->const_map_key); if (err < 0) { meta->const_map_key = -1; @@ -11433,13 +11433,16 @@ static void update_loop_inline_state(struct bpf_verifier_env *env, u32 subprogno state->callback_subprogno == subprogno); } -/* Returns whether or not the given map type can potentially elide +/* Returns whether or not the given map can potentially elide * lookup return value nullness check. This is possible if the key * is statically known. */ -static bool can_elide_value_nullness(enum bpf_map_type type) +static bool can_elide_value_nullness(const struct bpf_map *map) { - switch (type) { + if (map->map_flags & BPF_F_INNER_MAP) + return false; + + switch (map->map_type) { case BPF_MAP_TYPE_ARRAY: case BPF_MAP_TYPE_PERCPU_ARRAY: return true; @@ -11821,7 +11824,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn } if (func_id == BPF_FUNC_map_lookup_elem && - can_elide_value_nullness(meta.map.ptr->map_type) && + can_elide_value_nullness(meta.map.ptr) && meta.const_map_key >= 0 && meta.const_map_key < meta.map.ptr->max_entries) ret_flag &= ~PTR_MAYBE_NULL; From 8fccaeeb9e9c5685ea1799f1abd0c35709413f82 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 18 Jul 2026 07:44:40 -0400 Subject: [PATCH 2180/3297] bpf: Consistently use bpf_rcu_lock_held() everywhere [ Upstream commit 48a97ffc6c826640907d13b199e29008f4fe2c15 ] We have many places which open-code what's now is bpf_rcu_lock_held() macro, so replace all those places with a clean and short macro invocation. For that, move bpf_rcu_lock_held() macro into include/linux/bpf.h. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Acked-by: Jiri Olsa Link: https://lore.kernel.org/bpf/20251014201403.4104511-1-andrii@kernel.org Stable-dep-of: 2f884d371faf ("bpf: Allow LPM map access from sleepable BPF programs") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/bpf.h | 3 +++ include/linux/bpf_local_storage.h | 3 --- kernel/bpf/hashtab.c | 21 +++++++-------------- kernel/bpf/helpers.c | 12 ++++-------- 4 files changed, 14 insertions(+), 25 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f305ea93b9edd..e264c695f31ce 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2375,6 +2375,9 @@ bpf_prog_run_array_uprobe(const struct bpf_prog_array *array, bool bpf_jit_bypass_spec_v1(void); bool bpf_jit_bypass_spec_v4(void); +#define bpf_rcu_lock_held() \ + (rcu_read_lock_held() || rcu_read_lock_trace_held() || rcu_read_lock_bh_held()) + #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); extern struct mutex bpf_stats_enabled_mutex; diff --git a/include/linux/bpf_local_storage.h b/include/linux/bpf_local_storage.h index ab7244d8108f6..782f58feea356 100644 --- a/include/linux/bpf_local_storage.h +++ b/include/linux/bpf_local_storage.h @@ -18,9 +18,6 @@ #define BPF_LOCAL_STORAGE_CACHE_SIZE 16 -#define bpf_rcu_lock_held() \ - (rcu_read_lock_held() || rcu_read_lock_trace_held() || \ - rcu_read_lock_bh_held()) struct bpf_local_storage_map_bucket { struct hlist_head list; raw_spinlock_t lock; diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 469bc48b25127..7f8e029a6af8d 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -669,8 +669,7 @@ static void *__htab_map_lookup_elem(struct bpf_map *map, void *key) struct htab_elem *l; u32 hash, key_size; - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); key_size = map->key_size; @@ -1104,8 +1103,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, /* unknown flags */ return -EINVAL; - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); key_size = map->key_size; @@ -1212,8 +1210,7 @@ static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value /* unknown flags */ return -EINVAL; - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); key_size = map->key_size; @@ -1281,8 +1278,7 @@ static long htab_map_update_elem_in_place(struct bpf_map *map, void *key, /* unknown flags */ return -EINVAL; - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); key_size = map->key_size; @@ -1344,8 +1340,7 @@ static long __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key, /* unknown flags */ return -EINVAL; - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); key_size = map->key_size; @@ -1422,8 +1417,7 @@ static long htab_map_delete_elem(struct bpf_map *map, void *key) u32 hash, key_size; int ret; - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); key_size = map->key_size; @@ -1458,8 +1452,7 @@ static long htab_lru_map_delete_elem(struct bpf_map *map, void *key) u32 hash, key_size; int ret; - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); key_size = map->key_size; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 7357ddc1fc928..5aed5659822f1 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -42,8 +42,7 @@ */ BPF_CALL_2(bpf_map_lookup_elem, struct bpf_map *, map, void *, key) { - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); return (unsigned long) map->ops->map_lookup_elem(map, key); } @@ -59,8 +58,7 @@ const struct bpf_func_proto bpf_map_lookup_elem_proto = { BPF_CALL_4(bpf_map_update_elem, struct bpf_map *, map, void *, key, void *, value, u64, flags) { - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); return map->ops->map_update_elem(map, key, value, flags); } @@ -77,8 +75,7 @@ const struct bpf_func_proto bpf_map_update_elem_proto = { BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key) { - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); return map->ops->map_delete_elem(map, key); } @@ -134,8 +131,7 @@ const struct bpf_func_proto bpf_map_peek_elem_proto = { BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu) { - WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && - !rcu_read_lock_bh_held()); + WARN_ON_ONCE(!bpf_rcu_lock_held()); return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu); } From 57454944737f3ad9a8703aecbbb79713b513a94b Mon Sep 17 00:00:00 2001 From: Vlad Poenaru Date: Sat, 18 Jul 2026 07:44:41 -0400 Subject: [PATCH 2181/3297] bpf: Allow LPM map access from sleepable BPF programs [ Upstream commit 2f884d371fafea137afea504d49ee4a7c8d7985b ] trie_lookup_elem() annotates its rcu_dereference_check() walks with only rcu_read_lock_bh_held(). Because rcu_dereference_check(p, c) resolves to "c || rcu_read_lock_held()", this passes for XDP/NAPI and classic RCU readers but fails for sleepable BPF programs, which enter via __bpf_prog_enter_sleepable() and hold only rcu_read_lock_trace(). trie_update_elem() and trie_delete_elem() have the same problem in a different form: they walk the trie with plain rcu_dereference(), which asserts rcu_read_lock_held() unconditionally. Both are reachable from sleepable BPF programs via the bpf_map_update_elem / bpf_map_delete_elem helpers, and from the syscall path under classic rcu_read_lock(). In the writer paths the trie is actually protected by trie->lock (an rqspinlock taken across the walk); we never relied on the RCU read-side lock to keep nodes alive there. A sleepable LSM hook that ends up touching an LPM trie therefore triggers lockdep on debug kernels: ============================= WARNING: suspicious RCU usage 7.1.0-... Tainted: G E ----------------------------- kernel/bpf/lpm_trie.c:249 suspicious rcu_dereference_check() usage! 1 lock held by net_tests/540: #0: (rcu_tasks_trace_srcu_struct){....}-{0:0}, at: __bpf_prog_enter_sleepable+0x26/0x280 Call Trace: dump_stack_lvl lockdep_rcu_suspicious trie_lookup_elem bpf_prog_..._enforce_security_socket_connect bpf_trampoline_... security_socket_connect __sys_connect do_syscall_64 This is lockdep-only -- no UAF, since Tasks Trace RCU does serialize against the trie's reclaim path -- but it spams the console once per distinct callsite on every debug kernel running a sleepable BPF LSM that touches an LPM trie, which is increasingly common. For the lookup path, switch the rcu_dereference_check() annotation from rcu_read_lock_bh_held() to bpf_rcu_lock_held(), which accepts all three contexts (classic, BH, Tasks Trace). Other map types already follow this convention. For trie_update_elem() and trie_delete_elem(), annotate the walks as rcu_dereference_protected(*p, 1) -- matching trie_free() in the same file -- since trie->lock is held across the walk. rqspinlock has no lockdep_map, so the predicate degenerates to '1' rather than lockdep_is_held(&trie->lock); the protection is real but not machine-verifiable. trie_get_next_key() also uses bare rcu_dereference() but is reachable only from the BPF syscall, which holds classic rcu_read_lock() before dispatching, so it is left untouched. Fixes: 694cea395fde ("bpf: Allow RCU-protected lookups to happen from bh context") Cc: stable@vger.kernel.org Signed-off-by: Vlad Poenaru Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/r/20260609135558.193287-2-vlad.wing@gmail.com Signed-off-by: Alexei Starovoitov Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/lpm_trie.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index be66d7e520e0d..b05180661b736 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -246,7 +246,7 @@ static void *trie_lookup_elem(struct bpf_map *map, void *_key) /* Start walking the trie from the root node ... */ - for (node = rcu_dereference_check(trie->root, rcu_read_lock_bh_held()); + for (node = rcu_dereference_check(trie->root, bpf_rcu_lock_held()); node;) { unsigned int next_bit; size_t matchlen; @@ -280,7 +280,7 @@ static void *trie_lookup_elem(struct bpf_map *map, void *_key) */ next_bit = extract_bit(key->data, node->prefixlen); node = rcu_dereference_check(node->child[next_bit], - rcu_read_lock_bh_held()); + bpf_rcu_lock_held()); } if (!found) @@ -359,7 +359,7 @@ static long trie_update_elem(struct bpf_map *map, */ slot = &trie->root; - while ((node = rcu_dereference(*slot))) { + while ((node = rcu_dereference_protected(*slot, 1))) { matchlen = longest_prefix_match(trie, node, key); if (node->prefixlen != matchlen || @@ -482,7 +482,7 @@ static long trie_delete_elem(struct bpf_map *map, void *_key) trim = &trie->root; trim2 = trim; parent = NULL; - while ((node = rcu_dereference(*trim))) { + while ((node = rcu_dereference_protected(*trim, 1))) { matchlen = longest_prefix_match(trie, node, key); if (node->prefixlen != matchlen || From 47c0e0743302187eaf85e72809a4638286d09a65 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 18 Jul 2026 11:32:56 -0400 Subject: [PATCH 2182/3297] xfs: add a xfs_groups_to_rfsbs helper [ Upstream commit 0ec73eb3f12350799c4b3fb764225f6e38b42d1e ] Plus a rtgroup wrapper and use that to avoid overflows when converting zone/rtg counts to block counts. Signed-off-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino Stable-dep-of: 44cccefe6574 ("xfs: only log freed extents for the current RTG in zoned growfs") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/libxfs/xfs_group.h | 9 +++++++++ fs/xfs/libxfs/xfs_rtgroup.h | 8 ++++++++ fs/xfs/xfs_zone_gc.c | 3 +-- fs/xfs/xfs_zone_space_resv.c | 8 +++----- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/fs/xfs/libxfs/xfs_group.h b/fs/xfs/libxfs/xfs_group.h index 4423932a23130..4ae638f1c2c51 100644 --- a/fs/xfs/libxfs/xfs_group.h +++ b/fs/xfs/libxfs/xfs_group.h @@ -98,6 +98,15 @@ xfs_group_max_blocks( return xg->xg_mount->m_groups[xg->xg_type].blocks; } +static inline xfs_rfsblock_t +xfs_groups_to_rfsbs( + struct xfs_mount *mp, + uint32_t nr_groups, + enum xfs_group_type type) +{ + return (xfs_rfsblock_t)mp->m_groups[type].blocks * nr_groups; +} + static inline xfs_fsblock_t xfs_group_start_fsb( struct xfs_group *xg) diff --git a/fs/xfs/libxfs/xfs_rtgroup.h b/fs/xfs/libxfs/xfs_rtgroup.h index d4fcf591e63d0..a94e925ae67cb 100644 --- a/fs/xfs/libxfs/xfs_rtgroup.h +++ b/fs/xfs/libxfs/xfs_rtgroup.h @@ -371,4 +371,12 @@ static inline int xfs_initialize_rtgroups(struct xfs_mount *mp, # define xfs_rtgroup_get_geometry(rtg, rgeo) (-EOPNOTSUPP) #endif /* CONFIG_XFS_RT */ +static inline xfs_rfsblock_t +xfs_rtgs_to_rfsbs( + struct xfs_mount *mp, + uint32_t nr_groups) +{ + return xfs_groups_to_rfsbs(mp, nr_groups, XG_TYPE_RTG); +} + #endif /* __LIBXFS_RTGROUP_H */ diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index d18c43598d4b3..935b21c2b3bf5 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -181,8 +181,7 @@ xfs_zoned_need_gc( available = xfs_estimate_freecounter(mp, XC_FREE_RTAVAILABLE); if (available < - mp->m_groups[XG_TYPE_RTG].blocks * - (mp->m_max_open_zones - XFS_OPEN_GC_ZONES)) + xfs_rtgs_to_rfsbs(mp, mp->m_max_open_zones - XFS_OPEN_GC_ZONES)) return true; free = xfs_estimate_freecounter(mp, XC_FREE_RTEXTENTS); diff --git a/fs/xfs/xfs_zone_space_resv.c b/fs/xfs/xfs_zone_space_resv.c index 9cd38716fd25f..0e54e557a585e 100644 --- a/fs/xfs/xfs_zone_space_resv.c +++ b/fs/xfs/xfs_zone_space_resv.c @@ -54,12 +54,10 @@ xfs_zoned_default_resblks( { switch (ctr) { case XC_FREE_RTEXTENTS: - return (uint64_t)XFS_RESERVED_ZONES * - mp->m_groups[XG_TYPE_RTG].blocks + - mp->m_sb.sb_rtreserved; + return xfs_rtgs_to_rfsbs(mp, XFS_RESERVED_ZONES) + + mp->m_sb.sb_rtreserved; case XC_FREE_RTAVAILABLE: - return (uint64_t)XFS_GC_ZONES * - mp->m_groups[XG_TYPE_RTG].blocks; + return xfs_rtgs_to_rfsbs(mp, XFS_GC_ZONES); default: ASSERT(0); return 0; From adadb181ad42af8a178dc196e67230102e93a414 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 18 Jul 2026 11:32:57 -0400 Subject: [PATCH 2183/3297] xfs: only log freed extents for the current RTG in zoned growfs [ Upstream commit 44cccefe65749821d9a13523c8b763bf1262ef73 ] Otherwise a power fail or crash during growfs could lead to an elevated sb_rblocks counter. Note that the step function is much simpler compared to the classic RT allocator as zoned RT sections must be aligned to real time group boundaries. Fixes: 01b71e64bb87 ("xfs: support growfs on zoned file systems") Cc: # v6.15 Signed-off-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Reviewed-by: Damien Le Moal Signed-off-by: Carlos Maiolino Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_rtalloc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 59fad43063a78..907c88034c8dc 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -887,8 +887,7 @@ xfs_growfs_rt_sb_fields( static int xfs_growfs_rt_zoned( - struct xfs_rtgroup *rtg, - xfs_rfsblock_t nrblocks) + struct xfs_rtgroup *rtg) { struct xfs_mount *mp = rtg_mount(rtg); struct xfs_mount *nmp; @@ -900,7 +899,8 @@ xfs_growfs_rt_zoned( * Calculate new sb and mount fields for this round. Also ensure the * rtg_extents value is uptodate as the rtbitmap code relies on it. */ - nmp = xfs_growfs_rt_alloc_fake_mount(mp, nrblocks, + nmp = xfs_growfs_rt_alloc_fake_mount(mp, + xfs_rtgs_to_rfsbs(mp, rtg_rgno(rtg) + 1), mp->m_sb.sb_rextsize); if (!nmp) return -ENOMEM; @@ -1189,7 +1189,7 @@ xfs_growfs_rtg( } if (xfs_has_zoned(mp)) { - error = xfs_growfs_rt_zoned(rtg, nrblocks); + error = xfs_growfs_rt_zoned(rtg); goto out_rele; } From bce232923aa9e8ca0f80b023c179977bbe977878 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 18 Jul 2026 11:47:52 -0400 Subject: [PATCH 2184/3297] xfs: use bio_reuse in the zone GC code [ Upstream commit 0506d32f7c52e41f6e8db7c337e0ce6374c6ffbb ] Replace our somewhat fragile code to reuse the bio, which caused a regression in the past with the block layer bio_reuse helper. Signed-off-by: Christoph Hellwig Reviewed-by: Hans Holmberg Reviewed-by: Carlos Maiolino Reviewed-by: Damien Le Moal Signed-off-by: Carlos Maiolino [ sashal: open-coded the bio_reuse logic at the call site as 6.18 lacks commit 7ca44303f9f6 ("block: add a bio_reuse helper") ] Stable-dep-of: ae3692c7f440 ("xfs: add newly added RTGs to the free pool in growfs") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_zone_gc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index 935b21c2b3bf5..d2c2352c97e9b 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -812,9 +812,8 @@ xfs_zone_gc_write_chunk( { struct xfs_zone_gc_data *data = chunk->data; struct xfs_mount *mp = chunk->ip->i_mount; - phys_addr_t bvec_paddr = - bvec_phys(bio_first_bvec_all(&chunk->bio)); struct xfs_gc_bio *split_chunk; + unsigned short vcnt, i; if (chunk->bio.bi_status) xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); @@ -826,10 +825,11 @@ xfs_zone_gc_write_chunk( WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW); list_move_tail(&chunk->entry, &data->writing); + vcnt = chunk->bio.bi_vcnt; bio_reset(&chunk->bio, mp->m_rtdev_targp->bt_bdev, REQ_OP_WRITE); - bio_add_folio_nofail(&chunk->bio, chunk->scratch->folio, chunk->len, - offset_in_folio(chunk->scratch->folio, bvec_paddr)); - + for (i = 0; i < vcnt; i++) + chunk->bio.bi_iter.bi_size += chunk->bio.bi_io_vec[i].bv_len; + chunk->bio.bi_vcnt = vcnt; while ((split_chunk = xfs_zone_gc_split_write(data, chunk))) xfs_zone_gc_submit_write(data, split_chunk); xfs_zone_gc_submit_write(data, chunk); From e534790c4c272013c56261c55ab08a609a14f601 Mon Sep 17 00:00:00 2001 From: Ingo Rohloff Date: Sat, 18 Jul 2026 17:50:14 -0400 Subject: [PATCH 2185/3297] usb: dwc3: Support USB3340x ULPI PHY high-speed negotiation. [ Upstream commit a28de63356575612954d4e5d5f48a2488f50e16d ] The Microchip USB3340x ULPI PHY requires a delay when switching to the high-speed transmitter. See: http://ww1.microchip.com/downloads/en/DeviceDoc/80000645A.pdf Module 2 "Device Enumeration Failure with Link IP Systems" For details on the behavior and fix, refer to the AMD (formerly Xilinx) forum post: "USB stuck in full speed mode with USB3340 ULPI PHY, ZynqMP." This patch uses the USB PHY Vendor-ID and Product-ID to detect the USB3340 PHY and then applies the necessary fix if this PHY is found. Signed-off-by: Ingo Rohloff Acked-by: Thinh Nguyen Link: https://patch.msgid.link/20260305121452.54082-2-ingo.rohloff@lauterbach.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: e0f844d9d742 ("usb: dwc3: fix dwc3_readl() and dwc3_writel() calls in dwc3_ulpi_setup()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 20 ++++++++++++++++++++ drivers/usb/dwc3/core.h | 4 ++++ drivers/usb/dwc3/ulpi.c | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a1f99c3b5f378..6fda6adbca18a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -781,6 +781,24 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) return 0; } +static void dwc3_ulpi_setup(struct dwc3 *dwc) +{ + int index; + u32 reg; + + /* Don't do anything if there is no ULPI PHY */ + if (!dwc->ulpi) + return; + + if (dwc->enable_usb2_transceiver_delay) { + for (index = 0; index < dwc->num_usb2_ports; index++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg |= DWC3_GUSB2PHYCFG_XCVRDLY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + } + } +} + /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure @@ -1355,6 +1373,8 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc->ulpi_ready = true; } + dwc3_ulpi_setup(dwc); + if (!dwc->phys_ready) { ret = dwc3_core_get_phy(dwc); if (ret) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a35b3db1f9f3e..a39bf284c763f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -302,6 +302,7 @@ #define DWC3_GUSB2PHYCFG_SUSPHY BIT(6) #define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4) #define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8) +#define DWC3_GUSB2PHYCFG_XCVRDLY BIT(9) #define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3) #define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) #define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10) @@ -1161,6 +1162,8 @@ struct dwc3_glue_ops { * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. * @dis_split_quirk: set to disable split boundary. + * @enable_usb2_transceiver_delay: Set to insert a delay before the + * assertion of the TxValid signal during a HS Chirp. * @sys_wakeup: set if the device may do system wakeup. * @wakeup_configured: set if the device is configured for remote wakeup. * @suspended: set to track suspend event due to U3/L2. @@ -1403,6 +1406,7 @@ struct dwc3 { unsigned dis_metastability_quirk:1; unsigned dis_split_quirk:1; + unsigned enable_usb2_transceiver_delay:1; unsigned async_callbacks:1; unsigned sys_wakeup:1; unsigned wakeup_configured:1; diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c index 57daad15f502d..a256b7f5d78b4 100644 --- a/drivers/usb/dwc3/ulpi.c +++ b/drivers/usb/dwc3/ulpi.c @@ -10,10 +10,13 @@ #include #include #include +#include #include "core.h" #include "io.h" +#define USB_VENDOR_MICROCHIP 0x0424 + #define DWC3_ULPI_ADDR(a) \ ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ @@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_ops = { .write = dwc3_ulpi_write, }; +static void dwc3_ulpi_detect_config(struct dwc3 *dwc) +{ + struct ulpi *ulpi = dwc->ulpi; + + switch (ulpi->id.vendor) { + case USB_VENDOR_MICROCHIP: + switch (ulpi->id.product) { + case 0x0009: + /* Microchip USB3340 ULPI PHY */ + dwc->enable_usb2_transceiver_delay = true; + break; + default: + break; + } + break; + default: + break; + } +} + int dwc3_ulpi_init(struct dwc3 *dwc) { /* Register the interface */ @@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc) return PTR_ERR(dwc->ulpi); } + dwc3_ulpi_detect_config(dwc); + return 0; } From 41a4e80d5af04855e68ac88f5e2cd07fa67287f8 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sat, 18 Jul 2026 17:50:15 -0400 Subject: [PATCH 2186/3297] usb: dwc3: fix dwc3_readl() and dwc3_writel() calls in dwc3_ulpi_setup() [ Upstream commit e0f844d9d74200d311c6438a0f04270834ba5365 ] The dwc3_ulpi_setup() calls the register read and write calls with dwc3->regs when both these calls take the dwc3 structure directly. Chnage these two calls to fix the following sparse warning, and possibly a nasty bug in the dwc3_ulpi_setup() code: drivers/usb/dwc3/core.c:796:45: warning: incorrect type in argument 1 (different address spaces) drivers/usb/dwc3/core.c:796:45: expected struct dwc3 *dwc drivers/usb/dwc3/core.c:796:45: got void [noderef] __iomem *regs drivers/usb/dwc3/core.c:798:40: warning: incorrect type in argument 1 (different address spaces) drivers/usb/dwc3/core.c:798:40: expected struct dwc3 *dwc drivers/usb/dwc3/core.c:798:40: got void [noderef] __iomem *regs Cc: stable Fixes: 9accc68b1cf0 ("usb: dwc3: Add dwc pointer to dwc3_readl/writel") Acked-by: Thinh Nguyen Signed-off-by: Ben Dooks Link: https://patch.msgid.link/20260703162033.2847599-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6fda6adbca18a..f0a9bb5754a39 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -792,9 +792,9 @@ static void dwc3_ulpi_setup(struct dwc3 *dwc) if (dwc->enable_usb2_transceiver_delay) { for (index = 0; index < dwc->num_usb2_ports; index++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index)); reg |= DWC3_GUSB2PHYCFG_XCVRDLY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg); } } } From 6e5ef54b884f420013ff7b20c232bdb83fbb612e Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Sun, 19 Jul 2026 08:43:46 -0400 Subject: [PATCH 2187/3297] usb: atm: ueagle-atm: use dev_dbg() for 'device found' message [ Upstream commit 05c5075c3115010f97434c53d353d9a0d4b0e64e ] Convert dev_info() to dev_dbg(). Per 'Documentation/process/coding-style.rst': 13) Printing kernel messages ... When drivers are working properly they are quiet, so prefer to use dev_dbg/pr_debug unless something is wrong. While in there, correct the verb form and add 'with' for clarity. Suggested-by: Greg Kroah-Hartman Link: https://lore.kernel.org/all/2026051628-squatter-stature-c0e0@gregkh/ Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-1-010c8bc7b214@igalia.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index cd0f7b4bd82ab..43d966c855fa0 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2589,7 +2589,7 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) int ret; uea_enters(usb); - uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n", + uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", le16_to_cpu(usb->descriptor.idVendor), le16_to_cpu(usb->descriptor.idProduct), le16_to_cpu(usb->descriptor.bcdDevice), From b78826a657998261e0bb3e285c872364ecf9f2af Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Sun, 19 Jul 2026 08:43:47 -0400 Subject: [PATCH 2188/3297] usb: atm: ueagle-atm: remove function entry/exit debug messages [ Upstream commit 1414b2242c2e9d670c4d8b83480f13db57627ba9 ] Remove the driver-internal function entry/exit debug messages in favor of existing kernel-level function tracing mechanisms. Suggested-by: Greg Kroah-Hartman Link: https://lore.kernel.org/all/2026051657-scruffy-embark-45ea@gregkh/ Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-2-010c8bc7b214@igalia.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 56 ++++-------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 43d966c855fa0..0eea8a9912bcd 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -50,12 +50,6 @@ "[ueagle-atm vdbg] " format, ##args); \ } while (0) -#define uea_enters(usb_dev) \ - uea_vdbg(usb_dev, "entering %s\n" , __func__) - -#define uea_leaves(usb_dev) \ - uea_vdbg(usb_dev, "leaving %s\n" , __func__) - #define uea_err(usb_dev, format, args...) \ dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args) @@ -605,7 +599,6 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, u32 crc = 0; int ret, size; - uea_enters(usb); if (!fw_entry) { uea_err(usb, "firmware is not available\n"); goto err; @@ -669,7 +662,6 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, uea_err(usb, "firmware is corrupted\n"); err: release_firmware(fw_entry); - uea_leaves(usb); } /* @@ -680,7 +672,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) int ret; char *fw_name = EAGLE_FIRMWARE; - uea_enters(usb); uea_info(usb, "pre-firmware device, uploading firmware\n"); switch (ver) { @@ -709,7 +700,6 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) else uea_info(usb, "loading firmware %s\n", fw_name); - uea_leaves(usb); return ret; } @@ -1136,7 +1126,6 @@ static int uea_cmv_e1(struct uea_softc *sc, struct cmv_e1 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, " "offset : 0x%04x, data : 0x%08x\n", E1_FUNCTION_TYPE(function), @@ -1163,9 +1152,8 @@ static int uea_cmv_e1(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static int uea_cmv_e4(struct uea_softc *sc, @@ -1174,7 +1162,6 @@ static int uea_cmv_e4(struct uea_softc *sc, struct cmv_e4 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); memset(&cmv, 0, sizeof(cmv)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, " @@ -1198,9 +1185,8 @@ static int uea_cmv_e4(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static inline int uea_read_cmv_e1(struct uea_softc *sc, @@ -1294,7 +1280,6 @@ static int uea_stat_e1(struct uea_softc *sc) u32 data; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state); @@ -1437,7 +1422,6 @@ static int uea_stat_e4(struct uea_softc *sc) u32 tmp_arr[2]; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; /* XXX only need to be done before operationnal... */ @@ -1804,7 +1788,6 @@ static int uea_start_reset(struct uea_softc *sc) u16 zero = 0; /* ;-) */ int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_info(INS_TO_USBDEV(sc), "(re)booting started\n"); /* mask interrupt */ @@ -1872,7 +1855,6 @@ static int uea_start_reset(struct uea_softc *sc) return ret; sc->reset = 0; - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1888,7 +1870,6 @@ static int uea_kthread(void *data) int ret = -EAGAIN; set_freezable(); - uea_enters(INS_TO_USBDEV(sc)); while (!kthread_should_stop()) { if (ret < 0 || sc->reset) ret = uea_start_reset(sc); @@ -1897,7 +1878,7 @@ static int uea_kthread(void *data) if (ret != -EAGAIN) uea_wait(sc, 0, msecs_to_jiffies(1000)); } - uea_leaves(INS_TO_USBDEV(sc)); + return ret; } @@ -1910,8 +1891,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) u8 value; char *fw_name = FPGA930_FIRMWARE; - uea_enters(INS_TO_USBDEV(sc)); - ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev); if (ret) { uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n", @@ -1955,7 +1934,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) err1: release_firmware(fw_entry); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1965,7 +1943,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1; struct cmv_e1 *cmv = &intr->u.e1.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE) goto bad1; @@ -1989,7 +1966,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2003,7 +1979,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) sc->data = sc->data << 16 | sc->data >> 16; wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2011,14 +1986,12 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) "Function : %d, Subfunction : %d\n", E1_FUNCTION_TYPE(cmv->bFunction), E1_FUNCTION_SUBTYPE(cmv->bFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; bad1: uea_err(INS_TO_USBDEV(sc), "invalid cmv received, " "wPreamble %d, bDirection %d\n", le16_to_cpu(cmv->wPreamble), cmv->bDirection); - uea_leaves(INS_TO_USBDEV(sc)); } /* The modem send us an ack. First with check if it right */ @@ -2027,7 +2000,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4; struct cmv_e4 *cmv = &intr->u.e4.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n", be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction), be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress), @@ -2039,7 +2011,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2052,7 +2023,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) sc->data = be32_to_cpu(cmv->dwData[0]); sc->data1 = be32_to_cpu(cmv->dwData[1]); wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2060,7 +2030,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) "Function : %d, Subfunction : %d\n", E4_FUNCTION_TYPE(cmv->wFunction), E4_FUNCTION_SUBTYPE(cmv->wFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2088,8 +2057,6 @@ static void uea_intr(struct urb *urb) struct intr_pkt *intr = urb->transfer_buffer; int status = urb->status; - uea_enters(INS_TO_USBDEV(sc)); - if (unlikely(status < 0)) { uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", status); @@ -2129,8 +2096,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) int ret = -ENOMEM; int size; - uea_enters(INS_TO_USBDEV(sc)); - if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { size = E4_INTR_PKT_SIZE; sc->dispatch_cmv = uea_dispatch_cmv_e4; @@ -2187,7 +2152,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) goto err2; } - uea_leaves(INS_TO_USBDEV(sc)); return 0; err2: @@ -2197,7 +2161,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) sc->urb_int = NULL; kfree(intr); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -2207,7 +2170,7 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) static void uea_stop(struct uea_softc *sc) { int ret; - uea_enters(INS_TO_USBDEV(sc)); + ret = kthread_stop(sc->kthread); uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret); @@ -2221,7 +2184,6 @@ static void uea_stop(struct uea_softc *sc) flush_work(&sc->task); release_firmware(sc->dsp_firm); - uea_leaves(INS_TO_USBDEV(sc)); } /* syfs interface */ @@ -2494,8 +2456,6 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, int ret, ifnum = intf->altsetting->desc.bInterfaceNumber; unsigned int alt; - uea_enters(usb); - /* interface 0 is for firmware/monitoring */ if (ifnum != UEA_INTR_IFACE_NO) return -ENODEV; @@ -2588,7 +2548,6 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_device *usb = interface_to_usbdev(intf); int ret; - uea_enters(usb); uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", le16_to_cpu(usb->descriptor.idVendor), le16_to_cpu(usb->descriptor.idProduct), @@ -2619,7 +2578,6 @@ static void uea_disconnect(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); int ifnum = intf->altsetting->desc.bInterfaceNumber; - uea_enters(usb); /* ADI930 has 2 interfaces and eagle 3 interfaces. * Pre-firmware device has one interface @@ -2630,8 +2588,6 @@ static void uea_disconnect(struct usb_interface *intf) mutex_unlock(&uea_mutex); uea_info(usb, "ADSL device removed\n"); } - - uea_leaves(usb); } /* From 509b51327320bdeaef1969248177a446ded073ab Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Sun, 19 Jul 2026 08:43:48 -0400 Subject: [PATCH 2189/3297] usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect() [ Upstream commit e2674dfbed8a30d57e2bc872c4bfa6c3eec918bf ] ueagle-atm uses the asynchronous request_firmware_nowait() in .probe(), but does not wait for its completion, not even in .disconnect(); so, if the device is unplugged meanwhile, its teardown runs concurrently with that. Even though this inconsistency is worth addressing on its own, it has also triggered several bug reports in syzbot over the years (some auto-closed) where the firmware sysfs fallback mechanism (CONFIG_FW_LOADER_USER_HELPER) creates a firmware subdirectory in the device directory during its removal, which might hit unexpected conditions in kernfs, apparently, depending at which point the add and remove operations raced. (See links.) The pattern is: usb ?-?: Direct firmware load for ueagle-atm/eagle?.fw failed with error -2 usb ?-?: Falling back to sysfs fallback for: ueagle-atm/eagle?.fw Call trace: ... kernfs_create_dir_ns sysfs_create_dir_ns create_dir kobject_add_internal kobject_add_varg kobject_add class_dir_create_and_add get_device_parent device_add fw_load_sysfs_fallback fw_load_from_user_helper firmware_fallback_sysfs _request_firmware request_firmware_work_func ... (Some variations are observed, after fw_load_sysfs_fallback(), e.g., [1].) While the kernfs side is being looked at, the ueagle-atm side can be fixed by waiting for the pre-firmware load in the .disconnect() handler. This change has a similar approach to previous work by Andrey Tsygunka [2] (wait_for_completion() in .disconnect()), but it is relatively different in design/implementation; using the Originally-by tag for credit assignment. This has been tested with: - synthetic reproducer to check the error path; - USB gadget (virtual device) to check the firmware upload path; - QEMU device emulator to check the device ID re-enumeration path; (The latter two were written by Claude; no other code/text in this commit.) Links (year first reported): 2025 https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d 2025 https://syzbot.org/bug?extid=9af8471255ac36e34fd4 2024 https://syzbot.org/bug?extid=306212936b13e520679d 2023 https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 2022 https://syzbot.org/bug?extid=782984d6f1701b526edb 2021 https://syzbot.org/bug?id=f3f221579f4ef7e9691281f3c6f56c05f83e8490 2021 https://syzbot.org/bug?id=84d86f0d71394829df6fc53daf6642c045983881 2021 https://syzbot.org/bug?id=3302dc1c0e2b9c94f2e8edb404eabc9267bc6f90 [1] https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 [2] https://lore.kernel.org/lkml/20250410093146.3776801-2-aitsygunka@yandex.ru/ Cc: stable Reported-by: syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d Reported-by: syzbot+306212936b13e520679d@syzkaller.appspotmail.com Closes: https://syzbot.org/bug?extid=306212936b13e520679d Reported-by: syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 Originally-by: Andrey Tsygunka Fixes: b72458a80c75 ("[PATCH] USB: Eagle and ADI 930 usb adsl modem driver") Assisted-by: Claude:claude-opus-4.7 # usb gadget & qemu device for testing Signed-off-by: Mauricio Faria de Oliveira Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260526-ueagle-atm_req-fw-sync-v3-1-93c01961daaf@igalia.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 0eea8a9912bcd..a943a5257a59f 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -593,7 +593,9 @@ static int uea_send_modem_cmd(struct usb_device *usb, static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context) { - struct usb_device *usb = context; + struct usb_interface *intf = context; + struct usb_device *usb = interface_to_usbdev(intf); + struct completion *fw_done = usb_get_intfdata(intf); const u8 *pfw; u8 value; u32 crc = 0; @@ -662,15 +664,17 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, uea_err(usb, "firmware is corrupted\n"); err: release_firmware(fw_entry); + complete(fw_done); } /* * uea_load_firmware - Load usb firmware for pre-firmware devices. */ -static int uea_load_firmware(struct usb_device *usb, unsigned int ver) +static int uea_load_firmware(struct usb_interface *intf, unsigned int ver) { int ret; char *fw_name = EAGLE_FIRMWARE; + struct usb_device *usb = interface_to_usbdev(intf); uea_info(usb, "pre-firmware device, uploading firmware\n"); @@ -693,7 +697,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) } ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, - GFP_KERNEL, usb, + GFP_KERNEL, intf, uea_upload_pre_firmware); if (ret) uea_err(usb, "firmware %s is not available\n", fw_name); @@ -2556,8 +2560,23 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_reset_device(usb); - if (UEA_IS_PREFIRM(id)) - return uea_load_firmware(usb, UEA_CHIP_VERSION(id)); + if (UEA_IS_PREFIRM(id)) { + struct completion *fw_done; + + /* Wait for the firmware load to be done, in .disconnect() */ + fw_done = kzalloc_obj(*fw_done); + if (!fw_done) + return -ENOMEM; + + init_completion(fw_done); + usb_set_intfdata(intf, fw_done); + + ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id)); + if (ret) + kfree(fw_done); + + return ret; + } ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver); if (ret == 0) { @@ -2587,6 +2606,13 @@ static void uea_disconnect(struct usb_interface *intf) usbatm_usb_disconnect(intf); mutex_unlock(&uea_mutex); uea_info(usb, "ADSL device removed\n"); + } else if (usb->config->desc.bNumInterfaces == 1) { + struct completion *fw_done = usb_get_intfdata(intf); + + uea_dbg(usb, "pre-firmware device, waiting firmware upload\n"); + wait_for_completion(fw_done); + uea_dbg(usb, "pre-firmware device, finished waiting\n"); + kfree(fw_done); } } From c3e6860252102922205cb33f7f2be55b902f6180 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Jul 2026 22:25:46 -0400 Subject: [PATCH 2190/3297] new helper: simple_remove_by_name() [ Upstream commit 4051a9115ad24bb9a691774730ca9c1dd56de665 ] simple_recursive_removal(), but instead of victim dentry it takes parent + name. Used to be open-coded in fs/fuse/control.c, but there's no need to expose the guts of that thing there and there are other potential users, so let's lift it into libfs... Acked-by: Miklos Szeredi Signed-off-by: Al Viro Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/fuse/control.c | 7 +------ fs/libfs.c | 13 +++++++++++++ include/linux/fs.h | 2 ++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/fs/fuse/control.c b/fs/fuse/control.c index ce11f4b427c28..2e300f6d646c3 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -290,18 +290,13 @@ static void remove_one(struct dentry *dentry) */ void fuse_ctl_remove_conn(struct fuse_conn *fc) { - struct dentry *dentry; char name[32]; if (!fuse_control_sb || fc->no_control) return; sprintf(name, "%u", fc->dev); - dentry = lookup_noperm_positive_unlocked(&QSTR(name), fuse_control_sb->s_root); - if (!IS_ERR(dentry)) { - simple_recursive_removal(dentry, remove_one); - dput(dentry); // paired with lookup_noperm_positive_unlocked() - } + simple_remove_by_name(fuse_control_sb->s_root, name, remove_one); } static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fsc) diff --git a/fs/libfs.c b/fs/libfs.c index 96551deff8e50..52f00bfc8f7ba 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -647,6 +647,19 @@ void simple_recursive_removal(struct dentry *dentry, } EXPORT_SYMBOL(simple_recursive_removal); +void simple_remove_by_name(struct dentry *parent, const char *name, + void (*callback)(struct dentry *)) +{ + struct dentry *dentry; + + dentry = lookup_noperm_positive_unlocked(&QSTR(name), parent); + if (!IS_ERR(dentry)) { + simple_recursive_removal(dentry, callback); + dput(dentry); // paired with lookup_noperm_positive_unlocked() + } +} +EXPORT_SYMBOL(simple_remove_by_name); + /* caller holds parent directory with I_MUTEX_PARENT */ void locked_recursive_removal(struct dentry *dentry, void (*callback)(struct dentry *)) diff --git a/include/linux/fs.h b/include/linux/fs.h index 6374649060ba3..14f5accc97b74 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3655,6 +3655,8 @@ extern int simple_rename(struct mnt_idmap *, struct inode *, unsigned int); extern void simple_recursive_removal(struct dentry *, void (*callback)(struct dentry *)); +extern void simple_remove_by_name(struct dentry *, const char *, + void (*callback)(struct dentry *)); extern void locked_recursive_removal(struct dentry *, void (*callback)(struct dentry *)); extern int noop_fsync(struct file *, loff_t, loff_t, int); From 901c036cf6254cde695956c015ac89559bf72489 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Jul 2026 22:25:47 -0400 Subject: [PATCH 2191/3297] functionfs: don't abuse ffs_data_closed() on fs shutdown [ Upstream commit fcb8985143540cbcb8e91c0ea8b7fb5d37c88177 ] ffs_data_closed() has a seriously confusing logics in it: in addition to the normal "decrement a counter and do some work if it hits zero" there's "... and if it has somehow become negative, do that" bit. It's not a race, despite smelling rather fishy. What really happens is that in addition to "call that on close of files there, to match the increments of counter on opens" there's one call in ->kill_sb(). Counter starts at 0 and never goes negative over the lifetime of filesystem (or we have much worse problems everywhere - ->release() call of some file somehow unpaired with successful ->open() of the same). At the filesystem shutdown it will be 0 or, again, we have much worse problems - filesystem instance destroyed with files on it still open. In other words, at that call and at that call alone the decrement would go from 0 to -1, hitting that chunk (and not hitting the "if it hits 0" part). So that check is a weirdly spelled "called from ffs_kill_sb()". Just expand the call in the latter and kill the misplaced chunk in ffs_data_closed(). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Al Viro Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 5ef9a7a7f3bc3..d416ec8b8556a 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2086,12 +2086,18 @@ static int ffs_fs_init_fs_context(struct fs_context *fc) return 0; } +static void ffs_data_reset(struct ffs_data *ffs); + static void ffs_fs_kill_sb(struct super_block *sb) { kill_litter_super(sb); - if (sb->s_fs_info) - ffs_data_closed(sb->s_fs_info); + if (sb->s_fs_info) { + struct ffs_data *ffs = sb->s_fs_info; + ffs->state = FFS_CLOSING; + ffs_data_reset(ffs); + ffs_data_put(ffs); + } } static struct file_system_type ffs_fs_type = { @@ -2129,7 +2135,6 @@ static void functionfs_cleanup(void) /* ffs_data and ffs_function construction and destruction code **************/ static void ffs_data_clear(struct ffs_data *ffs); -static void ffs_data_reset(struct ffs_data *ffs); static void ffs_data_get(struct ffs_data *ffs) { @@ -2186,11 +2191,6 @@ static void ffs_data_closed(struct ffs_data *ffs) ffs_data_reset(ffs); } } - if (atomic_read(&ffs->opened) < 0) { - ffs->state = FFS_CLOSING; - ffs_data_reset(ffs); - } - ffs_data_put(ffs); } From 4744f07f6bb7cdf07349666d06059da4626a5000 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Jul 2026 22:25:48 -0400 Subject: [PATCH 2192/3297] functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}() [ Upstream commit fe47466282a69499ae17a22f064e827badb77078 ] A reference is held by the superblock (it's dropped in ffs_kill_sb()) and filesystem will not get to ->kill_sb() while there are any opened files, TYVM... Reviewed-by: Greg Kroah-Hartman Signed-off-by: Al Viro Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index d416ec8b8556a..9fd06780f5992 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2143,7 +2143,6 @@ static void ffs_data_get(struct ffs_data *ffs) static void ffs_data_opened(struct ffs_data *ffs) { - refcount_inc(&ffs->ref); if (atomic_add_return(1, &ffs->opened) == 1 && ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; @@ -2168,11 +2167,11 @@ static void ffs_data_put(struct ffs_data *ffs) static void ffs_data_closed(struct ffs_data *ffs) { - struct ffs_epfile *epfiles; - unsigned long flags; - if (atomic_dec_and_test(&ffs->opened)) { if (ffs->no_disconnect) { + struct ffs_epfile *epfiles; + unsigned long flags; + ffs->state = FFS_DEACTIVATED; spin_lock_irqsave(&ffs->eps_lock, flags); epfiles = ffs->epfiles; @@ -2191,7 +2190,6 @@ static void ffs_data_closed(struct ffs_data *ffs) ffs_data_reset(ffs); } } - ffs_data_put(ffs); } static struct ffs_data *ffs_data_new(const char *dev_name) From 5fb0b09180a0fff9a8891d31484cce6456138668 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Jul 2026 22:25:49 -0400 Subject: [PATCH 2193/3297] functionfs: switch to simple_remove_by_name() [ Upstream commit c7747fafaba0dcdad3d7da240e961927b4865f98 ] No need to return dentry from ffs_sb_create_file() or keep it around afterwards. To avoid subtle issues with getting to ffs from epfiles in ffs_epfiles_destroy(), pass the superblock as explicit argument. Callers have it anyway. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Al Viro Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 51 +++++++++++++----------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 9fd06780f5992..9816d672b509c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -162,8 +162,6 @@ struct ffs_epfile { struct ffs_data *ffs; struct ffs_ep *ep; /* P: ffs->eps_lock */ - struct dentry *dentry; - /* * Buffer for holding data from partial reads which may happen since * we’re rounding user read requests to a multiple of a max packet size. @@ -273,11 +271,11 @@ struct ffs_desc_helper { }; static int __must_check ffs_epfiles_create(struct ffs_data *ffs); -static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); +static void ffs_epfiles_destroy(struct super_block *sb, + struct ffs_epfile *epfiles, unsigned count); -static struct dentry * -ffs_sb_create_file(struct super_block *sb, const char *name, void *data, - const struct file_operations *fops); +static int ffs_sb_create_file(struct super_block *sb, const char *name, + void *data, const struct file_operations *fops); /* Devices management *******************************************************/ @@ -1881,9 +1879,8 @@ ffs_sb_make_inode(struct super_block *sb, void *data, } /* Create "regular" file */ -static struct dentry *ffs_sb_create_file(struct super_block *sb, - const char *name, void *data, - const struct file_operations *fops) +static int ffs_sb_create_file(struct super_block *sb, const char *name, + void *data, const struct file_operations *fops) { struct ffs_data *ffs = sb->s_fs_info; struct dentry *dentry; @@ -1891,16 +1888,16 @@ static struct dentry *ffs_sb_create_file(struct super_block *sb, dentry = d_alloc_name(sb->s_root, name); if (!dentry) - return NULL; + return -ENOMEM; inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms); if (!inode) { dput(dentry); - return NULL; + return -ENOMEM; } d_add(dentry, inode); - return dentry; + return 0; } /* Super block */ @@ -1943,10 +1940,7 @@ static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc) return -ENOMEM; /* EP0 file */ - if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations)) - return -ENOMEM; - - return 0; + return ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations); } enum { @@ -2180,7 +2174,7 @@ static void ffs_data_closed(struct ffs_data *ffs) flags); if (epfiles) - ffs_epfiles_destroy(epfiles, + ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); if (ffs->setup_state == FFS_SETUP_PENDING) @@ -2239,7 +2233,7 @@ static void ffs_data_clear(struct ffs_data *ffs) * copy of epfile will save us from use-after-free. */ if (epfiles) { - ffs_epfiles_destroy(epfiles, ffs->eps_count); + ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); ffs->epfiles = NULL; } @@ -2336,6 +2330,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs) { struct ffs_epfile *epfile, *epfiles; unsigned i, count; + int err; count = ffs->eps_count; epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL); @@ -2353,12 +2348,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs) else sprintf(epfile->name, "ep%u", i); epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0; - epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name, - epfile, - &ffs_epfile_operations); - if (!epfile->dentry) { - ffs_epfiles_destroy(epfiles, i - 1); - return -ENOMEM; + err = ffs_sb_create_file(ffs->sb, epfile->name, + epfile, &ffs_epfile_operations); + if (err) { + ffs_epfiles_destroy(ffs->sb, epfiles, i - 1); + return err; } } @@ -2366,16 +2360,15 @@ static int ffs_epfiles_create(struct ffs_data *ffs) return 0; } -static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) +static void ffs_epfiles_destroy(struct super_block *sb, + struct ffs_epfile *epfiles, unsigned count) { struct ffs_epfile *epfile = epfiles; + struct dentry *root = sb->s_root; for (; count; --count, ++epfile) { BUG_ON(mutex_is_locked(&epfile->mutex)); - if (epfile->dentry) { - simple_recursive_removal(epfile->dentry, NULL); - epfile->dentry = NULL; - } + simple_remove_by_name(root, epfile->name, NULL); } kfree(epfiles); From 8a2fdbf92cdc7d523a8d43823d96a4da1bfe6518 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Jul 2026 22:25:50 -0400 Subject: [PATCH 2194/3297] functionfs: use spinlock for FFS_DEACTIVATED/FFS_CLOSING transitions [ Upstream commit 2005aabe94eaab8608879d98afb901bc99bc3a31 ] When all files are closed, functionfs needs ffs_data_reset() to be done before any further opens are allowed. During that time we have ffs->state set to FFS_CLOSING; that makes ->open() fail with -EBUSY. Once ffs_data_reset() is done, it switches state (to FFS_READ_DESCRIPTORS) indicating that opening that thing is allowed again. There's a couple of additional twists: * mounting with -o no_disconnect delays ffs_data_reset() from doing that at the final ->release() to the first subsequent open(). That's indicated by ffs->state set to FFS_DEACTIVATED; if open() sees that, it immediately switches to FFS_CLOSING and proceeds with doing ffs_data_reset() before returning to userland. * a couple of usb callbacks need to force the delayed transition; unfortunately, they are done in locking environment that does not allow blocking and ffs_data_reset() can block. As the result, if these callbacks see FFS_DEACTIVATED, they change state to FFS_CLOSING and use schedule_work() to get ffs_data_reset() executed asynchronously. Unfortunately, the locking is rather insufficient. A fix attempted in e5bf5ee26663 ("functionfs: fix the open/removal races") had closed a bunch of UAF, but it didn't do anything to the callbacks, lacked barriers in transition from FFS_CLOSING to FFS_READ_DESCRIPTORS _and_ it had been too heavy-handed in open()/open() serialization - I've used ffs->mutex for that, and it's being held over actual IO on ep0, complete with copy_from_user(), etc. Even more unfortunately, the userland side is apparently racy enough to have the resulting timing changes (no failures, just a delayed return of open(2)) disrupt the things quite badly. Userland bugs or not, it's a clear regression that needs to be dealt with. Solution is to use a spinlock for serializing these state checks and transitions - unlike ffs->mutex it can be taken in these callbacks and it doesn't disrupt the timings in open(). We could introduce a new spinlock, but it's easier to use the one that is already there (ffs->eps_lock) instead - the locking environment is safe for it in all affected places. Since now it is held over all places that alter or check the open count (ffs->opened), there's no need to keep that atomic_t - int would serve just fine and it's simpler that way. Fixes: e5bf5ee26663 ("functionfs: fix the open/removal races") Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode") # v4.0 Tested-by: Samuel Wu Signed-off-by: Al Viro Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 9816d672b509c..8720ca0f64d09 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3736,6 +3736,7 @@ static int ffs_func_set_alt(struct usb_function *f, { struct ffs_function *func = ffs_func_from_usb(f); struct ffs_data *ffs = func->ffs; + unsigned long flags; int ret = 0, intf; if (alt > MAX_ALT_SETTINGS) @@ -3748,12 +3749,15 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->func) ffs_func_eps_disable(ffs->func); + spin_lock_irqsave(&ffs->eps_lock, flags); if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; + spin_unlock_irqrestore(&ffs->eps_lock, flags); INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return -ENODEV; } + spin_unlock_irqrestore(&ffs->eps_lock, flags); if (ffs->state != FFS_ACTIVE) return -ENODEV; @@ -3771,16 +3775,20 @@ static void ffs_func_disable(struct usb_function *f) { struct ffs_function *func = ffs_func_from_usb(f); struct ffs_data *ffs = func->ffs; + unsigned long flags; if (ffs->func) ffs_func_eps_disable(ffs->func); + spin_lock_irqsave(&ffs->eps_lock, flags); if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; + spin_unlock_irqrestore(&ffs->eps_lock, flags); INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return; } + spin_unlock_irqrestore(&ffs->eps_lock, flags); if (ffs->state == FFS_ACTIVE) { ffs->func = NULL; From 69faa3779250df14f51d5084f938a99809546e52 Mon Sep 17 00:00:00 2001 From: Tyler Baker Date: Sun, 19 Jul 2026 22:25:51 -0400 Subject: [PATCH 2195/3297] usb: gadget: f_fs: initialize reset_work at allocation time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3137b243c93982fe3460335e12f9247739766e10 ] ffs_fs_kill_sb() unconditionally calls cancel_work_sync() on ffs->reset_work when a functionfs instance is unmounted: ffs_data_reset(ffs); cancel_work_sync(&ffs->reset_work); However ffs->reset_work is only ever initialized via INIT_WORK() in ffs_func_set_alt() and ffs_func_disable(), and only on the FFS_DEACTIVATED path. That state is reached solely by ffs_data_closed() when the instance is mounted with the "no_disconnect" option, so for the common case (no "no_disconnect", or mounted and unmounted without ever being deactivated) reset_work is never initialized. ffs_data_new() allocates the ffs_data with kzalloc_obj() and does not initialize reset_work, and ffs_data_reset()/ffs_data_clear() do not touch it either, so reset_work.func is left NULL. cancel_work_sync() on such a work then trips the WARN_ON(!work->func) guard in __flush_work(): WARNING: kernel/workqueue.c:4301 at __flush_work+0x330/0x360, CPU#3: umount Call trace: __flush_work cancel_work_sync ffs_fs_kill_sb [usb_f_fs] deactivate_locked_super deactivate_super cleanup_mnt __cleanup_mnt task_work_run exit_to_user_mode_loop el0_svc On older kernels cancel_work_sync() on a zero-initialized work struct was a silent no-op, which hid the missing initialization. Initialize reset_work once in ffs_data_new() so it is always valid for the lifetime of the ffs_data, and drop the now-redundant INIT_WORK() calls from the two deactivation paths. Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode") Cc: stable Signed-off-by: Tyler Baker Cc: Loic Poulain Cc: Dmitry Baryshkov Cc: Srinivas Kandagatla Tested-by: Loic Poulain Reviewed-by: Peter Chen Acked-by: Michał Nazarewicz Link: https://patch.msgid.link/20260609193635.2284430-1-tyler.baker@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 8720ca0f64d09..deeb43836197a 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -289,6 +289,7 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data); static void ffs_release_dev(struct ffs_dev *ffs_dev); static int ffs_ready(struct ffs_data *ffs); static void ffs_closed(struct ffs_data *ffs); +static void ffs_reset_work(struct work_struct *work); /* Misc helper functions ****************************************************/ @@ -2206,6 +2207,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name) init_waitqueue_head(&ffs->ev.waitq); init_waitqueue_head(&ffs->wait); init_completion(&ffs->ep0req_completion); + INIT_WORK(&ffs->reset_work, ffs_reset_work); /* XXX REVISIT need to update it in some places, or do we? */ ffs->ev.can_stall = 1; @@ -3753,7 +3755,6 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return -ENODEV; } @@ -3784,7 +3785,6 @@ static void ffs_func_disable(struct usb_function *f) if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return; } From 952db4b985c798c3791aa0151654c57fe781d65a Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Mon, 20 Jul 2026 06:54:48 -0400 Subject: [PATCH 2196/3297] usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8bdcf96eb135aebacac319667f87db034fb38406 ] Currently, ffs_epfile_release unconditionally frees the endpoint's read_buffer when a file descriptor is closed. If userspace explicitly opens the endpoint multiple times and closes one, the read_buffer is destroyed. This can lead to silent data loss if other file descriptors are still actively reading from the endpoint. By tying the lifetime of the read_buffer to the ffs_epfile structure itself (which is destroyed when the functionfs instance is torn down in ffs_epfiles_destroy), we eliminate the brittle dependency on open/release calls while correctly matching the conceptual lifetime of unread data on the hardware endpoint. Fixes: 9353afbbfa7b ("usb: gadget: f_fs: buffer data from ‘oversized’ OUT requests") Cc: stable Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Neill Kapron Link: https://patch.msgid.link/20260619040609.4010746-3-nkapron@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin 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 deeb43836197a..3116060977185 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1353,7 +1353,6 @@ ffs_epfile_release(struct inode *inode, struct file *file) mutex_unlock(&epfile->dmabufs_mutex); - __ffs_epfile_read_buffer_free(epfile); ffs_data_closed(epfile->ffs); return 0; @@ -2370,6 +2369,7 @@ static void ffs_epfiles_destroy(struct super_block *sb, for (; count; --count, ++epfile) { BUG_ON(mutex_is_locked(&epfile->mutex)); + __ffs_epfile_read_buffer_free(epfile); simple_remove_by_name(root, epfile->name, NULL); } From 382fd8004cc6004842a6a9772f16b2750207c2a0 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 20 Jul 2026 09:30:39 -0400 Subject: [PATCH 2197/3297] crypto: atmel-sha204a - fail on hwrng registration error in probe path [ Upstream commit 49e05bb00f2e8168695f7af4d694c39e1423e8a2 ] Commit 13909a0c8897 ("crypto: atmel-sha204a - provide the otp content") overwrote the hwrng registration return value when creating the sysfs group, which allowed atmel_sha204a_probe() to succeed even if devm_hwrng_register() failed. Return immediately when devm_hwrng_register() fails, and report both hwrng and sysfs registration errors with dev_err(). Adjust the sysfs error log message for consistency. Fixes: 13909a0c8897 ("crypto: atmel-sha204a - provide the otp content") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/atmel-sha204a.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c index e189dfd471c07..8a3520c9a0ff5 100644 --- a/drivers/crypto/atmel-sha204a.c +++ b/drivers/crypto/atmel-sha204a.c @@ -183,8 +183,10 @@ static int atmel_sha204a_probe(struct i2c_client *client) i2c_priv->hwrng.quality = *quality; ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng); - if (ret) - dev_warn(&client->dev, "failed to register RNG (%d)\n", ret); + if (ret) { + dev_err(&client->dev, "failed to register RNG (%d)\n", ret); + return ret; + } /* otp read out */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) @@ -192,7 +194,7 @@ static int atmel_sha204a_probe(struct i2c_client *client) ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups); if (ret) { - dev_err(&client->dev, "failed to register sysfs entry\n"); + dev_err(&client->dev, "failed to create sysfs group (%d)\n", ret); return ret; } From 99d4ae3fbb5b198c6d5eac5a45f2ad4b46ecaf56 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Jul 2026 11:01:06 -0400 Subject: [PATCH 2198/3297] btrfs: concentrate the error handling of submit_one_sector() [ Upstream commit 44820d80026e0b509007d41c83d42f1213ee8589 ] Currently submit_one_sector() has only one failure path from btrfs_get_extent(). However the error handling is split into two parts, one inside submit_one_sector(), which clears the dirty flag and finishes the writeback for the fs block. The other part is to submit any remaining bio inside bio_ctrl and mark the ordered extent finished for the fs block. There is no special reason that we must split the error handling, let's just concentrate all the error handling into submit_one_sector(). Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba Stable-dep-of: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/extent_io.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 4b2bd3cc3ed33..b8e2b605d52c1 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1599,7 +1599,7 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, /* * Return 0 if we have submitted or queued the sector for submission. - * Return <0 for critical errors, and the sector will have its dirty flag cleared. + * Return <0 for critical errors, and the involved sector will be cleaned up. * * Caller should make sure filepos < i_size and handle filepos >= i_size case. */ @@ -1623,6 +1623,13 @@ static int submit_one_sector(struct btrfs_inode *inode, em = btrfs_get_extent(inode, NULL, filepos, sectorsize); if (IS_ERR(em)) { + /* + * bio_ctrl may contain a bio crossing several folios. + * Submit it immediately so that the bio has a chance + * to finish normally, other than marked as error. + */ + submit_one_bio(bio_ctrl); + /* * When submission failed, we should still clear the folio dirty. * Or the folio will be written back again but without any @@ -1631,6 +1638,13 @@ static int submit_one_sector(struct btrfs_inode *inode, btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize); btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize); btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize); + + /* + * Since there is no bio submitted to finish the ordered + * extent, we have to manually finish this sector. + */ + btrfs_mark_ordered_io_finished(inode, folio, filepos, + fs_info->sectorsize, false); return PTR_ERR(em); } @@ -1756,19 +1770,6 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode, } ret = submit_one_sector(inode, folio, cur, bio_ctrl, i_size); if (unlikely(ret < 0)) { - /* - * bio_ctrl may contain a bio crossing several folios. - * Submit it immediately so that the bio has a chance - * to finish normally, other than marked as error. - */ - submit_one_bio(bio_ctrl); - /* - * Failed to grab the extent map which should be very rare. - * Since there is no bio submitted to finish the ordered - * extent, we have to manually finish this sector. - */ - btrfs_mark_ordered_io_finished(inode, folio, cur, - fs_info->sectorsize, false); if (!found_error) found_error = ret; continue; From 1a648c50a5057e6a71b49a954ef438fd8aa22eb6 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Jul 2026 11:01:07 -0400 Subject: [PATCH 2199/3297] btrfs: replace for_each_set_bit() with for_each_set_bitmap() [ Upstream commit e6698b34fab33867ef3faeeea6feb165f31aae24 ] Inside extent_io.c, there are several simple call sites doing things like: for_each_set_bit(bit, bitmap, bitmap_size) { /* handle one fs block */ } The workload includes: - set_bit() Inside extent_writepage_io(). This can be replaced with a bitmap_set(). - btrfs_folio_set_lock() - btrfs_mark_ordered_io_finished() Inside writepage_delalloc(). Instead of calling it multiple times, we can pass a range into the function with one call. Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba Stable-dep-of: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/extent_io.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index b8e2b605d52c1..88b5581df7dc1 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1427,8 +1427,9 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, u64 delalloc_start = page_start; u64 delalloc_end = page_end; u64 delalloc_to_write = 0; + unsigned int start_bit; + unsigned int end_bit; int ret = 0; - int bit; /* Save the dirty bitmap as our submission bitmap will be a subset of it. */ if (btrfs_is_subpage(fs_info, folio)) { @@ -1438,10 +1439,12 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, bio_ctrl->submit_bitmap = 1; } - for_each_set_bit(bit, &bio_ctrl->submit_bitmap, blocks_per_folio) { - u64 start = page_start + (bit << fs_info->sectorsize_bits); + for_each_set_bitrange(start_bit, end_bit, &bio_ctrl->submit_bitmap, + blocks_per_folio) { + u64 start = page_start + (start_bit << fs_info->sectorsize_bits); + u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits; - btrfs_folio_set_lock(fs_info, folio, start, fs_info->sectorsize); + btrfs_folio_set_lock(fs_info, folio, start, len); } /* Lock all (subpage) delalloc ranges inside the folio first. */ @@ -1558,10 +1561,13 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, fs_info->sectorsize_bits, blocks_per_folio); - for_each_set_bit(bit, &bio_ctrl->submit_bitmap, bitmap_size) - btrfs_mark_ordered_io_finished(inode, folio, - page_start + (bit << fs_info->sectorsize_bits), - fs_info->sectorsize, false); + for_each_set_bitrange(start_bit, end_bit, &bio_ctrl->submit_bitmap, + bitmap_size) { + u64 start = page_start + (start_bit << fs_info->sectorsize_bits); + u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits; + + btrfs_mark_ordered_io_finished(inode, folio, start, len, false); + } return ret; } out: @@ -1727,8 +1733,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode, return ret; } - for (cur = start; cur < end; cur += fs_info->sectorsize) - set_bit((cur - folio_start) >> fs_info->sectorsize_bits, &range_bitmap); + bitmap_set(&range_bitmap, (start - folio_pos(folio)) >> fs_info->sectorsize_bits, + len >> fs_info->sectorsize_bits); bitmap_and(&bio_ctrl->submit_bitmap, &bio_ctrl->submit_bitmap, &range_bitmap, blocks_per_folio); From 6c78081d047c59222317f717882e7ad2bd6ae318 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Jul 2026 11:01:08 -0400 Subject: [PATCH 2200/3297] btrfs: remove folio parameter from ordered io related functions [ Upstream commit a11d6912fdd9e57aff889ec97256b1d6b4e5bf06 ] Both functions btrfs_finish_ordered_extent() and btrfs_mark_ordered_io_finished() are accepting an optional folio parameter. That @folio is passed into can_finish_ordered_extent(), which later will test and clear the ordered flag for the involved range. However I do not think there is any other call site that can clear ordered flags of an page cache folio and can affect can_finish_ordered_extent(). There are limited *_clear_ordered() callers out of can_finish_ordered_extent() function: - btrfs_migrate_folio() This is completely unrelated, it's just migrating the ordered flag to the new folio. - btrfs_cleanup_ordered_extents() We manually clean the ordered flags of all involved folios, then call btrfs_mark_ordered_io_finished() without a @folio parameter. So it doesn't need and didn't pass a @folio parameter in the first place. - btrfs_writepage_fixup_worker() This function is going to be removed soon, and we should not hit that function anymore. - btrfs_invalidate_folio() This is the real call site we need to bother with. If we already have a bio running, btrfs_finish_ordered_extent() in end_bbio_data_write() will be executed first, as btrfs_invalidate_folio() will wait for the writeback to finish. Thus if there is a running bio, it will not see the range has ordered flags, and just skip to the next range. If there is no bio running, meaning the ordered extent is created but the folio is not yet submitted. In that case btrfs_invalidate_folio() will manually clear the folio ordered range, but then manually finish the ordered extent with btrfs_dec_test_ordered_pending() without bothering the folio ordered flags. Meaning if the OE range with folio ordered flags will be finished manually without the need to call can_finish_ordered_extent(). This means all can_finish_ordered_extent() call sites should get a range that has folio ordered flag set, thus the old "return false" branch should never be triggered. Now we can: - Remove the @folio parameter from involved functions * btrfs_mark_ordered_io_finished() * btrfs_finish_ordered_extent() For call sites passing a @folio into those functions, let them manually clear the ordered flag of involved folios. - Move btrfs_finish_ordered_extent() out of the loop in end_bbio_data_write() We only need to call btrfs_finish_ordered_extent() once per bbio, not per folio. - Add an ASSERT() to make sure all folio ranges have ordered flags It's only for end_bbio_data_write(). And we already have enough safe nets to catch over-accounting of ordered extents. Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Stable-dep-of: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/compression.c | 2 +- fs/btrfs/direct-io.c | 9 ++++----- fs/btrfs/extent_io.c | 23 ++++++++++++++--------- fs/btrfs/inode.c | 10 ++++++---- fs/btrfs/ordered-data.c | 29 +++++------------------------ fs/btrfs/ordered-data.h | 6 ++---- 6 files changed, 32 insertions(+), 47 deletions(-) diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index fbf1ff5965994..bd55541cd6c52 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -324,7 +324,7 @@ static void btrfs_finish_compressed_write_work(struct work_struct *work) struct compressed_bio *cb = container_of(work, struct compressed_bio, write_end_work); - btrfs_finish_ordered_extent(cb->bbio.ordered, NULL, cb->start, cb->len, + btrfs_finish_ordered_extent(cb->bbio.ordered, cb->start, cb->len, cb->bbio.bio.bi_status == BLK_STS_OK); if (cb->writeback) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 3836414cbe371..3460a8bcbad76 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -625,7 +625,7 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length, pos += submitted; length -= submitted; if (write) - btrfs_finish_ordered_extent(dio_data->ordered, NULL, + btrfs_finish_ordered_extent(dio_data->ordered, pos, length, false); else btrfs_unlock_dio_extent(&BTRFS_I(inode)->io_tree, pos, @@ -657,9 +657,8 @@ static void btrfs_dio_end_io(struct btrfs_bio *bbio) } if (btrfs_op(bio) == BTRFS_MAP_WRITE) { - btrfs_finish_ordered_extent(bbio->ordered, NULL, - dip->file_offset, dip->bytes, - !bio->bi_status); + btrfs_finish_ordered_extent(bbio->ordered, dip->file_offset, + dip->bytes, !bio->bi_status); } else { btrfs_unlock_dio_extent(&inode->io_tree, dip->file_offset, dip->file_offset + dip->bytes - 1, NULL); @@ -735,7 +734,7 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio, ret = btrfs_extract_ordered_extent(bbio, dio_data->ordered); if (ret) { - btrfs_finish_ordered_extent(dio_data->ordered, NULL, + btrfs_finish_ordered_extent(dio_data->ordered, file_offset, dip->bytes, !ret); bio->bi_status = errno_to_blk_status(ret); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 88b5581df7dc1..f955b4003d424 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -523,6 +523,7 @@ static void end_bbio_data_write(struct btrfs_bio *bbio) int error = blk_status_to_errno(bio->bi_status); struct folio_iter fi; const u32 sectorsize = fs_info->sectorsize; + u32 bio_size = 0; ASSERT(!bio_flagged(bio, BIO_CLONED)); bio_for_each_folio_all(fi, bio) { @@ -530,6 +531,7 @@ static void end_bbio_data_write(struct btrfs_bio *bbio) u64 start = folio_pos(folio) + fi.offset; u32 len = fi.length; + bio_size += len; /* Our read/write should always be sector aligned. */ if (!IS_ALIGNED(fi.offset, sectorsize)) btrfs_err(fs_info, @@ -540,13 +542,15 @@ static void end_bbio_data_write(struct btrfs_bio *bbio) "incomplete page write with offset %zu and length %zu", fi.offset, fi.length); - btrfs_finish_ordered_extent(bbio->ordered, folio, start, len, - !error); if (error) mapping_set_error(folio->mapping, error); + + ASSERT(btrfs_folio_test_ordered(fs_info, folio, start, len)); + btrfs_folio_clear_ordered(fs_info, folio, start, len); btrfs_folio_clear_writeback(fs_info, folio, start, len); } + btrfs_finish_ordered_extent(bbio->ordered, bbio->file_offset, bio_size, !error); bio_put(bio); } @@ -1566,7 +1570,8 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, u64 start = page_start + (start_bit << fs_info->sectorsize_bits); u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits; - btrfs_mark_ordered_io_finished(inode, folio, start, len, false); + btrfs_folio_clear_ordered(fs_info, folio, start, len); + btrfs_mark_ordered_io_finished(inode, start, len, false); } return ret; } @@ -1642,6 +1647,7 @@ static int submit_one_sector(struct btrfs_inode *inode, * ordered extent. */ btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize); + btrfs_folio_clear_ordered(fs_info, folio, filepos, sectorsize); btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize); btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize); @@ -1649,8 +1655,8 @@ static int submit_one_sector(struct btrfs_inode *inode, * Since there is no bio submitted to finish the ordered * extent, we have to manually finish this sector. */ - btrfs_mark_ordered_io_finished(inode, folio, filepos, - fs_info->sectorsize, false); + btrfs_mark_ordered_io_finished(inode, filepos, fs_info->sectorsize, + false); return PTR_ERR(em); } @@ -1761,8 +1767,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode, spin_unlock_irqrestore(&inode->ordered_tree_lock, flags); btrfs_put_ordered_extent(ordered); - btrfs_mark_ordered_io_finished(inode, folio, cur, - fs_info->sectorsize, true); + btrfs_folio_clear_ordered(fs_info, folio, cur, fs_info->sectorsize); + btrfs_mark_ordered_io_finished(inode, cur, fs_info->sectorsize, true); /* * This range is beyond i_size, thus we don't need to * bother writing back. @@ -2640,8 +2646,7 @@ void extent_write_locked_range(struct inode *inode, const struct folio *locked_f if (IS_ERR(folio)) { cur_end = min(round_down(cur, PAGE_SIZE) + PAGE_SIZE - 1, end); cur_len = cur_end + 1 - cur; - btrfs_mark_ordered_io_finished(BTRFS_I(inode), NULL, - cur, cur_len, false); + btrfs_mark_ordered_io_finished(BTRFS_I(inode), cur, cur_len, false); mapping_set_error(mapping, PTR_ERR(folio)); cur = cur_end; continue; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d37e694b3b38c..29eaac1a81325 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -424,7 +424,7 @@ static inline void btrfs_cleanup_ordered_extents(struct btrfs_inode *inode, folio_put(folio); } - return btrfs_mark_ordered_io_finished(inode, NULL, offset, bytes, false); + return btrfs_mark_ordered_io_finished(inode, offset, bytes, false); } static int btrfs_dirty_inode(struct btrfs_inode *inode); @@ -2864,7 +2864,9 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work) * to reflect the errors and clean the page. */ mapping_set_error(folio->mapping, ret); - btrfs_mark_ordered_io_finished(inode, folio, page_start, + btrfs_folio_clear_ordered(fs_info, folio, page_start, + folio_size(folio)); + btrfs_mark_ordered_io_finished(inode, page_start, folio_size(folio), !ret); folio_clear_dirty_for_io(folio); } @@ -7679,11 +7681,11 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset, EXTENT_LOCKED | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, &cached_state); - spin_lock_irq(&inode->ordered_tree_lock); + spin_lock(&inode->ordered_tree_lock); set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags); ordered->truncated_len = min(ordered->truncated_len, cur - ordered->file_offset); - spin_unlock_irq(&inode->ordered_tree_lock); + spin_unlock(&inode->ordered_tree_lock); /* * If the ordered extent has finished, we're safe to delete all diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 8a8aa6ed405bd..89f6b6f8cc531 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -348,30 +348,13 @@ static void finish_ordered_fn(struct btrfs_work *work) } static bool can_finish_ordered_extent(struct btrfs_ordered_extent *ordered, - struct folio *folio, u64 file_offset, - u64 len, bool uptodate) + u64 file_offset, u64 len, bool uptodate) { struct btrfs_inode *inode = ordered->inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; lockdep_assert_held(&inode->ordered_tree_lock); - if (folio) { - ASSERT(folio->mapping); - ASSERT(folio_pos(folio) <= file_offset); - ASSERT(file_offset + len <= folio_end(folio)); - - /* - * Ordered flag indicates whether we still have - * pending io unfinished for the ordered extent. - * - * If it's not set, we need to skip to next range. - */ - if (!btrfs_folio_test_ordered(fs_info, folio, file_offset, len)) - return false; - btrfs_folio_clear_ordered(fs_info, folio, file_offset, len); - } - /* Now we're fine to update the accounting. */ if (WARN_ON_ONCE(len > ordered->bytes_left)) { btrfs_crit(fs_info, @@ -413,8 +396,7 @@ static void btrfs_queue_ordered_fn(struct btrfs_ordered_extent *ordered) } void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered, - struct folio *folio, u64 file_offset, u64 len, - bool uptodate) + u64 file_offset, u64 len, bool uptodate) { struct btrfs_inode *inode = ordered->inode; unsigned long flags; @@ -423,7 +405,7 @@ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered, trace_btrfs_finish_ordered_extent(inode, file_offset, len, uptodate); spin_lock_irqsave(&inode->ordered_tree_lock, flags); - ret = can_finish_ordered_extent(ordered, folio, file_offset, len, + ret = can_finish_ordered_extent(ordered, file_offset, len, uptodate); spin_unlock_irqrestore(&inode->ordered_tree_lock, flags); @@ -476,8 +458,7 @@ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered, * extent(s) covering it. */ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode, - struct folio *folio, u64 file_offset, - u64 num_bytes, bool uptodate) + u64 file_offset, u64 num_bytes, bool uptodate) { struct rb_node *node; struct btrfs_ordered_extent *entry = NULL; @@ -540,7 +521,7 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode, ASSERT(end + 1 - cur < U32_MAX); len = end + 1 - cur; - if (can_finish_ordered_extent(entry, folio, cur, len, uptodate)) { + if (can_finish_ordered_extent(entry, cur, len, uptodate)) { spin_unlock_irqrestore(&inode->ordered_tree_lock, flags); btrfs_queue_ordered_fn(entry); spin_lock_irqsave(&inode->ordered_tree_lock, flags); diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 1e6b0b182b295..ab71c164fb089 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -164,11 +164,9 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry); void btrfs_remove_ordered_extent(struct btrfs_inode *btrfs_inode, struct btrfs_ordered_extent *entry); void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered, - struct folio *folio, u64 file_offset, u64 len, - bool uptodate); + u64 file_offset, u64 len, bool uptodate); void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode, - struct folio *folio, u64 file_offset, - u64 num_bytes, bool uptodate); + u64 file_offset, u64 num_bytes, bool uptodate); bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode, struct btrfs_ordered_extent **cached, u64 file_offset, u64 io_size); From a4497a122e27f125ee650315d47942f9eaa5cb27 Mon Sep 17 00:00:00 2001 From: Ahsan Atta Date: Mon, 20 Jul 2026 14:08:35 -0400 Subject: [PATCH 2201/3297] crypto: qat - fix restarting state leak on allocation failure [ Upstream commit 7d3ed20f7e46b3e991936fedd7a28f3ff4aec8d2 ] In adf_dev_aer_schedule_reset(), ADF_STATUS_RESTARTING is set before allocating reset_data. If the allocation fails, the function returns -ENOMEM without queuing reset work, so nothing ever clears the bit. This leaves the device permanently stuck in the restarting state, causing all subsequent reset attempts to be silently skipped. Fix this by using test_and_set_bit() to atomically claim the RESTARTING state, preventing duplicate reset scheduling races under concurrent fatal error reporting. If the subsequent allocation fails, clear the bit to restore clean state so future reset attempts can proceed. Cc: stable@vger.kernel.org Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework") Signed-off-by: Ahsan Atta Co-developed-by: Maksim Lukoshkov Signed-off-by: Maksim Lukoshkov Reviewed-by: Giovanni Cabiddu Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/intel/qat/qat_common/adf_aer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index ad35d1887f162..f26cadc19d6bb 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -207,13 +207,14 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev, struct adf_reset_dev_data *reset_data; if (!adf_dev_started(accel_dev) || - test_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) + test_and_set_bit(ADF_STATUS_RESTARTING, &accel_dev->status)) return 0; - set_bit(ADF_STATUS_RESTARTING, &accel_dev->status); reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); - if (!reset_data) + if (!reset_data) { + clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); return -ENOMEM; + } reset_data->accel_dev = accel_dev; init_completion(&reset_data->compl); reset_data->mode = mode; From 998ee7f01ecfae48f32ffe790644ac237fe54886 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Jul 2026 11:01:09 -0400 Subject: [PATCH 2202/3297] btrfs: fix false IO failure after falling back to buffered write [ Upstream commit 66ff4d366e7eb4d31813d2acabf3af512ce03aa5 ] [BUG] The test case generic/362 will fail with "nodatasum" mount option (*): MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch # generic/362 0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad) # --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930 # +++ /home/adam/xfstests/results//generic/362.out.bad 2026-05-27 10:21:17.574771567 +0930 # @@ -1,2 +1,3 @@ # QA output created by 362 # +First write failed: Input/output error # Silence is golden # ... *: If the test case has been executed before with default data checksum, the failure will not reproduce. Need the following fix to make it reliably reproducible: https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/ [CAUSE] Inside __iomap_dio_rw(), the -EFAULT/-ENOTBLK error is not directly returned. Thus we never got an error pointer from __iomap_dio_rw(). The call chain looks like this: btrfs_direct_write() |- btrfs_dio_write() |- __iomap_dio_rw() | |- iomap_iter() | | |- btrfs_dio_iomap_begin() | | Now an ordered extent is allocated for the 4K write. | | | |- iomi.status = iomap_dio_iter() | | Where iomap_dio_iter() returned -EFAULT. | | | |- ret = iomap_iter() | | |- btrfs_dio_iomap_end() | | | |- btrfs_finish_ordered_extent(uptodate = false) | | | | |- can_finish_ordered_extent() | | | | |- btrfs_mark_ordered_extent_error() | | | | |- mapping_set_error() | | | | Now the address space is marked error. | | | | return -ENOTBLK | | |- return -ENOTBLK | |- if (ret == -ENOTBLK) { ret = 0; } | Now the return value is reset to 0. | Thus no error pointer will be returned. | |- ret = iomap_dio_complete() | Since no byte is submitted, @ret is 0. | |- Fallback to buffered IO | And the buffered write finished without error | |- filemap_fdatawait_range() |- filemap_check_errors() The previous error is recorded, thus an error is returned However the buffered write is properly submitted and finished, the error is from the btrfs_finish_ordered_extent() call with @uptodate = false. [FIX] When a short dio write happened, any range that is submitted will have btrfs_extract_ordered_extent() to be called, thus the submitted range will always have an OE just covering the submitted range. The remaining OE range is never submitted, thus they should be treated as truncated, not an error. So that we can properly reclaim and not insert an unnecessary file extent item, without marking the mapping as error. Extract a helper, btrfs_mark_ordered_extent_truncated(), and utilize that helper to mark the direct IO ordered extent as truncated, so it won't cause failure for the later buffered fallback. [REASON FOR NO FIXES TAG] The bug itself is pretty old, at commit f85781fb505e ("btrfs: switch to iomap for direct IO") we're already passing @uptodate=false finishing the OE. But at that time OE with IOERR won't call mapping_set_error(), so it's not exposed. Later commit d61bec08b904 ("btrfs: mark ordered extent and inode with error if we fail to finish") finally exposed the bug, but that commit is doing a correct job, not the root cause. Anyway the bug is very old, dating back to 5.1x days, thus only CC to stable. CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/direct-io.c | 17 ++++++++++++++--- fs/btrfs/inode.c | 6 +----- fs/btrfs/ordered-data.c | 12 ++++++++++++ fs/btrfs/ordered-data.h | 2 ++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 3460a8bcbad76..8434674e6e5b2 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -624,12 +624,23 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length, if (submitted < length) { pos += submitted; length -= submitted; - if (write) + if (write) { + /* + * We have a short write, if there is any range + * that is submitted properly, that part will have + * its own OE split from the original one. + * + * So for the OE at dio_data->ordered, it's the part + * that is not submitted, and should be marked + * as fully truncated. + */ + btrfs_mark_ordered_extent_truncated(dio_data->ordered, 0); btrfs_finish_ordered_extent(dio_data->ordered, - pos, length, false); - else + pos, length, true); + } else { btrfs_unlock_dio_extent(&BTRFS_I(inode)->io_tree, pos, pos + length - 1, NULL); + } ret = -ENOTBLK; } if (write) { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 29eaac1a81325..30a34cd905ba4 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7681,11 +7681,7 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset, EXTENT_LOCKED | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, &cached_state); - spin_lock(&inode->ordered_tree_lock); - set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags); - ordered->truncated_len = min(ordered->truncated_len, - cur - ordered->file_offset); - spin_unlock(&inode->ordered_tree_lock); + btrfs_mark_ordered_extent_truncated(ordered, cur - ordered->file_offset); /* * If the ordered extent has finished, we're safe to delete all diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 89f6b6f8cc531..b3ed4212bfecd 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -339,6 +339,18 @@ void btrfs_mark_ordered_extent_error(struct btrfs_ordered_extent *ordered) mapping_set_error(ordered->inode->vfs_inode.i_mapping, -EIO); } +void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered, + u64 truncate_len) +{ + struct btrfs_inode *inode = ordered->inode; + + ASSERT(truncate_len <= ordered->num_bytes); + spin_lock(&inode->ordered_tree_lock); + set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags); + ordered->truncated_len = min(ordered->truncated_len, truncate_len); + spin_unlock(&inode->ordered_tree_lock); +} + static void finish_ordered_fn(struct btrfs_work *work) { struct btrfs_ordered_extent *ordered_extent; diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index ab71c164fb089..2e7879c6cbc77 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -220,6 +220,8 @@ bool btrfs_try_lock_ordered_range(struct btrfs_inode *inode, u64 start, u64 end, struct btrfs_ordered_extent *btrfs_split_ordered_extent( struct btrfs_ordered_extent *ordered, u64 len); void btrfs_mark_ordered_extent_error(struct btrfs_ordered_extent *ordered); +void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered, + u64 truncate_len); int __init ordered_data_init(void); void __cold ordered_data_exit(void); From c3a3d3986719026c85950bfa6f41a251fc46e16f Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Jul 2026 12:51:15 -0400 Subject: [PATCH 2203/3297] btrfs: fix incorrect buffered IO fallback for append direct writes [ Upstream commit ff66fe6662330226b3f486014c375538d91c44aa ] [BUG] With the previous bug of short direct writes fixed, test case generic/362 (*) still fails with the following error with nodatasum mount option: # generic/362 0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad) # - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad) # --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930 # +++ /home/adam/xfstests/results//generic/362.out.bad 2026-05-27 10:13:09.072485767 +0930 # @@ -1,2 +1,3 @@ # QA output created by 362 # +Wrong file size after first write, got 8192 expected 4096 # Silence is golden # ... *: If the test case has been executed before with default data checksum, the failure will not reproduce. Need the following fix to make it reliably reproducible: https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/ [CAUSE] Inside btrfs_dio_iomap_begin() for a direct write, we increase the isize if it's beyond the current isize. But if the direct io finished short, we do not revert the isize to the previous value nor to the short write end. Then if we need to fall back to buffered writes, and the write has IOCB_APPEND flag, then the buffered write will be positioned at the incorrect isize. The call chain looks like this: btrfs_direct_write(pos=0, length=4K) |- __iomap_dio_rw() | |- iomap_iter() | | |- btrfs_dio_iomap_begin() | | |- btrfs_get_blocks_direct_write() | | |- i_size_write() | | Which updates the isize to the write end (4K). | | | |- iomap_dio_iter() | | Failed with -EFAULT on the first page. | | | |- iomap_iter() | | |- btrfs_dio_iomap_end() | | Detects a short write, return -ENOTBLK | |- if (ret == -ENOTBLK) { ret = 0;} | Which resets the return value. | |- ret = iomap_dio_complet() | Which returns 0. | |- btrfs_buffered_write(iocb, from); |- generic_write_checks() |- iocb->ki_pos = i_size_read() Which is still the new size (4K), other than the original isize 0. [FIX] Introduce the following btrfs_dio_data members: - old_isize - updated_isize If the direct write has enlarged the isize. Then if we got a short write, and btrfs_dio_data::updated_isize is set, revert to the correct isize based on old_isize and current file position. And here we call i_size_write() without holding an extent lock, which is a very special case that we're safe to do: - Only a single writer can be enlarging isize Enlarging isize will take the exclusive inode lock. - Buffered readers need to wait for the OE we're holding Buffered readers will lock extent and wait for OE of the folio range. Sometimes we can skip the OE wait, but since all page cache is invalidated, the OE wait can not be skipped. But I do not think this is the most elegant solution, nor covers all cases. E.g. if the bio is submitted but IO failed, we are unable to do the revert. I believe the more elegant one would be extend the EXTENT_DIO_LOCKED lifespan for direct writes, so that we can update the isize when a write beyond EOF finished successfully. However that change is too huge for a small bug fix. So only implement the minimal partial fix for now. [REASON FOR NO FIXES TAG] The bug is again very old, before commit f85781fb505e ("btrfs: switch to iomap for direct IO") we are already increasing isize without a proper rollback for short writes. Thus only a CC to stable. CC: stable@vger.kernel.org # 5.15+ Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/direct-io.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 8434674e6e5b2..3091132190b6b 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -15,10 +15,12 @@ struct btrfs_dio_data { ssize_t submitted; + loff_t old_isize; struct extent_changeset *data_reserved; struct btrfs_ordered_extent *ordered; bool data_space_reserved; bool nocow_done; + bool updated_isize; }; struct btrfs_dio_private { @@ -228,6 +230,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, bool space_reserved = false; u64 len = *lenp; u64 prev_len; + loff_t old_isize; int ret = 0; /* @@ -341,8 +344,14 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, * Need to update the i_size under the extent lock so buffered * readers will get the updated i_size when we unlock. */ - if (start + len > i_size_read(inode)) + old_isize = i_size_read(inode); + if (start + len > old_isize) { + if (!dio_data->updated_isize) { + dio_data->old_isize = old_isize; + dio_data->updated_isize = true; + } i_size_write(inode, start + len); + } out: if (ret && space_reserved) { btrfs_delalloc_release_extents(BTRFS_I(inode), len); @@ -625,6 +634,38 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length, pos += submitted; length -= submitted; if (write) { + /* + * Got a short write and have updated the isize, need to + * revert the isize change. + * + * Normally we need to update isize with extent lock hold, + * but we're safe due to the following factors: + * + * - Only a single writer can be enlarging isize + * Enlarging isize will take the exclusive inode lock. + * + * - Buffered readers need to wait for the OE we're holding + * Buffered readers will lock extent and wait for OE + * of the folio range, and since page cache is invalidated + * the OE wait can not be skipped. + * + * So here we are safe to revert the isize before + * finishing the OE, and no reader of the remaining range + * can see the enlarged size. + * + * TODO: Extend the DIO_LOCKED lifespan for direct writes, + * and only enlarge isize after a successful write. + */ + if (dio_data->updated_isize) { + u64 new_isize; + + if (submitted == 0) + new_isize = dio_data->old_isize; + else + new_isize = max(dio_data->old_isize, pos); + i_size_write(inode, new_isize); + dio_data->updated_isize = false; + } /* * We have a short write, if there is any range * that is submitted properly, that part will have From 19d9996435db84596e9f6e0a0c22489ec88dbb1c Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Wed, 1 Jul 2026 16:16:19 -0700 Subject: [PATCH 2204/3297] KVM: arm64: Ensure level is always initialized when relaxing perms commit 100baf0184896f859290a684f864b8200d8ac872 upstream. stage2_update_leaf_attrs() returns early before writing to @level if the table walker returned an error. At the same time, kvm_pgtable_stage2_relax_perms() uses the level as a TLBI TTL hint when the error was EAGAIN, indicating the vCPU raced with a table update and the TLB entry it hit is now stale. Fall back to an unknown TTL if none was provided by the walk. Cc: stable@vger.kernel.org Fixes: be097997a273 ("KVM: arm64: Always invalidate TLB for stage-2 permission faults") Signed-off-by: Oliver Upton Reviewed-by: Wei-Lin Chang Link: https://patch.msgid.link/20260701231620.3300204-2-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/pgtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index cbf8cd2e16735..3e056afea0722 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1291,7 +1291,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags) { int ret; - s8 level; + s8 level = TLBI_TTL_UNKNOWN; kvm_pte_t set = 0, clr = 0; if (prot & KVM_PTE_LEAF_ATTR_HI_SW) From ba37cd4d8a7519677b317d89fa4de87ae1452099 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 7 Jul 2026 17:29:35 +0100 Subject: [PATCH 2205/3297] KVM: arm64: Fix propagation of TLBI level in kvm_pgtable_stage2_relax_perms() commit 8d187d4b33c262c0f3e44842553521151d8629e8 upstream. Assigning the invalidation level (an s8 value) with TLBI_TTL_UNKNOWN (a 32bit signed value) is not ideal, to say the least. Instead of this, only pass TLBI_TTL_UNKNOWN to __kvm_tlb_flush_vmid_ipa_nsh() when we know for sure that we don't have a provided level. Fixes: 100baf0184896 ("KVM: arm64: Ensure level is always initialized when relaxing perms") Reported-by: Mark Brown Reviewed-by: Oliver Upton Link: https://lore.kernel.org/r/akztC7H2IsEKaq4i@sirena.org.uk Link: https://patch.msgid.link/20260707162935.1900874-1-maz@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/hyp/pgtable.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 3e056afea0722..3a088c5713674 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -1291,7 +1291,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags) { int ret; - s8 level = TLBI_TTL_UNKNOWN; + s8 level; kvm_pte_t set = 0, clr = 0; if (prot & KVM_PTE_LEAF_ATTR_HI_SW) @@ -1308,7 +1308,8 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level, flags); if (!ret || ret == -EAGAIN) - kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, level); + kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, + (ret == -EAGAIN) ? TLBI_TTL_UNKNOWN : level); return ret; } From 3b91c35961fa5553b4dd36db1f06e3b4acbfc05d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Thu, 4 Jun 2026 18:38:48 -0700 Subject: [PATCH 2206/3297] mm/damon/core: always put unsuccessfully committed target pids commit 6a66c557a2ab2609575bafd15e093669c05f9711 upstream. damon_commit_target() puts and gets the destination and the source target pids. It puts the destination target pid because it will be overwritten by the source target pid. It gets the source pid because the caller is supposed to eventually put the pids. In more detail, the caller will call damon_destroy_ctx() after damon_commit_ctx() to destroy the entire source context. And in this case, [f]vaddr operation set's cleanup_target() callback will put the pids. The commit operation is made at the context level. The operation can fail in multiple places including in the middle and after the targets commit operations. For any such failures, immediately the error is returned to the damon_commit_ctx() caller. If some or all of the source target pids were committed to the destination during the unsuccessful context commit attempt, those pids should be put twice. The source context will do the put operations using the above explained routine. However, let's suppose the destination context was not originally using [f]vaddr operation set and the commit failed before the ops of the source context is committed. The destination does not have the cleanup_target() ops callback, so it cannot put the pids via the damon_destroy_ctx(). As a result, the pids are leaked. The issue in the real world would be not very common. The commit feature is for changing parameters of running DAMON context while inheriting internal status like the monitoring results. The monitoring results of a physical address range ain't have things that are beneficial to be inherited to a virtual address ranges monitoring. So the problem-causing DAMON control would be not very common in the real world. That said, it is a supported feature. And damon_commit_target() failure due to memory allocation is relatively realistic [1] if there are a huge number of target regions. Fix by putting the pids in the commit operation in case of the failures. The issue was discovered [2] by Sashiko. Link: https://lore.kernel.org/20260605013849.83750-1-sj@kernel.org Link: https://lore.kernel.org/20260603112306.58490-1-akinobu.mita@gmail.com [1] Link: https://lore.kernel.org/20260320020056.835-1-sj@kernel.org [2] Fixes: 83dc7bbaecae ("mm/damon/sysfs: use damon_commit_ctx()") Signed-off-by: SeongJae Park Cc: # 6.11.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index afd1a372ecd78..2673190f9b856 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1173,10 +1173,36 @@ static int damon_commit_target( return 0; } +/* + * damon_revert_target_commits() - revert unsuccessful target commits. + * @dst: Commit destination context + * @failed: Commit failed destination target + * @src: Commit source context + * + * Revert target states that changed by damon_commit_target(), and cannot be + * cleaned up by the destination context's ops.cleanup_target(). + */ +static void damon_revert_target_commits(struct damon_ctx *dst, + struct damon_target *failed, struct damon_ctx *src) +{ + struct damon_target *target; + + if (!damon_target_has_pid(src)) + return; + if (dst->ops.cleanup_target) + return; + damon_for_each_target(target, dst) { + if (target == failed) + return; + put_pid(target->pid); + } +} + static int damon_commit_targets( struct damon_ctx *dst, struct damon_ctx *src) { struct damon_target *dst_target, *next, *src_target, *new_target; + struct damon_target *failed; int i = 0, j = 0, err; damon_for_each_target_safe(dst_target, next, dst) { @@ -1186,8 +1212,10 @@ static int damon_commit_targets( dst_target, damon_target_has_pid(dst), src_target, damon_target_has_pid(src), src->min_sz_region); - if (err) - return err; + if (err) { + failed = dst_target; + goto out; + } } else { struct damos *s; @@ -1201,22 +1229,29 @@ static int damon_commit_targets( } } + failed = NULL; damon_for_each_target_safe(src_target, next, src) { if (j++ < i) continue; new_target = damon_new_target(); - if (!new_target) - return -ENOMEM; + if (!new_target) { + err = -ENOMEM; + goto out; + } err = damon_commit_target(new_target, false, src_target, damon_target_has_pid(src), src->min_sz_region); if (err) { damon_destroy_target(new_target, NULL); - return err; + goto out; } damon_add_target(dst, new_target); } return 0; + +out: + damon_revert_target_commits(dst, failed, src); + return err; } /** @@ -1255,8 +1290,10 @@ int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src) */ if (!damon_attrs_equals(&dst->attrs, &src->attrs)) { err = damon_set_attrs(dst, &src->attrs); - if (err) + if (err) { + damon_revert_target_commits(dst, NULL, src); return err; + } } dst->ops = src->ops; dst->addr_unit = src->addr_unit; From ec976851ad93432bdbd0c53ec1348909d5366622 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 20 May 2026 08:03:10 -0700 Subject: [PATCH 2207/3297] mm/damon/core: trace esz at first setup commit 7e6cc35f5283eab81a14231a64ecd640b690c48c upstream. DAMON traces effective size quota from the second update, only if a change has been made by the update. Tracing only changed updates was an intentional decision to avoid unnecessary same value tracing. Always skipping the first value is just an unintended mistake. The mistake makes the tracepoint based investigation incomplete, because the first effective size quota is never traced. It is not a big issue when the 'consist' quota tuner is used, because it keeps changing the quota in the usual setup. However, when the 'temporal' tuner is used, the quota value is not changed before the goal achievement status is completely changed. For example, if the DAMOS scheme is started with an under-achieved goal, the quota is set to the maximum value, and kept the same value until the goal is achieved. Because DAMON skips the first value, the user cannot know what effective quota the current scheme is using. Only after the goal is achieved, the effective quota is changed to zero, and traced. Unconditionally trace the initial quota value to fix this problem. Note that the 'temporal' quota tuner was introduced by commit af738a6a00c1 ("mm/damon/core: introduce DAMOS_QUOTA_GOAL_TUNER_TEMPORAL"), which was added to 7.1-rc1. But even with the 'consist' quota tuner, the tracing is unintentionally incomplete. Hence this commit marks the introduction of the trace event as the broken commit. Link: https://lore.kernel.org/20260520150311.80925-1-sj@kernel.org Fixes: a86d695193bf ("mm/damon: add trace event for effective size quota") Signed-off-by: SeongJae Park Cc: # 6.17.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index 2673190f9b856..3e221c888e302 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2237,6 +2237,8 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) if (!quota->total_charged_sz && !quota->charged_from) { quota->charged_from = jiffies; damos_set_effective_quota(quota, c); + if (trace_damos_esz_enabled()) + damos_trace_esz(c, s, quota); } /* New charge window starts */ From 9d360fb820a3b9576d155e720ec44fef54d2efb5 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 29 Jun 2026 07:44:31 -0700 Subject: [PATCH 2208/3297] samples/damon/mtier: fail early if address range parameters are invalid commit 7746d72c64054976887928d64d2caf25c5a6dcc0 upstream. The comment on top of `struct damon_region` clearly says that For any use case, @ar should be non-zero positive size. which is now verified in damon_verify_new_region() if the kernel is built with DAMON_DEBUG_SANITY. The WARN_ONCE() can be triggered if the mtier sample module is enabled before node{0,1}_{start,end}_addr have been properly initialized, which is obviously not good. ------------[ cut here ]------------ start 0 >= end 0 WARNING: mm/damon/core.c:217 at damon_new_region+0xf4/0x118, CPU#59: bash/341468 Call trace: damon_new_region+0xf4/0x118 (P) damon_set_regions+0xfc/0x3c0 damon_sample_mtier_build_ctx+0xe8/0x3a8 damon_sample_mtier_start+0x1c/0x90 damon_sample_mtier_enable_store+0x98/0xb0 param_attr_store+0xb4/0x128 module_attr_store+0x2c/0x50 sysfs_kf_write+0x58/0x90 kernfs_fop_write_iter+0x16c/0x238 vfs_write+0x2c0/0x370 ksys_write+0x74/0x118 __arm64_sys_write+0x24/0x38 invoke_syscall+0xa8/0x118 el0_svc_common.constprop.0+0x48/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x54/0x370 el0t_64_sync_handler+0xa0/0xe8 el0t_64_sync+0x1ac/0x1b0 ---[ end trace 0000000000000000 ]--- Note that the same issue can happen if detect_node_addresses is true, and node 0 or 1 is memoryless. Fix it together by checking the validity of parameters right before damon_new_region() and fail early if they're invalid. Link: https://lore.kernel.org/20260629144432.133962-1-sj@kernel.org Fixes: 82a08bde3cf7 ("samples/damon: implement a DAMON module for memory tiering") Signed-off-by: Zenghui Yu Signed-off-by: SJ Park Reviewed-by: SJ Park Cc: # 6.16.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- samples/damon/mtier.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index 775838a23d935..dab8fb10b617a 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -120,6 +120,8 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) addr.end = promote ? node1_end_addr : node0_end_addr; } + if (addr.start >= addr.end) + goto free_out; region = damon_new_region(addr.start, addr.end); if (!region) goto free_out; From 459adfc6cd35f70fc9df3ac53c9930dad18580d0 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 25 Feb 2026 10:47:18 +0000 Subject: [PATCH 2209/3297] KVM: arm64: Deduplicate ASID retrieval code commit 54e367cb94d6bef941bbc1132d9959dc73bd4b6f upstream. We currently have three versions of the ASID retrieval code, one in the S1 walker, and two in the VNCR handling (although the last two are limited to the EL2&0 translation regime). Make this code common, and take this opportunity to also simplify the code a bit while switching over to the TTBRx_EL1_ASID macro. Reviewed-by: Joey Gouly Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/20260225104718.14209-1-maz@kernel.org [maz: cherry-pick as dependency of bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f] Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/kvm_nested.h | 2 + arch/arm64/kvm/at.c | 27 +------------ arch/arm64/kvm/nested.c | 60 +++++++++++++++-------------- 3 files changed, 35 insertions(+), 54 deletions(-) diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index 4c19688102396..6f768e361c894 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -371,6 +371,8 @@ int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu); int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu); void kvm_handle_s1e2_tlbi(struct kvm_vcpu *vcpu, u32 inst, u64 val); +u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime); + #define vncr_fixmap(c) \ ({ \ u32 __c = (c); \ diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 5cdf3faaabee2..2bdc49bef8613 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -483,31 +483,8 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, wr->pa |= va & GENMASK_ULL(va_bottom - 1, 0); wr->nG = (wi->regime != TR_EL2) && (desc & PTE_NG); - if (wr->nG) { - u64 asid_ttbr, tcr; - - switch (wi->regime) { - case TR_EL10: - tcr = vcpu_read_sys_reg(vcpu, TCR_EL1); - asid_ttbr = ((tcr & TCR_A1) ? - vcpu_read_sys_reg(vcpu, TTBR1_EL1) : - vcpu_read_sys_reg(vcpu, TTBR0_EL1)); - break; - case TR_EL20: - tcr = vcpu_read_sys_reg(vcpu, TCR_EL2); - asid_ttbr = ((tcr & TCR_A1) ? - vcpu_read_sys_reg(vcpu, TTBR1_EL2) : - vcpu_read_sys_reg(vcpu, TTBR0_EL2)); - break; - default: - BUG(); - } - - wr->asid = FIELD_GET(TTBR_ASID_MASK, asid_ttbr); - if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) || - !(tcr & TCR_ASID16)) - wr->asid &= GENMASK(7, 0); - } + if (wr->nG) + wr->asid = get_asid_by_regime(vcpu, wi->regime); return 0; diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 1ad295c844251..39f5ff02e63ac 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -818,6 +818,33 @@ int kvm_inject_s2_fault(struct kvm_vcpu *vcpu, u64 esr_el2) return kvm_inject_nested_sync(vcpu, esr_el2); } +u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime) +{ + enum vcpu_sysreg ttbr_elx; + u64 tcr; + u16 asid; + + switch (regime) { + case TR_EL10: + tcr = vcpu_read_sys_reg(vcpu, TCR_EL1); + ttbr_elx = (tcr & TCR_A1) ? TTBR1_EL1 : TTBR0_EL1; + break; + case TR_EL20: + tcr = vcpu_read_sys_reg(vcpu, TCR_EL2); + ttbr_elx = (tcr & TCR_A1) ? TTBR1_EL2 : TTBR0_EL2; + break; + default: + BUG(); + } + + asid = FIELD_GET(TTBRx_EL1_ASID, vcpu_read_sys_reg(vcpu, ttbr_elx)); + if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) || + !(tcr & TCR_ASID16)) + asid &= GENMASK(7, 0); + + return asid; +} + static void invalidate_vncr(struct vncr_tlb *vt) { vt->valid = false; @@ -1314,20 +1341,8 @@ static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu) if (read_vncr_el2(vcpu) != vt->gva) return false; - if (vt->wr.nG) { - u64 tcr = vcpu_read_sys_reg(vcpu, TCR_EL2); - u64 ttbr = ((tcr & TCR_A1) ? - vcpu_read_sys_reg(vcpu, TTBR1_EL2) : - vcpu_read_sys_reg(vcpu, TTBR0_EL2)); - u16 asid; - - asid = FIELD_GET(TTBR_ASID_MASK, ttbr); - if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) || - !(tcr & TCR_ASID16)) - asid &= GENMASK(7, 0); - - return asid == vt->wr.asid; - } + if (vt->wr.nG) + return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid; return true; } @@ -1428,21 +1443,8 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) if (read_vncr_el2(vcpu) != vt->gva) return; - if (vt->wr.nG) { - u64 tcr = vcpu_read_sys_reg(vcpu, TCR_EL2); - u64 ttbr = ((tcr & TCR_A1) ? - vcpu_read_sys_reg(vcpu, TTBR1_EL2) : - vcpu_read_sys_reg(vcpu, TTBR0_EL2)); - u16 asid; - - asid = FIELD_GET(TTBR_ASID_MASK, ttbr); - if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) || - !(tcr & TCR_ASID16)) - asid &= GENMASK(7, 0); - - if (asid != vt->wr.asid) - return; - } + if (vt->wr.nG && get_asid_by_regime(vcpu, TR_EL20) != vt->wr.asid) + return; vt->cpu = smp_processor_id(); From ea7a76d7d614b5f82b4d0785f9af3550e860a71a Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:04 -0700 Subject: [PATCH 2210/3297] KVM: arm64: nv: Re-translate VNCR before injecting abort commit bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f upstream. KVM faults in the VNCR page with FOLL_WRITE whenever the guest aborts for a write, similar to how a regular stage-2 mapping is handled. It is entirely possible that the guest reads from the VNCR before writing to it, in which case the PFN could only be read-only. Invalidate the VNCR TLB and re-fetch the translation upon taking a VNCR abort, allowing the host mapping to be faulted in for write the second time around. Interestingly enough, this also satisfies the ordering requirements of FEAT_ETS2/3 between descriptor updates and MMU faults. Cc: stable@vger.kernel.org Fixes: 2a359e072596 ("KVM: arm64: nv: Handle mapping of VNCR_EL2 at EL2") Reported-by: Sashiko Signed-off-by: Oliver Upton Link: https://patch.msgid.link/20260618234207.1063941-4-oupton@kernel.org Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 111 +++++++++++++++------------------------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 39f5ff02e63ac..10da38dff8b91 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -1329,88 +1329,61 @@ static void handle_vncr_perm(struct kvm_vcpu *vcpu) kvm_inject_nested_sync(vcpu, esr); } -static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu) -{ - struct vncr_tlb *vt = vcpu->arch.vncr_tlb; - - lockdep_assert_held_read(&vcpu->kvm->mmu_lock); - - if (!vt->valid) - return false; - - if (read_vncr_el2(vcpu) != vt->gva) - return false; - - if (vt->wr.nG) - return get_asid_by_regime(vcpu, TR_EL20) == vt->wr.asid; - - return true; -} - int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu) { struct vncr_tlb *vt = vcpu->arch.vncr_tlb; u64 esr = kvm_vcpu_get_esr(vcpu); + bool is_gmem = false; + bool perm; + int ret; WARN_ON_ONCE(!(esr & ESR_ELx_VNCR)); if (kvm_vcpu_abt_issea(vcpu)) return kvm_handle_guest_sea(vcpu); - if (esr_fsc_is_permission_fault(esr)) { - handle_vncr_perm(vcpu); - } else if (esr_fsc_is_translation_fault(esr)) { - bool valid, is_gmem = false; - int ret; - - scoped_guard(read_lock, &vcpu->kvm->mmu_lock) - valid = kvm_vncr_tlb_lookup(vcpu); - - if (!valid) - ret = kvm_translate_vncr(vcpu, &is_gmem); - else - ret = -EPERM; + if (!esr_fsc_is_translation_fault(esr) && !esr_fsc_is_permission_fault(esr)) { + WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr); + return 1; + } - switch (ret) { - case -EAGAIN: - /* Let's try again... */ - break; - case -ENOMEM: - /* - * For guest_memfd, this indicates that it failed to - * create a folio to back the memory. Inform userspace. - */ - if (is_gmem) - return 0; - /* Otherwise, let's try again... */ - break; - case -EFAULT: - case -EIO: - case -EHWPOISON: - if (is_gmem) - return 0; - fallthrough; - case -EINVAL: - case -ENOENT: - case -EACCES: - /* - * Translation failed, inject the corresponding - * exception back to EL2. - */ - esr &= ~ESR_ELx_FSC; - esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); + ret = kvm_translate_vncr(vcpu, &is_gmem); + switch (ret) { + case -EAGAIN: + /* Let's try again... */ + return 1; + case -ENOMEM: + /* + * For guest_memfd, this indicates that it failed to + * create a folio to back the memory. Inform userspace. + */ + if (is_gmem) + return 0; + /* Otherwise, let's try again... */ + break; + case -EFAULT: + case -EIO: + case -EHWPOISON: + if (is_gmem) + return 0; + fallthrough; + case -EINVAL: + case -ENOENT: + case -EACCES: + /* + * Translation failed, inject the corresponding + * exception back to EL2. + */ + esr &= ~ESR_ELx_FSC; + esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst); - kvm_inject_nested_sync(vcpu, esr); - break; - case -EPERM: - /* Hack to deal with POE until we get kernel support */ + kvm_inject_nested_sync(vcpu, esr); + break; + case 0: + perm = kvm_is_write_fault(vcpu) ? vt->wr.pw && vt->hpa_writable : vt->wr.pr; + if (!perm) handle_vncr_perm(vcpu); - break; - case 0: - break; - } - } else { - WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr); + break; } return 1; From 73b5d5cb1f5a1273dc3f0bb86e5a65527c3ec0c3 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 21 Jul 2026 08:22:50 -0400 Subject: [PATCH 2211/3297] iommufd: Propagate allocation failure in iommufd_veventq_deliver_fetch() [ Upstream commit 489e63dd120bad52eba63f5506c214750cd5bc75 ] When the kzalloc_obj() fails in iommufd_veventq_deliver_fetch(), it returns NULL, falsely advertising to userspace that the queue is empty. Propagate the -ENOMEM properly to the caller. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/25d29feac909e36f78c145fa99ef2d4cb7a415da.1780343944.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Signed-off-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/eventq.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 617e20b67bf2a..a934f02528b0b 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -272,8 +272,10 @@ iommufd_veventq_deliver_fetch(struct iommufd_veventq *veventq) /* Make a copy of the lost_events_header for copy_to_user */ if (next == &veventq->lost_events_header) { vevent = kzalloc(sizeof(*vevent), GFP_ATOMIC); - if (!vevent) + if (!vevent) { + vevent = ERR_PTR(-ENOMEM); goto out_unlock; + } } list_del(&next->node); if (vevent) @@ -323,6 +325,12 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf, return -EINVAL; while ((cur = iommufd_veventq_deliver_fetch(veventq))) { + if (IS_ERR(cur)) { + if (done == 0) + rc = PTR_ERR(cur); + break; + } + /* Validate the remaining bytes against the header size */ if (done >= count || sizeof(*hdr) > count - done) { iommufd_veventq_deliver_restore(veventq, cur); From 779480ea79551c31964e74b9aef0e730faa3aa11 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 21 Jul 2026 08:22:46 -0400 Subject: [PATCH 2212/3297] iommufd: Move vevent memory allocation outside spinlock [ Upstream commit 47443565d10c51366c9382dbc8597cd6c460b8a2 ] The veventq memory allocation happens inside the spinlock. Given its depth is decided by the user space, this leaves a vulnerability, where userspace can allocate large queues to exhaust atomic memory reserves. Move the allocation outside the spinlock and use GFP_NOWAIT, which can fail fast under memory pressure without dipping into the GFP_ATOMIC reserves or direct-reclaiming from the threaded IRQ handler. On allocation failure, queue the lost_events_header (so userspace learns of the drop) and return -ENOMEM so the caller learns of the kernel-side memory pressure. This is intentionally distinct from the queue-overflow path, which also queues the lost_events_header but returns 0: a full queue is an expected userspace-pacing condition rather than a kernel error. A subsequent change will cap the upper bound of the veventq_depth. Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") Link: https://patch.msgid.link/r/5ff36b5d80f7f6299f851be532a5195c1d2f1dae.1779408671.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Jason Gunthorpe Signed-off-by: Nicolin Chen Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/driver.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c index 21d4a35538f6a..37961409c6ac0 100644 --- a/drivers/iommu/iommufd/driver.c +++ b/drivers/iommu/iommufd/driver.c @@ -149,15 +149,18 @@ int iommufd_viommu_report_event(struct iommufd_viommu *viommu, goto out_unlock_veventqs; } - spin_lock(&veventq->common.lock); - if (veventq->num_events == veventq->depth) { + /* Pre-allocate to avoid GFP_ATOMIC; use GFP_NOWAIT to avoid sleeping */ + vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_NOWAIT); + if (!vevent) { + spin_lock(&veventq->common.lock); vevent = &veventq->lost_events_header; + rc = -ENOMEM; goto out_set_header; } - vevent = kzalloc(struct_size(vevent, event_data, data_len), GFP_ATOMIC); - if (!vevent) { - rc = -ENOMEM; + spin_lock(&veventq->common.lock); + if (veventq->num_events == veventq->depth) { + kfree(vevent); vevent = &veventq->lost_events_header; goto out_set_header; } From 267fdd9b6530c399dfd996e1a0a7628b45baf9f0 Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Tue, 21 Jul 2026 07:47:00 -0400 Subject: [PATCH 2213/3297] bpf: Reject BPF_MAP_TYPE_INODE_STORAGE creation if BPF LSM is uninitialized [ Upstream commit a6f0643e4f63cfaa0d5d4a69de4f132eac4b8fe4 ] When CONFIG_BPF_LSM=y is set, BPF inode storage maps (BPF_MAP_TYPE_INODE_STORAGE) are compiled into the kernel. However, if the BPF LSM is not explicitly enabled at boot time (e.g. omitted from the "lsm=" boot parameter), lsm_prepare() is never executed for the BPF LSM. Consequently, the BPF inode security blob offset (bpf_lsm_blob_sizes.lbs_inode) is never initialized and remains at its default compiled size of 8 bytes instead of being updated to a valid offset past the reserved struct rcu_head (typically 16 bytes or more). When a privileged user creates and updates a BPF_MAP_TYPE_INODE_STORAGE map, bpf_inode() evaluates inode->i_security + 8. This erroneously aliases the struct rcu_head.func callback pointer at the beginning of the inode->i_security blob. During subsequent map element cleanup or inode destruction, writing NULL to owner_storage clears the queued RCU callback pointer. When rcu_do_batch() later executes the queued callback, it attempts an instruction fetch at address 0x0, triggering an immediate kernel panic. Fix this by introducing a global bpf_lsm_initialized boolean flag marked with __ro_after_init. Set this flag to true inside bpf_lsm_init() when the LSM framework successfully registers the BPF LSM. Gate map allocation in inode_storage_map_alloc() on this flag, returning -EOPNOTSUPP if the BPF LSM is in turn uninitialized. This fail-fast approach prevents userspace from allocating inode storage maps when the supporting BPF LSM infrastructure is absent, avoiding zombie map states. Fixes: 8ea636848aca ("bpf: Implement bpf_local_storage for inodes") Reported-by: oxsignal Signed-off-by: Matt Bobrowski Signed-off-by: Daniel Borkmann Reviewed-by: Emil Tsalapatis Reviewed-by: Amery Hung Link: https://lore.kernel.org/bpf/20260628201103.3624525-1-mattbobrowski@google.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/bpf_lsm.h | 4 ++++ kernel/bpf/bpf_inode_storage.c | 9 +++++++++ security/bpf/hooks.c | 3 +++ 3 files changed, 16 insertions(+) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index 143775a27a2a0..dda272d78f013 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -14,6 +14,8 @@ #ifdef CONFIG_BPF_LSM +extern bool bpf_lsm_initialized __ro_after_init; + #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ RET bpf_lsm_##NAME(__VA_ARGS__); #include @@ -56,6 +58,8 @@ bool bpf_lsm_hook_returns_errno(u32 btf_id); #else /* !CONFIG_BPF_LSM */ +#define bpf_lsm_initialized false + static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id) { return false; diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c index e54cce2b91754..97c3e8f1a929a 100644 --- a/kernel/bpf/bpf_inode_storage.c +++ b/kernel/bpf/bpf_inode_storage.c @@ -181,6 +181,15 @@ static int notsupp_get_next_key(struct bpf_map *map, void *key, static struct bpf_map *inode_storage_map_alloc(union bpf_attr *attr) { + /* + * Do not allow allocation of BPF_MAP_TYPE_INODE_STORAGE if the BPF LSM + * was not initialized by the LSM framework at boot. Without proper + * initialization, the BPF inode security blob offset remains unprepared, + * causing bpf_inode() to calculate an invalid memory offset and corrupt + * inode->i_security. + */ + if (!bpf_lsm_initialized) + return ERR_PTR(-EOPNOTSUPP); return bpf_local_storage_map_alloc(attr, &inode_cache, false); } diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c index db759025abe11..9ea9eb9c5d25e 100644 --- a/security/bpf/hooks.c +++ b/security/bpf/hooks.c @@ -7,6 +7,8 @@ #include #include +bool bpf_lsm_initialized __ro_after_init; + static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = { #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ LSM_HOOK_INIT(NAME, bpf_lsm_##NAME), @@ -24,6 +26,7 @@ static int __init bpf_lsm_init(void) { security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks), &bpf_lsmid); + bpf_lsm_initialized = true; pr_info("LSM support for eBPF active\n"); return 0; } From a888f3d5970ff21e092bc9edcf3d9ffee9ae68a7 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Sat, 15 Nov 2025 18:00:00 +0800 Subject: [PATCH 2214/3297] pinctrl: airoha: fix pinctrl function mismatch issue commit f2bd5a0f59d052d16749bccf637690e51947a5d6 upstream. The blamed commit made the following changes: -#define PINCTRL_FUNC_DESC(id)... - .desc = PINCTRL_PINFUNCTION(#id, ... +#define PINCTRL_FUNC_DESC(id, table)... + .desc = PINCTRL_PINFUNCTION(#id, ... - PINCTRL_FUNC_DESC(pon)... + PINCTRL_FUNC_DESC("pon", pon)... It's clear that the id of funcs doesn't match the definition. Remove redundant #string from the definition to fix this issue: pinctrl-airoha ...: invalid function mdio in map table Fixes: 4043b0c45f85 ("pinctrl: airoha: generalize pins/group/function/confs handling") Signed-off-by: Chukun Pan Acked-by: Christian Marangi Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/mediatek/pinctrl-airoha.c index ae769ef0cdc6b..9f8e05e3671d6 100644 --- a/drivers/pinctrl/mediatek/pinctrl-airoha.c +++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c @@ -35,7 +35,7 @@ #define PINCTRL_FUNC_DESC(id, table) \ { \ - .desc = PINCTRL_PINFUNCTION(#id, table##_groups, \ + .desc = PINCTRL_PINFUNCTION(id, table##_groups, \ ARRAY_SIZE(table##_groups)),\ .groups = table##_func_group, \ .group_size = ARRAY_SIZE(table##_func_group), \ From 7425e7d82cb93c555ee947c381255265ab614d71 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 6 Dec 2025 18:23:45 -0800 Subject: [PATCH 2215/3297] perf symbol: Fix ENOENT case for filename__read_build_id commit 5c5f6fe32df2edb4f72bdca62ec2b9f20b7c5ba4 upstream. Some callers of filename__read_build_id assume the error value must be -1, fix by making them handle all < 0 values. If is_regular_file fails in filename__read_build_id then it could be the file is missing (ENOENT) and it would be wrong to return -EWOULDBLOCK in that case. Fix the logic so -EWOULDBLOCK is only reported if other errors with stat haven't occurred. Fixes: 834ebb5678d7 ("perf tools: Don't read build-ids from non-regular files") Signed-off-by: Ian Rogers Reviewed-by: James Clark Signed-off-by: Namhyung Kim Signed-off-by: Greg Kroah-Hartman --- tools/perf/builtin-buildid-cache.c | 6 ++++-- tools/perf/util/libbfd.c | 4 +++- tools/perf/util/symbol-elf.c | 4 +++- tools/perf/util/symbol-minimal.c | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index c98104481c8a1..539e779e32682 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -276,12 +276,14 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) { char filename[PATH_MAX]; struct build_id bid = { .size = 0, }; + int err; if (!dso__build_id_filename(dso, filename, sizeof(filename), false)) return true; - if (filename__read_build_id(filename, &bid) == -1) { - if (errno == ENOENT) + err = filename__read_build_id(filename, &bid); + if (err < 0) { + if (err == -ENOENT) return false; pr_warning("Problems with %s file, consider removing it from the cache\n", diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c index d9effc3ada995..63ea3fb53e77d 100644 --- a/tools/perf/util/libbfd.c +++ b/tools/perf/util/libbfd.c @@ -426,8 +426,10 @@ int libbfd__read_build_id(const char *filename, struct build_id *bid) if (!filename) return -EFAULT; + + errno = 0; if (!is_regular_file(filename)) - return -EWOULDBLOCK; + return errno == 0 ? -EWOULDBLOCK : -errno; fd = open(filename, O_RDONLY); if (fd < 0) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index bcaeb5ce56873..140f3d5a72411 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -916,8 +916,10 @@ int filename__read_build_id(const char *filename, struct build_id *bid) if (!filename) return -EFAULT; + + errno = 0; if (!is_regular_file(filename)) - return -EWOULDBLOCK; + return errno == 0 ? -EWOULDBLOCK : -errno; err = kmod_path__parse(&m, filename); if (err) diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 2102ed36f2dc2..0a71d14639527 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -113,8 +113,10 @@ int filename__read_build_id(const char *filename, struct build_id *bid) if (!filename) return -EFAULT; + + errno = 0; if (!is_regular_file(filename)) - return -EWOULDBLOCK; + return errno == 0 ? -EWOULDBLOCK : -errno; fd = open(filename, O_RDONLY); if (fd < 0) From 2fb74141ec54e1174b89260c8217cff7868ea5ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 21 Jan 2026 15:14:16 +0200 Subject: [PATCH 2216/3297] PCI: Fix BAR resize rollback path overwriting ret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 08d9eae76b85263173f8c833800e3cc409ee1be4 upstream. The commit 337b1b566db0 ("PCI: Fix restoring BARs on BAR resize rollback path") added BAR rollback to pci_do_resource_release_and_resize() in case of resize failure. On the rollback, pci_claim_resource() is called, which can fail and the code is prepared for that possibility. pci_claim_resource()'s return value, however, overwrites the original value of ret so pci_do_resource_release_and_resize() will return an incorrect value in the end (as pci_claim_resource() normally succeeds, in practice ret will be 0). Fix the issue by directly calling pci_claim_resource() inside the if (). Fixes: 337b1b566db0 ("PCI: Fix restoring BARs on BAR resize rollback path") Reported-by: Ville Syrjälä Link: https://lore.kernel.org/linux-pci/aW_w1oFQCzUxGYtu@intel.com/ Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Ville Syrjälä Reviewed-by: Ville Syrjälä Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260121131417.9582-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/setup-bus.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 03eadc81dc7d3..2aa7d9e35c90e 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2504,8 +2504,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size restore_dev_resource(dev_res); - ret = pci_claim_resource(dev, i); - if (ret) + if (pci_claim_resource(dev, i)) continue; if (i < PCI_BRIDGE_RESOURCES) { From 2764d031efd6d9cc6dbded9deee15026f97f8f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Wed, 21 Jan 2026 15:14:17 +0200 Subject: [PATCH 2217/3297] PCI: Fix Resizable BAR restore order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5528fd38f230c906fcebb202cc94fbb8ed8f122a upstream. The commit 337b1b566db0 ("PCI: Fix restoring BARs on BAR resize rollback path") changed BAR resize to layer rebar code and resource setup/restore code cleanly. Unfortunately, it did not consider how the value of the BAR Size field impacts the read-only bits in the Base Address Register (PCIe7 spec, sec. 7.8.6.3). That is, it very much matters in which order the BAR Size and Base Address Register are restored. Post-337b1b566db0 ("PCI: Fix restoring BARs on BAR resize rollback path") during BAR resize rollback, pci_do_resource_release_and_resize() attempts to restore the old address to the BAR that was resized, but it can fail to setup the address correctly if the address has low bits set that collide with the bits that are still read-only. As a result, kernel's resource and BAR will be out-of-sync. Fix this by restoring BAR Size before rolling back the resource changes and restoring the BAR. Fixes: 337b1b566db0 ("PCI: Fix restoring BARs on BAR resize rollback path") Reported-by: Ville Syrjälä Link: https://lore.kernel.org/linux-pci/aW_w1oFQCzUxGYtu@intel.com/ Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Tested-by: Ville Syrjälä Reviewed-by: Ville Syrjälä Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260121131417.9582-3-ilpo.jarvinen@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/pci/rebar.c | 18 +----------------- drivers/pci/setup-bus.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c index 22853cf538fc1..bff08ce95dbd5 100644 --- a/drivers/pci/rebar.c +++ b/drivers/pci/rebar.c @@ -215,7 +215,6 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size, int exclude_bars) { struct pci_host_bridge *host; - int old, ret; u32 sizes; /* Check if we must preserve the firmware's resource assignment */ @@ -233,21 +232,6 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size, if (!(sizes & BIT(size))) return -EINVAL; - old = pci_rebar_get_current_size(dev, resno); - if (old < 0) - return old; - - ret = pci_rebar_set_size(dev, resno, size); - if (ret) - return ret; - - ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); - if (ret) - goto error_resize; - return 0; - -error_resize: - pci_rebar_set_size(dev, resno, old); - return ret; + return pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); } EXPORT_SYMBOL(pci_resize_resource); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 2aa7d9e35c90e..7ec2ee3fe9093 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2452,12 +2452,20 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size struct resource *b_win, *r; LIST_HEAD(saved); unsigned int i; - int ret = 0; + int old, ret; b_win = pbus_select_window(bus, res); if (!b_win) return -EINVAL; + old = pci_rebar_get_current_size(pdev, resno); + if (old < 0) + return old; + + ret = pci_rebar_set_size(pdev, resno, size); + if (ret) + return ret; + pci_dev_for_each_resource(pdev, r, i) { if (i >= PCI_BRIDGE_RESOURCES) break; @@ -2490,7 +2498,15 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size return ret; restore: - /* Revert to the old configuration */ + /* + * Revert to the old configuration. + * + * BAR Size must be restored first because it affects the read-only + * bits in BAR (the old address might not be restorable otherwise + * due to low address bits). + */ + pci_rebar_set_size(pdev, resno, old); + list_for_each_entry(dev_res, &saved, list) { struct resource *res = dev_res->res; struct pci_dev *dev = dev_res->dev; From 453cb79a15641853170d3abc46021d8e88750b58 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 22 Jan 2026 09:58:46 -0800 Subject: [PATCH 2218/3297] perf inject: With --convert-callchain ignore the dummy event for dwarf stacks commit e786a04b4a5461dd7e2829422314a5a6d5a664d9 upstream. On hybrid systems there is generally >1 event and a dummy event. The perf inject --convert-callchain option is failing to convert perf.data files on such systems reporting "--convert-callchain requires DWARF call graph." The failing event is the dummy event that doesn't need to be set up for samples. As such ignore this event when checking the evsels. Fixes: 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/builtin-inject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 5ea08e80cf1a3..6859f4b183846 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -2735,7 +2735,7 @@ int cmd_inject(int argc, const char **argv) } evlist__for_each_entry(inject.session->evlist, evsel) { - if (!evsel__has_dwarf_callchain(evsel)) { + if (!evsel__has_dwarf_callchain(evsel) && !evsel__is_dummy_event(evsel)) { pr_err("--convert-callchain requires DWARF call graph.\n"); goto out_delete; } From 275eb399309479399b24affb372482bc5bdc41a1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 23 Jan 2026 10:37:49 -0800 Subject: [PATCH 2219/3297] seqlock: fix scoped_seqlock_read kernel-doc commit f88a31308db6a856229150039b0f56d59696ed31 upstream. Eliminate all kernel-doc warnings in seqlock.h: - correct the macro to have "()" immediately following the macro name - don't include the macro parameters in the short description (first line) - make the parameter names in the comments match the actual macro parameter names. - use "::" for the Example WARNING: include/linux/seqlock.h:1341 This comment starts with '/**', but isn't a kernel-doc comment. * scoped_seqlock_read (lock, ss_state) - execute the read side critical Documentation/locking/seqlock:242: include/linux/seqlock.h:1351: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] Warning: include/linux/seqlock.h:1357 function parameter '_seqlock' not described in 'scoped_seqlock_read' Warning: include/linux/seqlock.h:1357 function parameter '_target' not described in 'scoped_seqlock_read' Fixes: cc39f3872c08 ("seqlock: Introduce scoped_seqlock_read()") Signed-off-by: Randy Dunlap Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260123183749.3997533-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/seqlock.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index b7bcc4111e908..6ab7924f33f59 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -1300,15 +1300,14 @@ __scoped_seqlock_next(struct ss_tmp *sst, seqlock_t *lock, enum ss_state target) __scoped_seqlock_next(&_s, _seqlock, _target)) /** - * scoped_seqlock_read (lock, ss_state) - execute the read side critical - * section without manual sequence - * counter handling or calls to other - * helpers - * @lock: pointer to seqlock_t protecting the data - * @ss_state: one of {ss_lock, ss_lock_irqsave, ss_lockless} indicating - * the type of critical read section - * - * Example: + * scoped_seqlock_read() - execute the read-side critical section + * without manual sequence counter handling + * or calls to other helpers + * @_seqlock: pointer to seqlock_t protecting the data + * @_target: an enum ss_state: one of {ss_lock, ss_lock_irqsave, ss_lockless} + * indicating the type of critical read section + * + * Example:: * * scoped_seqlock_read (&lock, ss_lock) { * // read-side critical section From 1badb6866482d46634d7840447558943ddad6afb Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 14 Apr 2026 14:42:41 +0200 Subject: [PATCH 2220/3297] perf callchain: Handle multiple address spaces commit ae15db3e9b639491007cc1e9e99638e4b6091781 upstream. perf test 'perf inject to convert DWARF callchains to regular ones' fails on s390. It was introduced with commit 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") The failure comes the difference in output. Without the inject script to convert DWARF the callchains is: # perf record -F 999 --call-graph dwarf -- perf test -w noploop # perf report -i perf.data --stdio --no-children -q \ --percent-limit=1 > /tmp/111 # cat /tmp/111 99.30% perf-noploop perf [.] noploop | ---noploop run_workload (inlined) cmd_test run_builtin (inlined) handle_internal_command run_argv (inlined) main __libc_start_call_main __libc_start_main_impl (inlined) _start # With the inject script step the output is: # perf inject -i perf.data --convert-callchain -o /tmp/perf-inject-1.out # perf report -i /tmp/perf-inject-1.out --stdio --no-children -q \ --percent-limit=1 > /tmp/222 # cat /tmp/222 99.40% perf-noploop perf [.] noploop | ---noploop run_workload (inlined) cmd_test run_builtin (inlined) handle_internal_command run_argv (inlined) main _start # diff /tmp/111 /tmp/222 1c1 < 99.30% perf-noploop perf [.] noploop --- > 99.40% perf-noploop perf [.] noploop 10,11d9 < __libc_start_call_main < __libc_start_main_impl (inlined) # The difference are the symbols __libc_start_call_main and __libc_start_main_impl. On x86_64, kernel and user space share a single virtual address space, with the kernel mapped to the upper end of memory. The instruction pointer value alone is sufficient to distinguish between user space and kernel space addresses. This is not true for s390, which uses separate address spaces for user and kernel. The same virtual address can be valid in both address spaces, so the instruction pointer value alone cannot determine whether an address belongs to the kernel or user space. Instead, perf must rely on the cpumode metadata derived from the processor status word (PSW) at sample time. In function perf_event__convert_sample_callchain() the first part copies a kernel callchain and context entries, if any. It then appends additional entries ignoring the address space architecture. Taking that into account, the symbols at addresses 0x3ff970348cb __libc_start_call_main 0x3ff970349c5 __libc_start_main_impl (located after the kernel address space on s390) are now included. Output before: # perf test 83 83: perf inject to convert DWARF callchains to regular ones : FAILED! Output after: # perf test 83 83: perf inject to convert DWARF callchains to regular ones : Ok Question to Namhyung: In function perf_event__convert_sample_callchain() just before the for() loop this patch modifies, the kernel callchain is copied, see this comment and the next 5 lines: /* copy kernel callchain and context entries */ Then why is machine__kernel_ip() needed in the for() loop, when the kernel entries have been copied just before the loop? Note: This patch was tested on x86_64 virtual machine and succeeded. Fixes: 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") Signed-off-by: Thomas Richter Acked-by: Namhyung Kim Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Jan Polensky Cc: linux-s390@vger.kernel.org Cc: Sumanth Korikkar Cc: Vasily Gorbik Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/arch/common.c | 4 +++- tools/perf/builtin-inject.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 4908d54dd33b2..e9454df5235ec 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -237,5 +237,7 @@ int perf_env__lookup_objdump(struct perf_env *env, char **path) */ bool perf_env__single_address_space(struct perf_env *env) { - return strcmp(perf_env__arch(env), "sparc"); + const char *arch = perf_env__arch(env); + + return strcmp(arch, "s390") && strcmp(arch, "sparc"); } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 6859f4b183846..e8278999b12d5 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -452,7 +452,8 @@ static int perf_event__convert_sample_callchain(const struct perf_tool *tool, node = cursor->first; for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { - if (machine__kernel_ip(machine, node->ip)) + if (machine->single_address_space && + machine__kernel_ip(machine, node->ip)) /* kernel IPs were added already */; else if (node->ms.sym && node->ms.sym->inlined) /* we can't handle inlined callchains */; From 81e6faa5b64058258caab8406a297e207e356e66 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Thu, 28 May 2026 00:00:00 +0000 Subject: [PATCH 2221/3297] ksmbd: fix durable reconnect double-bind race in ksmbd_reopen_durable_fd commit 7ce4fc40018de07f05f3035241122d992610dbfb upstream. Two concurrent same-user DHnC reconnects can both observe fp->conn == NULL before either sets it. ksmbd_reopen_durable_fd() checks fp->conn to guard against a handle already being reconnected, but the check and the binding assignment are not atomic: both threads pass the guard, both call ksmbd_conn_get() on the same fp, and both eventually reach kfree(fp->owner.name) -- a double-free of the owner.name slab object. The double-bound ksmbd_file also causes a write-UAF on the 344-byte ksmbd_file_cache object when a concurrent smb2_close() spins on fp->f_lock after the object has been freed by the losing reconnect path. KASAN on 7.1-rc5 (48-thread concurrent reconnect, 3000 cycles): BUG: KASAN: double-free in ksmbd_reopen_durable_fd+0x268/0x308 BUG: KASAN: slab-use-after-free in _raw_spin_lock+0xac/0x150 Write of size 4 at offset 24 into freed ksmbd_file_cache object Five double-bind windows observed; 63 total KASAN reports triggered. Fix: validate and claim fp->conn under write_lock(&global_ft.lock) so the check-and-claim is atomic. ksmbd_lookup_durable_fd() already treats fp->conn != NULL as "in use" and skips such an fp; setting fp->conn before dropping the lock closes the race. ksmbd_conn_get() is a non-sleeping refcount increment, safe under the rwlock. The rollback path on __open_id() failure also clears fp->conn/tcon under the lock so concurrent readers see a consistent state. Fixes: b1f1e80620de ("ksmbd: centralize ksmbd_conn final release to plug transport leak") Assisted-by: Henry (Claude):claude-opus-4 Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/vfs_cache.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 25c216909cc4d..5b6d8bb8edb27 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1185,19 +1185,19 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) struct ksmbd_lock *smb_lock; unsigned int old_f_state; + write_lock(&global_ft.lock); if (!fp->is_durable || fp->conn || fp->tcon) { + write_unlock(&global_ft.lock); pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon); return -EBADF; } if (has_file_id(fp->volatile_id)) { + write_unlock(&global_ft.lock); pr_err("Still in use durable fd: %llu\n", fp->volatile_id); return -EBADF; } - old_f_state = fp->f_state; - fp->f_state = FP_NEW; - /* * Initialize fp's connection binding before publishing fp into the * session's file table. If __open_id() is ordered first, a @@ -1208,11 +1208,17 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) */ fp->conn = ksmbd_conn_get(conn); fp->tcon = work->tcon; + write_unlock(&global_ft.lock); + + old_f_state = fp->f_state; + fp->f_state = FP_NEW; __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); if (!has_file_id(fp->volatile_id)) { + write_lock(&global_ft.lock); fp->conn = NULL; fp->tcon = NULL; + write_unlock(&global_ft.lock); ksmbd_conn_put(conn); fp->f_state = old_f_state; return -EBADF; From b87cbd4d198ae377be4815d8102fa9dfefb91dcc Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:37 -0700 Subject: [PATCH 2222/3297] RDMA/bnxt_re: Initialize dpi variable to zero commit 978b27d6ce538bb832ccd69e45802824e4301c4b upstream. dpi is initialized only for BNXT_RE_ALLOC_WC_PAGE, but copied for all the cases. So initialize the dpi to 0. Fixes: eee6268421a2 ("RDMA/bnxt_re: Move the UAPI methods to a dedicated file") Fixes: 360da60d6c6e ("RDMA/bnxt_re: Enable low latency push") Link: https://patch.msgid.link/r/20260615224751.232802-2-selvin.xavier@broadcom.com Reviewed-by: Anantha Prabhu Signed-off-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/bnxt_re/uapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index d5e29d95a6385..d93cafc5dfa0d 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -76,8 +76,8 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * struct ib_ucontext *ib_uctx; struct bnxt_re_dev *rdev; u64 mmap_offset; + u32 dpi = 0; u32 length; - u32 dpi; u64 addr; int err; From 478c4d24193fe3e6aa2accd4874ae43000e4a217 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Mon, 15 Jun 2026 15:47:47 -0700 Subject: [PATCH 2223/3297] RDMA/bnxt_re: Avoid repeated requests to allocate WC pages commit 441baa79043431807115fd030d7d0bb14ed441a0 upstream. Applications can request multiple WC pages for the same ucontext. As of now, only 1 WC page per ucontext is supported. Add a lock to avoid concurrent access and a check to fail repeated requests. Also, if the mmap entry insert fails for the WC, free the Doorbell page index mapped for the WC page. Fixes: eee6268421a2 ("RDMA/bnxt_re: Move the UAPI methods to a dedicated file") Fixes: 360da60d6c6e ("RDMA/bnxt_re: Enable low latency push") Link: https://patch.msgid.link/r/20260615224751.232802-12-selvin.xavier@broadcom.com Reviewed-by: Kalesh AP Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 1 + drivers/infiniband/hw/bnxt_re/ib_verbs.h | 1 + drivers/infiniband/hw/bnxt_re/uapi.c | 33 +++++++++++++++++++----- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 8f380e9e41a8c..25d88c2c5735f 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -4356,6 +4356,7 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) goto fail; } spin_lock_init(&uctx->sh_lock); + mutex_init(&uctx->wcdpi_lock); resp.comp_mask = BNXT_RE_UCNTX_CMASK_HAVE_CCTX; chip_met_rev_num = rdev->chip_ctx->chip_num; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index a11f56730a31c..02b9c2e2d82b5 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -142,6 +142,7 @@ struct bnxt_re_ucontext { struct bnxt_re_dev *rdev; struct bnxt_qplib_dpi dpi; struct bnxt_qplib_dpi wcdpi; + struct mutex wcdpi_lock; /* serialises WC DPI alloc/free */ void *shpg; spinlock_t sh_lock; /* protect shpg */ struct rdma_user_mmap_entry *shpage_mmap; diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index d93cafc5dfa0d..ceec3fa0928ca 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -98,14 +98,23 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * switch (alloc_type) { case BNXT_RE_ALLOC_WC_PAGE: - if (cctx->modes.db_push) { + if (cctx->modes.db_push) { + mutex_lock(&uctx->wcdpi_lock); + /* already allocated — one WC page per context */ + if (uctx->wcdpi.dbr) { + mutex_unlock(&uctx->wcdpi_lock); + return -EEXIST; + } if (bnxt_qplib_alloc_dpi(&rdev->qplib_res, &uctx->wcdpi, - uctx, BNXT_QPLIB_DPI_TYPE_WC)) + uctx, BNXT_QPLIB_DPI_TYPE_WC)) { + mutex_unlock(&uctx->wcdpi_lock); return -ENOMEM; + } length = PAGE_SIZE; dpi = uctx->wcdpi.dpi; addr = (u64)uctx->wcdpi.umdbr; mmap_flag = BNXT_RE_MMAP_WC_DB; + mutex_unlock(&uctx->wcdpi_lock); } else { return -EINVAL; } @@ -132,8 +141,15 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * } entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); - if (!entry) + if (!entry) { + if (mmap_flag == BNXT_RE_MMAP_WC_DB) { + mutex_lock(&uctx->wcdpi_lock); + bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); + uctx->wcdpi.dbr = NULL; + mutex_unlock(&uctx->wcdpi_lock); + } return -ENOMEM; + } uobj->object = entry; uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); @@ -164,11 +180,16 @@ static int alloc_page_obj_cleanup(struct ib_uobject *uobject, switch (entry->mmap_flag) { case BNXT_RE_MMAP_WC_DB: - if (uctx && uctx->wcdpi.dbr) { + if (uctx) { struct bnxt_re_dev *rdev = uctx->rdev; - bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); - uctx->wcdpi.dbr = NULL; + mutex_lock(&uctx->wcdpi_lock); + if (uctx->wcdpi.dbr) { + bnxt_qplib_dealloc_dpi(&rdev->qplib_res, + &uctx->wcdpi); + uctx->wcdpi.dbr = NULL; + } + mutex_unlock(&uctx->wcdpi_lock); } break; case BNXT_RE_MMAP_DBR_BAR: From 221fc2f4d0eda59d02af2e751a9282fa013a8e97 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2026 16:17:26 +0200 Subject: [PATCH 2224/3297] Linux 6.18.40 Link: https://lore.kernel.org/r/20260721152514.750365251@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Peter Schneider Tested-by: Wentao Guan Tested-by: Ron Economos Tested-by: Shuah Khan Tested-by: Pavel Machek (CIP) Link: https://lore.kernel.org/r/20260722142910.893804136@linuxfoundation.org Tested-by: Brett A C Sheffield Tested-by: Pavel Machek (CIP) Tested-by: Florian Fainelli Tested-by: Peter Schneider Tested-by: Wentao Guan 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 f4eef2d694db7..aa1a53c513b41 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 39 +SUBLEVEL = 40 EXTRAVERSION = NAME = Baby Opossum Posse From 9f7268928ac0c5306a05d60c02c551c4b7a27a6f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 8 Apr 2026 13:53:56 +0200 Subject: [PATCH 2225/3297] posix-timers: Expand timer_[re]arm() callbacks with a boolean return value commit 6fdb2677a594ab38eade927919bbd4d9688bfa1c upstream. In order to catch expiry times which are already in the past the timer_arm() and timer_rearm() callbacks need to be able to report back to the caller whether the timer has been queued or not. Change the function signature and let all implementations return true for now. While at it simplify posix_cpu_timer_rearm(). No functional change intended. Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Acked-by: Peter Zijlstra (Intel) Acked-by: John Stultz Link: https://patch.msgid.link/20260408114952.130222296@kernel.org Signed-off-by: Salvatore Bonaccorso Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- kernel/time/alarmtimer.c | 6 ++++-- kernel/time/posix-cpu-timers.c | 18 ++++++++++-------- kernel/time/posix-timers.c | 6 ++++-- kernel/time/posix-timers.h | 4 ++-- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 189ee8bbc3e65..525df334b15a7 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -521,12 +521,13 @@ static void alarm_handle_timer(struct alarm *alarm, ktime_t now) * alarm_timer_rearm - Posix timer callback for rearming timer * @timr: Pointer to the posixtimer data struct */ -static void alarm_timer_rearm(struct k_itimer *timr) +static bool alarm_timer_rearm(struct k_itimer *timr) { struct alarm *alarm = &timr->it.alarm.alarmtimer; timr->it_overrun += alarm_forward_now(alarm, timr->it_interval); alarm_start(alarm, alarm->node.expires); + return true; } /** @@ -582,7 +583,7 @@ static void alarm_timer_wait_running(struct k_itimer *timr) * @absolute: Expiry value is absolute time * @sigev_none: Posix timer does not deliver signals */ -static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires, +static bool alarm_timer_arm(struct k_itimer *timr, ktime_t expires, bool absolute, bool sigev_none) { struct alarm *alarm = &timr->it.alarm.alarmtimer; @@ -594,6 +595,7 @@ static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires, alarm->node.expires = expires; else alarm_start(&timr->it.alarm.alarmtimer, expires); + return true; } /** diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 99affd9e228ec..d14523fbc146c 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -19,7 +19,7 @@ #include "posix-timers.h" -static void posix_cpu_timer_rearm(struct k_itimer *timer); +static bool posix_cpu_timer_rearm(struct k_itimer *timer); void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit) { @@ -1011,24 +1011,27 @@ static void check_process_timers(struct task_struct *tsk, /* * This is called from the signal code (via posixtimer_rearm) * when the last timer signal was delivered and we have to reload the timer. + * + * Return true unconditionally so the core code assumes the timer to be + * armed. Otherwise it would requeue the signal. */ -static void posix_cpu_timer_rearm(struct k_itimer *timer) +static bool posix_cpu_timer_rearm(struct k_itimer *timer) { clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); - struct task_struct *p; struct sighand_struct *sighand; + struct task_struct *p; unsigned long flags; u64 now; - rcu_read_lock(); + guard(rcu)(); p = cpu_timer_task_rcu(timer); if (!p) - goto out; + return true; /* Protect timer list r/w in arm_timer() */ sighand = lock_task_sighand(p, &flags); if (unlikely(sighand == NULL)) - goto out; + return true; /* * Fetch the current sample and update the timer's expiry time. @@ -1045,8 +1048,7 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer) */ arm_timer(timer, p); unlock_task_sighand(p, &flags); -out: - rcu_read_unlock(); + return true; } /** diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 56e17b625c72e..3082129d836c2 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -295,12 +295,13 @@ static inline int timer_overrun_to_int(struct k_itimer *timr) return (int)timr->it_overrun_last; } -static void common_hrtimer_rearm(struct k_itimer *timr) +static bool common_hrtimer_rearm(struct k_itimer *timr) { struct hrtimer *timer = &timr->it.real.timer; timr->it_overrun += hrtimer_forward_now(timer, timr->it_interval); hrtimer_restart(timer); + return true; } static bool __posixtimer_deliver_signal(struct kernel_siginfo *info, struct k_itimer *timr) @@ -802,7 +803,7 @@ SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id) return timer_overrun_to_int(scoped_timer); } -static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, +static bool common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, bool absolute, bool sigev_none) { struct hrtimer *timer = &timr->it.real.timer; @@ -829,6 +830,7 @@ static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires, if (!sigev_none) hrtimer_start_expires(timer, HRTIMER_MODE_ABS); + return true; } static int common_hrtimer_try_to_cancel(struct k_itimer *timr) diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h index 7f259e845d240..4ea9611dd7165 100644 --- a/kernel/time/posix-timers.h +++ b/kernel/time/posix-timers.h @@ -27,11 +27,11 @@ struct k_clock { int (*timer_del)(struct k_itimer *timr); void (*timer_get)(struct k_itimer *timr, struct itimerspec64 *cur_setting); - void (*timer_rearm)(struct k_itimer *timr); + bool (*timer_rearm)(struct k_itimer *timr); s64 (*timer_forward)(struct k_itimer *timr, ktime_t now); ktime_t (*timer_remaining)(struct k_itimer *timr, ktime_t now); int (*timer_try_to_cancel)(struct k_itimer *timr); - void (*timer_arm)(struct k_itimer *timr, ktime_t expires, + bool (*timer_arm)(struct k_itimer *timr, ktime_t expires, bool absolute, bool sigev_none); void (*timer_wait_running)(struct k_itimer *timr); }; From 6a7ecc25abe6f0fecc6e62a05096987200edbd02 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 3 Jul 2026 12:02:38 +0200 Subject: [PATCH 2226/3297] posix-cpu-timers: Prevent UAF caused by non-leader exec() race commit 920f893f735e92ba3a1cd9256899a186b161928d upstream. Wongi and Jungwoo decoded and reported a non-leader exec() related race which can result in an UAF: sys_timer_delete() exec() posix_cpu_timer_del() // Observes old leader p = pid_task(pid, pid_type); de_thread() switch_leader(); release_task(old_leader) __exit_signal(old_leader) sighand = lock(old_leader, sighand); posix_cpu_timers*_exit(); sighand = lock_task_sighand(p) unhash_task(old_leader); sh = lock(p, sighand) old_leader->sighand = NULL; unlock(sighand); (p->sighand == NULL) unlock(sh) return NULL; // Returns without action if(!sighand) return 0; free_posix_timer(); This is "harmless" unless the deleted timer was armed and enqueued in p->signal because on exec() a TGID targeted timer is inherited. As sys_timer_delete() freed the underlying posix timer object run_posix_cpu_timers() or any timerqueue related add/delete operations on other timers will access the freed object's timerqueue node, which results in an UAF. There is a similar problem vs. posix_cpu_timer_set(). For regular posix timers it just transiently returns -ESRCH to user space, but for the use case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is allocated on the stack. Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops to expire. While debating solutions Frederic pointed out another problem: posix_cpu_timer_del(tmr) __exit_signal(p) posix_cpu_timers*_exit(p); unhash_task(p); p->sighand = NULL; sh = lock_task_sighand(p) sighand = p->sighand; if (!sighand) return NULL; lock(sighand); if (!sh) WARN_ON_ONCE(timer_queued(tmr)); On weakly ordered architectures it is not guaranteed that posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit() when p->sighand is observed as NULL, which means the WARN() can be a false positive. Solve these issues by: 1) Changing the store in __exit_signal() to smp_store_release(). 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path of lock_task_sighand(). 3) Creating a helper function for looking up the task and locking sighand which does not return when sighand == NULL. Instead it retries the task lookup and only if that fails it gives up. 4) Using that helper in the three affected functions. observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit() and the ones in unhash_task(). gracefully. When the task lookup returns the old leader, but sighand == NULL then it retries. In the non-leader exec() case the subsequent task lookup will observe the new leader due to #1/#2. In normal exit() scenarios the subsequent lookup fails. When the task lookup fails, the function also checks whether the timer is still enqueued and issues a warning if that's the case. Unfortunately there is nothing which can be done about it, but as the task is already not longer visible the timer should not be accessed anymore. This check also requires memory ordering, which is not provided when the first lookup fails. To achieve that the check is preceeded by a smp_rmb() which pairs with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that the stores in posix_cpu_timers*_exit() are visible. The history of the non-leader exec() issue goes back to the early days of posix CPU timers, which stored a pointer to the group leader task in the timer. That obviously fails when a non-leader exec() switches the leader. commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems with mt exec") added a temporary workaround for that in 2010 which survived about 10 years. The fix for the workaround changed the task pointer to a pid pointer, but failed to see the subtle race described above. So the Fixes tag picks that commit, which seems to be halfways accurate. Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for review, feedback and suggestions and to Wongi and Jungwoo for the excellent bug report and analysis! Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task") Reported-by: Wongi Lee Reported-by: Jungwoo Lee Signed-off-by: Thomas Gleixner Reviewed-by: Oleg Nesterov Cc: stable@vger.kernel.org Signed-off-by: Salvatore Bonaccorso Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- kernel/exit.c | 7 +- kernel/signal.c | 10 +- kernel/time/posix-cpu-timers.c | 173 ++++++++++++++++++++++----------- 3 files changed, 130 insertions(+), 60 deletions(-) diff --git a/kernel/exit.c b/kernel/exit.c index c832946823f48..a32bc65569a50 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -212,7 +212,12 @@ static void __exit_signal(struct release_task_post *post, struct task_struct *ts __unhash_process(post, tsk, group_dead); write_sequnlock(&sig->stats_lock); - tsk->sighand = NULL; + /* + * Ensure that all preceeding state is visible. Pairs with + * the smp_acquire__after_ctrl_dep() in the sighand == NULL + * path of lock_task_sighand(). + */ + smp_store_release(&tsk->sighand, NULL); spin_unlock(&sighand->siglock); __cleanup_sighand(sighand); diff --git a/kernel/signal.c b/kernel/signal.c index 810098300ecdc..e1843b71efff1 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1364,8 +1364,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk, rcu_read_lock(); for (;;) { sighand = rcu_dereference(tsk->sighand); - if (unlikely(sighand == NULL)) + if (unlikely(sighand == NULL)) { + /* + * Pairs with the smp_store_release() in + * __exit_signal(). It ensures that all state + * modifications to the task preceeding the store are + * visible to the callers of lock_task_sighand(). + */ + smp_acquire__after_ctrl_dep(); break; + } /* * This sighand can be already freed and even reused, but diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index d14523fbc146c..2d64972fd0804 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -461,6 +461,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) trigger_base_recalc_expires(timer, p); } +/* + * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand. + * + * This can race with the reaping of the task: + * + * CPU0 CPU1 + * + * // Finds task + * p = pid_task(pid, pid_type); __exit_signal(p) + * lock(p, sighand); + * posix_cpu_timers*_exit(); + * sighand = lock_task_sighand(p); unhash_task(p); + * p->sighand = NULL; + * unlock(sighand); + * + * In this case sighand is NULL, which means the task and the associated timer + * queue cannot be longer accessed safely. + * + * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is + * dead it also invokes posix_cpu_timers_group_exit(). These functions delete + * all pending timers from the related timer queues. The POSIX timers (k_itimer) + * themself are still accessible, but not longer connected to the task. + * + * exec() works slightly differently. The task which exec()'s terminates all + * other threads in the thread group and runs __exit_signal() on them. As the + * thread group is not dead they only clean up the per task timers via + * posix_cpu_timers_exit(). + * + * As the TGID on exec() stays the same per process timers stay queued, if they + * are armed. This works without a problem when exec() is done by the thread + * group leader. If a non-leader thread exec()'s this can end up in the + * following scenario: + * + * CPU0 CPU1 + * // Returns old leader + * p = pid_task(pid, pid_type); de_thread() + * switch_leader() + * release_task(old leader) + * __exit_signal() + * old_leader->sighand = NULL; + * // Returns NULL + * sighand = lock_task_sighand(p) + * + * That's problematic for several functions: + * + * - posix_cpu_timer_del(): If the timer is still enqueued on the task the + * underlying k_itimer will be freed which results in a UAF in + * run_posix_cpu_timers() or on timerqueue related add/delete operations. + * If the timer is not enqueued, the failure is harmless + * + * - posix_cpu_timer_set(): Independent of the enqueued state that results in a + * transient failure which is user space visible (-ESRCH) for regular posix + * timers. But for the use case in do_cpu_nanosleep() it's the same UAF + * problem just that the timer is allocated on the stack. + * + * - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this + * silently ignores the rearm request, which is a functional problem as the + * timer wont expire anymore. + */ +static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags) +{ + enum pid_type type = clock_pid_type(timer->it_clock); + struct cpu_timer *ctmr = &timer->it.cpu; + + guard(rcu)(); + + for (;;) { + struct task_struct *t = pid_task(timer->it.cpu.pid, type); + + /* Fail if the task cannot be found. */ + if (!t) + break; + + /* Try to lock the task's sighand */ + if (lock_task_sighand(t, flags)) + return t; + + /* + * The next PID lookup might either fail or return the new + * leader. This is correct for both exit() and exec(). + */ + } + + /* + * If the timer is still enqueued, warn. There is nothing safe to do + * here as there might be two timers in there which are removed in + * parallel and that will cause more damage than good. This should never + * happen! + * + * Ensure that the stores to the timer and timerqueue are visible: + * + * __exit_signal() + * posix_cpu_timers*_exit() + * write_seqlock(seqlock) + * smp_wmb(); <------- + * __unhash_process() | !pid_task() + * ----> smp_rmb(); + * WARN_ON_ONCE(...) + */ + smp_rmb(); + WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); + return NULL; +} /* * Clean up a CPU-clock timer that is about to be destroyed. @@ -470,29 +573,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p) */ static int posix_cpu_timer_del(struct k_itimer *timer) { - struct cpu_timer *ctmr = &timer->it.cpu; - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; int ret = 0; - rcu_read_lock(); - p = cpu_timer_task_rcu(timer); - if (!p) - goto out; + p = timer_lock_sighand(timer, &flags); - /* - * Protect against sighand release/switch in exit/exec and process/ - * thread timer list entry concurrent read/writes. - */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) { - /* - * This raced with the reaping of the task. The exit cleanup - * should have removed this timer from the timer queue. - */ - WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node)); - } else { + if (likely(p)) { if (timer->it.cpu.firing) { /* * Prevent signal delivery. The timer cannot be dequeued @@ -508,11 +595,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer) unlock_task_sighand(p, &flags); } -out: - rcu_read_unlock(); - if (!ret) { - put_pid(ctmr->pid); + put_pid(timer->it.cpu.pid); timer->it_status = POSIX_TIMER_DISARMED; } return ret; @@ -626,21 +710,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); struct cpu_timer *ctmr = &timer->it.cpu; u64 old_expires, new_expires, now; - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; int ret = 0; - rcu_read_lock(); - p = cpu_timer_task_rcu(timer); - if (!p) { - /* - * If p has just been reaped, we can no - * longer get any information about it at all. - */ - rcu_read_unlock(); + p = timer_lock_sighand(timer, &flags); + /* + * If p has just been reaped, we can no longer get any information about + * it at all. + */ + if (!p) return -ESRCH; - } /* * Use the to_ktime conversion because that clamps the maximum @@ -648,20 +728,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, */ new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value)); - /* - * Protect against sighand release/switch in exit/exec and p->cpu_timers - * and p->signal->cpu_timers read/write in arm_timer() - */ - sighand = lock_task_sighand(p, &flags); - /* - * If p has just been reaped, we can no - * longer get any information about it at all. - */ - if (unlikely(sighand == NULL)) { - rcu_read_unlock(); - return -ESRCH; - } - /* Retrieve the current expiry time before disarming the timer */ old_expires = cpu_timer_getexpires(ctmr); @@ -698,7 +764,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, /* Retry if the timer expiry is running concurrently */ if (unlikely(ret)) { unlock_task_sighand(p, &flags); - goto out; + return ret; } /* Convert relative expiry time to absolute */ @@ -733,8 +799,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, */ if (!sigev_none && new_expires && now >= new_expires) cpu_timer_fire(timer); -out: - rcu_read_unlock(); return ret; } @@ -1018,19 +1082,12 @@ static void check_process_timers(struct task_struct *tsk, static bool posix_cpu_timer_rearm(struct k_itimer *timer) { clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); - struct sighand_struct *sighand; struct task_struct *p; unsigned long flags; u64 now; - guard(rcu)(); - p = cpu_timer_task_rcu(timer); - if (!p) - return true; - - /* Protect timer list r/w in arm_timer() */ - sighand = lock_task_sighand(p, &flags); - if (unlikely(sighand == NULL)) + p = timer_lock_sighand(timer, &flags); + if (unlikely(!p)) return true; /* From 2fe596715f840d053aed5cee5455f701bdcd2b50 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Jul 2026 13:05:57 +0200 Subject: [PATCH 2227/3297] Linux 6.18.41 Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index aa1a53c513b41..f3c450a0a2fbb 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 40 +SUBLEVEL = 41 EXTRAVERSION = NAME = Baby Opossum Posse From bffc0b27e457ce5d958379fbf11f00e3f5be4773 Mon Sep 17 00:00:00 2001 From: Guixiong Wei Date: Tue, 21 Jul 2026 23:07:57 +0800 Subject: [PATCH 2228/3297] platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6b63520ed14b17bbe9c2103debbd2152dde1fba3 upstream. When the last CPU of a legacy uncore die goes offline, uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug re-add, uncore_freq_add_entry() still populates sysfs attributes before assigning the new control CPU. As a result, the current frequency read returns -ENXIO and current_freq_khz is omitted from the recreated sysfs group. Assign control_cpu before the initial read paths and before create_attr_group() so sysfs recreation uses the new online CPU. If sysfs creation fails, restore control_cpu to -1 to keep the error path state consistent. Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") Cc: stable@vger.kernel.org Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Guixiong Wei Signed-off-by: Sasha Levin --- .../x86/intel/uncore-frequency/uncore-frequency-common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 65897fae17dfb..6091b617480ff 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -274,15 +274,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id); } + /* + * Set the control CPU before any read path so entry recreation after CPU + * hotplug can populate read-only attributes from the new online CPU. + */ + data->control_cpu = cpu; uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); ret = create_attr_group(data, data->name); if (ret) { + data->control_cpu = -1; if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) ida_free(&intel_uncore_ida, data->instance_id); } else { - data->control_cpu = cpu; data->valid = true; } From ce01a4e5cfac7adbe0be565f90cd32ecbb2f8337 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 22 Jul 2026 15:39:07 +0800 Subject: [PATCH 2229/3297] bpf: Fix ld_{abs,ind} failure path analysis in subprogs commit ee861486e377edc55361c08dcbceab3f6b6577bd upstream. Usage of ld_{abs,ind} instructions got extended into subprogs some time ago via commit 09b28d76eac4 ("bpf: Add abnormal return checks."). These are only allowed in subprograms when the latter are BTF annotated and have scalar return types. The code generator in bpf_gen_ld_abs() has an abnormal exit path (r0=0 + exit) from legacy cBPF times. While the enforcement is on scalar return types, the verifier must also simulate the path of abnormal exit if the packet data load via ld_{abs,ind} failed. This is currently not the case. Fix it by having the verifier simulate both success and failure paths, and extend it in similar ways as we do for tail calls. The success path (r0=unknown, continue to next insn) is pushed onto stack for later validation and the r0=0 and return to the caller is done on the fall-through side. Fixes: 09b28d76eac4 ("bpf: Add abnormal return checks.") Reported-by: STAR Labs SG Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260408191242.526279-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov [ Dropped visit_abnormal_return_insn changes: depends on 7.0 symbols from e40f5a6bf88a ("bpf: correct stack liveness for tail calls"); Hunk1: adapted IS_ERR/PTR_ERR to !branch/-EFAULT to match push_stack() NULL-on-failure convention. ] Signed-off-by: Philo Lu Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d1c51d812f3b6..463455180a8e3 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -17402,6 +17402,23 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) mark_reg_unknown(env, regs, BPF_REG_0); /* ld_abs load up to 32-bit skb data. */ regs[BPF_REG_0].subreg_def = env->insn_idx + 1; + /* + * See bpf_gen_ld_abs() which emits a hidden BPF_EXIT with r0=0 + * which must be explored by the verifier when in a subprog. + */ + if (env->cur_state->curframe) { + struct bpf_verifier_state *branch; + + mark_reg_scratched(env, BPF_REG_0); + branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false); + if (!branch) + return -EFAULT; + mark_reg_known_zero(env, regs, BPF_REG_0); + err = prepare_func_exit(env, &env->insn_idx); + if (err) + return err; + env->insn_idx--; + } return 0; } From 55904f1a4689afd31902addd868ae25456be1891 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 22 Jul 2026 15:39:08 +0800 Subject: [PATCH 2230/3297] selftests/bpf: Add tests for ld_{abs,ind} failure path in subprogs commit e0fcb42bc6f41bab2895757d6610616b3820eff7 upstream. Extend the verifier_ld_ind BPF selftests with subprogs containing ld_{abs,ind} and craft the test in a way where the invalid register read is rejected in the fixed case. Also add a success case each, and add additional coverage related to the BTF return type enforcement. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_ld_ind [...] #611/1 verifier_ld_ind/ld_ind: check calling conv, r1:OK #611/2 verifier_ld_ind/ld_ind: check calling conv, r1 @unpriv:OK #611/3 verifier_ld_ind/ld_ind: check calling conv, r2:OK #611/4 verifier_ld_ind/ld_ind: check calling conv, r2 @unpriv:OK #611/5 verifier_ld_ind/ld_ind: check calling conv, r3:OK #611/6 verifier_ld_ind/ld_ind: check calling conv, r3 @unpriv:OK #611/7 verifier_ld_ind/ld_ind: check calling conv, r4:OK #611/8 verifier_ld_ind/ld_ind: check calling conv, r4 @unpriv:OK #611/9 verifier_ld_ind/ld_ind: check calling conv, r5:OK #611/10 verifier_ld_ind/ld_ind: check calling conv, r5 @unpriv:OK #611/11 verifier_ld_ind/ld_ind: check calling conv, r7:OK #611/12 verifier_ld_ind/ld_ind: check calling conv, r7 @unpriv:OK #611/13 verifier_ld_ind/ld_abs: subprog early exit on ld_abs failure:OK #611/14 verifier_ld_ind/ld_ind: subprog early exit on ld_ind failure:OK #611/15 verifier_ld_ind/ld_abs: subprog with both paths safe:OK #611/16 verifier_ld_ind/ld_ind: subprog with both paths safe:OK #611/17 verifier_ld_ind/ld_abs: reject void return subprog:OK #611/18 verifier_ld_ind/ld_ind: reject void return subprog:OK #611 verifier_ld_ind:OK Summary: 1/18 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260408191242.526279-4-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Philo Lu Signed-off-by: Sasha Levin --- .../selftests/bpf/progs/verifier_ld_ind.c | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c index c925ba9a2e74c..09e81b99eecb4 100644 --- a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c +++ b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c @@ -107,4 +107,146 @@ __naked void ind_check_calling_conv_r7(void) : __clobber_all); } +/* + * ld_{abs,ind} subprog that always sets r0=1 on the success path. + * bpf_gen_ld_abs() emits a hidden exit with r0=0 when the load helper + * fails. The verifier must model this failure return so that callers + * account for r0=0 as a possible return value. + */ +__naked __noinline __used +static int ldabs_subprog(void) +{ + asm volatile ( + "r6 = r1;" + ".8byte %[ld_abs];" + "r0 = 1;" + "exit;" + : + : __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0)) + : __clobber_all); +} + +__naked __noinline __used +static int ldind_subprog(void) +{ + asm volatile ( + "r6 = r1;" + "r7 = 0;" + ".8byte %[ld_ind];" + "r0 = 1;" + "exit;" + : + : __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0)) + : __clobber_all); +} + +SEC("socket") +__description("ld_abs: subprog early exit on ld_abs failure") +__failure __msg("R9 !read_ok") +__naked void ld_abs_subprog_early_exit(void) +{ + asm volatile ( + "call ldabs_subprog;" + "if r0 != 0 goto l_exit_%=;" + "r0 = r9;" + "l_exit_%=:" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + +SEC("socket") +__description("ld_ind: subprog early exit on ld_ind failure") +__failure __msg("R9 !read_ok") +__naked void ld_ind_subprog_early_exit(void) +{ + asm volatile ( + "call ldind_subprog;" + "if r0 != 0 goto l_exit_%=;" + "r0 = r9;" + "l_exit_%=:" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + +SEC("socket") +__description("ld_abs: subprog with both paths safe") +__success +__naked void ld_abs_subprog_both_paths_safe(void) +{ + asm volatile ( + "call ldabs_subprog;" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + +SEC("socket") +__description("ld_ind: subprog with both paths safe") +__success +__naked void ld_ind_subprog_both_paths_safe(void) +{ + asm volatile ( + "call ldind_subprog;" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + +/* + * ld_{abs,ind} in subprogs require scalar (int) return type in BTF. + * A test with void return must be rejected. + */ +__naked __noinline __used +static void ldabs_void_subprog(void) +{ + asm volatile ( + "r6 = r1;" + ".8byte %[ld_abs];" + "r0 = 1;" + "exit;" + : + : __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0)) + : __clobber_all); +} + +SEC("socket") +__description("ld_abs: reject void return subprog") +__failure __msg("LD_ABS is only allowed in functions that return 'int'") +__naked void ld_abs_void_subprog_reject(void) +{ + asm volatile ( + "call ldabs_void_subprog;" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + +__naked __noinline __used +static void ldind_void_subprog(void) +{ + asm volatile ( + "r6 = r1;" + "r7 = 0;" + ".8byte %[ld_ind];" + "r0 = 1;" + "exit;" + : + : __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0)) + : __clobber_all); +} + +SEC("socket") +__description("ld_ind: reject void return subprog") +__failure __msg("LD_ABS is only allowed in functions that return 'int'") +__naked void ld_ind_void_subprog_reject(void) +{ + asm volatile ( + "call ldind_void_subprog;" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; From cd968dcdec6aee79a2d399e4f6e0eca63c3b45e1 Mon Sep 17 00:00:00 2001 From: Brian Witte Date: Wed, 4 Feb 2026 14:26:37 -0600 Subject: [PATCH 2231/3297] netfilter: nft_counter: serialize reset with spinlock [ Upstream commit 779c60a5190c42689534172f4b49e927c9959e4e ] Add a global static spinlock to serialize counter fetch+reset operations, preventing concurrent dump-and-reset from underrunning values. The lock is taken before fetching the total so that two parallel resets cannot both read the same counter values and then both subtract them. A global lock is used for simplicity since resets are infrequent. If this becomes a bottleneck, it can be replaced with a per-net lock later. Fixes: bd662c4218f9 ("netfilter: nf_tables: Add locking for NFT_MSG_GETOBJ_RESET requests") Fixes: 3d483faa6663 ("netfilter: nf_tables: Add locking for NFT_MSG_GETSETELEM_RESET requests") Fixes: 3cb03edb4de3 ("netfilter: nf_tables: Add locking for NFT_MSG_GETRULE_RESET requests") Suggested-by: Florian Westphal Signed-off-by: Brian Witte Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nft_counter.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index 0d70325280cc5..169ae93688bcc 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -32,6 +32,9 @@ struct nft_counter_percpu_priv { static DEFINE_PER_CPU(struct u64_stats_sync, nft_counter_sync); +/* control plane only: sync fetch+reset */ +static DEFINE_SPINLOCK(nft_counter_lock); + static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -148,13 +151,25 @@ static void nft_counter_fetch(struct nft_counter_percpu_priv *priv, } } +static void nft_counter_fetch_and_reset(struct nft_counter_percpu_priv *priv, + struct nft_counter_tot *total) +{ + spin_lock(&nft_counter_lock); + nft_counter_fetch(priv, total); + nft_counter_reset(priv, total); + spin_unlock(&nft_counter_lock); +} + static int nft_counter_do_dump(struct sk_buff *skb, struct nft_counter_percpu_priv *priv, bool reset) { struct nft_counter_tot total; - nft_counter_fetch(priv, &total); + if (unlikely(reset)) + nft_counter_fetch_and_reset(priv, &total); + else + nft_counter_fetch(priv, &total); if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes), NFTA_COUNTER_PAD) || @@ -162,9 +177,6 @@ static int nft_counter_do_dump(struct sk_buff *skb, NFTA_COUNTER_PAD)) goto nla_put_failure; - if (reset) - nft_counter_reset(priv, &total); - return 0; nla_put_failure: From 0c8a9022f4c56066f13f8285d562fceeb76325be Mon Sep 17 00:00:00 2001 From: Brian Witte Date: Wed, 4 Feb 2026 14:26:38 -0600 Subject: [PATCH 2232/3297] netfilter: nft_quota: use atomic64_xchg for reset [ Upstream commit 30c4d7fb59ac4c8d7fa7937df11eed10b368fa11 ] Use atomic64_xchg() to atomically read and zero the consumed value on reset, which is simpler than the previous read+sub pattern and doesn't require lock serialization. Fixes: bd662c4218f9 ("netfilter: nf_tables: Add locking for NFT_MSG_GETOBJ_RESET requests") Fixes: 3d483faa6663 ("netfilter: nf_tables: Add locking for NFT_MSG_GETSETELEM_RESET requests") Fixes: 3cb03edb4de3 ("netfilter: nf_tables: Add locking for NFT_MSG_GETRULE_RESET requests") Suggested-by: Pablo Neira Ayuso Signed-off-by: Brian Witte Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nft_quota.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index df0798da2329b..cb6c0e04ff675 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -140,11 +140,16 @@ static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv, u64 consumed, consumed_cap, quota; u32 flags = priv->flags; - /* Since we inconditionally increment consumed quota for each packet + /* Since we unconditionally increment consumed quota for each packet * that we see, don't go over the quota boundary in what we send to * userspace. */ - consumed = atomic64_read(priv->consumed); + if (reset) { + consumed = atomic64_xchg(priv->consumed, 0); + clear_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags); + } else { + consumed = atomic64_read(priv->consumed); + } quota = atomic64_read(&priv->quota); if (consumed >= quota) { consumed_cap = quota; @@ -160,10 +165,6 @@ static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv, nla_put_be32(skb, NFTA_QUOTA_FLAGS, htonl(flags))) goto nla_put_failure; - if (reset) { - atomic64_sub(consumed, priv->consumed); - clear_bit(NFT_QUOTA_DEPLETED_BIT, &priv->flags); - } return 0; nla_put_failure: From f6410d18c1e2da325df02be989d5bca5ed38b086 Mon Sep 17 00:00:00 2001 From: Brian Witte Date: Wed, 4 Feb 2026 14:26:36 -0600 Subject: [PATCH 2233/3297] netfilter: nf_tables: revert commit_mutex usage in reset path [ Upstream commit 7f261bb906bf527c4a6e2a646e2d5f3679f2a8bc ] It causes circular lock dependency between commit_mutex, nfnl_subsys_ipset and nlk_cb_mutex when nft reset, ipset list, and iptables-nft with '-m set' rule run at the same time. Previous patches made it safe to run individual reset handlers concurrently so commit_mutex is no longer required to prevent this. Fixes: bd662c4218f9 ("netfilter: nf_tables: Add locking for NFT_MSG_GETOBJ_RESET requests") Fixes: 3d483faa6663 ("netfilter: nf_tables: Add locking for NFT_MSG_GETSETELEM_RESET requests") Fixes: 3cb03edb4de3 ("netfilter: nf_tables: Add locking for NFT_MSG_GETRULE_RESET requests") Link: https://lore.kernel.org/all/aUh_3mVRV8OrGsVo@strlen.de/ Reported-by: Closes: https://syzkaller.appspot.com/bug?extid=ff16b505ec9152e5f448 Signed-off-by: Brian Witte Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nf_tables_api.c | 248 ++++++---------------------------- 1 file changed, 42 insertions(+), 206 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 0842916c19f50..c82c16bd4257e 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3908,23 +3908,6 @@ static int nf_tables_dump_rules(struct sk_buff *skb, return skb->len; } -static int nf_tables_dumpreset_rules(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk)); - int ret; - - /* Mutex is held is to prevent that two concurrent dump-and-reset calls - * do not underrun counters and quotas. The commit_mutex is used for - * the lack a better lock, this is not transaction path. - */ - mutex_lock(&nft_net->commit_mutex); - ret = nf_tables_dump_rules(skb, cb); - mutex_unlock(&nft_net->commit_mutex); - - return ret; -} - static int nf_tables_dump_rules_start(struct netlink_callback *cb) { struct nft_rule_dump_ctx *ctx = (void *)cb->ctx; @@ -3944,16 +3927,10 @@ static int nf_tables_dump_rules_start(struct netlink_callback *cb) return -ENOMEM; } } - return 0; -} - -static int nf_tables_dumpreset_rules_start(struct netlink_callback *cb) -{ - struct nft_rule_dump_ctx *ctx = (void *)cb->ctx; - - ctx->reset = true; + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETRULE_RESET) + ctx->reset = true; - return nf_tables_dump_rules_start(cb); + return 0; } static int nf_tables_dump_rules_done(struct netlink_callback *cb) @@ -4019,6 +3996,8 @@ static int nf_tables_getrule(struct sk_buff *skb, const struct nfnl_info *info, u32 portid = NETLINK_CB(skb).portid; struct net *net = info->net; struct sk_buff *skb2; + bool reset = false; + char *buf; if (info->nlh->nlmsg_flags & NLM_F_DUMP) { struct netlink_dump_control c = { @@ -4032,47 +4011,16 @@ static int nf_tables_getrule(struct sk_buff *skb, const struct nfnl_info *info, return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); } - skb2 = nf_tables_getrule_single(portid, info, nla, false); - if (IS_ERR(skb2)) - return PTR_ERR(skb2); - - return nfnetlink_unicast(skb2, net, portid); -} - -static int nf_tables_getrule_reset(struct sk_buff *skb, - const struct nfnl_info *info, - const struct nlattr * const nla[]) -{ - struct nftables_pernet *nft_net = nft_pernet(info->net); - u32 portid = NETLINK_CB(skb).portid; - struct net *net = info->net; - struct sk_buff *skb2; - char *buf; - - if (info->nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .start= nf_tables_dumpreset_rules_start, - .dump = nf_tables_dumpreset_rules, - .done = nf_tables_dump_rules_done, - .module = THIS_MODULE, - .data = (void *)nla, - }; - - return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); - } - - if (!try_module_get(THIS_MODULE)) - return -EINVAL; - rcu_read_unlock(); - mutex_lock(&nft_net->commit_mutex); - skb2 = nf_tables_getrule_single(portid, info, nla, true); - mutex_unlock(&nft_net->commit_mutex); - rcu_read_lock(); - module_put(THIS_MODULE); + if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETRULE_RESET) + reset = true; + skb2 = nf_tables_getrule_single(portid, info, nla, reset); if (IS_ERR(skb2)) return PTR_ERR(skb2); + if (!reset) + return nfnetlink_unicast(skb2, net, portid); + buf = kasprintf(GFP_ATOMIC, "%.*s:%u", nla_len(nla[NFTA_RULE_TABLE]), (char *)nla_data(nla[NFTA_RULE_TABLE]), @@ -6332,6 +6280,10 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) nla_nest_end(skb, nest); nlmsg_end(skb, nlh); + if (dump_ctx->reset && args.iter.count > args.iter.skip) + audit_log_nft_set_reset(table, cb->seq, + args.iter.count - args.iter.skip); + rcu_read_unlock(); if (args.iter.err && args.iter.err != -EMSGSIZE) @@ -6347,26 +6299,6 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) return -ENOSPC; } -static int nf_tables_dumpreset_set(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk)); - struct nft_set_dump_ctx *dump_ctx = cb->data; - int ret, skip = cb->args[0]; - - mutex_lock(&nft_net->commit_mutex); - - ret = nf_tables_dump_set(skb, cb); - - if (cb->args[0] > skip) - audit_log_nft_set_reset(dump_ctx->ctx.table, cb->seq, - cb->args[0] - skip); - - mutex_unlock(&nft_net->commit_mutex); - - return ret; -} - static int nf_tables_dump_set_start(struct netlink_callback *cb) { struct nft_set_dump_ctx *dump_ctx = cb->data; @@ -6613,8 +6545,13 @@ static int nf_tables_getsetelem(struct sk_buff *skb, { struct netlink_ext_ack *extack = info->extack; struct nft_set_dump_ctx dump_ctx; + int rem, err = 0, nelems = 0; + struct net *net = info->net; struct nlattr *attr; - int rem, err = 0; + bool reset = false; + + if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET) + reset = true; if (info->nlh->nlmsg_flags & NLM_F_DUMP) { struct netlink_dump_control c = { @@ -6624,7 +6561,7 @@ static int nf_tables_getsetelem(struct sk_buff *skb, .module = THIS_MODULE, }; - err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false); + err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, reset); if (err) return err; @@ -6635,75 +6572,21 @@ static int nf_tables_getsetelem(struct sk_buff *skb, if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS]) return -EINVAL; - err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false); + err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, reset); if (err) return err; nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { - err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, false); - if (err < 0) { - NL_SET_BAD_ATTR(extack, attr); - break; - } - } - - return err; -} - -static int nf_tables_getsetelem_reset(struct sk_buff *skb, - const struct nfnl_info *info, - const struct nlattr * const nla[]) -{ - struct nftables_pernet *nft_net = nft_pernet(info->net); - struct netlink_ext_ack *extack = info->extack; - struct nft_set_dump_ctx dump_ctx; - int rem, err = 0, nelems = 0; - struct nlattr *attr; - - if (info->nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .start = nf_tables_dump_set_start, - .dump = nf_tables_dumpreset_set, - .done = nf_tables_dump_set_done, - .module = THIS_MODULE, - }; - - err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true); - if (err) - return err; - - c.data = &dump_ctx; - return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); - } - - if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS]) - return -EINVAL; - - if (!try_module_get(THIS_MODULE)) - return -EINVAL; - rcu_read_unlock(); - mutex_lock(&nft_net->commit_mutex); - rcu_read_lock(); - - err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true); - if (err) - goto out_unlock; - - nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { - err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, true); + err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, reset); if (err < 0) { NL_SET_BAD_ATTR(extack, attr); break; } nelems++; } - audit_log_nft_set_reset(dump_ctx.ctx.table, nft_base_seq(info->net), nelems); - -out_unlock: - rcu_read_unlock(); - mutex_unlock(&nft_net->commit_mutex); - rcu_read_lock(); - module_put(THIS_MODULE); + if (reset) + audit_log_nft_set_reset(dump_ctx.ctx.table, nft_base_seq(net), + nelems); return err; } @@ -8567,19 +8450,6 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } -static int nf_tables_dumpreset_obj(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk)); - int ret; - - mutex_lock(&nft_net->commit_mutex); - ret = nf_tables_dump_obj(skb, cb); - mutex_unlock(&nft_net->commit_mutex); - - return ret; -} - static int nf_tables_dump_obj_start(struct netlink_callback *cb) { struct nft_obj_dump_ctx *ctx = (void *)cb->ctx; @@ -8596,16 +8466,10 @@ static int nf_tables_dump_obj_start(struct netlink_callback *cb) if (nla[NFTA_OBJ_TYPE]) ctx->type = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE])); - return 0; -} - -static int nf_tables_dumpreset_obj_start(struct netlink_callback *cb) -{ - struct nft_obj_dump_ctx *ctx = (void *)cb->ctx; - - ctx->reset = true; + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET) + ctx->reset = true; - return nf_tables_dump_obj_start(cb); + return 0; } static int nf_tables_dump_obj_done(struct netlink_callback *cb) @@ -8667,42 +8531,16 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info, static int nf_tables_getobj(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const nla[]) { - u32 portid = NETLINK_CB(skb).portid; - struct sk_buff *skb2; - - if (info->nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .start = nf_tables_dump_obj_start, - .dump = nf_tables_dump_obj, - .done = nf_tables_dump_obj_done, - .module = THIS_MODULE, - .data = (void *)nla, - }; - - return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); - } - - skb2 = nf_tables_getobj_single(portid, info, nla, false); - if (IS_ERR(skb2)) - return PTR_ERR(skb2); - - return nfnetlink_unicast(skb2, info->net, portid); -} - -static int nf_tables_getobj_reset(struct sk_buff *skb, - const struct nfnl_info *info, - const struct nlattr * const nla[]) -{ - struct nftables_pernet *nft_net = nft_pernet(info->net); u32 portid = NETLINK_CB(skb).portid; struct net *net = info->net; struct sk_buff *skb2; + bool reset = false; char *buf; if (info->nlh->nlmsg_flags & NLM_F_DUMP) { struct netlink_dump_control c = { - .start = nf_tables_dumpreset_obj_start, - .dump = nf_tables_dumpreset_obj, + .start = nf_tables_dump_obj_start, + .dump = nf_tables_dump_obj, .done = nf_tables_dump_obj_done, .module = THIS_MODULE, .data = (void *)nla, @@ -8711,18 +8549,16 @@ static int nf_tables_getobj_reset(struct sk_buff *skb, return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); } - if (!try_module_get(THIS_MODULE)) - return -EINVAL; - rcu_read_unlock(); - mutex_lock(&nft_net->commit_mutex); - skb2 = nf_tables_getobj_single(portid, info, nla, true); - mutex_unlock(&nft_net->commit_mutex); - rcu_read_lock(); - module_put(THIS_MODULE); + if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET) + reset = true; + skb2 = nf_tables_getobj_single(portid, info, nla, reset); if (IS_ERR(skb2)) return PTR_ERR(skb2); + if (!reset) + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); + buf = kasprintf(GFP_ATOMIC, "%.*s:%u", nla_len(nla[NFTA_OBJ_TABLE]), (char *)nla_data(nla[NFTA_OBJ_TABLE]), @@ -10030,7 +9866,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_rule_policy, }, [NFT_MSG_GETRULE_RESET] = { - .call = nf_tables_getrule_reset, + .call = nf_tables_getrule, .type = NFNL_CB_RCU, .attr_count = NFTA_RULE_MAX, .policy = nft_rule_policy, @@ -10084,7 +9920,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_set_elem_list_policy, }, [NFT_MSG_GETSETELEM_RESET] = { - .call = nf_tables_getsetelem_reset, + .call = nf_tables_getsetelem, .type = NFNL_CB_RCU, .attr_count = NFTA_SET_ELEM_LIST_MAX, .policy = nft_set_elem_list_policy, @@ -10130,7 +9966,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_obj_policy, }, [NFT_MSG_GETOBJ_RESET] = { - .call = nf_tables_getobj_reset, + .call = nf_tables_getobj, .type = NFNL_CB_RCU, .attr_count = NFTA_OBJ_MAX, .policy = nft_obj_policy, From 89890a5fcefadad81a778863c146f18e85ab34c2 Mon Sep 17 00:00:00 2001 From: Ryosuke Yasuoka Date: Mon, 13 Jul 2026 22:01:00 +0900 Subject: [PATCH 2234/3297] drm/virtio: fix deadlock in display_info_cb by removing hotplug from dequeue worker [ Upstream commit d1b894c5bbb3fee0012bd14356286dc2384e8213 ] A probe-time deadlock can occur between the dequeue worker and drm_client_register(). During probe, drm_client_register() holds clientlist_mutex and calls the fbdev hotplug callback, which triggers an atomic commit that ends up sleeping in virtio_gpu_queue_ctrl_sgs() waiting for virtqueue space. The dequeue worker that would free that space calls virtio_gpu_cmd_get_display_info_cb(), which invokes drm_kms_helper_hotplug_event() -> drm_client_dev_hotplug(), attempting to acquire the same clientlist_mutex. Since wake_up() is only called after the resp_cb loop, the probe thread is never woken and both threads deadlock. Fix this by removing the hotplug notification from virtio_gpu_cmd_get_display_info_cb(). The display data (outputs[i].info) is still updated synchronously in the callback. For the init path, drm_client_register() already fires an initial hotplug when the client is registered, which picks up the connector state updated by display_info_cb. For the runtime config_changed path, add a wait_event_timeout() in config_changed_work_func() so that display_info_cb updates the connector data before the hotplug notification is sent. Also replace drm_helper_hpd_irq_event() with drm_kms_helper_hotplug_event() since virtio-gpu never calls drm_kms_helper_poll_init() and thus drm_helper_hpd_irq_event() always returns false without doing anything. Fixes: 27655b9bb9f0 ("drm/client: Send hotplug event after registering a client") Closes: https://syzkaller.appspot.com/bug?id=d6dd6f86d3aaf7eebe7406e45c1c6e549453f224 Closes: https://syzkaller.appspot.com/bug?id=908bd910da5dd79b88de4cf7baf376cc873a922e Suggested-by: Dmitry Osipenko Signed-off-by: Ryosuke Yasuoka Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260713-virtiogpu_syzbot-v2-1-2958fa37d46d@redhat.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/virtio/virtgpu_kms.c | 5 ++++- drivers/gpu/drm/virtio/virtgpu_vq.c | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 1c15cbf326b78..4a25347734fda 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -48,7 +48,10 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_display_info(vgdev); virtio_gpu_notify(vgdev); - drm_helper_hpd_irq_event(vgdev->ddev); + wait_event_timeout(vgdev->resp_wq, + !vgdev->display_info_pending, + 5 * HZ); + drm_kms_helper_hotplug_event(vgdev->ddev); } events_clear |= VIRTIO_GPU_EVENT_DISPLAY; } diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 8181b22b9b46a..412384e16daea 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -839,9 +839,6 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev, vgdev->display_info_pending = false; spin_unlock(&vgdev->display_info_lock); wake_up(&vgdev->resp_wq); - - if (!drm_helper_hpd_irq_event(vgdev->ddev)) - drm_kms_helper_hotplug_event(vgdev->ddev); } static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev, From 8e39ed92d7c5c6bfc08dc45153916f49a4e98bab Mon Sep 17 00:00:00 2001 From: Kiryl Shutsemau Date: Wed, 22 Jul 2026 12:01:23 +0100 Subject: [PATCH 2235/3297] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race commit 04718f7c9290f95385f0dd328758753dc1c36dec upstream. Patch series "userfaultfd/pagemap: pre-existing fixes". These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this. All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review . They are independent of RWP, apply to mm-new directly, and carry Cc: stable@. 1: fs/proc/task_mmu: a missing huge_ptep_modify_prot_start() in make_uffd_wp_huge_pte() can lose hardware Dirty/Accessed updates when PAGEMAP_SCAN write-protects a hugetlb PTE. 2: fs/proc/task_mmu: pagemap_scan_hugetlb_entry() compares the range against HPAGE_SIZE rather than the hstate page size, so it never write-protects gigantic hugetlb pages. 3: fs/proc/task_mmu: PAGEMAP_SCAN with PM_SCAN_WP_MATCHING over an unpopulated hugetlb range self-deadlocks -- pagemap_scan_pte_hole() calls uffd_wp_range() while walk_hugetlb_range() holds the hugetlb vma lock for read, and hugetlb_change_protection() then takes it for write. Install the marker inline instead. 4: mm/huge_memory: change_non_present_huge_pmd() drops pmd_swp_uffd_wp on a device-private PMD permission downgrade, silently losing the uffd-wp marker. 5: userfaultfd: must_wait() applies pte_write() to a locklessly read PTE without checking pte_present(), so swap/migration entries decode random offset bits and a thread can stay parked on a stale fault. 6: userfaultfd: __VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT (41) to mk_vma_flags() unconditionally, an out-of-bounds write into the single-word vma_flags_t on 32-bit. Build the mask from config-gated per-mode masks so an unavailable bit is never materialised. This patch (of 6): make_uffd_wp_huge_pte() arms the UFFD_WP bit on a present HugeTLB PTE by calling huge_ptep_modify_prot_commit() with a ptent snapshot that was fetched without the corresponding huge_ptep_modify_prot_start(). The start helper is what atomically clears the entry so the kernel-owned snapshot stays consistent until the commit; without it, the hardware may set Dirty or Accessed in the live PTE between the original read and the commit, and huge_ptep_modify_prot_commit() (whose generic implementation just calls set_huge_pte_at()) then writes the stale snapshot back over the live hardware bits, losing the update. The non-hugetlb sibling make_uffd_wp_pte() does this correctly via ptep_modify_prot_start() / ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against. Link: https://lore.kernel.org/20260529172331.356655-1-kas@kernel.org Link: https://lore.kernel.org/20260529172331.356655-2-kas@kernel.org Link: https://lore.kernel.org/all/20260526130509.2748441-1-kirill@shutemov.name/ [1] Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Michal Hocko Cc: Mike Rapoport Cc: Peter Xu Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Balbir Singh Cc: Signed-off-by: Andrew Morton [ kas: adapt to the pre-softleaf idiom; apply the huge_ptep_modify_prot_start()/commit() fix to the present-PTE (!huge_pte_none()) branch ] Signed-off-by: Kiryl Shutsemau (Meta) Signed-off-by: Sasha Levin --- fs/proc/task_mmu.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 38b9d47426ca9..858b11009c269 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2485,15 +2485,19 @@ static void make_uffd_wp_huge_pte(struct vm_area_struct *vma, psize = huge_page_size(hstate_vma(vma)); - if (is_hugetlb_entry_migration(ptent)) + if (is_hugetlb_entry_migration(ptent)) { set_huge_pte_at(vma->vm_mm, addr, ptep, pte_swp_mkuffd_wp(ptent), psize); - else if (!huge_pte_none(ptent)) - huge_ptep_modify_prot_commit(vma, addr, ptep, ptent, - huge_pte_mkuffd_wp(ptent)); - else + } else if (!huge_pte_none(ptent)) { + pte_t old_pte, new_pte; + + old_pte = huge_ptep_modify_prot_start(vma, addr, ptep); + new_pte = huge_pte_mkuffd_wp(old_pte); + huge_ptep_modify_prot_commit(vma, addr, ptep, old_pte, new_pte); + } else { set_huge_pte_at(vma->vm_mm, addr, ptep, make_pte_marker(PTE_MARKER_UFFD_WP), psize); + } } #endif /* CONFIG_HUGETLB_PAGE */ From ec46baf830825da915aff1d4d0b417cb556b169a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 4 Dec 2025 11:43:32 +0100 Subject: [PATCH 2236/3297] seqlock: Cure some more scoped_seqlock() optimization fails commit 90dfeef1cd38dff19f8b3a752d13bfd79f0f7694 upstream. Arnd reported an x86 randconfig using gcc-15 tripped over __scoped_seqlock_bug(). Turns out GCC chose not to inline the scoped_seqlock helper functions and as such was not able to optimize properly. [ mingo: Clang fails the build too in some circumstances. ] Reported-by: Arnd Bergmann Tested-by: Arnd Bergmann Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Cc: Oleg Nesterov Link: https://patch.msgid.link/20251204104332.GG2528459@noisy.programming.kicks-ass.net Signed-off-by: Greg Kroah-Hartman --- include/linux/seqlock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 6ab7924f33f59..c4f9027a2aa2a 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -1224,7 +1224,7 @@ struct ss_tmp { spinlock_t *lock_irqsave; }; -static inline void __scoped_seqlock_cleanup(struct ss_tmp *sst) +static __always_inline void __scoped_seqlock_cleanup(struct ss_tmp *sst) { if (sst->lock) spin_unlock(sst->lock); @@ -1249,7 +1249,7 @@ static inline void __scoped_seqlock_bug(void) { } extern void __scoped_seqlock_bug(void); #endif -static inline void +static __always_inline void __scoped_seqlock_next(struct ss_tmp *sst, seqlock_t *lock, enum ss_state target) { switch (sst->state) { From 3958b1aeef43ba635ee7c3086986c6c7965352b4 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 28 Oct 2025 09:56:38 +0100 Subject: [PATCH 2237/3297] seqlock: Allow KASAN to fail optimizing commit b94d45b6bbb42571ec225d3be0e7457c8765a5b4 upstream. Some KASAN builds are failing to properly optimize this code -- luckily we don't care about core quality for KASAN builds, so just exclude it. Reported-by: kernel test robot Signed-off-by: Peter Zijlstra (Intel) Closes: https://lore.kernel.org/oe-kbuild-all/202510251641.idrNXhv5-lkp@intel.com/ Signed-off-by: Greg Kroah-Hartman --- include/linux/seqlock.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index c4f9027a2aa2a..f827cc3cb1460 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -1234,11 +1234,14 @@ static __always_inline void __scoped_seqlock_cleanup(struct ss_tmp *sst) extern void __scoped_seqlock_invalid_target(void); -#if defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000 +#if (defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000) || defined(CONFIG_KASAN) /* * For some reason some GCC-8 architectures (nios2, alpha) have trouble * determining that the ss_done state is impossible in __scoped_seqlock_next() * below. + * + * Similarly KASAN is known to confuse compilers enough to break this. But we + * don't care about code quality for KASAN builds anyway. */ static inline void __scoped_seqlock_bug(void) { } #else From 865dfe76c150baee84a4f4796bcc3223e00d8147 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 19 May 2026 13:03:15 +0200 Subject: [PATCH 2238/3297] seqlock: Allow UBSAN_ALIGNMENT to fail optimizing commit 88331c4ec23a28c1006ec532fa64763d4c695e90 upstream. With gcc-15 and gcc-16 with UBSAN_ALIGNMENT enabled the compiler fails to inline and optimize __scoped_seqlock_bug() away on s390: s390x-16.1.0-ld: kernel/sched/build_policy.o: in function `__scoped_seqlock_next': /.../seqlock.h:1286:(.text+0x22030): undefined reference to `__scoped_seqlock_bug' Fix this by adding UBSAN_ALIGNMENT to the list of config options where a not inlined empty __scoped_seqlock_bug() is allowed. Closes: https://lore.kernel.org/r/20260515092057.810542-1-arnd@kernel.org/ Reported-by: Arnd Bergmann Signed-off-by: Heiko Carstens Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260519110315.1385307-1-hca@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- include/linux/seqlock.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index f827cc3cb1460..6729245aa1543 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -1234,14 +1234,15 @@ static __always_inline void __scoped_seqlock_cleanup(struct ss_tmp *sst) extern void __scoped_seqlock_invalid_target(void); -#if (defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000) || defined(CONFIG_KASAN) +#if (defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000) || \ + defined(CONFIG_KASAN) || defined(CONFIG_UBSAN_ALIGNMENT) /* * For some reason some GCC-8 architectures (nios2, alpha) have trouble * determining that the ss_done state is impossible in __scoped_seqlock_next() * below. * - * Similarly KASAN is known to confuse compilers enough to break this. But we - * don't care about code quality for KASAN builds anyway. + * Similarly KASAN and UBSAN_ALIGNMENT are known to confuse compilers enough + * to break this. But we don't care about code quality for such builds anyway. */ static inline void __scoped_seqlock_bug(void) { } #else From f3477a6a4164f15287444eda685b5f6405dbd1e5 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 13 Jul 2026 08:15:33 -0700 Subject: [PATCH 2239/3297] KVM: x86: Check for invalid/obsolete root *after* making MMU pages available commit 2abd5287f08319fa35764566b15c6e22cb1068db upstream. Check for a "stale" page fault, i.e. for an invalid and/or obsolete root, after making MMU pages available for the shadow MMU. If reclaiming shadow pages zaps an in-use root, i.e. marks it invalid, then KVM will attempt to map memory into an invalid root. On its own, populating an invalid root is "fine", but because child shadow pages inherit their parent's role, any children created during the map/fetch will be created as invalid pages, thus violating KVM's invariant that invalid pages are never on the list of active MMU pages. Note, the underlying flaw has existed since KVM first started tracking invalid roots in 2008 (commit 2e53d63acba7, "KVM: MMU: ignore zapped root pagetables"), but the true badness only came along in 2020 (Linux 5.9) with the invariant that invalid shadow pages can't be on the list of active pages. Note #2, inheriting role.invalid when creating child shadow pages is also far from ideal; that flaw will be addressed separately. Reported-by: Hyunwoo Kim Fixes: f95eec9bed76 ("KVM: x86/mmu: Don't put invalid SPs back on the list of active pages") Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/mmu/mmu.c | 9 +++++---- arch/x86/kvm/mmu/paging_tmpl.h | 10 ++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index eadeca9786f6f..f225be4656ce1 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4806,16 +4806,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (r != RET_PF_CONTINUE) return r; - r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); - if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; - r = make_mmu_pages_available(vcpu); if (r) goto out_unlock; + if (is_page_fault_stale(vcpu, fault)) { + r = RET_PF_RETRY; + goto out_unlock; + } + r = direct_map(vcpu, fault); out_unlock: diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index ed762bb4b007b..af220c9c8ab8d 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -827,15 +827,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault } #endif - r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); - if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; - r = make_mmu_pages_available(vcpu); if (r) goto out_unlock; + + if (is_page_fault_stale(vcpu, fault)) { + r = RET_PF_RETRY; + goto out_unlock; + } + r = FNAME(fetch)(vcpu, fault, &walker); out_unlock: From dd50ad7935d57885318c5e9f3a784f2343fbcb59 Mon Sep 17 00:00:00 2001 From: Venkatesh Srinivas Date: Wed, 15 Jul 2026 23:42:35 +0000 Subject: [PATCH 2240/3297] KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN commit e800decd9c0ac4349bcd8f8f9b29fd21fe93165e upstream. On Intel platforms with a VMX preemption timer and APICv, if a VMM calls KVM_GET_LAPIC before KVM_GET_MSRS to save the vCPU state, it is possible to lose a pending timer interrupt. If the thread running these ioctls is migrated to another core after calling KVM_GET_LAPIC but before KVM_GET_MSRS and the guest is using their LAPIC timer in TSC-deadline mode, not only does the save LAPIC state not carry the pending interrupt, the TSCDEADLINE MSR will be zeroed. After migration across CPUs, KVM_GET_MSRS calls vcpu_load, posting the interrupt and clearing the MSR: vcpu_load() -> kvm_arch_vcpu_load() -> kvm_lapic_restart_hv_timer() -> start_hv_timer() -> apic_timer_expired() -> kvm_apic_inject_pending_timer_irqs() . post interrupt into the LAPIC state . clear IA32_TSCDEADLINE The saved LAPIC state will be missing the pending interrupt and the saved MSR will be zero. Oops. Fix by only posting an interrupt when we're attempting to enter the guest (vcpu->wants_to_run == true), not for vcpu_load from other paths. Assisted-by: gemini:gemini-3.1-pro-preview Debugged-by: David Matlack Debugged-by: Sean Christopherson Debugged-by: Jim Mattson Debugged-by: James Houghton Signed-off-by: Venkatesh Srinivas Message-ID: <20260715234234.15382-2-venkateshs@chromium.org> Reviewed-by: James Houghton Reviewed-by: Chao Gao Cc: stable@vger.kernel.org Fixes: ae95f566b3d2 ("KVM: X86: TSCDEADLINE MSR emulation fastpath", 2020-05-15) Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/lapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index f895019a55e57..e6e747507fd3e 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2065,7 +2065,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn) if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; - if (!from_timer_fn && apic->apicv_active) { + if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) { WARN_ON(kvm_get_running_vcpu() != vcpu); kvm_apic_inject_pending_timer_irqs(apic); return; From 8001d2ce9d9bd09118ce523aef595aa094573ae3 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 17 Jul 2026 12:30:11 +0200 Subject: [PATCH 2241/3297] KVM: nVMX: Hide shadow VMCS right after VMCLEAR commit 622ebfac01ba4f9c0060cebd41257fe46fc4a0b3 upstream. free_nested() frees the shadow VMCS while vmcs01 still points to it. But because it is asynchronous with respect to loaded_vmcs_clear(), the vCPU might migrate before the pointer is cleared and __loaded_vmcs_clear() may then execute VMCLEAR. The VMCS needs to stay attached until its explicit VMCLEAR completes, but then it can be hidden and the page safely freed. Fixes: 355f4fb1405e ("kvm: nVMX: VMCLEAR an active shadow VMCS after last use") Cc: stable@vger.kernel.org Signed-off-by: Hyunwoo Kim Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/nested.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index a5a525ae8e783..afb4be700f9a3 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -333,6 +333,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu) static void free_nested(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); + struct vmcs *shadow_vmcs; if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01)) vmx_switch_vmcs(vcpu, &vmx->vmcs01); @@ -350,9 +351,15 @@ static void free_nested(struct kvm_vcpu *vcpu) vmx->nested.current_vmptr = INVALID_GPA; if (enable_shadow_vmcs) { vmx_disable_shadow_vmcs(vmx); - vmcs_clear(vmx->vmcs01.shadow_vmcs); - free_vmcs(vmx->vmcs01.shadow_vmcs); + + /* + * Keep the pointer visible until after VMCLEAR, so migration + * can clear an active shadow VMCS on the old CPU. + */ + shadow_vmcs = vmx->vmcs01.shadow_vmcs; + vmcs_clear(shadow_vmcs); vmx->vmcs01.shadow_vmcs = NULL; + free_vmcs(shadow_vmcs); } kfree(vmx->nested.cached_vmcs12); vmx->nested.cached_vmcs12 = NULL; From ec9daa8fd1b6f45545c9839dca55bd867fad9e13 Mon Sep 17 00:00:00 2001 From: Phil Rosenthal Date: Sat, 18 Jul 2026 12:50:23 -0400 Subject: [PATCH 2242/3297] KVM: x86/mmu: Fix use-after-free on vendor module reload commit 52f2f7c30126037975389aa04d24c506a5177c35 upstream. mmu_destroy_caches() destroys pte_list_desc_cache and mmu_page_header_cache, but leaves both pointers unchanged. The pointers live in kvm.ko, and therefore survive when a vendor module is unloaded while kvm.ko remains loaded. If creation of pte_list_desc_cache fails during a subsequent vendor module load, its assignment sets pte_list_desc_cache to NULL and the error path calls mmu_destroy_caches(). mmu_page_header_cache still points to the cache destroyed during the preceding vendor module unload. Passing that stale pointer to kmem_cache_destroy() causes a slab use-after-free. Reproduce the issue on a v7.1.3 kernel with CONFIG_KASAN=y, CONFIG_KASAN_GENERIC=y, CONFIG_KVM=m, and CONFIG_KVM_INTEL=m. A one-shot test hook forces pte_list_desc_cache to NULL on the second invocation of kvm_mmu_vendor_module_init(): 1. Load kvm.ko and kvm-intel.ko, creating both caches. 2. Unload only kvm_intel, leaving kvm.ko loaded. 3. Reload kvm_intel and force initialization through the -ENOMEM path. KASAN reports: BUG: KASAN: slab-use-after-free in kvm_mmu_vendor_module_init+0x5b/0x170 [kvm] ... kmem_cache_destroy+0x21/0x1d0 kvm_mmu_vendor_module_init+0x5b/0x170 [kvm] ... Allocated by task 16817: __kmem_cache_create_args+0x12c/0x3b0 __kmem_cache_create.constprop.0+0xb6/0xf0 [kvm] kvm_mmu_vendor_module_init+0x13b/0x170 [kvm] ... Freed by task 16820: kmem_cache_destroy+0x117/0x1d0 kvm_mmu_vendor_module_exit+0x21/0x30 [kvm] Clear both pointers immediately after destroying their caches so that the stored state reflects the caches' lifetime and repeated cleanup is safe. With the fix applied, the same injected vendor module reload fails with -ENOMEM as expected and produces no KASAN report. Fixes: cb498ea2ce1d ("KVM: Portability: Combine kvm_init and kvm_init_x86") Cc: stable@vger.kernel.org Signed-off-by: Phil Rosenthal Message-ID: <20260718-kvm-mmu-cache-uaf-v3-1-e103b93c74e1@phil.gs> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/mmu/mmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index f225be4656ce1..e95d96dccfe4a 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -7393,7 +7393,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) static void mmu_destroy_caches(void) { kmem_cache_destroy(pte_list_desc_cache); + pte_list_desc_cache = NULL; kmem_cache_destroy(mmu_page_header_cache); + mmu_page_header_cache = NULL; } static void kvm_wake_nx_recovery_thread(struct kvm *kvm) From 19b1994069dd29478ba767de1f98f14a088198dc Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:51 +0200 Subject: [PATCH 2243/3297] can: bcm: add locking when updating filter and timer values commit 749179c2e25b95d22499ed29096b3e02d6dfd2b4 upstream. KCSAN detected a simultaneous access to timer values that can be overwritten in bcm_rx_setup() when updating timer and filter content while bcm_rx_handler(), bcm_rx_timeout_handler() or bcm_rx_thr_handler() run concurrently on incoming CAN traffic. Protect the timer (ival1/ival2/kt_ival1/kt_ival2/kt_lastmsg) and filter (nframes/flags/frames/last_frames) updates in bcm_rx_setup() with a new per-op bcm_rx_update_lock, taken with the matching scope in the RX handlers. memcpy_from_msg() is staged into a temporary buffer before the lock is taken, since it can sleep and must not run under a spinlock. hrtimer_cancel() is always called without bcm_rx_update_lock held, since bcm_rx_timeout_handler()/bcm_rx_thr_handler() take the same lock and a running callback would otherwise deadlock against the canceller. Also close a related race: bcm_rx_setup() cleared the RTR flag in the stored reply frame's can_id as a separate, unprotected step after the frame content was already installed, so a concurrent bcm_rx_handler() could transmit a stale reply with CAN_RTR_FLAG still set. Fold that normalization into the initial frame preparation instead (on the staged buffer for updates, directly on op->frames pre-registration for new ops), so the installed frame is always atomically self-consistent. bcm_rx_handler()'s RX_RTR_FRAME check now takes a lock-protected snapshot of op->flags before deciding whether to call bcm_can_tx(), but does not hold the lock across that call. Also take a lock-protected snapshot of the currframe in bcm_can_tx() to avoid partly overwrites by content updates in bcm_tx_setup(). Finally check if a TX_RESET_MULTI_IDX/SETTIMER might have reset op->currframe between the two locked sections in bcm_can_tx(). Omit calling hrtimer_forward() with zero interval in bcm_rx_thr_handler(). kt_ival2 may have been concurrently cleared by bcm_rx_setup() before it cancels this timer, so check kt_ival2 inside the bcm_rx_update_lock. Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates") Reported-by: syzbot+75e5e4ae00c3b4bb544e@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-can/6975d5cf.a00a0220.33ccc7.0022.GAE@google.com/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-3-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 176 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 133 insertions(+), 43 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index a40519638a262..cfc495106aac4 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -128,6 +128,7 @@ struct bcm_op { struct sock *sk; struct net_device *rx_reg_dev; spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */ + spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; struct bcm_sock { @@ -292,21 +293,27 @@ static int bcm_proc_show(struct seq_file *m, void *v) * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op */ -static void bcm_can_tx(struct bcm_op *op) +static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) { struct sk_buff *skb; struct net_device *dev; - struct canfd_frame *cf; + struct canfd_frame cframe; + bool cyclic = !cf; + unsigned int idx = 0; int err; /* no target device? => exit */ if (!op->ifindex) return; - /* read currframe under lock protection */ - spin_lock_bh(&op->bcm_tx_lock); - cf = op->frames + op->cfsiz * op->currframe; - spin_unlock_bh(&op->bcm_tx_lock); + if (cyclic) { + /* read currframe under lock protection */ + spin_lock_bh(&op->bcm_tx_lock); + idx = op->currframe; + memcpy(&cframe, op->frames + op->cfsiz * idx, op->cfsiz); + cf = &cframe; + spin_unlock_bh(&op->bcm_tx_lock); + } dev = dev_get_by_index(sock_net(op->sk), op->ifindex); if (!dev) { @@ -335,14 +342,20 @@ static void bcm_can_tx(struct bcm_op *op) if (!err) op->frames_abs++; - op->currframe++; + /* only advance the cyclic sequence if nothing reset currframe while + * we were sending - a concurrent TX_RESET_MULTI_IDX means this + * frame's bookkeeping belongs to a sequence that no longer exists + */ + if (!cyclic || op->currframe == idx) { + op->currframe++; - /* reached last frame? */ - if (op->currframe >= op->nframes) - op->currframe = 0; + /* reached last frame? */ + if (op->currframe >= op->nframes) + op->currframe = 0; - if (op->count > 0) - op->count--; + if (op->count > 0) + op->count--; + } spin_unlock_bh(&op->bcm_tx_lock); out: @@ -456,7 +469,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) struct bcm_msg_head msg_head; if (op->kt_ival1 && (op->count > 0)) { - bcm_can_tx(op); + bcm_can_tx(op, NULL); if (!op->count && (op->flags & TX_COUNTEVT)) { /* create notification to user */ @@ -473,7 +486,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) } } else if (op->kt_ival2) { - bcm_can_tx(op); + bcm_can_tx(op, NULL); } return bcm_tx_set_expiry(op, &op->timer) ? @@ -617,6 +630,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); struct bcm_msg_head msg_head; + spin_lock_bh(&op->bcm_rx_update_lock); + /* if user wants to be informed, when cyclic CAN-Messages come back */ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) { /* clear received CAN frames to indicate 'nothing received' */ @@ -633,6 +648,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) msg_head.can_id = op->can_id; msg_head.nframes = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + bcm_send_to_user(op, &msg_head, NULL, 0); return HRTIMER_NORESTART; @@ -681,15 +698,26 @@ static int bcm_rx_thr_flush(struct bcm_op *op) static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); + enum hrtimer_restart ret; - if (bcm_rx_thr_flush(op)) { + spin_lock_bh(&op->bcm_rx_update_lock); + + /* kt_ival2 may have been concurrently cleared by bcm_rx_setup() + * before it cancels this timer - never forward with a zero + * interval in that case. + */ + if (bcm_rx_thr_flush(op) && op->kt_ival2) { hrtimer_forward_now(hrtimer, op->kt_ival2); - return HRTIMER_RESTART; + ret = HRTIMER_RESTART; } else { /* rearm throttle handling */ op->kt_lastmsg = 0; - return HRTIMER_NORESTART; + ret = HRTIMER_NORESTART; } + + spin_unlock_bh(&op->bcm_rx_update_lock); + + return ret; } /* @@ -699,8 +727,10 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) { struct bcm_op *op = (struct bcm_op *)data; const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data; + struct canfd_frame rtrframe; unsigned int i; unsigned char traffic_flags; + bool rtr_frame; if (op->can_id != rxframe->can_id) return; @@ -724,9 +754,18 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) /* update statistics */ op->frames_abs++; - if (op->flags & RX_RTR_FRAME) { + /* snapshot the flag under lock: op->flags/op->frames may be updated + * concurrently by bcm_rx_setup(). + */ + spin_lock_bh(&op->bcm_rx_update_lock); + rtr_frame = op->flags & RX_RTR_FRAME; + if (rtr_frame) + memcpy(&rtrframe, op->frames, op->cfsiz); + spin_unlock_bh(&op->bcm_rx_update_lock); + + if (rtr_frame) { /* send reply for RTR-request (placed in op->frames[0]) */ - bcm_can_tx(op); + bcm_can_tx(op, &rtrframe); return; } @@ -738,6 +777,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } + spin_lock_bh(&op->bcm_rx_update_lock); + if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, @@ -773,6 +814,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) rx_starttimer: bcm_rx_starttimer(op); + + spin_unlock_bh(&op->bcm_rx_update_lock); } /* @@ -1115,7 +1158,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, list_add_rcu(&op->list, &bo->tx_ops); if (op->flags & TX_ANNOUNCE) - bcm_can_tx(op); + bcm_can_tx(op, NULL); if (op->flags & STARTTIMER) bcm_tx_start_timer(op); @@ -1129,6 +1172,24 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } +static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, + struct bcm_op *op, void *new_frames) +{ + /* funny feature in RX(!)_SETUP only for RTR-mode: + * copy can_id into frame BUT without RTR-flag to + * prevent a full-load-loopback-test ... ;-] + * normalize this on the staged buffer, before it is + * ever installed into op->frames. + */ + if (msg_head->flags & RX_RTR_FRAME) { + struct canfd_frame *frame0 = new_frames; + + if ((msg_head->flags & TX_CP_CAN_ID) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG; + } +} + /* * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg) */ @@ -1163,6 +1224,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check the given can_id */ op = bcm_find_op(&bo->rx_ops, msg_head, ifindex); if (op) { + void *new_frames = NULL; + /* update existing BCM operation */ /* @@ -1174,19 +1237,48 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return -E2BIG; if (msg_head->nframes) { - /* update CAN frames content */ - err = memcpy_from_msg(op->frames, msg, + /* get new CAN frames content before locking */ + new_frames = kmalloc(msg_head->nframes * op->cfsiz, + GFP_KERNEL); + if (!new_frames) + return -ENOMEM; + + err = memcpy_from_msg(new_frames, msg, msg_head->nframes * op->cfsiz); - if (err < 0) + if (err < 0) { + kfree(new_frames); return err; + } - /* clear last_frames to indicate 'nothing received' */ - memset(op->last_frames, 0, msg_head->nframes * op->cfsiz); + bcm_rx_setup_rtr_check(msg_head, op, new_frames); } + spin_lock_bh(&op->bcm_rx_update_lock); op->nframes = msg_head->nframes; op->flags = msg_head->flags; + if (msg_head->nframes) { + /* update CAN frames content */ + memcpy(op->frames, new_frames, + msg_head->nframes * op->cfsiz); + + /* clear last_frames to indicate 'nothing received' */ + memset(op->last_frames, 0, + msg_head->nframes * op->cfsiz); + } + + if (msg_head->flags & SETTIMER) { + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + op->kt_lastmsg = 0; + } + spin_unlock_bh(&op->bcm_rx_update_lock); + + /* free temporary frames / kfree(NULL) is safe */ + kfree(new_frames); + /* Only an update -> do not call can_rx_register() */ do_rx_register = 0; @@ -1197,6 +1289,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return -ENOMEM; spin_lock_init(&op->bcm_tx_lock); + spin_lock_init(&op->bcm_rx_update_lock); op->can_id = msg_head->can_id; op->nframes = msg_head->nframes; op->cfsiz = CFSIZ(msg_head->flags); @@ -1238,6 +1331,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, kfree(op); return err; } + + bcm_rx_setup_rtr_check(msg_head, op, op->frames); } /* bcm_can_tx / bcm_tx_timeout_handler needs this */ @@ -1262,29 +1357,22 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check flags */ if (op->flags & RX_RTR_FRAME) { - struct canfd_frame *frame0 = op->frames; - /* no timers in RTR-mode */ hrtimer_cancel(&op->thrtimer); hrtimer_cancel(&op->timer); - - /* - * funny feature in RX(!)_SETUP only for RTR-mode: - * copy can_id into frame BUT without RTR-flag to - * prevent a full-load-loopback-test ... ;-] - */ - if ((op->flags & TX_CP_CAN_ID) || - (frame0->can_id == op->can_id)) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - } else { if (op->flags & SETTIMER) { - /* set timer value */ - op->ival1 = msg_head->ival1; - op->ival2 = msg_head->ival2; - op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); - op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + /* set timers (locked) for newly created op */ + if (do_rx_register) { + spin_lock_bh(&op->bcm_rx_update_lock); + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); + op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + op->kt_lastmsg = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + } /* disable an active timer due to zero value? */ if (!op->kt_ival1) @@ -1294,9 +1382,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, * In any case cancel the throttle timer, flush * potentially blocked msgs and reset throttle handling */ - op->kt_lastmsg = 0; hrtimer_cancel(&op->thrtimer); + + spin_lock_bh(&op->bcm_rx_update_lock); bcm_rx_thr_flush(op); + spin_unlock_bh(&op->bcm_rx_update_lock); } if ((op->flags & STARTTIMER) && op->kt_ival1) From 8104bcdb2612fdda95169ddc3b49747b2ff98d24 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:52 +0200 Subject: [PATCH 2244/3297] can: bcm: fix CAN frame rx/tx statistics commit e6c24ba95fc3f1b5e1dcd28b1c6e59ef61a9daa5 upstream. KCSAN detected a data race within the bcm_rx_handler() when two CAN frames have been simultaneously received and processed in a single rx op by two different CPUs. Use atomic operations with (signed) long data types to access the statistics in the hot path to fix the KCSAN complaint. Additionally simplify the update and check of statistics overflow by using the atomic operations in separate bcm_update_[rx|tx]_stats() functions. The rx variant runs under bcm_rx_update_lock to prevent races when resetting the two rx counters; the tx variant runs under bcm_tx_lock and only needs to guard its own counter's overflow. As the rx path resets its values already at LONG_MAX / 100, there is no conflict between the two locking domains (bcm_rx_update_lock vs. bcm_tx_lock) even for ops that use both paths. The rx statistics update and the frames_filtered update in bcm_rx_changed() were previously performed in two separate bcm_rx_update_lock sections. For an rx op subscribed on all interfaces (ifindex == 0), bcm_rx_handler() can run concurrently on different CPUs, so a counter reset by one CPU between these two sections could leave frames_filtered larger than frames_abs on another CPU, producing a bogus (even negative) reduction percentage in procfs. Update the statistics in the same critical section as bcm_rx_changed() to close this gap, which also removes the now unneeded extra lock/unlock pair around the traffic_flags calculation. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-4-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 69 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index cfc495106aac4..1d4323cdf48a4 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -111,7 +111,7 @@ struct bcm_op { int ifindex; canid_t can_id; u32 flags; - unsigned long frames_abs, frames_filtered; + atomic_long_t frames_abs, frames_filtered; struct bcm_timeval ival1, ival2; struct hrtimer timer, thrtimer; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; @@ -228,10 +228,13 @@ static int bcm_proc_show(struct seq_file *m, void *v) list_for_each_entry_rcu(op, &bo->rx_ops, list) { - unsigned long reduction; + long reduction, frames_filtered, frames_abs; + + frames_filtered = atomic_long_read(&op->frames_filtered); + frames_abs = atomic_long_read(&op->frames_abs); /* print only active entries & prevent division by zero */ - if (!op->frames_abs) + if (!frames_abs) continue; seq_printf(m, "rx_op: %03X %-5s ", op->can_id, @@ -253,9 +256,9 @@ static int bcm_proc_show(struct seq_file *m, void *v) (long long)ktime_to_us(op->kt_ival2)); seq_printf(m, "# recv %ld (%ld) => reduction: ", - op->frames_filtered, op->frames_abs); + frames_filtered, frames_abs); - reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; + reduction = 100 - (frames_filtered * 100) / frames_abs; seq_printf(m, "%s%ld%%\n", (reduction == 100) ? "near " : "", reduction); @@ -279,7 +282,8 @@ static int bcm_proc_show(struct seq_file *m, void *v) seq_printf(m, "t2=%lld ", (long long)ktime_to_us(op->kt_ival2)); - seq_printf(m, "# sent %ld\n", op->frames_abs); + seq_printf(m, "# sent %ld\n", + atomic_long_read(&op->frames_abs)); } seq_putc(m, '\n'); @@ -289,6 +293,24 @@ static int bcm_proc_show(struct seq_file *m, void *v) } #endif /* CONFIG_PROC_FS */ +static void bcm_update_rx_stats(struct bcm_op *op) +{ + /* prevent overflow of the reduction% calculation in bcm_proc_show() */ + if (atomic_long_inc_return(&op->frames_abs) > LONG_MAX / 100) { + atomic_long_set(&op->frames_filtered, 0); + atomic_long_set(&op->frames_abs, 0); + } +} + +static void bcm_update_tx_stats(struct bcm_op *op) +{ + /* tx_op has no reduction% calculation - use the full range and + * just keep the displayed counter non-negative on overflow + */ + if (atomic_long_inc_return(&op->frames_abs) == LONG_MAX) + atomic_long_set(&op->frames_abs, 0); +} + /* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op @@ -340,7 +362,7 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) spin_lock_bh(&op->bcm_tx_lock); if (!err) - op->frames_abs++; + bcm_update_tx_stats(op); /* only advance the cyclic sequence if nothing reset currframe while * we were sending - a concurrent TX_RESET_MULTI_IDX means this @@ -500,12 +522,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data) { struct bcm_msg_head head; - /* update statistics */ - op->frames_filtered++; - - /* prevent statistics overflow */ - if (op->frames_filtered > ULONG_MAX/100) - op->frames_filtered = op->frames_abs = 0; + /* update statistics (frames_filtered <= frames_abs) */ + if (atomic_long_read(&op->frames_abs)) + atomic_long_inc(&op->frames_filtered); /* this element is not throttled anymore */ data->flags &= ~RX_THR; @@ -751,24 +770,30 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) op->rx_stamp = skb->tstamp; /* save originator for recvfrom() */ op->rx_ifindex = skb->dev->ifindex; - /* update statistics */ - op->frames_abs++; - /* snapshot the flag under lock: op->flags/op->frames may be updated - * concurrently by bcm_rx_setup(). - */ + /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh(&op->bcm_rx_update_lock); + rtr_frame = op->flags & RX_RTR_FRAME; - if (rtr_frame) + if (rtr_frame) { + bcm_update_rx_stats(op); + /* snapshot RTR content under lock */ memcpy(&rtrframe, op->frames, op->cfsiz); - spin_unlock_bh(&op->bcm_rx_update_lock); + spin_unlock_bh(&op->bcm_rx_update_lock); - if (rtr_frame) { /* send reply for RTR-request (placed in op->frames[0]) */ bcm_can_tx(op, &rtrframe); return; } + /* update statistics in the same critical section as bcm_rx_changed() + * below: frames_filtered must never be checked/incremented against a + * frames_abs snapshot from a concurrent bcm_rx_handler() call on + * another CPU for the same (wildcard) op, or frames_filtered can end + * up larger than frames_abs. + */ + bcm_update_rx_stats(op); + /* compute flags to distinguish between own/local/remote CAN traffic */ traffic_flags = 0; if (skb->sk) { @@ -777,8 +802,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } - spin_lock_bh(&op->bcm_rx_update_lock); - if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, From bd46f55dec608daa44b45dcf3328517630ad8e40 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:53 +0200 Subject: [PATCH 2245/3297] can: bcm: extend bcm_tx_lock usage for data and timer updates commit 12ce799f7ab1e05bd8fbf79e46f403bfe5597ebc upstream. Stage new CAN frame content for an existing tx op into a kmalloc()'d buffer and validate it there, mirroring the approach already used in bcm_rx_setup(). Only copy the validated data into op->frames while holding op->bcm_tx_lock, so bcm_can_tx() and bcm_tx_timeout_handler() can no longer observe a partially updated or unvalidated frame. Add a missing error path for memcpy_from_msg() when copying CAN frame data from userspace. Also move the kt_ival1/kt_ival2/ival1/ival2 updates in bcm_tx_setup() under op->bcm_tx_lock, and read kt_ival1/kt_ival2/count under the same lock in bcm_tx_set_expiry() and bcm_tx_timeout_handler(), closing the torn 64-bit ktime_t read on 32-bit platforms. Fixes: c2aba69d0c36 ("can: bcm: add locking for bcm_op runtime updates") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-6-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 104 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 29 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 1d4323cdf48a4..d7f91afda3c85 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -127,7 +127,7 @@ struct bcm_op { struct canfd_frame last_sframe; struct sock *sk; struct net_device *rx_reg_dev; - spinlock_t bcm_tx_lock; /* protect currframe/count in runtime updates */ + spinlock_t bcm_tx_lock; /* protect tx data and timer updates */ spinlock_t bcm_rx_update_lock; /* protect filter/timer data updates */ }; @@ -467,12 +467,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt) { ktime_t ival; + spin_lock_bh(&op->bcm_tx_lock); + if (op->kt_ival1 && op->count) ival = op->kt_ival1; - else if (op->kt_ival2) + else if (op->kt_ival2) { ival = op->kt_ival2; - else + } else { + spin_unlock_bh(&op->bcm_tx_lock); return false; + } + + spin_unlock_bh(&op->bcm_tx_lock); hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival)); return true; @@ -489,25 +495,47 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); struct bcm_msg_head msg_head; + bool tx_ival1, tx_ival2; + + /* snapshot kt_ival1/kt_ival2/count under lock to avoid torn + * ktime_t reads racing with concurrent bcm_tx_setup() updates + */ + spin_lock_bh(&op->bcm_tx_lock); + tx_ival1 = op->kt_ival1 && (op->count > 0); + tx_ival2 = !!op->kt_ival2; + spin_unlock_bh(&op->bcm_tx_lock); + + if (tx_ival1) { + u32 flags, count; + struct bcm_timeval ival1, ival2; - if (op->kt_ival1 && (op->count > 0)) { bcm_can_tx(op, NULL); - if (!op->count && (op->flags & TX_COUNTEVT)) { + /* snapshot variables under lock to avoid torn reads racing + * with concurrent bcm_tx_setup() updates + */ + spin_lock_bh(&op->bcm_tx_lock); + flags = op->flags; + count = op->count; + ival1 = op->ival1; + ival2 = op->ival2; + spin_unlock_bh(&op->bcm_tx_lock); + + if (!count && (flags & TX_COUNTEVT)) { /* create notification to user */ memset(&msg_head, 0, sizeof(msg_head)); msg_head.opcode = TX_EXPIRED; - msg_head.flags = op->flags; - msg_head.count = op->count; - msg_head.ival1 = op->ival1; - msg_head.ival2 = op->ival2; + msg_head.flags = flags; + msg_head.count = count; + msg_head.ival1 = ival1; + msg_head.ival2 = ival2; msg_head.can_id = op->can_id; msg_head.nframes = 0; bcm_send_to_user(op, &msg_head, NULL, 0); } - } else if (op->kt_ival2) { + } else if (tx_ival2) { bcm_can_tx(op, NULL); } @@ -1031,6 +1059,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* check the given can_id */ op = bcm_find_op(&bo->tx_ops, msg_head, ifindex); if (op) { + void *new_frames; + /* update existing BCM operation */ /* @@ -1041,11 +1071,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (msg_head->nframes > op->nframes) return -E2BIG; - /* update CAN frames content */ + /* get new CAN frames content into a staging buffer before + * locking: validate and normalize the frames there so that + * bcm_can_tx() / bcm_tx_timeout_handler() never observe a + * partially updated or unvalidated frame in op->frames + */ + new_frames = kmalloc(msg_head->nframes * op->cfsiz, GFP_KERNEL); + if (!new_frames) + return -ENOMEM; + for (i = 0; i < msg_head->nframes; i++) { - cf = op->frames + op->cfsiz * i; + cf = new_frames + op->cfsiz * i; err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz); + if (err < 0) { + kfree(new_frames); + return err; + } if (op->flags & CAN_FD_FRAME) { if (cf->len > 64) @@ -1055,36 +1097,38 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -EINVAL; } - if (err < 0) + if (err < 0) { + kfree(new_frames); return err; + } if (msg_head->flags & TX_CP_CAN_ID) { /* copy can_id into frame */ cf->can_id = msg_head->can_id; } } + + spin_lock_bh(&op->bcm_tx_lock); + + /* update CAN frames content */ + memcpy(op->frames, new_frames, msg_head->nframes * op->cfsiz); + op->flags = msg_head->flags; - /* only lock for unlikely count/nframes/currframe changes */ if (op->nframes != msg_head->nframes || - op->flags & TX_RESET_MULTI_IDX || - op->flags & SETTIMER) { - - spin_lock_bh(&op->bcm_tx_lock); + op->flags & TX_RESET_MULTI_IDX) { + /* potentially update changed nframes */ + op->nframes = msg_head->nframes; + /* restart multiple frame transmission */ + op->currframe = 0; + } - if (op->nframes != msg_head->nframes || - op->flags & TX_RESET_MULTI_IDX) { - /* potentially update changed nframes */ - op->nframes = msg_head->nframes; - /* restart multiple frame transmission */ - op->currframe = 0; - } + if (op->flags & SETTIMER) + op->count = msg_head->count; - if (op->flags & SETTIMER) - op->count = msg_head->count; + spin_unlock_bh(&op->bcm_tx_lock); - spin_unlock_bh(&op->bcm_tx_lock); - } + kfree(new_frames); } else { /* insert new BCM operation for the given can_id */ @@ -1160,10 +1204,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & SETTIMER) { /* set timer values */ + spin_lock_bh(&op->bcm_tx_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); + spin_unlock_bh(&op->bcm_tx_lock); /* disable an active timer due to zero values? */ if (!op->kt_ival1 && !op->kt_ival2) From deb6a697cce3f021e731df543597f37a5e54caab Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:54 +0200 Subject: [PATCH 2246/3297] can: bcm: validate frame length in bcm_rx_setup() for RTR replies commit 62ec41f364648be79d54d94d0d240ee326948afd upstream. bcm_tx_setup() validates cf->len against the CAN/CAN FD DLC limits before installing frames for TX_SETUP, but bcm_rx_setup() never did the same for the RTR-reply frame configured via RX_SETUP with RX_RTR_FRAME. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-7-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 59 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index d7f91afda3c85..2de64d7a99a21 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1241,22 +1241,37 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } -static void bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, - struct bcm_op *op, void *new_frames) +static int bcm_rx_setup_rtr_check(struct bcm_msg_head *msg_head, + struct bcm_op *op, void *new_frames) { + struct canfd_frame *frame0 = new_frames; + + if (!(msg_head->flags & RX_RTR_FRAME)) + return 0; + + /* this frame is sent out as-is by bcm_can_tx() whenever a matching + * remote request is received, so validate its length the same way + * bcm_tx_setup() validates TX_SETUP frames before installing it + */ + if (msg_head->flags & CAN_FD_FRAME) { + if (frame0->len > 64) + return -EINVAL; + } else { + if (frame0->len > 8) + return -EINVAL; + } + /* funny feature in RX(!)_SETUP only for RTR-mode: * copy can_id into frame BUT without RTR-flag to * prevent a full-load-loopback-test ... ;-] * normalize this on the staged buffer, before it is * ever installed into op->frames. */ - if (msg_head->flags & RX_RTR_FRAME) { - struct canfd_frame *frame0 = new_frames; + if ((msg_head->flags & TX_CP_CAN_ID) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - if ((msg_head->flags & TX_CP_CAN_ID) || - frame0->can_id == op->can_id) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG; - } + return 0; } /* @@ -1319,7 +1334,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, return err; } - bcm_rx_setup_rtr_check(msg_head, op, new_frames); + err = bcm_rx_setup_rtr_check(msg_head, op, new_frames); + if (err < 0) { + kfree(new_frames); + return err; + } } spin_lock_bh(&op->bcm_rx_update_lock); @@ -1392,16 +1411,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (msg_head->nframes) { err = memcpy_from_msg(op->frames, msg, msg_head->nframes * op->cfsiz); - if (err < 0) { - if (op->frames != &op->sframe) - kfree(op->frames); - if (op->last_frames != &op->last_sframe) - kfree(op->last_frames); - kfree(op); - return err; - } + if (err < 0) + goto free_op; - bcm_rx_setup_rtr_check(msg_head, op, op->frames); + err = bcm_rx_setup_rtr_check(msg_head, op, op->frames); + if (err < 0) + goto free_op; } /* bcm_can_tx / bcm_tx_timeout_handler needs this */ @@ -1500,6 +1515,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } return msg_head->nframes * op->cfsiz + MHSIZ; + +free_op: + if (op->frames != &op->sframe) + kfree(op->frames); + if (op->last_frames != &op->last_sframe) + kfree(op->last_frames); + kfree(op); + return err; } /* From b024c21c9066f6957b7d4a8f2037e4b000c5e041 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:55 +0200 Subject: [PATCH 2247/3297] can: bcm: add missing device refcount for CAN filter removal commit d59948293ea34b6337ce2b5febab8510de70048c upstream. sashiko-bot remarked a problem with a concurrent device unregistration in isotp.c which also is present in the bcm.c code. A former fix for raw.c commit c275a176e4b6 ("can: raw: add missing refcount for memory leak fix") introduced a netdevice_tracker which solves the issue for bcm.c too. bcm_release(), bcm_delete_rx_op() and bcm_notifier() relied on dev_get_by_index(ifindex) to re-find the device for an rx_op before unregistering its filter. If a concurrent NETDEV_UNREGISTER has already unlisted the device from the ifindex table, that lookup fails and can_rx_unregister() is silently skipped, leaving a stale CAN filter pointing at the soon-to-be-freed bcm_op/socket. Hold a netdev_hold()/netdev_put() tracked reference on op->rx_reg_dev from the moment the rx filter is registered in bcm_rx_setup() until it is unregistered in bcm_rx_unreg(), and use that reference directly in bcm_release() and bcm_delete_rx_op() instead of re-looking the device up by ifindex. Reported-by: sashiko-bot@kernel.org Closes: https://sashiko.dev/#/patchset/20260707094716.63578-1-socketcan@hartkopp.net Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-8-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 2de64d7a99a21..ca0dd2229f5b8 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -932,6 +932,7 @@ static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op) /* mark as removed subscription */ op->rx_reg_dev = NULL; + dev_put(dev); } else printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device " "mismatch %p %p\n", op->rx_reg_dev, dev); @@ -962,17 +963,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() + * + * op->rx_reg_dev is a tracked reference taken + * when the subscription was registered, so it + * stays valid here even if a concurrent + * NETDEV_UNREGISTER already unlisted the dev. */ - if (op->rx_reg_dev) { - struct net_device *dev; - - dev = dev_get_by_index(sock_net(op->sk), - op->ifindex); - if (dev) { - bcm_rx_unreg(dev, op); - dev_put(dev); - } - } + if (op->rx_reg_dev) + bcm_rx_unreg(op->rx_reg_dev, op); } else can_rx_unregister(sock_net(op->sk), NULL, op->can_id, @@ -1491,7 +1489,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, bcm_rx_handler, op, "bcm", sk); - op->rx_reg_dev = dev; + /* keep a reference so that a later + * unregister can safely reach the device even + * if a concurrent NETDEV_UNREGISTER has + * already unlisted it by ifindex + */ + if (!err) { + op->rx_reg_dev = dev; + dev_hold(dev); + } dev_put(dev); } else { /* the requested device is gone - do not @@ -1864,16 +1870,14 @@ static int bcm_release(struct socket *sock) * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() + * + * op->rx_reg_dev is a tracked reference taken + * when the subscription was registered, so it + * stays valid here even if a concurrent + * NETDEV_UNREGISTER already unlisted the device. */ - if (op->rx_reg_dev) { - struct net_device *dev; - - dev = dev_get_by_index(net, op->ifindex); - if (dev) { - bcm_rx_unreg(dev, op); - dev_put(dev); - } - } + if (op->rx_reg_dev) + bcm_rx_unreg(op->rx_reg_dev, op); } else can_rx_unregister(net, NULL, op->can_id, REGMASK(op->can_id), From 6be3e1fedf03eab36a2c09d755d1171287b2014b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:56 +0200 Subject: [PATCH 2248/3297] can: bcm: fix stale rx/tx ops after device removal commit 3b762c0d950383ab7a002686c9136b9aa55d2d70 upstream. RX: an RX_SETUP update(!) for an existing op skipped can_rx_register() unconditionally, even when a concurrent NETDEV_UNREGISTER had already torn down its registration (op->rx_reg_dev == NULL). This silently did not re-enable frame delivery for that updated filter. bcm_rx_setup() now re-registers in that case, while leaving rx_ops with ifindex = 0 (all CAN devices) which never carry a tracked rx_reg_dev registered as-is. TX: bcm_notify() only handled bo->rx_ops on NETDEV_UNREGISTER, leaving tx_ops with an active cyclic transmission re-arming its hrtimer indefinitely to execute bcm_tx_timeout_handler(). Cancelling the hrtimer prevents the runaway timer and any injection into a later reused ifindex, since nothing else calls bcm_can_tx() for the op until an explicit TX_SETUP update re-arms it. Unlike bcm_rx_unreg(), which clears the tracked rx_reg_dev for rx_ops, the ifindex is intentionally left unchanged for tx_ops. bcm_tx_setup() always rejects ifindex 0, so clearing it would strand the op: neither a later TX_SETUP (bcm_find_op()) nor TX_DELETE (bcm_delete_tx_op()) could ever find it again, since both require an exact ifindex match. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260708094536.DDF821F00A3A@smtp.kernel.org/ Closes: https://lore.kernel.org/linux-can/20260708154039.347ED1F000E9@smtp.kernel.org/ Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-9-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 54 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index ca0dd2229f5b8..8c138376591f3 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1281,6 +1281,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; int do_rx_register; + int new_op = 0; int err = 0; if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) { @@ -1365,8 +1366,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* free temporary frames / kfree(NULL) is safe */ kfree(new_frames); - /* Only an update -> do not call can_rx_register() */ - do_rx_register = 0; + /* Don't register a new CAN filter for the rx_op update unless + * a concurrent NETDEV_UNREGISTER notifier already tore down + * the previous registration. In this case the receiver needs + * to be re-registered here so that this update doesn't + * silently stop delivering frames for the given ifindex. + * Ops with ifindex = 0 (all CAN interfaces) never carry a + * tracked rx_reg_dev and stay registered as-is. + */ + do_rx_register = (ifindex && !op->rx_reg_dev) ? 1 : 0; } else { /* insert new BCM operation for the given can_id */ @@ -1433,6 +1441,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* call can_rx_register() */ do_rx_register = 1; + new_op = 1; } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */ @@ -1446,7 +1455,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (op->flags & SETTIMER) { /* set timers (locked) for newly created op */ - if (do_rx_register) { + if (new_op) { spin_lock_bh(&op->bcm_rx_update_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; @@ -1476,7 +1485,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, HRTIMER_MODE_REL_SOFT); } - /* now we can register for can_ids, if we added a new bcm_op */ + /* now we can register for can_ids, if we added a new bcm_op + * or need to re-register after a NETDEV_UNREGISTER tore down + * the previous registration of an existing op + */ if (do_rx_register) { if (ifindex) { struct net_device *dev; @@ -1506,18 +1518,32 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -ENODEV; } - } else + } else { err = can_rx_register(sock_net(sk), NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, "bcm", sk); + } + if (err) { - /* this bcm rx op is broken -> remove it */ - bcm_remove_op(op); + /* newly created bcm rx op is broken -> remove it */ + if (new_op) { + bcm_remove_op(op); + return err; + } + + /* an existing op just stays unregistered. + * Cancel op->timer and (defensively) op->thrtimer. + * Other settings can't be reached until the next + * successful RX_SETUP. + */ + hrtimer_cancel(&op->timer); + hrtimer_cancel(&op->thrtimer); return err; } - /* add this bcm_op to the list of the rx_ops */ - list_add_rcu(&op->list, &bo->rx_ops); + /* add a new bcm_op to the list of the rx_ops */ + if (new_op) + list_add_rcu(&op->list, &bo->rx_ops); } return msg_head->nframes * op->cfsiz + MHSIZ; @@ -1733,11 +1759,19 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, case NETDEV_UNREGISTER: lock_sock(sk); - /* remove device specific receive entries */ + /* rx_ops: remove device specific receive entries */ list_for_each_entry(op, &bo->rx_ops, list) if (op->rx_reg_dev == dev) bcm_rx_unreg(dev, op); + /* tx_ops: stop device specific cyclic transmissions on the + * vanishing ifindex. Cancelling the timer is enough to stop + * cyclic bcm_can_tx() calls as there is no re-arming. + */ + list_for_each_entry(op, &bo->tx_ops, list) + if (op->ifindex == dev->ifindex) + hrtimer_cancel(&op->timer); + /* remove device reference, if this is our bound device */ if (bo->bound && bo->ifindex == dev->ifindex) { #if IS_ENABLED(CONFIG_PROC_FS) From 136de17f38630307991c59aa7080012a99451783 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:57 +0200 Subject: [PATCH 2249/3297] can: bcm: fix data race on rx_stamp/rx_ifindex in bcm_rx_handler() commit 58fd6cbc8541216af1d7ed272ea7ac2b66d50fd8 upstream. For an rx op subscribed on all interfaces (ifindex == 0), the same op is registered once in the shared per-netns wildcard filter list, so bcm_rx_handler() can run concurrently on different CPUs for frames arriving on different net devices. op->rx_stamp and op->rx_ifindex were written before bcm_rx_update_lock was taken, allowing concurrent writers to race each other - including a torn store of the 64-bit rx_stamp on 32-bit platforms. Beyond a torn store bcm_send_to_user() must report the timestamp/ifindex of the very same frame whose content it is delivering. So the assignment is placed in the same unbroken bcm_rx_update_lock section as the content comparison. As a side effect, the RTR-request frame feature (which never reach bcm_send_to_user()) no longer updates rx_stamp/rx_ifindex, since only the notification path needs them. Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260707145135.5BC831F00A3A@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-10-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 8c138376591f3..079a6a801f42e 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -794,11 +794,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) /* disable timeout */ hrtimer_cancel(&op->timer); - /* save rx timestamp */ - op->rx_stamp = skb->tstamp; - /* save originator for recvfrom() */ - op->rx_ifindex = skb->dev->ifindex; - /* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh(&op->bcm_rx_update_lock); @@ -830,6 +825,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } + /* save rx timestamp and originator for recvfrom() under lock. + * For an op subscribed on all interfaces (ifindex == 0) + * bcm_rx_handler() can run concurrently on different CPUs so + * the CAN content and the meta data must be bundled correctly. + */ + op->rx_stamp = skb->tstamp; + op->rx_ifindex = skb->dev->ifindex; + if (op->flags & RX_FILTER_ID) { /* the easiest case */ bcm_rx_update_and_send(op, op->last_frames, rxframe, From eca8b44d51fc6ab61022258ec968e55e3073b79e Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 12:25:58 +0200 Subject: [PATCH 2250/3297] can: bcm: track a single source interface for ANYDEV timeout/throttle ops commit 2f5976f54a04e9f18b25283036ac3136be453b17 upstream. An ANYDEV rx op (ifindex == 0) with an active RX timeout and/or throttle timer has no defined semantics when matching frames arrive from several interfaces: bcm_rx_handler() can run concurrently for the same op on different CPUs, racing hrtimer_cancel()/ bcm_rx_starttimer() against bcm_rx_timeout_handler() and causing spurious RX_TIMEOUT notifications and last_frames corruption. The same concurrency lets throttled multiplex frames from different interfaces clobber the single rx_ifindex/rx_stamp fields shared by the op. Add op->if_detected to track the first interface that delivers a matching frame while a timeout/throttle timer is configured, and reject frames from any other interface for that op. The claim is decided in bcm_rx_handler() before hrtimer_cancel() touches op->timer, so a rejected frame can never disturb the claimed interface's watchdog. RTR-mode ops are excluded via RX_RTR_FRAME, independent of kt_ival1/kt_ival2, since those may briefly hold a stale value from an earlier non-RTR configuration. The claim is released in bcm_notify() on NETDEV_UNREGISTER and in bcm_rx_setup() when SETTIMER reconfigures the timer values. A (re-)claim is only possible on CAN devices in NETREG_REGISTERED dev->reg_state to cover the release in bcm_notify() where reg_state becomes NETREG_UNREGISTERING until synchronize_net(). Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-can/20260709105031.1A39C1F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260714-bcm_fixes-v15-11-562f7e3e42da@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Sasha Levin --- net/can/bcm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 079a6a801f42e..76bc7f7274253 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -116,6 +116,7 @@ struct bcm_op { struct hrtimer timer, thrtimer; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; int rx_ifindex; + int if_detected; /* first received ifindex in ANYDEV rx_op mode */ int cfsiz; u32 count; u32 nframes; @@ -791,6 +792,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) return; } + /* An ANYDEV op with an active RX timeout and/or throttle timer + * tracks a single source interface: claim the first interface that + * delivers a matching frame and reject frames from any other one, + * before hrtimer_cancel() below can touch op->timer - this avoids + * racing bcm_rx_timeout_handler() across concurrent interfaces. + * RX_RTR_FRAME ops are excluded, as kt_ival1/kt_ival2 may briefly + * hold a stale value from an earlier non-RTR configuration. + */ + if (!op->ifindex) { + spin_lock_bh(&op->bcm_rx_update_lock); + + if (!(op->flags & RX_RTR_FRAME) && + (op->kt_ival1 || op->kt_ival2)) { + /* don't claim to vanishing interface */ + if (!op->if_detected && + skb->dev->reg_state == NETREG_REGISTERED) + op->if_detected = skb->dev->ifindex; + + if (op->if_detected != skb->dev->ifindex) { + spin_unlock_bh(&op->bcm_rx_update_lock); + return; + } + } + + spin_unlock_bh(&op->bcm_rx_update_lock); + } + /* disable timeout */ hrtimer_cancel(&op->timer); @@ -825,10 +853,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) traffic_flags |= RX_OWN; } - /* save rx timestamp and originator for recvfrom() under lock. - * For an op subscribed on all interfaces (ifindex == 0) - * bcm_rx_handler() can run concurrently on different CPUs so - * the CAN content and the meta data must be bundled correctly. + /* save rx timestamp and originator for recvfrom() under lock: an + * ANYDEV op without an active timer can still run concurrently on + * different CPUs, so content and meta data must be bundled here. */ op->rx_stamp = skb->tstamp; op->rx_ifindex = skb->dev->ifindex; @@ -1363,6 +1390,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); op->kt_lastmsg = 0; + op->if_detected = 0; /* reclaim ifindex in ANYDEV mode */ } spin_unlock_bh(&op->bcm_rx_update_lock); @@ -1763,10 +1791,21 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, lock_sock(sk); /* rx_ops: remove device specific receive entries */ - list_for_each_entry(op, &bo->rx_ops, list) + list_for_each_entry(op, &bo->rx_ops, list) { if (op->rx_reg_dev == dev) bcm_rx_unreg(dev, op); + /* release an ANYDEV op's claim (see bcm_rx_handler()) + * on this now confirmed-gone interface. + */ + if (!op->ifindex) { + spin_lock_bh(&op->bcm_rx_update_lock); + if (op->if_detected == dev->ifindex) + op->if_detected = 0; + spin_unlock_bh(&op->bcm_rx_update_lock); + } + } + /* tx_ops: stop device specific cyclic transmissions on the * vanishing ifindex. Cancelling the timer is enough to stop * cyclic bcm_can_tx() calls as there is no re-arming. From ec9f66c91bffdb69d309bae6dfb387562db7ebc8 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 24 Jul 2026 14:59:09 +0200 Subject: [PATCH 2251/3297] dmaengine: sh: rz-dmac: Move interrupt request after everything is set up commit 731712403ddb39d1a76a11abf339a0615bc85de7 upstream. Once the interrupt is requested, the interrupt handler may run immediately. Since the IRQ handler can access channel->ch_base, which is initialized only after requesting the IRQ, this may lead to invalid memory access. Likewise, the IRQ thread may access uninitialized data (the ld_free, ld_queue, and ld_active lists), which may also lead to issues. Request the interrupts only after everything is set up. To keep the error path simpler, use dmam_alloc_coherent() instead of dma_alloc_coherent(). Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-2-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul [tm: Kept the channel->irq field in rz_dmac_chan_probe() instead of upstream's local `irq` variable, as commit 04e227718ab8 ("dmaengine: sh: rz-dmac: Make channel irq local") is not present in this tree. Likewise kept platform_get_irq_byname() instead of platform_get_irq_byname_optional() for the error IRQ in rz_dmac_probe(), as commit 6b3a6b6dc074 ("dmaengine: sh: rz_dmac: make error interrupt optional") is not present in this tree either; its early return on failure becomes a goto err jump to match the new call order.] Signed-off-by: Tommaso Merciai Signed-off-by: Sasha Levin --- drivers/dma/sh/rz-dmac.c | 96 ++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 58 deletions(-) diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 818d1ef6f0bf9..4db84b83177a6 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -811,27 +811,6 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, channel->index = index; channel->mid_rid = -EINVAL; - /* Request the channel interrupt. */ - scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); - channel->irq = platform_get_irq_byname(pdev, pdev_irqname); - if (channel->irq < 0) - return channel->irq; - - irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", - dev_name(dmac->dev), index); - if (!irqname) - return -ENOMEM; - - ret = devm_request_threaded_irq(dmac->dev, channel->irq, - rz_dmac_irq_handler, - rz_dmac_irq_handler_thread, 0, - irqname, channel); - if (ret) { - dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", - channel->irq, ret); - return ret; - } - /* Set io base address for each channel */ if (index < 8) { channel->ch_base = dmac->base + CHANNEL_0_7_OFFSET + @@ -844,9 +823,9 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, } /* Allocate descriptors */ - lmdesc = dma_alloc_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - &channel->lmdesc.base_dma, GFP_KERNEL); + lmdesc = dmam_alloc_coherent(&pdev->dev, + sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, + &channel->lmdesc.base_dma, GFP_KERNEL); if (!lmdesc) { dev_err(&pdev->dev, "Can't allocate memory (lmdesc)\n"); return -ENOMEM; @@ -862,7 +841,26 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, INIT_LIST_HEAD(&channel->ld_free); INIT_LIST_HEAD(&channel->ld_active); - return 0; + /* Request the channel interrupt. */ + scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); + channel->irq = platform_get_irq_byname(pdev, pdev_irqname); + if (channel->irq < 0) + return channel->irq; + + irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u", + dev_name(dmac->dev), index); + if (!irqname) + return -ENOMEM; + + ret = devm_request_threaded_irq(dmac->dev, channel->irq, + rz_dmac_irq_handler, + rz_dmac_irq_handler_thread, 0, + irqname, channel); + if (ret) + dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", + channel->irq, ret); + + return ret; } static void rz_dmac_put_device(void *_dev) @@ -932,7 +930,6 @@ static int rz_dmac_probe(struct platform_device *pdev) const char *irqname = "error"; struct dma_device *engine; struct rz_dmac *dmac; - int channel_num; int ret; int irq; u8 i; @@ -964,19 +961,6 @@ static int rz_dmac_probe(struct platform_device *pdev) return PTR_ERR(dmac->ext_base); } - /* Register interrupt handler for error */ - irq = platform_get_irq_byname(pdev, irqname); - if (irq < 0) - return irq; - - ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, - irqname, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", - irq, ret); - return ret; - } - /* Initialize the channels. */ INIT_LIST_HEAD(&dmac->engine.channels); @@ -1002,6 +986,21 @@ static int rz_dmac_probe(struct platform_device *pdev) goto err; } + /* Register interrupt handler for error */ + irq = platform_get_irq_byname(pdev, irqname); + if (irq < 0) { + ret = irq; + goto err; + } + + ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, + irqname, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", + irq, ret); + goto err; + } + /* Register the DMAC as a DMA provider for DT. */ ret = of_dma_controller_register(pdev->dev.of_node, rz_dmac_of_xlate, NULL); @@ -1040,16 +1039,6 @@ static int rz_dmac_probe(struct platform_device *pdev) dma_register_err: of_dma_controller_free(pdev->dev.of_node); err: - channel_num = i ? i - 1 : 0; - for (i = 0; i < channel_num; i++) { - struct rz_dmac_chan *channel = &dmac->channels[i]; - - dma_free_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - channel->lmdesc.base, - channel->lmdesc.base_dma); - } - reset_control_assert(dmac->rstc); err_pm_runtime_put: pm_runtime_put(&pdev->dev); @@ -1062,18 +1051,9 @@ static int rz_dmac_probe(struct platform_device *pdev) static void rz_dmac_remove(struct platform_device *pdev) { struct rz_dmac *dmac = platform_get_drvdata(pdev); - unsigned int i; dma_async_device_unregister(&dmac->engine); of_dma_controller_free(pdev->dev.of_node); - for (i = 0; i < dmac->n_channels; i++) { - struct rz_dmac_chan *channel = &dmac->channels[i]; - - dma_free_coherent(&pdev->dev, - sizeof(struct rz_lmdesc) * DMAC_NR_LMDESC, - channel->lmdesc.base, - channel->lmdesc.base_dma); - } reset_control_assert(dmac->rstc); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); From 5f4de3c717d34a24d555af581947742980778c02 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 3 Jun 2026 17:37:49 +0900 Subject: [PATCH 2252/3297] gpu: host1x: Fix use-after-free in host1x_bo_clear_cached_mappings [ Upstream commit 266cddf7bd0f6c79b6c0633aef742a22bf70265b ] __host1x_bo_unpin() drops the last reference to the mapping and frees it, so we can't dereference mapping afterwards. The cache itself outlives the mapping, so use the cache local variable instead. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-tegra/ah6ErK6f4kVudVIA@stanley.mountain/T/#u Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260603-host1x-bocache-leak-fix-v1-1-494101dbfd30@nvidia.com Signed-off-by: Sasha Levin --- drivers/gpu/host1x/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 3801eab63282f..3313ead8bfcc2 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -1001,10 +1001,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo) if (WARN_ON(!cache)) continue; - mutex_lock(&mapping->cache->lock); + mutex_lock(&cache->lock); WARN_ON(kref_read(&mapping->ref) != 1); __host1x_bo_unpin(&mapping->ref); - mutex_unlock(&mapping->cache->lock); + mutex_unlock(&cache->lock); } } EXPORT_SYMBOL(host1x_bo_clear_cached_mappings); From 0892b427c4b8b7b416467f50b74305e416f4235c Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Fri, 15 May 2026 11:34:52 +0900 Subject: [PATCH 2253/3297] crypto: tegra - Don't touch bo refcount in host1x bo pin/unpin [ Upstream commit f8c9c57d750346abd213ffed2ae3cacb0268e9f1 ] Since commit "gpu: host1x: Allow entries in BO caches to be freed", host1x_bo_pin() and host1x_bo_unpin() handle the bo's refcount themselves. .pin/.unpin callbacks should not adjust it. Signed-off-by: Mikko Perttunen Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin --- drivers/crypto/tegra/tegra-se-main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c index 2755f19ad05e9..77b923a6b7c18 100644 --- a/drivers/crypto/tegra/tegra-se-main.c +++ b/drivers/crypto/tegra/tegra-se-main.c @@ -52,7 +52,7 @@ tegra_se_cmdbuf_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_dire return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -93,7 +93,6 @@ static void tegra_se_cmdbuf_unpin(struct host1x_bo_mapping *map) dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0); sg_free_table(map->sgt); kfree(map->sgt); - host1x_bo_put(map->bo); kfree(map); } From 6cbed4be9a6ca391f8dca3cf8f285d144e29b747 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 26 May 2026 10:14:04 -0400 Subject: [PATCH 2254/3297] xprtrdma: Clear receive-side ownership pointers on release [ Upstream commit 2ae8e7afbc63bf84243367f89eb43571f0345a74 ] Three small ownership-state cleanups land the transport in a state that lets future reviewers reason about each pointer locally rather than tracing the whole reply path: rpcrdma_rep_put() clears rep->rr_rqst before the rep enters rb_free_reps so that no rep on the free list still carries a stale rqst pointer. rpcrdma_reply_handler() and rpcrdma_unpin_rqst() are the only sites that set rr_rqst; rpcrdma_reply_handler() hands the rep through rpcrdma_rep_put(), and rpcrdma_unpin_rqst() NULLs rr_rqst directly because its error path abandons the rep for teardown cleanup rather than returning it to rb_free_reps. rpcrdma_reply_put() NULLs req->rl_reply before calling rpcrdma_rep_put(). The previous order placed the rep on rb_free_reps while req->rl_reply still pointed at it; the window was harmless because xprt_rdma_free_slot() holds the req exclusively across the pair, but closing it makes the invariant 'rep on rb_free_reps implies no req references it' strictly checkable. rpcrdma_sendctx_unmap() and rpcrdma_sendctx_cancel() clear req->rl_sendctx after dropping the sendctx pointer in the sendctx ring. Without this, req->rl_sendctx survives across Send completion and points at a sendctx that may already have been reassigned by rpcrdma_sendctx_get_locked() to a different req. No caller dereferences the stale pointer today -- rpcrdma_prepare_send_sges() overwrites it before the next Send -- but a NULL is a more honest representation of 'the Send is no longer outstanding' and lets the assertion patch that follows trip on any future regression. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin --- net/sunrpc/xprtrdma/rpc_rdma.c | 4 ++++ net/sunrpc/xprtrdma/verbs.c | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 5d1f96bffd749..e2694fb03ba1b 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -542,6 +542,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) rpcrdma_sendctx_dma_unmap(sc); sc->sc_req = NULL; + req->rl_sendctx = NULL; rpcrdma_req_put(req); } @@ -550,8 +551,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc) */ static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc) { + struct rpcrdma_req *req = sc->sc_req; + rpcrdma_sendctx_dma_unmap(sc); sc->sc_req = NULL; + req->rl_sendctx = NULL; } /* Prepare an SGE for the RPC-over-RDMA transport header. diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index efd91ed249fab..e2d3cee825cd7 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1080,9 +1080,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf) * @buf: buffer pool * @rep: rep to release * + * The rep's transient association with an rpc_rqst, established + * by rpcrdma_reply_handler() and torn down here, must not survive + * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free + * list to re-post them, and a non-NULL rr_rqst on a free-listed rep + * would imply the rep is still referenced by a req. */ void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep) { + rep->rr_rqst = NULL; llist_add(&rep->rr_node, &buf->rb_free_reps); } @@ -1265,9 +1271,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt) */ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req) { - if (req->rl_reply) { - rpcrdma_rep_put(buffers, req->rl_reply); + struct rpcrdma_rep *rep = req->rl_reply; + + if (rep) { req->rl_reply = NULL; + rpcrdma_rep_put(buffers, rep); } /* I2: rl_reply NULL after the put closes the * 'rep on rb_free_reps still referenced by req' window. From d03a740e087de7dcb2a26dc1123377bd3d1d84ca Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Wed, 8 Apr 2026 09:03:59 -0700 Subject: [PATCH 2255/3297] Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data() [ Upstream commit 875115b82c295277b81b6dfee7debc725f44e854 ] The `ims_pcu_process_data()` processes incoming URB data byte by byte. However, it fails to check if the `read_pos` index exceeds IMS_PCU_BUF_SIZE. If a malicious USB device sends a packet larger than IMS_PCU_BUF_SIZE, `read_pos` will increment indefinitely. Moreover, since `read_pos` is located immediately after `read_buf`, the attacker can overwrite `read_pos` itself to arbitrarily control the index. This manipulated `read_pos` is subsequently used in `ims_pcu_handle_response()` to copy data into `cmd_buf`, leading to a heap buffer overflow. Specifically, an attacker can overwrite the `cmd_done.wait.head` located at offset 136 relative to `cmd_buf` in the `ims_pcu_handle_response()`. Consequently, when the driver calls `complete(&pcu->cmd_done)`, it triggers a control flow hijack by using the manipulated pointer. Fix this by adding a bounds check for `read_pos` before writing to `read_buf`. If the packet is too long, discard it, log a warning, and reset the parser state. Fixes: 628329d524743 ("Input: add IMS Passenger Control Unit driver") Co-developed-by: Sanghoon Choi Signed-off-by: Sanghoon Choi Signed-off-by: Seungjin Bae Link: https://patch.msgid.link/20251221211442.841549-2-eeodqql09@gmail.com [dtor: factor out resetting packet state, reset checksum as well] Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin --- drivers/input/misc/ims-pcu.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 94113e14bf599..7c837094c498e 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -448,6 +448,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu) } } +static void ims_pcu_reset_packet(struct ims_pcu *pcu) +{ + pcu->have_stx = true; + pcu->have_dle = false; + pcu->read_pos = 0; + pcu->check_sum = 0; +} + static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) { int i; @@ -460,6 +468,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) continue; if (pcu->have_dle) { + if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { + dev_warn(pcu->dev, + "Packet too long (%d bytes), discarding\n", + pcu->read_pos); + ims_pcu_reset_packet(pcu); + continue; + } + pcu->have_dle = false; pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; @@ -472,10 +488,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) dev_warn(pcu->dev, "Unexpected STX at byte %d, discarding old data\n", pcu->read_pos); + ims_pcu_reset_packet(pcu); pcu->have_stx = true; - pcu->have_dle = false; - pcu->read_pos = 0; - pcu->check_sum = 0; break; case IMS_PCU_PROTOCOL_DLE: @@ -495,12 +509,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) ims_pcu_handle_response(pcu); } - pcu->have_stx = false; - pcu->have_dle = false; - pcu->read_pos = 0; + ims_pcu_reset_packet(pcu); break; default: + if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { + dev_warn(pcu->dev, + "Packet too long (%d bytes), discarding\n", + pcu->read_pos); + ims_pcu_reset_packet(pcu); + continue; + } + pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; break; From afe9cda0862aa82b5d1d5152d7193319d7c18137 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 22 May 2026 10:29:41 -0700 Subject: [PATCH 2256/3297] Input: ims-pcu - fix logic error in packet reset [ Upstream commit 2c9b85a14abb4811e8d4773ccd13559e59792efb ] ims_pcu_reset_packet() incorrectly sets have_stx to true, which implies that the start-of-packet delimiter has already been received. This causes the protocol parser to skip waiting for the next STX byte and potentially process garbage data. Correctly set have_stx to false when resetting the packet state. Fixes: 875115b82c29 ("Input: ims-pcu - fix heap-buffer-overflow in ims_pcu_process_data()") Cc: stable@vger.kernel.org Reported-by: Sashiko bot Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin --- 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 7c837094c498e..168662a2cc890 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -450,7 +450,7 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu) static void ims_pcu_reset_packet(struct ims_pcu *pcu) { - pcu->have_stx = true; + pcu->have_stx = false; pcu->have_dle = false; pcu->read_pos = 0; pcu->check_sum = 0; From 903f2edc0477025dbb8363187a60fec06cee65bb Mon Sep 17 00:00:00 2001 From: Junxi Qian Date: Sat, 25 Jul 2026 17:04:08 +0800 Subject: [PATCH 2257/3297] fuse: fix writeback array overflow when max_pages is one [ Upstream commit c3880a7b10e487e033dc6f388bda118436566f7a ] fuse_iomap_writeback_range() appends one folio pointer and one fuse_folio_desc for every dirty range that is merged into the current writeback request. The merge decision checks the byte budget against fc->max_pages and fc->max_write, but it does not check whether the folio and descriptor arrays still have another free slot. This is not sufficient for fuseblk, where the filesystem block size can be smaller than PAGE_SIZE. With writeback cache enabled and max_pages negotiated as one, contiguous sub-page dirty ranges can fit within the byte budget while spanning more than one folio. The next append can then write past the one-slot folios and descs arrays. Split the request when the number of already attached folios has reached fc->max_pages. This keeps the folio/descriptor slot accounting in sync with the send decision. Fixes: ef7e7cbb323f ("fuse: use iomap for writeback") Cc: stable@vger.kernel.org Reviewed-by: Joanne Koong Signed-off-by: Junxi Qian Link: https://patch.msgid.link/20260506122415.205340-1-qjx1298677004@gmail.com Acked-by: Miklos Szeredi Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/fuse/file.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 79d1b502e7319..585dd90361b65 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2140,7 +2140,10 @@ static bool fuse_writepage_need_send(struct fuse_conn *fc, loff_t pos, WARN_ON(!ap->num_folios); - /* Reached max pages */ + /* Reached max pages or max folio slots */ + if (ap->num_folios >= fc->max_pages) + return true; + if ((bytes + PAGE_SIZE - 1) >> PAGE_SHIFT > fc->max_pages) return true; From 0b9858484d09687519a9129b0f70f73c8dc7ffc2 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:55 +0530 Subject: [PATCH 2258/3297] arm64: tegra: Remove fallback compatible for GPCDMA [ Upstream commit ee7863e43228a3143398dc5bbb943c9a735a8fca ] Remove the fallback compatible string "nvidia,tegra186-gpcdma" for GPCDMA in Tegra264. Tegra186 compatible cannot work on Tegra264 because of the register offset changes and absence of the reset property. Fixes: 65ef237e4810 ("arm64: tegra: Add Tegra264 support") Signed-off-by: Akhil R Reviewed-by: Jon Hunter Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/nvidia/tegra264.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi index 872a69553e3c8..b16d380209a50 100644 --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi @@ -50,7 +50,7 @@ }; gpcdma: dma-controller@8400000 { - compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma"; + compatible = "nvidia,tegra264-gpcdma"; reg = <0x0 0x08400000 0x0 0x210000>; interrupts = , , From 4c22b4e3ff5025055c58e1b526ecdfdd16d0d6cf Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Wed, 21 Jan 2026 16:15:34 +0530 Subject: [PATCH 2259/3297] arm64: tegra: Fix CPU compatible string to cortex-a78ae on Tegra234 [ Upstream commit 0dfa1e960f86e032007882b032c5cc7d14ebe73e ] The Tegra234 SoC uses Cortex-A78AE cores, not Cortex-A78. Update the compatible string for all CPU nodes to match the actual hardware. Tegra234 hardware reports: # head /proc/cpuinfo | egrep 'implementer|part' CPU implementer : 0x41 CPU part : 0xd42 Which maps to (from arch/arm64/include/asm/cputype.h): #define ARM_CPU_IMP_ARM 0x41 #define ARM_CPU_PART_CORTEX_A78AE 0xD42 Fixes: a12cf5c339b08 ("arm64: tegra: Describe Tegra234 CPU hierarchy") Signed-off-by: Sumit Gupta Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin --- arch/arm64/boot/dts/nvidia/tegra234.dtsi | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi index 5b245977d6a03..345587fa1d9ad 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi @@ -5345,7 +5345,7 @@ #size-cells = <0>; cpu0_0: cpu@0 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00000>; @@ -5364,7 +5364,7 @@ }; cpu0_1: cpu@100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00100>; @@ -5383,7 +5383,7 @@ }; cpu0_2: cpu@200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00200>; @@ -5402,7 +5402,7 @@ }; cpu0_3: cpu@300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00300>; @@ -5421,7 +5421,7 @@ }; cpu1_0: cpu@10000 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10000>; @@ -5440,7 +5440,7 @@ }; cpu1_1: cpu@10100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10100>; @@ -5459,7 +5459,7 @@ }; cpu1_2: cpu@10200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10200>; @@ -5478,7 +5478,7 @@ }; cpu1_3: cpu@10300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10300>; @@ -5497,7 +5497,7 @@ }; cpu2_0: cpu@20000 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20000>; @@ -5516,7 +5516,7 @@ }; cpu2_1: cpu@20100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20100>; @@ -5535,7 +5535,7 @@ }; cpu2_2: cpu@20200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20200>; @@ -5554,7 +5554,7 @@ }; cpu2_3: cpu@20300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20300>; From 4cc4d6fb08e75725df5577cf44b66099a4851ab8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2026 20:18:23 +0000 Subject: [PATCH 2260/3297] net: dropreason: add SKB_DROP_REASON_RECURSION_LIMIT [ Upstream commit d15d3de94a4766fb43d7fe7a72ed0479fb268131 ] ip[6]tunnel_xmit() can drop packets if a too deep recursion level is detected. Add SKB_DROP_REASON_RECURSION_LIMIT drop reason. We will use this reason later in __dev_queue_xmit(). Signed-off-by: Eric Dumazet Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260312201824.203093-2-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 6860b467f569 ("xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()") Signed-off-by: Sasha Levin --- include/net/dropreason-core.h | 3 +++ include/net/ip6_tunnel.h | 2 +- net/ipv4/ip_tunnel_core.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h index 58d91ccc56e0b..de74e537b4ede 100644 --- a/include/net/dropreason-core.h +++ b/include/net/dropreason-core.h @@ -129,6 +129,7 @@ FN(DUALPI2_STEP_DROP) \ FN(PSP_INPUT) \ FN(PSP_OUTPUT) \ + FN(RECURSION_LIMIT) \ FNe(MAX) /** @@ -616,6 +617,8 @@ enum skb_drop_reason { SKB_DROP_REASON_PSP_INPUT, /** @SKB_DROP_REASON_PSP_OUTPUT: PSP output checks failed */ SKB_DROP_REASON_PSP_OUTPUT, + /** @SKB_DROP_REASON_RECURSION_LIMIT: Dead loop on virtual device. */ + SKB_DROP_REASON_RECURSION_LIMIT, /** * @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which * shouldn't be used as a real 'reason' - only for tracing code gen diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 359b595f1df93..b99805ee2fd14 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -162,7 +162,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, dev->name); DEV_STATS_INC(dev, tx_errors); } - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_RECURSION_LIMIT); return; } diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 4b5fd4b13722c..fc993c78cbcc5 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -65,7 +65,7 @@ void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, DEV_STATS_INC(dev, tx_errors); } ip_rt_put(rt); - kfree_skb(skb); + kfree_skb_reason(skb, SKB_DROP_REASON_RECURSION_LIMIT); return; } From 23bbb9eafec7a49197d7a1be1919480d25add1cd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Mar 2026 20:18:24 +0000 Subject: [PATCH 2261/3297] net: plumb drop reasons to __dev_queue_xmit() [ Upstream commit 045f977dd4ebdd3ad8e96cf684917adfc5805adb ] Add drop reasons to __dev_queue_xmit(): - SKB_DROP_REASON_DEV_READY : device is not UP. - SKB_DROP_REASON_RECURSION_LIMIT : recursion limit on virtual device is hit. Also add an unlikely() for the SKB_DROP_REASON_DEV_READY case, and reduce indentation level. Signed-off-by: Eric Dumazet Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260312201824.203093-3-edumazet@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 6860b467f569 ("xfrm: propagate -EINPROGRESS from validate_xmit_xfrm()") Signed-off-by: Sasha Levin --- net/core/dev.c | 83 ++++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 8dee703a0916e..246d654551ae5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4702,9 +4702,10 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) { struct net_device *dev = skb->dev; struct netdev_queue *txq = NULL; - struct Qdisc *q; - int rc = -ENOMEM; + enum skb_drop_reason reason; + int cpu, rc = -ENOMEM; bool again = false; + struct Qdisc *q; skb_reset_mac_header(skb); skb_assert_len(skb); @@ -4773,59 +4774,61 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) * Check this and shot the lock. It is not prone from deadlocks. *Either shot noqueue qdisc, it is even simpler 8) */ - if (dev->flags & IFF_UP) { - int cpu = smp_processor_id(); /* ok because BHs are off */ + if (unlikely(!(dev->flags & IFF_UP))) { + reason = SKB_DROP_REASON_DEV_READY; + goto drop; + } - if (!netif_tx_owned(txq, cpu)) { - bool is_list = false; + cpu = smp_processor_id(); /* ok because BHs are off */ - if (dev_xmit_recursion()) - goto recursion_alert; + if (likely(!netif_tx_owned(txq, cpu))) { + bool is_list = false; - skb = validate_xmit_skb(skb, dev, &again); - if (!skb) - goto out; + if (dev_xmit_recursion()) + goto recursion_alert; - HARD_TX_LOCK(dev, txq, cpu); + skb = validate_xmit_skb(skb, dev, &again); + if (!skb) + goto out; - if (!netif_xmit_stopped(txq)) { - is_list = !!skb->next; + HARD_TX_LOCK(dev, txq, cpu); - dev_xmit_recursion_inc(); - skb = dev_hard_start_xmit(skb, dev, txq, &rc); - dev_xmit_recursion_dec(); + if (!netif_xmit_stopped(txq)) { + is_list = !!skb->next; - /* GSO segments a single SKB into - * a list of frames. TCP expects error - * to mean none of the data was sent. - */ - if (is_list) - rc = NETDEV_TX_OK; - } - HARD_TX_UNLOCK(dev, txq); - if (!skb) /* xmit completed */ - goto out; + dev_xmit_recursion_inc(); + skb = dev_hard_start_xmit(skb, dev, txq, &rc); + dev_xmit_recursion_dec(); - net_crit_ratelimited("Virtual device %s asks to queue packet!\n", - dev->name); - /* NETDEV_TX_BUSY or queue was stopped */ - if (!is_list) - rc = -ENETDOWN; - } else { - /* Recursion is detected! It is possible, - * unfortunately + /* GSO segments a single SKB into a list of frames. + * TCP expects error to mean none of the data was sent. */ -recursion_alert: - net_crit_ratelimited("Dead loop on virtual device %s, fix it urgently!\n", - dev->name); - rc = -ENETDOWN; + if (is_list) + rc = NETDEV_TX_OK; } + HARD_TX_UNLOCK(dev, txq); + if (!skb) /* xmit completed */ + goto out; + + net_crit_ratelimited("Virtual device %s asks to queue packet!\n", + dev->name); + /* NETDEV_TX_BUSY or queue was stopped */ + if (!is_list) + rc = -ENETDOWN; + } else { + /* Recursion is detected! It is possible unfortunately. */ +recursion_alert: + net_crit_ratelimited("Dead loop on virtual device %s, fix it urgently!\n", + dev->name); + rc = -ENETDOWN; } + reason = SKB_DROP_REASON_RECURSION_LIMIT; +drop: rcu_read_unlock_bh(); dev_core_stats_tx_dropped_inc(dev); - kfree_skb_list(skb); + kfree_skb_list_reason(skb, reason); return rc; out: rcu_read_unlock_bh(); From eae16fbc7ce20246b46e349e7ec0043a0f181b71 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sun, 21 Jun 2026 12:03:26 +0200 Subject: [PATCH 2262/3297] xfrm: propagate -EINPROGRESS from validate_xmit_xfrm() [ Upstream commit 6860b467f569f732b11cbc588ae7e195e90e7e23 ] validate_xmit_xfrm() returns NULL both when a packet is dropped and when it is stolen by async crypto (-EINPROGRESS from ->xmit()). Callers cannot distinguish the two cases. f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.") changed the semantics of a NULL return from "dropped" to "stolen or dropped", but __dev_queue_xmit() was not updated. On virtual/bridge interfaces (noqueue qdisc) __dev_queue_xmit() initialises rc=-ENOMEM and jumps to out: when skb is NULL, returning -ENOMEM to the caller even though the packet will be delivered correctly via xfrm_dev_resume(). Return ERR_PTR(-EINPROGRESS) from validate_xmit_xfrm() for the async case so callers can tell it apart from a real drop. Update __dev_queue_xmit() to handle ERR_PTR(-EINPROGRESS) from validate_xmit_skb() correctly. Update validate_xmit_skb_list() to use IS_ERR_OR_NULL() so that ERR_PTR(-EINPROGRESS) is not mistakenly added to the transmitted list. Fixes: f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.") Suggested-by: Sabrina Dubroca Signed-off-by: Petr Wozniak Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/core/dev.c | 10 ++++++++-- net/xfrm/xfrm_device.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 246d654551ae5..a83083e8761b1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4005,6 +4005,9 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb, return NULL; } +/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS) + * if stolen by async xfrm crypto (delivered via xfrm_dev_resume()). + */ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again) { netdev_features_t features; @@ -4076,7 +4079,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d skb->prev = skb; skb = validate_xmit_skb(skb, dev, again); - if (!skb) + if (IS_ERR_OR_NULL(skb)) continue; if (!head) @@ -4788,8 +4791,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) goto recursion_alert; skb = validate_xmit_skb(skb, dev, &again); - if (!skb) + if (IS_ERR_OR_NULL(skb)) { + if (PTR_ERR(skb) == -EINPROGRESS) + rc = NET_XMIT_SUCCESS; goto out; + } HARD_TX_LOCK(dev, txq, cpu); diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 550457e4c4f01..8b526e3676389 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur err = x->type_offload->xmit(x, skb, esp_features); if (err) { if (err == -EINPROGRESS) - return NULL; + return ERR_PTR(-EINPROGRESS); XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); kfree_skb(skb); @@ -224,7 +224,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur pskb = skb2; } - return skb; + return skb ? skb : ERR_PTR(-EINPROGRESS); } EXPORT_SYMBOL_GPL(validate_xmit_xfrm); From 33e1b0d25ca0d2818c635ff80e6aa0d295e08a98 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sun, 21 Jun 2026 12:03:27 +0200 Subject: [PATCH 2263/3297] xfrm: fix stale skb->prev after async crypto steals a GSO segment [ Upstream commit 3f4c3919baf0944ad96580467c302bc6c7758b00 ] skb_gso_segment() leaves the segment list head with ->prev pointing at the last segment, an invariant validate_xmit_skb_list() relies on when it sets its tail pointer (tail = skb->prev). When validate_xmit_xfrm() walks a GSO list and some segments are stolen by async crypto (->xmit() returns -EINPROGRESS), those segments are unlinked from the list but the head ->prev is never updated. If the last segment is the one stolen, the returned head still has ->prev pointing at it, even though it is now owned by the crypto engine and may be freed. validate_xmit_skb_list() later does tail->next = skb, writing through that stale pointer -- a use-after-free. Repoint skb->prev at the last retained segment before returning. Fixes: f53c723902d1 ("net: Add asynchronous callbacks for xfrm on layer 2.") Signed-off-by: Petr Wozniak Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_device.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 8b526e3676389..4719aba331cd6 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -224,6 +224,14 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur pskb = skb2; } + /* skb_gso_segment() set skb->prev to the last segment, but async + * crypto may have stolen it above without updating ->prev. Repoint + * it at the last retained segment so validate_xmit_skb_list() does + * not chain onto a segment now owned by the crypto engine. + */ + if (skb) + skb->prev = pskb; + return skb ? skb : ERR_PTR(-EINPROGRESS); } EXPORT_SYMBOL_GPL(validate_xmit_xfrm); From 59114e0ff6e3a61efcd80d20b91d0761b7a34866 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 2 Jun 2026 21:54:06 +0000 Subject: [PATCH 2264/3297] firmware: arm_ffa: Respect firmware advertised RX/TX buffer size limits [ Upstream commit 53716a4d745f1dac7aff33f3d1494b701eb2f888 ] FFA_FEATURES reports the minimum size and alignment boundary required for RXTX_MAP. In FF-A v1.2 and later it can also report a maximum buffer size, with zero meaning that no maximum is enforced. The driver only used the minimum value and then rounded it up to PAGE_SIZE before invoking RXTX_MAP after commit 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP"). On systems where PAGE_SIZE is larger than the advertised minimum, this can exceed a non-zero maximum reported by firmware. Older implementations do not advertise a maximum and may also reject the rounded-up size. Decode the maximum size and clamp the page-aligned minimum to it when it is present. If no maximum is advertised and RXTX_MAP rejects the rounded size with INVALID_PARAMETERS, retry with the advertised minimum size. Record drv_info->rxtx_bufsz only after RXTX_MAP succeeds so it reflects the size registered with firmware. While there, also update RXTX_MAP_MIN_BUFSZ() to use FIELD_GET() for consistency. Fixes: 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") Suggested-by: Sudeep Holla Signed-off-by: Seth Forshee Link: https://patch.msgid.link/20260602-b4-ffa-rxtx-map-fixes-v2-1-7cb06508da84@nvidia.com (sudeep.holla: Minor rewording subject and commit message) Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 3fbc75caf0c50..c76eabb8436a8 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,9 @@ (FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r))) #define RXTX_MAP_MIN_BUFSZ_MASK GENMASK(1, 0) -#define RXTX_MAP_MIN_BUFSZ(x) ((x) & RXTX_MAP_MIN_BUFSZ_MASK) +#define RXTX_MAP_MAX_BUFSZ_MASK GENMASK(31, 16) +#define RXTX_MAP_MIN_BUFSZ(x) (FIELD_GET(RXTX_MAP_MIN_BUFSZ_MASK, (x))) +#define RXTX_MAP_MAX_BUFSZ(x) (FIELD_GET(RXTX_MAP_MAX_BUFSZ_MASK, (x))) #define FFA_MAX_NOTIFICATIONS 64 @@ -2088,7 +2091,7 @@ static int __init ffa_init(void) { int ret; u32 buf_sz; - size_t rxtx_bufsz = SZ_4K; + size_t rxtx_min_bufsz = SZ_4K, rxtx_max_bufsz = 0, rxtx_bufsz; ret = ffa_transport_init(&invoke_ffa_fn); if (ret) @@ -2111,15 +2114,18 @@ static int __init ffa_init(void) ret = ffa_features(FFA_FN_NATIVE(RXTX_MAP), 0, &buf_sz, NULL); if (!ret) { if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 1) - rxtx_bufsz = SZ_64K; + rxtx_min_bufsz = SZ_64K; else if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 2) - rxtx_bufsz = SZ_16K; + rxtx_min_bufsz = SZ_16K; else - rxtx_bufsz = SZ_4K; + rxtx_min_bufsz = SZ_4K; + + rxtx_max_bufsz = RXTX_MAP_MAX_BUFSZ(buf_sz) * SZ_4K; + if (rxtx_max_bufsz != 0 && rxtx_max_bufsz < rxtx_min_bufsz) + rxtx_max_bufsz = rxtx_min_bufsz; } - rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz); - drv_info->rxtx_bufsz = rxtx_bufsz; + rxtx_bufsz = min_not_zero(PAGE_ALIGN(rxtx_min_bufsz), rxtx_max_bufsz); drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); if (!drv_info->rx_buffer) { ret = -ENOMEM; @@ -2135,10 +2141,17 @@ static int __init ffa_init(void) ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), virt_to_phys(drv_info->rx_buffer), rxtx_bufsz / FFA_PAGE_SIZE); + if (ret == -EINVAL && !rxtx_max_bufsz && rxtx_min_bufsz < rxtx_bufsz) { + rxtx_bufsz = rxtx_min_bufsz; + ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), + virt_to_phys(drv_info->rx_buffer), + rxtx_bufsz / FFA_PAGE_SIZE); + } if (ret) { pr_err("failed to register FFA RxTx buffers\n"); goto free_pages; } + drv_info->rxtx_bufsz = rxtx_bufsz; mutex_init(&drv_info->rx_lock); mutex_init(&drv_info->tx_lock); From 98d2d468b4faa1fdc68c0c6c238389906ee3490c Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 6 Jun 2026 16:01:55 -0400 Subject: [PATCH 2265/3297] IB/mad: Drop unmatched RMPP responses before reassembly [ Upstream commit d2e52d610b9b09694261632340b801a421e0b0c5 ] Kernel-handled RMPP receive processing starts reassembly for active DATA responses before the response is matched to an outstanding send. The normal match happens later, after ib_process_rmpp_recv_wc() has either assembled a complete message or consumed the segment. That ordering lets an unsolicited response that routes to a kernel RMPP agent by the high TID bits allocate or extend RMPP receive state before the full TID and source address are checked against a real request. A reordered burst can therefore reach the receive-side insertion path even though the response would not match any send. For kernel-handled RMPP DATA responses, require the existing ib_find_send_mad() match before entering RMPP reassembly. The matcher already checks the full TID, management class and source address/GID against the agent wait, backlog and in-flight send lists. If there is no match, drop the response without creating RMPP state. This leaves the RMPP window behavior unchanged and only rejects responses that have no corresponding request. Fixes: fa619a77046b ("[PATCH] IB: Add RMPP implementation") Assisted-by: Codex:gpt-5-5-xhigh Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/3170ff3bc389a930bb1641f2caa394a0b2241579.1780774907.git.michael.bommarito@gmail.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/core/mad.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 8f26bfb695861..5150cd53d4435 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -2031,6 +2031,24 @@ void ib_mark_mad_done(struct ib_mad_send_wr_private *mad_send_wr) change_mad_state(mad_send_wr, IB_MAD_STATE_EARLY_RESP); } +static bool is_kernel_rmpp_data_response(struct ib_mad_agent_private *agent, + struct ib_mad_recv_wc *mad_recv_wc) +{ + const struct ib_mad_hdr *mad_hdr = &mad_recv_wc->recv_buf.mad->mad_hdr; + struct ib_rmpp_mad *rmpp_mad; + + if (!ib_mad_kernel_rmpp_agent(&agent->agent) || + !ib_response_mad(mad_hdr) || + !ib_is_mad_class_rmpp(mad_hdr->mgmt_class)) + return false; + + rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; + + return (ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & + IB_MGMT_RMPP_FLAG_ACTIVE) && + rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA; +} + static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, struct ib_mad_recv_wc *mad_recv_wc) { @@ -2050,6 +2068,18 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, } list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list); + if (is_kernel_rmpp_data_response(mad_agent_priv, mad_recv_wc)) { + spin_lock_irqsave(&mad_agent_priv->lock, flags); + mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc); + spin_unlock_irqrestore(&mad_agent_priv->lock, flags); + + if (!mad_send_wr) { + ib_free_recv_mad(mad_recv_wc); + deref_mad_agent(mad_agent_priv); + return; + } + } + if (ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)) { mad_recv_wc = ib_process_rmpp_recv_wc(mad_agent_priv, mad_recv_wc); From fdb53a9b260718000a242fdabb05873749f46fbb Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 09:53:17 +0800 Subject: [PATCH 2266/3297] mtd: mtdswap: remove debugfs stats file on teardown [ Upstream commit 66fb31358108d10245b9e4ef0eef3e7d9747055e ] mtdswap_add_debugfs() creates an mtdswap_stats debugfs file under the per-MTD debugfs directory, but mtdswap_remove_dev() never removes it before freeing the mtdswap_dev. Store the returned dentry and remove it during device teardown before the driver-private state is freed. Fixes: a32159024620 ("mtd: Add mtdswap block driver") Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Sasha Levin --- drivers/mtd/mtdswap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index d8f2e5be2d315..42ff7deace80c 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -125,6 +125,7 @@ struct mtdswap_dev { char *page_buf; char *oob_buf; + struct dentry *debugfs_stats; }; struct mtdswap_oobdata { @@ -1262,7 +1263,8 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d) if (IS_ERR_OR_NULL(root)) return -1; - debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops); + d->debugfs_stats = debugfs_create_file("mtdswap_stats", 0400, root, + d, &mtdswap_fops); return 0; } @@ -1463,6 +1465,7 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev) { struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev); + debugfs_remove(d->debugfs_stats); del_mtd_blktrans_dev(dev); mtdswap_cleanup(d); kfree(d); From 0ac9c7d9ccfd775c7033cf79a095f335d60214c5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 21:57:29 +0800 Subject: [PATCH 2267/3297] mtd: nand: mtk-ecc: stop on ECC idle timeouts [ Upstream commit 16f7ec8d5dc100eafd2c8e06cd30340a30b104a1 ] mtk_ecc_wait_idle() logs when the encoder or decoder does not become idle, but returns void. Callers can therefore configure a non-idle ECC engine or read parity bytes after an unconfirmed encoder idle state. Return the idle poll result and propagate it from the enable and encode paths that require the engine to be idle before continuing. Fixes: 1d6b1e464950 ("mtd: mediatek: driver for MTK Smart Device") Signed-off-by: Pengpeng Hou Signed-off-by: Miquel Raynal Signed-off-by: Sasha Levin --- drivers/mtd/nand/ecc-mtk.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c index c75bb8b80cc1e..96703f0a418ea 100644 --- a/drivers/mtd/nand/ecc-mtk.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -123,8 +123,8 @@ static int mt7622_ecc_regs[] = { [ECC_DECIRQ_STA] = 0x144, }; -static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, - enum mtk_ecc_operation op) +static inline int mtk_ecc_wait_idle(struct mtk_ecc *ecc, + enum mtk_ecc_operation op) { struct device *dev = ecc->dev; u32 val; @@ -136,6 +136,8 @@ static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, if (ret) dev_warn(dev, "%s NOT idle\n", op == ECC_ENCODE ? "encoder" : "decoder"); + + return ret; } static irqreturn_t mtk_ecc_irq(int irq, void *id) @@ -312,7 +314,11 @@ int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config) return ret; } - mtk_ecc_wait_idle(ecc, op); + ret = mtk_ecc_wait_idle(ecc, op); + if (ret) { + mutex_unlock(&ecc->lock); + return ret; + } ret = mtk_ecc_config(ecc, config); if (ret) { @@ -412,7 +418,9 @@ int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, if (ret) goto timeout; - mtk_ecc_wait_idle(ecc, ECC_ENCODE); + ret = mtk_ecc_wait_idle(ecc, ECC_ENCODE); + if (ret) + goto timeout; /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ len = (config->strength * ecc->caps->parity_bits + 7) >> 3; From 5e1b2ca6b34939e70fb0785e8222b53cf060016f Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 10 Jun 2026 10:29:26 -0700 Subject: [PATCH 2268/3297] btrfs: reject free space cache with more entries than pages [ Upstream commit a2d8d5647ed854e38f941741aea45b9eb15a6350 ] When loading a v1 free space cache, __load_free_space_cache() takes num_entries and num_bitmaps straight from the on-disk btrfs_free_space_header. That header is stored in the tree_root under a key with type 0, which the tree-checker has no case for, so neither count is validated before the load trusts it. The load loops num_entries times and maps the next page whenever the current one runs out, going through io_ctl_check_crc() -> io_ctl_map_page(), which does io_ctl->pages[io_ctl->index++]. But pages[] is allocated in io_ctl_init() from the cache inode's i_size, not from num_entries: num_pages = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); io_ctl->pages = kcalloc(num_pages, sizeof(struct page *), GFP_NOFS); So if num_entries claims more records than the pages can hold, io_ctl->index runs off the end of pages[]. The write side never hits this because io_ctl_add_entry() and io_ctl_add_bitmap() both stop once io_ctl->index >= io_ctl->num_pages; the read side just never had the same check. To trigger it, take a clean cache (num_entries = here), set num_entries in the header to 0x10000, and fix up the leaf checksum so it still passes the tree-checker. The cache inode has i_size = 65536, so num_pages is 16 and pages[] is a 16-pointer (kmalloc-128) array. The load now tries to read 65536 entries, io_ctl->index walks up to 16, and pages[16] is read past the array: BUG: KASAN: slab-out-of-bounds in io_ctl_check_crc (fs/btrfs/free-space-cache.c:420 fs/btrfs/free-space-cache.c:565) Read of size 8 at addr ffff88800c833a80 by task kworker/u8:3/58 io_ctl_check_crc (fs/btrfs/free-space-cache.c:420 fs/btrfs/free-space-cache.c:565) __load_free_space_cache (fs/btrfs/free-space-cache.c:655 fs/btrfs/free-space-cache.c:820) load_free_space_cache (fs/btrfs/free-space-cache.c:1017) caching_thread (fs/btrfs/block-group.c:880) btrfs_work_helper (fs/btrfs/async-thread.c:312) process_one_work worker_thread kthread ret_from_fork free-space-cache.c:420 is io_ctl_map_page(), inlined into io_ctl_check_crc() at line 565, which is why that is the frame KASAN names. The out-of-bounds slot is then treated as a struct page and handed to crc32c(), so the bad read turns into a GP fault. Add the missing check to io_ctl_check_crc(), which is where both the entry loop and the bitmap loop end up. When num_entries is too large the load now fails like any corrupt cache: __load_free_space_cache() drops it and rebuilds the free space from the extent tree, so a valid cache is never rejected. Reported-by: Weiming Shi Fixes: 5b0e95bf607d ("Btrfs: inline checksums into the disk free space cache") Link: https://lore.kernel.org/linux-btrfs/CAPpSM+RMPByMCKXvM5QFKToxsyNccfuFLWMdD0mfd0wh2Ja62w@mail.gmail.com/ Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Qu Wenruo Signed-off-by: Xiang Mei Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/free-space-cache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index ab873bd671920..9828da12765f5 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -554,6 +554,9 @@ static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index) u32 crc = ~(u32)0; unsigned offset = 0; + if (index >= io_ctl->num_pages) + return -EIO; + if (index == 0) offset = sizeof(u32) * io_ctl->num_pages; From 60a23d4ea169e27403f3bb023bb98036797c0206 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 11 Jun 2026 15:16:21 +0100 Subject: [PATCH 2269/3297] btrfs: fix root leak if its reloc root is unexpected in merge_reloc_roots() [ Upstream commit ce6050bafb4e33377dc17fcc357736bfc351180c ] If we have an unexpected reloc_root for our root, we jump to the out label but never drop the reference we obtained for root, resulting in a leak. Add a missing btrfs_put_root() call. Fixes: 24213fa46c70 ("btrfs: do proper error handling in merge_reloc_roots") Reviewed-by: Qu Wenruo Reviewed-by: Johannes Thumshirn Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/relocation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index fc76013b1a3e0..ea68328d289cb 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1857,6 +1857,7 @@ void merge_reloc_roots(struct reloc_control *rc) * corruption, e.g. bad reloc tree key offset. */ ret = -EINVAL; + btrfs_put_root(root); goto out; } ret = merge_reloc_root(rc, root); From a84ca16ce07e749bdf32d36d0939560523e3bd42 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Fri, 14 Nov 2025 16:00:04 +0000 Subject: [PATCH 2270/3297] btrfs: use bool type for btrfs_path members used as booleans [ Upstream commit d7fe41044b3ac8f9b5965de499a13ac9ae947e79 ] Many fields of struct btrfs_path are used as booleans but their type is an unsigned int (of one 1 bit width to save space). Change the type to bool keeping the :1 suffix so that they combine with the previous u8 fields in order to save space. This makes the code more clear by using explicit true/false and more in line with the preferred style, preserving the size of the structure. Reviewed-by: Johannes Thumshirn Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Stable-dep-of: 3dcd50730814 ("btrfs: fallback to transaction csum tree on a commit root csum miss") Signed-off-by: Sasha Levin --- fs/btrfs/backref.c | 16 ++++++++-------- fs/btrfs/block-group.c | 8 ++++---- fs/btrfs/ctree.c | 28 ++++++++++++++-------------- fs/btrfs/ctree.h | 16 ++++++++-------- fs/btrfs/defrag.c | 4 ++-- fs/btrfs/dev-replace.c | 4 ++-- fs/btrfs/extent-tree.c | 8 ++++---- fs/btrfs/file-item.c | 12 ++++++------ fs/btrfs/free-space-cache.c | 4 ++-- fs/btrfs/free-space-tree.c | 4 ++-- fs/btrfs/inode-item.c | 2 +- fs/btrfs/inode.c | 4 ++-- fs/btrfs/qgroup.c | 4 ++-- fs/btrfs/raid-stripe-tree.c | 4 ++-- fs/btrfs/relocation.c | 8 ++++---- fs/btrfs/scrub.c | 20 ++++++++++---------- fs/btrfs/send.c | 14 +++++++------- fs/btrfs/tree-log.c | 20 ++++++++++---------- fs/btrfs/volumes.c | 6 +++--- fs/btrfs/xattr.c | 2 +- 20 files changed, 94 insertions(+), 94 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index e050d0938dc45..0c47b4570a7bf 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -1408,12 +1408,12 @@ static int find_parent_nodes(struct btrfs_backref_walk_ctx *ctx, if (!path) return -ENOMEM; if (!ctx->trans) { - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; } if (ctx->time_seq == BTRFS_SEQ_LAST) - path->skip_locking = 1; + path->skip_locking = true; again: head = NULL; @@ -1560,7 +1560,7 @@ static int find_parent_nodes(struct btrfs_backref_walk_ctx *ctx, btrfs_release_path(path); - ret = add_missing_keys(ctx->fs_info, &preftrees, path->skip_locking == 0); + ret = add_missing_keys(ctx->fs_info, &preftrees, !path->skip_locking); if (ret) goto out; @@ -2833,8 +2833,8 @@ struct btrfs_backref_iter *btrfs_backref_iter_alloc(struct btrfs_fs_info *fs_inf } /* Current backref iterator only supports iteration in commit root */ - ret->path->search_commit_root = 1; - ret->path->skip_locking = 1; + ret->path->search_commit_root = true; + ret->path->skip_locking = true; ret->fs_info = fs_info; return ret; @@ -3307,8 +3307,8 @@ static int handle_indirect_tree_backref(struct btrfs_trans_handle *trans, level = cur->level + 1; /* Search the tree to find parent blocks referring to the block */ - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; path->lowest_level = level; ret = btrfs_search_slot(NULL, root, tree_key, path, 0, 0); path->lowest_level = 0; diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 1e57f7d04c47e..e6bac34ebe83d 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -613,8 +613,8 @@ static int sample_block_group_extent_item(struct btrfs_caching_control *caching_ extent_root = btrfs_extent_root(fs_info, max_t(u64, block_group->start, BTRFS_SUPER_INFO_OFFSET)); - path->skip_locking = 1; - path->search_commit_root = 1; + path->skip_locking = true; + path->search_commit_root = true; path->reada = READA_FORWARD; search_offset = index * div_u64(block_group->length, max_index); @@ -744,8 +744,8 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl) * root to add free space. So we skip locking and search the commit * root, since its read-only */ - path->skip_locking = 1; - path->search_commit_root = 1; + path->skip_locking = true; + path->search_commit_root = true; path->reada = READA_FORWARD; key.objectid = last; diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 27e2adc2ee717..0f2e9f33dd0cb 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1709,9 +1709,9 @@ static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root, level = btrfs_header_level(b); /* * Ensure that all callers have set skip_locking when - * p->search_commit_root = 1. + * p->search_commit_root is true. */ - ASSERT(p->skip_locking == 1); + ASSERT(p->skip_locking); goto out; } @@ -3858,10 +3858,10 @@ static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans, } btrfs_release_path(path); - path->keep_locks = 1; - path->search_for_split = 1; + path->keep_locks = true; + path->search_for_split = true; ret = btrfs_search_slot(trans, root, &key, path, 0, 1); - path->search_for_split = 0; + path->search_for_split = false; if (ret > 0) ret = -EAGAIN; if (ret < 0) @@ -3888,11 +3888,11 @@ static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans, if (ret) goto err; - path->keep_locks = 0; + path->keep_locks = false; btrfs_unlock_up_safe(path, 1); return 0; err: - path->keep_locks = 0; + path->keep_locks = false; return ret; } @@ -4610,11 +4610,11 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key, u32 nritems; int level; int ret = 1; - int keep_locks = path->keep_locks; + const bool keep_locks = path->keep_locks; ASSERT(!path->nowait); ASSERT(path->lowest_level == 0); - path->keep_locks = 1; + path->keep_locks = true; again: cur = btrfs_read_lock_root_node(root); level = btrfs_header_level(cur); @@ -4704,7 +4704,7 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key, * 0 is returned if another key is found, < 0 if there are any errors * and 1 is returned if there are no higher keys in the tree * - * path->keep_locks should be set to 1 on the search made before + * path->keep_locks should be set to true on the search made before * calling this function. */ int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path, @@ -4803,13 +4803,13 @@ int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, next = NULL; btrfs_release_path(path); - path->keep_locks = 1; + path->keep_locks = true; if (time_seq) { ret = btrfs_search_old_slot(root, &key, path, time_seq); } else { if (path->need_commit_sem) { - path->need_commit_sem = 0; + path->need_commit_sem = false; need_commit_sem = true; if (path->nowait) { if (!down_read_trylock(&fs_info->commit_root_sem)) { @@ -4822,7 +4822,7 @@ int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, } ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); } - path->keep_locks = 0; + path->keep_locks = false; if (ret < 0) goto done; @@ -4961,7 +4961,7 @@ int btrfs_next_old_leaf(struct btrfs_root *root, struct btrfs_path *path, if (need_commit_sem) { int ret2; - path->need_commit_sem = 1; + path->need_commit_sem = true; ret2 = finish_need_commit_sem_search(path); up_read(&fs_info->commit_root_sem); if (ret2) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 16dd11c485313..692370fc07b28 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -65,21 +65,21 @@ struct btrfs_path { * set by btrfs_split_item, tells search_slot to keep all locks * and to force calls to keep space in the nodes */ - unsigned int search_for_split:1; + bool search_for_split:1; /* Keep some upper locks as we walk down. */ - unsigned int keep_locks:1; - unsigned int skip_locking:1; - unsigned int search_commit_root:1; - unsigned int need_commit_sem:1; - unsigned int skip_release_on_error:1; + bool keep_locks:1; + bool skip_locking:1; + bool search_commit_root:1; + bool need_commit_sem:1; + bool skip_release_on_error:1; /* * Indicate that new item (btrfs_search_slot) is extending already * existing item and ins_len contains only the data size and not item * header (ie. sizeof(struct btrfs_item) is not included). */ - unsigned int search_for_extension:1; + bool search_for_extension:1; /* Stop search if any locks need to be taken (for read) */ - unsigned int nowait:1; + bool nowait:1; }; #define BTRFS_PATH_AUTO_FREE(path_name) \ diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index a4cc1bc635622..2e3c011d410a6 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -472,7 +472,7 @@ static int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, memcpy(&key, &root->defrag_progress, sizeof(key)); } - path->keep_locks = 1; + path->keep_locks = true; ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION); if (ret < 0) @@ -515,7 +515,7 @@ static int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, /* * Now that we reallocated the node we can find the next key. Note that * btrfs_find_next_key() can release our path and do another search - * without COWing, this is because even with path->keep_locks = 1, + * without COWing, this is because even with path->keep_locks == true, * btrfs_search_slot() / ctree.c:unlock_up() does not keeps a lock on a * node when path->slots[node_level - 1] does not point to the last * item or a slot beyond the last item (ctree.c:unlock_up()). Therefore diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index a4eaef60549ee..b6c7da8e1bc8b 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -489,8 +489,8 @@ static int mark_block_group_to_copy(struct btrfs_fs_info *fs_info, } path->reada = READA_FORWARD; - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; key.objectid = src_dev->devid; key.type = BTRFS_DEV_EXTENT_KEY; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6ef7cc116bfcc..d2d96f366dac3 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -788,7 +788,7 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, want = extent_ref_type(parent, owner); if (insert) { extra_size = btrfs_extent_inline_ref_size(want); - path->search_for_extension = 1; + path->search_for_extension = true; } else extra_size = -1; @@ -954,7 +954,7 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, if (!path->keep_locks) { btrfs_release_path(path); - path->keep_locks = 1; + path->keep_locks = true; goto again; } @@ -975,11 +975,11 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, *ref_ret = (struct btrfs_extent_inline_ref *)ptr; out: if (path->keep_locks) { - path->keep_locks = 0; + path->keep_locks = false; btrfs_unlock_up_safe(path, 1); } if (insert) - path->search_for_extension = 0; + path->search_for_extension = false; return ret; } diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 4b7c40f05e8f9..a2f851eaadca0 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -394,8 +394,8 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) * between reading the free space cache and updating the csum tree. */ if (btrfs_is_free_space_inode(inode)) { - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; } /* @@ -423,8 +423,8 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) * from across transactions. */ if (bbio->csum_search_commit_root) { - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; down_read(&fs_info->commit_root_sem); } @@ -1168,10 +1168,10 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, } btrfs_release_path(path); - path->search_for_extension = 1; + path->search_for_extension = true; ret = btrfs_search_slot(trans, root, &file_key, path, csum_size, 1); - path->search_for_extension = 0; + path->search_for_extension = false; if (ret < 0) goto out; diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 9828da12765f5..7ee3d8f05c1a2 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -971,8 +971,8 @@ int load_free_space_cache(struct btrfs_block_group *block_group) path = btrfs_alloc_path(); if (!path) return 0; - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; /* * We must pass a path with search_commit_root set to btrfs_iget in diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index c3734892d6548..f9ffb1c8988d9 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1699,8 +1699,8 @@ int btrfs_load_free_space_tree(struct btrfs_caching_control *caching_ctl) * Just like caching_thread() doesn't want to deadlock on the extent * tree, we don't want to deadlock on the free space tree. */ - path->skip_locking = 1; - path->search_commit_root = 1; + path->skip_locking = true; + path->search_commit_root = true; path->reada = READA_FORWARD; info = btrfs_search_free_space_info(NULL, block_group, path, 0); diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index 7e14e1bbcf389..b73e1dd97208a 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c @@ -312,7 +312,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, if (!path) return -ENOMEM; - path->skip_release_on_error = 1; + path->skip_release_on_error = true; ret = btrfs_insert_empty_item(trans, root, path, &key, ins_len); if (ret == -EEXIST) { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 30a34cd905ba4..36f75c6a8344d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7170,8 +7170,8 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, * point the commit_root has everything we need. */ if (btrfs_is_free_space_inode(inode)) { - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; } ret = btrfs_lookup_file_extent(NULL, root, path, objectid, start, 0); diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 261aa65019207..b4c6cbbcd168e 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -3882,8 +3882,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) * Rescan should only search for commit root, and any later difference * should be recorded by qgroup */ - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; while (!ret && !(stopped = rescan_should_stop(fs_info))) { trans = btrfs_start_transaction(fs_info->fs_root, 0); diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c index cc6f6095cc9fd..f6f02463dd1a9 100644 --- a/fs/btrfs/raid-stripe-tree.c +++ b/fs/btrfs/raid-stripe-tree.c @@ -394,8 +394,8 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, return -ENOMEM; if (stripe->rst_search_commit_root) { - path->skip_locking = 1; - path->search_commit_root = 1; + path->skip_locking = true; + path->search_commit_root = true; } ret = btrfs_search_slot(NULL, stripe_root, &stripe_key, path, 0, 0); diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index ea68328d289cb..86430435d3acc 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -3176,8 +3176,8 @@ static int __add_tree_block(struct reloc_control *rc, key.offset = blocksize; } - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; ret = btrfs_search_slot(NULL, rc->extent_root, &key, path, 0, 0); if (ret < 0) return ret; @@ -3369,8 +3369,8 @@ int find_next_extent(struct reloc_control *rc, struct btrfs_path *path, key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = 0; - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; ret = btrfs_search_slot(NULL, rc->extent_root, &key, path, 0, 0); if (ret < 0) diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 16936d17166ee..368f1d4444935 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -463,10 +463,10 @@ static noinline_for_stack struct scrub_ctx *scrub_setup_ctx( refcount_set(&sctx->refs, 1); sctx->is_dev_replace = is_dev_replace; sctx->fs_info = fs_info; - sctx->extent_path.search_commit_root = 1; - sctx->extent_path.skip_locking = 1; - sctx->csum_path.search_commit_root = 1; - sctx->csum_path.skip_locking = 1; + sctx->extent_path.search_commit_root = true; + sctx->extent_path.skip_locking = true; + sctx->csum_path.search_commit_root = true; + sctx->csum_path.skip_locking = true; for (i = 0; i < SCRUB_TOTAL_STRIPES; i++) { int ret; @@ -2103,10 +2103,10 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, * as the data stripe bytenr may be smaller than previous extent. Thus * we have to use our own extent/csum paths. */ - extent_path.search_commit_root = 1; - extent_path.skip_locking = 1; - csum_path.search_commit_root = 1; - csum_path.skip_locking = 1; + extent_path.search_commit_root = true; + extent_path.skip_locking = true; + csum_path.search_commit_root = true; + csum_path.skip_locking = true; for (int i = 0; i < data_stripes; i++) { int stripe_index; @@ -2630,8 +2630,8 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx, return -ENOMEM; path->reada = READA_FORWARD; - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; key.objectid = scrub_dev->devid; key.type = BTRFS_DEV_EXTENT_KEY; diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 04473387ee8bf..34a57f11e4bb9 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -633,9 +633,9 @@ static struct btrfs_path *alloc_path_for_send(void) path = btrfs_alloc_path(); if (!path) return NULL; - path->search_commit_root = 1; - path->skip_locking = 1; - path->need_commit_sem = 1; + path->search_commit_root = true; + path->skip_locking = true; + path->need_commit_sem = true; return path; } @@ -7643,10 +7643,10 @@ static int btrfs_compare_trees(struct btrfs_root *left_root, goto out; } - left_path->search_commit_root = 1; - left_path->skip_locking = 1; - right_path->search_commit_root = 1; - right_path->skip_locking = 1; + left_path->search_commit_root = true; + left_path->skip_locking = true; + right_path->search_commit_root = true; + right_path->skip_locking = true; /* * Strategy: Go to the first items of both trees. Then do diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index a0e12b4fb9561..f84065b69464c 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -602,9 +602,9 @@ static int overwrite_item(struct walk_control *wc) insert: btrfs_release_path(wc->subvol_path); /* try to insert the key into the destination tree */ - wc->subvol_path->skip_release_on_error = 1; + wc->subvol_path->skip_release_on_error = true; ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &wc->log_key, item_size); - wc->subvol_path->skip_release_on_error = 0; + wc->subvol_path->skip_release_on_error = false; dst_eb = wc->subvol_path->nodes[0]; dst_slot = wc->subvol_path->slots[0]; @@ -5734,8 +5734,8 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb, search_path = btrfs_alloc_path(); if (!search_path) return -ENOMEM; - search_path->search_commit_root = 1; - search_path->skip_locking = 1; + search_path->search_commit_root = true; + search_path->skip_locking = true; while (cur_offset < item_size) { u64 parent; @@ -6052,8 +6052,8 @@ static int conflicting_inode_is_dir(struct btrfs_root *root, u64 ino, key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (WARN_ON_ONCE(ret > 0)) { @@ -6073,8 +6073,8 @@ static int conflicting_inode_is_dir(struct btrfs_root *root, u64 ino, } btrfs_release_path(path); - path->search_commit_root = 0; - path->skip_locking = 0; + path->search_commit_root = false; + path->skip_locking = false; return ret; } @@ -7232,8 +7232,8 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans, path = btrfs_alloc_path(); if (!path) return -ENOMEM; - path->skip_locking = 1; - path->search_commit_root = 1; + path->skip_locking = true; + path->search_commit_root = true; key.objectid = ino; key.type = BTRFS_INODE_REF_KEY; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 630fb5885692b..863f69b9cf14b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1839,8 +1839,8 @@ static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, } path->reada = READA_FORWARD; - path->search_commit_root = 1; - path->skip_locking = 1; + path->search_commit_root = true; + path->skip_locking = true; key.objectid = device->devid; key.type = BTRFS_DEV_EXTENT_KEY; @@ -7627,7 +7627,7 @@ int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info) * chunk tree, to keep it simple, just skip locking on the chunk tree. */ ASSERT(!test_bit(BTRFS_FS_OPEN, &fs_info->flags)); - path->skip_locking = 1; + path->skip_locking = true; /* * Read all device items, and then all the chunk items. All diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c index b6f01d6c79e7f..b6e91e8fb7e38 100644 --- a/fs/btrfs/xattr.c +++ b/fs/btrfs/xattr.c @@ -97,7 +97,7 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, path = btrfs_alloc_path(); if (!path) return -ENOMEM; - path->skip_release_on_error = 1; + path->skip_release_on_error = true; if (!value) { di = btrfs_lookup_xattr(trans, root, path, From 8edc9252517808b87b53311da688a5a365b0b70c Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Thu, 11 Jun 2026 14:40:17 -0700 Subject: [PATCH 2271/3297] btrfs: fallback to transaction csum tree on a commit root csum miss [ Upstream commit 3dcd50730814e5220072d2b26d0587af6bfb6dbe ] We have been running with commit root csums enabled for some time and have noticed a slight uptick in zero csum errors. Investigating those revealed that they were same transaction reads of extents that were just relocated, but the extent map generation was long ago. It turns out that relocation intentionally does not update the extent generation (replace_file_extents()), but must write a new csum since the data has moved, so we must account for this with commit root csum reading. Luckily this is a short lived condition: after the relocation transaction the commit root will once again have the csum. So we can add a generic fallback to the lookup to try again with the transaction csum root. Fixes: f07b855c56b1 ("btrfs: try to search for data csums in commit root") Reviewed-by: Filipe Manana Signed-off-by: Boris Burkov Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/file-item.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index a2f851eaadca0..9b19b33e85b68 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -350,6 +350,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits; int ret = 0; u32 bio_offset = 0; + bool using_commit_root = false; if ((inode->flags & BTRFS_INODE_NODATASUM) || test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state)) @@ -423,6 +424,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) * from across transactions. */ if (bbio->csum_search_commit_root) { + using_commit_root = true; path->search_commit_root = true; path->skip_locking = true; down_read(&fs_info->commit_root_sem); @@ -455,6 +457,28 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) * assume this is the case. */ if (count == 0) { + /* + * If an extent is relocated in the current transaction + * then relocation writes a new csum without updating + * the extent map generation. Until the next commit, we + * will see a hole in that case, so we need to fallback + * to searching the transaction csum root. + * + * Note that a commit root lookup of a referenced extent can + * only miss, not return a stale csum. A freed extent's csum + * is deleted in the same transaction and its bytenr is not + * reusable until that transaction has committed and the + * extent is unpinned. + */ + if (using_commit_root) { + up_read(&fs_info->commit_root_sem); + using_commit_root = false; + path->search_commit_root = false; + path->skip_locking = false; + btrfs_release_path(path); + continue; + } + memset(csum_dst, 0, csum_size); count = 1; @@ -473,7 +497,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) bio_offset += count * sectorsize; } - if (bbio->csum_search_commit_root) + if (using_commit_root) up_read(&fs_info->commit_root_sem); return ret; } From 996c5c19d5b5ac5b98a7b5a406b548305841c301 Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Wed, 17 Jun 2026 16:35:00 -0700 Subject: [PATCH 2272/3297] firmware: arm_ffa: Fix NULL dereference in ffa_partition_info_get() [ Upstream commit 8ae5f8e4836667fcaffdf2e3c6068b0a8b364dd8 ] ffa_partition_info_get() passes uuid_str directly to uuid_parse() without a NULL check. When a caller passes NULL, uuid_parse() -> __uuid_parse() -> uuid_is_valid() dereferences the pointer, causing a kernel panic: | Unable to handle kernel NULL pointer dereference at virtual address | 0000000000000040 | pc : uuid_parse+0x40/0xac | lr : ffa_partition_info_get+0x1c/0x94 [arm_ffa] Add a NULL guard before uuid_parse() so a NULL argument returns -ENODEV instead of crashing. Callers are expected to always supply a valid partition UUID, so NULL is not a supported input. Fixes: d0c0bce83122 ("firmware: arm_ffa: Setup in-kernel users of FFA partitions") Signed-off-by: Unnathi Chalicheemala Link: https://patch.msgid.link/20260617-ffa_partition_nullptr_fix-v2-1-bc801b4ce34c@oss.qualcomm.com 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 c76eabb8436a8..feded15bb5e92 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1133,7 +1133,7 @@ static int ffa_partition_info_get(const char *uuid_str, uuid_t uuid; struct ffa_partition_info *pbuf; - if (uuid_parse(uuid_str, &uuid)) { + if (!uuid_str || uuid_parse(uuid_str, &uuid)) { pr_err("invalid uuid (%s)\n", uuid_str); return -ENODEV; } From 57acd519283333fa5b7a809a42a01222ac1faabe Mon Sep 17 00:00:00 2001 From: John Stultz Date: Sat, 6 Dec 2025 02:22:03 +0000 Subject: [PATCH 2273/3297] sched/ext: Avoid null ptr traversal when ->put_prev_task() is called with NULL next [ Upstream commit 12b5cd99a05f7cbc2ceb88b3b9601d404ef2236a ] Early when trying to get sched_ext and proxy-exe working together, I kept tripping over NULL ptr in put_prev_task_scx() on the line: if (sched_class_above(&ext_sched_class, next->sched_class)) { Which was due to put_prev_task() passes a NULL next, calling: prev->sched_class->put_prev_task(rq, prev, NULL); put_prev_task_scx() already guards for a NULL next in the switch_class case, but doesn't seem to have a guard for sched_class_above() check. I can't say I understand why this doesn't trip usually without proxy-exec. And in newer kernels there are way fewer put_prev_task(), and I can't easily reproduce the issue now even with proxy-exec. But we still have one put_prev_task() call left in core.c that seems like it could trip this, so I wanted to send this out for consideration. tj: put_prev_task() can be called with NULL @next; however, when @p is queued, that doesn't happen, so this condition shouldn't currently be triggerable. The connection isn't straightforward or necessarily reliable, so add the NULL check even if it can't currently be triggered. Link: http://lkml.kernel.org/r/20251206022218.1541878-1-jstultz@google.com Signed-off-by: John Stultz Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo Stable-dep-of: b7d9c359e5cf ("sched_ext: Don't warn on core-sched forced idle in put_prev_task_scx()") Signed-off-by: Sasha Levin --- 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 d8280f8744331..d399494364c0a 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -2396,7 +2396,7 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, * ops.enqueue() that @p is the only one available for this cpu, * which should trigger an explicit follow-up scheduling event. */ - if (sched_class_above(&ext_sched_class, next->sched_class)) { + if (next && sched_class_above(&ext_sched_class, next->sched_class)) { WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST)); do_enqueue_task(rq, p, SCX_ENQ_LAST, -1); } else { From 2907e9d0f05b506dfd58aec589a23042a56ef36b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 29 Jun 2026 12:55:48 -1000 Subject: [PATCH 2274/3297] sched_ext: Don't warn on core-sched forced idle in put_prev_task_scx() [ Upstream commit b7d9c359e5cf867f7eb23df3bb1c6b9e58af24da ] put_prev_task_scx() warns when a runnable task drops to a lower sched_class without SCX_OPS_ENQ_LAST, on the assumption that balance_one() would have kept it running. Core scheduling breaks that: a forced-idle SMT sibling reschedules through the core_pick fast path in pick_next_task(), which skips pick_task_scx() and thus balance_one(), so a runnable task can drop to idle with ENQ_LAST unset. Gate the warning on sched_cpu_cookie_match(): a cookie mismatch means core scheduling forced the idle, while a match (or core scheduling off) still catches a genuine missing-ENQ_LAST drop. Fixes: 7c65ae81ea86 ("sched_ext: Don't call put_prev_task_scx() before picking the next task") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi Signed-off-by: Sasha Levin --- kernel/sched/ext.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index d399494364c0a..83baf27088888 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -2395,9 +2395,14 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, * sched_class, %SCX_OPS_ENQ_LAST must be set. Tell * ops.enqueue() that @p is the only one available for this cpu, * which should trigger an explicit follow-up scheduling event. + * + * Core scheduling can force this CPU idle while @p stays + * runnable. @p's cookie then won't match the core's, so skip + * the warning in that case. */ if (next && sched_class_above(&ext_sched_class, next->sched_class)) { - WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST)); + WARN_ON_ONCE(sched_cpu_cookie_match(rq, p) && + !(sch->ops.flags & SCX_OPS_ENQ_LAST)); do_enqueue_task(rq, p, SCX_ENQ_LAST, -1); } else { do_enqueue_task(rq, p, 0, -1); From d7fc6f351c478586980a521d63b0214d9c055e78 Mon Sep 17 00:00:00 2001 From: Antony Antony Date: Sat, 27 Jun 2026 10:23:43 +0200 Subject: [PATCH 2275/3297] xfrm: reject optional IPTFS templates in outbound policies [ Upstream commit ea528f18231ec0f33317be57f8866913b19aba6e ] syzbot reported a stack-out-of-bounds read in xfrm_state_find() which flows from xfrm_tmpl_resolve_one(). Commit 3d776e31c841 ("xfrm: Reject optional tunnel/BEET mode templates in outbound policies") disallowed optional tunnel and BEET in outbound policies to prevent this. Later when IPTFS added, it was not covered by that fix and can still trigger the out-of-bounds read; Extend the check to disallow optional IPTFS in outbound policies as well. IPTFS should be identical to tunnel mode. IN and FWD policies are not affected: xfrm_tmpl_resolve_one() is only reachable via the outbound path. Reproducer, before: ip link add dummy0 type dummy ip link set dummy0 up ip addr add 10.1.1.1/24 dev dummy0 ip xfrm policy add src 10.1.1.1/32 dst 10.1.1.2/32 dir out tmpl src fc00::dead:1 dst fc00::dead:2 proto esp reqid 1 mode iptfs level use tmpl src fc00::dead:1 dst fc00::dead:2 proto esp reqid 2 mode transport ping -W 1 -c 1 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. [ 64.168420] ================================================================== [ 64.169977] BUG: KASAN: stack-out-of-bounds in __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] Read of size 4 at addr ffff88800e1ffd20 by task ping/2844 [ 64.169977] CPU: 2 UID: 0 PID: 2844 Comm: ping Not tainted 7.1.0-rc7-00180-geb23b588430a #98 PREEMPT(full) [ 64.169977] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 64.169977] Call Trace: [ 64.169977] [ 64.169977] dump_stack_lvl+0x47/0x70 [ 64.169977] ? __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] print_report+0x152/0x4b0 [ 64.169977] ? ksys_mmap_pgoff+0x6d/0xa0 [ 64.169977] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 64.169977] ? rcu_read_unlock_sched+0xa/0x20 [ 64.169977] ? __virt_addr_valid+0x21b/0x230 [ 64.169977] ? __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] kasan_report+0xa8/0xd0 [ 64.169977] ? __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] __xfrm6_addr_hash+0x11e/0x170 [ 64.169977] __xfrm_dst_hash+0x24/0xc0 [ 64.169977] xfrm_state_find+0xa2d/0x2f90 [ 64.169977] ? __pfx_xfrm_state_find+0x10/0x10 [ 64.169977] ? __pfx_ftrace_graph_ret_addr+0x10/0x10 [ 64.169977] ? __pfx_ftrace_graph_ret_addr+0x10/0x10 [ 64.169977] xfrm_tmpl_resolve_one+0x210/0x570 [ 64.169977] ? __pfx_xfrm_tmpl_resolve_one+0x10/0x10 [ 64.169977] ? __pfx_stack_trace_consume_entry+0x10/0x10 [ 64.169977] ? kernel_text_address+0x5b/0x80 [ 64.169977] ? __kernel_text_address+0xe/0x30 [ 64.169977] ? unwind_get_return_address+0x5e/0x90 [ 64.169977] ? arch_stack_walk+0x8c/0xe0 [ 64.169977] xfrm_tmpl_resolve+0x130/0x200 [ 64.169977] ? __pfx_xfrm_tmpl_resolve+0x10/0x10 [ 64.169977] ? __pfx_xfrm_policy_inexact_lookup_rcu+0x10/0x10 [ 64.169977] ? __refcount_add_not_zero.constprop.0+0xb2/0x110 [ 64.169977] ? __pfx___refcount_add_not_zero.constprop.0+0x10/0x10 [ 64.169977] xfrm_resolve_and_create_bundle+0xd5/0x310 [ 64.169977] ? __pfx_xfrm_resolve_and_create_bundle+0x10/0x10 [ 64.169977] ? __pfx_xfrm_policy_lookup_bytype+0x10/0x10 [ 64.169977] ? __pfx_xfrm_policy_lookup_bytype+0x10/0x10 [ 64.169977] xfrm_lookup_with_ifid+0x3d8/0xb80 [ 64.169977] ? __pfx_xfrm_lookup_with_ifid+0x10/0x10 [ 64.169977] ? ip_route_output_key_hash+0xc6/0x110 [ 64.169977] ? kasan_save_track+0x10/0x30 [ 64.169977] xfrm_lookup_route+0x18/0xe0 [ 64.169977] ip4_datagram_release_cb+0x4c9/0x530 [ 64.169977] ? __pfx_ip4_datagram_release_cb+0x10/0x10 [ 64.169977] ? do_raw_spin_lock+0x71/0xc0 [ 64.169977] ? __pfx_do_raw_spin_lock+0x10/0x10 [ 64.169977] release_sock+0xb0/0x170 [ 64.169977] udp_connect+0x43/0x50 [ 64.169977] __sys_connect+0xa6/0x100 [ 64.169977] ? alloc_fd+0x2e9/0x300 [ 64.169977] ? __pfx___sys_connect+0x10/0x10 [ 64.169977] ? preempt_latency_start+0x1f/0x70 [ 64.169977] ? fd_install+0x7e/0x150 [ 64.169977] ? rcu_read_unlock_sched+0xa/0x20 [ 64.169977] ? __sys_socket+0xdf/0x130 [ 64.169977] ? __pfx___sys_socket+0x10/0x10 [ 64.169977] ? vma_refcount_put+0x43/0xa0 [ 64.169977] __x64_sys_connect+0x7e/0x90 [ 64.169977] do_syscall_64+0x11b/0x2b0 [ 64.169977] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 64.169977] RIP: 0033:0x7f4851ecb570 [ 64.169977] Code: 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 80 3d f9 ca 0d 00 00 74 17 b8 2a 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 58 c3 0f 1f 80 00 00 00 00 48 83 ec 18 89 54 [ 64.169977] RSP: 002b:00007ffc830e3498 EFLAGS: 00000202 ORIG_RAX: 000000000000002a [ 64.169977] RAX: ffffffffffffffda RBX: 00007ffc830e34d0 RCX: 00007f4851ecb570 [ 64.169977] RDX: 0000000000000010 RSI: 00007ffc830e34d0 RDI: 0000000000000005 [ 64.169977] RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000000 [ 64.169977] R10: 0000000000000006 R11: 0000000000000202 R12: 0000000000000005 [ 64.169977] R13: 0000000000000000 R14: 00005619a863f340 R15: 0000000000000000 [ 64.169977] [ 64.169977] The buggy address belongs to stack of task ping/2844 [ 64.169977] and is located at offset 88 in frame: [ 64.169977] ip4_datagram_release_cb+0x0/0x530 [ 64.169977] This frame has 1 object: [ 64.169977] [32, 88) 'fl4' [ 64.169977] The buggy address belongs to the physical page: [ 64.169977] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0xe1ff [ 64.169977] flags: 0x4000000000000000(zone=1) [ 64.169977] raw: 4000000000000000 0000000000000000 ffffea0000387fc8 0000000000000000 [ 64.169977] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000 [ 64.169977] page dumped because: kasan: bad access detected [ 64.169977] Memory state around the buggy address: [ 64.169977] ffff88800e1ffc00: f2 f2 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 [ 64.169977] ffff88800e1ffc80: 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 [ 64.169977] >ffff88800e1ffd00: 00 00 00 00 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 [ 64.169977] ^ [ 64.169977] ffff88800e1ffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 [ 64.169977] ffff88800e1ffe00: f1 f1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 64.169977] ================================================================== [ 64.245153] Disabling lock debugging due to kernel taint After the fix: ip xfrm policy add src 10.1.1.1/32 dst 10.1.1.2/32 dir out tmpl \ src fc00::dead:1 dst fc00::dead:2 proto esp reqid 1 mode iptfs \ level use tmpl src fc00::dead:1 dst fc00::dead:2 proto esp reqid 2 \ mode transport Error: Mode in optional template not allowed in outbound policy. Fixes: d1716d5a44c3 ("xfrm: add generic iptfs defines and functionality") Reported-by: syzbot+0ac4d84afe1066a1f3e9@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a3ceb94.43b4ff68.30a095.0004.GAE@google.com/T/ Signed-off-by: Antony Antony Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_user.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 9ad6619888c73..3641ccccbc415 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2068,13 +2068,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family, switch (ut[i].mode) { case XFRM_MODE_TUNNEL: case XFRM_MODE_BEET: + case XFRM_MODE_IPTFS: if (ut[i].optional && dir == XFRM_POLICY_OUT) { NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy"); return -EINVAL; } break; - case XFRM_MODE_IPTFS: - break; default: if (ut[i].family != prev_family) { NL_SET_ERR_MSG(extack, "Mode in template doesn't support a family change"); From c4ef25de94d7338bcd39f5dbfc6d3b415e0a1516 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 17 Jun 2026 14:21:05 +0300 Subject: [PATCH 2276/3297] RDMA/cma: Fix hardware address comparison length in netevent callback [ Upstream commit 18313833e2c6de222a4f6c072da759d0d5888528 ] The cited commit hardcoded the hardware address comparison len to ETH_ALEN. This breaks IPoIB, which uses 20-byte addresses. By truncating the memcmp, the CMA may incorrectly assume the target address is unchanged and fails to abort the stalled connection. Fix this by replacing ETH_ALEN with the dynamic neigh->dev->addr_len to correctly evaluate the full address regardless of the link layer. Fixes: 925d046e7e52 ("RDMA/core: Add a netevent notifier to cma") Signed-off-by: Or Gerlitz Link: https://patch.msgid.link/20260617-fix-cma-ipoib-v1-1-03f869344304@ddn.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/core/cma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index ce511800b056c..648acc2222cf5 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -5237,7 +5237,7 @@ static int cma_netevent_callback(struct notifier_block *self, list_for_each_entry(current_id, &ips_node->id_list, id_list_entry) { if (!memcmp(current_id->id.route.addr.dev_addr.dst_dev_addr, - neigh->ha, ETH_ALEN)) + neigh->ha, neigh->dev->addr_len)) continue; cma_id_get(current_id); if (!queue_work(cma_wq, ¤t_id->id.net_work)) From dbb945b80a3a4a21a80292c7bebdd0d674209507 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Thu, 5 Mar 2026 17:08:24 +0000 Subject: [PATCH 2277/3297] RDMA/umem: Add pinned revocable dmabuf import interface [ Upstream commit ff85a2ebacbdaec9f28c4660c991295ace93cd1c ] Added an interface for importing a pinned but revocable dmabuf. This interface can be used by drivers that are capable of revocation so that they can import dmabufs from exporters that may require it, such as VFIO. This interface implements a two step process, where drivers will first call ib_umem_dmabuf_get_pinned_revocable_and_lock() which will pin and map the dmabuf (and provide a functional move_notify/invalidate_mappings callback), but will return with the lock still held so that the driver can then populate the callback via ib_umem_dmabuf_set_revoke_locked() without races from concurrent revocations. This scheme also allows for easier integration with drivers that may not have actually allocated their internal MR objects at the time of the get_pinned_revocable* call. Signed-off-by: Jacob Moroni Link: https://patch.msgid.link/20260305170826.3803155-4-jmoroni@google.com Signed-off-by: Leon Romanovsky Stable-dep-of: a846aecb931b ("RDMA/irdma: Prevent rereg_mr for non-mem regions") Signed-off-by: Sasha Levin --- drivers/infiniband/core/umem_dmabuf.c | 61 +++++++++++++++++++++++++++ include/rdma/ib_umem.h | 19 +++++++++ 2 files changed, 80 insertions(+) diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c index b239ac09ec292..4fc73f53342b7 100644 --- a/drivers/infiniband/core/umem_dmabuf.c +++ b/drivers/infiniband/core/umem_dmabuf.c @@ -206,6 +206,10 @@ static void ib_umem_dmabuf_revoke_locked(struct dma_buf_attachment *attach) if (umem_dmabuf->revoked) return; + + if (umem_dmabuf->pinned_revoke) + umem_dmabuf->pinned_revoke(umem_dmabuf->private); + ib_umem_dmabuf_unmap_pages(umem_dmabuf); if (umem_dmabuf->pinned) { dma_buf_unpin(umem_dmabuf->attach); @@ -214,6 +218,11 @@ static void ib_umem_dmabuf_revoke_locked(struct dma_buf_attachment *attach) umem_dmabuf->revoked = 1; } +static struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_revocable_ops = { + .allow_peer2peer = true, + .move_notify = ib_umem_dmabuf_revoke_locked, +}; + static struct ib_umem_dmabuf * ib_umem_dmabuf_get_pinned_and_lock(struct ib_device *device, struct device *dma_device, @@ -266,6 +275,58 @@ ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, } EXPORT_SYMBOL(ib_umem_dmabuf_get_pinned_with_dma_device); +/** + * ib_umem_dmabuf_get_pinned_revocable_and_lock - Map & pin a revocable dmabuf + * @device: IB device. + * @offset: Start offset. + * @size: Length. + * @fd: dmabuf fd. + * @access: Access flags. + * + * Obtains a umem from a dmabuf for drivers/devices that can support revocation. + * + * Returns with dma_resv_lock held upon success. The driver must set the revoke + * callback prior to unlock by calling ib_umem_dmabuf_set_revoke_locked(). + * + * When a revocation occurs, the revoke callback will be called. The driver must + * ensure that the region is no longer accessed when the callback returns. Any + * subsequent access attempts should also probably cause an AE for MRs. + * + * If the umem is used for an MR, the driver must ensure that the key remains in + * use such that it cannot be obtained by a new region until this region is + * fully deregistered (i.e., ibv_dereg_mr). If a driver needs to serialize with + * revoke calls, it can use dma_resv_lock. + * + * If successful, then the revoke callback may be called at any time and will + * also be called automatically upon ib_umem_release (serialized). The revoke + * callback will be called one time at most. + * + * Return: A pointer to ib_umem_dmabuf on success, or an ERR_PTR on failure. + */ +struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_revocable_and_lock(struct ib_device *device, + unsigned long offset, size_t size, + int fd, int access) +{ + const struct dma_buf_attach_ops *ops = + &ib_umem_dmabuf_attach_pinned_revocable_ops; + + return ib_umem_dmabuf_get_pinned_and_lock(device, device->dma_device, + offset, size, fd, access, + ops); +} +EXPORT_SYMBOL(ib_umem_dmabuf_get_pinned_revocable_and_lock); + +void ib_umem_dmabuf_set_revoke_locked(struct ib_umem_dmabuf *umem_dmabuf, + void (*revoke)(void *priv), void *priv) +{ + dma_resv_assert_held(umem_dmabuf->attach->dmabuf->resv); + + umem_dmabuf->pinned_revoke = revoke; + umem_dmabuf->private = priv; +} +EXPORT_SYMBOL(ib_umem_dmabuf_set_revoke_locked); + struct ib_umem_dmabuf *ib_umem_dmabuf_get_pinned(struct ib_device *device, unsigned long offset, size_t size, int fd, diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 5122f78467675..90b1ea19a90a0 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -36,6 +36,7 @@ struct ib_umem_dmabuf { struct scatterlist *last_sg; unsigned long first_sg_offset; unsigned long last_sg_trim; + void (*pinned_revoke)(void *priv); void *private; u8 pinned : 1; u8 revoked : 1; @@ -141,6 +142,12 @@ struct ib_umem_dmabuf *ib_umem_dmabuf_get_pinned(struct ib_device *device, size_t size, int fd, int access); struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_revocable_and_lock(struct ib_device *device, + unsigned long offset, size_t size, + int fd, int access); +void ib_umem_dmabuf_set_revoke_locked(struct ib_umem_dmabuf *umem_dmabuf, + void (*revoke)(void *priv), void *priv); +struct ib_umem_dmabuf * ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, struct device *dma_device, unsigned long offset, size_t size, @@ -201,6 +208,18 @@ ib_umem_dmabuf_get_pinned(struct ib_device *device, unsigned long offset, return ERR_PTR(-EOPNOTSUPP); } +static inline struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_revocable_and_lock(struct ib_device *device, + unsigned long offset, size_t size, + int fd, int access) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void +ib_umem_dmabuf_set_revoke_locked(struct ib_umem_dmabuf *umem_dmabuf, + void (*revoke)(void *priv), void *priv) {} + static inline struct ib_umem_dmabuf * ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, struct device *dma_device, From ca1c29f05274b737dc964e28b97803750d7cf7ec Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 14:19:36 +0000 Subject: [PATCH 2278/3297] RDMA/irdma: Prevent rereg_mr for non-mem regions [ Upstream commit a846aecb931b4d65d5eafa92a0623545af46d4f2 ] When a QP/CQ/SRQ is created, a two step process is used where the buffer is allocated in userspace and explicitly registered with the normal reg_mr mechanism prior to creating the actual QP/CQ/SRQ object. These special registrations are indicated via an ABI field so the driver knows that they do not have a valid mkey and to skip the actual CQP command submission. Since these are real MR objects from the core's perspective, it is possible for a user application to invoke rereg_mr on them and cause a real CQP op to be emitted with the zero-initialized mkey value of 0. Fix this by preventing rereg_mr on these special regions. Fixes: 5ac388db27c4 ("RDMA/irdma: Add support to re-register a memory region") Signed-off-by: Jacob Moroni Reviewed-by: David Hu Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 2b85e32134a5b..c210f9a37fb97 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -3756,6 +3756,9 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + if (iwmr->type != IRDMA_MEMREG_TYPE_MEM) + return ERR_PTR(-EINVAL); + ret = ib_umem_check_rereg(iwmr->region, flags, new_access); if (ret) return ERR_PTR(ret); From 1cd56258fe1a0b13a6eb9458cbe4c82ad3caf332 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Tue, 2 Jun 2026 21:44:22 +0000 Subject: [PATCH 2279/3297] RDMA/irdma: Remove redundant legacy_mode checks [ Upstream commit ed8621be482bf18dcd217aa024e95758bf9d28f2 ] The driver has the following invariants: 1. legacy_mode is only allowed on GEN_1 hardware (enforced in irdma_alloc_ucontext). 2. GEN_1 hardware does not set IRDMA_FEATURE_CQ_RESIZE or IRDMA_FEATURE_RTS_AE. These feature flags are only set for GEN_2 and GEN_3 hardware. Therefore, legacy_mode is always false if IRDMA_FEATURE_CQ_RESIZE or IRDMA_FEATURE_RTS_AE is set, so remove the redundant checks. Link: https://patch.msgid.link/r/20260602214423.1315105-1-jmoroni@google.com Signed-off-by: Jacob Moroni Signed-off-by: Jason Gunthorpe Stable-dep-of: b9b088907156 ("RDMA/irdma: Prevent user-triggered null deref on QP create") Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/uk.c | 9 +++------ drivers/infiniband/hw/irdma/user.h | 1 - drivers/infiniband/hw/irdma/verbs.c | 7 +------ 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/irdma/uk.c b/drivers/infiniband/hw/irdma/uk.c index 4cc81d61be7fa..7aab007619667 100644 --- a/drivers/infiniband/hw/irdma/uk.c +++ b/drivers/infiniband/hw/irdma/uk.c @@ -1540,15 +1540,12 @@ static const struct irdma_wqe_uk_ops iw_wqe_uk_ops_gen_1 = { * irdma_setup_connection_wqes - setup WQEs necessary to complete * connection. * @qp: hw qp (user and kernel) - * @info: qp initialization info */ -static void irdma_setup_connection_wqes(struct irdma_qp_uk *qp, - struct irdma_qp_uk_init_info *info) +static void irdma_setup_connection_wqes(struct irdma_qp_uk *qp) { u16 move_cnt = 1; - if (!info->legacy_mode && - (qp->uk_attrs->feature_flags & IRDMA_FEATURE_RTS_AE)) + if (qp->uk_attrs->feature_flags & IRDMA_FEATURE_RTS_AE) move_cnt = 3; qp->conn_wqes = move_cnt; @@ -1699,7 +1696,7 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info) sq_ring_size = qp->sq_size << info->sq_shift; IRDMA_RING_INIT(qp->sq_ring, sq_ring_size); if (info->first_sq_wq) { - irdma_setup_connection_wqes(qp, info); + irdma_setup_connection_wqes(qp); qp->swqe_polarity = 1; qp->first_sq_wq = true; } else { diff --git a/drivers/infiniband/hw/irdma/user.h b/drivers/infiniband/hw/irdma/user.h index aeebf768174ab..a0b409a895f0e 100644 --- a/drivers/infiniband/hw/irdma/user.h +++ b/drivers/infiniband/hw/irdma/user.h @@ -560,7 +560,6 @@ struct irdma_qp_uk_init_info { u8 sq_shift; u8 rq_shift; int abi_ver; - bool legacy_mode; struct irdma_srq_uk *srq_uk; }; diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index c210f9a37fb97..dfd5f0caabb87 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -632,7 +632,6 @@ static int irdma_setup_umode_qp(struct ib_udata *udata, iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; iwqp->user_mode = 1; if (req.user_wqe_bufs) { - info->qp_uk_init_info.legacy_mode = ucontext->legacy_mode; spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, &ucontext->qp_reg_mem_list); @@ -2068,10 +2067,6 @@ static int irdma_resize_cq(struct ib_cq *ibcq, int entries, rdma_udata_to_drv_context(udata, struct irdma_ucontext, ibucontext); - /* CQ resize not supported with legacy GEN_1 libi40iw */ - if (ucontext->legacy_mode) - return -EOPNOTSUPP; - if (ib_copy_from_udata(&req, udata, min(sizeof(req), udata->inlen))) return -EINVAL; @@ -2550,7 +2545,7 @@ static int irdma_create_cq(struct ib_cq *ibcq, cqmr = &iwpbl->cq_mr; if (rf->sc_dev.hw_attrs.uk_attrs.feature_flags & - IRDMA_FEATURE_CQ_RESIZE && !ucontext->legacy_mode) { + IRDMA_FEATURE_CQ_RESIZE) { spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); iwpbl_shadow = irdma_get_pbl( (unsigned long)req.user_shadow_area, From ec675b4cdfd378d8c9dd8c93126c024f2469bd79 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 16:40:13 +0000 Subject: [PATCH 2280/3297] RDMA/irdma: Prevent user-triggered null deref on QP create [ Upstream commit b9b0889071569d43623c260074e159cd8f26adb1 ] Previously, the user QP creation path would only attempt to populate iwqp->iwpbl if the user-provided req.user_wqe_bufs field was non-zero. The problem is that iwqp->iwpbl is unconditionally dereferenced later on in irdma_setup_virt_qp. While there was a check for iwqp->iwpbl != NULL, this check would only occur if req.user_wqe_bufs was non-zero. The end result is that a user could send a zero user_wqe_bufs value and trigger a null ptr deref. Fix this by unconditionally calling irdma_get_pbl and bailing if it fails, similar to the CQ and SRQ paths. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Link: https://patch.msgid.link/r/20260617164013.280790-1-jmoroni@google.com Signed-off-by: Jacob Moroni Reviewed-by: David Hu Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index dfd5f0caabb87..5f5b79109d547 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -631,17 +631,16 @@ static int irdma_setup_umode_qp(struct ib_udata *udata, iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; iwqp->user_mode = 1; - if (req.user_wqe_bufs) { - spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, - &ucontext->qp_reg_mem_list); - spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); - if (!iwqp->iwpbl) { - ret = -ENODATA; - ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); - return ret; - } + spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); + iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, + &ucontext->qp_reg_mem_list); + spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); + + if (!iwqp->iwpbl) { + ret = -ENODATA; + ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); + return ret; } if (!ucontext->use_raw_attrs) { From 14e519f93a48cc217afea2a7096c866dda8ab785 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 12:17:51 +0800 Subject: [PATCH 2281/3297] RDMA/erdma: initialize ret for empty receive WR lists [ Upstream commit 2815a277c53e9a84784d6410cd55a9da5b33068d ] erdma_post_recv() returns ret after walking the receive work request list. If the caller passes an empty list, the loop is skipped and ret is not assigned. Initialize ret to 0 so an empty receive work request list returns success instead of stack data. Fixes: 155055771704 ("RDMA/erdma: Add verbs implementation") Link: https://patch.msgid.link/r/20260618041752.481193-1-ruoyuw560@gmail.com Signed-off-by: Ruoyu Wang Reviewed-by: Cheng Xu Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/erdma/erdma_qp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c index 25f6c49aec779..e002343832f74 100644 --- a/drivers/infiniband/hw/erdma/erdma_qp.c +++ b/drivers/infiniband/hw/erdma/erdma_qp.c @@ -734,7 +734,7 @@ int erdma_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr, const struct ib_recv_wr *wr = recv_wr; struct erdma_qp *qp = to_eqp(ibqp); unsigned long flags; - int ret; + int ret = 0; spin_lock_irqsave(&qp->lock, flags); From d842ef03d91458a52dc74cb98c747e10f7bfd633 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 12:17:52 +0800 Subject: [PATCH 2282/3297] RDMA/mana_ib: initialize err for empty send WR lists [ Upstream commit 155fd5ce2382b0ffbec0d7ee7b3a6818a27a5aed ] mana_ib_post_send() returns err after walking the send work request list. If the caller passes an empty list, the loop is skipped and err is not assigned. Initialize err to 0 so an empty send work request list returns success instead of stack data. Fixes: c8017f5b4856 ("RDMA/mana_ib: UD/GSI work requests") Link: https://patch.msgid.link/r/20260618041752.481193-2-ruoyuw560@gmail.com Signed-off-by: Ruoyu Wang Reviewed-by: Long Li Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mana/wr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mana/wr.c b/drivers/infiniband/hw/mana/wr.c index 1813567d3b16c..36a1d506f08f6 100644 --- a/drivers/infiniband/hw/mana/wr.c +++ b/drivers/infiniband/hw/mana/wr.c @@ -144,7 +144,7 @@ static int mana_ib_post_send_ud(struct mana_ib_qp *qp, const struct ib_ud_wr *wr int mana_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, const struct ib_send_wr **bad_wr) { - int err; + int err = 0; struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); for (; wr; wr = wr->next) { From e221dde026af207ff38aff17046266bda23c5d9e Mon Sep 17 00:00:00 2001 From: Danila Chernetsov Date: Sat, 27 Jun 2026 09:59:51 +0000 Subject: [PATCH 2283/3297] RDMA/hns: Fix potential integer overflow in mhop hem cleanup [ Upstream commit 9f0f2d2121f16d420199a82ac5bbc242269133b3 ] In hns_roce_cleanup_mhop_hem_table(), the expression: obj = i * buf_chunk_size / table->obj_size; is evaluated using 32-bit unsigned arithmetic because 'buf_chunk_size' is u32 and the usual arithmetic conversions convert 'i' to unsigned int. The result is assigned to a u64 variable, but the multiplication may overflow before the assignment. For sufficiently large HEM tables, this produces an incorrect object index passed to hns_roce_table_mhop_put(). Cast 'i' to u64 before the multiplication so that the intermediate calculation is performed with 64-bit arithmetic. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: a25d13cbe816 ("RDMA/hns: Add the interfaces to support multi hop addressing for the contexts in hip08") Link: https://patch.msgid.link/r/20260627095951.51378-1-listdansp@mail.ru Signed-off-by: Danila Chernetsov Reviewed-by: Junxian Huang Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/hw/hns/hns_roce_hem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index 1680d0ac071ed..ae7a3939124f2 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -842,7 +842,7 @@ static void hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev *hr_dev, mhop.bt_chunk_size; for (i = 0; i < table->num_hem; ++i) { - obj = i * buf_chunk_size / table->obj_size; + obj = (u64)i * buf_chunk_size / table->obj_size; if (table->hem[i]) hns_roce_table_mhop_put(hr_dev, table, obj, 0); } From fcc9d50022bcdb1f9f7ed04955c72b4a7355af3d Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 30 Jun 2026 14:00:40 +0800 Subject: [PATCH 2284/3297] RDMA/siw: publish QP after initialization [ Upstream commit bb27fcc67c429d97f785c92c35a6c5adebb05d7f ] siw_create_qp() currently calls siw_qp_add() before the queues, CQ pointers, state, completion, and device list entry are ready. A QPN lookup can therefore reach a QP that is still being constructed. Move siw_qp_add() to the end of siw_create_qp(), after QP initialization and before adding the QP to the siw device list. Fixes: f29dd55b0236 ("rdma/siw: queue pair methods") Link: https://patch.msgid.link/r/20260630060040.966461-1-ruoyuw560@gmail.com Suggested-by: Bernard Metzler Signed-off-by: Ruoyu Wang Acked-by: Bernard Metzler Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- drivers/infiniband/sw/siw/siw_verbs.c | 44 +++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index efa2f097b5828..9fbade0b85715 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -316,6 +316,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, struct siw_ucontext *uctx = rdma_udata_to_drv_context(udata, struct siw_ucontext, base_ucontext); + struct siw_uresp_create_qp uresp = {}; unsigned long flags; int num_sqe, num_rqe, rv = 0; size_t length; @@ -369,11 +370,6 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, spin_lock_init(&qp->rq_lock); spin_lock_init(&qp->orq_lock); - rv = siw_qp_add(sdev, qp); - if (rv) - goto err_atomic; - - /* All queue indices are derived from modulo operations * on a free running 'get' (consumer) and 'put' (producer) * unsigned counter. Having queue sizes at power of two @@ -391,14 +387,14 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (qp->sendq == NULL) { rv = -ENOMEM; - goto err_out_xa; + goto err_out; } if (attrs->sq_sig_type != IB_SIGNAL_REQ_WR) { if (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) qp->attrs.flags |= SIW_SIGNAL_ALL_WR; else { rv = -EINVAL; - goto err_out_xa; + goto err_out; } } qp->pd = pd; @@ -424,7 +420,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (qp->recvq == NULL) { rv = -ENOMEM; - goto err_out_xa; + goto err_out; } qp->attrs.rq_size = num_rqe; } @@ -439,11 +435,8 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, qp->attrs.state = SIW_QP_STATE_IDLE; if (udata) { - struct siw_uresp_create_qp uresp = {}; - uresp.num_sqe = num_sqe; uresp.num_rqe = num_rqe; - uresp.qp_id = qp_id(qp); if (qp->sendq) { length = num_sqe * sizeof(struct siw_sqe); @@ -452,7 +445,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, length, &uresp.sq_key); if (!qp->sq_entry) { rv = -ENOMEM; - goto err_out_xa; + goto err_out; } } @@ -464,9 +457,23 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (!qp->rq_entry) { uresp.sq_key = SIW_INVAL_UOBJ_KEY; rv = -ENOMEM; - goto err_out_xa; + goto err_out; } } + } + qp->tx_cpu = siw_get_tx_cpu(sdev); + if (qp->tx_cpu < 0) { + rv = -EINVAL; + goto err_out; + } + init_completion(&qp->qp_free); + + rv = siw_qp_add(sdev, qp); + if (rv) + goto err_out_tx; + + if (udata) { + uresp.qp_id = qp_id(qp); if (udata->outlen < sizeof(uresp)) { rv = -EINVAL; @@ -476,22 +483,19 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (rv) goto err_out_xa; } - qp->tx_cpu = siw_get_tx_cpu(sdev); - if (qp->tx_cpu < 0) { - rv = -EINVAL; - goto err_out_xa; - } + INIT_LIST_HEAD(&qp->devq); spin_lock_irqsave(&sdev->lock, flags); list_add_tail(&qp->devq, &sdev->qp_list); spin_unlock_irqrestore(&sdev->lock, flags); - init_completion(&qp->qp_free); - return 0; err_out_xa: xa_erase(&sdev->qp_xa, qp_id(qp)); +err_out_tx: + siw_put_tx_cpu(qp->tx_cpu); +err_out: if (uctx) { rdma_user_mmap_entry_remove(qp->sq_entry); rdma_user_mmap_entry_remove(qp->rq_entry); From f98ae09c727dcf34f745c875661c64b642e4abfa Mon Sep 17 00:00:00 2001 From: Xue Lei Date: Wed, 1 Jul 2026 20:10:42 +0800 Subject: [PATCH 2285/3297] mtd: fix double free and WARN_ON in add_mtd_device() error paths [ Upstream commit 9d4af746af8ce27eefc2338b2feaa1e01f28b6c3 ] When device_register() or mtd_nvmem_add() fails inside add_mtd_device() for a partition, the error handling triggers mtd_release() via put_device() or device_unregister(). mtd_release() calls release_mtd_partition() which frees the mtd_info structure. However, callers such as mtd_add_partition() and add_mtd_partitions() also call free_partition() in their error paths, resulting in a double free. Additionally, release_mtd_partition() hits WARN_ON(!list_empty( &mtd->part.node)) because the partition node is still linked in the parent's partitions list when the release callback fires from the add_mtd_device() error path. Fix this by overriding dev->type and dev->release before put_device() in the error paths, so that device_release() invokes a no-op function instead of mtd_release(). For the mtd_nvmem_add() failure case, device_unregister() is replaced with device_del() to separate the device removal from the final kobject reference drop, allowing the override to take effect before put_device() is called. The callers' error paths (list_del + free_partition) remain the sole owners of mtd_info lifetime on add_mtd_device() failure, which is the expected contract. The normal partition teardown path is not affected: del_mtd_device() goes through kref_put() -> mtd_device_release() -> device_unregister() with dev->type still set to &mtd_devtype, so mtd_release() -> release_mtd_partition() continues to work correctly for the regular removal case. Reported-by: syzbot+e9c76b56dc05023b8117@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e9c76b56dc05023b8117 Fixes: 19bfa9ebebb5 ("mtd: use refcount to prevent corruption") Signed-off-by: Xue Lei Signed-off-by: Miquel Raynal Signed-off-by: Sasha Levin --- drivers/mtd/mtdcore.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 64808493b4f5e..42cabd3c1efad 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -104,6 +104,15 @@ static void mtd_release(struct device *dev) device_destroy(&mtd_class, index + 1); } +/* + * No-op device release used in add_mtd_device() error paths. + * Prevents mtd_release() from being called via device_release(), + * which would free the mtd_info that the caller still manages. + */ +static void mtd_dev_release_nop(struct device *dev) +{ +} + static void mtd_device_release(struct kref *kref) { struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt); @@ -798,10 +807,8 @@ int add_mtd_device(struct mtd_info *mtd) mtd_check_of_node(mtd); of_node_get(mtd_get_of_node(mtd)); error = device_register(&mtd->dev); - if (error) { - put_device(&mtd->dev); + if (error) goto fail_added; - } /* Add the nvmem provider */ error = mtd_nvmem_add(mtd); @@ -839,8 +846,16 @@ int add_mtd_device(struct mtd_info *mtd) return 0; fail_nvmem_add: - device_unregister(&mtd->dev); + device_del(&mtd->dev); fail_added: + /* + * Clear type and set nop release to prevent mtd_release() -> + * release_mtd_partition() -> free_partition() from freeing mtd. + * The caller handles cleanup on failure. + */ + mtd->dev.type = NULL; + mtd->dev.release = mtd_dev_release_nop; + put_device(&mtd->dev); of_node_put(mtd_get_of_node(mtd)); fail_devname: idr_remove(&mtd_idr, i); From 124382a2a9756678f9c9db9a4ff369ecfac17a95 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Sat, 4 Jul 2026 16:27:36 +0530 Subject: [PATCH 2286/3297] selftests/alsa: Fix memory leak in find_controls error path [ Upstream commit cb89f0c1aed02eb233c4271f76f830b37e222ff6 ] In find_controls(), card_data is allocated with malloc() but when snd_ctl_open_lconf() fails, the code jumps to next_card without freeing the allocated memory. This results in a memory leak for each card where snd_ctl_open_lconf() fails. Add free(card_data) before goto next_card to ensure proper cleanup of the allocated memory in the error path. Fixes: 5aaf9efffc57 ("kselftest: alsa: Add simplistic test for ALSA mixer controls kselftest") Signed-off-by: Malaya Kumar Rout Link: https://patch.msgid.link/20260704105736.94874-1-malayarout91@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- tools/testing/selftests/alsa/mixer-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index e113dafa5c246..cfb7968fbac07 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -84,6 +84,7 @@ static void find_controls(void) if (err < 0) { ksft_print_msg("Failed to get hctl for card %d: %s\n", card, snd_strerror(err)); + free(card_data); goto next_card; } From 9e632a70f20449f34b158d617c3b751fc9181226 Mon Sep 17 00:00:00 2001 From: Aleksandrova Alyona Date: Wed, 24 Jun 2026 17:48:46 +0300 Subject: [PATCH 2287/3297] RDMA/irdma: Prevent overflows in memory contiguity checks [ Upstream commit 3cda0dfe8c651dcbb9e38977905d3d3b1750c4ab ] irdma_check_mem_contiguous() and irdma_check_mr_contiguous() verify that PBL entries describe physically contiguous memory ranges. Both functions calculate byte offsets using 32-bit operands. For example, with 4 KiB pages, pg_size * pg_idx overflows 32-bit arithmetic when pg_idx reaches 1048576. In the level-2 check, PBLE_PER_PAGE is 512, so i * pg_size * PBLE_PER_PAGE overflows when i reaches 2048. These values are reachable in the driver. For MRs, palloc->total_cnt comes from iwmr->page_cnt, which is calculated by ib_umem_num_dma_blocks(). The MR size is limited by IRDMA_MAX_MR_SIZE, so a 4 GiB MR with 4 KiB pages can reach page_cnt of 1048576. PBLE resources do not exclude this value either: for gen3, the limit is based on avail_sds * MAX_PBLE_PER_SD, and MAX_PBLE_PER_SD is 0x40000, so 4 SDs are enough for 1048576 PBLEs. Cast one operand to u64 before the multiplications so that the offset calculations are performed in 64-bit arithmetic. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs") Signed-off-by: Aleksandrova Alyona Link: https://patch.msgid.link/20260624144846.61242-1-aga@itb.spb.ru Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/irdma/verbs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 5f5b79109d547..4084168d0194f 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -2800,7 +2800,7 @@ static bool irdma_check_mem_contiguous(u64 *arr, u32 npages, u32 pg_size) u32 pg_idx; for (pg_idx = 0; pg_idx < npages; pg_idx++) { - if ((*arr + (pg_size * pg_idx)) != arr[pg_idx]) + if ((*arr + ((u64)pg_size * pg_idx)) != arr[pg_idx]) return false; } @@ -2833,7 +2833,7 @@ static bool irdma_check_mr_contiguous(struct irdma_pble_alloc *palloc, for (i = 0; i < lvl2->leaf_cnt; i++, leaf++) { arr = leaf->addr; - if ((*start_addr + (i * pg_size * PBLE_PER_PAGE)) != *arr) + if ((*start_addr + ((u64)i * pg_size * PBLE_PER_PAGE)) != *arr) return false; ret = irdma_check_mem_contiguous(arr, leaf->cnt, pg_size); if (!ret) From 9845a35986a658816f7752f7ebd7c455a4c7dfdf Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 11:01:17 +0800 Subject: [PATCH 2288/3297] xfrm: clear mode callbacks after failed mode setup [ Upstream commit 2538bd3cd1ff5af655908469544ac7b7ae259386 ] xfrm_state_gc_task can run long after a failed IPTFS state setup. In the reproduced case, __xfrm_init_state() cached x->mode_cbs, IPTFS setup returned -ENOMEM before publishing mode_data, and the temporary module reference from xfrm_get_mode_cbs() was dropped immediately. The dead state then kept x->mode_cbs until deferred GC ran after xfrm_iptfs had been unloaded. Clear x->mode_cbs when mode init or clone fails before publishing mode_data. Those states never installed mode-specific state or the long-term IPTFS module pin, so deferred GC has nothing mode-specific to destroy and must not retain a callback table pointer past the temporary lookup reference. The buggy scenario involves two paths, with each column showing the order within that path: failed setup path: 1. cache x->mode_cbs 2. mode setup fails before mode_data 3. drop the temporary module ref 4. dead state keeps x->mode_cbs cached GC/unload path: 1. xfrm_state_put() queues GC work 2. xfrm_iptfs unloads later 3. xfrm_state_gc_task runs 4. GC dereferences stale x->mode_cbs This also covers the failed clone path where clone_state() returns before publishing mode_data. Validation reproduced this kernel report: Kernel panic - not syncing: Fatal exception CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y failslab_stacktrace_filter matched xfrm_iptfs frames ack_error=-12 FAULT_INJECTION: forcing a failure BUG: unable to handle page fault Workqueue: events xfrm_state_gc_task RIP: xfrm_state_gc_task+0x142/0x650 Modules linked in: esp4_offload xfrm_user [last unloaded: xfrm_iptfs] Kernel panic - not syncing: Fatal exception Fixes: 4b3faf610cc6 ("xfrm: iptfs: add new iptfs xfrm mode impl") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_state.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 89daad5db10c9..b9049c2297bd5 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2070,8 +2070,11 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, x->mode_cbs = orig->mode_cbs; if (x->mode_cbs && x->mode_cbs->clone_state) { - if (x->mode_cbs->clone_state(x, orig)) + if (x->mode_cbs->clone_state(x, orig)) { + if (!x->mode_data) + x->mode_cbs = NULL; goto error; + } } @@ -3253,6 +3256,8 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack) if (x->mode_cbs->init_state) err = x->mode_cbs->init_state(x); module_put(x->mode_cbs->owner); + if (err && !x->mode_data) + x->mode_cbs = NULL; } error: return err; From d8aaf06b29f5a0b6186cf68d21c7d63678ee3891 Mon Sep 17 00:00:00 2001 From: Chen YanJun Date: Wed, 1 Jul 2026 11:31:52 +0800 Subject: [PATCH 2289/3297] xfrm: iptfs: propagate SKBFL_SHARED_FRAG in iptfs_skb_add_frags() [ Upstream commit 430ea57d6daf765e88f90046afbfd1e071cb7200 ] When iptfs_skb_add_frags() copies frag references from the source frag walk into a new SKB, it increments the page reference count via __skb_frag_ref() but does not propagate SKBFL_SHARED_FRAG to the destination SKB's skb_shinfo->flags. If the source SKB carries shared frags (e.g. from a page-pool backed receive path), the new inner SKB will appear to ESP as having privately owned frags. A subsequent esp_input() call for a nested transport-mode SA then takes the no-COW fast path and decrypts in place, writing over pages that are still referenced by the outer IPTFS SKB. This causes kernel-visible memory corruption and can trigger a panic. All other frag-transfer helpers in the kernel (skb_try_coalesce, skb_gro_receive, __pskb_copy_fclone, skb_shift, skb_segment) correctly propagate SKBFL_SHARED_FRAG; align iptfs_skb_add_frags() with this convention by setting the flag inside the loop immediately after __skb_frag_ref() and nr_frags++, so every exit path that attaches a frag unconditionally propagates SKBFL_SHARED_FRAG. Fixes: 5f2b6a909574 ("xfrm: iptfs: add skb-fragment sharing code") Signed-off-by: Chen YanJun Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_iptfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c index fe8e4f21b3281..d39253b06c4ef 100644 --- a/net/xfrm/xfrm_iptfs.c +++ b/net/xfrm/xfrm_iptfs.c @@ -480,6 +480,7 @@ static int iptfs_skb_add_frags(struct sk_buff *skb, } __skb_frag_ref(tofrag); shinfo->nr_frags++; + shinfo->flags |= SKBFL_SHARED_FRAG; /* see if we are done */ fraglen = tofrag->len; From ff636d7b7cba6dea82ecf580415ea57f2c1a11b6 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Thu, 2 Jul 2026 01:05:16 +0000 Subject: [PATCH 2290/3297] xfrm6: clear dst.dev on error to avoid double netdev_put in xfrm6_fill_dst() [ Upstream commit 136992de9bb91871084ae52d172610541c76e4d2 ] On the error path where in6_dev_get(dev) returns NULL, xfrm6_fill_dst() releases the device reference with netdev_put() but leaves xdst->u.dst.dev set. dst_destroy() later calls netdev_put(dst->dev) again, so the same net_device reference is released twice, underflowing its refcount (ref_tracker WARNING + "unregister_netdevice: waiting for to become free"). Clear xdst->u.dst.dev after the netdev_put(), the same way the XFRM device-offload paths xfrm_dev_state_add() and xfrm_dev_policy_add() in net/xfrm/xfrm_device.c NULL ->dev when releasing the reference on error. ref_tracker: reference already released. ref_tracker: allocated in: xfrm6_fill_dst (net/ipv6/xfrm6_policy.c:86) ... udpv6_sendmsg (net/ipv6/udp.c:1696) ... ref_tracker: freed in: xfrm6_fill_dst (net/ipv6/xfrm6_policy.c:90) ... WARNING: lib/ref_tracker.c:322 at ref_tracker_free+0x58b/0x780 dst_destroy (net/core/dst.c:115) rcu_core handle_softirqs ... Fixes: 84c4a9dfbf43 ("xfrm6: release dev before returning error") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/ipv6/xfrm6_policy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 125ea9a5b8a08..3b749475f6ed6 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -88,6 +88,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt6.rt6i_idev = in6_dev_get(dev); if (!xdst->u.rt6.rt6i_idev) { netdev_put(dev, &xdst->u.dst.dev_tracker); + xdst->u.dst.dev = NULL; return -ENODEV; } From 7acc5ed2f33608a3d83b64f50a5766843b6e2485 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Fri, 3 Jul 2026 05:19:32 +0000 Subject: [PATCH 2291/3297] xfrm: policy: preallocate inexact bins before xfrm_hash_rebuild reinsert [ Upstream commit f38f8cce2f7e79775b3db7e8a5eacda04ac908e4 ] xfrm_hash_rebuild()'s first loop preallocates the bins/chains the reinsert loop needs, so the reinsert (after hlist_del_rcu()) cannot allocate or fail. But its guard is inverted: it skips policies with prefixlen < threshold and preallocates for the rest. prefixlen < threshold is exactly when policy_hash_bysel() returns NULL and the reinsert takes the allocating xfrm_policy_inexact_insert() path. So the loop preallocates for the exact policies (which never allocate) and skips the inexact ones, whose bin/node is then allocated GFP_ATOMIC during reinsert. On failure the error path only WARN_ONCE()s and continues, leaving a poisoned bydst node; the next rebuild's hlist_del_rcu() dereferences LIST_POISON2 and takes a GPF. Reachable under memory pressure, deterministic via failslab. Invert the guard so preallocation covers exactly the reinserted policies; the reinsert then allocates nothing and cannot fail. Crash: Oops: general protection fault, probably for non-canonical address 0xfbd59c0000000024: 0000 [#1] SMP KASAN NOPTI KASAN: maybe wild-memory-access in range [0xdead...] ... Workqueue: events xfrm_hash_rebuild RIP: 0010:xfrm_hash_rebuild+0x5b3/0x1190 RAX: dead000000000122 (LIST_POISON2 + offset) ... Call Trace: hlist_del_rcu (include/linux/rculist.h:599) xfrm_hash_rebuild (net/xfrm/xfrm_policy.c:1365) process_one_work (kernel/workqueue.c:3322) worker_thread (kernel/workqueue.c:3486) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) ... Kernel panic - not syncing: Fatal exception in interrupt Fixes: 24969facd704 ("xfrm: policy: store inexact policies in an rhashtable") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Florian Westphal Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_policy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 4dad1f8172286..25f9cbd3ce6a4 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1329,8 +1329,8 @@ static void xfrm_hash_rebuild(struct work_struct *work) } } - if (policy->selector.prefixlen_d < dbits || - policy->selector.prefixlen_s < sbits) + if (policy->selector.prefixlen_d >= dbits && + policy->selector.prefixlen_s >= sbits) continue; bin = xfrm_policy_inexact_alloc_bin(policy, dir); From 9293574ac208d18c11073538851fb69355beb3b5 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 20 Jun 2026 00:25:42 +0800 Subject: [PATCH 2292/3297] wifi: cfg80211: cancel sched scan results work on unregister [ Upstream commit edf0730be33696a1bd142792830d392129e495cc ] cfg80211_sched_scan_results() can queue rdev->sched_scan_res_wk from a driver result notification while a scheduled scan request is present. The work callback recovers the containing cfg80211_registered_device and then locks the wiphy and walks the scheduled-scan request list. wiphy_unregister() already makes the wiphy unreachable and drains rdev work items before cfg80211_dev_free() can release the object, but it does not drain sched_scan_res_wk. A queued or running result work item can therefore cross the unregister/free boundary and access freed rdev state. The buggy scenario involves two paths, with each column showing the order within that path: scheduled-scan result path: unregister/free path: 1. cfg80211_sched_scan_results() 1. interface teardown stops and queues rdev->sched_scan_res_wk. removes the scheduled scan request. 2. cfg80211_wq starts the work 2. wiphy_unregister() drains other item and recovers rdev. rdev work items. 3. The worker locks rdev->wiphy 3. cfg80211_dev_free() destroys and and walks rdev state. frees rdev. Cancel sched_scan_res_wk in wiphy_unregister() alongside the other rdev work items. cancel_work_sync() removes a pending result notification and waits for an already running callback, so cfg80211_dev_free() cannot free rdev while this work item is still active. Validation reproduced this kernel report: BUG: KASAN: use-after-free in cfg80211_sched_scan_results_wk+0x4a6/0x530 Workqueue: cfg80211 cfg80211_sched_scan_results_wk [cfg80211] Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 cfg80211_sched_scan_results_wk+0x4a6/0x530 srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x224/0x430 kasan_report+0xac/0xe0 lockdep_hardirqs_on_prepare+0xea/0x1a0 process_one_work+0x8d0/0x18f0 (kernel/workqueue.c:3212) lock_is_held_type+0x8f/0x100 worker_thread+0x5ad/0xfd0 __kthread_parkme+0xc6/0x200 kthread+0x31e/0x410 trace_hardirqs_on+0x1a/0x170 ret_from_fork+0x576/0x810 __switch_to+0x57e/0xe20 __switch_to_asm+0x33/0x70 ret_from_fork_asm+0x1a/0x30 Fixes: 807f8a8c3004 ("cfg80211/nl80211: add support for scheduled scans") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260619162542.3878296-1-zzzccc427@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/wireless/core.c b/net/wireless/core.c index 6b204ca91a140..34b76479f888b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1195,6 +1195,7 @@ void wiphy_unregister(struct wiphy *wiphy) /* this has nothing to do now but make sure it's gone */ cancel_work_sync(&rdev->wiphy_work); + cancel_work_sync(&rdev->sched_scan_res_wk); cancel_work_sync(&rdev->rfkill_block); cancel_work_sync(&rdev->conn_work); flush_work(&rdev->event_work); From f442e581a88937671a22ceb3806c186265ef6254 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Sat, 20 Jun 2026 12:22:39 +0530 Subject: [PATCH 2293/3297] wifi: ipw2100: fix potential memory leak in ipw2100_pci_init_one() [ Upstream commit 0d388f62031dbabcba0f44bb91b59f10e88cac17 ] The memory allocated in the ipw2100_alloc_device() function is not freed in some of the error paths in ipw2100_pci_init_one(). Fix that by converting the direct return into a goto to the error path return. The error path when pci_enable_device() fails cannot jump to fail, since at this point priv is not set, so perform error handling inline. Fixes: 2c86c275015c ("Add ipw2100 wireless driver.") Signed-off-by: Abdun Nihaal Link: https://patch.msgid.link/20260620065242.93798-1-nihaal@cse.iitm.ac.in Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 215814861cbda..0808e69c3c178 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -6162,6 +6162,8 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_enable_device.\n"); + free_libipw(dev, 0); + pci_iounmap(pci_dev, ioaddr); return err; } @@ -6174,16 +6176,14 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_set_dma_mask.\n"); - pci_disable_device(pci_dev); - return err; + goto fail; } err = pci_request_regions(pci_dev, DRV_NAME); if (err) { printk(KERN_WARNING DRV_NAME "Error calling pci_request_regions.\n"); - pci_disable_device(pci_dev); - return err; + goto fail; } /* We disable the RETRY_TIMEOUT register (0x41) to keep From e67dc2b8d5ac4bb804000b6732768a9ae678912f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 Jun 2026 21:48:56 +0200 Subject: [PATCH 2294/3297] wifi: cfg80211: Fix an error handling path in cfg80211_wext_siwscan() [ Upstream commit c6659f66d4ee4841aafae5659d2ef5e4c5c63cb6 ] If the test against IEEE80211_MAX_SSID_LEN fails, then 'creq' leaks. Use the existing error handling path to fix it. Fixes: 2a5193119269 ("cfg80211/nl80211: scanning (and mac80211 update to use it)") Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/a1be7eea4da0da18f90589af252bb76a18a61978.1781984889.git.christophe.jaillet@wanadoo.fr Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/scan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 2f2104bb0d52f..27d5f99dbb228 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3627,8 +3627,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, /* translate "Scan for SSID" request */ if (wreq) { if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { - if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; + if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out; + } memcpy(creq->req.ssids[0].ssid, wreq->essid, wreq->essid_len); creq->req.ssids[0].ssid_len = wreq->essid_len; From 6dc76371a9a360c29de00df5b11563102d9d675a Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 20 Jun 2026 21:45:18 -0500 Subject: [PATCH 2295/3297] wifi: mac80211_hwsim: clamp virtio RX length before skb_put [ Upstream commit 10a2b430f8f06ae14b9590b6f6faa6b588ef0654 ] hwsim_virtio_rx_work() passes the virtqueue used-ring length reported by the device straight to skb_put() on a fixed-size receive skb. A backend reporting a length larger than the skb tailroom drives skb_put() past the buffer end and hits skb_over_panic() -- a host-triggerable guest panic (denial of service). Clamp the length to the skb's available room before skb_put(). A conforming device never reports more than the posted buffer size, so valid frames are unaffected; a truncated over-report then fails the length/header checks in hwsim_virtio_handle_cmd() and is dropped, so truncating rather than dropping here cannot be turned into a parsing problem. Fixes: 5d44fe7c9808 ("mac80211_hwsim: add frame transmission support over virtio") Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260620-b4-disp-474bee37-v1-1-1a4d37f3e2d4@proton.me Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/virtual/mac80211_hwsim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 20815fdc9d376..a0724e1c53070 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -6898,6 +6898,7 @@ static void hwsim_virtio_rx_work(struct work_struct *work) skb->data = skb->head; skb_reset_tail_pointer(skb); + len = min(len, skb_end_offset(skb)); skb_put(skb, len); hwsim_virtio_handle_cmd(skb); From d62b55b7c7dc62887d7fd5648fb38f0bfaef53ae Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 21 Jun 2026 02:35:31 -0700 Subject: [PATCH 2296/3297] wifi: mac80211: fix unsol_bcast_probe_resp double free on alloc failure [ Upstream commit 1d067abcd37062426c59ec73dbc4e87a63f33fea ] ieee80211_set_unsol_bcast_probe_resp() calls kfree_rcu() on the old template before allocating the replacement. If the kzalloc() then fails, it returns -ENOMEM while link->u.ap.unsol_bcast_probe_resp still points at the object already queued for freeing. A later update or AP teardown re-queues that same rcu_head; the second free is caught by KASAN when the RCU sheaf is processed in softirq: BUG: KASAN: double-free in rcu_free_sheaf (mm/slub.c:5850) Free of addr ffff88800d06f300 by task exploit/145 ... __rcu_free_sheaf_prepare (mm/slub.c:2634 mm/slub.c:2940) rcu_free_sheaf (mm/slub.c:5850) rcu_core (kernel/rcu/tree.c:2617 kernel/rcu/tree.c:2869) handle_softirqs (kernel/softirq.c:622) The buggy address belongs to the cache kmalloc-128 of size 128 Queue the old object for kfree_rcu() only after the new one is published, matching ieee80211_set_probe_resp() and ieee80211_set_s1g_short_beacon(). Fixes: 3b1c256eb4ae ("wifi: mac80211: fixes in FILS discovery updates") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260621093532.884188-1-xmei5@asu.edu Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/cfg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d32eacbb7517d..001e4bb55b619 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1157,8 +1157,6 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata, link_conf->unsol_bcast_probe_resp_interval = params->interval; old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata); - if (old) - kfree_rcu(old, rcu_head); if (params->tmpl && params->tmpl_len) { new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); @@ -1171,6 +1169,9 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata, RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL); } + if (old) + kfree_rcu(old, rcu_head); + *changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP; return 0; } From 5baaa1042f71dd4b8e418f2cdd516808702d229b Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 21 Jun 2026 02:35:32 -0700 Subject: [PATCH 2297/3297] wifi: mac80211: fix fils_discovery double free on alloc failure [ Upstream commit 286e52a799fa158bdbd77da1426c4d93f9a6e7ad ] ieee80211_set_fils_discovery() calls kfree_rcu() on the old template before allocating the replacement. If the kzalloc() then fails, it returns -ENOMEM while link->u.ap.fils_discovery still points at the object already queued for freeing. A later update or AP teardown (ieee80211_stop_ap()) re-queues that same rcu_head; the second free is caught by KASAN when the RCU sheaf is processed in softirq: BUG: KASAN: double-free in rcu_free_sheaf (mm/slub.c:5850) Free of addr ffff88800c065280 by task swapper/0/0 ... __rcu_free_sheaf_prepare (mm/slub.c:2634 mm/slub.c:2940) rcu_free_sheaf (mm/slub.c:5850) rcu_core (kernel/rcu/tree.c:2617 kernel/rcu/tree.c:2869) handle_softirqs (kernel/softirq.c:622) The buggy address belongs to the cache kmalloc-96 of size 96 Queue the old object for kfree_rcu() only after the new one is published, matching ieee80211_set_probe_resp() and ieee80211_set_s1g_short_beacon(). Fixes: 3b1c256eb4ae ("wifi: mac80211: fixes in FILS discovery updates") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260621093532.884188-2-xmei5@asu.edu Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/cfg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 001e4bb55b619..f6232948bc0f4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1124,9 +1124,6 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata, fd->max_interval = params->max_interval; old = sdata_dereference(link->u.ap.fils_discovery, sdata); - if (old) - kfree_rcu(old, rcu_head); - if (params->tmpl && params->tmpl_len) { new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); if (!new) @@ -1138,6 +1135,9 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata, RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL); } + if (old) + kfree_rcu(old, rcu_head); + *changed |= BSS_CHANGED_FILS_DISCOVERY; return 0; } From 6cda91bbb8dc3d22ef0323008a12dcf73a5129da Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 24 Jun 2026 16:53:43 +0800 Subject: [PATCH 2298/3297] wifi: libertas: fix memory leak in helper_firmware_cb() [ Upstream commit 63c2391deefb31e1b801b7f32bd502ca4808639b ] helper_firmware_cb() neglects to free the single-stage firmware image after a successful async load, leading to a memory leak in the USB firmware-download path. Fix this memory leak by calling release_firmware() immediately after lbs_fw_loaded() returns. 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 the current wireless tree. An x86_64 allyesconfig build showed no new warnings. As we do not have compatible Libertas USB hardware for exercising this firmware-download path, no runtime testing was able to be performed. Fixes: 1dfba3060fe7 ("libertas: move firmware lifetime handling to firmware.c") Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260624085343.575508-1-dawei.feng@seu.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/marvell/libertas/firmware.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/marvell/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c index f124110944b7e..9bf7d4c207b9e 100644 --- a/drivers/net/wireless/marvell/libertas/firmware.c +++ b/drivers/net/wireless/marvell/libertas/firmware.c @@ -78,6 +78,7 @@ static void helper_firmware_cb(const struct firmware *firmware, void *context) } else { /* No main firmware needed for this helper --> success! */ lbs_fw_loaded(priv, 0, firmware, NULL); + release_firmware(firmware); } } From 2aa1789880fa5e41049b0f6a74a4fc2fa1997610 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 16:30:28 +0800 Subject: [PATCH 2299/3297] wifi: mac80211: defer link RX stats percpu free to RCU [ Upstream commit aa2eb62525188269cdd402a583b9a8ed94657ff0 ] sta_remove_link() frees a removed MLO link's RX stats percpu buffer right away, but defers only the link container to RCU: sta_info_free_link(&alloc->info); kfree_rcu(alloc, rcu_head); The RX fast path reads link_sta under rcu_read_lock and writes the percpu stats. A reader that resolved link_sta before the removal keeps the pointer. The container stays alive from the kfree_rcu, so the read still works. But the percpu block it points to is already freed. This needs uses_rss. That is when pcpu_rx_stats exists. The full STA teardown frees the deflink stats only after synchronize_net(). The link removal path had no such barrier. The race is hard to win in practice, but the free should still wait for RCU. Free the link together with its data from a single RCU callback, so the percpu block is reclaimed only after readers drain. Fixes: c71420db653a ("wifi: mac80211: RCU-ify link STA pointers") Link: https://lore.kernel.org/r/20260626080158.3589711-1-maoyixie.tju@gmail.com Suggested-by: Johannes Berg Co-developed-by: Kaixuan Li Signed-off-by: Kaixuan Li Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/20260627083028.3826810-1-maoyixie.tju@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/sta_info.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index b0d9bb830f293..54a6b20a4c5cd 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -355,6 +355,15 @@ static void sta_info_free_link(struct link_sta_info *link_sta) free_percpu(link_sta->pcpu_rx_stats); } +static void sta_link_free_rcu(struct rcu_head *head) +{ + struct sta_link_alloc *alloc = + container_of(head, struct sta_link_alloc, rcu_head); + + sta_info_free_link(&alloc->info); + kfree(alloc); +} + static void sta_accumulate_removed_link_stats(struct sta_info *sta, int link_id) { struct link_sta_info *link_sta = wiphy_dereference(sta->local->hw.wiphy, @@ -427,10 +436,8 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id, RCU_INIT_POINTER(sta->link[link_id], NULL); RCU_INIT_POINTER(sta->sta.link[link_id], NULL); - if (alloc) { - sta_info_free_link(&alloc->info); - kfree_rcu(alloc, rcu_head); - } + if (alloc) + call_rcu(&alloc->rcu_head, sta_link_free_rcu); ieee80211_sta_recalc_aggregates(&sta->sta); } From d38f5d868a0a4770e3bcd0925e16c46acdbc9509 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 27 Jun 2026 17:05:10 -0700 Subject: [PATCH 2300/3297] wifi: p54: validate RX frame length in p54_rx_eeprom_readback() [ Upstream commit ebd6d37fa94bee929e0b4c9ca19fdf9b1dcf6cea ] p54_rx_eeprom_readback() copies the requested EEPROM slice out of a device-supplied readback frame without checking that the skb actually holds that many bytes. Commit da1b9a55ff11 ("wifi: p54: prevent buffer-overflow in p54_rx_eeprom_readback()") closed the destination overflow by copying a fixed priv->eeprom_slice_size (and rejecting a mismatched advertised len), but the source side is still unbounded: nothing verifies the frame is long enough to supply that many bytes. A malicious USB device can send a short frame whose advertised len matches priv->eeprom_slice_size while the payload is truncated. The equality check passes and memcpy() reads past the end of the skb, leaking adjacent heap: BUG: KASAN: slab-out-of-bounds in p54_rx (drivers/net/wireless/intersil/p54/txrx.c:507) Read of size 1016 at addr ffff88800f077114 by task swapper/0/0 Call Trace: ... __asan_memcpy (mm/kasan/shadow.c:105) p54_rx (drivers/net/wireless/intersil/p54/txrx.c:507) p54u_rx_cb (drivers/net/wireless/intersil/p54/p54usb.c:163) __usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1657) dummy_timer (drivers/usb/gadget/udc/dummy_hcd.c:2005) ... The buggy address belongs to the object at ffff88800f0770c0 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 84 bytes inside of allocated 704-byte region [ffff88800f0770c0, ffff88800f077380) Check that the slice fits in the skb before copying. Fixes: 7cb770729ba8 ("p54: move eeprom code into common library") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Acked-by: Christian Lamparter Link: https://patch.msgid.link/20260628000510.4152481-1-xmei5@asu.edu Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/intersil/p54/txrx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c index 1294a1d6528e2..9f491334c8d04 100644 --- a/drivers/net/wireless/intersil/p54/txrx.c +++ b/drivers/net/wireless/intersil/p54/txrx.c @@ -499,11 +499,19 @@ static void p54_rx_eeprom_readback(struct p54_common *priv, if (le16_to_cpu(eeprom->v2.len) != priv->eeprom_slice_size) return; + if (eeprom->v2.data + priv->eeprom_slice_size > + skb_tail_pointer(skb)) + return; + memcpy(priv->eeprom, eeprom->v2.data, priv->eeprom_slice_size); } else { if (le16_to_cpu(eeprom->v1.len) != priv->eeprom_slice_size) return; + if (eeprom->v1.data + priv->eeprom_slice_size > + skb_tail_pointer(skb)) + return; + memcpy(priv->eeprom, eeprom->v1.data, priv->eeprom_slice_size); } From 133684982dd0c24359fcc641d19d89cc17d6e5ef Mon Sep 17 00:00:00 2001 From: Peddolla Harshavardhan Reddy Date: Fri, 3 Jul 2026 13:55:23 +0530 Subject: [PATCH 2301/3297] wifi: cfg80211: convert pmsr_free_wk to wiphy_work to fix deadlock [ Upstream commit 2b0eab425e1f658d8fe1df7590e3b9af5959505e ] When a netlink socket that owns a PMSR session is closed, cfg80211_release_pmsr() clears the request's nl_portid and queues pmsr_free_wk to call cfg80211_pmsr_process_abort() asynchronously. If the interface tears down concurrently, cfg80211_pmsr_wdev_down() is called under wiphy_lock and calls cancel_work_sync(&pmsr_free_wk) to wait for any running work. The work function acquires wiphy_lock via guard(wiphy) before calling process_abort. This is a deadlock: wdev_down holds wiphy_lock and blocks inside cancel_work_sync(); pmsr_free_wk blocks trying to acquire that same wiphy_lock. Neither thread can proceed. The same deadlock is reachable from cfg80211_leave_locked(), which calls cfg80211_pmsr_wdev_down() for all interface types under wiphy_lock. Fix this by converting pmsr_free_wk from a plain work_struct to a wiphy_work. The wiphy_work dispatcher holds wiphy_lock when running work items, so the explicit guard(wiphy) in the work function is no longer needed. wiphy_work_cancel() can be called safely while holding wiphy_lock - since wiphy_lock prevents the work from running concurrently, wiphy_work_cancel() never blocks, eliminating the deadlock. Remove the cancel_work_sync() for pmsr_free_wk from the NETDEV_GOING_DOWN handler. cfg80211_leave(), called unconditionally just before it, already cancels any pending work under wiphy_lock via wiphy_work_cancel() inside cfg80211_pmsr_wdev_down(). Fixes: 6dccbc9f3e1d ("wifi: cfg80211: cancel pmsr_free_wk in cfg80211_pmsr_wdev_down") Signed-off-by: Peddolla Harshavardhan Reddy Link: https://patch.msgid.link/20260703082523.2629324-1-peddolla.reddy@oss.qualcomm.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- include/net/cfg80211.h | 2 +- net/wireless/core.c | 3 +-- net/wireless/core.h | 2 +- net/wireless/pmsr.c | 8 +++----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 820e299f06b5e..1509be85139ec 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -6740,7 +6740,7 @@ struct wireless_dev { struct list_head pmsr_list; spinlock_t pmsr_lock; - struct work_struct pmsr_free_wk; + struct wiphy_work pmsr_free_wk; unsigned long unprot_beacon_reported; diff --git a/net/wireless/core.c b/net/wireless/core.c index 34b76479f888b..efbf7b3046342 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1444,7 +1444,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev) INIT_LIST_HEAD(&wdev->mgmt_registrations); INIT_LIST_HEAD(&wdev->pmsr_list); spin_lock_init(&wdev->pmsr_lock); - INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); + wiphy_work_init(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); #ifdef CONFIG_CFG80211_WEXT wdev->wext.default_key = -1; @@ -1579,7 +1579,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, } /* since we just did cfg80211_leave() nothing to do there */ cancel_work_sync(&wdev->disconnect_wk); - cancel_work_sync(&wdev->pmsr_free_wk); break; case NETDEV_DOWN: wiphy_lock(&rdev->wiphy); diff --git a/net/wireless/core.h b/net/wireless/core.h index d5d78752227af..9bfd39af1742e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -566,7 +566,7 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len); void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid); void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev); -void cfg80211_pmsr_free_wk(struct work_struct *work); +void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work); void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id); void cfg80211_remove_links(struct wireless_dev *wdev); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 13801cf35e9fc..d2546c27879fa 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -625,13 +625,11 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev) } } -void cfg80211_pmsr_free_wk(struct work_struct *work) +void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work) { struct wireless_dev *wdev = container_of(work, struct wireless_dev, pmsr_free_wk); - guard(wiphy)(wdev->wiphy); - cfg80211_pmsr_process_abort(wdev); } @@ -647,7 +645,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev) } spin_unlock_bh(&wdev->pmsr_lock); - cancel_work_sync(&wdev->pmsr_free_wk); + wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk); if (found) cfg80211_pmsr_process_abort(wdev); @@ -662,7 +660,7 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid) list_for_each_entry(req, &wdev->pmsr_list, list) { if (req->nl_portid == portid) { req->nl_portid = 0; - schedule_work(&wdev->pmsr_free_wk); + wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk); } } spin_unlock_bh(&wdev->pmsr_lock); From fb052a6e2fa866384d8edc237746583ec94c15af Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Wed, 10 Jun 2026 19:22:09 +0800 Subject: [PATCH 2302/3297] wifi: nl80211: free RNR data on MBSSID mismatch [ Upstream commit 07a95ec2b54774201fdf4ef7ffb0ca2ab19ed29c ] nl80211_parse_beacon() rejects EMA RNR data when there are fewer RNR entries than MBSSID entries. The rejected RNR allocation has not been attached to the beacon data yet, so free it before returning the error. Fixes: dbbb27e183b1 ("cfg80211: support RNR for EMA AP") Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260610112208.1308-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/nl80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6e41a4269e1c5..e765c992eb20e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6232,8 +6232,10 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, if (IS_ERR(rnr)) return PTR_ERR(rnr); - if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) + if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) { + kfree(rnr); return -EINVAL; + } bcn->rnr_ies = rnr; } From f649dc9c5e657dbacb58f149cbe5e1fc98b12871 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 00:19:46 +0800 Subject: [PATCH 2303/3297] wifi: cfg80211: derive S1G beacon TSF from S1G fields [ Upstream commit 4e5a4641e7b4763656336b7891d01359aaf363cd ] cfg80211_inform_bss_frame_data() parses S1G beacons with the extension frame layout, but still reads the TSF from the regular probe response layout after the S1G branch. For S1G beacons that reads bytes at the regular management-frame timestamp offset instead of the S1G timestamp. Use the 32-bit S1G beacon timestamp and the S1G Beacon Compatibility element's TSF completion field when informing an S1G BSS. Keep the regular management-frame timestamp read in the non-S1G branch. Fixes: 9eaffe5078ca ("cfg80211: convert S1G beacon to scan results") Signed-off-by: Zhao Li Tested-by: Lachlan Hodges Reviewed-by: Lachlan Hodges Link: https://patch.msgid.link/20260611161943.91069-6-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/scan.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 27d5f99dbb228..af1e2ce1628da 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3326,14 +3326,15 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, bssid = ext->u.s1g_beacon.sa; capability = le16_to_cpu(compat->compat_info); beacon_interval = le16_to_cpu(compat->beacon_int); + tsf = le32_to_cpu(ext->u.s1g_beacon.timestamp); + tsf |= (u64)le32_to_cpu(compat->tsf_completion) << 32; } else { bssid = mgmt->bssid; beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); + tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); } - tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); - if (ieee80211_is_probe_resp(mgmt->frame_control)) ftype = CFG80211_BSS_FTYPE_PRESP; else if (ext) From f8c547e543e12b6d9202cd36193c051670107071 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:18:55 +0800 Subject: [PATCH 2304/3297] wifi: nl80211: validate nested MBSSID IE blobs [ Upstream commit 7f4b01812323443b55e4c65381c9dc851ff009e3 ] Validate each nested NL80211_ATTR_MBSSID_ELEMS entry as a well-formed information-element stream before storing it for beacon construction. RNR parsing already validates each nested blob with validate_ie_attr() before storing it. Apply the same syntactic IE validation to MBSSID entries before counting and copying their data and length pointers. Fixes: dc1e3cb8da8b ("nl80211: MBSSID and EMA support in AP mode") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612131854.43575-3-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/nl80211.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e765c992eb20e..5583edf485277 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6034,7 +6034,8 @@ static int nl80211_parse_mbssid_config(struct wiphy *wiphy, } static struct cfg80211_mbssid_elems * -nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) +nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs, + struct netlink_ext_ack *extack) { struct nlattr *nl_elems; struct cfg80211_mbssid_elems *elems; @@ -6045,6 +6046,12 @@ nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) return ERR_PTR(-EINVAL); nla_for_each_nested(nl_elems, attrs, rem_elems) { + int ret; + + ret = validate_ie_attr(nl_elems, extack); + if (ret) + return ERR_PTR(ret); + if (num_elems >= 255) return ERR_PTR(-EINVAL); num_elems++; @@ -6216,7 +6223,8 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, if (attrs[NL80211_ATTR_MBSSID_ELEMS]) { struct cfg80211_mbssid_elems *mbssid = nl80211_parse_mbssid_elems(&rdev->wiphy, - attrs[NL80211_ATTR_MBSSID_ELEMS]); + attrs[NL80211_ATTR_MBSSID_ELEMS], + extack); if (IS_ERR(mbssid)) return PTR_ERR(mbssid); From 0b6efde0ed9707303ce14a938d919eb54fcc329d Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:18:56 +0800 Subject: [PATCH 2305/3297] wifi: nl80211: constrain MBSSID TX link ID range [ Upstream commit 172f06023669f0a96d32511669ff45c600731380 ] MBSSID transmitted-profile link IDs are valid only in the range 0..IEEE80211_MLD_MAX_NUM_LINKS - 1. Constrain the nl80211 policy to reject out-of-range values during attribute validation. Fixes: 37523c3c47b3 ("wifi: nl80211: add link id of transmitted profile for MLO MBSSID") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612131854.43575-4-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5583edf485277..237e5d7eeb47c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -501,7 +501,7 @@ nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = { [NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 }, [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG }, [NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID] = - NLA_POLICY_MAX(NLA_U8, IEEE80211_MLD_MAX_NUM_LINKS), + NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS - 1), }; static const struct nla_policy From 0caf6416bfbb3ee4c6b780115fb05b4f41979b13 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:36:57 +0800 Subject: [PATCH 2306/3297] wifi: cfg80211: validate PMSR measurement type data [ Upstream commit 41aa973eb05922848dded26875c55ef982ac1c49 ] PMSR request parsing accepts missing or duplicated measurement type entries in NL80211_PMSR_REQ_ATTR_DATA. Track whether one measurement type was already provided, reject a second one immediately, and return an error if the request data block contains no measurement type at all. Fixes: 9bb7e0f24e7e7 ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612133656.92900-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/pmsr.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index d2546c27879fa..fd14a3c2eab45 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -196,6 +196,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, { struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1]; + bool have_measurement_type = false; struct nlattr *treq; int err, rem; @@ -248,6 +249,14 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, } nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) { + if (have_measurement_type) { + NL_SET_ERR_MSG_ATTR(info->extack, treq, + "multiple measurement types in request data"); + return -EINVAL; + } + + have_measurement_type = true; + switch (nla_type(treq)) { case NL80211_PMSR_TYPE_FTM: err = pmsr_parse_ftm(rdev, treq, out, info); @@ -257,10 +266,16 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, "unsupported measurement type"); err = -EINVAL; } + if (err) + return err; } - if (err) - return err; + if (!have_measurement_type) { + NL_SET_ERR_MSG_ATTR(info->extack, + req[NL80211_PMSR_REQ_ATTR_DATA], + "missing measurement type in request data"); + return -EINVAL; + } return 0; } From cfbda103aeae61071a122a6fc2bfe98cffbd7165 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:04 +0800 Subject: [PATCH 2307/3297] wifi: cfg80211: validate PMSR FTM preamble range [ Upstream commit 36230936468f0ba4930e94aef496fc229d4bb951 ] PMSR FTM request parsing accepts preamble values outside the enumerated nl80211 preamble range. Reject out-of-range values before using them in the parser capability bit test using the policy. Fixes: 9bb7e0f24e7e7 ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612133703.93274-2-enderaoelyther@gmail.com [drop unnecessary check] Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/nl80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 237e5d7eeb47c..776dcf8835d71 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -347,7 +347,9 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = { static const struct nla_policy nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG }, - [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = + NLA_POLICY_RANGE(NLA_U32, NL80211_PREAMBLE_LEGACY, + NL80211_PREAMBLE_HE), [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] = NLA_POLICY_MAX(NLA_U8, 15), [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 }, From fbaa8c31ef940e32d66544117e7c9093ddd28877 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:11 +0800 Subject: [PATCH 2308/3297] wifi: cfg80211: reject unsupported PMSR FTM location requests [ Upstream commit 69ef6a7ec277f16d216be8da2b3cbe872786c999 ] PMSR FTM location request flags are syntactically valid, but they must be rejected when the device capability does not advertise support for them. Return an error immediately after rejecting unsupported LCI or civic location request bits so the request cannot reach the driver. Fixes: 9bb7e0f24e7e7 ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612133710.93544-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/pmsr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index fd14a3c2eab45..06563f6084870 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -114,6 +114,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI], "FTM: LCI request not supported"); + return -EOPNOTSUPP; } out->ftm.request_civicloc = @@ -122,6 +123,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC], "FTM: civic location request not supported"); + return -EOPNOTSUPP; } out->ftm.trigger_based = From 44eda4a8d1dcda72d357d97311a96b4f01fc83f6 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 23:24:41 +0800 Subject: [PATCH 2309/3297] wifi: mac80211: avoid non-S1G AID fallback for S1G assoc [ Upstream commit 035ed430ce6a2c35b01e211844a9f0a7643e57a4 ] When assoc_data->s1g is set and no AID Response element is present, falling back to mgmt->u.assoc_resp.aid reads the non-S1G association-response layout. Keep the fallback for non-S1G only. If a successful S1G association response omits the AID Response element, abandon the association instead of proceeding with AID 0. Initialize aid to 0 for other S1G responses so the later mask and logging flow keeps a defined value without reading the non-S1G layout. Fixes: 2a8a6b7c4cb0 ("wifi: mac80211: handle station association response with S1G") Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260612152440.25955-2-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/mlme.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8854e47b05672..9ec4125c06d19 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6476,7 +6476,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; - u16 capab_info, status_code, aid; + u16 capab_info, status_code, aid = 0; struct ieee80211_elems_parse_params parse_params = { .bss = NULL, .link_id = -1, @@ -6553,8 +6553,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (elems->aid_resp) aid = le16_to_cpu(elems->aid_resp->aid); - else + else if (!assoc_data->s1g) aid = le16_to_cpu(mgmt->u.assoc_resp.aid); + else if (status_code == WLAN_STATUS_SUCCESS) + goto abandon_assoc; /* * The 5 MSB of the AID field are reserved for a non-S1G STA. For From a424985c3ef2a87ce6057a853e18d0c441a86be8 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Mon, 6 Jul 2026 22:08:41 +0800 Subject: [PATCH 2310/3297] wifi: mac80211: free AP_VLAN bc_buf SKBs outside IRQ lock [ Upstream commit f3858d5b1432098c1936e03d6e03dd0e33facf60 ] ieee80211_do_stop() removes AP_VLAN packets from the parent AP ps->bc_buf while holding ps->bc_buf.lock with IRQs disabled. It then calls ieee80211_free_txskb() before dropping the lock. ieee80211_free_txskb() is not just a passive SKB release. For SKBs with TX status state it can report a dropped frame through cfg80211/nl80211, and that path can reach netlink tap transmit. This is the same reason the pending queue cleanup in ieee80211_do_stop() already unlinks SKBs under the queue lock and frees them after IRQ state is restored. The buggy scenario involves two paths, with each column showing the order within that path: AP_VLAN management TX: AP_VLAN stop: 1. attach ACK-status state 1. clear the running state 2. queue a multicast SKB on 2. take ps->bc_buf.lock with IRQs parent ps->bc_buf disabled 3. unlink the AP_VLAN SKB 4. call ieee80211_free_txskb() Unlink matching AP_VLAN SKBs from ps->bc_buf under the existing lock, but move them to a local free queue. Drop the lock and restore IRQ state before calling ieee80211_free_txskb(). WARNING: kernel/softirq.c:430 at __local_bh_enable_ip Fixes: 397a7a24ef8c ("mac80211: free ps->bc_buf skbs on vlan device stop") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260706140841.581566-1-zzzccc427@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/iface.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 72c129478da08..499126317a914 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -581,6 +581,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do WARN_ON(!list_empty(&sdata->u.ap.vlans)); } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { /* remove all packets in parent bc_buf pointing to this dev */ + __skb_queue_head_init(&freeq); ps = &sdata->bss->ps; spin_lock_irqsave(&ps->bc_buf.lock, flags); @@ -588,10 +589,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do if (skb->dev == sdata->dev) { __skb_unlink(skb, &ps->bc_buf); local->total_ps_buffered--; - ieee80211_free_txskb(&local->hw, skb); + __skb_queue_tail(&freeq, skb); } } spin_unlock_irqrestore(&ps->bc_buf.lock, flags); + + skb_queue_walk_safe(&freeq, skb, tmp) { + __skb_unlink(skb, &freeq); + ieee80211_free_txskb(&local->hw, skb); + } } if (going_down) From 5c342437ea44bb829680ca9e4f683dd5b325b219 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 14:44:01 +0800 Subject: [PATCH 2311/3297] wifi: brcmfmac: initialize SDIO data work before cleanup [ Upstream commit 2a665946e0407a05a3f81bd56a08553c446498e0 ] brcmf_sdio_probe() stores the newly allocated bus in sdiodev->bus before allocating the ordered workqueue. If that allocation fails, the function jumps to fail and calls brcmf_sdio_remove(). brcmf_sdio_remove() unconditionally cancels bus->datawork. Initialize the work item before the first failure path that can reach brcmf_sdio_remove(), so the cleanup path always observes a valid work object. This issue was found by our static analysis tool and then confirmed by manual review of the probe error path and the remove-time work drain. The problem pattern is an early setup failure that reaches a cleanup helper which cancels an embedded work item before its initializer has run. A QEMU PoC forced alloc_ordered_workqueue() to fail at the same point in brcmf_sdio_probe(), before INIT_WORK(&bus->datawork) is reached. The resulting fail path calls brcmf_sdio_remove(), and DEBUG_OBJECTS reports the invalid work drain with brcmf_sdio_probe() and brcmf_sdio_remove() in the stack. Fixes: 9982464379e8 ("brcmfmac: make sdio suspend wait for threads to freeze") Signed-off-by: Runyu Xiao Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260619064401.1048976-1-runyu.xiao@seu.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index a0e88dbaaebe6..9ab62a2667c7b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4465,6 +4465,7 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->sdiodev = sdiodev; sdiodev->bus = bus; skb_queue_head_init(&bus->glom); + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); bus->txbound = BRCMF_TXBOUND; bus->rxbound = BRCMF_RXBOUND; bus->txminmax = BRCMF_TXMINMAX; @@ -4479,7 +4480,6 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) goto fail; } brcmf_sdiod_freezer_count(sdiodev); - INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); bus->brcmf_wq = wq; /* attempt to attach to the dongle */ From 11ac7a5e75f5132f1778e0c60981d30dc29fb869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?HE=20WEI=20=28=E3=82=AE=E3=82=AB=E3=82=AF=29?= Date: Tue, 7 Jul 2026 18:48:28 +0900 Subject: [PATCH 2312/3297] wifi: cfg80211: bound element ID read when checking non-inheritance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit cb8afea4655ff004fa7feee825d5c79783525383 ] cfg80211_is_element_inherited() reads the first data octet of the candidate element (id = elem->data[0]) to look it up in an extension non-inheritance list. It does so after testing elem->id, but without verifying that the element actually has a data octet. A zero-length extension element (WLAN_EID_EXTENSION with length 0) therefore makes it read one octet past the end of the element. _ieee802_11_parse_elems_full() runs this check for every element of a frame once a non-inheritance context exists -- e.g. while parsing a per-STA profile of a Multi-Link element in a (re)association response, or a non-transmitted BSS profile -- so a crafted frame from an AP can trigger a one-octet slab-out-of-bounds read during element parsing: BUG: KASAN: slab-out-of-bounds in cfg80211_is_element_inherited Read of size 1 ... in net/wireless/scan.c Return early (treat the element as inherited) when an extension element carries no data, mirroring the existing handling of empty ID lists. The bug was found by fuzzing ieee802_11_parse_elems_full() under KASAN. Fixes: f7dacfb11475 ("cfg80211: support non-inheritance element") Signed-off-by: HE WEI (ギカク) Link: https://patch.msgid.link/20260707094828.16465-1-skyexpoc@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index af1e2ce1628da..11f7a27bb7046 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -205,7 +205,7 @@ bool cfg80211_is_element_inherited(const struct element *elem, return true; if (elem->id == WLAN_EID_EXTENSION) { - if (!ext_id_len) + if (!ext_id_len || !elem->datalen) return true; loop_len = ext_id_len; list = &non_inherit_elem->data[3 + id_len]; From cf5708c9d78c98214c62b1e5d049cd527a543b8e Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:38 +0000 Subject: [PATCH 2313/3297] firmware: arm_ffa: Fix out-of-bound writes in ffa_setup_and_transmit() [ Upstream commit 3383ffb7ef937317361713ffcc21921a7848511a ] Sashiko (locally) reports multiple out-of-bound issues in ffa_setup_and_transmit: 1) Writing ep_mem_access->reserved can write out of bounds for FFA versions < 1.2 as ffa_emad_size_get() returns 16 bytes in that case while reserved has an offset of 24. Instead of zeroing fields, memset the struct to zero first based on the FFA version. 2) Make sure there is enough size to write constituents. While at it, convert the only sizeof() in the driver that uses a type instead of variable. Reviewed-by: Sudeep Holla Fixes: 111a833dc5cb ("firmware: arm_ffa: Set reserved/MBZ fields to zero in the memory descriptors") Signed-off-by: Mostafa Saleh Signed-off-by: Sebastian Ene Link: https://patch.msgid.link/20260702103848.1647249-2-sebastianene@google.com Signed-off-by: Marc Zyngier Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index feded15bb5e92..6f576c39e4832 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -719,11 +719,10 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, for (idx = 0; idx < args->nattrs; idx++) { ep_mem_access = buffer + ffa_mem_desc_offset(buffer, idx, drv_info->version); + memset(ep_mem_access, 0, ffa_emad_size_get(drv_info->version)); ep_mem_access->receiver = args->attrs[idx].receiver; ep_mem_access->attrs = args->attrs[idx].attrs; ep_mem_access->composite_off = composite_offset; - ep_mem_access->flag = 0; - ep_mem_access->reserved = 0; ffa_emad_impdef_value_init(drv_info->version, ep_mem_access->impdef_val, args->attrs[idx].impdef_val); @@ -763,7 +762,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, constituents = buffer; } - if ((void *)constituents - buffer > max_fragsize) { + if ((void *)constituents + sizeof(*constituents) - buffer > max_fragsize) { pr_err("Memory Region Fragment > Tx Buffer size\n"); return -EFAULT; } @@ -772,7 +771,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, constituents->pg_cnt = args->sg->length / FFA_PAGE_SIZE; constituents->reserved = 0; constituents++; - frag_len += sizeof(struct ffa_mem_region_addr_range); + frag_len += sizeof(*constituents); } while ((args->sg = sg_next(args->sg))); return ffa_transmit_fragment(func_id, addr, buf_sz, frag_len, From b39b08e6bee812514b449dc874076890e6b871a0 Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 2 Jul 2026 10:38:39 +0000 Subject: [PATCH 2314/3297] firmware: arm_ffa: Fix Endpoint Memory Access Descriptor offset calculation [ Upstream commit b4d961351aa84fdf0148783fb1f3a1391b8a0adb ] Use the descriptor's `ep_mem_offset` to calculate the start of the endpoint memory access array and to comply with the FF-A spec instead of defaulting to `sizeof(struct ffa_mem_region)`. This requires moving `ffa_mem_region_additional_setup()` earlier in the setup flow. Also, add sanity checks to ensure the calculated descriptor offsets do not exceed `max_fragsize`. Fixes: 113580530ee7 ("firmware: arm_ffa: Update memory descriptor to support v1.1 format") Reviewed-by: Sudeep Holla Signed-off-by: Mostafa Saleh Signed-off-by: Sebastian Ene Link: https://patch.msgid.link/20260702103848.1647249-3-sebastianene@google.com Signed-off-by: Marc Zyngier Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 20 +++++++++++++++----- include/linux/arm_ffa.h | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 6f576c39e4832..1d0bd586da4d6 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -707,19 +707,30 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, struct ffa_composite_mem_region *composite; struct ffa_mem_region_addr_range *constituents; struct ffa_mem_region_attributes *ep_mem_access; - u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg); + u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg), ep_offset; + u32 emad_end, emad_size = ffa_emad_size_get(drv_info->version); mem_region->tag = args->tag; mem_region->flags = args->flags; mem_region->sender_id = drv_info->vm_id; mem_region->attributes = ffa_memory_attributes_get(func_id); + + ffa_mem_region_additional_setup(drv_info->version, mem_region); composite_offset = ffa_mem_desc_offset(buffer, args->nattrs, drv_info->version); + if (composite_offset + sizeof(*composite) > max_fragsize) + return -ENXIO; for (idx = 0; idx < args->nattrs; idx++) { - ep_mem_access = buffer + - ffa_mem_desc_offset(buffer, idx, drv_info->version); - memset(ep_mem_access, 0, ffa_emad_size_get(drv_info->version)); + ep_offset = ffa_mem_desc_offset(buffer, idx, drv_info->version); + if (check_add_overflow(ep_offset, emad_size, &emad_end)) + return -ENXIO; + + if (emad_end > max_fragsize) + return -ENXIO; + + ep_mem_access = buffer + ep_offset; + memset(ep_mem_access, 0, emad_size); ep_mem_access->receiver = args->attrs[idx].receiver; ep_mem_access->attrs = args->attrs[idx].attrs; ep_mem_access->composite_off = composite_offset; @@ -729,7 +740,6 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, } mem_region->handle = 0; mem_region->ep_count = args->nattrs; - ffa_mem_region_additional_setup(drv_info->version, mem_region); composite = buffer + composite_offset; composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg); diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 81e603839c4a5..62d67dae8b703 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -445,7 +445,7 @@ ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version) if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(ffa_version)) offset += offsetof(struct ffa_mem_region, ep_mem_offset); else - offset += sizeof(struct ffa_mem_region); + offset += buf->ep_mem_offset; return offset; } From 4801f6690f9846e7ffde8c694c3b4b305ebefbf7 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Sat, 27 Jun 2026 13:12:05 +0000 Subject: [PATCH 2315/3297] ASoC: meson: aiu: fifo-spdif: soft reset the S/PDIF datapath on start/stop [ Upstream commit 6b59c53c8adc2b522327407af5e1793a65b67e4b ] The I2S FIFO soft-resets its fast domain on start (AIU_RST_SOFT bit 0 + AIU_I2S_SYNC read in aiu_fifo_i2s_trigger), mirroring the downstream vendor driver's audio_out_i2s_enable(). The S/PDIF FIFO has no equivalent: it only toggles the IEC958 DCU, so a stale datapath FIFO can be replayed, producing the "machine gun noise" buffer underrun - on start when switching outputs, and on stop when playback ends. The latter is audible on devices with an always-on S/PDIF-fed DAC (e.g. the ES7144 on the WeTek Play2). The vendor driver resets the IEC958 fast domain (AIU_RST_SOFT bit 2) on both enable and disable (audio_hw_958_enable), and when reconfiguring (audio_hw_958_reset clears AIU_958_DCU_FF_CTRL then resets). Do the same: reset before enabling the DCU on start, and before disabling on stop. Fixes: 6ae9ca9ce986bf ("ASoC: meson: aiu: add i2s and spdif support") Signed-off-by: Christian Hewitt Reviewed-by: Martin Blumenstingl Link: https://patch.msgid.link/20260627131205.808800-1-christianshewitt@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/meson/aiu-fifo-spdif.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c index e0e00ec026dcc..a9861c5d66374 100644 --- a/sound/soc/meson/aiu-fifo-spdif.c +++ b/sound/soc/meson/aiu-fifo-spdif.c @@ -24,6 +24,7 @@ #define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7) #define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) #define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0) +#define AIU_RST_SOFT_958_FAST BIT(2) #define AIU_FIFO_SPDIF_BLOCK 8 @@ -68,11 +69,15 @@ static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_soc_component_write(component, AIU_RST_SOFT, + AIU_RST_SOFT_958_FAST); fifo_spdif_dcu_enable(component, true); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: + snd_soc_component_write(component, AIU_RST_SOFT, + AIU_RST_SOFT_958_FAST); fifo_spdif_dcu_enable(component, false); break; default: From f33ad19e3e3d63c0d43b8be96754aae156c8c5af Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:36 +0530 Subject: [PATCH 2316/3297] ASoC: amd: ps: disable MSI on resume in ACP PCI driver [ Upstream commit 5893013efabb056399a01e267f410cf76eba25eb ] BIOS/firmware may re-enable MSI in PCI config space during system level resume even though this driver only uses legacy INTx interrupts. If MSI is left enabled with stale address/data registers, the device will write interrupts to a bogus address causing IOMMU IO_PAGE_FAULT and interrupt delivery failure. Clear the MSI Enable bit before reinitializing the ACP hardware on system level resume. Fixes: 491628388005 ("ASoC: amd: ps: add callback functions for acp pci driver pm ops") Signed-off-by: Vijendar Mukunda Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260707060130.2514138-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/ps/pci-ps.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c62299b292041..34efb42be768f 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -683,8 +683,37 @@ static int snd_acp_runtime_resume(struct device *dev) return acp_hw_runtime_resume(dev); } +static void acp_disable_msi_on_resume(struct pci_dev *pdev) +{ + u16 control; + + if (!pdev->msi_cap) + return; + + pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &control); + if (control & PCI_MSI_FLAGS_ENABLE) { + dev_warn(&pdev->dev, + "ACP: MSI unexpectedly enabled after resume (flags=0x%04x), disabling\n", + control); + control &= ~PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, control); + } +} + static int snd_acp_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); + + /* + * BIOS/firmware may re-enable MSI in PCI config space during + * system resume even though this driver only uses legacy INTx + * interrupts. If MSI is left enabled with stale address/data + * registers, the device will write interrupts to a bogus address + * causing IOMMU IO_PAGE_FAULT and interrupt delivery failure. + * Explicitly clear the MSI Enable bit before reinitializing + * the ACP hardware. + */ + acp_disable_msi_on_resume(pdev); return acp_hw_resume(dev); } From 3b2d32f528152037d15d0afd1b654af0058ab7d8 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:37 +0530 Subject: [PATCH 2317/3297] ASoC: amd: ps: fix wrong ACP version string in pci_request_regions() [ Upstream commit f7697ecf6eab9d4887dd731038b3dc405c7e755e ] The driver handles ACP6.3/7.0/7.1/7.2 platforms but the region was claimed with the stale name "AMD ACP6.2 audio" left over from the original ACP6.2 driver. Correct it to "AMD ACP6.3 audio". Fixes: 95e43a170bb1 ("ASoC: amd: add Pink Sardine ACP PCI driver") Signed-off-by: Vijendar Mukunda Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260707060130.2514138-3-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/ps/pci-ps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 34efb42be768f..8f37c250d4b89 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -595,7 +595,7 @@ static int snd_acp63_probe(struct pci_dev *pci, return -ENODEV; } - ret = pci_request_regions(pci, "AMD ACP6.2 audio"); + ret = pci_request_regions(pci, "AMD ACP6.3 audio"); if (ret < 0) { dev_err(&pci->dev, "pci_request_regions failed\n"); goto disable_pci; From 9f393d8160435a303db4aec7685c9aeff9b1aa94 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:38 +0530 Subject: [PATCH 2318/3297] ASoC: amd: ps: replace bitwise OR with logical OR in IRQ return check [ Upstream commit dec5aaa27603e1d7b426ce3504af6d1a62e4d444 ] The condition 'irq_flag | wake_irq_flag' uses bitwise OR to combine two integer flags that are used as booleans. Replace with logical OR '||' to correctly express the intended boolean check. Signed-off-by: Vijendar Mukunda Reviewed-by: Mario Limonciello (AMD) Fixes: 7f91f012c1df0 ("ASoC: amd: ps: fix for irq handler return status") Link: https://patch.msgid.link/20260707060130.2514138-4-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/ps/pci-ps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 8f37c250d4b89..cf06d4ae299b5 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -248,7 +248,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) if (sdw_dma_irq_flag) return IRQ_WAKE_THREAD; - if (irq_flag | wake_irq_flag) + if (irq_flag || wake_irq_flag) return IRQ_HANDLED; else return IRQ_NONE; From 7f2cb99eaf53c5f88931a3001b252301633a582e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 8 Jul 2026 11:34:30 +0100 Subject: [PATCH 2319/3297] ASoC: cs42l43: Correct report for forced microphone jack [ Upstream commit f74e6e15485b68b92b2807071e822db6309b7e38 ] Currently if the jack is forced to the microphone mode, it will report as line in. Correct the report to microphone. Fixes: fc918cbe874e ("ASoC: cs42l43: Add support for the cs42l43") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260708103430.1395207-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs42l43-jack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index ecba6c7952384..36fb67059e065 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -310,6 +310,7 @@ irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data) #define CS42L43_JACK_ABSENT 0x0 #define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT) +#define CS42L43_JACK_MICROPHONE (SND_JACK_MECHANICAL | SND_JACK_MICROPHONE) #define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE) #define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET) #define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) @@ -871,7 +872,7 @@ static const struct cs42l43_jack_override_mode { .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) | CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK, - .report = CS42L43_JACK_LINEIN, + .report = CS42L43_JACK_MICROPHONE, }, [CS42L43_JACK_RAW_OPTICAL] = { .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, From fb343716fad469bdbdbde2a923baa72a124e4250 Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 6 Jul 2026 21:01:09 +0530 Subject: [PATCH 2320/3297] ASoC: tas2562: fix deprecated 'shut-down' GPIO always cleared after lookup [ Upstream commit 3238c634725afbb2a137fdda762208510828f71d ] In tas2562_parse_dt(), the fallback lookup for the deprecated "shut-down" GPIO property is broken due to a missing pair of braces. The code intends to reset sdz_gpio to NULL only when the lookup returns an error that is not -EPROBE_DEFER (so the driver gracefully continues without a GPIO). However, without braces the statement: tas2562->sdz_gpio = NULL; falls outside the IS_ERR() check and is executed unconditionally for every path through the if block, including a successful GPIO lookup. This means any device using the deprecated 'shut-down' DT property will always have sdz_gpio == NULL after probe, making the GPIO completely non-functional. Fix this by adding the missing braces to scope the NULL assignment inside the IS_ERR() branch, matching the pattern already used for the primary 'shutdown' GPIO lookup above. Fixes: f78a97003b8b ("ASoC: tas2562: Update shutdown GPIO property") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260706153109.10953-1-udaykhare77@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/tas2562.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 8e00dcc09d0c2..795c66cb47134 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -675,11 +675,12 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562) if (tas2562->sdz_gpio == NULL) { tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down", GPIOD_OUT_HIGH); - if (IS_ERR(tas2562->sdz_gpio)) + if (IS_ERR(tas2562->sdz_gpio)) { if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) return -EPROBE_DEFER; - tas2562->sdz_gpio = NULL; + tas2562->sdz_gpio = NULL; + } } if (tas2562->model_id == TAS2110) From 640a33e77f91bb72125a662376d824adcc6b15e0 Mon Sep 17 00:00:00 2001 From: Pushpendra Singh Date: Wed, 8 Jul 2026 12:53:39 +0530 Subject: [PATCH 2321/3297] firmware: arm_scmi: Rate-limit queue-full warnings in IRQ context [ Upstream commit a4447c0693830d5ecadd6e755cb7fdc55d86aacc ] The scmi_notify() function is called from interrupt context to queue received notification events onto a per-protocol kfifo. When the kfifo is full, it logs a warning via dev_warn() for every dropped event. Under conditions where the platform sends a burst of SCMI notifications faster than the deferred worker can drain the queue, this results in a flood of dev_warn() calls from IRQ context. Each call acquires the console lock and may execute blocking console writes, causing the CPU to be held in interrupt context for an extended period and leading to observable system stalls. Fix this by switching to dev_warn_ratelimited() to limit the frequency of log messages when the notification queue is full. This reduces console overhead in interrupt context and prevents CPU stalls caused by excessive logging, while still preserving diagnostic visibility. Fixes: bd31b249692e ("firmware: arm_scmi: Add notification dispatch and delivery") Signed-off-by: Pushpendra Singh Link: https://patch.msgid.link/20260708072339.3021140-1-pushpendra.singh@oss.qualcomm.com Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_scmi/notify.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 2047edbdc5f6b..9bf7f43ab868c 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -600,9 +600,9 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, return -EINVAL; } if (kfifo_avail(&r_evt->proto->equeue.kfifo) < sizeof(eh) + len) { - dev_warn(handle->dev, - "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n", - proto_id, evt_id, ktime_to_ns(ts)); + dev_warn_ratelimited(handle->dev, + "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n", + proto_id, evt_id, ktime_to_ns(ts)); return -ENOMEM; } From 88f87cb4b52ed7a1595fd3420d0d9c236ce40824 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Jul 2026 19:25:39 +0200 Subject: [PATCH 2322/3297] cpufreq: Make cpufreq_update_pressure() fall back to cpuinfo.max_freq [ Upstream commit d2d5c129d07ea8eb91cd8a8633b5774116c4d171 ] If arch_scale_freq_ref() is not defined for a given arch (like x86, for example), cpufreq_update_pressure() will always set cpufreq_pressure to zero for all CPUs in the system, which is generally problematic on systems with asymmetric capacity [1]. However, in the absence of arch_scale_freq_ref(), it is reasonable to assume that cpuinfo.max_freq is the maximum sustainable frequency for the given cpufreq policy. Moreover, there are cases in which arch_scale_freq_ref() would need to be defined to return essentially the cpuinfo.max_freq value anyway (for example, intel_pstate on hybrid platforms). For the above reasons, update cpufreq_update_pressure() to fall back to using cpuinfo.max_freq as the reference frequency if zero is returned by arch_scale_freq_ref(). Fixes: 75d659317bb1 ("cpufreq: Add a cpufreq pressure feedback for the scheduler") Link: https://lore.kernel.org/lkml/CAKfTPtBuRLfYNnR4w--cFZYZy-R8gaPEgVwCcaMmbCcJ2H-muQ@mail.gmail.com/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Viresh Kumar Reviewed-by: Zhongqiu Han Tested-by: Ricardo Neri # cluster scheduling Acked-by: Vincent Guittot Link: https://patch.msgid.link/5086499.GXAFRqVoOG@rafael.j.wysocki Signed-off-by: Sasha Levin --- drivers/cpufreq/cpufreq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a70df77f9b7d1..ced96d9e254ee 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2584,6 +2584,9 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy) cpu = cpumask_first(policy->related_cpus); max_freq = arch_scale_freq_ref(cpu); + if (!max_freq) + max_freq = policy->cpuinfo.max_freq; + capped_freq = policy->max; /* From c9574b8a8edeb4edd3ac6472c27ef7184bdb2baa Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 6 Jul 2026 11:01:59 +0200 Subject: [PATCH 2323/3297] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF [ Upstream commit ec4215683e47424c9c4762fd3c60f552a3119142 ] pppol2tp_recv() runs in the L2TP UDP-encap softirq RX path: l2tp_udp_encap_recv() -> l2tp_recv_common() -> pppol2tp_recv() -> ppp_input(&po->chan) It runs under rcu_read_lock() holding only an l2tp_session reference and takes NO reference on the internal PPP channel (struct channel, chan->ppp) that ppp_input() dereferences. The pppox socket is SOCK_RCU_FREE, so 'po' and the embedded ppp_channel are RCU-safe. But the internal struct channel is a separate allocation that ppp_release_channel() frees with a plain kfree(): close(data socket) -> pppol2tp_release() -> pppox_unbind_sock() -> ppp_unregister_channel() -> ppp_release_channel() -> kfree(pch) For a channel that is bound (PPPIOCGCHAN) but not attached to a ppp unit (no PPPIOCCONNECT, pch->ppp == NULL) and not bridged, teardown skips both ppp_disconnect_channel()'s synchronize_net() and ppp_unbridge_channels()'s synchronize_rcu(), so the kfree() has no grace period. rcu_read_lock() in pppol2tp_recv() does not protect against a plain kfree(), so an in-flight ppp_input() on one CPU can dereference the channel just freed by close() on another CPU. The bug is reachable by an unprivileged user. Defer the channel free to an RCU callback via call_rcu() so the grace period fences any in-flight ppp_input(). The disconnect and unbridge teardown paths already fence with synchronize_net()/synchronize_rcu(); call_rcu() does the same here without stalling the close() path. Fixes: ee40fb2e1eb5 ("l2tp: protect sock pointer of struct pppol2tp_session with RCU") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Reviewed-by: Qingfang Deng Link: https://patch.msgid.link/E793FCF2-58DE-4387-A983-C7B4BC3158BD@doyensec.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ppp/ppp_generic.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 507d216256c0d..72e98286fba2f 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -185,6 +185,7 @@ struct channel { struct list_head clist; /* link in list of channels per unit */ spinlock_t upl; /* protects `ppp' and 'bridge' */ struct channel __rcu *bridge; /* "bridged" ppp channel */ + struct rcu_head rcu; /* for RCU-deferred free of the channel */ #ifdef CONFIG_PPP_MULTILINK u8 avail; /* flag used in multilink stuff */ u8 had_frag; /* >= 1 fragments have been sent */ @@ -3570,6 +3571,18 @@ ppp_disconnect_channel(struct channel *pch) return err; } +/* Purge after the grace period: a late ppp_input() may still queue an + * skb on pch->file.rq before the last RCU reader drains. + */ +static void ppp_release_channel_free(struct rcu_head *rcu) +{ + struct channel *pch = container_of(rcu, struct channel, rcu); + + skb_queue_purge(&pch->file.xq); + skb_queue_purge(&pch->file.rq); + kfree(pch); +} + /* * Free up the resources used by a ppp channel. */ @@ -3585,9 +3598,7 @@ static void ppp_destroy_channel(struct channel *pch) pr_err("ppp: destroying undead channel %p !\n", pch); return; } - skb_queue_purge(&pch->file.xq); - skb_queue_purge(&pch->file.rq); - kfree(pch); + call_rcu(&pch->rcu, ppp_release_channel_free); } static void __exit ppp_cleanup(void) @@ -3600,6 +3611,7 @@ static void __exit ppp_cleanup(void) device_destroy(&ppp_class, MKDEV(PPP_MAJOR, 0)); class_unregister(&ppp_class); unregister_pernet_device(&ppp_net_ops); + rcu_barrier(); /* wait for RCU callbacks before module unload */ } /* From cb8be318b4432abd88d3172ec157330f27a5f7a7 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 4 Jul 2026 10:14:21 -0700 Subject: [PATCH 2324/3297] ipv4: fib: free fib_alias with kfree_rcu() on insert error path [ Upstream commit f2f152e94a67bc746afaf05a1b2702c195553112 ] fib_table_insert() publishes new_fa into the leaf's fa_list with fib_insert_alias() before calling the fib entry notifiers. When a notifier fails, the error path removes new_fa with fib_remove_alias() (hlist_del_rcu) and frees it right away with kmem_cache_free(). fib_table_lookup() walks that list under rcu_read_lock() only, so a concurrent lookup that already reached new_fa keeps reading it after the free: BUG: KASAN: slab-use-after-free in fib_table_lookup (net/ipv4/fib_trie.c:1601) Read of size 1 at addr ffff88810676d4eb by task exploit/297 Call Trace: fib_table_lookup (net/ipv4/fib_trie.c:1601) ip_route_output_key_hash_rcu (net/ipv4/route.c:2814) ip_route_output_key_hash (net/ipv4/route.c:2705) __ip4_datagram_connect (net/ipv4/datagram.c:49) udp_connect (net/ipv4/udp.c:2144) __sys_connect (net/socket.c:2167) __x64_sys_connect (net/socket.c:2173) do_syscall_64 entry_SYSCALL_64_after_hwframe which belongs to the cache ip_fib_alias of size 56 Triggering the error path needs CAP_NET_ADMIN and a registered fib notifier that can reject a route; a netdevsim device whose IPv4 FIB resource is exhausted is enough. Free new_fa with alias_free_mem_rcu(), as fib_table_delete() already does for a fib_alias removed from the trie. Fixes: a6c76c17df02 ("ipv4: Notify route after insertion to the routing table") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260704171421.1786806-1-bestswngs@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/fib_trie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 1308213791f19..2dc87e2156d37 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1385,7 +1385,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, out_remove_new_fa: fib_remove_alias(t, tp, l, new_fa); out_free_new_fa: - kmem_cache_free(fn_alias_kmem, new_fa); + alias_free_mem_rcu(new_fa); out: fib_release_info(fi); err: From c75a950e77356e526672cba4584080c6c8b793b6 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 5 Jul 2026 22:24:36 -0500 Subject: [PATCH 2325/3297] net/iucv: take a reference on the socket found in afiucv_hs_rcv() [ Upstream commit 4fa349156043dc119721d067329714179f501749 ] afiucv_hs_rcv() looks up the destination socket under iucv_sk_list.lock, drops the lock, and then passes the socket to the afiucv_hs_callback_*() handlers without holding a reference. AF_IUCV sockets are not RCU-protected and are freed synchronously by iucv_sock_kill() -> sock_put(), so a concurrent close can free the socket in the window between read_unlock() and the handler, which then dereferences freed memory (for example sk->sk_data_ready() in afiucv_hs_callback_syn()). Take a reference with sock_hold() while the socket is still on the list and release it with sock_put() once the handler has run. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Signed-off-by: Bryam Vargas Reviewed-by: Hidayath Khan Link: https://patch.msgid.link/20260705-b4-disp-fc79c0dc-v1-1-d2cdcb57afa9@proton.me Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/iucv/af_iucv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index c66b90c912e78..bd0405373c5e9 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2089,6 +2089,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, } } } + if (sk) + sock_hold(sk); read_unlock(&iucv_sk_list.lock); if (!iucv) sk = NULL; @@ -2138,6 +2140,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, kfree_skb(skb); } + if (sk) + sock_put(sk); return err; } From 2c77ed279c4bb58f3e63579659c46f09356a4124 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Sat, 27 Jun 2026 12:57:25 +0200 Subject: [PATCH 2326/3297] udmabuf: Ensure to perform cache synchronisation in begin_cpu_udmabuf() [ Upstream commit 1d0e25c1ddf2063c499264fb2ba0fa6a3e4f8a00 ] The message of commit 504e2b4ab97a ("dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning") says: > The CPU sync at map/unmap time is also redundant for udmabuf: > begin_cpu_udmabuf() and end_cpu_udmabuf() already perform explicit > cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU > access is requested through the dma-buf interface. This, however, does not apply to the first time begin_cpu_udmabuf() is called on an udmabuf, in which case the implementation previously relied on get_sg_table() to perform the cache synchronisation. Ensure to call dma_sync_sgtable_for_cpu() in that case as well. Fixes: 504e2b4ab97a ("dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning") Signed-off-by: Robert Mader Reviewed-by: Mikhail Gavrilov Signed-off-by: Vivek Kasireddy Link: https://patch.msgid.link/20260627105725.9083-1-robert.mader@collabora.com Signed-off-by: Sasha Levin --- drivers/dma-buf/udmabuf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index be47d1ff6c508..30b0f0c72e928 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -249,21 +249,22 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, { struct udmabuf *ubuf = buf->priv; struct device *dev = ubuf->device->this_device; - int ret = 0; if (!ubuf->sg) { ubuf->sg = get_sg_table(dev, buf, direction); if (IS_ERR(ubuf->sg)) { + int ret; + ret = PTR_ERR(ubuf->sg); ubuf->sg = NULL; + return ret; } else { ubuf->sg_dir = direction; } - } else { - dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction); } - return ret; + dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction); + return 0; } static int end_cpu_udmabuf(struct dma_buf *buf, From c7a15091237205770bd9bd4d14eb1f3029d97a34 Mon Sep 17 00:00:00 2001 From: David Jeffery Date: Mon, 15 Jun 2026 13:46:30 -0400 Subject: [PATCH 2327/3297] scsi: core: wake eh reliably when using scsi_schedule_eh [ Upstream commit dccf3b1798b70f94e958b3d00b83010399e6fb05 ] Drivers which use the scsi_schedule_eh function to run the error handler currently risk the error handler thread never waking once all commands are timed out or inactive. There is no enforced memory order between setting the host into error recovery state and counting busy commands. This can result in a race with scsi_dec_host_busy where neither CPU sees both conditions of all commands inactive and the host error state to request waking the error handler. To fix this, run the scsi_schedule_eh's scsi_eh_wakeup from a new work item which will use rcu to ensure scsi_schedule_eh's call to scsi_host_busy will occur after the error state is globally visible and will be seen by any current scsi_dec_host_busy callers. Fixes: 6eb045e092ef ("scsi: core: avoid host-wide host_busy counter for scsi_mq") Signed-off-by: David Jeffery Link: https://patch.msgid.link/20260615174630.11492-1-djeffery@redhat.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/hosts.c | 2 ++ drivers/scsi/scsi_error.c | 22 +++++++++++++++++++++- drivers/scsi/scsi_priv.h | 1 + include/scsi/scsi_host.h | 3 +++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 17173239301e6..a1055fe4922f6 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -343,6 +343,7 @@ static void scsi_host_dev_release(struct device *dev) /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */ rcu_barrier(); + cancel_work_sync(&shost->eh_work); if (shost->tmf_work_q) destroy_workqueue(shost->tmf_work_q); if (shost->ehandler) @@ -408,6 +409,7 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); + INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup); index = ida_alloc(&host_index_ida, GFP_KERNEL); if (index < 0) { diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 2fd4ca96b3089..13d46b6e0359e 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -73,6 +73,26 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy) } } +void scsi_rcu_eh_wakeup(struct work_struct *work) +{ + struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work); + unsigned long flags; + unsigned int busy; + + /* + * Ensure any running scsi_dec_host_busy has completed its rcu section + * so changes to host state and host_eh_scheduled are visible to all + * future calls of scsi_dec_host_busy + */ + synchronize_rcu(); + + busy = scsi_host_busy(shost); + + spin_lock_irqsave(shost->host_lock, flags); + scsi_eh_wakeup(shost, busy); + spin_unlock_irqrestore(shost->host_lock, flags); +} + /** * scsi_schedule_eh - schedule EH for SCSI host * @shost: SCSI host to invoke error handling on. @@ -88,7 +108,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) { shost->host_eh_scheduled++; - scsi_eh_wakeup(shost, scsi_host_busy(shost)); + queue_work(shost->tmf_work_q, &shost->eh_work); } spin_unlock_irqrestore(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 5b2b19f5e8ec8..68866b18a6005 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -91,6 +91,7 @@ extern enum blk_eh_timer_return scsi_timeout(struct request *req); extern int scsi_error_handler(void *host); extern enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *cmd); extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy); +extern void scsi_rcu_eh_wakeup(struct work_struct *work); extern void scsi_eh_scmd_add(struct scsi_cmnd *); void scsi_eh_ready_devs(struct Scsi_Host *shost, struct list_head *work_q, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index f5a2432612360..936403c15e53d 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -714,6 +714,9 @@ struct Scsi_Host { */ struct device *dma_dev; + /* Used for an rcu-synchronizing eh wakeup */ + struct work_struct eh_work; + /* Delay for runtime autosuspend */ int rpm_autosuspend_delay; From daa80b422ed920a3c0c45153020b0ad7af7fb5a5 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:26 -0700 Subject: [PATCH 2328/3297] ata: sata_dwc_460ex: enable SATA interrupts only after IRQ handler is registered [ Upstream commit 4bbc16a353a98023e5ddfca7c1fc0e49971cf4d0 ] sata_dwc_enable_interrupts() is called before platform_get_irq() and ata_host_activate(), leaving the SATA controller's interrupt mask enabled without a registered handler. If a later step fails (irq request, phy init, etc.) or if the controller asserts an interrupt during probe, the irq line may fire with no handler, causing a spurious interrupt storm. Move sata_dwc_enable_interrupts() after ata_host_activate() so that interrupts are only unmasked once the handler is registered and the core is fully initialized. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/sata_dwc_460ex.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 7a4f59202156e..4029e082577fe 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1168,9 +1168,6 @@ static int sata_dwc_probe(struct platform_device *ofdev) /* Save dev for later use in dev_xxx() routines */ hsdev->dev = dev; - /* Enable SATA Interrupts */ - sata_dwc_enable_interrupts(hsdev); - /* Get SATA interrupt number */ irq = irq_of_parse_and_map(np, 0); if (!irq) { @@ -1203,6 +1200,8 @@ static int sata_dwc_probe(struct platform_device *ofdev) if (err) dev_err(dev, "failed to activate host"); + /* Enable SATA Interrupts */ + sata_dwc_enable_interrupts(hsdev); return 0; error_out: From 678d874e6ae1134e3cc78b4de8cb745e42ca417a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:25 -0700 Subject: [PATCH 2329/3297] ata: sata_dwc_460ex: use platform_get_irq() [ Upstream commit a4af122106f73ea510bb35a9ea1dedd980fc0db7 ] Replace irq_of_parse_and_map() with platform_get_irq() in both sata_dwc_dma_init_old() and sata_dwc_probe(). This is the preferred way to obtain IRQs for platform devices and provides better error reporting. Remove the now-unnecessary #include . irq_of_parse_and_map() requires irq_dispose_mapping(), which is missing. Also fix unused variable when CONFIG_SATA_DWC_OLD_DMA is disabled. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/sata_dwc_460ex.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 4029e082577fe..639eab75210f2 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -226,7 +225,6 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev, struct sata_dwc_device *hsdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; hsdev->dma = devm_kzalloc(dev, sizeof(*hsdev->dma), GFP_KERNEL); if (!hsdev->dma) @@ -236,11 +234,9 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev, hsdev->dma->id = pdev->id; /* Get SATA DMA interrupt number */ - hsdev->dma->irq = irq_of_parse_and_map(np, 1); - if (!hsdev->dma->irq) { - dev_err(dev, "no SATA DMA irq\n"); - return -ENODEV; - } + hsdev->dma->irq = platform_get_irq(pdev, 1); + if (hsdev->dma->irq < 0) + return hsdev->dma->irq; /* Get physical SATA DMA register base address */ hsdev->dma->regs = devm_platform_ioremap_resource(pdev, 1); @@ -1125,7 +1121,6 @@ static const struct ata_port_info sata_dwc_port_info[] = { static int sata_dwc_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; - struct device_node *np = dev->of_node; struct sata_dwc_device *hsdev; u32 idr, versionr; char *ver = (char *)&versionr; @@ -1169,14 +1164,12 @@ static int sata_dwc_probe(struct platform_device *ofdev) hsdev->dev = dev; /* Get SATA interrupt number */ - irq = irq_of_parse_and_map(np, 0); - if (!irq) { - dev_err(dev, "no SATA DMA irq\n"); - return -ENODEV; - } + irq = platform_get_irq(ofdev, 0); + if (irq < 0) + return irq; #ifdef CONFIG_SATA_DWC_OLD_DMA - if (!of_property_present(np, "dmas")) { + if (!of_property_present(dev->of_node, "dmas")) { err = sata_dwc_dma_init_old(ofdev, hsdev); if (err) return err; From d28920db56960115985fed43b5aec90f74cc56c2 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:27 -0700 Subject: [PATCH 2330/3297] ata: sata_dwc_460ex: fix clear_interrupt_bit() clearing all pending interrupts [ Upstream commit 66c4e310ad71f41e41736d33dd8a1fb5eaaec7f3 ] clear_interrupt_bit() ignores the bit argument and performs a read-write-back of the entire INTPR register. If INTPR uses standard Write-1-to-Clear semantics, this clears every pending interrupt bit, not just the intended one. Coalesced interrupts (e.g. DMAT + NEWFP) would be cleared together, silently losing the second event. Write only the specific bit to clear so that other pending interrupts are preserved. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/sata_dwc_460ex.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 639eab75210f2..d3472bfc606ff 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -394,8 +394,7 @@ static void clear_serror(struct ata_port *ap) static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) { - sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, - sata_dwc_readl(&hsdev->sata_dwc_regs->intpr)); + sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, bit); } static u32 qcmd_tag_to_mask(u8 tag) From 1842d45f461a78988254631893329bdf4596e954 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:28 -0700 Subject: [PATCH 2331/3297] ata: sata_dwc_460ex: fix infinite loop in NCQ tag completion bit-scanning [ Upstream commit c2130f6553f4a5cbdc259de069600117a995f197 ] The hand-rolled bit-scanning loop in the NCQ completion path has an infinite loop bug. When tag_mask has only high bits set (e.g. 0x80000000), the inner while loop left-shifts tag_mask until it overflows to 0. At that point !(0 & 1) is always true and 0 <<= 1 stays 0, causing an infinite loop in hardirq context with a spinlock held. Replace the open-coded bit-scanning with __ffs() which correctly finds the least significant set bit and is bounded by the width of the argument. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/sata_dwc_460ex.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index d3472bfc606ff..36cc28b898650 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -607,14 +607,9 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) status = ap->ops->sff_check_status(ap); dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status); - tag = 0; while (tag_mask) { - while (!(tag_mask & 0x00000001)) { - tag++; - tag_mask <<= 1; - } - - tag_mask &= (~0x00000001); + tag = __ffs(tag_mask); + tag_mask &= ~(1U << tag); qc = ata_qc_from_tag(ap, tag); if (unlikely(!qc)) { dev_err(ap->dev, "failed to get qc"); From b133f007ba02ce7f4d4972108e62c91d4e6232b9 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Fri, 10 Jul 2026 12:13:31 +0200 Subject: [PATCH 2332/3297] accel/ivpu: Fix wrong register read in LNL failure diagnostics [ Upstream commit e4159045c2704dfe146f0ccb0445d9d074cd6882 ] diagnose_failure_lnl() read VPU_HW_BTRS_MTL_INTERRUPT_STAT instead of VPU_HW_BTRS_LNL_INTERRUPT_STAT, which on LNL and newer parts is a different register with a different bit layout, so failure diagnostics decoded the wrong register and reported a bogus error cause. Read the LNL interrupt status register instead. Fixes: 8a27ad81f7d3 ("accel/ivpu: Split IP and buttress code") Reviewed-by: Andrzej Kacprowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260710101331.1899505-1-karol.wachowski@linux.intel.com Signed-off-by: Sasha Levin --- drivers/accel/ivpu/ivpu_hw_btrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c index aa33f562d29c1..e9786204feca4 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.c +++ b/drivers/accel/ivpu/ivpu_hw_btrs.c @@ -845,7 +845,7 @@ static void diagnose_failure_mtl(struct ivpu_device *vdev) static void diagnose_failure_lnl(struct ivpu_device *vdev) { - u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK; + u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK; if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) { ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n", From f4e23e661a259975d1c5ddbc40df9521ad33a540 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Jul 2026 11:56:06 +0200 Subject: [PATCH 2333/3297] ALSA: usb-audio: Skip DSD quirk for Musical Fidelity M6s DAC [ Upstream commit 93b47e66cc6d6c6382d44b44f5e7f6fc3a7b38c3 ] Salvador reported that the recent fix for applying the DSD quirk to Musical Fidelity devices broke for his M6s DAC model (2772:0502). Although this is basically a firmware bug, the model in question is fairly old, and no further firmware update can be expected, so it'd be better to address in the driver side. As an ad hoc workaround, skip the DSD quirk for this device by adding an empty quirk entry of 2772:0502; this essentially skips the later DSD quirk entry by the match with the vendor 2772. Fixes: da3a7efff64e ("ALSA: usb-audio: Update for native DSD support quirks") Reported-by: Salvador Blaya Closes: https://lore.kernel.org/CAOdyq+qFaqCh=tK_wNnA64hv5pQuA1Y09ANxQ=xK8yR-t4mf9Q@mail.gmail.com Tested-by: Salvador Blaya Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260709095614.1418838-1-tiwai@suse.de Signed-off-by: Sasha Levin --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 1b4cfc2b68f9d..3d81b003a5109 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2380,6 +2380,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_DSD_RAW), DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */ QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */ + 0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */ DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x2a70, 0x1881, /* OnePlus Technology (Shenzhen) Co., Ltd. BE02T */ From a087ed960fce54e9302796229e9d545bbc9bcd4a Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 4 Jul 2026 16:10:30 -0700 Subject: [PATCH 2334/3297] Bluetooth: qca: fix NVM tag length underflow in TLV parser [ Upstream commit c90164ca0f7036942ba088eb7ea8d3f6c2352020 ] In the TLV_TYPE_NVM branch of qca_tlv_check_data() the tag loop bound is "while (idx < length - sizeof(struct tlv_type_nvm))". "length" is a signed int from the firmware TLV header and sizeof(struct tlv_type_nvm) is a size_t (12), so "length" is converted to size_t and any firmware-supplied "length" < 12 makes the subtraction wrap to a huge value. The loop body then reads a 12-byte struct tlv_type_nvm past the end of the short vmalloc'd firmware buffer (and the EDL_TAG_ID_* handlers can write past it). Rewrite the bound as "idx + sizeof(struct tlv_type_nvm) <= length"; both operands are non-negative, so it no longer underflows and a "length" too small for one record correctly skips the loop. BUG: KASAN: vmalloc-out-of-bounds in qca_download_firmware.isra.0 (drivers/bluetooth/btqca.c:421) Read of size 2 at addr ffffc900000e5004 by task kworker/u9:0/52 Workqueue: hci0 hci_power_on Call Trace: ... kasan_report (mm/kasan/report.c:595) qca_download_firmware.isra.0 (drivers/bluetooth/btqca.c:421 drivers/bluetooth/btqca.c:617) qca_uart_setup (drivers/bluetooth/btqca.c:948) qca_setup (drivers/bluetooth/hci_qca.c:2029) hci_uart_setup (drivers/bluetooth/hci_ldisc.c:438) hci_dev_open_sync (net/bluetooth/hci_sync.c:5227) hci_power_on (net/bluetooth/hci_core.c:920) process_one_work (kernel/workqueue.c:3322) worker_thread (kernel/workqueue.c:3486) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Fixes: 2e4edfa1e2bd ("Bluetooth: qca: add missing firmware sanity checks") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Reported-by: Weiming Shi Reviewed-by: Johan Hovold Acked-by: Bartosz Golaszewski Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btqca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 86a48d009d1ba..afab479ac8944 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -413,7 +413,7 @@ static int qca_tlv_check_data(struct hci_dev *hdev, idx = 0; data = tlv->data; - while (idx < length - sizeof(struct tlv_type_nvm)) { + while (idx + sizeof(struct tlv_type_nvm) <= length) { tlv_nvm = (struct tlv_type_nvm *)(data + idx); tag_id = le16_to_cpu(tlv_nvm->tag_id); From 57059ff14d81df4a970b2ea8d8f54431bb91a025 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Tue, 7 Jul 2026 12:15:18 +0800 Subject: [PATCH 2335/3297] Bluetooth: MGMT: revalidate LOAD_CONN_PARAM queued update [ Upstream commit 2bf282f8f715f5d05d6f4c49ffb3bd241c5e667e ] MGMT_OP_LOAD_CONN_PARAM queues conn_update_sync() when a single parameter update changes an existing LE central connection. The queued work currently stores a borrowed hci_conn_params entry from hdev->le_conn_params. A later LOAD_CONN_PARAM request can clear disabled parameters and free that entry before hci_cmd_sync_work() runs the queued callback. Do not keep the borrowed hci_conn_params pointer in queued work. Queue the hci_conn instead and hold a reference until the queued callback completes. When the work runs, revalidate that the connection is still present, look up the current hci_conn_params entry, and cancel the update if userspace removed that entry while the work was pending. Copy the interval values from the current params entry under hdev->lock, then drop the lock and keep using hci_le_conn_update_sync() to issue the update. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in conn_update_sync+0x2a/0xf0 [bluetooth] Read of size 1 at addr ffff88810c697126 by task kworker/u17:0/377 Workqueue: hci0 hci_cmd_sync_work [bluetooth] Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 kasan_report+0xe0/0x110 conn_update_sync+0x2a/0xf0 [bluetooth] hci_cmd_sync_work+0x187/0x210 [bluetooth] process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Allocated by task 466: hci_conn_params_add+0xa6/0x240 [bluetooth] load_conn_param+0x4e1/0x850 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] Freed by task 474: kfree+0x313/0x590 hci_conn_params_clear_disabled+0x9b/0xc0 [bluetooth] load_conn_param+0x4bf/0x850 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] Fixes: 0ece498c27d8c ("Bluetooth: MGMT: Make MGMT_OP_LOAD_CONN_PARAM update existing connection") Suggested-by: Luiz Augusto von Dentz Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/mgmt.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 410910f64c1c4..4c8b172e85d00 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7924,14 +7924,36 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, static int conn_update_sync(struct hci_dev *hdev, void *data) { - struct hci_conn_params *params = data; - struct hci_conn *conn; + struct hci_conn *conn = data; + struct hci_conn_params *params; + struct hci_conn_params local = {}; - conn = hci_conn_hash_lookup_le(hdev, ¶ms->addr, params->addr_type); - if (!conn) - return -ECANCELED; + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn) || conn->role != HCI_ROLE_MASTER) + goto cancel; + + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (!params) + goto cancel; + + local.conn_min_interval = params->conn_min_interval; + local.conn_max_interval = params->conn_max_interval; + local.conn_latency = params->conn_latency; + local.supervision_timeout = params->supervision_timeout; - return hci_le_conn_update_sync(hdev, conn, params); + hci_dev_unlock(hdev); + + return hci_le_conn_update_sync(hdev, conn, &local); + +cancel: + hci_dev_unlock(hdev); + return -ECANCELED; +} + +static void conn_update_sync_destroy(struct hci_dev *hdev, void *data, int err) +{ + hci_conn_put(data); } static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, @@ -8041,9 +8063,13 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, (conn->le_conn_min_interval != min || conn->le_conn_max_interval != max || conn->le_conn_latency != latency || - conn->le_supv_timeout != timeout)) - hci_cmd_sync_queue(hdev, conn_update_sync, - hci_param, NULL); + conn->le_supv_timeout != timeout)) { + hci_conn_get(conn); + if (hci_cmd_sync_queue(hdev, conn_update_sync, + conn, + conn_update_sync_destroy) < 0) + hci_conn_put(conn); + } } } From 83b7e67698d0b93f685875ce82c8d335436834f7 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:40 +0300 Subject: [PATCH 2336/3297] Bluetooth: hci_sync: extend conn_hash lookup critical sections [ Upstream commit d5efd6e4b8b0634af6843178fe1a7dd2b2178a3d ] Using RCU-protected pointers outside the critical sections without refcount is incorrect and may result to UAF. Extend critical section to cover both hci_conn_hash lookup and use of the returned conn. Add surrounding rcu_read_lock() also when return value is not used, in preparation for RCU lockdep requirement to hci_lookup_le_connect(). This avoids concurrent deletion of the conn before we are done dereferencing it. Also, make sure to hold hdev->lock when accessing hdev->accept_list. Fixes: 6d0417e4e1cf ("Bluetooth: hci_conn: Fix not setting conn_timeout for Broadcast Receiver") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_sync.c | 42 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index a8404027724de..651caa4a05d36 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -1054,14 +1054,19 @@ static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa) * In this kind of scenario skip the update and let the random * address be updated at the next cycle. */ + rcu_read_lock(); + if (bacmp(&hdev->random_addr, BDADDR_ANY) && (hci_dev_test_flag(hdev, HCI_LE_ADV) || hci_lookup_le_connect(hdev))) { bt_dev_dbg(hdev, "Deferring random address update"); hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); + rcu_read_unlock(); return 0; } + rcu_read_unlock(); + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa, HCI_CMD_TIMEOUT); } @@ -2647,12 +2652,17 @@ static int hci_pause_addr_resolution(struct hci_dev *hdev) /* Cannot disable addr resolution if scanning is enabled or * when initiating an LE connection. */ + rcu_read_lock(); + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) || hci_lookup_le_connect(hdev)) { + rcu_read_unlock(); bt_dev_err(hdev, "Command not allowed when scan/LE connect"); return -EPERM; } + rcu_read_unlock(); + /* Cannot disable addr resolution if advertising is enabled. */ err = hci_pause_advertising_sync(hdev); if (err) { @@ -2790,6 +2800,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) { struct hci_conn *conn; + rcu_read_lock(); + conn = hci_conn_hash_lookup_create_pa_sync(hdev); if (conn) { struct conn_params pa; @@ -2799,6 +2811,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) bacpy(&pa.addr, &conn->dst); pa.addr_type = conn->dst_type; + rcu_read_unlock(); + /* Clear first since there could be addresses left * behind. */ @@ -2808,6 +2822,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) err = hci_le_add_accept_list_sync(hdev, &pa, &num_entries); goto done; + } else { + rcu_read_unlock(); } } @@ -2818,10 +2834,13 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) * the controller. */ list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) { - if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) + rcu_read_lock(); + + if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) { + rcu_read_unlock(); continue; + } - /* Pointers not dereferenced, no locks needed */ pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns, &b->bdaddr, b->bdaddr_type); @@ -2829,6 +2848,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev) &b->bdaddr, b->bdaddr_type); + rcu_read_unlock(); + /* If the device is not likely to connect or report, * remove it from the acceptlist. */ @@ -2955,6 +2976,8 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, if (sent) { struct hci_conn *conn; + rcu_read_lock(); + conn = hci_conn_hash_lookup_ba(hdev, PA_LINK, &sent->bdaddr); if (conn) { @@ -2979,8 +3002,12 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, phy++; } + rcu_read_unlock(); + if (num_phy) goto done; + } else { + rcu_read_unlock(); } } } @@ -3231,12 +3258,16 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev) /* If there is at least one pending LE connection, we should * keep the background scan running. */ + bool exists; /* If controller is connecting, we should not start scanning * since some controllers are not able to scan and connect at * the same time. */ - if (hci_lookup_le_connect(hdev)) + rcu_read_lock(); + exists = hci_lookup_le_connect(hdev); + rcu_read_unlock(); + if (exists) return 0; bt_dev_dbg(hdev, "start background scanning"); @@ -3451,6 +3482,7 @@ int hci_write_fast_connectable_sync(struct hci_dev *hdev, bool enable) } static bool disconnected_accept_list_entries(struct hci_dev *hdev) + __must_hold(&hdev->lock) { struct bdaddr_list *b; @@ -3491,12 +3523,16 @@ int hci_update_scan_sync(struct hci_dev *hdev) if (hdev->scanning_paused) return 0; + hci_dev_lock(hdev); + if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) || disconnected_accept_list_entries(hdev)) scan = SCAN_PAGE; else scan = SCAN_DISABLED; + hci_dev_unlock(hdev); + if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) scan |= SCAN_INQUIRY; From ca58ad287bfc5b9d31a72ecb8650289df2b57250 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:41 +0300 Subject: [PATCH 2337/3297] Bluetooth: mgmt: fix locking in unpair_device/disconnect_sync [ Upstream commit 16cd66443957e4ad42155c6fec401012f600c6f8 ] Dereferencing RCU-protected pointers outside critical sections is invalid and may lead to UAF. Take hdev->lock for hci_conn lookup and hci_abort_conn(). Don't use RCU to ensure the conn is fully initialized at this point. Fixes: 227a0cdf4a028 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/mgmt.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4c8b172e85d00..0ef1192e5e452 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3082,6 +3082,8 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data) struct mgmt_cp_unpair_device *cp = cmd->param; struct hci_conn *conn; + hci_dev_lock(hdev); + if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); @@ -3089,6 +3091,11 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data) conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); + if (conn) + hci_conn_get(conn); + + hci_dev_unlock(hdev); + if (!conn) return 0; @@ -3096,6 +3103,7 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data) * will clean up the connection no matter the error. */ hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); + hci_conn_put(conn); return 0; } @@ -3243,6 +3251,8 @@ static int disconnect_sync(struct hci_dev *hdev, void *data) struct mgmt_cp_disconnect *cp = cmd->param; struct hci_conn *conn; + hci_dev_lock(hdev); + if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); @@ -3250,6 +3260,11 @@ static int disconnect_sync(struct hci_dev *hdev, void *data) conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); + if (conn) + hci_conn_get(conn); + + hci_dev_unlock(hdev); + if (!conn) return -ENOTCONN; @@ -3257,6 +3272,7 @@ static int disconnect_sync(struct hci_dev *hdev, void *data) * will clean up the connection no matter the error. */ hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); + hci_conn_put(conn); return 0; } From d5b3b484b62bb0f4542e7622789d28871626cdf0 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:42 +0300 Subject: [PATCH 2338/3297] Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds [ Upstream commit da55f570191d5d72f10c607a7043b947eb05ea46 ] Dereferencing RCU-protected pointers outside critical sections is invalid and may lead to UAF. Use of hci_conn in hci_sync callbacks also needs to hold refcount to avoid UAF. Take appropriate locks for hci_conn lookups, and take refcount for hci_conn pointers stored in mgmt_pending_cmd so that the pointer stays valid. When accessing conn->state, ensure hdev->lock is held to avoid data race. Fixes: 7b445e220db9 ("Bluetooth: MGMT: Fix holding hci_conn reference while command is queued") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/mgmt.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0ef1192e5e452..79eb605be2804 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7325,6 +7325,9 @@ static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err) rp.max_tx_power = HCI_TX_POWER_INVALID; } + if (conn) + hci_conn_put(conn); + mgmt_cmd_complete(cmd->sk, cmd->hdev->id, MGMT_OP_GET_CONN_INFO, status, &rp, sizeof(rp)); @@ -7339,6 +7342,8 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data) int err; __le16 handle; + hci_dev_lock(hdev); + /* Make sure we are still connected */ if (cp->addr.type == BDADDR_BREDR) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, @@ -7346,12 +7351,16 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data) else conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); - if (!conn || conn->state != BT_CONNECTED) + if (!conn || conn->state != BT_CONNECTED) { + hci_dev_unlock(hdev); return MGMT_STATUS_NOT_CONNECTED; + } - cmd->user_data = conn; + cmd->user_data = hci_conn_get(conn); handle = cpu_to_le16(conn->handle); + hci_dev_unlock(hdev); + /* Refresh RSSI each time */ err = hci_read_rssi_sync(hdev, handle); @@ -7485,6 +7494,9 @@ static void get_clock_info_complete(struct hci_dev *hdev, void *data, int err) } complete: + if (conn) + hci_conn_put(conn); + mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, status, &rp, sizeof(rp)); @@ -7501,15 +7513,21 @@ static int get_clock_info_sync(struct hci_dev *hdev, void *data) memset(&hci_cp, 0, sizeof(hci_cp)); hci_read_clock_sync(hdev, &hci_cp); + hci_dev_lock(hdev); + /* Make sure connection still exists */ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); - if (!conn || conn->state != BT_CONNECTED) + if (!conn || conn->state != BT_CONNECTED) { + hci_dev_unlock(hdev); return MGMT_STATUS_NOT_CONNECTED; + } - cmd->user_data = conn; + cmd->user_data = hci_conn_get(conn); hci_cp.handle = cpu_to_le16(conn->handle); hci_cp.which = 0x01; /* Piconet clock */ + hci_dev_unlock(hdev); + return hci_read_clock_sync(hdev, &hci_cp); } From cefb44c367b2b52e50f97bc8526d39df9bcf5e60 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 9 Jul 2026 14:22:50 +0800 Subject: [PATCH 2339/3297] Bluetooth: hci_qca: Clear memdump state on invalid dump size [ Upstream commit bf587a10c33e5571a299742e45bc18960b9912e7 ] qca_controller_memdump() allocates qca->qca_memdump before processing the first dump packet. For a sequence-zero packet it then disables IBS, marks memdump collection active, and reads the advertised dump size. If the controller reports a zero dump size, the error path frees the local qca_memdump object and returns without clearing qca->qca_memdump or undoing the collection state. A later memdump work item initializes its local pointer from qca->qca_memdump and skips allocation when that pointer is non-NULL, so it can operate on freed memory. The stale collection and IBS-disabled flags can also leave waiters or later transmit handling blocked behind an aborted dump. Clear the saved pointer and memdump state before returning from the invalid-size path, matching the cleanup used when hci_devcd_init() fails. A static analysis checker reported the stale memdump state, and manual source review confirmed the invalid-size failure path. Fixes: 06d3fdfcdf5c ("Bluetooth: hci_qca: Add qcom devcoredump support") Signed-off-by: Ruoyu Wang Reviewed-by: Paul Menzel Reviewed-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/hci_qca.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index c649a3f702c04..b510b03dae870 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1083,6 +1083,10 @@ static void qca_controller_memdump(struct work_struct *work) if (!(qca_memdump->ram_dump_size)) { bt_dev_err(hu->hdev, "Rx invalid memdump size"); kfree(qca_memdump); + qca->qca_memdump = NULL; + qca->memdump_state = QCA_MEMDUMP_COLLECTED; + clear_and_wake_up_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); + clear_bit(QCA_IBS_DISABLED, &qca->flags); kfree_skb(skb); mutex_unlock(&qca->hci_memdump_lock); return; From 7e08ab7a061b17ac1989a225c6afb53f44a86808 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:56 +0800 Subject: [PATCH 2340/3297] smb/client: handle overlapping allocated ranges in fallocate [ Upstream commit b09ae45d85dc816987a71db9eebc54b0ae288e94 ] smb3_simple_fallocate_range() can skip holes when an allocated range returned by the server starts before the current fallocate offset. The skipped hole is not zero-filled, but fallocate still returns success. A later write to that hole may therefore fail with ENOSPC. The function queries allocated ranges so that it can preserve existing contents and write zeroes only into holes. However, the server may return a range that starts before the current fallocate offset. For example, assume the fallocate request is [100, 400) and the only allocated range returned by the server is [0, 200): Request: [100, 400) Server range: [ 0, 200) allocated Correct: [100, 200) allocated data, skip [200, 400) hole, zero-fill Current: [100, 300) skipped [300, 400) zero-filled afterwards The current code adds the full server range length, 200, to the current offset 100 and moves to 300. As a result, the hole in [200, 300) is skipped without being zero-filled. Fix this by advancing only over the part of the allocated range that overlaps the current fallocate offset. Ignore ranges that end before the current offset and reject ranges whose end offset overflows. This also prevents a malformed range length from causing an out-of-bounds zero-buffer read. Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation") Signed-off-by: Huiwen He Reviewed-by: ChenXiaoSong Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/smb2ops.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 786a7e963f1ff..5bbe98dc0529b 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3546,6 +3546,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; u32 out_data_len; char *buf = NULL; + u64 range_start, range_len, range_end; loff_t l; int rc; @@ -3582,13 +3583,21 @@ static int smb3_simple_fallocate_range(unsigned int xid, goto out; } - if (off < le64_to_cpu(tmp_data->file_offset)) { + range_start = le64_to_cpu(tmp_data->file_offset); + range_len = le64_to_cpu(tmp_data->length); + if (check_add_overflow(range_start, range_len, &range_end) || + range_end > S64_MAX) { + rc = -EINVAL; + goto out; + } + + if (off < range_start) { /* * We are at a hole. Write until the end of the region * or until the next allocated data, * whichever comes next. */ - l = le64_to_cpu(tmp_data->file_offset) - off; + l = range_start - off; if (len < l) l = len; rc = smb3_simple_fallocate_write_range(xid, tcon, @@ -3605,11 +3614,13 @@ static int smb3_simple_fallocate_range(unsigned int xid, * until the end of the data or the end of the region * we are supposed to fallocate, whichever comes first. */ - l = le64_to_cpu(tmp_data->length); - if (len < l) - l = len; - off += l; - len -= l; + if (off < range_end) { + l = range_end - off; + if (len < l) + l = len; + off += l; + len -= l; + } tmp_data = &tmp_data[1]; out_data_len -= sizeof(struct file_allocated_range_buffer); From 479425744b21927a5e5e82a2fac8cd604f2aba5d Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:02:25 +0800 Subject: [PATCH 2341/3297] drm/i915/gt: use correct selftest config symbol [ Upstream commit a82f1bb8191aec98a971a2196136016ef70c0880 ] intel_engine_user.c checks CONFIG_DRM_I915_SELFTESTS before running the engine UABI isolation check. Kconfig defines DRM_I915_SELFTEST, without the trailing "S", and the rest of i915 uses CONFIG_DRM_I915_SELFTEST. Because CONFIG_DRM_I915_SELFTESTS is not backed by any Kconfig symbol, the IS_ENABLED() test is always false. Use the existing selftest symbol so the debug/selftest guarded path can be reached when selftests are enabled. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the Kconfig definition and the inconsistent guard in intel_engine_user.c. Fixes: 750e76b4f9f6 ("drm/i915/gt: Move the [class][inst] lookup for engines onto the GT") Signed-off-by: Pengpeng Hou Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260705080225.436-1-pengpeng@iscas.ac.cn (cherry picked from commit 14a2012a490258f3f93857bc4f1b203405964be7) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/gt/intel_engine_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gt/intel_engine_user.c b/drivers/gpu/drm/i915/gt/intel_engine_user.c index 833987015b8bb..ed42b9d05a07d 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_user.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_user.c @@ -257,7 +257,7 @@ void intel_engines_driver_register(struct drm_i915_private *i915) p = &prev->rb_right; } - if (IS_ENABLED(CONFIG_DRM_I915_SELFTESTS) && + if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST) && IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) { struct intel_engine_cs *engine; unsigned int isolation; From 00ba4bf8798242253fefc1fa6a78db1d445fd024 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 7 May 2026 10:22:23 +0200 Subject: [PATCH 2342/3297] can: raw: add locking for raw flags bitfield [ Upstream commit 1e5185c090589f4146d728ab36417d8a5419f127 ] With commit 890e5198a6e5 ("can: raw: use bitfields to store flags in struct raw_sock") the formerly separate integer values have been integrated into a single bitfield. This led to a read-modify-write operation when changing a flag in raw_setsockopt() which now needs a locking to prevent concurrent access. Instead of adding a lock/unlock hell in each of the flag manipulations this patch introduces a wrapper for a new raw_setsockopt_locked() function analogue to the isotp_setsockopt[_locked]() approach in net/can/isotp.c Fixes: 890e5198a6e5 ("can: raw: use bitfields to store flags in struct raw_sock") Reported-by: Eulgyu Kim Closes: https://lore.kernel.org/linux-can/20260503112200.22727-1-eulgyukim@snu.ac.kr/ Tested-by: Eulgyu Kim Signed-off-by: Oliver Hartkopp Reviewed-by: Vincent Mailhol Tested-by: Vincent Mailhol Link: https://patch.msgid.link/20260504111928.41856-1-socketcan@hartkopp.net [mkl: use Closes tag instead of Link] Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- net/can/raw.c | 66 +++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/net/can/raw.c b/net/can/raw.c index 263e7167d2f52..365559d19dc32 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -560,8 +560,8 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr, return RAW_MIN_NAMELEN; } -static int raw_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) +static int raw_setsockopt_locked(struct socket *sock, int optname, + sockptr_t optval, unsigned int optlen) { struct sock *sk = sock->sk; struct raw_sock *ro = raw_sk(sk); @@ -573,9 +573,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, int flag; int err = 0; - if (level != SOL_CAN_RAW) - return -EINVAL; - switch (optname) { case CAN_RAW_FILTER: if (optlen % sizeof(struct can_filter) != 0) @@ -596,17 +593,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, return -EFAULT; } - rtnl_lock(); - lock_sock(sk); - dev = ro->dev; - if (ro->bound && dev) { - if (dev->reg_state != NETREG_REGISTERED) { - if (count > 1) - kfree(filter); - err = -ENODEV; - goto out_fil; - } + if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) { + if (count > 1) + kfree(filter); + return -ENODEV; } if (ro->bound) { @@ -620,7 +611,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, if (err) { if (count > 1) kfree(filter); - goto out_fil; + return err; } /* remove old filter registrations */ @@ -640,11 +631,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, } ro->filter = filter; ro->count = count; - - out_fil: - release_sock(sk); - rtnl_unlock(); - break; case CAN_RAW_ERR_FILTER: @@ -656,16 +642,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, err_mask &= CAN_ERR_MASK; - rtnl_lock(); - lock_sock(sk); - dev = ro->dev; - if (ro->bound && dev) { - if (dev->reg_state != NETREG_REGISTERED) { - err = -ENODEV; - goto out_err; - } - } + if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) + return -ENODEV; /* remove current error mask */ if (ro->bound) { @@ -674,7 +653,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, err_mask); if (err) - goto out_err; + return err; /* remove old err_mask registration */ raw_disable_errfilter(sock_net(sk), dev, sk, @@ -683,11 +662,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, /* link new err_mask to the socket */ ro->err_mask = err_mask; - - out_err: - release_sock(sk); - rtnl_unlock(); - break; case CAN_RAW_LOOPBACK: @@ -767,6 +741,26 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, return err; } +static int raw_setsockopt(struct socket *sock, int level, int optname, + sockptr_t optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + int err; + + if (level != SOL_CAN_RAW) + return -EINVAL; + + rtnl_lock(); + lock_sock(sk); + + err = raw_setsockopt_locked(sock, optname, optval, optlen); + + release_sock(sk); + rtnl_unlock(); + + return err; +} + static int raw_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { From c1bbd0a6906b8d06f1f28a5e1b6d243c81c1f8fa Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 21:33:09 -0700 Subject: [PATCH 2343/3297] powerpc/85xx: Add fsl,ifc to common device ids [ Upstream commit 25957f7c3dac3265332d766b71233e3622f17e14 ] Add fsl,ifc to mpc85xx_common_ids so that of_platform_bus_probe creates a platform device for the IFC node even without 'simple-bus' in its compatible property. On P1010 and similar platforms the IFC node is a direct child of the root, so it must be explicitly matched to be populated. Fixes: 0bf51cc9e9e5 ("powerpc: dts: mpc85xx: remove "simple-bus" compatible from ifc node") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260604043309.91280-1-rosenp@gmail.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/85xx/common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c index 757811155587d..c11deb2f50ed4 100644 --- a/arch/powerpc/platforms/85xx/common.c +++ b/arch/powerpc/platforms/85xx/common.c @@ -42,6 +42,8 @@ static const struct of_device_id mpc85xx_common_ids[] __initconst = { { .compatible = "fsl,qoriq-pcie-v2.3", }, { .compatible = "fsl,qoriq-pcie-v2.2", }, { .compatible = "fsl,fman", }, + /* IFC NAND and NOR controllers */ + { .compatible = "fsl,ifc", }, {}, }; From 5c3a1cede86fdcc6e7c45176f26cd1eb990e6703 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 8 May 2026 15:16:37 +0200 Subject: [PATCH 2344/3297] powerpc/time: Prepare to stop elapsing in dynticks-idle [ Upstream commit c8ba971cf8567d49eb5f43ee90c4e50424331c18 ] Currently the tick subsystem stores the idle cputime accounting in private fields, allowing cohabitation with architecture idle vtime accounting. The former is fetched on online CPUs, the latter on offline CPUs. For consolidation purpose, architecture vtime accounting will continue to account the cputime but will make a break when the idle tick is stopped. The dyntick cputime accounting will then be relayed by the tick subsystem so that the idle cputime is still seen advancing coherently even when the tick isn't there to flush the idle vtime. Prepare for that and introduce three new APIs which will be used in subsequent patches: - vtime_dynticks_start() is deemed to be called when idle enters in dyntick mode. The idle cputime that elapsed so far is accumulated. - vtime_dynticks_stop() is deemed to be called when idle exits from dyntick mode. The vtime entry clocks are fast-forward to current time so that idle accounting restarts elapsing from now. - vtime_reset() is deemed to be called from dynticks idle IRQ entry to fast-forward the clock to current time so that the IRQ time is still accounted by vtime while nohz cputime is paused. Also accumulated vtime won't be flushed from dyntick-idle ticks to avoid accounting twice the idle cputime, along with nohz accounting. Signed-off-by: Frederic Weisbecker Signed-off-by: Thomas Gleixner Tested-by: Shrikanth Hegde Reviewed-by: Shrikanth Hegde Link: https://patch.msgid.link/20260508131647.43868-6-frederic@kernel.org Stable-dep-of: c1c1ffa490fc ("powerpc/vtime: Initialize starttime at boot for native accounting") Signed-off-by: Sasha Levin --- arch/powerpc/kernel/time.c | 41 ++++++++++++++++++++++++++++++++++++++ include/linux/vtime.h | 6 ++++++ 2 files changed, 47 insertions(+) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index b4472288e0d43..3460d1a5a97c2 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -376,6 +376,47 @@ void vtime_task_switch(struct task_struct *prev) acct->starttime = acct0->starttime; } } + +#ifdef CONFIG_NO_HZ_COMMON +/** + * vtime_reset - Fast forward vtime entry clocks + * + * Called from dynticks idle IRQ entry to fast-forward the clocks to current time + * so that the IRQ time is still accounted by vtime while nohz cputime is paused. + */ +void vtime_reset(void) +{ + struct cpu_accounting_data *acct = get_accounting(current); + + acct->starttime = mftb(); +#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME + acct->startspurr = read_spurr(acct->starttime); +#endif +} + +/** + * vtime_dyntick_start - Inform vtime about entry to idle-dynticks + * + * Called when idle enters in dyntick mode. The idle cputime that elapsed so far + * is accumulated and the tick subsystem takes over the idle cputime accounting. + */ +void vtime_dyntick_start(void) +{ + vtime_account_idle(current); +} + +/** + * vtime_dyntick_stop - Inform vtime about exit from idle-dynticks + * + * Called when idle exits from dyntick mode. The vtime entry clocks are + * fast-forward to current time so that idle accounting restarts elapsing from + * now. + */ +void vtime_dyntick_stop(void) +{ + vtime_reset(); +} +#endif /* CONFIG_NO_HZ_COMMON */ #endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ void __no_kcsan __delay(unsigned long loops) diff --git a/include/linux/vtime.h b/include/linux/vtime.h index 29dd5b91dd7d6..3fc04b849e4e3 100644 --- a/include/linux/vtime.h +++ b/include/linux/vtime.h @@ -32,11 +32,17 @@ extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset); extern void vtime_account_softirq(struct task_struct *tsk); extern void vtime_account_hardirq(struct task_struct *tsk); extern void vtime_flush(struct task_struct *tsk); +extern void vtime_reset(void); +extern void vtime_dyntick_start(void); +extern void vtime_dyntick_stop(void); #else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ static inline void vtime_account_irq(struct task_struct *tsk, unsigned int offset) { } static inline void vtime_account_softirq(struct task_struct *tsk) { } static inline void vtime_account_hardirq(struct task_struct *tsk) { } static inline void vtime_flush(struct task_struct *tsk) { } +static inline void vtime_reset(void) { } +static inline void vtime_dyntick_start(void) { } +static inline void vtime_dyntick_stop(void) { } #endif /* From c3e61df6fabca9eb092c76994a6006c45741c231 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Fri, 5 Jun 2026 18:13:29 +0530 Subject: [PATCH 2345/3297] powerpc/vtime: Initialize starttime at boot for native accounting [ Upstream commit c1c1ffa490fc33591e90852ed0d38804dd20bc36 ] It was observed that /proc/stat had very large value for one ore more CPUs. It was more visible after recent code simplifications around cpustats. System has 240 CPUs. cat /proc/uptime; 194.18 46500.55 cat /proc/stat cpu 5966 39 837032887 4650070 164 185 100 0 0 0 cpu0 108 0 837030890 19109 24 4 23 0 0 0 Since uptime is 194s, system time of each CPU can't be more than 19400. Sum of system time of all CPUs can't be more than 19400*240 4656000. In fact huge value is close to mftb(). Note mftb doesn't reset on powerVM when the LPAR restart. It only resets when whole system resets. The same issue exists for kexec too. This happens since starttime is not setup at init time. Once it is set then subsequent vtime_delta will return the right delta. Fix it by initializing the starttime during CPU initialization. This fixes the large times seen. cat /proc/uptime; cat /proc/stat 15.78 3694.63 cpu 6035 35 1347 369479 23 144 49 0 0 0 cpu0 19 0 38 1508 0 1 14 0 0 0 Now, system time is reported as expected. Fixes: cf9efce0ce31 ("powerpc: Account time using timebase rather than PURR") Reviewed-by: Christophe Leroy (CS GROUP) Suggested-by: Christophe Leroy (CS GROUP) Signed-off-by: Shrikanth Hegde Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260605124329.377533-1-sshegde@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/kernel/time.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 3460d1a5a97c2..11145c40183dd 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -377,7 +377,6 @@ void vtime_task_switch(struct task_struct *prev) } } -#ifdef CONFIG_NO_HZ_COMMON /** * vtime_reset - Fast forward vtime entry clocks * @@ -394,6 +393,7 @@ void vtime_reset(void) #endif } +#ifdef CONFIG_NO_HZ_COMMON /** * vtime_dyntick_start - Inform vtime about entry to idle-dynticks * @@ -933,6 +933,7 @@ static void __init set_decrementer_max(void) static void __init init_decrementer_clockevent(void) { register_decrementer_clockevent(smp_processor_id()); + vtime_reset(); } void secondary_cpu_time_init(void) @@ -948,6 +949,7 @@ void secondary_cpu_time_init(void) /* FIME: Should make unrelated change to move snapshot_timebase * call here ! */ register_decrementer_clockevent(smp_processor_id()); + vtime_reset(); } /* From 250474c69bc3fc48a5fc21d7c349f279caad947a Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:57 +0200 Subject: [PATCH 2346/3297] bpf, sockmap: Reject unhashed UDP sockets on sockmap update [ Upstream commit 66efd3368ae10d05e08fbe6425b50fdec7186ac7 ] UDP sockets get SOCK_RCU_FREE set when (auto-)bound. This means sk_is_refcounted(unbound) = true, while sk_is_refcounted(bound) = false. Because sockmap accepts unbound UDP sockets, a BPF program can increment a socket's refcount via lookup. If the socket is subsequently bound, the transition from unbound to bound causes bpf_sk_release() to skip the decrement of the refcount, causing a memory leak. unreferenced object 0xffff88810bc2eb40 (size 1984): comm "test_progs", pid 2451, jiffies 4295320596 hex dump (first 32 bytes): 7f 00 00 01 7f 00 00 01 d2 04 1b b7 04 d2 00 00 ................ 02 00 01 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............ backtrace (crc bdee079d): kmem_cache_alloc_noprof+0x557/0x660 sk_prot_alloc+0x69/0x240 sk_alloc+0x30/0x460 inet_create+0x2ce/0xf80 __sock_create+0x25b/0x5c0 __sys_socket+0x119/0x1d0 __x64_sys_socket+0x72/0xd0 do_syscall_64+0xa1/0x5f0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Instead of special-casing for refcounted sockets, reject unhashed UDP sockets during sockmap updates, as there is no benefit to supporting those. This effectively reverts the commit under Fixes, with two exceptions: 1. sock_map_sk_state_allowed() maintains a fall-through `return true`. 2. In the spirit of commit b8b8315e39ff ("bpf, sockmap: Remove unhash handler for BPF sockmap usage"), the proto::unhash BPF handler is not reintroduced. Historical note: this issue is related to commit 67312adc96b5 ("bpf: reject unhashed sockets in bpf_sk_assign"). Fixes: 0c48eefae712 ("sock_map: Lift socket state restriction for datagram sockets") Suggested-by: Kuniyuki Iwashima Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-2-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- net/core/sock_map.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 5947b38e4f8b6..70bbc78fb079f 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -542,6 +542,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk) { if (sk_is_tcp(sk)) return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN); + if (sk_is_udp(sk)) + return sk_hashed(sk); if (sk_is_stream_unix(sk)) return (1 << sk->sk_state) & TCPF_ESTABLISHED; if (sk_is_vsock(sk) && From 5b06cf93341fe17fc16a01036ac7e25496e8048c Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 13 Jul 2026 16:29:12 +0800 Subject: [PATCH 2347/3297] drm/panthor: Check debugfs GEM lock initialization [ Upstream commit 022e901333c3054656a640794e842bab7af5a75c ] drmm_mutex_init() can fail while registering the managed cleanup action. When that happens, drmm_add_action_or_reset() destroys the mutex before returning the error. Continuing initialization would therefore leave the debugfs GEM object list with an unusable lock. Propagate the error as is already done for the other managed mutexes in panthor_device_init(). Fixes: a3707f53eb3f ("drm/panthor: show device-wide list of DRM GEM objects over DebugFS") Signed-off-by: Linmao Li Reviewed-by: Liviu Dudau Link: https://patch.msgid.link/20260713082912.321021-1-lilinmao@kylinos.cn Signed-off-by: Liviu Dudau Signed-off-by: Sasha Levin --- drivers/gpu/drm/panthor/panthor_device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index 962a10e00848e..cdf82d83de7ab 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -182,7 +182,10 @@ int panthor_device_init(struct panthor_device *ptdev) return ret; #ifdef CONFIG_DEBUG_FS - drmm_mutex_init(&ptdev->base, &ptdev->gems.lock); + ret = drmm_mutex_init(&ptdev->base, &ptdev->gems.lock); + if (ret) + return ret; + INIT_LIST_HEAD(&ptdev->gems.node); #endif From 1d9a2f01b3c4e5c88e06b2db4b5460c2ec884722 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Wed, 8 Jul 2026 12:02:14 +0200 Subject: [PATCH 2348/3297] s390/checksum: Fix csum_partial() without vector facility [ Upstream commit 4bb06b60d982355e22647b3d12d6619419f8c1fa ] Currently csum_partial() calls csum_copy() with copy=false and dst=NULL. On machines without the vector facility, csum_copy() falls back to cksm(dst, ...), causing the checksum to be calculated from address zero instead of the source buffer. The VX implementation already checksums data loaded from src. Make the fallback do the same by passing src to cksm(). Fixes: dcd3e1de9d17 ("s390/checksum: provide csum_partial_copy_nocheck()") Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik Signed-off-by: Sasha Levin --- arch/s390/lib/csum-partial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/lib/csum-partial.c b/arch/s390/lib/csum-partial.c index 458abd9bac702..9d74ceff136c5 100644 --- a/arch/s390/lib/csum-partial.c +++ b/arch/s390/lib/csum-partial.c @@ -23,7 +23,7 @@ static __always_inline __wsum csum_copy(void *dst, const void *src, int len, __w if (!cpu_has_vx()) { if (copy) memcpy(dst, src, len); - return cksm(dst, len, sum); + return cksm(src, len, sum); } kernel_fpu_begin(&vxstate, KERNEL_VXR_V16V23); fpu_vlvgf(16, (__force u32)sum, 1); From 3f398d45f3c75a4bf424339843bb2db18da9d8f4 Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Mon, 13 Jul 2026 17:30:56 -0700 Subject: [PATCH 2349/3297] riscv: hwprobe: Avoid uninitialized read in hwprobe_get_cpus() [ Upstream commit 5caae1deee89a6582c761d5dcd4b924b744426cc ] When cpusetsize < cpumask_size(), hwprobe_get_cpus() did not fully initialize its copy of the cpu mask, which could cause non-deterministic results from the riscv_hwprobe syscall on a system with more than 8 CPUs when the supplied cpu mask is empty. Address this by fully initializing the cpu mask. Fixes: e178bf146e4b ("RISC-V: hwprobe: Introduce which-cpus flag") Signed-off-by: Mark Harris Reviewed-by: Nam Cao Reviewed-by: Michael Ellerman Link: https://patch.msgid.link/20260714003056.73707-1-mark.hsj@gmail.com Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/sys_hwprobe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c index 199d13f86f313..460fb297672e6 100644 --- a/arch/riscv/kernel/sys_hwprobe.c +++ b/arch/riscv/kernel/sys_hwprobe.c @@ -409,6 +409,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs, if (cpusetsize > cpumask_size()) cpusetsize = cpumask_size(); + cpumask_clear(&cpus); ret = copy_from_user(&cpus, cpus_user, cpusetsize); if (ret) return -EFAULT; From cfb66ad4aa8e584abe02998560c274cbb3795776 Mon Sep 17 00:00:00 2001 From: Shuhao Fu Date: Thu, 7 May 2026 10:22:26 +0200 Subject: [PATCH 2350/3297] can: j1939: fix lockless local-destination check [ Upstream commit e4e8af62adab2fdcca230006f829407a953070cd ] j1939_priv.ents[].nusers is documented as protected by priv->lock, and its updates already happen under that lock. j1939_can_recv() also reads it under read_lock_bh(). However, j1939_session_skb_queue() and j1939_tp_send() still read priv->ents[da].nusers without taking the lock. Those transport-side checks decide whether to set J1939_ECU_LOCAL_DST, so they can race with j1939_local_ecu_get() and j1939_local_ecu_put() while userspace is binding or releasing sockets concurrently with TP traffic. This can misclassify TP/ETP sessions as local or remote and take the wrong transport path. Fix both transport paths by routing the destination-locality check through a helper that reads ents[].nusers under read_lock_bh(&priv->lock). Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Signed-off-by: Shuhao Fu Tested-by: Oleksij Rempel Acked-by: Oleksij Rempel Link: https://patch.msgid.link/20260419140614.GA4041240@chcpu16 Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- net/can/j1939/transport.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 8656ab388c83e..9ec929bc0c0da 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -350,6 +350,18 @@ static void j1939_session_skb_drop_old(struct j1939_session *session) } } +static bool j1939_address_is_local(struct j1939_priv *priv, u8 addr) +{ + bool local = false; + + read_lock_bh(&priv->lock); + if (j1939_address_is_unicast(addr) && priv->ents[addr].nusers) + local = true; + read_unlock_bh(&priv->lock); + + return local; +} + void j1939_session_skb_queue(struct j1939_session *session, struct sk_buff *skb) { @@ -358,8 +370,7 @@ void j1939_session_skb_queue(struct j1939_session *session, j1939_ac_fixup(priv, skb); - if (j1939_address_is_unicast(skcb->addr.da) && - priv->ents[skcb->addr.da].nusers) + if (j1939_address_is_local(priv, skcb->addr.da)) skcb->flags |= J1939_ECU_LOCAL_DST; skcb->flags |= J1939_ECU_LOCAL_SRC; @@ -2017,8 +2028,7 @@ struct j1939_session *j1939_tp_send(struct j1939_priv *priv, return ERR_PTR(ret); /* fix DST flags, it may be used there soon */ - if (j1939_address_is_unicast(skcb->addr.da) && - priv->ents[skcb->addr.da].nusers) + if (j1939_address_is_local(priv, skcb->addr.da)) skcb->flags |= J1939_ECU_LOCAL_DST; /* src is always local, I'm sending ... */ From 65129a03a80185e8b34b55d0a1d27d62130223ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Winiarski?= Date: Wed, 12 Nov 2025 14:22:07 +0100 Subject: [PATCH 2351/3297] drm/xe: Allow the caller to pass guc_buf_cache size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit dca2701a6277399f9d255f09b4c34d9a7171c09b ] An upcoming change will use GuC buffer cache as a place where GuC migration data will be stored, and the memory requirement for that is larger than indirect data. Allow the caller to pass the size based on the intended usecase. Reviewed-by: Michal Wajdeczko Link: https://patch.msgid.link/20251112132220.516975-12-michal.winiarski@intel.com Signed-off-by: Michał Winiarski Stable-dep-of: 56441f9e08ad ("drm/xe/vf: Fix VF CCS attach/detach race with in-flight BO moves") Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_guc_buf.c | 44 ++++++++++++++++++++++++--------- drivers/gpu/drm/xe/xe_guc_buf.h | 1 + 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_buf.c b/drivers/gpu/drm/xe/xe_guc_buf.c index 502ca3a4ee606..989c546d05689 100644 --- a/drivers/gpu/drm/xe/xe_guc_buf.c +++ b/drivers/gpu/drm/xe/xe_guc_buf.c @@ -13,6 +13,8 @@ #include "xe_guc_buf.h" #include "xe_sa.h" +#define XE_GUC_BUF_CACHE_DEFAULT_SIZE SZ_8K + static struct xe_guc *cache_to_guc(struct xe_guc_buf_cache *cache) { return container_of(cache, struct xe_guc, buf); @@ -23,21 +25,12 @@ static struct xe_gt *cache_to_gt(struct xe_guc_buf_cache *cache) return guc_to_gt(cache_to_guc(cache)); } -/** - * xe_guc_buf_cache_init() - Initialize the GuC Buffer Cache. - * @cache: the &xe_guc_buf_cache to initialize - * - * The Buffer Cache allows to obtain a reusable buffer that can be used to pass - * indirect H2G data to GuC without a need to create a ad-hoc allocation. - * - * Return: 0 on success or a negative error code on failure. - */ -int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache) +static int guc_buf_cache_init(struct xe_guc_buf_cache *cache, u32 size) { struct xe_gt *gt = cache_to_gt(cache); struct xe_sa_manager *sam; - sam = __xe_sa_bo_manager_init(gt_to_tile(gt), SZ_8K, 0, sizeof(u32)); + sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32)); if (IS_ERR(sam)) return PTR_ERR(sam); cache->sam = sam; @@ -48,6 +41,35 @@ int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache) return 0; } +/** + * xe_guc_buf_cache_init() - Initialize the GuC Buffer Cache. + * @cache: the &xe_guc_buf_cache to initialize + * + * The Buffer Cache allows to obtain a reusable buffer that can be used to pass + * data to GuC or read data from GuC without a need to create a ad-hoc allocation. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache) +{ + return guc_buf_cache_init(cache, XE_GUC_BUF_CACHE_DEFAULT_SIZE); +} + +/** + * xe_guc_buf_cache_init_with_size() - Initialize the GuC Buffer Cache. + * @cache: the &xe_guc_buf_cache to initialize + * @size: size in bytes + * + * Like xe_guc_buf_cache_init(), except it allows the caller to make the cache + * buffer larger, allowing to accommodate larger objects. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_guc_buf_cache_init_with_size(struct xe_guc_buf_cache *cache, u32 size) +{ + return guc_buf_cache_init(cache, max(XE_GUC_BUF_CACHE_DEFAULT_SIZE, size)); +} + /** * xe_guc_buf_cache_dwords() - Number of dwords the GuC Buffer Cache supports. * @cache: the &xe_guc_buf_cache to query diff --git a/drivers/gpu/drm/xe/xe_guc_buf.h b/drivers/gpu/drm/xe/xe_guc_buf.h index 0d67604d96bdd..8204e58962501 100644 --- a/drivers/gpu/drm/xe/xe_guc_buf.h +++ b/drivers/gpu/drm/xe/xe_guc_buf.h @@ -12,6 +12,7 @@ #include "xe_guc_buf_types.h" int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache); +int xe_guc_buf_cache_init_with_size(struct xe_guc_buf_cache *cache, u32 size); u32 xe_guc_buf_cache_dwords(struct xe_guc_buf_cache *cache); struct xe_guc_buf xe_guc_buf_reserve(struct xe_guc_buf_cache *cache, u32 dwords); struct xe_guc_buf xe_guc_buf_from_data(struct xe_guc_buf_cache *cache, From bf293b5bcff41217dacc86b9aea96fa7bd20447e Mon Sep 17 00:00:00 2001 From: Satyanarayana K V P Date: Tue, 18 Nov 2025 12:07:44 +0000 Subject: [PATCH 2352/3297] drm/xe/sa: Shadow buffer support in the sub-allocator pool [ Upstream commit 1f2cf5295cdba71818288c9e495b4ef5097565ed ] The existing sub-allocator is limited to managing a single buffer object. This enhancement introduces shadow buffer functionality to support scenarios requiring dual buffer management. The changes include added shadow buffer object creation capability, Management for both primary and shadow buffers, and appropriate locking mechanisms for thread-safe operations. This enables more flexible buffer allocation strategies in scenarios where shadow buffering is required. Signed-off-by: Satyanarayana K V P Suggested-by: Matthew Brost Cc: Michal Wajdeczko Cc: Matthew Auld Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20251118120745.3460172-2-satyanarayana.k.v.p@intel.com Stable-dep-of: 56441f9e08ad ("drm/xe/vf: Fix VF CCS attach/detach race with in-flight BO moves") Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_guc_buf.c | 2 +- drivers/gpu/drm/xe/xe_sa.c | 67 +++++++++++++++++++++++++++- drivers/gpu/drm/xe/xe_sa.h | 20 ++++++++- drivers/gpu/drm/xe/xe_sa_types.h | 3 ++ drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 3 ++ 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_buf.c b/drivers/gpu/drm/xe/xe_guc_buf.c index 989c546d05689..3de6377ae2d52 100644 --- a/drivers/gpu/drm/xe/xe_guc_buf.c +++ b/drivers/gpu/drm/xe/xe_guc_buf.c @@ -30,7 +30,7 @@ static int guc_buf_cache_init(struct xe_guc_buf_cache *cache, u32 size) struct xe_gt *gt = cache_to_gt(cache); struct xe_sa_manager *sam; - sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32)); + sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32), 0); if (IS_ERR(sam)) return PTR_ERR(sam); cache->sam = sam; diff --git a/drivers/gpu/drm/xe/xe_sa.c b/drivers/gpu/drm/xe/xe_sa.c index fedd017d6dd36..d9e0d30f8135e 100644 --- a/drivers/gpu/drm/xe/xe_sa.c +++ b/drivers/gpu/drm/xe/xe_sa.c @@ -29,6 +29,7 @@ static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg) kvfree(sa_manager->cpu_ptr); sa_manager->bo = NULL; + sa_manager->shadow = NULL; } /** @@ -37,12 +38,14 @@ static void xe_sa_bo_manager_fini(struct drm_device *drm, void *arg) * @size: number of bytes to allocate * @guard: number of bytes to exclude from suballocations * @align: alignment for each suballocated chunk + * @flags: flags for suballocator * * Prepares the suballocation manager for suballocations. * * Return: a pointer to the &xe_sa_manager or an ERR_PTR on failure. */ -struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 guard, u32 align) +struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, + u32 guard, u32 align, u32 flags) { struct xe_device *xe = tile_to_xe(tile); struct xe_sa_manager *sa_manager; @@ -79,6 +82,26 @@ struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u3 memset(sa_manager->cpu_ptr, 0, bo->ttm.base.size); } + if (flags & XE_SA_BO_MANAGER_FLAG_SHADOW) { + struct xe_bo *shadow; + + ret = drmm_mutex_init(&xe->drm, &sa_manager->swap_guard); + if (ret) + return ERR_PTR(ret); + + shadow = xe_managed_bo_create_pin_map(xe, tile, size, + XE_BO_FLAG_VRAM_IF_DGFX(tile) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE); + if (IS_ERR(shadow)) { + drm_err(&xe->drm, "Failed to prepare %uKiB BO for SA manager (%pe)\n", + size / SZ_1K, shadow); + return ERR_CAST(shadow); + } + sa_manager->shadow = shadow; + } + drm_suballoc_manager_init(&sa_manager->base, managed_size, align); ret = drmm_add_action_or_reset(&xe->drm, xe_sa_bo_manager_fini, sa_manager); @@ -88,6 +111,48 @@ struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u3 return sa_manager; } +/** + * xe_sa_bo_swap_shadow() - Swap the SA BO with shadow BO. + * @sa_manager: the XE sub allocator manager + * + * Swaps the sub-allocator primary buffer object with shadow buffer object. + * + * Return: None. + */ +void xe_sa_bo_swap_shadow(struct xe_sa_manager *sa_manager) +{ + struct xe_device *xe = tile_to_xe(sa_manager->bo->tile); + + xe_assert(xe, sa_manager->shadow); + lockdep_assert_held(&sa_manager->swap_guard); + + swap(sa_manager->bo, sa_manager->shadow); + if (!sa_manager->bo->vmap.is_iomem) + sa_manager->cpu_ptr = sa_manager->bo->vmap.vaddr; +} + +/** + * xe_sa_bo_sync_shadow() - Sync the SA Shadow BO with primary BO. + * @sa_bo: the sub-allocator buffer object. + * + * Synchronize sub-allocator shadow buffer object with primary buffer object. + * + * Return: None. + */ +void xe_sa_bo_sync_shadow(struct drm_suballoc *sa_bo) +{ + struct xe_sa_manager *sa_manager = to_xe_sa_manager(sa_bo->manager); + struct xe_device *xe = tile_to_xe(sa_manager->bo->tile); + + xe_assert(xe, sa_manager->shadow); + lockdep_assert_held(&sa_manager->swap_guard); + + xe_map_memcpy_to(xe, &sa_manager->shadow->vmap, + drm_suballoc_soffset(sa_bo), + xe_sa_bo_cpu_addr(sa_bo), + drm_suballoc_size(sa_bo)); +} + /** * __xe_sa_bo_new() - Make a suballocation but use custom gfp flags. * @sa_manager: the &xe_sa_manager diff --git a/drivers/gpu/drm/xe/xe_sa.h b/drivers/gpu/drm/xe/xe_sa.h index 99dbf0eea5402..90ffa93a000ce 100644 --- a/drivers/gpu/drm/xe/xe_sa.h +++ b/drivers/gpu/drm/xe/xe_sa.h @@ -14,12 +14,14 @@ struct dma_fence; struct xe_tile; -struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 guard, u32 align); +#define XE_SA_BO_MANAGER_FLAG_SHADOW BIT(0) +struct xe_sa_manager *__xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, + u32 guard, u32 align, u32 flags); struct drm_suballoc *__xe_sa_bo_new(struct xe_sa_manager *sa_manager, u32 size, gfp_t gfp); static inline struct xe_sa_manager *xe_sa_bo_manager_init(struct xe_tile *tile, u32 size, u32 align) { - return __xe_sa_bo_manager_init(tile, size, SZ_4K, align); + return __xe_sa_bo_manager_init(tile, size, SZ_4K, align, 0); } /** @@ -68,4 +70,18 @@ static inline void *xe_sa_bo_cpu_addr(struct drm_suballoc *sa) drm_suballoc_soffset(sa); } +void xe_sa_bo_swap_shadow(struct xe_sa_manager *sa_manager); +void xe_sa_bo_sync_shadow(struct drm_suballoc *sa_bo); + +/** + * xe_sa_bo_swap_guard() - Retrieve the SA BO swap guard within sub-allocator. + * @sa_manager: the &xe_sa_manager + * + * Return: Sub alloctor swap guard mutex. + */ +static inline struct mutex *xe_sa_bo_swap_guard(struct xe_sa_manager *sa_manager) +{ + return &sa_manager->swap_guard; +} + #endif diff --git a/drivers/gpu/drm/xe/xe_sa_types.h b/drivers/gpu/drm/xe/xe_sa_types.h index cb7238799dcb2..1085c9c37d6b6 100644 --- a/drivers/gpu/drm/xe/xe_sa_types.h +++ b/drivers/gpu/drm/xe/xe_sa_types.h @@ -12,6 +12,9 @@ struct xe_bo; struct xe_sa_manager { struct drm_suballoc_manager base; struct xe_bo *bo; + struct xe_bo *shadow; + /** @swap_guard: Timeline guard updating @bo and @shadow */ + struct mutex swap_guard; void *cpu_ptr; bool is_iomem; }; diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 739a3eb180b53..1a7d767632dad 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -160,9 +160,12 @@ static int alloc_bb_pool(struct xe_tile *tile, struct xe_sriov_vf_ccs_ctx *ctx) offset = 0; xe_map_memset(xe, &sa_manager->bo->vmap, offset, MI_NOOP, bb_pool_size); + xe_map_memset(xe, &sa_manager->shadow->vmap, offset, MI_NOOP, + bb_pool_size); offset = bb_pool_size - sizeof(u32); xe_map_wr(xe, &sa_manager->bo->vmap, offset, u32, MI_BATCH_BUFFER_END); + xe_map_wr(xe, &sa_manager->shadow->vmap, offset, u32, MI_BATCH_BUFFER_END); ctx->mem.ccs_bb_pool = sa_manager; From c1b19d8556265f6098f68855d48e1a40d5d69f7f Mon Sep 17 00:00:00 2001 From: Satyanarayana K V P Date: Tue, 18 Nov 2025 12:07:45 +0000 Subject: [PATCH 2353/3297] drm/xe/vf: Shadow buffer management for CCS read/write operations [ Upstream commit fa18290bf0723b02bfa8d30d2e14722f0d096c2c ] CCS copy command consist of 5-dword sequence. If vCPU halts during save/restore operations while these sequences are being programmed, incomplete writes can cause page faults during IGPU CCS metadata saving. Use shadow buffer management to prevent partial write issues during CCS operations. Signed-off-by: Satyanarayana K V P Suggested-by: Matthew Brost Cc: Michal Wajdeczko Cc: Matthew Auld Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20251118120745.3460172-3-satyanarayana.k.v.p@intel.com Stable-dep-of: 56441f9e08ad ("drm/xe/vf: Fix VF CCS attach/detach race with in-flight BO moves") Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_migrate.c | 57 ++++++++++++++++++++++++++-- drivers/gpu/drm/xe/xe_migrate.h | 3 ++ drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 19 ++++++++-- drivers/gpu/drm/xe/xe_sriov_vf_ccs.h | 1 + 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index b6905f35d6c81..6a0c088d09fb3 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -33,6 +33,7 @@ #include "xe_res_cursor.h" #include "xe_sa.h" #include "xe_sched_job.h" +#include "xe_sriov_vf_ccs.h" #include "xe_sync.h" #include "xe_trace_bo.h" #include "xe_validation.h" @@ -1022,12 +1023,16 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, u32 batch_size, batch_size_allocated; struct xe_device *xe = gt_to_xe(gt); struct xe_res_cursor src_it, ccs_it; + struct xe_sriov_vf_ccs_ctx *ctx; + struct xe_sa_manager *bb_pool; u64 size = xe_bo_size(src_bo); struct xe_bb *bb = NULL; u64 src_L0, src_L0_ofs; u32 src_L0_pt; int err; + ctx = &xe->sriov.vf.ccs.contexts[read_write]; + xe_res_first_sg(xe_bo_sg(src_bo), 0, size, &src_it); xe_res_first_sg(xe_bo_sg(src_bo), xe_bo_ccs_pages_start(src_bo), @@ -1060,11 +1065,15 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, size -= src_L0; } + bb_pool = ctx->mem.ccs_bb_pool; + guard(mutex) (xe_sa_bo_swap_guard(bb_pool)); + xe_sa_bo_swap_shadow(bb_pool); + bb = xe_bb_ccs_new(gt, batch_size, read_write); if (IS_ERR(bb)) { drm_err(&xe->drm, "BB allocation failed.\n"); err = PTR_ERR(bb); - goto err_ret; + return err; } batch_size_allocated = batch_size; @@ -1113,10 +1122,52 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, xe_assert(xe, (batch_size_allocated == bb->len)); src_bo->bb_ccs[read_write] = bb; + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + xe_sa_bo_sync_shadow(bb->bo); return 0; +} -err_ret: - return err; +/** + * xe_migrate_ccs_rw_copy_clear() - Clear the CCS read/write batch buffer + * content. + * @src_bo: The buffer object @src is currently bound to. + * @read_write : Creates BB commands for CCS read/write. + * + * Directly clearing the BB lacks atomicity and can lead to undefined + * behavior if the vCPU is halted mid-operation during the clearing + * process. To avoid this issue, we use a shadow buffer object approach. + * + * First swap the SA BO address with the shadow BO, perform the clearing + * operation on the BB, update the shadow BO in the ring buffer, then + * sync the shadow and the actual buffer to maintain consistency. + * + * Returns: None. + */ +void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, + enum xe_sriov_vf_ccs_rw_ctxs read_write) +{ + struct xe_bb *bb = src_bo->bb_ccs[read_write]; + struct xe_device *xe = xe_bo_device(src_bo); + struct xe_sriov_vf_ccs_ctx *ctx; + struct xe_sa_manager *bb_pool; + u32 *cs; + + xe_assert(xe, IS_SRIOV_VF(xe)); + + ctx = &xe->sriov.vf.ccs.contexts[read_write]; + bb_pool = ctx->mem.ccs_bb_pool; + + guard(mutex) (xe_sa_bo_swap_guard(bb_pool)); + xe_sa_bo_swap_shadow(bb_pool); + + cs = xe_sa_bo_cpu_addr(bb->bo); + memset(cs, MI_NOOP, bb->len * sizeof(u32)); + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + + xe_sa_bo_sync_shadow(bb->bo); + + xe_bb_free(bb, NULL); + src_bo->bb_ccs[read_write] = NULL; } /** diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index bc55f650204b8..438c3bb66d9b2 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -131,6 +131,9 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, struct xe_bo *src_bo, enum xe_sriov_vf_ccs_rw_ctxs read_write); +void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, + enum xe_sriov_vf_ccs_rw_ctxs read_write); + struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate); struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate); int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo, diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 1a7d767632dad..d62156195f321 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -148,7 +148,8 @@ static int alloc_bb_pool(struct xe_tile *tile, struct xe_sriov_vf_ccs_ctx *ctx) xe_sriov_info(xe, "Allocating %s CCS BB pool size = %lldMB\n", ctx->ctx_id ? "Restore" : "Save", bb_pool_size / SZ_1M); - sa_manager = xe_sa_bo_manager_init(tile, bb_pool_size, SZ_16); + sa_manager = __xe_sa_bo_manager_init(tile, bb_pool_size, SZ_4K, SZ_16, + XE_SA_BO_MANAGER_FLAG_SHADOW); if (IS_ERR(sa_manager)) { xe_sriov_err(xe, "Suballocator init failed with error: %pe\n", @@ -314,6 +315,18 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe) return err; } +#define XE_SRIOV_VF_CCS_RW_BB_ADDR_OFFSET (2 * sizeof(u32)) +void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx) +{ + u64 addr = xe_sa_manager_gpu_addr(ctx->mem.ccs_bb_pool); + struct xe_lrc *lrc = xe_exec_queue_lrc(ctx->mig_q); + struct xe_device *xe = gt_to_xe(ctx->mig_q->gt); + + xe_device_wmb(xe); + xe_map_wr(xe, &lrc->bo->vmap, XE_SRIOV_VF_CCS_RW_BB_ADDR_OFFSET, u32, addr); + xe_device_wmb(xe); +} + /** * xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO. * @bo: the &buffer object to which batch buffer commands will be added. @@ -374,9 +387,7 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo) if (!bb) continue; - memset(bb->cs, MI_NOOP, bb->len * sizeof(u32)); - xe_bb_free(bb, NULL); - bo->bb_ccs[ctx_id] = NULL; + xe_migrate_ccs_rw_copy_clear(bo, ctx_id); } return 0; } diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h index 0745c0ff02282..a9c6512f87034 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h @@ -19,6 +19,7 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo); int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo); int xe_sriov_vf_ccs_register_context(struct xe_device *xe); void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p); +void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx); static inline bool xe_sriov_vf_ccs_ready(struct xe_device *xe) { From 35ba43b541117bfb595e4b807ba447cf4335cc7d Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Mon, 13 Jul 2026 23:24:40 -0700 Subject: [PATCH 2354/3297] drm/xe/vf: Fix VF CCS attach/detach race with in-flight BO moves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 56441f9e08ad68697295b8835266d2bc48ab59b5 ] xe_bo_move() attaches VF CCS read/write batch buffers (BBs) to a BO after it transitions NULL/SYSTEM -> TT, and detaches them after it transitions TT -> SYSTEM. Both operations were done synchronously on the CPU immediately after building the move's copy/clear fence, without waiting for that fence to signal. This creates two races with VF migration: - Attach happens too late relative to the copy job it is meant to protect. If the copy job is submitted before the CCS BBs are attached, a VF migration event that pauses execution mid-copy can observe partially copied CCS metadata without the attach state needed to correctly save/restore it. - Detach happens too early relative to the copy job that moves data out of TT. The CCS BBs are torn down right after the copy fence is obtained, while the actual blit may still be in flight. A VF migration event that pauses execution mid-copy can then race the save/restore path against the still-running blit, and the CCS BBs it would need to make sense of the paused state have already been removed. Fix both races: - Move the attach call to before the copy/clear job is submitted, so the CCS BBs are already registered by the time the copy runs. On attach failure, unwind and bail out of the move. xe_migrate_ccs_rw_copy() now takes the destination resource explicitly, since bo->ttm.resource is not updated to the new resource until after the move commits. - Detach only after explicitly waiting for the copy fence to signal, instead of tearing down the CCS BBs immediately after obtaining it. While here, also fix xe_sriov_vf_ccs_attach_bo() to properly unwind and propagate errors: the per-context loop previously never broke out on error, silently discarding earlier failures. Unwind by clearing each attached context directly via xe_migrate_ccs_rw_copy_clear() instead of reusing xe_sriov_vf_ccs_detach_bo(), which requires both contexts to be attached before it will clean up either one. Fixes: 864690cf4dd6 ("drm/xe/vf: Attach and detach CCS copy commands with BO") Cc: Michal Wajdeczko Cc: Matthew Auld Cc: Michał Winiarski Cc: Satyanarayana K V P Assisted-by: GitHub_Copilot:claude-sonnet-5 Signed-off-by: Matthew Brost Acked-by: Satyanarayana K V P Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260714062440.3421225-1-matthew.brost@intel.com (cherry picked from commit d45ad0aa7a1eb5d7288b5ed948b05695611dc39e) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_bo.c | 34 +++++++++++++++++++--------- drivers/gpu/drm/xe/xe_migrate.c | 5 +++- drivers/gpu/drm/xe/xe_migrate.h | 1 + drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 20 ++++++++++++++-- drivers/gpu/drm/xe/xe_sriov_vf_ccs.h | 3 ++- 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 0dabe85351392..1d46b61bf1285 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -930,6 +930,21 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict, xe_pm_runtime_get_noresume(xe); } + /* + * Attach CCS BBs before submitting the copy job below so a VF + * migration racing the copy sees valid, up to date attach state. + */ + if (IS_VF_CCS_READY(xe) && + ((move_lacks_source && new_mem->mem_type == XE_PL_TT) || + (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) && + handle_system_ccs) { + ret = xe_sriov_vf_ccs_attach_bo(bo, new_mem); + if (ret) { + xe_pm_runtime_put(xe); + goto out; + } + } + if (move_lacks_source) { u32 flags = 0; @@ -967,22 +982,19 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict, ttm_bo_move_null(ttm_bo, new_mem); } - dma_fence_put(fence); - xe_pm_runtime_put(xe); - /* - * CCS meta data is migrated from TT -> SMEM. So, let us detach the - * BBs from BO as it is no longer needed. + * Detach must wait for the copy above to complete: a VF migration + * racing an in-flight copy must still see valid CCS BBs, so don't + * tear them down until the copy fence has signaled. */ if (IS_VF_CCS_READY(xe) && old_mem_type == XE_PL_TT && - new_mem->mem_type == XE_PL_SYSTEM) + new_mem->mem_type == XE_PL_SYSTEM) { + dma_fence_wait(fence, false); xe_sriov_vf_ccs_detach_bo(bo); + } - if (IS_VF_CCS_READY(xe) && - ((move_lacks_source && new_mem->mem_type == XE_PL_TT) || - (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) && - handle_system_ccs) - ret = xe_sriov_vf_ccs_attach_bo(bo); + dma_fence_put(fence); + xe_pm_runtime_put(xe); out: if ((!ttm_bo->resource || ttm_bo->resource->mem_type == XE_PL_SYSTEM) && diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 6a0c088d09fb3..e0a14360d312e 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -1001,6 +1001,8 @@ static int emit_flush_invalidate(struct xe_exec_queue *q, u32 *dw, int i, * @tile: Tile whose migration context to be used. * @q : Execution to be used along with migration context. * @src_bo: The buffer object @src is currently bound to. + * @new_mem: The (not yet committed) destination resource @src_bo is being + * moved into; src_bo->ttm.resource is still the old resource. * @read_write : Creates BB commands for CCS read/write. * * Creates batch buffer instructions to copy CCS metadata from CCS pool to @@ -1012,12 +1014,13 @@ static int emit_flush_invalidate(struct xe_exec_queue *q, u32 *dw, int i, */ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, struct xe_bo *src_bo, + struct ttm_resource *new_mem, enum xe_sriov_vf_ccs_rw_ctxs read_write) { bool src_is_pltt = read_write == XE_SRIOV_VF_CCS_READ_CTX; bool dst_is_pltt = read_write == XE_SRIOV_VF_CCS_WRITE_CTX; - struct ttm_resource *src = src_bo->ttm.resource; + struct ttm_resource *src = new_mem; struct xe_migrate *m = tile->migrate; struct xe_gt *gt = tile->primary_gt; u32 batch_size, batch_size_allocated; diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index 438c3bb66d9b2..8bac669685ee9 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -129,6 +129,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m, int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, struct xe_bo *src_bo, + struct ttm_resource *new_mem, enum xe_sriov_vf_ccs_rw_ctxs read_write); void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index d62156195f321..e03714ef7ae99 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -330,6 +330,8 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx) /** * xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO. * @bo: the &buffer object to which batch buffer commands will be added. + * @new_mem: the (not yet committed) destination resource @bo is being moved + * into; bo->ttm.resource is still the old resource at this point. * * This function shall be called only by VF. It inserts the PTEs and copy * command instructions in the BO by calling xe_migrate_ccs_rw_copy() @@ -337,7 +339,7 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx) * * Returns: 0 if successful, negative error code on failure. */ -int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo) +int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem) { struct xe_device *xe = xe_bo_device(bo); enum xe_sriov_vf_ccs_rw_ctxs ctx_id; @@ -356,7 +358,21 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo) xe_assert(xe, !bb); ctx = &xe->sriov.vf.ccs.contexts[ctx_id]; - err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, ctx_id); + err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, new_mem, ctx_id); + if (err) + goto err_unwind; + } + return 0; + +err_unwind: + /* + * Clean up any contexts already attached. Can't reuse + * xe_sriov_vf_ccs_detach_bo() here as it requires both contexts + * attached before cleaning up either one. + */ + for_each_ccs_rw_ctx(ctx_id) { + if (bo->bb_ccs[ctx_id]) + xe_migrate_ccs_rw_copy_clear(bo, ctx_id); } return err; } diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h index a9c6512f87034..00d2c9dbe00d9 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h @@ -11,11 +11,12 @@ #include "xe_sriov_vf_ccs_types.h" struct drm_printer; +struct ttm_resource; struct xe_device; struct xe_bo; int xe_sriov_vf_ccs_init(struct xe_device *xe); -int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo); +int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem); int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo); int xe_sriov_vf_ccs_register_context(struct xe_device *xe); void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p); From 5e5f298d0af6416af87b2dbb0faff80141deb9a9 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 13 Jul 2026 15:17:59 -0700 Subject: [PATCH 2355/3297] drm/xe/wopcm: fix WOPCM size for LNL+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ad87e2476b3b246580f407afc8ffa91d621bc849 ] Starting on LNL the WOPCM size is 8MB instead of 4, so we need to avoid using the [0, 8MB) range of the GGTT as that can be unaccessible from the microcontrollers. Note that the proper long-term fix here is to read the WOPCM size from the HW, but that is a more serious rework that would be difficult to backport, so we can do that as a follow-up. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Signed-off-by: Daniele Ceraolo Spurio Cc: Rodrigo Vivi Cc: Shuicheng Lin Cc: Matt Roper Reviewed-by: Shuicheng Lin Link: https://patch.msgid.link/20260713221758.3285744-2-daniele.ceraolospurio@intel.com (cherry picked from commit 3033b0b24ed0e2f5e56bdd4d9c183417c365a45b) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_wopcm.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_wopcm.c b/drivers/gpu/drm/xe/xe_wopcm.c index ada0d0aa6b749..2fd2a82be89f8 100644 --- a/drivers/gpu/drm/xe/xe_wopcm.c +++ b/drivers/gpu/drm/xe/xe_wopcm.c @@ -49,9 +49,9 @@ */ /* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */ -/* FIXME: Larger size require for 2 tile PVC, do a proper probe sooner or later */ +/* FIXME: Larger size require for some platforms, do a proper probe sooner or later */ #define DGFX_WOPCM_SIZE SZ_4M -/* FIXME: Larger size require for MTL, do a proper probe sooner or later */ +#define LNL_WOPCM_SIZE SZ_8M #define MTL_WOPCM_SIZE SZ_4M #define WOPCM_SIZE SZ_2M @@ -181,9 +181,14 @@ static int __wopcm_init_regs(struct xe_device *xe, struct xe_gt *gt, u32 xe_wopcm_size(struct xe_device *xe) { - return IS_DGFX(xe) ? DGFX_WOPCM_SIZE : - xe->info.platform == XE_METEORLAKE ? MTL_WOPCM_SIZE : - WOPCM_SIZE; + if (xe->info.platform >= XE_LUNARLAKE) + return LNL_WOPCM_SIZE; + else if (IS_DGFX(xe)) + return DGFX_WOPCM_SIZE; + else if (xe->info.platform == XE_METEORLAKE) + return MTL_WOPCM_SIZE; + else + return WOPCM_SIZE; } /** From 6ecb252efa0b413ac3d9979fb4eec247f8fc1258 Mon Sep 17 00:00:00 2001 From: Qihang Date: Thu, 9 Jul 2026 22:49:55 +0800 Subject: [PATCH 2356/3297] ksmbd: pin conn during async oplock break notification [ Upstream commit aa5d8f3f96aa11a4a54ce993c11ce8af11c546f9 ] smb2_oplock_break_noti() and smb2_lease_break_noti() store a ksmbd_conn pointer in an async ksmbd_work and then queue that work on ksmbd-io. The work only increments conn->r_count, which prevents teardown from passing the pending-request wait after the increment, but it does not pin the struct ksmbd_conn object. If connection teardown races with an oplock break notification, the last conn reference can be dropped before the queued worker finishes. The worker then uses the freed conn in ksmbd_conn_write() and ksmbd_conn_r_count_dec(). Take a real conn reference when publishing the conn pointer to the async work item, and drop it after the notification work has decremented r_count. Apply the same lifetime rule to lease break notification, which uses the same work->conn pattern. Fixes: 3aa660c05924 ("ksmbd: prevent connection release during oplock break notification") Signed-off-by: Qihang Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/oplock.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index a019c4496ae43..ff0dbf3f8cbf4 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -705,6 +705,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) out: ksmbd_free_work_struct(work); ksmbd_conn_r_count_dec(conn); + ksmbd_conn_put(conn); } /** @@ -740,7 +741,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) br_info->open_trunc = opinfo->open_trunc; work->request_buf = (char *)br_info; - work->conn = conn; + work->conn = ksmbd_conn_get(conn); work->sess = opinfo->sess; ksmbd_conn_r_count_inc(conn); @@ -814,6 +815,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) out: ksmbd_free_work_struct(work); ksmbd_conn_r_count_dec(conn); + ksmbd_conn_put(conn); } /** @@ -853,7 +855,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); work->request_buf = (char *)br_info; - work->conn = conn; + work->conn = ksmbd_conn_get(conn); work->sess = opinfo->sess; ksmbd_conn_r_count_inc(conn); From f0e337e7db67cc1c832958bbb6c4026bdceacfdb Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Mon, 13 Jul 2026 21:55:10 +0000 Subject: [PATCH 2357/3297] ksmbd: validate compound request size before reading StructureSize2 [ Upstream commit 15b38176fd1530372905c602fde51fe89ec8c877 ] When ksmbd validates a compound (chained) SMB2 request, ksmbd_smb2_check_message() reads pdu->StructureSize2 without first checking that the compound element is large enough to contain it. StructureSize2 is a 2-byte field at offset 64 (__SMB2_HEADER_STRUCTURE_SIZE) from the start of each element. The compound-walking logic only guarantees that a full 64-byte SMB2 header is present for the trailing element: when NextCommand is 0, len is reduced to the number of bytes remaining after next_smb2_rcv_hdr_off. A remote client can craft a compound request whose last element has exactly 64 bytes, so the 2-byte StructureSize2 read at offset 64 extends one byte past the receive buffer, producing a slab-out-of-bounds read. BUG: KASAN: slab-out-of-bounds in ksmbd_smb2_check_message (fs/smb/server/smb2misc.c:402) Read of size 2 at addr ffff888012ae31ac by task kworker/0:1/14 The buggy address is located 172 bytes inside of allocated 173-byte region Workqueue: ksmbd-io handle_ksmbd_work Call Trace: ... kasan_report (mm/kasan/report.c:595) ksmbd_smb2_check_message (fs/smb/server/smb2misc.c:402) handle_ksmbd_work (fs/smb/server/server.c:119) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Reject any compound element that is too small to hold StructureSize2 before dereferencing it. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smb2misc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index 67a2d7a793f6e..b11d854d3fcfb 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -400,6 +400,11 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) return 1; } + if (len < __SMB2_HEADER_STRUCTURE_SIZE + sizeof(__le16)) { + ksmbd_debug(SMB, "Message is too small for StructureSize2\n"); + return 1; + } + if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { if (!(command == SMB2_OPLOCK_BREAK_HE && (le16_to_cpu(pdu->StructureSize2) == OP_BREAK_STRUCT_SIZE_20 || From c23abdb922c398601bdf6c8fc8b164582d97c60b Mon Sep 17 00:00:00 2001 From: Vinod Govindapillai Date: Mon, 15 Jun 2026 23:33:49 +0300 Subject: [PATCH 2358/3297] drm/i915/wm: clear the plane ddb_y entries on plane disable [ Upstream commit e89978c1cff54e265345c66e1177d19ea5a8bc00 ] The UV/Y plane DDB entriess are never cleared on sk_wm_plane_disable_noatomic() and can leave stale DDB state for NV12 planes on pre-Gen11 devices Fixes: d34b59d5ba41 ("drm/i915: Add skl_wm_plane_disable_noatomic()") Assisted-by: Copilot:claude-sonnet-4.6 Signed-off-by: Vinod Govindapillai Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260615203355.218578-2-vinod.govindapillai@intel.com (cherry picked from commit 60f68a6ba298fd1e971a2d91576304bee89a16fc) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/skl_watermark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index a5862998bf144..eb54fffc69886 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -3815,7 +3815,7 @@ void skl_wm_plane_disable_noatomic(struct intel_crtc *crtc, return; skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0); - skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0); + skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb_y[plane->id], 0, 0); crtc_state->wm.skl.plane_min_ddb[plane->id] = 0; crtc_state->wm.skl.plane_interim_ddb[plane->id] = 0; From f6b522033bc830d804a26aeaa5c800bfdf218876 Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Wed, 15 Jul 2026 01:04:30 +0300 Subject: [PATCH 2359/3297] drm/i915/selftests: Fix GT PM sort comparators [ Upstream commit 612978b83f45bf7018815209db5395d759db6f26 ] Compare the sampled clock values instead of their addresses. Comparing addresses leaves the samples unsorted, preventing the code from discarding the minimum and maximum samples. Fixes: 1a5392479207 ("drm/i915/selftests: Measure CS_TIMESTAMP") Signed-off-by: Emre Cecanpunar Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260714220430.238433-1-emreleno@gmail.com (cherry picked from commit 682ea2d28d18bb06f9fc663cb5ab7e80dc0e606a) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/gt/selftest_gt_pm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c index 33351deeea4f0..07eaf71955c44 100644 --- a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c +++ b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c @@ -16,9 +16,9 @@ static int cmp_u64(const void *A, const void *B) { const u64 *a = A, *b = B; - if (a < b) + if (*a < *b) return -1; - else if (a > b) + else if (*a > *b) return 1; else return 0; @@ -28,9 +28,9 @@ static int cmp_u32(const void *A, const void *B) { const u32 *a = A, *b = B; - if (a < b) + if (*a < *b) return -1; - else if (a > b) + else if (*a > *b) return 1; else return 0; From 6875ee2bef48f5d9f045d81a8a4d68893f768a8a Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Thu, 16 Jul 2026 08:13:05 -0700 Subject: [PATCH 2360/3297] accel/amdxdna: Fix use-after-free of mm_struct in job scheduler [ Upstream commit faebb7ba1ac65fa5810b640df02ce04e509fdc11 ] amdxdna_cmd_submit() stores current->mm in job->mm without holding any reference. aie2_sched_job_run() later access job->mm from the DRM scheduler worker thread. With only a raw pointer and no structural reference, the mm_struct can be freed before the scheduler runs the job. Fix this by calling mmgrab() to hold a structural mm_count reference for the lifetime of the job, paired with mmdrop() in every cleanup path. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260716151305.1595780-1-lizhi.hou@amd.com Signed-off-by: Sasha Levin --- drivers/accel/amdxdna/amdxdna_ctx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index cfee89681ff3c..347e046d2a0fb 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -393,6 +393,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job) amdxdna_arg_bos_put(job); amdxdna_gem_put_obj(job->cmd_bo); dma_fence_put(job->fence); + mmdrop(job->mm); } int amdxdna_cmd_submit(struct amdxdna_client *client, @@ -443,6 +444,7 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, job->hwctx = hwctx; job->mm = current->mm; + mmgrab(job->mm); job->fence = amdxdna_fence_create(hwctx); if (!job->fence) { @@ -475,6 +477,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, cmd_put: amdxdna_gem_put_obj(job->cmd_bo); free_job: + if (job->mm) + mmdrop(job->mm); kfree(job); return ret; } From e666af5dcc905ba694745963174d232deb478c55 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 10 Jul 2026 18:13:17 +0000 Subject: [PATCH 2361/3297] tcp: fix TIME_WAIT socket reference leak on PSP policy failure [ Upstream commit 2c1931a81122c3cdc4c89448fe0442c69e21c0d5 ] Release the TIME_WAIT socket reference and jump to discard_it upon PSP policy failure in both IPv4 and IPv6 receive paths. This prevents a memory leak of tcp_tw_bucket structures. Fixes: 659a2899a57d ("tcp: add datapath logic for PSP with inline key exchange") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Reviewed-by: Daniel Zahka Link: https://patch.msgid.link/20260710181317.4060230-1-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/tcp_ipv4.c | 6 ++++-- net/ipv6/tcp_ipv6.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f0106c2be1cf2..a1ed4fa264836 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2452,8 +2452,10 @@ int tcp_v4_rcv(struct sk_buff *skb) } drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb); - if (drop_reason) - break; + if (drop_reason) { + inet_twsk_put(inet_twsk(sk)); + goto discard_it; + } } /* to ACK */ fallthrough; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 59b5900dd42bd..ad9f69d49f2e3 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1984,8 +1984,10 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) } drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb); - if (drop_reason) - break; + if (drop_reason) { + inet_twsk_put(inet_twsk(sk)); + goto discard_it; + } } /* to ACK */ fallthrough; From 51c2fcc4cd2e4c52bd1970558f6e5356fdc51154 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Fri, 10 Jul 2026 21:36:25 +0200 Subject: [PATCH 2362/3297] dpll: fix NULL pointer dereference in dpll_msg_add_pin_ref_sync() [ Upstream commit d2e914a4a0d0f753dbae830264850d044026167c ] When a dpll_pin is shared across multiple dpll_device instances and those devices are being unregistered (e.g. during driver module removal), a NULL pointer dereference can occur in dpll_msg_add_pin_ref_sync(). This happens under the following conditions: - A pin is registered with two or more dpll devices (dpll_A, dpll_B) - The pin has ref_sync pairs with other pins - During unregistration of dpll_A's pins, a ref_sync partner pin is unregistered first, removing it from dpll_A->pin_refs - But since the partner pin is still registered with dpll_B, its dpll_refs is not empty, so dpll_pin_ref_sync_pair_del() does NOT run and the partner stays in the pin's ref_sync_pins xarray - When the pin itself is then unregistered from dpll_A, the delete notification calls dpll_msg_add_pin_ref_sync() which finds the partner in ref_sync_pins, passes dpll_pin_available() (partner is still registered with dpll_B), but dpll_pin_on_dpll_priv(dpll_A, partner) returns NULL because partner was already removed from dpll_A->pin_refs - The NULL priv pointer is passed to the driver's ref_sync_get callback, which dereferences it BUG: kernel NULL pointer dereference, address: 0000000000000034 Oops: Oops: 0000 [#1] SMP NOPTI RIP: 0010:zl3073x_dpll_input_pin_ref_sync_get+0x73/0x80 [zl3073x] Call Trace: dpll_msg_add_pin_ref_sync+0xb8/0x200 dpll_cmd_pin_get_one+0x3b6/0x4b0 dpll_pin_event_send+0x72/0x140 __dpll_pin_unregister+0x5a/0x2b0 dpll_pin_unregister+0x49/0x70 Fix this by skipping ref_sync pins whose priv pointer cannot be resolved for the current dpll device. Fixes: 58256a26bfb3 ("dpll: add reference sync get/set") Signed-off-by: Ivan Vecera Reviewed-by: Vadim Fedorenko Reviewed-by: Jiri Pirko Link: https://patch.msgid.link/20260710193625.1378822-1-ivecera@redhat.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/dpll/dpll_netlink.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index de8b065280d15..eb23ee401aaef 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -486,6 +486,9 @@ dpll_msg_add_pin_ref_sync(struct sk_buff *msg, struct dpll_pin *pin, if (!dpll_pin_available(ref_sync_pin)) continue; ref_sync_pin_priv = dpll_pin_on_dpll_priv(dpll, ref_sync_pin); + /* Pin may have been unregistered from this dpll already */ + if (!ref_sync_pin_priv) + continue; if (WARN_ON(!ops->ref_sync_get)) return -EOPNOTSUPP; ret = ops->ref_sync_get(pin, pin_priv, ref_sync_pin, From fed1b1ddab41a0e7a462ac690a0c8af6ff793624 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sat, 11 Jul 2026 11:05:37 -0400 Subject: [PATCH 2363/3297] net/sched: act_tunnel_key: Defer dst_release to RCU callback [ Upstream commit f1f5c8a3955f8fda3f84ed883ac8daa1847e724c ] Fix a race-condition use-after-free in tunnel_key_release_params(). The function releases the metadata_dst of the old params synchronously via dst_release() while deferring the params struct free with kfree_rcu(). A concurrent tunnel_key_act() reader on the datapath may still hold the old params pointer (under rcu_read_lock_bh) and proceed to call dst_clone(¶ms->tcft_enc_metadata->dst) after the writer's dst_release has already pushed the dst's rcuref to RCUREF_DEAD. zdi-disclosures@trendmicro.com produced a poc which i (and Victor) verified that KASAN reports: ================================================================== BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 BUG: KASAN: slab-use-after-free in atomic_sub_return_release include/linux/atomic/atomic-instrumented.h:326 BUG: KASAN: slab-use-after-free in __rcuref_put include/linux/rcuref.h:109 BUG: KASAN: slab-use-after-free in rcuref_put include/linux/rcuref.h:173 BUG: KASAN: slab-use-after-free in dst_release+0x5b/0x370 net/core/dst.c:168 Write of size 4 at addr ffff88806158de40 by task poc/9388 CPU: 0 UID: 0 PID: 9388 Comm: poc Tainted: G W 7.1.0-rc7 #7 PREEMPT(lazy) Tainted: [W]=WARN Hardware name: QEMU Ubuntu 25.10 PC v2 (i440FX + PIIX, + 10.1 machine, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:94 dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 print_report+0x139/0x4ad mm/kasan/report.c:482 kasan_report+0xe4/0x1d0 mm/kasan/report.c:595 check_region_inline mm/kasan/generic.c:186 kasan_check_range+0x125/0x200 mm/kasan/generic.c:200 instrument_atomic_read_write include/linux/instrumented.h:112 atomic_sub_return_release include/linux/atomic/atomic-instrumented.h:326 __rcuref_put include/linux/rcuref.h:109 rcuref_put include/linux/rcuref.h:173 dst_release+0x5b/0x370 net/core/dst.c:168 refdst_drop include/net/dst.h:272 skb_dst_drop include/net/dst.h:284 skb_release_head_state+0x293/0x400 net/core/skbuff.c:1163 skb_release_all net/core/skbuff.c:1187 [..] Allocated by task 9391: kasan_save_stack+0x30/0x50 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 __kasan_kmalloc+0x9a/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 __do_kmalloc_node mm/slub.c:5296 __kmalloc_noprof+0x2f1/0x830 mm/slub.c:5308 kmalloc_noprof include/linux/slab.h:954 kzalloc_noprof include/linux/slab.h:1188 offload_action_alloc+0x2f/0x130 net/core/flow_offload.c:35 tcf_action_offload_add_ex+0x1ba/0x880 net/sched/act_api.c:258 tcf_action_offload_add net/sched/act_api.c:293 tcf_action_init+0x66e/0xa20 net/sched/act_api.c:1547 tcf_action_add+0xf6/0x5d0 net/sched/act_api.c:2101 [..] Freed by task 9391: kasan_save_stack+0x30/0x50 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 __kasan_slab_free+0x6b/0x90 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 slab_free_hook mm/slub.c:2689 slab_free mm/slub.c:6251 kfree+0x21f/0x6b0 mm/slub.c:6566 tcf_action_offload_add_ex+0x4ad/0x880 net/sched/act_api.c:284 tcf_action_offload_add net/sched/act_api.c:293 tcf_action_init+0x66e/0xa20 net/sched/act_api.c:1547 tcf_action_add+0xf6/0x5d0 net/sched/act_api.c:2101 The buggy address belongs to the object at ffff88806158de00 which belongs to the cache kmalloc-256 of size 256 The buggy address is located 64 bytes inside of freed 256-byte region [ffff88806158de00, ffff88806158df00) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff88806158d600 pfn:0x6158c head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x4fff00000000240(workingset|head|node=1|zone=1|lastcpupid=0x7ff) page_type: f5(slab) raw: 04fff00000000240 ffff88801c841b40 ffffea0001856290 ffffea0001856190 raw: ffff88806158d600 0000000800100009 00000000f5000000 0000000000000000 head: 04fff00000000240 ffff88801c841b40 ffffea0001856290 ffffea0001856190 head: ffff88806158d600 0000000800100009 00000000f5000000 0000000000000000 head: 04fff00000000001 ffffffffffffff81 00000000ffffffff 00000000ffffffff head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000002 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 1, migratetype Unmovable, gfp_mask 0xd2820(GFP_ATOMIC|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 9391, tgid 9378 (poc), ts 123227323196, free_ts 0 set_page_owner include/linux/page_owner.h:32 post_alloc_hook+0xfe/0x140 mm/page_alloc.c:1853 prep_new_page mm/page_alloc.c:1861 get_page_from_freelist+0x110c/0x2fc0 mm/page_alloc.c:3941 __alloc_frozen_pages_noprof+0x263/0x2bc0 mm/page_alloc.c:5221 alloc_slab_page mm/slub.c:3278 allocate_slab mm/slub.c:3467 new_slab+0xa6/0x690 mm/slub.c:3525 refill_objects+0x271/0x420 mm/slub.c:7272 refill_sheaf mm/slub.c:2816 __pcs_replace_empty_main+0x373/0x630 mm/slub.c:4652 alloc_from_pcs mm/slub.c:4750 slab_alloc_node mm/slub.c:4884 __do_kmalloc_node mm/slub.c:5295 __kmalloc_noprof+0x66d/0x830 mm/slub.c:5308 kmalloc_noprof include/linux/slab.h:954 metadata_dst_alloc+0x26/0x90 net/core/dst.c:298 tun_rx_dst include/net/dst_metadata.h:144 __ip_tun_set_dst include/net/dst_metadata.h:208 tunnel_key_init+0xb01/0x1b90 net/sched/act_tunnel_key.c:451 tcf_action_init_1+0x46b/0x6c0 net/sched/act_api.c:1428 tcf_action_init+0x448/0xa20 net/sched/act_api.c:1503 tcf_action_add+0xf6/0x5d0 net/sched/act_api.c:2101 [..] ================================================================== Fix by moving dst_release() into a custom RCU callback that runs after the grace period, matching the lifetime of the containing params struct. Readers in the datapath therefore always find a live rcuref when calling dst_clone(). Fixes: 9174c3df1cd18 ("net/sched: act_tunnel_key: fix memory leak in case of action replace") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Reviewed-by: Davide Caratti Link: https://patch.msgid.link/20260711150537.7946-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/act_tunnel_key.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index 876b30c5709e1..b14807761d829 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -342,14 +342,20 @@ static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = { [TCA_TUNNEL_KEY_ENC_TTL] = { .type = NLA_U8 }, }; -static void tunnel_key_release_params(struct tcf_tunnel_key_params *p) +static void tunnel_key_release_params_rcu(struct rcu_head *head) { - if (!p) - return; + struct tcf_tunnel_key_params *p = container_of(head, typeof(*p), rcu); + if (p->tcft_action == TCA_TUNNEL_KEY_ACT_SET) dst_release(&p->tcft_enc_metadata->dst); + kfree(p); +} - kfree_rcu(p, rcu); +static void tunnel_key_release_params(struct tcf_tunnel_key_params *p) +{ + if (!p) + return; + call_rcu(&p->rcu, tunnel_key_release_params_rcu); } static int tunnel_key_init(struct net *net, struct nlattr *nla, From a8d20ba0ab518c9ccbcde258f25fc1ee6e51d5db Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Jul 2026 14:12:35 -0400 Subject: [PATCH 2364/3297] sctp: fix auth_hmacs array size in struct sctp_cookie [ Upstream commit e0b5252a59383b77d1b8dbeda00b7184dd95f4d3 ] The auth_hmacs array in struct sctp_cookie is supposed to store a complete SCTP_AUTH_HMAC_ALGO parameter, which consists of a struct sctp_paramhdr followed by N HMAC identifiers. However, the array size was calculated using an extra 2 bytes instead of sizeof(struct sctp_paramhdr), which is 4 bytes. When four HMAC identifiers are configured, the HMAC-ALGO parameter stored in the endpoint is larger than the auth_hmacs buffer in the cookie. As a result, sctp_association_init() copies beyond the end of auth_hmacs when initializing the association, corrupting the adjacent auth_chunks field. This can lead to an invalid HMAC identifier being accepted and later cause an out-of-bounds read in sctp_auth_get_hmac(). Fix the array size calculation by including the full SCTP parameter header size. Fixes: 1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals") Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zihan Xi Reported-by: Ren Wei Signed-off-by: Xin Long Link: https://patch.msgid.link/634a0de0d5de29532915e6d47c92a0cbc206e03f.1783707155.git.lucien.xin@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- include/net/sctp/structs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 2ae390219efdd..4bbba29f7341e 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -316,7 +316,8 @@ struct sctp_cookie { __u8 auth_random[sizeof(struct sctp_paramhdr) + SCTP_AUTH_RANDOM_LENGTH]; - __u8 auth_hmacs[SCTP_AUTH_NUM_HMACS * sizeof(__u16) + 2]; + __u8 auth_hmacs[sizeof(struct sctp_paramhdr) + + SCTP_AUTH_NUM_HMACS * sizeof(__u16)]; __u8 auth_chunks[sizeof(struct sctp_paramhdr) + SCTP_AUTH_MAX_CHUNKS]; /* This is a shim for my peer's INIT packet, followed by From 5f6e7b32bd1fbde10fd31a4143260735ea535b8a Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 11 Jul 2026 04:50:00 -0700 Subject: [PATCH 2365/3297] mpls: fix NULL deref in mpls_valid_fib_dump_req() on CONFIG_INET=n [ Upstream commit 56d96fededd61192cd7cc8d2b0f36adfd59036c3 ] On CONFIG_INET=n builds, mpls_valid_fib_dump_req() walks the parsed attribute table itself instead of calling ip_valid_fib_dump_req(). The RTA_OIF arm passes tb[RTA_OIF] to nla_get_u32() without checking it is present, so an RTM_GETROUTE dump for AF_MPLS with strict checking and no RTA_OIF hits a NULL dereference. RTM_GETROUTE is RTNL_KIND_GET, which rtnetlink_rcv_msg() permits without CAP_NET_ADMIN, so an unprivileged user can trigger it. Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:mpls_valid_fib_dump_req (net/mpls/af_mpls.c:2189) Call Trace: mpls_dump_routes (net/mpls/af_mpls.c:2236) netlink_dump (net/netlink/af_netlink.c:2331) __netlink_dump_start (net/netlink/af_netlink.c:2446) rtnetlink_rcv_msg (net/core/rtnetlink.c:7033) netlink_rcv_skb (net/netlink/af_netlink.c:2556) netlink_unicast (net/netlink/af_netlink.c:1345) netlink_sendmsg (net/netlink/af_netlink.c:1900) __sock_sendmsg (net/socket.c:790) ____sys_sendmsg (net/socket.c:2684) ___sys_sendmsg (net/socket.c:2738) __sys_sendmsg (net/socket.c:2770) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Skip unset attributes, as ip_valid_fib_dump_req() does. Fixes: 196cfebf8972 ("net/mpls: Handle kernel side filtering of route dumps") Assisted-by: Claude:claude-opus-4-8 Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: David Ahern Link: https://patch.msgid.link/20260711114958.1009619-3-bestswngs@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/mpls/af_mpls.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 1c70cb26e7ba1..f7f60025d042a 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -2123,6 +2123,9 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, int ifindex; if (i == RTA_OIF) { + if (!tb[i]) + continue; + ifindex = nla_get_u32(tb[i]); filter->dev = __dev_get_by_index(net, ifindex); if (!filter->dev) From 217774e143d7b5a88739193284b6421be3978601 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 15 Jul 2026 16:59:05 +0200 Subject: [PATCH 2366/3297] usb: core: sysfs: add lock to bos_descriptors_read() commit 4e0197fbb0eec588795d5431716a244d9ac8fa93 upstream. Add a lock to the function bos_descriptors_read(). This function accesses udev->bos, which could be simultaneously freed in usb_reset_and_verify_device(), a function that is commonly called in drivers all over the kernel. Assisted-by: gkh_clanker_t1000 Signed-off-by: Griffin Kroah-Hartman Acked-by: Alan Stern Cc: stable Link: https://patch.msgid.link/20260715-usb_core_patches_3-v1-1-53021f5576fd@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/sysfs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index a07866f1060cf..d22dc78457d79 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -899,10 +899,15 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj, { struct device *dev = kobj_to_dev(kobj); struct usb_device *udev = to_usb_device(dev); - struct usb_host_bos *bos = udev->bos; + struct usb_host_bos *bos; struct usb_bos_descriptor *desc; size_t desclen, n = 0; + int rc; + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; + bos = udev->bos; if (bos) { desc = bos->desc; desclen = le16_to_cpu(desc->wTotalLength); @@ -911,6 +916,7 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj, memcpy(buf, (void *) desc + off, n); } } + usb_unlock_device(udev); return n; } static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */ From fb1b50ab699211e777dca5ccfb648788b6a6e519 Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Wed, 15 Jul 2026 22:08:10 +0800 Subject: [PATCH 2367/3297] wifi: at76c50x-usb: avoid length underflow in at76_guess_freq() commit 61a799ffd1e5a4fd3702d547828b7ff3d161468e upstream. at76_guess_freq() checks only that the received frame is at least a bare 802.11 header (24 bytes) before subtracting the fixed management-body offset: len -= el_off; For both beacon and probe response frames, el_off is 36. If the frame is shorter than el_off, subtracting it causes the calculated IE length to wrap. The length is eventually passed to cfg80211_find_elem_match() as a very large unsigned value, so the element walk runs beyond the RX skb. This path is reached from at76_rx_tasklet() while scanning. If the device delivers a truncated beacon or probe response, the oversized IE length causes an out-of-bounds read during scanning. Skip the IE lookup if the frame does not reach the variable elements, before subtracting el_off. Fixes: 1264b951463a ("at76c50x-usb: add driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Huihui Huang Link: https://patch.msgid.link/20260715140815.1242033-1-hhhuang@smu.edu.sg Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/atmel/at76c50x-usb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index aa683eacaf38e..b983b51ffee71 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -1521,13 +1521,16 @@ static inline int at76_guess_freq(struct at76_priv *priv) if (ieee80211_is_probe_resp(hdr->frame_control)) { el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; } else if (ieee80211_is_beacon(hdr->frame_control)) { el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; } else { goto exit; } + + if (len < el_off) + goto exit; + + el = priv->rx_skb->data + el_off; len -= el_off; el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); From 7714fb896ed308cf13d32d317040adc4f200b8e4 Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Thu, 11 Jun 2026 15:12:01 +0800 Subject: [PATCH 2368/3297] usb: core: port: Deattach Type-C connector on component unbind commit e0b291fe117964037e0ba382eff4bb365d531c3a upstream. connector_unbind() is the mirror of connector_bind(), but it is missing the symmetric call to typec_deattach() that connector_bind() makes via: if (port_dev->child) typec_attach(port_dev->connector, &port_dev->child->dev); When a Thunderbolt dock is unplugged, two teardown paths race: 1. The component framework calls connector_unbind() first, which sets port_dev->connector = NULL without calling typec_deattach(). This leaves port->usb2_dev/port->usb3_dev in struct typec_port pointing at the USB device that is about to be freed. 2. usb_disconnect() then calls typec_deattach(port_dev->connector, ...), but port_dev->connector is already NULL, so the call is a no-op and port->usb2_dev is never cleared. 3. Concurrently, UCSI detects a PD partner-disconnect event and calls typec_unregister_partner(), which reads port->usb2_dev (now a dangling pointer to freed memory) and passes it to typec_partner_unlink_device() -> sysfs_remove_link() -> dev_name() on the freed device, corrupting the typec/UCSI partner state. This corruption leaves the Thunderbolt tunnel in an inconsistent state on the next dock hot-plug. On affected hardware the dock's I225/igc NIC fails to enumerate: AER fires a slot reset while the igc driver is still initialising ("PCIe link lost"), and the subsequent igc_reset attempt hits igc_rd32 on an already-detached device: igc 0000:2e:00.0 eth0: PCIe link lost, device now detached igc: Failed to read reg 0x0! WARNING: CPU: 9 PID: 129 at drivers/net/ethernet/intel/igc/igc_main.c:7005 igc_rd32+0xa4/0xc0 [igc] Call Trace: igc_disable_pcie_master+0x16/0xa0 [igc] igc_reset_hw_base+0x14/0x170 [igc] igc_reset+0x63/0x110 [igc] igc_io_slot_reset+0x9e/0xd0 [igc] report_slot_reset+0x5d/0xc0 pcie_do_recovery+0x209/0x400 aer_isr_one_error_type+0x235/0x430 aer_isr+0x4e/0x80 irq_thread+0xf4/0x1f0 4. UCSI later handles the PD partner-disconnect and calls typec_unregister_partner(), which still sees the stale port->usb2_dev and tries to remove its sysfs link a second time: kernfs: can not remove 'typec', no directory WARNING: CPU: 6 PID: 55 at fs/kernfs/dir.c:1706 kernfs_remove_by_name_ns+0xe9/0xf0 Workqueue: events ucsi_handle_connector_change [typec_ucsi] Call Trace: sysfs_remove_link+0x19/0x50 typec_unregister_partner+0x6e/0x120 [typec] ucsi_unregister_partner+0x107/0x150 [typec_ucsi] ucsi_handle_connector_change+0x3ec/0x490 [typec_ucsi] process_one_work+0x18e/0x3e0 worker_thread+0x2e3/0x420 kthread+0x10a/0x230 ret_from_fork+0x121/0x140 ret_from_fork_asm+0x1a/0x30 With worse timing the same stale pointer is dereferenced after the backing memory is freed, turning the warning into a use-after-free. Fix the asymmetry: call typec_deattach() before clearing port_dev->connector, matching what connector_bind() does on the bind side. typec_partner_deattach() is already protected by port->partner_link_lock, so it serialises safely with the concurrent typec_unregister_partner() path. Fixes: 11110783f5ea ("usb: Inform the USB Type-C class about enumerated devices") Cc: stable Signed-off-by: Chia-Lin Kao (AceLan) Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260611071201.1235545-1-acelan.kao@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/port.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index a47df5d32f7c4..7cae194843556 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -726,6 +726,8 @@ static void connector_unbind(struct device *dev, struct device *connector, void sysfs_remove_link(&connector->kobj, dev_name(dev)); sysfs_remove_link(&dev->kobj, "connector"); + if (port_dev->child) + typec_deattach(port_dev->connector, &port_dev->child->dev); port_dev->connector = NULL; } From 0950ac52426b0ab32d3b8cf4afe1711668b19cb8 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Jul 2026 19:47:11 +0800 Subject: [PATCH 2369/3297] usb: musb: omap2430: Do not put borrowed of_node in probe commit c947360ae63eee1c9eacc030dd6f5a53f717addf upstream. omap2430_probe() stores pdev->dev.of_node in a local np variable. This is a borrowed pointer and the probe function does not take a reference to it. The success and error paths nevertheless call of_node_put(np). This drops a reference that is owned by the platform device, and can leave pdev->dev.of_node with an unbalanced reference count. Do not put the borrowed platform device node from omap2430_probe(). References taken for the child MUSB device are handled by the device core, and the ctrl-module phandle reference is still released separately. Fixes: ffbe2feac59b ("usb: musb: omap2430: Fix probe regression for missing resources") Cc: stable Reviewed-by: Johan Hovold Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260713114711.955253-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index aadabc415145b..77133ee77063c 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -455,7 +455,6 @@ 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,7 +464,6 @@ 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 8eca14198c2020e4cb105f46126b48dd7c936b20 Mon Sep 17 00:00:00 2001 From: Huang Wei Date: Thu, 16 Jul 2026 11:33:41 +0800 Subject: [PATCH 2370/3297] USB: storage: add NO_ATA_1X quirk for Longmai USB Key commit 3b4ca2e01c1dd8c00b675b794732945f460a471b upstream. The Longmai Technologies USB Key (0x04b4:0xb708) advertises itself as a SCSI/Bulk-only mass storage device but does not correctly handle ATA pass-through commands. When such a command (ATA_12 or ATA_16) is sent to the device it fails to respond and the transfer eventually times out, leaving the device unusable. Add an unusual_devs entry for this device that sets the US_FL_NO_ATA_1X flag, so usb-storage short-circuits ATA pass-through commands and returns INVALID COMMAND OPERATION CODE (0x20 0x05 0x24 0x00) instead of forwarding them to the device. Information about the device in /sys/kernel/debug/usb/devices: T: Bus=02 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 12 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=06 Prot=50 MxPS=64 #Cfgs= 1 P: Vendor=04b4 ProdID=b708 Rev= 1.00 S: Manufacturer=Longmai Technologies S: Product=USB Key C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Reported-by: Ai Chao Cc: stable Signed-off-by: Huang Wei Acked-by: Alan Stern Link: https://patch.msgid.link/20260716033341.2830872-1-huangwei@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 255968f9ca42a..ac22fa3187343 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -395,6 +395,13 @@ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, USB_SC_DEVICE, USB_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), +/* Reported by Ai Chao */ +UNUSUAL_DEV( 0x04b4, 0xb708, 0x0000, 0xffff, + "Longmai Technologies", + "USB Key", + USB_SC_SCSI, USB_PR_BULK, NULL, + US_FL_NO_ATA_1X), + /* * Reported by Simon Levitt * This entry needs Sub and Proto fields From ace1a0adfc7866410263b624c69545033647467f Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Thu, 16 Jul 2026 18:41:26 +0800 Subject: [PATCH 2371/3297] usb: chipidea: fix usage_count leak when autosuspend_delay is negative commit fc3afb5728e297994863f8a2a01b88a920bbf53e upstream. The probe() calls pm_runtime_use_autosuspend(), but remove() does not call pm_runtime_dont_use_autosuspend(). This can lead to a usage_count leak if autosuspend_delay is set to a negative value. The pm_runtime_use_autosuspend() also notes that it's important to undo this with pm_runtime_dont_use_autosuspend() at driver exit time. Fixes: 1f874edcb731 ("usb: chipidea: add runtime power management support") Cc: stable Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Xu Yang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260716104126.2763454-1-xu.yang_2@oss.nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 4d3a89b9d3860..628b83099764a 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1265,6 +1265,7 @@ static void ci_hdrc_remove(struct platform_device *pdev) usb_role_switch_unregister(ci->role_switch); if (ci->supports_runtime_pm) { + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); From e239ea91b48180ed48a86ac25643832a02c88456 Mon Sep 17 00:00:00 2001 From: Jinchao Wang Date: Thu, 16 Jul 2026 06:42:17 -0400 Subject: [PATCH 2372/3297] usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback commit d5e5cd3654d2b5359a12ea6586120f05b28634ee upstream. dummy_hcd embeds a single shared usb_request (dum->fifo_req) that the "emulated single-request FIFO" fast-path in dummy_queue() reuses for small IN transfers: it copies the caller's request into it (req->req = *_req) and queues it, treating list_empty(&fifo_req.queue) as "the slot is free". The completion side (dummy_timer/transfer/nuke/dummy_dequeue) follows the standard pattern: list_del_init(&req->queue) unlinks the request, then the lock is dropped and usb_gadget_giveback_request() invokes req->complete(). But list_del_init() makes fifo_req.queue look empty *before* the completion callback returns, so a concurrent dummy_queue() on another CPU sees the slot as free, reuses fifo_req and runs req->req = *_req -- overwriting req->complete while dummy_timer is mid-calling it. The indirect call then jumps to a clobbered pointer, causing a general protection fault / page fault in dummy_timer (syzkaller extid faf3a6cf579fc65591ca). The clobbering write is an in-bounds memcpy on a live shared object, so KASAN cannot flag it. Add a fifo_req_busy bit covering the shared request's whole lifetime: set it in dummy_queue() when the FIFO fast-path takes fifo_req (making it the fast-path guard, replacing the list_empty(&fifo_req.queue) test), and clear it after the completion callback has returned, via a dummy_giveback() helper used at all four gadget-request giveback sites. The shared slot can no longer be reused until its completion callback has finished. Reported-by: syzbot+faf3a6cf579fc65591ca@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=faf3a6cf579fc65591ca Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Signed-off-by: Jinchao Wang Reviewed-by: Alan Stern Link: https://patch.msgid.link/5db8bba5b3499a86cd2e776f9918126b68b2508b.1784198306.git.wangjinchao600@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 40 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 4a78536644ba1..5e3cbe0338bff 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -278,6 +278,7 @@ struct dummy { unsigned ints_enabled:1; unsigned udc_suspended:1; unsigned pullup:1; + unsigned fifo_req_busy:1; /* * HOST side support @@ -329,6 +330,26 @@ static inline struct dummy *gadget_dev_to_dummy(struct device *dev) /* DEVICE/GADGET SIDE UTILITY ROUTINES */ +/* + * Give back a gadget request with dum->lock dropped around the callback. + * If @req is the shared fifo_req, clear fifo_req_busy afterward: the flag + * was set in dummy_queue() when the shared request was taken and must stay + * set until its completion callback has returned; list_del_init() alone + * makes the request look idle while the callback is still running. + * Caller holds dum->lock and has already done list_del_init() + status. + */ +static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep, + struct dummy_request *req) +{ + bool fifo = req == &dum->fifo_req; + + spin_unlock(&dum->lock); + usb_gadget_giveback_request(_ep, &req->req); + spin_lock(&dum->lock); + if (fifo) + dum->fifo_req_busy = 0; +} + /* called with spinlock held */ static void nuke(struct dummy *dum, struct dummy_ep *ep) { @@ -339,9 +360,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep) list_del_init(&req->queue); req->req.status = -ESHUTDOWN; - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); } } @@ -728,10 +747,11 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, /* implement an emulated single-request FIFO */ if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && - list_empty(&dum->fifo_req.queue) && + !dum->fifo_req_busy && list_empty(&ep->queue) && _req->length <= FIFO_SIZE) { req = &dum->fifo_req; + dum->fifo_req_busy = 1; req->req = *_req; req->req.buf = dum->fifo_buf; memcpy(dum->fifo_buf, _req->buf, _req->length); @@ -785,9 +805,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req) dev_dbg(udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(_ep, _req); - spin_lock(&dum->lock); + dummy_giveback(dum, _ep, req); } spin_unlock_irqrestore(&dum->lock, flags); return retval; @@ -1523,9 +1541,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb, if (req->req.status != -EINPROGRESS) { list_del_init(&req->queue); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); /* requests might have been unlinked... */ rescan = 1; @@ -1910,9 +1926,7 @@ static enum hrtimer_restart dummy_timer(struct hrtimer *t) dev_dbg(udc_dev(dum), "stale req = %p\n", req); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); ep->already_seen = 0; goto restart; } From f45089eaad0a083d71d84ff175741d7e157d9b69 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 15:07:17 +0000 Subject: [PATCH 2373/3297] usb: gadget: f_midi: cancel pending IN work before freeing the midi object commit 5650c18d93a1db7e27cb5a40b394747eb4686d5b upstream. The f_midi driver embeds a work item (midi->work) whose handler, f_midi_in_work(), dereferences the enclosing struct f_midi through container_of(). This work is armed from two sites: f_midi_complete(), on a normal IN-endpoint completion, and f_midi_in_trigger(), on an ALSA rawmidi output-stream start. Neither f_midi_disable() nor f_midi_unbind() cancels midi->work. f_midi_disable() only disables the endpoints and drains the in_req_fifo; it does not synchronize the work item, and the sound card is released asynchronously to the final free of the midi object. The midi object is reference-counted (midi->free_ref) and is freed in f_midi_free() only once both the usb_function reference and the rawmidi private_data reference have been dropped. In f_midi_unbind(), f_midi_disable() runs before the sound card is released, so while the USB endpoints are already disabled the rawmidi device is still usable by an open substream. A concurrent userspace write on such a substream can reach f_midi_in_trigger() and queue midi->work again after f_midi_disable() has returned. A work item armed this way may still be pending when the last reference drops and f_midi_free() proceeds to kfree(midi), letting f_midi_in_work() dereference the struct after it has been freed, a use-after-free. For this reason cancelling midi->work in f_midi_disable() would not be sufficient: the ALSA trigger path can rearm the work after disable() returns. Cancelling at the refcount-zero free site is the boundary after which neither arming source can survive, because by then both references that keep the midi object alive have been dropped: the USB endpoints are already disabled and the rawmidi device has been released. Fix this by calling cancel_work_sync(&midi->work) in the refcount-zero block of f_midi_free(), before the embedded work_struct is freed along with the rest of the structure. opts->lock is a sleeping mutex, so calling cancel_work_sync() under it is permitted, and the handler takes midi->transmit_lock rather than opts->lock, so no self-deadlock can occur while it waits for a running instance of the work to finish. This issue was found by an in-house static analysis tool. Fixes: 8653d71ce3763 ("usb/gadget: f_midi: Replace tasklet with work") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260709150717.399083-1-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_midi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index da82598fcef8a..28eca6698adaa 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1302,6 +1302,7 @@ static void f_midi_free(struct usb_function *f) opts = container_of(f->fi, struct f_midi_opts, func_inst); mutex_lock(&opts->lock); if (!--midi->free_ref) { + cancel_work_sync(&midi->work); kfree(midi->id); kfifo_free(&midi->in_req_fifo); kfree(midi); From 4cde0b38cc0cb8b7dc17295801015148de37d1d2 Mon Sep 17 00:00:00 2001 From: Melbin K Mathew Date: Thu, 9 Jul 2026 21:56:22 +0100 Subject: [PATCH 2374/3297] usb: gadget: printer: fix infinite loop in printer_read() commit c2e819be6a5c7f34344926b4bd7e3dfca58cf48a upstream. printer_read() uses the same variable for the requested copy size and the number of bytes actually copied to user space. copy_to_user() returns the number of bytes not copied, so when it fails to copy anything, the computed copied length becomes zero. In that case len, buf, current_rx_bytes and current_rx_buf are left unchanged. If RX data is available and the user buffer remains unwritable, the read loop can repeat indefinitely. Track the copied length separately and return -EFAULT, or the number of bytes already copied, if an iteration makes no progress. Fixes: b185f01a9ab7 ("usb: gadget: printer: factor out f_printer") Cc: stable Reviewed-by: Peter Chen Signed-off-by: Melbin K Mathew Link: https://patch.msgid.link/20260709205622.55700-1-mlbnkm1@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_printer.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index a3884afa42b4b..3db07d0fba89c 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -431,7 +431,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) { struct printer_dev *dev = fd->private_data; unsigned long flags; - size_t size; + size_t size, not_copied, copied; size_t bytes_copied; struct usb_request *req; /* This is a pointer to the current USB rx request. */ @@ -524,10 +524,12 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) else size = len; - size -= copy_to_user(buf, current_rx_buf, size); - bytes_copied += size; - len -= size; - buf += size; + not_copied = copy_to_user(buf, current_rx_buf, size); + copied = size - not_copied; + + bytes_copied += copied; + len -= copied; + buf += copied; spin_lock_irqsave(&dev->lock, flags); @@ -542,6 +544,17 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) if (dev->interface < 0) goto out_disabled; + if (!copied) { + dev->current_rx_req = current_rx_req; + dev->current_rx_bytes = current_rx_bytes; + dev->current_rx_buf = current_rx_buf; + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return bytes_copied ? bytes_copied : -EFAULT; + } + + size = copied; + /* If we not returning all the data left in this RX request * buffer then adjust the amount of data left in the buffer. * Othewise if we are done with this RX request buffer then From e5ecfb776752616fe93809d5db619cd3b239e721 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Jul 2026 16:15:34 +0200 Subject: [PATCH 2375/3297] USB: gadget: snps-udc: fix device name leak on probe failure commit 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b upstream. The gadget device name is set by UDC core when registering the gadget and must not be set before to avoid leaking the name in intermediate error paths (e.g. when detecting an older chip revision). Fixes: 12ad0fcaf2fb ("usb: gadget: amd5536udc: let udc-core manage gadget->dev") Cc: stable Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260702141536.90887-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/snps_udc_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 373942ceb0769..2f790cc65257b 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -3133,7 +3133,6 @@ int udc_probe(struct udc *dev) /* device struct setup */ dev->gadget.ops = &udc_ops; - dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.name = name; dev->gadget.max_speed = USB_SPEED_HIGH; From 66956a5a4258c5032d7b88743baa0033114a2820 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Jul 2026 16:15:33 +0200 Subject: [PATCH 2376/3297] USB: gadget: fsl-udc: fix device name leak on probe failure commit 6b874d00c466e73c6448a89856407fe46b2f50e4 upstream. The gadget device name is set by UDC core when registering the gadget and must not be set before to avoid leaking the name in intermediate error paths (e.g. on dma pool creation failure). Fixes: eab35c4e6d95 ("usb: gadget: fsl_udc_core: let udc-core manage gadget->dev") Cc: stable Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260702141536.90887-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 4dea8bc30cf6d..6964af493c243 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2474,7 +2474,6 @@ static int fsl_udc_probe(struct platform_device *pdev) udc_controller->gadget.name = driver_name; /* Setup gadget.dev and register with kernel */ - dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.of_node = pdev->dev.of_node; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) From 12b3edca90d4dea345e2ae5382b364a3f3c5f587 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Jul 2026 16:15:35 +0200 Subject: [PATCH 2377/3297] USB: gadget: fsl-udc: fix dev_printk() device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c4dd150fceab281496acb3a643ae712aacb74864 upstream. A change replacing custom printk() macros with dev_printk() incorrectly used the gadget struct device instead of the controller struct device (including for messages printed before the gadget device name has been initialised). Switch to using the controller platform device with dev_printk() so that the controller device and driver names are included in log messages as expected. Fixes: 6025f20f16c2 ("usb: gadget: fsl-udc: Replace custom log wrappers by dev_{err,warn,dbg,vdbg}") Cc: stable Cc: Uwe Kleine-König Signed-off-by: Johan Hovold Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260702141536.90887-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 92 ++++++++++++--------------- drivers/usb/gadget/udc/fsl_usb2_udc.h | 1 + 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 6964af493c243..ed311f0d74b5e 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -184,7 +184,7 @@ __acquires(ep->udc->lock) usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); if (status && (status != -ESHUTDOWN)) - dev_vdbg(&udc->gadget.dev, "complete %s req %p stat %d len %u/%u\n", + dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n", ep->ep.name, &req->req, status, req->req.actual, req->req.length); @@ -286,7 +286,7 @@ static int dr_controller_setup(struct fsl_udc *udc) timeout = jiffies + FSL_UDC_RESET_TIMEOUT; while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) { if (time_after(jiffies, timeout)) { - dev_err(&udc->gadget.dev, "udc reset timeout!\n"); + dev_err(udc->dev, "udc reset timeout!\n"); return -ETIMEDOUT; } cpu_relax(); @@ -309,7 +309,7 @@ static int dr_controller_setup(struct fsl_udc *udc) tmp &= USB_EP_LIST_ADDRESS_MASK; fsl_writel(tmp, &dr_regs->endpointlistaddr); - dev_vdbg(&udc->gadget.dev, + dev_vdbg(udc->dev, "vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x\n", udc->ep_qh, (int)tmp, fsl_readl(&dr_regs->endpointlistaddr)); @@ -500,7 +500,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; break; default: - dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type); + dev_vdbg(udc->dev, "error ep type is %d\n", ep_type); return; } if (zlt) @@ -613,7 +613,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, spin_unlock_irqrestore(&udc->lock, flags); retval = 0; - dev_vdbg(&udc->gadget.dev, "enabled %s (ep%d%s) maxpacket %d\n", + dev_vdbg(udc->dev, "enabled %s (ep%d%s) maxpacket %d\n", ep->ep.name, ep->ep.desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", max); @@ -634,13 +634,8 @@ static int fsl_ep_disable(struct usb_ep *_ep) int ep_num; ep = container_of(_ep, struct fsl_ep, ep); - if (!_ep || !ep->ep.desc) { - /* - * dev_vdbg(&udc->gadget.dev, "%s not enabled\n", - * _ep ? ep->ep.name : NULL); - */ + if (!_ep || !ep->ep.desc) return -EINVAL; - } /* disable ep on controller */ ep_num = ep_index(ep); @@ -664,7 +659,7 @@ static int fsl_ep_disable(struct usb_ep *_ep) ep->stopped = 1; spin_unlock_irqrestore(&udc->lock, flags); - dev_vdbg(&udc->gadget.dev, "disabled %s OK\n", _ep->name); + dev_vdbg(udc->dev, "disabled %s OK\n", _ep->name); return 0; } @@ -724,9 +719,6 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) { u32 temp, bitmask, tmp_stat; - /* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr); - dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */ - bitmask = ep_is_in(ep) ? (1 << (ep_index(ep) + 16)) : (1 << (ep_index(ep))); @@ -813,7 +805,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, *is_last = 0; if ((*is_last) == 0) - dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n"); + dev_vdbg(udc_controller->dev, "multi-dtd request!\n"); /* Fill in the transfer size; set active bit */ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); @@ -825,7 +817,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, mb(); - dev_vdbg(&udc_controller->gadget.dev, "length = %d address= 0x%x\n", *length, (int)*dma); + dev_vdbg(udc_controller->dev, "length = %d address= 0x%x\n", *length, (int)*dma); return dtd; } @@ -876,11 +868,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* catch various bogus parameters */ if (!_req || !req->req.complete || !req->req.buf || !list_empty(&req->queue)) { - dev_vdbg(&udc->gadget.dev, "%s, bad params\n", __func__); + dev_vdbg(udc->dev, "%s, bad params\n", __func__); return -EINVAL; } if (unlikely(!ep->ep.desc)) { - dev_vdbg(&udc->gadget.dev, "%s, bad ep\n", __func__); + dev_vdbg(udc->dev, "%s, bad ep\n", __func__); return -EINVAL; } if (usb_endpoint_xfer_isoc(ep->ep.desc)) { @@ -1040,7 +1032,7 @@ static int fsl_ep_set_halt(struct usb_ep *_ep, int value) udc->ep0_dir = 0; } out: - dev_vdbg(&udc->gadget.dev, "%s %s halt stat %d\n", ep->ep.name, + dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name, value ? "set" : "clear", status); return status; @@ -1109,7 +1101,7 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep) /* Wait until flush complete */ while (fsl_readl(&dr_regs->endptflush)) { if (time_after(jiffies, timeout)) { - dev_err(&udc_controller->gadget.dev, + dev_err(udc_controller->dev, "ep flush timeout\n"); return; } @@ -1182,7 +1174,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active) udc = container_of(gadget, struct fsl_udc, gadget); spin_lock_irqsave(&udc->lock, flags); - dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active)); + dev_vdbg(udc->dev, "VBUS %s\n", str_on_off(is_active)); udc->vbus_active = (is_active != 0); if (can_pullup(udc)) fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), @@ -1548,7 +1540,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, udc->ep0_state = WAIT_FOR_SETUP; break; case WAIT_FOR_SETUP: - dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n"); + dev_err(udc->dev, "Unexpected ep0 packets\n"); break; default: ep0stall(udc); @@ -1617,7 +1609,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, errors = hc32_to_cpu(curr_td->size_ioc_sts); if (errors & DTD_ERROR_MASK) { if (errors & DTD_STATUS_HALTED) { - dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe); + dev_err(udc->dev, "dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */ tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts); tmp &= ~errors; @@ -1628,26 +1620,26 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, break; } if (errors & DTD_STATUS_DATA_BUFF_ERR) { - dev_vdbg(&udc->gadget.dev, "Transfer overflow\n"); + dev_vdbg(udc->dev, "Transfer overflow\n"); status = -EPROTO; break; } else if (errors & DTD_STATUS_TRANSACTION_ERR) { - dev_vdbg(&udc->gadget.dev, "ISO error\n"); + dev_vdbg(udc->dev, "ISO error\n"); status = -EILSEQ; break; } else - dev_err(&udc->gadget.dev, + dev_err(udc->dev, "Unknown error has occurred (0x%x)!\n", errors); } else if (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_STATUS_ACTIVE) { - dev_vdbg(&udc->gadget.dev, "Request not complete\n"); + dev_vdbg(udc->dev, "Request not complete\n"); status = REQ_UNCOMPLETE; return status; } else if (remaining_length) { if (direction) { - dev_vdbg(&udc->gadget.dev, + dev_vdbg(udc->dev, "Transmit dTD remaining length not zero\n"); status = -EPROTO; break; @@ -1655,8 +1647,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, break; } } else { - dev_vdbg(&udc->gadget.dev, - "dTD transmitted successful\n"); + dev_vdbg(udc->dev, "dTD transmitted successful\n"); } if (j != curr_req->dtd_count - 1) @@ -1699,7 +1690,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) /* If the ep is configured */ if (!curr_ep->ep.name) { - dev_warn(&udc->gadget.dev, "Invalid EP?\n"); + dev_warn(udc->dev, "Invalid EP?\n"); continue; } @@ -1708,7 +1699,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) queue) { status = process_ep_req(udc, i, curr_req); - dev_vdbg(&udc->gadget.dev, + dev_vdbg(udc->dev, "status of process_ep_req= %d, ep = %d\n", status, ep_num); if (status == REQ_UNCOMPLETE) @@ -1829,7 +1820,7 @@ static void reset_irq(struct fsl_udc *udc) while (fsl_readl(&dr_regs->endpointprime)) { /* Wait until all endptprime bits cleared */ if (time_after(jiffies, timeout)) { - dev_err(&udc->gadget.dev, "Timeout for reset\n"); + dev_err(udc->dev, "Timeout for reset\n"); break; } cpu_relax(); @@ -1839,7 +1830,7 @@ static void reset_irq(struct fsl_udc *udc) fsl_writel(0xffffffff, &dr_regs->endptflush); if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { - dev_vdbg(&udc->gadget.dev, "Bus reset\n"); + dev_vdbg(udc->dev, "Bus reset\n"); /* Bus is reseting */ udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue @@ -1847,7 +1838,7 @@ static void reset_irq(struct fsl_udc *udc) reset_queues(udc, true); udc->usb_state = USB_STATE_DEFAULT; } else { - dev_vdbg(&udc->gadget.dev, "Controller reset\n"); + dev_vdbg(udc->dev, "Controller reset\n"); /* initialize usb hw reg except for regs for EP, not * touch usbintr reg */ dr_controller_setup(udc); @@ -1881,7 +1872,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Clear notification bits */ fsl_writel(irq_src, &dr_regs->usbsts); - /* dev_vdbg(&udc->gadget.dev, "irq_src [0x%8x]", irq_src); */ + /* dev_vdbg(udc->dev, "irq_src [0x%8x]", irq_src); */ /* Need to resume? */ if (udc->usb_state == USB_STATE_SUSPENDED) @@ -1890,7 +1881,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* USB Interrupt */ if (irq_src & USB_STS_INT) { - dev_vdbg(&udc->gadget.dev, "Packet int\n"); + dev_vdbg(udc->dev, "Packet int\n"); /* Setup package, we only support ep0 as control ep */ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) { tripwire_handler(udc, 0, @@ -1919,7 +1910,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Reset Received */ if (irq_src & USB_STS_RESET) { - dev_vdbg(&udc->gadget.dev, "reset int\n"); + dev_vdbg(udc->dev, "reset int\n"); reset_irq(udc); status = IRQ_HANDLED; } @@ -1931,7 +1922,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) } if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { - dev_vdbg(&udc->gadget.dev, "Error IRQ %x\n", irq_src); + dev_vdbg(udc->dev, "Error IRQ %x\n", irq_src); } spin_unlock_irqrestore(&udc->lock, flags); @@ -1967,7 +1958,7 @@ static int fsl_udc_start(struct usb_gadget *g, udc_controller->transceiver->otg, &udc_controller->gadget); if (retval < 0) { - dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n"); + dev_err(udc_controller->dev, "can't bind to transceiver\n"); udc_controller->driver = NULL; return retval; } @@ -2252,7 +2243,7 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL); if (!udc->eps) { - dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n"); + dev_err(udc->dev, "kmalloc udc endpoint status failed\n"); goto eps_alloc_failed; } @@ -2267,7 +2258,7 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->ep_qh = dma_alloc_coherent(&pdev->dev, size, &udc->ep_qh_dma, GFP_KERNEL); if (!udc->ep_qh) { - dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n"); + dev_err(udc->dev, "malloc QHs for udc failed\n"); goto ep_queue_alloc_failed; } @@ -2278,14 +2269,14 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL), struct fsl_req, req); if (!udc->status_req) { - dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n"); + dev_err(udc->dev, "kzalloc for udc status request failed\n"); goto udc_status_alloc_failed; } /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); if (!udc->status_req->req.buf) { - dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n"); + dev_err(udc->dev, "kzalloc for udc request buffer failed\n"); goto udc_req_buf_alloc_failed; } @@ -2373,6 +2364,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (udc_controller == NULL) return -ENOMEM; + udc_controller->dev = &pdev->dev; pdata = dev_get_platdata(&pdev->dev); udc_controller->pdata = pdata; spin_lock_init(&udc_controller->lock); @@ -2382,7 +2374,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_OTG) { udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(udc_controller->transceiver)) { - dev_err(&udc_controller->gadget.dev, "Can't find OTG driver!\n"); + dev_err(&pdev->dev, "Can't find OTG driver!\n"); ret = -ENODEV; goto err_kfree; } @@ -2398,7 +2390,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_DEVICE) { if (!request_mem_region(res->start, resource_size(res), driver_name)) { - dev_err(&udc_controller->gadget.dev, "request mem region for %s failed\n", pdev->name); + dev_err(&pdev->dev, "failed to request mem region\n"); ret = -EBUSY; goto err_kfree; } @@ -2429,7 +2421,7 @@ static int fsl_udc_probe(struct platform_device *pdev) /* Read Device Controller Capability Parameters register */ dccparams = fsl_readl(&dr_regs->dccparams); if (!(dccparams & DCCPARAMS_DC)) { - dev_err(&udc_controller->gadget.dev, "This SOC doesn't support device role\n"); + dev_err(&pdev->dev, "This SOC doesn't support device role\n"); ret = -ENODEV; goto err_exit; } @@ -2447,14 +2439,14 @@ static int fsl_udc_probe(struct platform_device *pdev) ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED, driver_name, udc_controller); if (ret != 0) { - dev_err(&udc_controller->gadget.dev, "cannot request irq %d err %d\n", + dev_err(&pdev->dev, "cannot request irq %d err %d\n", udc_controller->irq, ret); goto err_exit; } /* Initialize the udc structure including QH member and other member */ if (struct_udc_setup(udc_controller, pdev)) { - dev_err(&udc_controller->gadget.dev, "Can't initialize udc data structure\n"); + dev_err(&pdev->dev, "Can't initialize udc data structure\n"); ret = -ENOMEM; goto err_free_irq; } diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h index cc1756f3e89d1..53922bc58ca01 100644 --- a/drivers/usb/gadget/udc/fsl_usb2_udc.h +++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h @@ -470,6 +470,7 @@ struct fsl_ep { #define EP_DIR_OUT 0 struct fsl_udc { + struct device *dev; struct usb_gadget gadget; struct usb_gadget_driver *driver; struct fsl_usb2_platform_data *pdata; From 40c706a0224bde194667e3378c689b542fec4b44 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Fri, 3 Jul 2026 08:37:24 +0000 Subject: [PATCH 2378/3297] usb: gadget: f_ncm: validate datagram bounds in ncm_unwrap_ntb() commit 1febec7e47cdcd01f43fb0211094e3010474666e upstream. When unpacking host-supplied NTBs, ncm_unwrap_ntb() checks datagram length against frame_max but does not verify that the datagram fits within the declared block length. Additionally, when decoding multiple NTBs from a single socket buffer, subsequent block lengths are not checked against the actual remaining buffer data. With these checks missing, a malicious USB host can specify datagram offsets and lengths that point beyond the block, or supply secondary NTB headers declaring lengths larger than the buffer. skb_put_data() then copies adjacent kernel memory from skb_shared_info into the network skb. Fix this by verifying that sufficient buffer space remains for the NTB header before parsing, handling zero-length block declarations, ensuring that block lengths never exceed the remaining buffer space, and verifying that each datagram payload stays strictly within the block boundary. Fixes: 427694cfaafa ("usb: gadget: ncm: Handle decoding of multiple NTB's in unwrap call") Fixes: 2b74b0a04d3e ("USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()") Cc: stable Assisted-by: Jetski:Gemini-2.5-Pro Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260703083725.1903850-1-sonalipradhan@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ncm.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 04be7047aa3d2..fb57d57781ddb 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1189,6 +1189,10 @@ static int ncm_unwrap_ntb(struct gether *port, frame_max = ncm_opts->max_segment_size; parse_ntb: + if (to_process < (int)opts->nth_size) { + INFO(port->func.config->cdev, "Packet too small for headers\n"); + goto err; + } tmp = (__le16 *)ntb_ptr; /* dwSignature */ @@ -1209,8 +1213,12 @@ static int ncm_unwrap_ntb(struct gether *port, tmp++; /* skip wSequence */ block_len = get_ncm(&tmp, opts->block_length); + if (block_len == 0) + block_len = to_process; + /* (d)wBlockLength */ - if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) { + if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max) || + (block_len > to_process)) { INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len); goto err; } @@ -1273,7 +1281,7 @@ static int ncm_unwrap_ntb(struct gether *port, index = index2; /* wDatagramIndex[0] */ if ((index < opts->nth_size) || - (index > block_len - opts->dpe_size)) { + (index > block_len)) { INFO(port->func.config->cdev, "Bad index: %#X\n", index); goto err; @@ -1285,7 +1293,8 @@ static int ncm_unwrap_ntb(struct gether *port, * ethernet hdr + crc or larger than max frame size */ if ((dg_len < 14 + crc_len) || - (dg_len > frame_max)) { + (dg_len > frame_max) || + (dg_len > block_len - index)) { INFO(port->func.config->cdev, "Bad dgram length: %#X\n", dg_len); goto err; @@ -1310,7 +1319,7 @@ static int ncm_unwrap_ntb(struct gether *port, dg_len2 = get_ncm(&tmp, opts->dgram_item_len); /* wDatagramIndex[1] */ - if (index2 > block_len - opts->dpe_size) { + if (index2 > block_len) { INFO(port->func.config->cdev, "Bad index: %#X\n", index2); goto err; From dcf3e2f164435b5844706cb8eefef29ebee0eedb Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 02:09:04 +0000 Subject: [PATCH 2379/3297] usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown commit 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb upstream. The Broadcom BDC UDC driver registers its IRQ handler with devm_request_irq() in bdc_udc_init(), so the IRQ is released by devm only after bdc_remove() returns. devm releases resources in reverse LIFO order, but bdc_remove() runs bdc_udc_exit() and bdc_hw_exit() -> bdc_mem_free() manually before returning: bdc_udc_exit() tears down individual endpoint objects via bdc_free_ep(), while bdc_hw_exit() -> bdc_mem_free() frees and NULLs the DMA-coherent status-report ring (bdc->srr.sr_bds) and kfree()s bdc->bdc_ep_array. Both happen while the IRQ handler (bdc_udc_interrupt, requested with IRQF_SHARED) remains deliverable in the window up to the post-remove devm free_irq(). On receipt of a shared interrupt in that window, bdc_udc_interrupt() dereferences bdc->srr.sr_bds[bdc->srr.dqp_index] (NULL or freed DMA) and dispatches sr_handler callbacks that index into bdc_ep_array, causing a NULL-deref or use-after-free. The same window affects the delayed_work bdc->func_wake_notify, which is armed from the IRQ handler via bdc_sr_uspc() -> handle_link_state_change() -> schedule_delayed_work() and may self-rearm from its own callback bdc_func_wake_timer(). No cancel exists anywhere in the driver, so a queued work item that fires after bdc_remove() returns and the bdc structure is devm-freed dereferences freed memory. Replace devm_request_irq() with request_irq() and add an explicit free_irq(bdc->irq, bdc) in bdc_remove(). Clear BDC_GIE before free_irq() to stop the device from asserting interrupts, then free_irq() drains any in-flight handler, then cancel_delayed_work_sync() drains the func_wake_notify delayed work. This ordering ensures the IRQ handler and delayed work cannot interfere with the subsequent endpoint and DMA teardown in bdc_udc_exit() and bdc_hw_exit(). Wire the matching free_irq() into the bdc_udc_init() error path so the IRQ is released on probe failure, and route the bdc_init_ep() failure through err0 instead of returning directly. This issue was found by an in-house static analysis tool. Fixes: efed421a94e6 ("usb: gadget: Add UDC driver for Broadcom USB3.0 device controller IP BDC") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260709020904.502611-1-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bdc/bdc_core.c | 20 ++++++++++++++++++++ drivers/usb/gadget/udc/bdc/bdc_udc.c | 7 ++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index f47aac078f6be..de44cda273fb2 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -586,9 +586,29 @@ static int bdc_probe(struct platform_device *pdev) static void bdc_remove(struct platform_device *pdev) { struct bdc *bdc; + unsigned long flags; + u32 temp; bdc = platform_get_drvdata(pdev); dev_dbg(bdc->dev, "%s ()\n", __func__); + /* + * Disable the device interrupt source before freeing the IRQ: + * clear BDC_GIE so the controller stops asserting interrupts, + * then free_irq drains any in-flight handler. + */ + spin_lock_irqsave(&bdc->lock, flags); + temp = bdc_readl(bdc->regs, BDC_BDCSC); + temp &= ~BDC_GIE; + bdc_writel(bdc->regs, BDC_BDCSC, temp); + spin_unlock_irqrestore(&bdc->lock, flags); + free_irq(bdc->irq, bdc); + /* + * Drain func_wake_notify after free_irq: the IRQ handler arms this + * delayed_work via bdc_sr_uspc -> handle_link_state_change -> + * schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so + * the IRQ must be released first to prevent re-arm after cancel. + */ + cancel_delayed_work_sync(&bdc->func_wake_notify); bdc_udc_exit(bdc); bdc_hw_exit(bdc); bdc_phy_exit(bdc); diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 23826fd7a8e69..7a12219edac6f 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -530,8 +530,8 @@ int bdc_udc_init(struct bdc *bdc) bdc->gadget.name = BRCM_BDC_NAME; - ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt, - IRQF_SHARED, BRCM_BDC_NAME, bdc); + ret = request_irq(bdc->irq, bdc_udc_interrupt, IRQF_SHARED, + BRCM_BDC_NAME, bdc); if (ret) { dev_err(bdc->dev, "failed to request irq #%d %d\n", @@ -542,7 +542,7 @@ int bdc_udc_init(struct bdc *bdc) ret = bdc_init_ep(bdc); if (ret) { dev_err(bdc->dev, "bdc init ep fail: %d\n", ret); - return ret; + goto err0; } ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget); @@ -571,6 +571,7 @@ int bdc_udc_init(struct bdc *bdc) err1: usb_del_gadget_udc(&bdc->gadget); err0: + free_irq(bdc->irq, bdc); bdc_free_ep(bdc); return ret; From 1f03658f3e9b2f8fd1d1003ba389a0390b49a350 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Tue, 30 Jun 2026 00:50:04 +0500 Subject: [PATCH 2380/3297] usb: gadget: uvc: clamp SEND_RESPONSE length to the response buffer commit b70dc75e85ba968b7b76eebfe5d63000080b875b upstream. uvc_send_response() builds the UVC control response from a user-supplied struct uvc_request_data: req->length = min_t(unsigned int, uvc->event_length, data->length); ... memcpy(req->buf, data->data, req->length); req->length is clamped to uvc->event_length, which is taken from the host control request wLength (up to UVC_MAX_REQUEST_SIZE, 64), and to data->length, which comes from the UVCIOC_SEND_RESPONSE ioctl and is only checked for being negative. The source buffer data->data is only 60 bytes, so a response with uvc->event_length and data->length both greater than 60 makes memcpy() read past the end of data->data. Clamp req->length to sizeof(data->data) as well. Fixes: a5eaaa1f33e7 ("usb: gadget: uvc: use capped length value") Cc: stable Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260629195004.148405-1-meatuni001@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_v4l2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 23bafb07133f2..b5384c88b84c9 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -200,6 +200,8 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) return usb_ep_set_halt(cdev->gadget->ep0); req->length = min_t(unsigned int, uvc->event_length, data->length); + if (req->length > sizeof(data->data)) + req->length = sizeof(data->data); req->zero = data->length < uvc->event_length; memcpy(req->buf, data->data, req->length); From c1611c6744e3aa95f40ce6a21bca5037ab01a349 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sat, 11 Jul 2026 17:36:30 +0000 Subject: [PATCH 2381/3297] USB: serial: ftdi_sio: add support for E+H FXA291 commit fad0fd120e29041b3e6cdf41bb12e3184fb524a2 upstream. The Commubox FXA291 by Endress+Hauser AG is a USB serial converter based on FT232B which is used to communicate with field devices. It enumerates using the FTDI vendor ID and a custom PID. usb 1-9: New USB device found, idVendor=0403, idProduct=e510, bcdDevice= 4.00 usb 1-9: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 1-9: Product: FXA291 usb 1-9: Manufacturer: Endress+Hauser usb 1-9: SerialNumber: 00000000 ftdi_sio 1-9:1.0: FTDI USB Serial Device converter detected usb 1-9: Detected FT232B usb 1-9: FTDI USB Serial Device converter now attached to ttyUSB0 Signed-off-by: Tim Pambor Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 ++ drivers/usb/serial/ftdi_sio_ids.h | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ceddfb1852e16..8eb50640e8b73 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1075,6 +1075,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) }, /* Abacus Electrics */ { USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) }, + /* Endress+Hauser AG devices */ + { USB_DEVICE(FTDI_VID, FTDI_EH_FXA291_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 6c76cfebfd0e4..9c83c17853c87 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -313,6 +313,11 @@ #define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkuhr (UDF 77) */ #define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */ +/* + * Endress+Hauser AG product ids (FTDI_VID) + */ +#define FTDI_EH_FXA291_PID 0xE510 + /* * EVER Eco Pro UPS (http://www.ever.com.pl/) */ From cbe00048b69d67c8a78293cb7681b4c9963b26c7 Mon Sep 17 00:00:00 2001 From: Sunho Park Date: Tue, 14 Jul 2026 19:42:30 +0900 Subject: [PATCH 2382/3297] USB: serial: io_edgeport: cap received transmit credits commit faaddd811c5099f11a5f52e68a6b31a5898cda4f upstream. The interrupt-status packet reports transmit credits returned by the device. edge_interrupt_callback() adds the 16-bit value to txCredits without checking maxTxCredits. edge_write() uses txCredits minus the software FIFO count as the amount of data that fits. Since the FIFO is allocated with maxTxCredits bytes, txCredits exceeding maxTxCredits can cause OOB write in ring buffer. Cap accumulated credits at maxTxCredits. Conforming devices should never hit the cap. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Sunho Park Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index ad73040b30c8d..9b8fb8467b537 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -646,7 +646,8 @@ static void edge_interrupt_callback(struct urb *urb) if (edge_port && edge_port->open) { spin_lock_irqsave(&edge_port->ep_lock, flags); - edge_port->txCredits += txCredits; + edge_port->txCredits = min(edge_port->txCredits + txCredits, + edge_port->maxTxCredits); spin_unlock_irqrestore(&edge_port->ep_lock, flags); dev_dbg(dev, "%s - txcredits for port%d = %d\n", From 601f75671b8fb4d4ee540e91f17fe0208c83b63a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Jul 2026 16:31:35 +0200 Subject: [PATCH 2383/3297] USB: serial: keyspan_pda: fix data loss on receive throttling commit 42a97c0480f96a2977e6d51ce512adc780f1ef5d upstream. Killing the interrupt-in urb when the line disciple requests throttling may lead to data loss if an ongoing transfer is cancelled. Instead set a flag to prevent the completion handler from resubmitting the urb until the port is unthrottled. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan_pda.c | 44 +++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index f800bf5ac1de9..e500ffd150854 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -35,6 +35,8 @@ struct keyspan_pda_private { struct work_struct unthrottle_work; struct usb_serial *serial; struct usb_serial_port *port; + bool throttled; + bool throttle_req; }; static int keyspan_pda_write_start(struct usb_serial_port *port); @@ -150,6 +152,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) int retval; int status = urb->status; struct keyspan_pda_private *priv; + bool throttled = false; unsigned long flags; priv = usb_get_serial_port_data(port); @@ -211,16 +214,24 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) } exit: - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(&port->dev, - "%s - usb_submit_urb failed with result %d\n", - __func__, retval); + spin_lock_irqsave(&port->lock, flags); + if (priv->throttle_req) { + priv->throttled = true; + throttled = true; + } + spin_unlock_irqrestore(&port->lock, flags); + + if (!throttled) { + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(&port->dev, "failed to resubmit in urb: %d\n", retval); + } } static void keyspan_pda_rx_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct keyspan_pda_private *priv = usb_get_serial_port_data(port); /* * Stop receiving characters. We just turn off the URB request, and @@ -230,16 +241,29 @@ static void keyspan_pda_rx_throttle(struct tty_struct *tty) * send an XOFF, although it might make sense to foist that off upon * the device too. */ - usb_kill_urb(port->interrupt_in_urb); + spin_lock_irq(&port->lock); + priv->throttle_req = true; + spin_unlock_irq(&port->lock); } static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct keyspan_pda_private *priv = usb_get_serial_port_data(port); + bool throttled; + int ret; - /* just restart the receive interrupt URB */ - if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL)) - dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n"); + spin_lock_irq(&port->lock); + throttled = priv->throttled; + priv->throttled = false; + priv->throttle_req = false; + spin_unlock_irq(&port->lock); + + if (throttled) { + ret = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (ret) + dev_err(&port->dev, "failed to submit in urb: %d\n", ret); + } } static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud) @@ -579,6 +603,8 @@ static int keyspan_pda_open(struct tty_struct *tty, spin_lock_irq(&port->lock); priv->tx_room = rc; + priv->throttled = false; + priv->throttle_req = false; spin_unlock_irq(&port->lock); rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); From 67d2626827e3c2f1c0c79c5b35076b9fa2cbd36b Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Wed, 8 Jul 2026 18:00:01 +0800 Subject: [PATCH 2384/3297] USB: serial: option: add TDTECH MT5710-CN commit 55645e4f3c6022ffb160ad3617d2b624eaa38501 upstream. Add support for the TDTECH MT5710-CN (5G redcap) module based on the Huawei HiSilicon Balong chip. T: Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=3466 ProdID=3301 Rev=ff.ff S: Manufacturer=TD Tech Ltd. S: Product=TDTECH MT571X S: SerialNumber=0123456789ABCDEF C:* #Ifs= 6 Cfg#= 1 Atr=c0 MxPwr= 0mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0d Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_ncm E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm 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=06 Prot=13 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=06 Prot=12 Driver=option E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=1c Driver=option 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=06 Prot=14 Driver=option E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Interface: ECM / NCM + DIAG + AT + SERIAL + GPS Signed-off-by: Chukun Pan Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index b4b58e48e2253..89624f38fec2e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2497,6 +2497,7 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */ .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x3466, 0x3301, 0xff) }, /* TDTECH MT5710-CN */ { 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 fe8cde072293cfbe484135398a8ffddda722aca6 Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 13 Jul 2026 16:43:21 +0800 Subject: [PATCH 2385/3297] usb: typec: ucsi: Detect and skip duplicate altmodes from buggy firmware commit 67c92c6419ea6dbc5b1f3e9691aecea956e3e81c upstream. Some firmware implementations incorrectly return the same altmode multiple times at different offsets when queried via UCSI_GET_ALTERNATE_MODES. This causes sysfs duplicate filename errors and kernel call traces when the driver attempts to register the same altmode twice: sysfs: cannot create duplicate filename '/devices/.../typec/port0/port0.0/partner' typec-thunderbolt port0-partner.1: failed to create symlinks typec-thunderbolt port0-partner.1: probe with driver typec-thunderbolt failed with error -17 The matching rules differ by recipient: - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug): Two altmodes with identical SVID and VDO are byte-for-byte duplicates and the second has no observable function, so drop it. - UCSI_RECIPIENT_SOP (partner): The typec class binds each partner altmode to a port altmode of the same SVID via altmode_match()/device_find_child(), which returns the first port altmode with a matching SVID. If the partner advertises more altmodes for SVID X than the port advertises, the surplus partner altmode(s) collapse onto an already-paired port altmode and trigger the "duplicate filename .../partner" sysfs error during typec_altmode_create_links(). Use the port-side altmode count for SVID X as the authoritative cap and reject any partner altmode that would exceed it. This preserves legitimate multi-Mode partner altmodes (vendor SVIDs that the port really does advertise more than once) while filtering the firmware-generated duplicates that have no port counterpart, and is therefore stricter than a plain SVID+VDO comparison (which still admits the Thunderbolt case where firmware reports the same SVID twice with different VDOs) without being over-broad like a plain SVID match (which would falsely drop legitimate vendor multi-Mode entries). If a duplicate is detected, skip it and emit a clean warning instead of generating a kernel call trace: ucsi_acpi USBC000:00: con2: Firmware bug: duplicate partner altmode SVID 0x8087 at offset 1, ignoring. ucsi_acpi USBC000:00: con2: VDO mismatch: 0x8087a043 vs 0x00000001 The duplicate detection logic lives in a reusable helper ucsi_altmode_is_duplicate() and is invoked from ucsi_register_altmodes(). It applies to all three recipient types: partner (SOP), port (CON), and plug (SOP_P) altmodes. Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes") Cc: stable Signed-off-by: Chia-Lin Kao (AceLan) unchanged: still SVID+VDO exact-dup match. Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260713084323.287516-1-acelan.kao@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 132 ++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index b4b0c46d2fa44..fc41f00acfdfd 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -487,6 +487,129 @@ static int ucsi_register_altmode(struct ucsi_connector *con, return ret; } +static void ucsi_dump_duplicate_altmode(struct ucsi_connector *con, + u8 recipient, u16 svid, + u32 existing_vdo, u32 new_vdo, + int offset) +{ + static const char * const recipient_names[] = { + [UCSI_RECIPIENT_CON] = "port", + [UCSI_RECIPIENT_SOP] = "partner", + [UCSI_RECIPIENT_SOP_P] = "plug", + [UCSI_RECIPIENT_SOP_PP] = "cable plug prime", + }; + + dev_warn(con->ucsi->dev, + "con%d: Firmware bug: duplicate %s altmode SVID 0x%04x at offset %d, ignoring but please contact the BIOS vendor to fix this issue.\n", + con->num, recipient_names[recipient], svid, offset); + + if (existing_vdo != new_vdo) + dev_warn(con->ucsi->dev, + "con%d: VDO mismatch: 0x%08x vs 0x%08x\n", + con->num, existing_vdo, new_vdo); +} + +/* Count altmodes in @altmodes that advertise @svid. */ +static int ucsi_altmode_count_svid(struct typec_altmode **altmodes, u16 svid) +{ + int count = 0; + int k; + + for (k = 0; k < UCSI_MAX_ALTMODES; k++) { + if (!altmodes[k]) + break; + if (altmodes[k]->svid == svid) + count++; + } + + return count; +} + +/* + * Check if an altmode is a duplicate. Some firmware implementations + * incorrectly return the same altmode multiple times, causing sysfs errors. + * Returns true if the altmode should be skipped. + * + * The matching rules differ by recipient: + * + * - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug): + * Two altmodes with identical SVID and VDO are byte-for-byte duplicates + * and the second has no observable function. Drop them. + * + * - UCSI_RECIPIENT_SOP (partner): + * The typec class binds each partner altmode to a port altmode of the + * same SVID via altmode_match()/device_find_child(), which returns the + * first port altmode with a matching SVID. If the partner advertises + * more altmodes for SVID X than the port advertises, the surplus + * partner altmode(s) collapse onto an already-paired port altmode and + * trigger a "duplicate filename .../partner" sysfs error during + * typec_altmode_create_links(). Use the port-side altmode count for + * SVID X as the authoritative cap and reject any partner altmode that + * would exceed it. This preserves legitimate multi-Mode partner + * altmodes (e.g. vendor SVIDs that the port really does advertise + * twice) while filtering the firmware-generated duplicates that have + * no port counterpart. + */ +static bool ucsi_altmode_is_duplicate(struct ucsi_connector *con, u8 recipient, + const struct ucsi_altmode *alt_batch, int batch_idx, + u16 svid, u32 vdo, int offset) +{ + struct typec_altmode **altmodes; + int port_count, partner_count; + int k; + + /* Check for duplicates within the current batch first */ + for (k = 0; k < batch_idx; k++) { + if (alt_batch[k].svid == svid && alt_batch[k].mid == vdo) { + ucsi_dump_duplicate_altmode(con, recipient, svid, + vdo, vdo, offset); + return true; + } + } + + switch (recipient) { + case UCSI_RECIPIENT_SOP: + /* + * Cap partner altmodes per SVID by the port-side count: + * any further partner altmode for that SVID would alias an + * already-paired port altmode and break typec sysfs. + */ + port_count = ucsi_altmode_count_svid(con->port_altmode, svid); + partner_count = ucsi_altmode_count_svid(con->partner_altmode, + svid); + if (port_count && partner_count >= port_count) { + ucsi_dump_duplicate_altmode(con, recipient, svid, + con->partner_altmode[partner_count - 1]->vdo, + vdo, offset); + return true; + } + return false; + case UCSI_RECIPIENT_CON: + altmodes = con->port_altmode; + break; + case UCSI_RECIPIENT_SOP_P: + altmodes = con->plug_altmode; + break; + default: + return false; + } + + /* CON and SOP_P: drop only exact SVID+VDO duplicates. */ + for (k = 0; k < UCSI_MAX_ALTMODES; k++) { + if (!altmodes[k]) + break; + + if (altmodes[k]->svid != svid || altmodes[k]->vdo != vdo) + continue; + + ucsi_dump_duplicate_altmode(con, recipient, svid, + altmodes[k]->vdo, vdo, offset); + return true; + } + + return false; +} + static int ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) { @@ -611,6 +734,15 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) if (!alt[j].svid) return 0; + /* + * Check for duplicates in current batch and already + * registered altmodes. Skip if duplicate found. + */ + if (ucsi_altmode_is_duplicate(con, recipient, alt, j, + alt[j].svid, alt[j].mid, + i - num + j)) + continue; + memset(&desc, 0, sizeof(desc)); desc.vdo = alt[j].mid; desc.svid = alt[j].svid; From 0905b3ce1deb7228c779388fa8a8ea811821833c Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 13 Jul 2026 16:43:22 +0800 Subject: [PATCH 2386/3297] usb: typec: ucsi: Add duplicate detection to nvidia registration path commit f1aa17f72f9b9589bd724dc826c5b17d164193d1 upstream. Extend the duplicate altmode detection to ucsi_register_altmodes_nvidia() which is used when a driver provides the update_altmodes() callback. This ensures all drivers benefit from duplicate detection, whether they use the standard registration path or the nvidia path with update_altmodes callback. Without this fix, drivers using the nvidia path (like yoga_c630) would still encounter duplicate altmode registration errors from buggy firmware. Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes") Cc: stable Signed-off-by: Chia-Lin Kao (AceLan) Link: https://patch.msgid.link/20260713084323.287516-2-acelan.kao@canonical.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index fc41f00acfdfd..01a15ab5a734a 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -664,19 +664,25 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) /* now register altmodes */ for (i = 0; i < max_altmodes; i++) { - memset(&desc, 0, sizeof(desc)); - if (multi_dp) { - desc.svid = updated[i].svid; - desc.vdo = updated[i].mid; - } else { - desc.svid = orig[i].svid; - desc.vdo = orig[i].mid; - } - desc.roles = TYPEC_PORT_DRD; + struct ucsi_altmode *altmode_array = multi_dp ? updated : orig; - if (!desc.svid) + if (!altmode_array[i].svid) return 0; + /* + * Check for duplicates in current array and already + * registered altmodes. Skip if duplicate found. + */ + if (ucsi_altmode_is_duplicate(con, recipient, altmode_array, i, + altmode_array[i].svid, + altmode_array[i].mid, i)) + continue; + + memset(&desc, 0, sizeof(desc)); + desc.svid = altmode_array[i].svid; + desc.vdo = altmode_array[i].mid; + desc.roles = TYPEC_PORT_DRD; + ret = ucsi_register_altmode(con, &desc, recipient); if (ret) return ret; From 731acda5ba777240d51c3f027add0712537107fe Mon Sep 17 00:00:00 2001 From: Georgi Valkov Date: Mon, 13 Jul 2026 01:17:09 +0300 Subject: [PATCH 2387/3297] wifi: mwifiex: fix freeze for 60 seconds caused by request_firmware [ Upstream commit 121a96c5a0db8d18e2ba2cb89660cca8a40508fe ] Fix regression in rgpower table loading, caused by using request_firmware(): when the requested firmware does not exist, e.g. nxp/rgpower_WW.bin does not exist on OpenWRT builds for WRT3200ACM, request_firmware() falls back to firmware_fallback_sysfs(), which expects the firmware to be provided by user space using SYSFS. No such utility is provided in this configuration, so the entire system locks up for 60 seconds, until the request times out. During this time, no other log messages are observed, and the device does not respond to commands over UART. The request_firmware() call is performed in the following context: current->comm kworker/1:2 in_task 1 irqs_disabled 0 in_atomic 0 Fixed by using request_firmware_direct(). This prevents fallback to SYSFS, and avoids delay. The rgpower table is optional. The driver falls back to the device tree power table if the firmware is not present. The error code is printed for debugging and returned to the caller, which only cares for success or failure, so there are no side effects. Fixes: 7b6f16a25806 ("wifi: mwifiex: add rgpower table loading support") Signed-off-by: Georgi Valkov Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260712221709.7099-1-gvalkov@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index ef6722ffdc74d..358de94eeb5ed 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -196,6 +196,7 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv) struct mwifiex_adapter *adapter = priv->adapter; char rgpower_table_name[30]; char country_code[3]; + int ret; strscpy(country_code, domain_info->country_code, sizeof(country_code)); @@ -214,16 +215,17 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv) adapter->rgpower_data = NULL; } - if ((request_firmware(&adapter->rgpower_data, rgpower_table_name, - adapter->dev))) { + ret = request_firmware_direct(&adapter->rgpower_data, rgpower_table_name, + adapter->dev); + + if (ret) { mwifiex_dbg( adapter, INFO, - "info: %s: failed to request regulatory power table\n", - __func__); - return -EIO; + "info: %s: failed to request regulatory power table: %d\n", + __func__, ret); } - return 0; + return ret; } static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv) From 03eb7a8099474c49915a62f2efee577816b306e6 Mon Sep 17 00:00:00 2001 From: Xie Bo Date: Mon, 27 Jul 2026 10:42:22 +0800 Subject: [PATCH 2388/3297] RISC-V: KVM: Serialize virtual interrupt pending state updates commit d024a0a7879e6f37c0152aacf6d8e37b214a1738 upstream. KVM RISC-V tracks guest local interrupt state with two bitmaps: - irqs_pending: interrupts that should be visible to the guest - irqs_pending_mask: interrupts whose pending state changed The current code updates those bitmaps with independent atomic bitops and assumes a multiple-producer, single-consumer protocol. That model does not actually hold. kvm_riscv_vcpu_sync_interrupts() is not a pure consumer. When the guest changes guest-visible HVIP state, sync_interrupts() writes both irqs_pending and irqs_pending_mask to reflect the new guest state back into KVM state. As a result, irqs_pending and irqs_pending_mask form a single logical state transition, but they are not updated atomically as a pair. This allows a race where a newly injected interrupt is lost. For example: CPU0 CPU1 ---- ---- kvm_riscv_vcpu_set_interrupt(VS_SOFT) set_bit(VS_SOFT, irqs_pending) kvm_riscv_vcpu_sync_interrupts() sees guest-cleared HVIP.VSSIP sets irqs_pending_mask clear_bit(IRQ_VS_SOFT, irqs_pending) set_bit(VS_SOFT, irqs_pending_mask) kvm_vcpu_kick() After that interleaving, a later flush can update HVIP without VSSIP even though a new virtual interrupt was injected. In practice, the guest can remain blocked in WFI with work pending. The same pending/mask protocol is shared by VS soft interrupts, PMU overflow delivery, and AIA high interrupt synchronization, so the race is not limited to one interrupt source. Fix this by serializing all updates to irqs_pending and irqs_pending_mask with a per-vCPU raw spinlock. This keeps the pending bit and the dirty mask as one state transition across: - set/unset interrupt - guest HVIP sync - interrupt flush to guest CSR state - vCPU reset - AIA CSR writes that clear dirty state Use non-atomic bitmap operations while holding the lock. Hold the lock across the AIA sync, flush, and pending checks as well, so both bitmap words share the same serialization domain. This intentionally replaces the existing lockless protocol instead of trying to repair it with additional barriers. The problem is not memory ordering on a single field; it is that two separate bitmaps encode one shared state machine while both producers and sync paths can modify them. A per-vCPU raw spinlock keeps the fix small, local, and suitable for backporting. Fixes: cce69aff689e ("RISC-V: KVM: Implement VCPU interrupts and requests handling") Cc: stable@vger.kernel.org Signed-off-by: Xie Bo Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260715020359.1521354-2-xb@ultrarisc.com Signed-off-by: Anup Patel [ bo: Adapt the AIA and general CSR helpers to their 6.18.y layout. ] Signed-off-by: Sasha Levin --- arch/riscv/include/asm/kvm_host.h | 10 ++--- arch/riscv/kvm/aia.c | 35 ++++++++++++---- arch/riscv/kvm/vcpu.c | 68 ++++++++++++++++++++++--------- arch/riscv/kvm/vcpu_onereg.c | 8 +++- 4 files changed, 87 insertions(+), 34 deletions(-) diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 4d794573e3dbe..fd3ba183cd824 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -220,13 +220,13 @@ struct kvm_vcpu_arch { /* * VCPU interrupts * - * We have a lockless approach for tracking pending VCPU interrupts - * implemented using atomic bitops. The irqs_pending bitmap represent - * pending interrupts whereas irqs_pending_mask represent bits changed - * in irqs_pending. Our approach is modeled around multiple producer - * and single consumer problem where the consumer is the VCPU itself. + * The irqs_pending bitmap represents pending interrupts whereas + * irqs_pending_mask represents bits changed in irqs_pending. Updates + * to these bitmaps are serialized so vcpu interrupt sync/flush cannot + * drop a newly injected interrupt while syncing guest-visible HVIP. */ #define KVM_RISCV_VCPU_NR_IRQS 64 + raw_spinlock_t irqs_pending_lock; DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS); DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index dad3181856600..b8b5060821556 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -50,12 +50,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu) struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; unsigned long mask, val; + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (!kvm_riscv_aia_available()) return; - if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) { - mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0); - val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask; + mask = vcpu->arch.irqs_pending_mask[1]; + if (mask) { + vcpu->arch.irqs_pending_mask[1] = 0; + val = vcpu->arch.irqs_pending[1] & mask; csr->hviph &= ~mask; csr->hviph |= val; @@ -66,6 +69,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (kvm_riscv_aia_available()) csr->vsieh = ncsr_read(CSR_VSIEH); } @@ -74,13 +79,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { unsigned long seip; +#ifdef CONFIG_32BIT + unsigned long flags; + bool pending; +#endif if (!kvm_riscv_aia_available()) return false; #ifdef CONFIG_32BIT - if (READ_ONCE(vcpu->arch.irqs_pending[1]) & - (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask))) + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + pending = vcpu->arch.irqs_pending[1] & + (vcpu->arch.aia_context.guest_csr.vsieh & + upper_32_bits(mask)); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + + if (pending) return true; #endif @@ -198,6 +212,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, unsigned long val) { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; +#ifdef CONFIG_32BIT + unsigned long flags; +#endif if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long)) return -ENOENT; @@ -206,8 +223,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, ((unsigned long *)csr)[reg_num] = val; #ifdef CONFIG_32BIT - if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0); + if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + vcpu->arch.irqs_pending_mask[1] = 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, + flags); + } #endif } diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index d26c4967c20e2..6f8e9b105da7e 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -78,6 +78,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu, static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) { + unsigned long flags; bool loaded; /** @@ -102,8 +103,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) kvm_riscv_vcpu_aia_reset(vcpu); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS); bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); kvm_riscv_vcpu_pmu_reset(vcpu); @@ -147,6 +150,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) /* Setup VCPU hfence queue */ spin_lock_init(&vcpu->arch.hfence_lock); + raw_spin_lock_init(&vcpu->arch.irqs_pending_lock); spin_lock_init(&vcpu->arch.reset_state.lock); @@ -348,10 +352,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu) { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; unsigned long mask, val; + unsigned long flags; - if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) { - mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0); - val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask; + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + + mask = vcpu->arch.irqs_pending_mask[0]; + if (mask) { + vcpu->arch.irqs_pending_mask[0] = 0; + val = vcpu->arch.irqs_pending[0] & mask; csr->hvip &= ~mask; csr->hvip |= val; @@ -359,11 +367,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu) /* Flush AIA high interrupts */ kvm_riscv_vcpu_aia_flush_interrupts(vcpu); + + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); } void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) { unsigned long hvip; + unsigned long flags; struct kvm_vcpu_arch *v = &vcpu->arch; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; @@ -372,34 +383,41 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) /* Sync-up HVIP.VSSIP bit changes does by Guest */ hvip = ncsr_read(CSR_HVIP); + + raw_spin_lock_irqsave(&v->irqs_pending_lock, flags); + if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) { if (hvip & (1UL << IRQ_VS_SOFT)) { - if (!test_and_set_bit(IRQ_VS_SOFT, - v->irqs_pending_mask)) - set_bit(IRQ_VS_SOFT, v->irqs_pending); + if (!__test_and_set_bit(IRQ_VS_SOFT, + v->irqs_pending_mask)) + __set_bit(IRQ_VS_SOFT, v->irqs_pending); } else { - if (!test_and_set_bit(IRQ_VS_SOFT, - v->irqs_pending_mask)) - clear_bit(IRQ_VS_SOFT, v->irqs_pending); + if (!__test_and_set_bit(IRQ_VS_SOFT, + v->irqs_pending_mask)) + __clear_bit(IRQ_VS_SOFT, v->irqs_pending); } } /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */ if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) { if (!(hvip & (1UL << IRQ_PMU_OVF)) && - !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) - clear_bit(IRQ_PMU_OVF, v->irqs_pending); + !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) + __clear_bit(IRQ_PMU_OVF, v->irqs_pending); } /* Sync-up AIA high interrupts */ kvm_riscv_vcpu_aia_sync_interrupts(vcpu); + raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags); + /* Sync-up timer CSRs */ kvm_riscv_vcpu_timer_sync(vcpu); } int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) { + unsigned long flags; + /* * We only allow VS-mode software, timer, and external * interrupts when irq is one of the local interrupts @@ -412,9 +430,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) irq != IRQ_PMU_OVF) return -EINVAL; - set_bit(irq, vcpu->arch.irqs_pending); - smp_mb__before_atomic(); - set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + __set_bit(irq, vcpu->arch.irqs_pending); + __set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); kvm_vcpu_kick(vcpu); @@ -423,6 +442,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) { + unsigned long flags; + /* * We only allow VS-mode software, timer, counter overflow and external * interrupts when irq is one of the local interrupts @@ -435,26 +456,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) irq != IRQ_PMU_OVF) return -EINVAL; - clear_bit(irq, vcpu->arch.irqs_pending); - smp_mb__before_atomic(); - set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + __clear_bit(irq, vcpu->arch.irqs_pending); + __set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); return 0; } bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { + unsigned long flags; unsigned long ie; + bool ret; + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK) << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask; ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK & (unsigned long)mask; - if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie) - return true; + ret = vcpu->arch.irqs_pending[0] & ie; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); /* Check AIA high interrupts */ - return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + if (!ret) + ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + + return ret; } void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu) diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index 865dae903aa0f..3e8f9a2834121 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -522,6 +522,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, unsigned long reg_val) { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; + unsigned long flags; if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long)) return -ENOENT; @@ -533,8 +534,11 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, ((unsigned long *)csr)[reg_num] = reg_val; - if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0); + if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + vcpu->arch.irqs_pending_mask[0] = 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + } return 0; } From 958624d6386036d966805def6250972947201817 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 27 Jul 2026 14:07:33 -0400 Subject: [PATCH 2389/3297] Revert "drm/amd/display: Add missing kdoc for ALLM parameters" This reverts commit 038a0f01dda595dceda8f0c9c5172b1297281929. Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index ae858a43c35f4..b3d55cac35694 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -447,8 +447,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, * * @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.) * @info_packet: output structure where to store VSIF - * @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated - * @ALLMValue: ALLM bit value to advertise in HF-VSIF */ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, struct dc_info_packet *info_packet) From fe7892d46921ee56b135779d32c40cb462e6c6c5 Mon Sep 17 00:00:00 2001 From: Xincheng Zhang Date: Tue, 28 Jul 2026 13:43:41 +0800 Subject: [PATCH 2390/3297] usb: xhci-pci: Limit VIA VL805 DMA addressing to 36 bits commit 1fc50f1ecde39feb4fccdaf4bc71aa6c0eb25c49 upstream. The VIA VL805/806 xHCI controller advertises AC64, but fails to handle DMA addresses at or above 0x1000000000. On systems with large amounts of RAM, this can cause USB device failures when the controller is given DMA addresses beyond its usable address width. Do not use XHCI_NO_64BIT_SUPPORT for this controller. That quirk clears the cached AC64 capability and limits DMA to 32 bits, causing unnecessary bouncing for addresses between 4GiB and 64GiB and hiding the controller's real AC64 capability from code that may need to distinguish register access width from usable DMA address width. Track the usable DMA address width separately from the AC64 capability. Initialize the generic xhci->dma_mask_bits field to 64 and let PCI quirks reduce it for controllers with narrower DMA support. Set VIA VL805/806 to 36 bits so the DMA API only hands it addresses in the range it can handle while keeping HCCPARAMS1.AC64 visible. Cc: stable@kernel.org Signed-off-by: Xincheng Zhang Link: https://patch.msgid.link/20260630-xhci-via-dma-fix-v3-1-690dcb8cf75a@ultrarisc.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/host/xhci-pci.c | 1 + drivers/usb/host/xhci.c | 15 ++++++++++----- drivers/usb/host/xhci.h | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index f67a4d9562046..734f7e7415835 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -448,6 +448,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_TRB_OVERFETCH; + xhci->dma_mask_bits = 36; } if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a06758addbf82..23b104c2956c7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5459,6 +5459,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + xhci->dma_mask_bits = 64; /* xhci-plat or xhci-pci might have set max_interrupters already */ if ((!xhci->max_interrupters) || xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) @@ -5505,12 +5506,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->quirks & XHCI_NO_64BIT_SUPPORT) xhci->hcc_params &= ~BIT(0); - /* Set dma_mask and coherent_dma_mask to 64-bits, - * if xHC supports 64-bit addressing */ + /* + * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports + * 64-bit addressing, unless a controller-specific quirk callback + * limits the usable address width. + */ if (HCC_64BIT_ADDR(xhci->hcc_params) && - !dma_set_mask(dev, DMA_BIT_MASK(64))) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + !dma_set_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits))) { + xhci_dbg(xhci, "Enabling %u-bit DMA addresses.\n", + xhci->dma_mask_bits); + dma_set_coherent_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits)); } else { /* * This is to avoid error in cases where a 32-bit USB diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 58a51f09cceb8..4cd4cb0e431d1 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1524,6 +1524,7 @@ struct xhci_hcd { /* imod_interval in ns (I * 250ns) */ u32 imod_interval; u32 page_size; + unsigned int dma_mask_bits; /* MSI-X/MSI vectors */ int nvecs; /* optional clocks */ From 938bcf99f53e808a3f0f432b8453d2f1e3ffeeea Mon Sep 17 00:00:00 2001 From: KP Singh Date: Tue, 28 Jul 2026 15:25:08 +0800 Subject: [PATCH 2391/3297] selftests/bpf: Adjust verifier_map_ptr for the map's excl field commit 38498c0ebacd54dbaac3513a548a13f1a8455c4e upstream. Adding the u32 excl field at offset 32 of struct bpf_map right after the sha[SHA256_DIGEST_SIZE] hash shifts the ops pointer from offset 32 to 40. Therefore, fix up the test case. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_map_ptr [...] #637/1 verifier_map_ptr/bpf_map_ptr: read with negative offset rejected:OK #637/2 verifier_map_ptr/bpf_map_ptr: read with negative offset rejected @unpriv:OK #637/3 verifier_map_ptr/bpf_map_ptr: write rejected:OK #637/4 verifier_map_ptr/bpf_map_ptr: write rejected @unpriv:OK #637/5 verifier_map_ptr/bpf_map_ptr: read non-existent field rejected:OK #637/6 verifier_map_ptr/bpf_map_ptr: read non-existent field rejected @unpriv:OK #637/7 verifier_map_ptr/bpf_map_ptr: read ops field accepted:OK #637/8 verifier_map_ptr/bpf_map_ptr: read ops field accepted @unpriv:OK [...] Summary: 2/18 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: KP Singh Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260601150248.394863-7-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- tools/testing/selftests/bpf/progs/verifier_map_ptr.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c index e2767d27d8aaf..d8e822d1a8bab 100644 --- a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c +++ b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c @@ -70,13 +70,15 @@ __naked void bpf_map_ptr_write_rejected(void) : __clobber_all); } -/* The first element of struct bpf_map is a SHA256 hash of 32 bytes, accessing - * into this array is valid. The opts field is now at offset 33. +/* + * struct bpf_map starts with the SHA256 hash sha[32] at offset 0 (a readable + * byte array), followed by the u32 excl field at offset 32. Reading a u32 at + * offset 33 runs past the end of excl and is rejected. */ SEC("socket") __description("bpf_map_ptr: read non-existent field rejected") __failure -__msg("cannot access ptr member ops with moff 32 in struct bpf_map with off 33 size 4") +__msg("access beyond the end of member excl (mend:36) in struct bpf_map with off 33 size 4") __failure_unpriv __msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN") __flag(BPF_F_ANY_ALIGNMENT) From 593072aae7fc82189adf832c8b54c61d19702188 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 28 Jul 2026 15:25:09 +0800 Subject: [PATCH 2392/3297] selftests/bpf: Keep verifier_map_ptr exercising ops pointer access commit 082c412097716b93ff1365689fc4ddcd1ce8296f upstream. sashiko complained that 38498c0ebacd ("selftests/bpf: Adjust verifier_map_ptr for the map's excl field") would slightly decrease the test coverage given before the test was against the verifier rejecting the ops pointer. Recover the old test with the right offsets and add the existing one as an additional test case. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_map_ptr [ 1.672932] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel #637/1 verifier_map_ptr/bpf_map_ptr: read with negative offset rejected:OK #637/2 verifier_map_ptr/bpf_map_ptr: read with negative offset rejected @unpriv:OK #637/3 verifier_map_ptr/bpf_map_ptr: write rejected:OK #637/4 verifier_map_ptr/bpf_map_ptr: write rejected @unpriv:OK #637/5 verifier_map_ptr/bpf_map_ptr: read non-existent field rejected:OK #637/6 verifier_map_ptr/bpf_map_ptr: read non-existent field rejected @unpriv:OK #637/7 verifier_map_ptr/bpf_map_ptr: read beyond excl field rejected:OK #637/8 verifier_map_ptr/bpf_map_ptr: read beyond excl field rejected @unpriv:OK #637/9 verifier_map_ptr/bpf_map_ptr: read ops field accepted:OK #637/10 verifier_map_ptr/bpf_map_ptr: read ops field accepted @unpriv:OK #637/11 verifier_map_ptr/bpf_map_ptr: r = 0, map_ptr = map_ptr + r:OK #637/12 verifier_map_ptr/bpf_map_ptr: r = 0, map_ptr = map_ptr + r @unpriv:OK #637/13 verifier_map_ptr/bpf_map_ptr: r = 0, r = r + map_ptr:OK #637/14 verifier_map_ptr/bpf_map_ptr: r = 0, r = r + map_ptr @unpriv:OK #637 verifier_map_ptr:OK [...] Summary: 2/20 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260602133052.423725-4-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- .../selftests/bpf/progs/verifier_map_ptr.c | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c index d8e822d1a8bab..1661936598703 100644 --- a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c +++ b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c @@ -72,17 +72,43 @@ __naked void bpf_map_ptr_write_rejected(void) /* * struct bpf_map starts with the SHA256 hash sha[32] at offset 0 (a readable - * byte array), followed by the u32 excl field at offset 32. Reading a u32 at - * offset 33 runs past the end of excl and is rejected. + * byte array), the u32 excl field at offset 32, and the ops pointer at offset + * 40. Reading a u32 at offset 41 reaches into the middle of the ops pointer, + * i.e. a partial pointer access, which is rejected. */ SEC("socket") __description("bpf_map_ptr: read non-existent field rejected") __failure -__msg("access beyond the end of member excl (mend:36) in struct bpf_map with off 33 size 4") +__msg("cannot access ptr member ops with moff 40 in struct bpf_map with off 41 size 4") __failure_unpriv __msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN") __flag(BPF_F_ANY_ALIGNMENT) __naked void read_non_existent_field_rejected(void) +{ + asm volatile (" \ + r6 = 0; \ + r1 = %[map_array_48b] ll; \ + r6 = *(u32*)(r1 + 41); \ + r0 = 1; \ + exit; \ +" : + : __imm_addr(map_array_48b) + : __clobber_all); +} + +/* + * The u32 excl field spans offsets 32..35 (mend 36). Reading a u32 at offset + * 33 starts inside excl but extends past its end, which the verifier rejects + * as an out-of-bounds scalar access. + */ +SEC("socket") +__description("bpf_map_ptr: read beyond excl field rejected") +__failure +__msg("access beyond the end of member excl (mend:36) in struct bpf_map with off 33 size 4") +__failure_unpriv +__msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN") +__flag(BPF_F_ANY_ALIGNMENT) +__naked void read_beyond_excl_field_rejected(void) { asm volatile (" \ r6 = 0; \ @@ -105,7 +131,7 @@ __naked void ptr_read_ops_field_accepted(void) asm volatile (" \ r6 = 0; \ r1 = %[map_array_48b] ll; \ - r6 = *(u64*)(r1 + 0); \ + r6 = *(u64*)(r1 + 40); \ r0 = 1; \ exit; \ " : From 48a69cedde7388294e4ea6fd804156cd62bc04fc Mon Sep 17 00:00:00 2001 From: Cheng Yongkang Date: Fri, 5 Jun 2026 08:32:10 -0700 Subject: [PATCH 2393/3297] wifi: ath9k: hif_usb: don't dereference hif_dev after re-arming firmware request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit dad9f96945d77ecd4708f730c06ef54dcd8cc057 ] ath9k_hif_request_firmware() re-arms an asynchronous firmware load via request_firmware_nowait(), passing hif_dev as the completion context, and then still dereferences hif_dev: dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", hif_dev->fw_name); The re-armed callback ath9k_hif_usb_firmware_cb() runs on the "events" workqueue and, when the firmware is missing, walks the retry chain into ath9k_hif_usb_firmware_fail() -> complete_all(&hif_dev->fw_done). That releases the wait_for_completion(&hif_dev->fw_done) in a concurrent ath9k_hif_usb_disconnect(), which then kfree()s hif_dev. The trailing dev_info() in the frame that re-armed the request can therefore read freed memory (hif_dev->udev, the first field of struct hif_device_usb): BUG: KASAN: slab-use-after-free in ath9k_hif_request_firmware Read of size 8 ... by task kworker/... ath9k_hif_request_firmware ath9k_hif_usb_firmware_cb drivers/net/wireless/ath/ath9k/hif_usb.c:1247 request_firmware_work_func Allocated by ...: ath9k_hif_usb_probe drivers/net/wireless/ath/ath9k/hif_usb.c Freed by ...: ath9k_hif_usb_disconnect -> kfree drivers/net/wireless/ath/ath9k/hif_usb.c The fw_done barrier only makes disconnect wait for the firmware chain to *terminate*; it does not protect the outer ath9k_hif_request_firmware() frame that re-armed the request and keeps touching hif_dev afterwards. Drop the post-request dev_info(): it is the only use of hif_dev after the async request is armed, and it is purely informational (the dev_err() on the failure path runs only when request_firmware_nowait() did not arm a callback, so hif_dev is still alive there). This was first reported by syzbot as a single, non-reproduced crash that was later auto-obsoleted, and was independently rediscovered by the reFuzz fuzzer, which produced a C reproducer (USB-gadget connect/disconnect of an ath9k_htc device whose firmware download fails). The vulnerable code is unchanged and still present in v7.1-rc6, where the slab-use-after-free reproduces under KASAN once the (sub-microsecond) race window is widened. Fixes: e904cf6fe230 ("ath9k_htc: introduce support for different fw versions") Reported-by: syzbot+50122cbc2874b1eb25b0@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=50122cbc2874b1eb25b0 Signed-off-by: Cheng Yongkang Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260605153210.20471-1-1020691186@qq.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath9k/hif_usb.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index fe9abe8cd268f..0a3d2190b9bee 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1225,15 +1225,10 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev, ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, &hif_dev->udev->dev, GFP_KERNEL, hif_dev, ath9k_hif_usb_firmware_cb); - if (ret) { + if (ret) dev_err(&hif_dev->udev->dev, "ath9k_htc: Async request for firmware %s failed\n", hif_dev->fw_name); - return ret; - } - - dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", - hif_dev->fw_name); return ret; } From e5394605f9a985cc3a8263e610ba84b33cbe7b0c Mon Sep 17 00:00:00 2001 From: Gaole Zhang Date: Tue, 9 Jun 2026 17:06:09 +0800 Subject: [PATCH 2394/3297] wifi: ath11k: fix NULL pointer dereference in ath11k_hal_srng_access_begin [ Upstream commit e8d85672dd7e2523f774caafba8f858384e18df7 ] In ATH11K_QMI_EVENT_FW_READY, ATH11K_FLAG_REGISTERED is set unconditionally even when ath11k_core_qmi_firmware_ready() fails. This leaves the driver in an inconsistent state where initialization is considered complete although the firmware ready handling did not finish successfully. During the subsequent SSR, the driver enters the restart path based on this incorrect state and dereferences uninitialized srng members, resulting in a NULL pointer dereference. Call trace: ath11k_hal_srng_access_begin+0xc/0x60 [ath11k] (P) ath11k_ce_cleanup_pipes+0x17c/0x180 [ath11k] ath11k_core_restart+0x40/0x168 [ath11k] Fix this by: - skipping firmware_ready if ATH11K_FLAG_REGISTERED is already set - setting ATH11K_FLAG_REGISTERED only when firmware_ready succeeds - setting ATH11K_FLAG_QMI_FAIL and aborting the FW_READY handling on error Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.2.0.c2-00204-QCAMSLSWPLZ-1 Fixes: 6fe62a8cec51c ("wifi: ath11k: Add cold boot calibration support on WCN6750") Signed-off-by: Gaole Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260609090609.4041009-1-gaole.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/qmi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index aea56c38bf8f3..f23d75c8ad679 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -3295,9 +3295,14 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); - ath11k_core_qmi_firmware_ready(ab); - set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); - + if (!test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) { + set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); + break; + } + set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); + } break; case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE: break; From ec477af3a7e8d3964e62fd24ef01cdebb96b8e4e Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 28 Apr 2026 12:12:26 +0800 Subject: [PATCH 2395/3297] hwmon: (corsair-psu) Stop device IO before calling hid_hw_stop [ Upstream commit 9ab8656548cd737b98d0b19c4253aff8d68e97f4 ] hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within corsairpsu_probe(). If the probe operation fails after "io start" has been initiated, this race condition will result in a uaf vulnerability [1]. CPU0 CPU1 ==== ==== corsairpsu_probe() hid_device_io_start() ... unlock driver_input_lock hid_hw_stop() kfree(hidraw) __hid_input_report() ... acquire driver_input_lock hid_report_raw_event() hidraw_report_event() ... access hidraw's list_lock // trigger uaf Consequently, when corsairpsu_probe() fails and hid_hw_stop() needs to be executed, the io_started flag is first cleared while holding the driver_input_lock to prevent potential race conditions involving input reports. [1] BUG: KASAN: slab-use-after-free in rt_spin_lock+0x83/0x400 kernel/locking/spinlock_rt.c:56 Call Trace: hidraw_report_event+0x5d/0x3a0 drivers/hid/hidraw.c:577 hid_report_raw_event+0x311/0x1730 drivers/hid/hid-core.c:2076 __hid_input_report drivers/hid/hid-core.c:2152 [inline] hid_input_report+0x44e/0x580 drivers/hid/hid-core.c:2174 hid_irq_in+0x47e/0x6d0 drivers/hid/usbhid/hid-core.c:286 __usb_hcd_giveback_urb+0x3b3/0x5e0 drivers/usb/core/hcd.c:1657 dummy_timer+0x8a9/0x47d0 drivers/usb/gadget/udc/dummy_hcd.c:2005 Allocated by task 10: hidraw_connect+0x57/0x430 drivers/hid/hidraw.c:606 hid_connect+0x5bf/0x19d0 drivers/hid/hid-core.c:2277 hid_hw_start+0xa8/0x120 drivers/hid/hid-core.c:2387 corsairpsu_probe+0xd9/0x3c0 drivers/hwmon/corsair-psu.c:782 Freed by task 10: hidraw_disconnect+0x4f/0x60 drivers/hid/hidraw.c:662 hid_disconnect drivers/hid/hid-core.c:2362 [inline] hid_hw_stop+0x101/0x1e0 drivers/hid/hid-core.c:2407 corsairpsu_probe+0x327/0x3c0 drivers/hwmon/corsair-psu.c:826 Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Fixes: d115b51e0e56 ("hwmon: add Corsair PSU HID controller driver") Reported-by: syzbot+9eebf5f6544c5e873858@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=9eebf5f6544c5e873858 Tested-by: syzbot+9eebf5f6544c5e873858@syzkaller.appspotmail.com Signed-off-by: Edward Adam Davis Link: https://lore.kernel.org/r/tencent_BB7C33EB9EA41B7B4B5F1B8B25C0BA13BB08@qq.com [groeck: Updated subject and description; call hid_device_io_stop() only if IO has been started] Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/corsair-psu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index ea28c9219507a..4681af6843596 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -831,6 +831,7 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id fail_and_close: hid_hw_close(hdev); + hid_device_io_stop(hdev); fail_and_stop: hid_hw_stop(hdev); return ret; From 56d2deb6448378118dbe68c4fbb3fbae5f65b18c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 17:52:54 -0700 Subject: [PATCH 2396/3297] hwmon: (corsair-cpro) Stop device IO before calling hid_hw_stop [ Upstream commit 94c87871b051d7ad758828a805215a2ec194512a ] Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: 40c3a44542257 ("hwmon: add Corsair Commander Pro driver") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/corsair-cpro.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index b7b911f8359c7..71e48b3bba60f 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -645,6 +645,7 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) out_hw_close: hid_hw_close(hdev); + hid_device_io_stop(hdev); out_hw_stop: hid_hw_stop(hdev); return ret; From f36e12cc8cfe996d627b8a82bd9df9e43270f6e2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 17:59:10 -0700 Subject: [PATCH 2397/3297] hwmon: (gigabyte_waterforce) Stop device IO before calling hid_hw_stop [ Upstream commit ff0c5c53d08274e200b48a4d53aa078265e873cb ] Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: 42ac68e3d4ba0 ("hwmon: Add driver for Gigabyte AORUS Waterforce AIO coolers") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/gigabyte_waterforce.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/gigabyte_waterforce.c b/drivers/hwmon/gigabyte_waterforce.c index 27487e215bddf..4eea05f8b569c 100644 --- a/drivers/hwmon/gigabyte_waterforce.c +++ b/drivers/hwmon/gigabyte_waterforce.c @@ -371,13 +371,15 @@ static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id if (IS_ERR(priv->hwmon_dev)) { ret = PTR_ERR(priv->hwmon_dev); hid_err(hdev, "hwmon registration failed with %d\n", ret); - goto fail_and_close; + goto fail_and_io_stop; } waterforce_debugfs_init(priv); return 0; +fail_and_io_stop: + hid_device_io_stop(hdev); fail_and_close: hid_hw_close(hdev); fail_and_stop: From 205cff797a94757ec88ba299c8e2bf2e1e3f4bbf Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 18:00:32 -0700 Subject: [PATCH 2398/3297] hwmon: (nzxt-smart2) Stop device IO before calling hid_hw_stop [ Upstream commit 59d104b54b0b42e30fd2a68d24ee5c49dcc54d1e ] Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: 53e68c20aeb1e ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/nzxt-smart2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index 58ef9fa0184be..e2316c46629d6 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -768,7 +768,7 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev, out_hw_close: hid_hw_close(hdev); - + hid_device_io_stop(hdev); out_hw_stop: hid_hw_stop(hdev); return ret; From 305c23993e43db9a3681978691b1f9f2a1b26299 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 18:01:58 -0700 Subject: [PATCH 2399/3297] hwmon: (nzxt-kraken3) Stop device IO before calling hid_hw_stop [ Upstream commit f151d0143ac4e086f92f52328ebdbdc50933d8ef ] Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: f3b4b146eb107 ("hwmon: Add driver for NZXT Kraken X and Z series AIO CPU coolers") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/nzxt-kraken3.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/nzxt-kraken3.c b/drivers/hwmon/nzxt-kraken3.c index d00409bcab93a..05525406c5fbb 100644 --- a/drivers/hwmon/nzxt-kraken3.c +++ b/drivers/hwmon/nzxt-kraken3.c @@ -948,7 +948,7 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id ret = kraken3_init_device(hdev); if (ret < 0) { hid_err(hdev, "device init failed with %d\n", ret); - goto fail_and_close; + goto fail_and_stop_io; } ret = kraken3_get_fw_ver(hdev); @@ -960,13 +960,15 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id if (IS_ERR(priv->hwmon_dev)) { ret = PTR_ERR(priv->hwmon_dev); hid_err(hdev, "hwmon registration failed with %d\n", ret); - goto fail_and_close; + goto fail_and_stop_io; } kraken3_debugfs_init(priv, device_name); return 0; +fail_and_stop_io: + hid_device_io_stop(hdev); fail_and_close: hid_hw_close(hdev); fail_and_stop: From 7d1658b066de30f4b23afc14814d22416a971e6e Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 7 Jul 2026 10:18:03 +0000 Subject: [PATCH 2400/3297] watchdog: pretimeout: Fix UAF in watchdog_unregister_governor() [ Upstream commit 7362ba0f9c96ac3ad6a2ca3995bd9fc9a28a8661 ] When a watchdog governor is unregistered, it updates existing watchdog devices that were using this governor by falling back to `default_gov`. If the governor being unregistered is currently set as `default_gov`, the `default_gov` is never cleared. This leads to 2 use-after-free issues: 1. New watchdog devices registered after this point will inherit the dangling `default_gov`. 2. Existing watchdog devices using the unregistered governor will have their `wdd->gov` reassigned to the dangling `default_gov`. Fix the UAF by clearing `default_gov` if it matches the governor being unregistered. Fixes: da0d12ff2b82 ("watchdog: pretimeout: add panic pretimeout governor") Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20260707101803.3598173-1-tzungbi@kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/watchdog_pretimeout.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c index 2526436dc74dd..ea05f84f07e18 100644 --- a/drivers/watchdog/watchdog_pretimeout.c +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -167,6 +167,8 @@ void watchdog_unregister_governor(struct watchdog_governor *gov) } spin_lock_irq(&pretimeout_lock); + if (default_gov == gov) + default_gov = NULL; list_for_each_entry(p, &pretimeout_list, entry) if (p->wdd->gov == gov) p->wdd->gov = default_gov; From 8681e5addf7171602616e256a09057697dcc75ca Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 8 Jul 2026 10:41:54 +0800 Subject: [PATCH 2401/3297] watchdog: airoha: Prevent division by zero when clock frequency is zero [ Upstream commit bcfcd7619f277842430d197556463b401b839ee9 ] clk_get_rate() can return 0 when the clock provider is not properly configured or the clock is unmanaged. The driver uses wdt_freq as a divisor directly in airoha_wdt_probe() to compute max_timeout and in airoha_wdt_get_timeleft() to compute the remaining time, which results in a division by zero. Add a check for wdt_freq == 0 in probe and return -EINVAL with dev_err_probe() to prevent the division by zero and provide a diagnostic message. Fixes: 3cf67f3769b8 ("watchdog: Add support for Airoha EN7851 watchdog") Signed-off-by: Wayen Yan Link: https://lore.kernel.org/r/178347932594.81327.4834644880399144119@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/watchdog/airoha_wdt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/watchdog/airoha_wdt.c b/drivers/watchdog/airoha_wdt.c index dc8ca11c14d81..4bd333189b87e 100644 --- a/drivers/watchdog/airoha_wdt.c +++ b/drivers/watchdog/airoha_wdt.c @@ -147,6 +147,9 @@ static int airoha_wdt_probe(struct platform_device *pdev) /* Watchdog ticks at half the bus rate */ airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2; + if (!airoha_wdt->wdt_freq) + return dev_err_probe(dev, -EINVAL, + "invalid clock frequency\n"); /* Initialize struct watchdog device */ wdog_dev = &airoha_wdt->wdog_dev; From a154ca3c441a67d36b3a9ea63a4f11b06abe6223 Mon Sep 17 00:00:00 2001 From: Dmitry Morgun Date: Sat, 30 May 2026 11:42:52 +0000 Subject: [PATCH 2402/3297] wifi: ath11k: fix potential buffer underflow in ath11k_hal_rx_msdu_list_get() [ Upstream commit 7f11e70629650ff6ea140984e5ce188b775b2683 ] When the first entry in msdu_details has a zero buffer address, the code accesses msdu_details[i - 1] with i == 0, causing a buffer underflow. Fix similarly to ath12k_wifi7_hal_rx_msdu_list_get() by adding a separate check for i == 0 before the main condition to prevent the out-of-bounds access. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Dmitry Morgun Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260530114252.42615-1-d.morgun@ispras.ru Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 5666f66474455..330446f279cd6 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4609,6 +4609,9 @@ static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar, msdu_details = &msdu_link->msdu_link[0]; for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (!i && FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu_details[i].buf_addr_info.info0) == 0) + break; if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR, msdu_details[i].buf_addr_info.info0) == 0) { msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; From 9a9b0ea72d8b9504c7bd410be85d57451c9b485c Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 23 Jun 2026 16:16:48 +0200 Subject: [PATCH 2403/3297] wifi: ath11k: Flush the posted write after writing to PCIE_SOC_GLOBAL_RESET [ Upstream commit 0fe8010fc5b147607fc19ba010ba469afc95f35f ] ath11k_pci_soc_global_reset() tries to reset the device by writing to the PCIE_SOC_GLOBAL_RESET register. But it doesn't do a read-back to ensure that the write gets flushed to the device before the delay. This may lead to the delay on the host to be insufficient, if the posted write doesn't reach the device before the delay. So add a read-back after writing to the PCIE_SOC_GLOBAL_RESET register and before the delay. Compile tested only. Fixes: f3c603d412b3 ("ath11k: reset MHI during power down and power up") Reported-by: Alex Williamson Closes: https://lore.kernel.org/linux-pci/20260622160822.09350246@shazbot.org Signed-off-by: Manivannan Sadhasivam Reviewed-by: Baochen Qiang Reviewed-by: Raj Kumar Bhagat Link: https://patch.msgid.link/20260623141649.41087-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 7114eca8810db..c7048219f3e08 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -199,6 +199,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -208,6 +210,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); From 51516fda914cce2b50139f446332d551c97cafdf Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 23 Jun 2026 16:16:49 +0200 Subject: [PATCH 2404/3297] wifi: ath12k: Flush the posted write after writing to PCIE_SOC_GLOBAL_RESET [ Upstream commit 55f3aa06951cac78b0206bde961c8cf11929a27a ] ath12k_pci_soc_global_reset() tries to reset the device by writing to the PCIE_SOC_GLOBAL_RESET register. But it doesn't do a read-back to ensure that the write gets flushed to the device before the delay. This may lead to the delay on the host to be insufficient, if the posted write doesn't reach the device before the delay. So add a read-back after writing to the PCIE_SOC_GLOBAL_RESET register and before the delay. Compile tested only. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Reported-by: Alex Williamson Closes: https://lore.kernel.org/linux-pci/20260622160822.09350246@shazbot.org Signed-off-by: Manivannan Sadhasivam Reviewed-by: Baochen Qiang Reviewed-by: Raj Kumar Bhagat Tested-by: Raj Kumar Bhagat Link: https://patch.msgid.link/20260623141649.41087-2-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath12k/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 60b8f7361b7f6..37f41635d3b00 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -240,6 +240,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -249,6 +251,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); From 0177e578d7a885037b0fb82286c12e9d0360cc10 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 7 Jul 2026 23:04:54 +0800 Subject: [PATCH 2405/3297] firewire: net: Fix fragmented datagram reassembly [ Upstream commit d52a13adbb8ccbab99cd3bad36804e87d8b5c052 ] fwnet_frag_new() keeps a sorted list of received fragments for a partial datagram. When a new fragment is adjacent to an existing fragment, the code checks whether the new fragment also closes the gap to the next or previous list entry. Those neighbor lookups currently assume that the current fragment always has a real next or previous fragment. At a list edge, the next or previous entry is the list head, not a struct fwnet_fragment_info. The gap checks also compare against the old edge of the current fragment instead of the edge after adding the new fragment. As a result, a fragment that bridges two existing ranges may leave two adjacent ranges unmerged, so fwnet_pd_is_complete() can miss a complete datagram. Check for the list head before looking up the neighboring fragment, and compare the neighbor against the new fragment's far edge when deciding whether to merge all three ranges. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: c76acec6d551 ("firewire: add IPv4 support") Signed-off-by: Ruoyu Wang Link: https://lore.kernel.org/r/20260707150454.2265951-1-ruoyuw560@gmail.com Signed-off-by: Takashi Sakamoto Signed-off-by: Sasha Levin --- drivers/firewire/net.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index e829454089550..354c81409946a 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -298,31 +298,34 @@ static struct fwnet_fragment_info *fwnet_frag_new( if (fi->offset + fi->len == offset) { /* The new fragment can be tacked on to the end */ /* Did the new fragment plug a hole? */ - fi2 = list_entry(fi->fi_link.next, - struct fwnet_fragment_info, fi_link); - if (fi->offset + fi->len == fi2->offset) { - /* glue fragments together */ - fi->len += len + fi2->len; - list_del(&fi2->fi_link); - kfree(fi2); - } else { - fi->len += len; + if (!list_is_last(&fi->fi_link, &pd->fi_list)) { + fi2 = list_next_entry(fi, fi_link); + if (offset + len == fi2->offset) { + /* glue fragments together */ + fi->len += len + fi2->len; + list_del(&fi2->fi_link); + kfree(fi2); + + return fi; + } } + fi->len += len; return fi; } if (offset + len == fi->offset) { /* The new fragment can be tacked on to the beginning */ /* Did the new fragment plug a hole? */ - fi2 = list_entry(fi->fi_link.prev, - struct fwnet_fragment_info, fi_link); - if (fi2->offset + fi2->len == fi->offset) { - /* glue fragments together */ - fi2->len += fi->len + len; - list_del(&fi->fi_link); - kfree(fi); - - return fi2; + if (!list_is_first(&fi->fi_link, &pd->fi_list)) { + fi2 = list_prev_entry(fi, fi_link); + if (fi2->offset + fi2->len == offset) { + /* glue fragments together */ + fi2->len += fi->len + len; + list_del(&fi->fi_link); + kfree(fi); + + return fi2; + } } fi->offset = offset; fi->len += len; From eb636fbc443149b3501c3f97e26225ddcb314a0f Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 25 Jun 2026 23:29:07 +0000 Subject: [PATCH 2406/3297] wifi: ath6kl: fix OOB read from firmware num_msg in TX complete handler [ Upstream commit 3a21c89215cc18f1a97c5e5bfd1da6d4f3d44495 ] The firmware-controlled num_msg field (u8, 0-255) drives the loop in ath6kl_wmi_tx_complete_event_rx() without validation against the buffer length. This allows out-of-bounds reads of up to 1020 bytes past the WMI event buffer when the firmware sends an inflated num_msg. Add a check that the buffer is large enough to hold the fixed struct and the num_msg variable-length entries. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Signed-off-by: Tristan Madani Link: https://patch.msgid.link/20260625232907.3620746-1-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath6kl/wmi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 08a154bce1396..aab1ad80a8b6c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -484,6 +484,18 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) evt = (struct wmi_tx_complete_event *) datap; + if (len < sizeof(*evt)) { + ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d\n", + len); + return -EINVAL; + } + + if (len < sizeof(*evt) + evt->num_msg * sizeof(struct tx_complete_msg_v1)) { + ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d for %u msgs\n", + len, evt->num_msg); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n", evt->num_msg, evt->msg_len, evt->msg_type); From 33b5342d2080657054ddf89ef1199b426a37dae8 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:08 +0000 Subject: [PATCH 2407/3297] wifi: ath6kl: fix OOB read from firmware IE lengths in connect event [ Upstream commit 6b47b29730de3232b919d8362749f6814c5f2a33 ] The firmware-controlled beacon_ie_len, assoc_req_len, and assoc_resp_len fields in ath6kl_wmi_connect_event_rx() are not validated against the buffer length. Their sum (up to 765) can exceed the actual WMI event data, causing out-of-bounds reads during IE parsing and state corruption of wmi->is_wmm_enabled. Add a check that the total IE length fits within the buffer. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Signed-off-by: Tristan Madani Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260421135009.348084-3-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath6kl/wmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index aab1ad80a8b6c..0cdcbc3c77966 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -874,6 +874,14 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, ev = (struct wmi_connect_event *) datap; + if (len < sizeof(*ev) + ev->beacon_ie_len + + ev->assoc_req_len + ev->assoc_resp_len) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "connect event: IE lengths %u+%u+%u exceed buffer %d\n", + ev->beacon_ie_len, ev->assoc_req_len, + ev->assoc_resp_len, len); + return -EINVAL; + } if (vif->nw_type == AP_NETWORK) { /* AP mode start/STA connected event */ struct net_device *dev = vif->ndev; From 9aee949c68dc6dccbc54333537b109c53fe2079f Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:49:26 +0000 Subject: [PATCH 2408/3297] wifi: carl9170: bound memcpy length in cmd callback to prevent OOB read [ Upstream commit 4cde55b2feff9504d1f993ab80e84e7ccb62791c ] When the firmware sends a command response with a length mismatch, carl9170_cmd_callback() logs the mismatch and calls carl9170_restart() but then falls through to memcpy(ar->readbuf, buffer + 4, len - 4). Since len comes from the firmware and can exceed ar->readlen, this copies more data than the readbuf was allocated for. Bound the memcpy to min(len - 4, ar->readlen) so that the response is still completed -- avoiding repeated restarts from queued garbage -- while preventing an overread past the response buffer. Fixes: a84fab3cbfdc ("carl9170: 802.11 rx/tx processing and usb backend") Signed-off-by: Tristan Madani Acked-by: Christian Lamparter Closes: https://syzkaller.appspot.com/bug?extid=5c1ca6ccaa1215781cac Link: https://patch.msgid.link/20260421134929.325662-2-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/carl9170/rx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 6833430130f4c..f6855efc05c0f 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -150,7 +150,8 @@ static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) spin_lock(&ar->cmd_lock); if (ar->readbuf) { if (len >= 4) - memcpy(ar->readbuf, buffer + 4, len - 4); + memcpy(ar->readbuf, buffer + 4, + min_t(u32, len - 4, ar->readlen)); ar->readbuf = NULL; } From fab6ff91d5b8c4af62e2ced42fb357fa3eb9fd59 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:49:27 +0000 Subject: [PATCH 2409/3297] wifi: carl9170: fix OOB read from off-by-two in TX status handler [ Upstream commit a3f42f1049ad80c65560d2b078ad426c3134f78d ] The bounds check in carl9170_tx_process_status() uses `i > ((cmd->hdr.len / 2) + 1)` which is off by two, allowing 2 extra iterations past valid _tx_status entries when the firmware- controlled hdr.ext exceeds hdr.len/2. Fix by using the correct comparison `i >= (cmd->hdr.len / 2)`. Fixes: a84fab3cbfdc ("carl9170: 802.11 rx/tx processing and usb backend") Signed-off-by: Tristan Madani Acked-by: Christian Lamparter Closes: https://syzkaller.appspot.com/bug?extid=5c1ca6ccaa1215781cac Link: https://patch.msgid.link/20260421134929.325662-3-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/carl9170/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index b7717f9e1e9b9..b0eee8f892af7 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -692,7 +692,7 @@ void carl9170_tx_process_status(struct ar9170 *ar, unsigned int i; for (i = 0; i < cmd->hdr.ext; i++) { - if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) { + if (WARN_ON(i >= (cmd->hdr.len / 2))) { print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE, (void *) cmd, cmd->hdr.len + 4); break; From 4503829843353dbb18b879c35be1cdfc9af677b7 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:49:28 +0000 Subject: [PATCH 2410/3297] wifi: carl9170: fix buffer overflow in rx_stream failover path [ Upstream commit a1a21995c2e1cc2ca6b2226cfe4f5f018370182a ] The failover continuation in carl9170_rx_stream() copies the full tlen from the second USB transfer instead of capping at rx_failover_missing bytes. When both transfers are near maximum size, the total exceeds the 65535-byte failover SKB, triggering skb_over_panic. Limit the copy size to the missing byte count. Fixes: a84fab3cbfdc ("carl9170: 802.11 rx/tx processing and usb backend") Signed-off-by: Tristan Madani Acked-by: Christian Lamparter Closes: https://syzkaller.appspot.com/bug?extid=5c1ca6ccaa1215781cac Link: https://patch.msgid.link/20260421134929.325662-4-tristmd@gmail.com [Fix checkpatch CHECK:PARENTHESIS_ALIGNMENT] Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/carl9170/rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index f6855efc05c0f..0383d5c9698bf 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -918,7 +918,9 @@ static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len) } } - skb_put_data(ar->rx_failover, tbuf, tlen); + skb_put_data(ar->rx_failover, tbuf, + min_t(unsigned int, tlen, + ar->rx_failover_missing)); ar->rx_failover_missing -= tlen; if (ar->rx_failover_missing <= 0) { From f49ff44831d525a4e26a60273cf50b59001cdf93 Mon Sep 17 00:00:00 2001 From: You-Kai Zheng Date: Tue, 16 Jun 2026 18:39:07 +0800 Subject: [PATCH 2411/3297] btrfs: declare btrfs_ioctl_search_args_v2::buf as __u8 [ Upstream commit b95181f3929ff98949fa9460ca93eccebbf2d7fc ] The variable-sized buffer buf in struct btrfs_ioctl_search_args_v2 is declared as __u64[], but it holds a packed byte stream of search results, where all offsets into the buffer are in bytes. Declaring buf as __u64[] makes it easy for user space to write incorrect pointer arithmetic: adding a byte offset directly to a __u64 pointer scales the offset by 8, landing at byte position offset*8 instead of offset. This recently caused an infinite loop in btrfs-progs: the accessor read all-zero data from misaddressed items, which fed zeroed search keys back into the ioctl loop and spun forever. The issue was worked around at the time by disabling TREE_SEARCH_V2 entirely in btrfs-progs (d73e69824854: "btrfs-progs: temporarily disable usage of v2 of search tree ioctl"). The kernel side already treats buf as a byte buffer, so change the declaration to __u8[] to match the actual semantics and prevent similar misuse in user space. The change is ABI compatible: both the structure size and alignment are unchanged. Fixes: cc68a8a5a433 ("btrfs: new ioctl TREE_SEARCH_V2") Reviewed-by: Qu Wenruo Signed-off-by: You-Kai Zheng Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- include/uapi/linux/btrfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 8e710bbb688e0..fb89c5c37eb04 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -597,7 +597,7 @@ struct btrfs_ioctl_search_args_v2 { __u64 buf_size; /* in - size of buffer * out - on EOVERFLOW: needed size * to store item */ - __u64 buf[]; /* out - found items */ + __u8 buf[]; /* out - found items */ }; /* With a @src_length of zero, the range from @src_offset->EOF is cloned! */ From 39f196f64bd3842898799745cd2e64c37bf38dff Mon Sep 17 00:00:00 2001 From: Dave Chen Date: Mon, 29 Jun 2026 15:08:43 +0800 Subject: [PATCH 2412/3297] btrfs: fix u32 to s64 type conversion in dirty_metadata_bytes accounting [ Upstream commit 8b5a09ceb61b18b1f0797cd30a549d7dc85d8d50 ] The percpu_counter dirty_metadata_bytes is updated by negating eb->len and passing it to percpu_counter_add_batch(), whose amount parameter is s64. Since commit 84cda1a6087d ("btrfs: cache folio size and shift in extent_buffer"), eb->len is u32. The u32 result of -eb->len, when widened to the s64 parameter, becomes a large positive value instead of the intended negative value. For eb->len == 16384 the counter adds +4294950912 instead of subtracting 16384. The counter therefore grows on every metadata writeback instead of shrinking by the extent buffer size, permanently exceeding BTRFS_DIRTY_METADATA_THRESH and causing __btrfs_btree_balance_dirty() to trigger balance_dirty_pages_ratelimited() unconditionally, adding unnecessary writeback pressure. Cast eb->len to s64 before negation at both call sites so the subtraction is performed in signed 64-bit arithmetic. Reviewed-by: Filipe Manana Fixes: 84cda1a6087d ("btrfs: cache folio size and shift in extent_buffer") Signed-off-by: Dave Chen Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/extent_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index f955b4003d424..80fc2d59bfeb8 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1949,7 +1949,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, - -eb->len, + -(s64)eb->len, fs_info->dirty_metadata_batch); ret = true; } else { @@ -3731,7 +3731,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, return; buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY); - percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len, + percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len, fs_info->dirty_metadata_batch); for (int i = 0; i < num_extent_folios(eb); i++) { From 9304713b70e7e1450e3a76e758836fe5391bfa95 Mon Sep 17 00:00:00 2001 From: Leo Martins Date: Tue, 30 Jun 2026 12:58:05 -0700 Subject: [PATCH 2413/3297] btrfs: don't propagate EXTENT_FLAG_LOGGING to split extent maps [ Upstream commit 5eff4d5b17fa1950e80bfd1ba43dc0699e61a644 ] When btrfs_drop_extent_map_range() splits an extent map, the new split maps inherit the original map's flags through a local 'flags' variable. Commit f86f7a75e2fb ("btrfs: use the flags of an extent map to identify the compression type") changed the EXTENT_FLAG_LOGGING clearing to operate on em->flags instead of that local 'flags' copy, so a split of an extent map that is currently being logged wrongly inherits EXTENT_FLAG_LOGGING. The flag is then never cleared on the split, and when it is freed while still on the inode's modified_extents list (for example by the extent map shrinker) it trips the WARN_ON(!list_empty(&em->list)) in btrfs_free_extent_map() and leads to a use-after-free. Clear EXTENT_FLAG_LOGGING from the local 'flags' copy used for the splits and only clear EXTENT_FLAG_PINNED from em->flags, restoring the behaviour prior to f86f7a75e2fb. CC: Jeff Layton Link: https://lore.kernel.org/all/20260629-btrfs-skip-logging-v1-1-4e3a28c1acaf@kernel.org/ Fixes: f86f7a75e2fb ("btrfs: use the flags of an extent map to identify the compression type") Reviewed-by: Jeff Layton Reviewed-by: Filipe Manana Signed-off-by: Leo Martins Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/extent_map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 7e38c23a0c1cb..21eb104ee2aef 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -854,13 +854,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, goto next; } - flags = em->flags; /* * In case we split the extent map, we want to preserve the * EXTENT_FLAG_LOGGING flag on our extent map, but we don't want * it on the new extent maps. */ - em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING); + flags = em->flags & ~EXTENT_FLAG_LOGGING; + em->flags &= ~EXTENT_FLAG_PINNED; modified = !list_empty(&em->list); /* From 797dc567146c7e3c4f8d9680e4fbc76e0a6d9151 Mon Sep 17 00:00:00 2001 From: Guanghui Yang <3497809730@qq.com> Date: Sun, 12 Jul 2026 03:17:28 +0000 Subject: [PATCH 2414/3297] btrfs: free mapping node on duplicate reloc root insert [ Upstream commit 6a8269b6459ed870a8156c106a0f597383907872 ] __add_reloc_root() allocates a mapping_node before inserting it into rc->reloc_root_tree. If rb_simple_insert() finds an existing entry, it returns the existing rb_node and leaves the newly allocated node unlinked. The error path then returns -EEXIST without freeing the new node. Since the node was never inserted into reloc_root_tree, the later cleanup in put_reloc_control() cannot find it either. Free the newly allocated node before returning -EEXIST. The callers currently assert that -EEXIST should not happen, so this is a defensive cleanup for an unexpected duplicate insert path. If the path is ever reached, the local allocation should still be released. Fixes: 57a304cfd43b ("btrfs: do not panic in __add_reloc_root") Reviewed-by: Qu Wenruo Signed-off-by: Guanghui Yang <3497809730@qq.com> Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/relocation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 86430435d3acc..80e5de6736e82 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -496,6 +496,7 @@ static int __add_reloc_root(struct btrfs_root *root) btrfs_err(fs_info, "Duplicate root found for start=%llu while inserting into relocation tree", node->bytenr); + kfree(node); return -EEXIST; } From 41ae2b7d37c3dd82302167496836cca9f0328374 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 22:45:39 +0800 Subject: [PATCH 2415/3297] ASoC: tas2781: bound firmware description string parsing [ Upstream commit bc889dfcea9294a1eae7f8e2f3573a90764ae4d0 ] The TAS2781 firmware parser reads several variable-length description strings with strlen() before checking that the string terminator is present inside the firmware blob. A malformed firmware image without a NUL terminator can therefore make the parser walk past the end of the firmware buffer before the later size checks run. Add a small bounded string-length helper and use it for all description fields that are parsed from the firmware buffer. Keep the existing size checks for the fixed bytes that follow each string. Fixes: 915f5eadebd2 ("ASoC: tas2781: firmware lib") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706144540.93929-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/tas2781-fmwlib.c | 63 ++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 78fd0a5dc6f22..2f6522f76df9b 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1082,13 +1083,42 @@ static int tasdevice_load_block_kernel( return 0; } +static int tasdevice_fw_strnlen(const struct firmware *fmw, int offset) +{ + const u8 *start; + const u8 *nul; + size_t remaining; + size_t len; + + if (offset < 0 || offset >= fmw->size) + return -EINVAL; + + start = fmw->data + offset; + remaining = fmw->size - offset; + nul = memchr(start, '\0', remaining); + if (!nul) + return -EINVAL; + + len = nul - start; + if (len > INT_MAX) + return -EOVERFLOW; + + return len; +} + static int fw_parse_variable_hdr(struct tasdevice_priv *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, const struct firmware *fmw, int offset) { const unsigned char *buf = fmw->data; - int len = strlen((char *)&buf[offset]); + int len; + len = tasdevice_fw_strnlen(fmw, offset); + if (len < 0) { + dev_err(tas_priv->dev, "%s: Description error\n", __func__); + offset = len; + goto out; + } len++; if (offset + len + 8 > fmw->size) { @@ -1220,7 +1250,12 @@ static int fw_parse_data(struct tasdevice_fw *tas_fmw, memcpy(img_data->name, &data[offset], 64); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + offset = n; + goto out; + } n++; if (offset + n + 2 > fmw->size) { dev_err(tas_fmw->dev, "%s: Description error\n", __func__); @@ -1293,7 +1328,12 @@ static int fw_parse_program_data(struct tasdevice_priv *tas_priv, } offset += 64; - n = strlen((char *)&buf[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } /* skip '\0' and 5 unused bytes */ n += 6; if (offset + n > fmw->size) { @@ -1356,7 +1396,12 @@ static int fw_parse_configuration_data( memcpy(config->name, &data[offset], 64); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } n += 15; if (offset + n > fmw->size) { dev_err(tas_priv->dev, "Description err\n"); @@ -2038,7 +2083,8 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, { struct tasdevice_calibration *calibration; unsigned char *data = (unsigned char *)fmw->data; - unsigned int i, n; + unsigned int i; + int n; if (offset + 2 > fmw->size) { dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__); @@ -2070,7 +2116,12 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, calibration = &(tas_fmw->calibrations[i]); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } /* skip '\0' and 2 unused bytes */ n += 3; if (offset + n > fmw->size) { From eca9fcf9f5d893ed9ca0bc869baecf0692265c2e Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 14 Jul 2026 19:33:03 +0800 Subject: [PATCH 2416/3297] ASoC: sun4i-codec: Set quirks.playback_only for H616 codec [ Upstream commit d9e96f859de3ea3e99bce927a988449a1816483c ] The H616 codec does not have capture capabilities. Set the .playback_only quirks flag to denote this. This was somehow missing from the original driver patch, even though the patch prior to it in the series added this quirk. Fixes: 9155c321a1d0 ("ASoC: sun4i-codec: support allwinner H616 codec") Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260714113304.270224-1-wens@kernel.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sunxi/sun4i-codec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 93733ff2e32a0..3b92318e18ba6 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -2236,6 +2236,7 @@ static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = { .reg_dac_fifoc = REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31), .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, .has_reset = true, + .playback_only = true, .dma_max_burst = SUN4I_DMA_MAX_BURST, }; From 08433c71f15984ddd5f5a307cf3f0aa9b84583aa Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:36:25 +0800 Subject: [PATCH 2417/3297] ALSA: hda: cs35l41: validate and free ACPI mute object [ Upstream commit 3b597d24dc0455ae926f1053f97c2725038fc3cd ] cs35l41_get_acpi_mute_state() evaluates a _DSM method to get the ACPI mute state and reads the first byte from the returned object. However, the returned ACPI object is owned by the caller and is never freed after use, so each successful query leaks the _DSM result object. The code also assumes that the returned object is a buffer with at least one byte. A malformed firmware response can return a different object type or an empty buffer, and the direct ret->buffer.pointer dereference can then access an invalid pointer. Use the typed _DSM helper, validate that the returned buffer contains at least one byte, and free the ACPI object after reading it. Fixes: 447106e92a0c ("ALSA: hda: cs35l41: Support mute notifications for CS35L41 HDA") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260708113625.752913-1-lgs201920130244@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 438a19bd979bf..de2fd6471f059 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1437,10 +1437,19 @@ static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle guid_parse(CS35L41_UUID, &guid); if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) { - ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL); + ret = acpi_evaluate_dsm_typed(handle, &guid, 0, + CS35L41_DSM_GET_MUTE, NULL, + ACPI_TYPE_BUFFER); + if (!ret) return -EINVAL; + if (!ret->buffer.length || !ret->buffer.pointer) { + ACPI_FREE(ret); + return -EINVAL; + } + mute = *ret->buffer.pointer; + ACPI_FREE(ret); dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute); } From 9153fa1ee99bf1168bc8c864a66155ed1453fb9c Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 18:06:20 +0800 Subject: [PATCH 2418/3297] ASoC: bt-sco: fix duplicate DAPM widget names for wideband DAI [ Upstream commit 0b604e886ece11b71c4daaeccc512c784b89b014 ] The bt-sco-pcm-wb DAI uses the same stream_name strings as bt-sco-pcm ("Playback" and "Capture"). This causes duplicate DAPM AIF widget names within the same component, leading to debugfs warnings: debugfs: 'Playback' already exists in 'dapm' debugfs: 'Capture' already exists in 'dapm' Give the wideband DAI distinct stream names ("WB Playback" and "WB Capture") and add corresponding DAPM AIF widgets and routes for them. Fixes: 5947e1b4992e ("ASoC: bt-sco: extend rate and add a general compatible string") Assisted-by: VeroCoder:claude-sonnet-4-5 Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715100620.1387159-1-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/bt-sco.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 3afcef2dfa352..c0bf45b76cb8c 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -17,11 +17,17 @@ static const struct snd_soc_dapm_widget bt_sco_widgets[] = { SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("BT_SCO_RX_WB", "WB Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("BT_SCO_TX_WB", "WB Capture", 0, + SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_dapm_route bt_sco_routes[] = { { "BT_SCO_TX", NULL, "RX" }, { "TX", NULL, "BT_SCO_RX" }, + { "BT_SCO_TX_WB", NULL, "RX" }, + { "TX", NULL, "BT_SCO_RX_WB" }, }; static struct snd_soc_dai_driver bt_sco_dai[] = { @@ -45,14 +51,14 @@ static struct snd_soc_dai_driver bt_sco_dai[] = { { .name = "bt-sco-pcm-wb", .playback = { - .stream_name = "Playback", + .stream_name = "WB Playback", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { - .stream_name = "Capture", + .stream_name = "WB Capture", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, From 1ddb3e0e502a1fdc7a2a9a677561616cb12ed09c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 5 May 2026 17:11:23 +0100 Subject: [PATCH 2419/3297] ASoC: cs35l56: Don't use devres to unregister component [ Upstream commit bee87cf0f1248c0f20710d7a79df41fe892d9f88 ] Manually call snd_soc_unregister_component() from cs35l56_remove() instead of using devres cleanup. This ensures that the component is destroyed before cs35l56_remove() starts cleanup of anything the component code could be using. Devres cleanup happens after the driver remove() callback, so if snd_soc_register_component() is used, it will not be destroyed until after cs35l56_remove() has returned. But there is some cleanup that must be done in cs35l56_remove(), or wrapped in a custom devres cleanup handler to ensure correct ordering. The simplest option is to call snd_soc_unregister_component() at the start of cs35l56_remove(). Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Closes: https://sashiko.dev/#/patchset/20260501103002.2843735-1-rf%40opensource.cirrus.com Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260505161124.3621000-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown Stable-dep-of: 93c2a8ea2454 ("ASoC: cs35l56: Fix potential probe() deadlock") Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 7b57ef2336ea8..0f40af6606a42 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1437,9 +1437,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err; } - ret = devm_snd_soc_register_component(cs35l56->base.dev, - &soc_component_dev_cs35l56, - cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); + ret = snd_soc_register_component(cs35l56->base.dev, + &soc_component_dev_cs35l56, + cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); goto err_remove_wm_adsp; @@ -1541,6 +1541,8 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_init, "SND_SOC_CS35L56_CORE"); void cs35l56_remove(struct cs35l56_private *cs35l56) { + snd_soc_unregister_component(cs35l56->base.dev); + cs35l56->base.init_done = false; /* From 3c26e8bb14cc64c53843615394cb17b52e56dda1 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 16 Jul 2026 14:20:44 +0100 Subject: [PATCH 2420/3297] ASoC: cs35l56: Fix potential probe() deadlock [ Upstream commit 93c2a8ea2454b7b14eb378a58cad8a83c0ffc903 ] On I2C/SPI call cs35l56_init() before calling snd_soc_register_component() to prevent the potential for a deadlock on init_completion. For most buses all the hardware would be ready when probe() returns, but on SoundWire, probe() must return before the SoundWire bus driver will enumerate the device. All access to the registers must be deferred until the driver receives an ATTACHED notification. But anything that could return -EPROBE_DEFER must be called during probe, and that includes snd_soc_register_component(). Because of that, on SoundWire the ASoC component can be created before the registers are accssible, so cs35l56_component_probe() waits for init_completion to signal that the registers are accessible. On I2C/SPI this 2-stage startup isn't required so their probe() functions simply called cs35l56_common_probe() and then cs35l56_init(). The problem with this was that snd_soc_register_component() was still called early. If this triggered ASoC to create the card, ASoC would call cs35l56_component_probe() which waits on init_completion - but this would be running inside the cs35l56 driver probe() so blocking it from reaching the code that signals init_completion, causing a deadlock. Fixes: e496112529006 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Reported-by: Salman S. Tahir Closes: https://lore.kernel.org/linux-sound/95c21574-97d5-4311-9263-9e174d22d22c@opensource.cirrus.com/T/#u Tested-by: Salman S. Tahir Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260716132045.1469156-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56-i2c.c | 4 +--- sound/soc/codecs/cs35l56-spi.c | 4 +--- sound/soc/codecs/cs35l56.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 0492ddc4102d8..8259714d8b269 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -55,9 +55,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) if (ret != 0) return ret; - ret = cs35l56_init(cs35l56); - if (ret == 0) - ret = cs35l56_irq_request(&cs35l56->base, client->irq); + ret = cs35l56_irq_request(&cs35l56->base, client->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index 9bc9b7c98390d..b1eb924a5b6cc 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -44,9 +44,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) if (ret != 0) return ret; - ret = cs35l56_init(cs35l56); - if (ret == 0) - ret = cs35l56_irq_request(&cs35l56->base, spi->irq); + ret = cs35l56_irq_request(&cs35l56->base, spi->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 0f40af6606a42..f8468b3ae593a 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1437,6 +1437,16 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err; } + /* + * On SoundWire the cs35l56_init() cannot be run until after the + * device has been enumerated by the SoundWire core. + */ + if (!cs35l56->sdw_peripheral) { + ret = cs35l56_init(cs35l56); + if (ret) + goto err_remove_wm_adsp; + } + ret = snd_soc_register_component(cs35l56->base.dev, &soc_component_dev_cs35l56, cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); @@ -1451,6 +1461,11 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) wm_adsp2_remove(&cs35l56->dsp); err: + if (pm_runtime_enabled(cs35l56->base.dev)) { + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); + pm_runtime_disable(cs35l56->base.dev); + } + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); From cdf895bfd8035069169fa5723862381828f14924 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 16 Jul 2026 14:20:45 +0100 Subject: [PATCH 2421/3297] ASoC: cs35l56: Use complete_all() to signal init_completion [ Upstream commit e0bffb63a2eda0af82ed7e6357ac67c2db990c21 ] In cs35l56_init() use complete_all() to signal init_completion instead of complete(). cs35l56_init() was signaling init_completion using the complete() function. This only releases ONE waiter. If cs35l56_component_probe() was called multiple times the first time would consume that one signal, then future calls would timeout waiting for the completion. This could happen if: - The component is probed, removed, then probed again without the cs35l56 module being removed. - A call to component_probe() returns an error and ASoC calls it again later. It should use complete_all() so that after it has been signaled it will allow any code that waits on it to continue immediately. The one case where the driver must wait for initialization to run again is when waiting for a reboot after firmware download, and here the code correctly calls reinit_completion() first. Fixes: e496112529006 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260716132045.1469156-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index f8468b3ae593a..e3334b2d84532 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1548,7 +1548,7 @@ int cs35l56_init(struct cs35l56_private *cs35l56) return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n"); cs35l56->base.init_done = true; - complete(&cs35l56->init_completion); + complete_all(&cs35l56->init_completion); return 0; } From a076b0c457c71a5436706c11b9c2fe047e2ce650 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Wed, 15 Jul 2026 21:57:04 +0300 Subject: [PATCH 2422/3297] wifi: iwlwifi: mvm: validate SAR GEO response payload size [ Upstream commit 408d7da38272ce48e2db79b8a9895999f94d7655 ] The SAR GEO command response is cast to iwl_geo_tx_power_profiles_resp without verifying the payload length. A malformed or unexpected firmware response can lead to reading an invalid structure layout. Add an explicit size check before accessing the response data and return -EIO when the payload size is wrong. Fixes: f604324eefec ("iwlwifi: remove iwl_validate_sar_geo_profile() export") Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.7e749b7d374a.I4ef54548bff6c6e7c7a57bee771ac12508aad677@changeid Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index aa517978fc7a3..cfca5a3ee78ec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -955,12 +955,22 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) return ret; } + if (IWL_FW_CHECK(mvm, + iwl_rx_packet_payload_len(cmd.resp_pkt) != + sizeof(*resp), + "Wrong size for iwl_geo_tx_power_profiles_resp: %d\n", + iwl_rx_packet_payload_len(cmd.resp_pkt))) { + ret = -EIO; + goto out; + } + resp = (void *)cmd.resp_pkt->data; ret = le32_to_cpu(resp->profile_idx); if (WARN_ON(ret > BIOS_GEO_MAX_PROFILE_NUM)) ret = -EIO; +out: iwl_free_resp(&cmd); return ret; } From 70a6de303c9b3aa0e57e57af1580a227577090bf Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:06 +0300 Subject: [PATCH 2423/3297] wifi: iwlwifi: fix pointer arithmetic in iwl_add_mcc_to_tas_block_list [ Upstream commit bc796f84ec9a95b356959ec7caf1d4fce33f3a76 ] The expression list[*size++] increments the pointer 'size' rather than the u8 value it points to (operator precedence: ++ binds to the pointer before the dereference). As a result the block-list entry is written at the correct index but *size is never incremented, so the caller's count stays at zero and subsequent calls overwrite slot 0 every time. Change to list[(*size)++] so that the value pointed to by size is incremented after use as the array index. Fixes: 5f4656610edb ("wifi: iwlwifi: extend TAS_CONFIG cmd support for v5") Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.d2cd92242582.Ife4140a4e27be2a1cd9f886c5a9b376ce182a019@changeid Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index e1f28b0532530..818eb1c4b158f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -483,7 +483,7 @@ bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc) if (*size >= IWL_WTAS_BLACK_LIST_MAX) return false; - list[*size++] = mcc; + list[(*size)++] = mcc; return true; } IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list); From eae7fdf7d4469d8405925cb264580ce7e8c06524 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:07 +0300 Subject: [PATCH 2424/3297] wifi: iwlwifi: validate payload length in iwl_pnvm_complete_fn [ Upstream commit daec24a5ed5da77a108e246ad77aa8b889911f93 ] iwl_pnvm_complete_fn() casts pkt->data directly to struct iwl_pnvm_init_complete_ntfy and reads the status field without first verifying that the firmware notification payload is large enough to contain that structure. Add a WARN_ON_ONCE check against sizeof(*pnvm_ntf) and return early without reading uninitialised memory if the payload is too short. Fixes: b3e4c0f34c17 ("iwlwifi: move PNVM implementation to common code") Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.7f2a669e5c75.I00465dcfcbccb250ae9af2d9bb305e24de1ba394@changeid Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index f297e82d63d23..5de3a9c0b3ef0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2020-2025 Intel Corporation + * Copyright(c) 2020-2026 Intel Corporation */ #include "iwl-drv.h" @@ -12,6 +12,7 @@ #include "fw/api/alive.h" #include "fw/uefi.h" #include "fw/img.h" +#include "fw/dbg.h" #define IWL_PNVM_REDUCED_CAP_BIT BIT(25) @@ -26,6 +27,12 @@ static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_trans *trans = (struct iwl_trans *)data; struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data; + if (IWL_FW_CHECK(trans, + iwl_rx_packet_payload_len(pkt) < sizeof(*pnvm_ntf), + "Bad notif len: %d\n", + iwl_rx_packet_payload_len(pkt))) + return true; + IWL_DEBUG_FW(trans, "PNVM complete notification received with status 0x%0x\n", le32_to_cpu(pnvm_ntf->status)); From 2d5dec517b5390d4825419044d39e0ac84e47d28 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Wed, 15 Jul 2026 21:57:08 +0300 Subject: [PATCH 2425/3297] wifi: iwlwifi: mvm: fix read in wake packet notification handler [ Upstream commit 9d7657aae8c1579584c67b0b66114a6a98db8b2f ] In iwl_mvm_wowlan_store_wake_pkt(), packet_len was initialized from notif->wake_packet_length before the explicit check that len >= sizeof(*notif). Move the assignment of packet_len to after the size check so that notif->wake_packet_length is only accessed once the payload length has been validated. Fixes: 219ed58feda9 ("wifi: iwlwifi: mvm: Add support for wowlan wake packet notification") Signed-off-by: Shahar Tzarfati Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.99d5cf85a528.Ic4aa736011d4fe88e0cd19723d1d48bb24642198@changeid Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 11afe373961f3..46e95e25dcd44 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2783,7 +2783,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm, struct iwl_wowlan_status_data *status, u32 len) { - u32 data_size, packet_len = le32_to_cpu(notif->wake_packet_length); + u32 data_size, packet_len; if (len < sizeof(*notif)) { IWL_ERR(mvm, "Invalid WoWLAN wake packet notification!\n"); @@ -2802,6 +2802,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm, return -EIO; } + packet_len = le32_to_cpu(notif->wake_packet_length); data_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, wake_packet); /* data_size got the padding from the notification, remove it. */ From d0a57f19fe2865b9747484f5f9c631f944ed9a0f Mon Sep 17 00:00:00 2001 From: Diego Fernando Mancera Gomez Date: Fri, 17 Jul 2026 02:07:04 -0600 Subject: [PATCH 2426/3297] usb: atm: ueagle-atm: reject descriptors that confuse probe and disconnect [ Upstream commit 71132cedd1ecbc4032d76e9928c18a10f7e39b80 ] uea_probe() distinguishes a pre-firmware device from a post-firmware one using the USB id (UEA_IS_PREFIRM()), and stores a different object as the interface data in each case: a 'struct completion' for a pre-firmware device (to be waited on in .disconnect()), or a 'struct usbatm_data' for a post-firmware one. uea_disconnect() instead tells the two apart by the number of interfaces of the active configuration (a pre-firmware device exposes a single interface, ADI930 has 2 and eagle has 3), and casts the interface data accordingly. Because the two handlers use different criteria, a crafted device that advertises a pre-firmware id together with a multi-interface descriptor (or a post-firmware id with a single interface) makes them disagree: the small 'struct completion' stored by uea_probe() is then passed to usbatm_usb_disconnect(), which casts it to 'struct usbatm_data' and takes instance->serialize, reading past the end of the allocation: BUG: KASAN: slab-out-of-bounds in __mutex_lock+0x152a/0x1b80 Read of size 8 at addr ffff8880470e2c60 by task kworker/1:2/982 ... __mutex_lock+0x152a/0x1b80 usbatm_usb_disconnect+0x70/0x820 uea_disconnect+0x133/0x2c0 usb_unbind_interface+0x1dd/0x9e0 ... which belongs to the cache kmalloc-96 of size 96 The buggy address is located 0 bytes to the right of allocated 96-byte region [ffff8880470e2c00, ffff8880470e2c60) Reject such inconsistent descriptors in uea_probe() so that both handlers always make the same pre/post-firmware decision. Reported-by: syzbot+e62a973f8322b3bbe3ac@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e62a973f8322b3bbe3ac Fixes: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()") Signed-off-by: Diego Fernando Mancera Gomez Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260717080704.1264-1-diegomancera.dev@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/atm/ueagle-atm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index a943a5257a59f..f1f1fc16a4711 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2550,6 +2550,7 @@ static struct usbatm_driver uea_usbatm_driver = { static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *usb = interface_to_usbdev(intf); + bool single_iface = usb->config->desc.bNumInterfaces == 1; int ret; uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", @@ -2558,6 +2559,22 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) le16_to_cpu(usb->descriptor.bcdDevice), chip_name[UEA_CHIP_VERSION(id)]); + /* + * uea_probe() decides between the pre-firmware and post-firmware case + * from the USB id and stores a different object as interface data in + * each case: a struct completion for a pre-firmware device, a struct + * usbatm_data for a post-firmware one. uea_disconnect() instead tells + * the two apart by the number of interfaces (a pre-firmware device + * exposes a single interface, ADI930 has 2 and eagle has 3). A crafted + * device advertising a pre-firmware id together with a multi-interface + * descriptor (or the other way around) makes the two disagree, so that + * usbatm_usb_disconnect() treats the small completion object as a + * struct usbatm_data and reads out of bounds. Reject such inconsistent + * descriptors so both paths make the same decision. + */ + if (UEA_IS_PREFIRM(id) != single_iface) + return -ENODEV; + usb_reset_device(usb); if (UEA_IS_PREFIRM(id)) { From d5913f97b5b60364953e17c4d8e0626ea44a15d6 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Wed, 3 Jun 2026 11:05:21 +0000 Subject: [PATCH 2427/3297] drivers/virt: pkvm: Fix end calculation in mmio_guard_ioremap_hook() [ Upstream commit 62c740f823a8e47ffe56e45a7472c27cf988e2f6 ] Sashiko (locally) reports a logical issues in mmio_guard_ioremap_hook() mmio_guard_ioremap_hook() attempts to handle unaligned addresses and sizes. However, aligning the start address before adding the size, might shift the end to the page before. Fixes: 0f1269495800 ("drivers/virt: pkvm: Intercept ioremap using pKVM MMIO_GUARD hypercall") Signed-off-by: Mostafa Saleh Reviewed-by: Catalin Marinas Tested-by: Aneesh Kumar K.V (Arm) Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c index 4230b817a80bd..d66291def0f40 100644 --- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c +++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c @@ -82,8 +82,8 @@ static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size, if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE) return 0; + end = PAGE_ALIGN(phys + size); phys = PAGE_ALIGN_DOWN(phys); - end = phys + PAGE_ALIGN(size); while (phys < end) { const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID; From 22e449c1dd543ee5ebdc6f790bf8130b7d76a9cf Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sat, 11 Jul 2026 09:42:07 +0200 Subject: [PATCH 2428/3297] hwmon: (asus-ec-sensors) fix looping over banks while reading from EC [ Upstream commit e741d13cc2abfc6fccebe2008057aa52e285223e ] Do not assume there are only bank 0 and bank 1 available, just use '!=' for bank comparison. Fixes: d0ddfd241e57 ("hwmon: (asus-ec-sensors) add driver for ASUS EC") Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260711074217.554656-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/asus-ec-sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 95c50d3a788ce..aac4706cd437f 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -1050,7 +1050,7 @@ static int asus_ec_block_read(const struct device *dev, } for (ireg = 0; ireg < ec->nr_registers; ireg++) { reg_bank = register_bank(ec->registers[ireg]); - if (reg_bank < bank) { + if (reg_bank != bank) { continue; } ec_read(register_index(ec->registers[ireg]), From dd6f730be95b57f3e4d12d55d6922460dfc21964 Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sun, 12 Jul 2026 13:05:03 +0200 Subject: [PATCH 2429/3297] hwmon: (asus-ec-sensors) fix EC read intervals [ Upstream commit 60710b2af13b81da71b429d3f8b19dd70310729d ] Take INITIAL_JIFFIES into account when setting up next update time. Fixes: d0ddfd241e57 ("hwmon: (asus-ec-sensors) add driver for ASUS EC") Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260712110650.1240071-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/asus-ec-sensors.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index aac4706cd437f..52986b64881ea 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -870,7 +870,7 @@ struct ec_sensors_data { /* sorted list of unique register banks */ u8 banks[ASUS_EC_MAX_BANK + 1]; /* in jiffies */ - unsigned long last_updated; + u64 next_update; struct lock_data lock_data; /* number of board EC sensors */ u8 nr_sensors; @@ -1139,13 +1139,12 @@ static int get_cached_value_or_update(const struct device *dev, int sensor_index, struct ec_sensors_data *state, s32 *value) { - if (time_after(jiffies, state->last_updated + HZ)) { + if (time_after64(get_jiffies_64(), state->next_update)) { if (update_ec_sensors(dev, state)) { dev_err(dev, "update_ec_sensors() failure\n"); return -EIO; } - - state->last_updated = jiffies; + state->next_update = get_jiffies_64() + HZ; } *value = state->sensors[sensor_index].cached_value; @@ -1263,6 +1262,7 @@ static int asus_ec_probe(struct platform_device *pdev) if (!ec_data) return -ENOMEM; + ec_data->next_update = INITIAL_JIFFIES; dev_set_drvdata(dev, ec_data); ec_data->board_info = pboard_info; From d6959dd79088a08852258746bf66d8a6185aca01 Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sun, 12 Jul 2026 15:05:05 +0200 Subject: [PATCH 2430/3297] hwmon: (asus-ec-sensors) add missed handle for ENOMEM [ Upstream commit 9813c1f49efeadbcb17e4a41972350ac783f9cac ] Add missing return value check in the setup function. Fixes: d0ddfd241e57 ("hwmon: (asus-ec-sensors) add driver for ASUS EC") Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260712130602.1256700-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/asus-ec-sensors.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 52986b64881ea..13fd212ad4040 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -1353,9 +1353,11 @@ static int asus_ec_probe(struct platform_device *pdev) if (!nr_count[type]) continue; - asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev, - nr_count[type], type, - hwmon_attributes[type]); + status = asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); + if (status) + return status; *ptr_asus_ec_ci++ = asus_ec_hwmon_chan++; } From 2fdd6d196c656b376cc251e1e9ff110b3ed522e1 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Thu, 16 Jul 2026 13:25:23 +0800 Subject: [PATCH 2431/3297] smb: client: validate DFS referral PathConsumed [ Upstream commit f6f5ee2aa33b350c671721b965251c42cebb962e ] parse_dfs_referrals() validates that the response contains the fixed referral entry array and, on for-next, the per-referral string offsets. However, the response also contains a PathConsumed value that is later used for DFS path parsing. If a malformed response provides a PathConsumed value larger than the search name, later DFS parsing can advance beyond the end of the path. Validate PathConsumed against the search name length before storing it in the parsed referral. Fixes: 4ecce920e13a ("CIFS: move DFS response parsing out of SMB1 code") Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Yichong Chen Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/misc.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index f378437113129..d9771d224527d 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -957,6 +957,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, int i, rc = 0; char *data_end; struct dfs_referral_level_3 *ref; + unsigned int path_consumed; + size_t search_name_len; if (rsp_size < sizeof(*rsp)) { cifs_dbg(VFS | ONCE, @@ -1004,6 +1006,7 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, rc = -ENOMEM; goto parse_DFS_referrals_exit; } + search_name_len = strlen(searchName); /* collect necessary data from referrals */ for (i = 0; i < *num_of_nodes; i++) { @@ -1012,21 +1015,34 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, struct dfs_info3_param *node = (*target_nodes)+i; node->flags = le32_to_cpu(rsp->DFSFlags); + path_consumed = le16_to_cpu(rsp->PathConsumed); if (is_unicode) { - __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, - GFP_KERNEL); - if (tmp == NULL) { + size_t search_name_utf16_len = search_name_len * 2 + 2; + __le16 *tmp; + + if (path_consumed > search_name_utf16_len) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + tmp = kmalloc(search_name_utf16_len, GFP_KERNEL); + if (!tmp) { rc = -ENOMEM; goto parse_DFS_referrals_exit; } - cifsConvertToUTF16((__le16 *) tmp, searchName, + cifsConvertToUTF16((__le16 *)tmp, searchName, PATH_MAX, nls_codepage, remap); - node->path_consumed = cifs_utf16_bytes(tmp, - le16_to_cpu(rsp->PathConsumed), - nls_codepage); + node->path_consumed = cifs_utf16_bytes(tmp, path_consumed, + nls_codepage); kfree(tmp); - } else - node->path_consumed = le16_to_cpu(rsp->PathConsumed); + } else { + if (path_consumed > search_name_len) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + node->path_consumed = path_consumed; + } node->server_type = le16_to_cpu(ref->ServerType); node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); From c5bf6b39be235ef578af4d39872f0c68cda3b937 Mon Sep 17 00:00:00 2001 From: Qing Ming Date: Sat, 23 May 2026 16:15:43 +0800 Subject: [PATCH 2432/3297] ovpn: avoid putting unrelated P2P peer on socket release [ Upstream commit b52c5103f64ee825996ca1ab8df7283cde8c5f86 ] ovpn_peer_release_p2p() is called when an OVPN UDP socket is being destroyed. It checks the currently published P2P peer and releases it only if that peer still uses the socket being destroyed. A peer replacement can publish a new peer before the old UDP socket is destroyed. When the old socket destruction path runs afterwards, ovpn_peer_release_p2p() observes the new peer through ovpn->peer. Since the new peer uses a different socket, the function takes the socket mismatch branch. That branch still calls ovpn_peer_put(peer). At this point, however, peer is the currently published replacement peer, not the peer associated with the socket being destroyed. Dropping its reference can free it while ovpn->peer still points to it, leading to later use-after-free accesses from the peer and socket cleanup paths. KASAN reports this as a slab-use-after-free on the kmalloc-1k ovpn_peer object. In the reproducer, the object is allocated from ovpn_peer_new() via ovpn_nl_peer_new_doit(), and freed through ovpn_peer_release_rcu() from RCU callback processing. Observed access sites include ovpn_peer_remove(), ovpn_socket_release(), ovpn_nl_peer_del_notify(), and unlock_ovpn(). Fix this by returning from the socket mismatch branch without putting the peer. Fixes: f6226ae7a0cd ("ovpn: introduce the ovpn_socket object") Signed-off-by: Qing Ming Reviewed-by: Simon Horman Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- drivers/net/ovpn/peer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 87a83321f1dd5..795741645e5eb 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1162,7 +1162,6 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk, ovpn_sock = rcu_access_pointer(peer->sock); if (!ovpn_sock || ovpn_sock->sk != sk) { spin_unlock_bh(&ovpn->lock); - ovpn_peer_put(peer); return; } } From 47cd68e050a63cfbaeb1ea965f8caa14c9685fbb Mon Sep 17 00:00:00 2001 From: longlong yan Date: Wed, 3 Jun 2026 15:27:41 +0800 Subject: [PATCH 2433/3297] selftests/net: ovpn: fix getaddrinfo memory leak in ovpn_parse_remote() [ Upstream commit 0bd9cfebc1c91e1066e56d6261b99691b9df6008 ] The ovpn_parse_remote() function has two memory management issues: 1. When both 'host' and 'vpnip' are non-NULL, the first getaddrinfo() allocation is leaked because 'result' is overwritten by the second getaddrinfo() call without freeing the first allocation. 2. When both 'host' and 'vpnip' are NULL, 'result' is an uninitialized stack variable passed to freeaddrinfo(), which is undefined behavior. Fix by initializing 'result' to NULL and calling freeaddrinfo() after the first getaddrinfo() result is consumed. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: longlong yan Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- tools/testing/selftests/net/ovpn/ovpn-cli.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c index 0a5226196a2e7..44418118f6ea7 100644 --- a/tools/testing/selftests/net/ovpn/ovpn-cli.c +++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c @@ -1746,7 +1746,7 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host, const char *service, const char *vpnip) { int ret; - struct addrinfo *result; + struct addrinfo *result = NULL; struct addrinfo hints = { .ai_family = ovpn->sa_family, .ai_socktype = SOCK_DGRAM, @@ -1770,6 +1770,8 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host, } memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen); + freeaddrinfo(result); + result = NULL; } if (vpnip) { From 5b96227c0e8b212b74838424c929fc889aedb555 Mon Sep 17 00:00:00 2001 From: Marco Baffo Date: Mon, 8 Jun 2026 16:04:46 +0200 Subject: [PATCH 2434/3297] ovpn: fix use after free in unlock_ovpn() [ Upstream commit e1ad6fe5db719874efa45b2caf9934552e09fc43 ] unlock_ovpn() iterates over the release_list using llist_for_each_entry() and drops the peer reference inside the loop body via ovpn_peer_put(). If this drops the last reference, the peer is eventually freed. However, llist_for_each_entry() reads peer->release_entry.next in the loop advance expression, which runs after the body. By that time the peer may have already been freed, resulting in a use after free when advancing to the next list entry. Fix this by using llist_for_each_entry_safe(), which caches the next pointer before executing the loop body. Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object") Signed-off-by: Marco Baffo Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- drivers/net/ovpn/peer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 795741645e5eb..7e7ccae132517 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -26,11 +26,12 @@ static void unlock_ovpn(struct ovpn_priv *ovpn, struct llist_head *release_list) __releases(&ovpn->lock) { - struct ovpn_peer *peer; + struct ovpn_peer *peer, *next; spin_unlock_bh(&ovpn->lock); - llist_for_each_entry(peer, release_list->first, release_entry) { + llist_for_each_entry_safe(peer, next, release_list->first, + release_entry) { ovpn_socket_release(peer); ovpn_peer_put(peer); } From 2f0f661998941a89ad41154b5320aed4d08d1b43 Mon Sep 17 00:00:00 2001 From: Marco Baffo Date: Mon, 8 Jun 2026 16:06:42 +0200 Subject: [PATCH 2435/3297] ovpn: use monotonic clock for peer keepalive timeouts [ Upstream commit f7e6287ccd3abeed9e638b581dc3fdf742106ba3 ] Replace ktime_get_real_seconds() with the monotonic ktime_get_boottime_seconds() to ensure the keepalive mechanism is robust against system clock modifications. Right now, the driver uses ktime_get_real_seconds() to track peer timeouts, relying on the system wall-clock. An administrative time adjustment or an NTP sync that steps the clock forward can cause `now' to instantly exceed `last_recv + timeout'. When this occurs, the driver artificially expires healthy peers. Depending on the OpenVPN user-space configuration, this triggers a premature tunnel restart (if --keepalive or --ping-restart is used) or a complete disconnection of the client (if --ping-exit is used). Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism") Signed-off-by: Marco Baffo Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- drivers/net/ovpn/io.c | 4 ++-- drivers/net/ovpn/peer.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index c03e58e28a860..0008a3d30c198 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -137,7 +137,7 @@ void ovpn_decrypt_post(void *data, int ret) } /* keep track of last received authenticated packet for keepalive */ - WRITE_ONCE(peer->last_recv, ktime_get_real_seconds()); + WRITE_ONCE(peer->last_recv, ktime_get_boottime_seconds()); rcu_read_lock(); sock = rcu_dereference(peer->sock); @@ -291,7 +291,7 @@ void ovpn_encrypt_post(void *data, int ret) ovpn_peer_stats_increment_tx(&peer->link_stats, orig_len); /* keep track of last sent packet for keepalive */ - WRITE_ONCE(peer->last_sent, ktime_get_real_seconds()); + WRITE_ONCE(peer->last_sent, ktime_get_boottime_seconds()); /* skb passed down the stack - don't free it */ skb = NULL; err_unlock: diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 7e7ccae132517..b7c103fda2ae6 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -45,7 +45,7 @@ static void unlock_ovpn(struct ovpn_priv *ovpn, */ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout) { - time64_t now = ktime_get_real_seconds(); + time64_t now = ktime_get_boottime_seconds(); netdev_dbg(peer->ovpn->dev, "scheduling keepalive for peer %u: interval=%u timeout=%u\n", @@ -1352,7 +1352,7 @@ void ovpn_peer_keepalive_work(struct work_struct *work) { struct ovpn_priv *ovpn = container_of(work, struct ovpn_priv, keepalive_work.work); - time64_t next_run = 0, now = ktime_get_real_seconds(); + time64_t next_run = 0, now = ktime_get_boottime_seconds(); LLIST_HEAD(release_list); spin_lock_bh(&ovpn->lock); From 538d862cc0dbd5c732fe26d5aad98eae039e6676 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:58:26 +0800 Subject: [PATCH 2436/3297] hwmon: occ: validate poll response sensor blocks [ Upstream commit 70e76e700fc6c46afb4e17aec099a1ea089b4a22 ] The OCC poll response parser walks a counted list of sensor data blocks. It used the static backing-array capacity as the parse boundary, but a transport response makes only data_length bytes current and valid. A truncated response can therefore make the parser consume a block header or block extent outside the current response. Use data_length as the parent boundary, prove the fixed poll header and each current block header before reading them, and prove the complete block before advancing. Keep parsed sensor metadata local until the complete response has passed validation, then publish it. Propagate malformed-response errors before publishing the OCC as active. Fixes: aa195fe49b03 ("hwmon (occ): Parse OCC poll response") Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260720115826.14813-1-pengpeng@iscas.ac.cn Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/occ/common.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index e18e80e832fd3..175208d712b06 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -1052,32 +1052,49 @@ static int occ_setup_sensor_attrs(struct occ *occ) } /* only need to do this once at startup, as OCC won't change sensors on us */ -static void occ_parse_poll_response(struct occ *occ) +static int occ_parse_poll_response(struct occ *occ) { unsigned int i, old_offset, offset = 0, size = 0; + u16 data_length; struct occ_sensor *sensor; - struct occ_sensors *sensors = &occ->sensors; + struct occ_sensors parsed = {}; + struct occ_sensors *sensors = &parsed; struct occ_response *resp = &occ->resp; struct occ_poll_response *poll = (struct occ_poll_response *)&resp->data[0]; struct occ_poll_response_header *header = &poll->header; struct occ_sensor_data_block *block = &poll->block; + data_length = get_unaligned_be16(&resp->data_length); + if (data_length < sizeof(*header) || data_length > OCC_RESP_DATA_BYTES) { + dev_err(occ->bus_dev, "invalid OCC poll response length %u\n", + data_length); + return -EMSGSIZE; + } + dev_info(occ->bus_dev, "OCC found, code level: %.16s\n", header->occ_code_level); for (i = 0; i < header->num_sensor_data_blocks; ++i) { block = (struct occ_sensor_data_block *)((u8 *)block + offset); + if (size + sizeof(*header) + sizeof(block->header) > + data_length) { + dev_err(occ->bus_dev, + "truncated OCC sensor block header\n"); + return -EMSGSIZE; + } + old_offset = offset; offset = (block->header.num_sensors * block->header.sensor_length) + sizeof(block->header); - size += offset; /* validate all the length/size fields */ - if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) { - dev_warn(occ->bus_dev, "exceeded response buffer\n"); - return; + if (size + sizeof(*header) + offset > data_length) { + dev_err(occ->bus_dev, + "exceeded OCC poll response length\n"); + return -EMSGSIZE; } + size += offset; dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n", old_offset, offset - 1, block->header.eye_catcher, @@ -1107,6 +1124,9 @@ static void occ_parse_poll_response(struct occ *occ) dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size, sizeof(*header), size + sizeof(*header)); + occ->sensors = parsed; + + return 0; } int occ_active(struct occ *occ, bool active) @@ -1138,10 +1158,12 @@ int occ_active(struct occ *occ, bool active) goto unlock; } - occ->active = true; occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; - occ_parse_poll_response(occ); + rc = occ_parse_poll_response(occ); + if (rc) + goto unlock; + occ->active = true; rc = occ_setup_sensor_attrs(occ); if (rc) { dev_err(occ->bus_dev, From 9d208de7a8f64d5efe1e6d6db0c7243da0584bdf Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sat, 18 Jul 2026 02:05:59 +0100 Subject: [PATCH 2437/3297] regulator: mt6358: use regmap helper to read fixed LDO calibration [ Upstream commit 1d26f125501f3fbe6c259ab75bf6516299a0bf0e ] The "fixed" LDOs with output voltage calibration use mt6358_get_buck_voltage_sel as their get_voltage_sel op, but the MT6358_REG_FIXED and MT6366_REG_FIXED entries do not populate da_vsel_reg/da_vsel_mask. The op therefore reads register 0x0 with a zero mask and shifts the result by ffs(0) - 1 = -1, which is undefined behaviour and gets flagged by UBSAN on every boot on MT6366 boards: UBSAN: shift-out-of-bounds in drivers/regulator/mt6358-regulator.c:384:38 shift exponent -1 is negative Call trace: mt6358_get_buck_voltage_sel+0xc8/0x120 regulator_get_voltage_rdev+0x70/0x170 set_machine_constraints+0x504/0xc38 regulator_register+0x324/0xc68 Besides the undefined shift, the returned selector is always 0, so the actual calibration offset programmed in _ANA_CON0 is never reported. The descriptor already carries the correct vsel_reg/vsel_mask (the ANA_CON0 calibration field), matching the regulator_set_voltage_sel_regmap op already in use. Read the selector back through regulator_get_voltage_sel_regmap instead. Fixes: cf08fa74c716 ("regulator: mt6358: Add output voltage fine tuning to fixed regulators") Signed-off-by: Daniel Golle Reviewed-by: Chen-Yu Tsai Tested-by: Chen-Yu Tsai Link: https://patch.msgid.link/dcd98d81dede338c9bbb9700a9613c848b702e49.1784336005.git.daniel@makrotopia.org Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/regulator/mt6358-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/mt6358-regulator.c b/drivers/regulator/mt6358-regulator.c index e4745f616cea3..44bef0bdfdbe3 100644 --- a/drivers/regulator/mt6358-regulator.c +++ b/drivers/regulator/mt6358-regulator.c @@ -492,7 +492,7 @@ static const struct regulator_ops mt6358_volt_fixed_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .get_voltage_sel = mt6358_get_buck_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, From 8881daaafadbe7fb2b7341d16a3949114409c90c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:47:17 +0800 Subject: [PATCH 2438/3297] Bluetooth: btusb: validate Realtek vendor event length [ Upstream commit df541cd485ff80a5ddc579d99687bc7506df9851 ] btusb_recv_event_realtek() reads the event code at data[0] and the Realtek subevent code at data[2] before deciding whether to consume a vendor event as a coredump. For example, the two-byte event ff 00 contains a complete vendor-event header declaring zero parameters. The old classifier still reads a nonexistent third byte and can misclassify the event as a coredump if the adjacent byte is 0x34. Require the HCI event header and first parameter to be present before inspecting the Realtek subevent code. Short events continue through the normal HCI receive path, which owns their protocol validation. Fixes: 044014ce85a1 ("Bluetooth: btrtl: Add Realtek devcoredump support") Signed-off-by: Pengpeng Hou Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btusb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b9e5adecfed60..fea3cb502a4e0 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2716,7 +2716,9 @@ static int btusb_setup_realtek(struct hci_dev *hdev) static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb) { - if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) { + if (skb->len >= HCI_EVENT_HDR_SIZE + 1 && + skb->data[0] == HCI_VENDOR_PKT && + skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) { struct rtk_dev_coredump_hdr hdr = { .code = RTK_DEVCOREDUMP_CODE_MEMDUMP, }; From 4264dbc84ca0ac9f78583b7775d66f11b2f4e722 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 6 Jul 2026 14:06:27 +0200 Subject: [PATCH 2439/3297] net: phy: marvell: fix return code [ Upstream commit 7d8ca62d6a9ef593780161586b4efc811ac094fe ] Return the correct error code, not the value written to the register. Fixes: a219912e0fec ("net: phy: marvell: implement config_inband() method") Signed-off-by: Michael Walle Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260706120637.1947685-1-mwalle@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/marvell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index c248c90510ae5..f191b8c432188 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -753,7 +753,7 @@ static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes) err = phy_modify(phydev, MII_M1111_PHY_EXT_SR, MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr); if (err < 0) - return extsr; + return err; return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR, BMCR_ANENABLE, bmcr); From 9f4f75df77c8949aae48a6ba73000a81362d0c65 Mon Sep 17 00:00:00 2001 From: Danielle Ratson Date: Tue, 7 Jul 2026 11:03:04 +0300 Subject: [PATCH 2440/3297] netlink: specs: rt-link: convert bridge port flag attributes to u8 [ Upstream commit f6e3b21608e974c4aaa4cfd73a239dacf1d8a9a3 ] A number of IFLA_BRPORT_* attributes are documented in the rt-link spec as having the "flag" type, i.e. a payload-less NLA_FLAG attribute whose meaning is presence-only. This does not match the kernel, which emits these attributes with nla_put_u8() and validates them as NLA_U8 in br_port_policy[]. The values are not mere presence flags but carry a u8 payload (0/1). Convert these bridge port attributes from "flag" to "u8" so the spec reflects the actual wire format. Fixes: 077b6022d24b ("doc/netlink/specs: Add sub-message type to rt_link family") Reviewed-by: Petr Machata Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: Danielle Ratson Link: https://patch.msgid.link/a57cdfcfc4a6dcb92106c25b4dde5059fde2bd44.1783236731.git.danieller@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/netlink/specs/rt-link.yaml | 40 ++++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index ae4db7e032770..692b3cd47c310 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1564,31 +1564,31 @@ attribute-sets: type: u32 - name: mode - type: flag + type: u8 - name: guard - type: flag + type: u8 - name: protect - type: flag + type: u8 - name: fast-leave - type: flag + type: u8 - name: learning - type: flag + type: u8 - name: unicast-flood - type: flag + type: u8 - name: proxyarp - type: flag + type: u8 - name: learning-sync - type: flag + type: u8 - name: proxyarp-wifi - type: flag + type: u8 - name: root-id type: binary @@ -1635,34 +1635,34 @@ attribute-sets: type: pad - name: mcast-flood - type: flag + type: u8 - name: mcast-to-ucast - type: flag + type: u8 - name: vlan-tunnel - type: flag + type: u8 - name: bcast-flood - type: flag + type: u8 - name: group-fwd-mask type: u16 - name: neigh-suppress - type: flag + type: u8 - name: isolated - type: flag + type: u8 - name: backup-port type: u32 - name: mrp-ring-open - type: flag + type: u8 - name: mrp-in-open - type: flag + type: u8 - name: mcast-eht-hosts-limit type: u32 @@ -1671,10 +1671,10 @@ attribute-sets: type: u32 - name: locked - type: flag + type: u8 - name: mab - type: flag + type: u8 - name: mcast-n-groups type: u32 @@ -1683,7 +1683,7 @@ attribute-sets: type: u32 - name: neigh-vlan-suppress - type: flag + type: u8 - name: backup-nhid type: u32 From 1bc55c29cd85818e9052f17deb287d5a11fb817f Mon Sep 17 00:00:00 2001 From: David Lee Date: Tue, 7 Jul 2026 10:44:37 +0000 Subject: [PATCH 2441/3297] net/packet: avoid fanout hook re-registration after unregister [ Upstream commit 50aff80475abd3533eef4320477037e6fcc6b56e ] packet_set_ring() temporarily detaches a socket from packet delivery while reconfiguring its ring. It records the previous running state, clears po->num, unregisters the protocol hook when needed, drops po->bind_lock, and later restores po->num and re-registers the hook from the saved was_running value. That unlocked window can race with NETDEV_UNREGISTER. The notifier can observe the socket as not running, skip __unregister_prot_hook(), and invalidate the per-socket binding by setting po->ifindex to -1 and clearing po->prot_hook.dev. A one-member fanout group can still retain its shared fanout hook device pointer. When packet_set_ring() resumes, re-registering solely from the stale was_running state can re-add the fanout hook after the device has been unregistered. Treat po->ifindex == -1 as an invalidated binding after reacquiring po->bind_lock. This is distinct from ifindex 0, the normal unbound/wildcard state: ifindex -1 marks an existing device binding that was invalidated when the device was unregistered. Restore po->num as before, but do not re-register the hook if device unregister already detached the socket. Fixes: dc99f600698d ("packet: Add fanout support.") Link: https://lore.kernel.org/netdev/20260701113947.23180-1-david.lee@trailofbits.com/ Signed-off-by: David Lee Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260707104440.833129-1-david.lee@trailofbits.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/packet/af_packet.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8b3961fba750b..2989f1481aef4 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, spin_lock(&po->bind_lock); WRITE_ONCE(po->num, num); - if (was_running) + /* + * NETDEV_UNREGISTER may have invalidated the binding while bind_lock + * was dropped above. Do not re-add a fanout hook to a dead device. + */ + if (was_running && READ_ONCE(po->ifindex) != -1) register_prot_hook(sk); spin_unlock(&po->bind_lock); From 992dce02bdabbd9883255ea9b36494e34a7821d7 Mon Sep 17 00:00:00 2001 From: Zhaolong Zhang Date: Tue, 7 Jul 2026 09:06:22 +0800 Subject: [PATCH 2442/3297] bonding: fix devconf_all NULL dereference when IPv6 is disabled [ Upstream commit 1c975de3343cdef506f2eecc833cc1f14b0401c4 ] When booting with the 'ipv6.disable=1' parameter, the devconf_all is never initialized because inet6_init() exits before addrconf_init() is called which initializes it. bond_send_validate(), however, will still call bond_ns_send_all() even ipv6 is indeed disabled. It will lead to NULL derefence of net->ipv6.devconf_all in ip6_pol_route(). BUG: kernel NULL pointer dereference, address: 000000000000000c [...] Workqueue: bond0 bond_arp_monitor [bonding] RIP: 0010:ip6_pol_route+0x69/0x480 [...] Call Trace: ? srso_return_thunk+0x5/0x5f ? __pfx_ip6_pol_route_output+0x10/0x10 fib6_rule_lookup+0xfe/0x260 ? wakeup_preempt+0x8a/0x90 ? srso_return_thunk+0x5/0x5f ? srso_return_thunk+0x5/0x5f ? sched_balance_rq+0x369/0x810 ip6_route_output_flags+0xd7/0x170 bond_ns_send_all+0xde/0x280 [bonding] bond_ab_arp_probe+0x296/0x320 [bonding] ? srso_return_thunk+0x5/0x5f bond_activebackup_arp_mon+0xb4/0x2c0 [bonding] process_one_work+0x196/0x370 worker_thread+0x1af/0x320 ? srso_return_thunk+0x5/0x5f ? __pfx_worker_thread+0x10/0x10 kthread+0xe3/0x120 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x199/0x260 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Fix this by adding ipv6_mod_enabled() condition check in the caller. Fixes: 4e24be018eb9 ("bonding: add new parameter ns_targets") Signed-off-by: Qianheng Peng Signed-off-by: Zhaolong Zhang Reviewed-by: Hangbin Liu Link: https://patch.msgid.link/20260707010622.487333-1-zhangzl2013@126.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e38cae22fe025..52af663e12e0c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3422,7 +3422,8 @@ static void bond_send_validate(struct bonding *bond, struct slave *slave) { bond_arp_send_all(bond, slave); #if IS_ENABLED(CONFIG_IPV6) - bond_ns_send_all(bond, slave); + if (likely(ipv6_mod_enabled())) + bond_ns_send_all(bond, slave); #endif } From 9591042533140dfe6608d9344806d567dcd39d02 Mon Sep 17 00:00:00 2001 From: Aldo Ariel Panzardo Date: Tue, 7 Jul 2026 19:43:14 -0700 Subject: [PATCH 2443/3297] rds: drop incoming messages that cross network namespace boundaries [ Upstream commit 5521ae71e32a8069ed4ca6e792179dc57bc43ab2 ] rds_find_bound() looks up the destination socket using a global rhashtable keyed solely on (addr, port, scope_id). Network namespaces are not part of the key, so a sender in netns A can deliver an incoming message (inc) to a socket that lives in a different netns B. When this happens, inc->i_conn points to an rds_connection whose c_net is netns A, but the receiving rs lives in netns B. Once the child process that created netns A exits, cleanup_net() calls rds_loop_exit_net() -> rds_loop_kill_conns() -> rds_conn_destroy(), freeing that connection. If the survivor socket in netns B still holds the inc, any subsequent dereference of inc->i_conn is a use-after-free. There are two dangerous sites in rds_clear_recv_queue(): 1. inc->i_conn->c_lcong (offset 88 of freed rds_connection, size 200) read via rds_recv_rcvbuf_delta() -- confirmed by KASAN. 2. inc->i_conn->c_trans->inc_free(inc) (function pointer at offset 80) called via rds_inc_put() when the inc refcount reaches zero -- same race window, potential call-through-freed-object primitive. The bug is reachable from unprivileged user namespaces (CLONE_NEWUSER + CLONE_NEWNET), available since Linux 3.8. Fix this by rejecting the delivery in rds_recv_incoming() when the socket returned by rds_find_bound() belongs to a different network namespace than the connection that carried the message. Use the existing rds_conn_net() / sock_net() helpers and net_eq() for the comparison. Fixes: c809195f5523 ("rds: clean up loopback rds_connections on netns deletion") Signed-off-by: Aldo Ariel Panzardo Reviewed-by: Allison Henderson Tested-by: Allison Henderson Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260708024314.601139-1-achender@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/rds/recv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/rds/recv.c b/net/rds/recv.c index 66205d6924bf3..4b12fa4f1e786 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -366,6 +366,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr, goto out; } + /* + * rds_find_bound() uses a global (netns-agnostic) hash table. + * An RDS connection created in netns A can match a socket bound + * in the init netns, delivering inc cross-netns with inc->i_conn + * pointing into netns A. When cleanup_net() then frees that conn, + * any subsequent dereference of inc->i_conn is a use-after-free. + * Drop the inc if the receiving socket lives in a different netns. + */ + if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) { + rds_stats_inc(s_recv_drop_no_sock); + rds_sock_put(rs); + rs = NULL; + goto out; + } + /* Process extension headers */ rds_recv_incoming_exthdrs(inc, rs); From b78547914bee5db4563b552abf736e5b0f4ab20a Mon Sep 17 00:00:00 2001 From: Zhixing Chen Date: Wed, 8 Jul 2026 12:22:44 +0800 Subject: [PATCH 2444/3297] gtp: parse extension headers before reading inner protocol [ Upstream commit 96e37e2f618e931aa97af95e707dcdfb1ec41264 ] GTPv1-U packets may carry a chain of extension headers before the inner IP packet. The receive path already parses and skips these extension headers, but it currently reads the inner protocol before doing so. As a result, the first extension header byte is interpreted as the inner IP version. Packets with extension headers are then dropped before PDP lookup. Parse the extension header chain before calling gtp_inner_proto(), so the inner protocol is read from the actual inner IP header. Fixes: c75fc0b9e5be ("gtp: identify tunnel via GTP device + GTP version + TEID + family") Signed-off-by: Zhixing Chen Link: https://patch.msgid.link/20260708042244.120898-1-running910@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/gtp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 2f8626c3824d6..937d913ce5c33 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -826,13 +826,17 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) if (!pskb_may_pull(skb, hdrlen)) return -1; + gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); + + if (gtp1->flags & GTP1_F_EXTHDR && + gtp_parse_exthdrs(skb, &hdrlen) < 0) + return -1; + if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) { netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n"); return -1; } - gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); - pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid), gtp_proto_to_family(inner_proto)); if (!pctx) { @@ -840,10 +844,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) return 1; } - if (gtp1->flags & GTP1_F_EXTHDR && - gtp_parse_exthdrs(skb, &hdrlen) < 0) - return -1; - return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto); } From c9165e199f56997f2f2deb5d3ec2dfab98dfa288 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Wed, 8 Jul 2026 17:35:34 +0800 Subject: [PATCH 2445/3297] rxrpc: fix io_thread race in rxrpc_wake_up_io_thread() [ Upstream commit 745fb794c3e933c023af9dbb5876a5e16ad2dc71 ] rxrpc_wake_up_io_thread() checks local->io_thread before waking it, but then reloads the pointer for wake_up_process(). local->io_thread is cleared with WRITE_ONCE() when the I/O thread exits, so the second load can see NULL even if the first load did not. Take a READ_ONCE() snapshot and use it for both the NULL check and the wake_up_process() call, as rxrpc_encap_rcv() already does. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Signed-off-by: Xuanqiang Luo Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708093534.53486-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/rxrpc/ar-internal.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index d2b31d15851b9..38b0997ce0cb3 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1266,9 +1266,11 @@ int rxrpc_io_thread(void *data); void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb); static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local) { - if (!local->io_thread) + struct task_struct *io_thread = READ_ONCE(local->io_thread); + + if (!io_thread) return; - wake_up_process(READ_ONCE(local->io_thread)); + wake_up_process(io_thread); } static inline bool rxrpc_protocol_error(struct sk_buff *skb, enum rxrpc_abort_reason why) From 26ac2d3602347f0377fbcd5214bc28a9d735ae68 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:10:25 +0800 Subject: [PATCH 2446/3297] dpaa2-switch: put MAC endpoint device on disconnect [ Upstream commit 4c1eabbef7a1707635652e956e39db1269c3af2b ] fsl_mc_get_endpoint() returns the MAC endpoint device with a reference taken through device_find_child(). The switch port connect path stores that device in mac->mc_dev and keeps it for the lifetime of the connected MAC object. However, the disconnect path only closes the MAC and frees the dpaa2_mac object. It does not drop the endpoint device reference stored in mac->mc_dev, so every successful connect leaks that device reference when the MAC is later disconnected. Drop the endpoint device reference before freeing the dpaa2_mac object. Fixes: 84cba72956fd ("dpaa2-switch: integrate the MAC endpoint support") Signed-off-by: Guangshuo Li Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708111025.749311-1-lgs201920130244@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 789a46f6938ee..846300b77e8ac 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1511,6 +1511,7 @@ static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) dpaa2_mac_disconnect(mac); dpaa2_mac_close(mac); + put_device(&mac->mc_dev->dev); kfree(mac); } From 46e3bed4b071095ecc9384a7b349e1908728531f Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 8 Jul 2026 19:16:16 +0800 Subject: [PATCH 2447/3297] net: airoha: Fix potential use-after-free in airoha_ppe_deinit() [ Upstream commit 2484568a335cd7bda951c75b3a7d95ea36161ae7 ] airoha_ppe_deinit() replaces the NPU pointer with NULL via rcu_replace_pointer() but does not wait for existing RCU readers to exit before calling ppe_deinit() and airoha_npu_put(). This can cause a use-after-free if a reader in an RCU read-side critical section still holds a reference to the NPU when it is freed. The init path (airoha_ppe_init) already calls synchronize_rcu() after rcu_assign_pointer(), but the deinit path introduced in commit 6abcf751bc08 ("net: airoha: Fix schedule while atomic in airoha_ppe_deinit()") omitted the matching barrier when switching from rcu_read_lock()/rcu_dereference() to rcu_replace_pointer(). Add synchronize_rcu() before ppe_deinit() to ensure all existing RCU readers have completed before the NPU resources are released. Fixes: 6abcf751bc084804a9e5b3051442e8a2ce67f48a ("net: airoha: Fix schedule while atomic in airoha_ppe_deinit()") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178351022574.97989.6880403520276841703@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_ppe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 798839aa1010b..8defd194568f2 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1575,6 +1575,7 @@ void airoha_ppe_deinit(struct airoha_eth *eth) npu = rcu_replace_pointer(eth->npu, NULL, lockdep_is_held(&flow_offload_mutex)); if (npu) { + synchronize_rcu(); npu->ops.ppe_deinit(npu); airoha_npu_put(npu); } From f112df0744e2d77baa68eeebb860021bbaaa022a Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:17:37 +0800 Subject: [PATCH 2448/3297] dpaa2-eth: put MAC endpoint device on disconnect [ Upstream commit b4b201cc93ff70150853aba03e14d314d1980ca0 ] fsl_mc_get_endpoint() returns the MAC endpoint device with a reference taken through device_find_child(). The Ethernet connect path stores that device in mac->mc_dev and keeps it for the lifetime of the connected MAC object. However, the disconnect path only disconnects and closes the MAC before freeing the dpaa2_mac object. It does not drop the endpoint device reference stored in mac->mc_dev, so every successful connect leaks that device reference when the MAC is later disconnected. Drop the endpoint device reference after closing the MAC and before freeing the dpaa2_mac object. Fixes: 719479230893 ("dpaa2-eth: add MAC/PHY support through phylink") Signed-off-by: Guangshuo Li Reviewed-by: Ioana Ciornei Reviewed-by: Ioana Ciornei Link: https://patch.msgid.link/20260708111738.750391-1-lgs201920130244@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 18d86badd6ea7..6fd478572ef8f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4732,6 +4732,7 @@ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) dpaa2_mac_disconnect(mac); dpaa2_mac_close(mac); + put_device(&mac->mc_dev->dev); kfree(mac); } From 76fc5604308a109bf5838c2a0a0eb3ac6819f1ea Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 8 Jul 2026 19:35:29 +0800 Subject: [PATCH 2449/3297] net: airoha: Fix DMA direction for NPU mailbox buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6f884eb87a79e0c482baef2ad96c96b81d024235 ] airoha_npu_send_msg() always maps the mailbox buffer with DMA_TO_DEVICE, but some callers expect the NPU to write response data back into the same buffer: - airoha_npu_wlan_msg_get() (NPU_OP_GET): NPU writes response into the buffer, then the caller reads it via memcpy() - airoha_npu_ppe_stats_setup() (NPU_OP_SET): NPU writes back npu_stats_addr field in the response On non-cache-coherent architectures like EN7581 (Cortex-A53 without hardware cache coherency for NPU DMA), DMA_TO_DEVICE unmap is a no-op — it does not invalidate the CPU cache. If the NPU-written cache line is still present in the CPU cache when the caller reads the buffer, the CPU observes stale data instead of the NPU response. This is a timing-sensitive bug: small mailbox buffers (~24 bytes) typically fit in a single cache line and may survive in the cache until the caller reads them, producing silent data corruption rather than a crash. The bug is more likely to trigger when the caller reads the response immediately after dma_unmap_single() without intervening cache-evicting operations. Fix by using DMA_BIDIRECTIONAL for both map and unmap, which ensures dma_unmap_single() invalidates the CPU cache on non-coherent systems. The mailbox buffers are small so there is no performance concern. Fixes: c52918744ee1e49cea86622a2633b9782446428f ("net: airoha: npu: Move memory allocation in airoha_npu_send_msg() caller") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178351055214.98729.11403147818632027428@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_npu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c index 8c883f2b2d36b..d6547834d3c78 100644 --- a/drivers/net/ethernet/airoha/airoha_npu.c +++ b/drivers/net/ethernet/airoha/airoha_npu.c @@ -154,7 +154,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, dma_addr_t dma_addr; int ret; - dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE); + dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL); ret = dma_mapping_error(npu->dev, dma_addr); if (ret) return ret; @@ -177,7 +177,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, spin_unlock_bh(&npu->cores[core].lock); - dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE); + dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL); return ret; } From d053eb7e09e10cbdca3fca8b35c1017d438091b2 Mon Sep 17 00:00:00 2001 From: Guanghui Feng Date: Thu, 16 Jul 2026 22:16:22 +0800 Subject: [PATCH 2450/3297] iommu/amd: Wait for completion instead of returning early in iommu_completion_wait() [ Upstream commit 1e75a8255f11c81fb07e81e5029cfd75804350a0 ] need_sync is a per-IOMMU flag shared by all domains and devices behind that IOMMU. It is set whenever a command is queued with sync == true and cleared when a completion-wait (CWAIT) command is queued. However, a cleared need_sync only means that a covering CWAIT has been queued, not that all previously queued commands have actually completed in hardware. iommu_completion_wait() read need_sync locklessly and returned early when it was false. This breaks the "block until all previously queued commands have completed" contract in a multi-CPU scenario: CPU2: queue inv-B => need_sync = true CPU1: queue CWAIT(N); need_sync = false; then wait_on_sem(N) CPU2: read need_sync == false => return 0 (no wait!) CPU2 returns without waiting for any sequence number even though its inv-B may not have completed yet (CWAIT(N), queued after inv-B, has not been signaled). CPU2 then proceeds to, for example, free page-table pages while the IOMMU can still walk stale translations, opening a use-after-free window. This is a logical race in the meaning of the flag, not a memory-visibility issue, so barriers alone do not help. Fix it without losing the optimization of avoiding redundant CWAIT commands: take iommu->lock before testing need_sync, and when it is false do not return early but wait for the last allocated sequence number (cmd_sem_val). Since need_sync == false implies no sync command was queued after the last CWAIT, that CWAIT is FIFO-ordered after every not-yet-completed command, so waiting for its sequence number guarantees all prior commands (possibly queued by another CPU) have completed. The common path with pending work is unchanged and no extra hardware command is issued. Signed-off-by: Guanghui Feng Fixes: 815b33fdc279 ("x86/amd-iommu: Cleanup completion-wait handling") Reviewed-by: Vasant Hegde Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/iommu/amd/iommu.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index c458eec8a5bba..12302647e2675 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -1408,11 +1408,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu) int ret; u64 data; - if (!iommu->need_sync) - return 0; - raw_spin_lock_irqsave(&iommu->lock, flags); + if (!iommu->need_sync) { + /* + * No command has been queued since the last completion-wait. + * A concurrent CPU may have already queued that CWAIT and + * cleared need_sync; need_sync == false only means a covering + * CWAIT is queued, not that all prior commands have completed. + * Wait for the last allocated sequence number so that any + * command queued before this call (possibly on another CPU) + * is guaranteed to have completed before returning. + */ + data = iommu->cmd_sem_val; + raw_spin_unlock_irqrestore(&iommu->lock, flags); + return wait_on_sem(iommu, data); + } + data = get_cmdsem_val(iommu); build_completion_wait(&cmd, iommu, data); @@ -1422,9 +1434,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu) if (ret) return ret; - ret = wait_on_sem(iommu, data); - - return ret; + return wait_on_sem(iommu, data); } static void domain_flush_complete(struct protection_domain *domain) From 0f7eaeb950adb77f71beb546e5ab30f90b41fe6f Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 11 Jul 2026 14:03:02 -0700 Subject: [PATCH 2451/3297] wifi: mac80211: tear down new links on vif update error path [ Upstream commit 952c02b33f56207a160421bcd61e7ac53c9c59ae ] When ieee80211_vif_update_links() adds new links it allocates a link container for each and calls ieee80211_link_init() (which registers the per-link debugfs files with file->private_data pointing into the container) and ieee80211_link_setup(). If the subsequent drv_change_vif_links() fails, the error path restores the old pointers and jumps to 'free', which frees the new containers but never removes their debugfs entries or stops the links. The debugfs files survive with file->private_data dangling at the freed container, so a later open()+read() (e.g. link-1/txpower) dereferences freed memory in ieee80211_if_read_link(), a use-after-free. The removal path already dismantles links correctly via ieee80211_tear_down_links(), which removes each link's keys and debugfs entries and calls ieee80211_link_stop(); the add path on the error branch does not. Commit be1ba9ed221f ("wifi: mac80211: avoid weird state in error path") hardened this same error path for the link-removal case (new_links == 0) but left the newly-added links' teardown unaddressed. drv_change_vif_links() can fail at runtime on MLO drivers (internal allocation / queue / firmware command failures). Remove the new links' debugfs entries and stop them before freeing. BUG: KASAN: slab-use-after-free in ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127) Read of size 8 at addr ffff888011290000 by task exploit/145 Call Trace: ... ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127) short_proxy_read (fs/debugfs/file.c:373) vfs_read (fs/read_write.c:572) ksys_read (fs/read_write.c:716) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) ... Oops: general protection fault, probably for non-canonical address 0xdffffc000000000a RIP: 0010:ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127) Kernel panic - not syncing: Fatal exception Fixes: 170cd6a66d9a ("wifi: mac80211: add netdev per-link debugfs data and driver hook") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260711210302.2098404-1-xmei5@asu.edu Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/link.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 05b0472bda40e..235e370c2b59e 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -382,6 +382,10 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, memcpy(sdata->link, old_data, sizeof(old_data)); memcpy(sdata->vif.link_conf, old, sizeof(old)); ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links); + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + ieee80211_link_debugfs_remove(&links[link_id]->data); + ieee80211_link_stop(&links[link_id]->data); + } /* and free (only) the newly allocated links */ memset(to_free, 0, sizeof(links)); goto free; From 3b1d4fc3b73ea6faf008a0996ce6190c6e43efc3 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 22:34:08 +0800 Subject: [PATCH 2452/3297] nfp: Check resource mutex allocation [ Upstream commit a61b4db34a753bdf5c9e77a7f3d3dddd41dcfacc ] nfp_cpp_resource_find() allocates a CPP mutex handle for the matching resource-table entry and then reports success. nfp_resource_try_acquire() immediately passes that handle to nfp_cpp_mutex_trylock(). However, nfp_cpp_mutex_alloc() returns NULL on failure. If that happens for a matching table entry, the resource lookup still returns success and the following trylock dereferences a NULL mutex pointer while opening the resource. nfp_resource_acquire() already treats failure to allocate the table mutex as -ENOMEM. Do the same for the resource mutex and fail the lookup before publishing the rest of the resource handle. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: f01a2161577d ("nfp: add support for resources") Signed-off-by: Ruoyu Wang Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708143408.3168425-1-ruoyuw560@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c index 279ea0b569557..55525f45e447b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c @@ -96,6 +96,9 @@ static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res) res->mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET, addr, key); + if (!res->mutex) + return -ENOMEM; + res->cpp_id = NFP_CPP_ID(entry.region.cpp_target, entry.region.cpp_action, entry.region.cpp_token); From 59cbe6cfa0fa23c192351cc284e30707309f6741 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 22:34:15 +0800 Subject: [PATCH 2453/3297] wan: wanxl: Only reset hardware after BAR mapping [ Upstream commit 91957b89da995607cb654b1f9a3c126ddbaee10f ] wanxl_pci_init_one() stores the freshly allocated card in driver data before the PLX BAR is mapped. Several early probe failures then unwind through wanxl_pci_remove_one(), including failure to allocate the coherent status area or to restore the DMA mask. wanxl_pci_remove_one() unconditionally calls wanxl_reset(), and wanxl_reset() dereferences card->plx. On those early failures card->plx is still NULL, so the error path can dereference a NULL MMIO pointer. Only issue the hardware reset once the BAR mapping exists. The remaining cleanup in wanxl_pci_remove_one() already checks whether later resources were allocated. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260708143415.3169358-1-ruoyuw560@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/wan/wanxl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index 5a9e262188efe..c38dd741401e1 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -514,7 +514,8 @@ static void wanxl_pci_remove_one(struct pci_dev *pdev) if (card->irq) free_irq(card->irq, card); - wanxl_reset(card); + if (card->plx) + wanxl_reset(card); for (i = 0; i < RX_QUEUE_LENGTH; i++) if (card->rx_skbs[i]) { From a0980682d84d2466d916628f3d6694955b64e80d Mon Sep 17 00:00:00 2001 From: Enrico Zanda Date: Wed, 8 Jul 2026 16:22:42 +0100 Subject: [PATCH 2454/3297] vhost-net: fix TX stall when vhost owns virtio-net header [ Upstream commit 3c0d10f233f19153f81fef685b5c6716776a5af3 ] When vhost owns the virtio-net header, i.e. when VHOST_NET_F_VIRTIO_NET_HDR is negotiated, sock_hlen is 0, meaning that no header will be forwarded to the TAP device. In the current vhost_net_build_xdp() implementation, when sock_hlen == 0, the gso pointer can point at the start of the Ethernet frame instead of a virtio-net header. This results in a wrong interpretation of the destination MAC address bytes as struct virtio_net_hdr fields. This can, for some MAC addresses, trigger -EINVAL and return early before the TX descriptor is completed, which can stall vhost-net TX. Before 97b2409f28e0, the gso pointer was set to the zeroed padding area, using it as a synthetic virtio-net header. Restore that behavior. Fixes: 97b2409f28e0 ("vhost-net: reduce one userspace copy when building XDP buff") Signed-off-by: Enrico Zanda Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260708152242.2268848-1-enrico.zanda@arm.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/vhost/net.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index b2b90732b1d59..89d5438ba7af9 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -724,10 +724,12 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq, goto err; } - gso = buf + pad - sock_hlen; - - if (!sock_hlen) + if (!sock_hlen) { memset(buf, 0, pad); + gso = buf; + } else { + gso = buf + pad - sock_hlen; + } if ((gso->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && vhost16_to_cpu(vq, gso->csum_start) + From d21464d93f8ba464dc3d7b4b31c6e0adcd9f659c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?HE=20WEI=20=28=E3=82=AE=E3=82=AB=E3=82=AF=29?= Date: Wed, 15 Jul 2026 22:57:11 +0900 Subject: [PATCH 2455/3297] wifi: mwifiex: bound uAP association event IEs to the event buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f0858bfc7d3cab411a447b88e3ef970e575032c9 ] mwifiex_process_uap_event() handles EVENT_UAP_STA_ASSOC by exposing the (re)association request IEs that the firmware copies into the event: sinfo->assoc_req_ies = &event->data[len]; len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control; sinfo->assoc_req_ies_len = le16_to_cpu(event->len) - (u16)len; event->len is supplied by the device firmware and is never validated, and the subtraction is unchecked. assoc_req_ies points into adapter->event_body[MAX_EVENT_SIZE], a fixed-size array embedded in the kmalloc()'d struct mwifiex_adapter. On the ap_11n_enabled path mwifiex_set_sta_ht_cap() walks these IEs with cfg80211_find_ie(), whose for_each_element() loop dereferences each element header. A firmware-reported event->len larger than the bytes actually received makes assoc_req_ies_len describe IEs that extend past event_body, so the walk reads out of the adapter slab object, a slab-out-of-bounds read (KASAN: slab-out-of-bounds in cfg80211_find_ie). An event->len smaller than the header instead makes the int subtraction negative, which wraps to a huge size_t when stored in assoc_req_ies_len. The same length is handed to cfg80211_new_sta(), so a more modest over-claim can also copy stale event_body bytes into the NL80211_CMD_NEW_STATION notification. A malicious or malfunctioning mwifiex device (USB/SDIO/PCIe) can deliver such an event while the interface is in AP/uAP mode. Validate event->len before use: reject a length that underflows the header or that would place the IEs outside the event_body[] buffer the event was copied into. event->len here is struct mwifiex_assoc_event.len, a payload field internal to this event, not the transport frame length, so it is validated in this handler rather than at the generic MWIFIEX_TYPE_EVENT receive path, which only sees the event cause and the transport frame length. The bound is against event_body[MAX_EVENT_SIZE] rather than the actually-received length because the transports store the event differently (USB and SDIO leave the 4-byte event header in event_skb, PCIe strips it via skb_pull), whereas event_body is the single fixed buffer all of them copy the event into. This is the event-path analogue of the receive-path bounds checks added in commit 119585281617 ("wifi: mwifiex: Fix OOB and integer underflow when rx packets"). Fixes: e568634ae7ac ("mwifiex: add AP event handling framework") Signed-off-by: HE WEI (ギカク) Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260715135711.34688-1-skyexpoc@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- .../net/wireless/marvell/mwifiex/uap_event.c | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c index 245cb99a3daad..fc9da9006a782 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_event.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -123,11 +123,31 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) len = ETH_ALEN; if (len != -1) { + u16 evt_len = le16_to_cpu(event->len); + sinfo->assoc_req_ies = &event->data[len]; len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control; - sinfo->assoc_req_ies_len = - le16_to_cpu(event->len) - (u16)len; + + /* + * event->len is reported by the device firmware + * and is not otherwise validated. Reject a + * length that underflows the header, or that + * would place the association request IEs + * outside the fixed-size event_body[] buffer the + * event was copied into; otherwise the IE walk + * in mwifiex_set_sta_ht_cap() reads past + * event_body and out of the adapter slab object. + */ + if (evt_len < len || + (u8 *)&event->frame_control + evt_len > + adapter->event_body + MAX_EVENT_SIZE) { + mwifiex_dbg(adapter, ERROR, + "invalid STA assoc event length\n"); + kfree(sinfo); + return -1; + } + sinfo->assoc_req_ies_len = evt_len - (u16)len; } } cfg80211_new_sta(priv->netdev, event->sta_addr, sinfo, From e5ebe8544df1a1c3611739a8622156094fe470df Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:46:13 +0800 Subject: [PATCH 2456/3297] iommu/amd: Bound the early ACPI HID map [ Upstream commit fb80117fddb5b477218dc99bb53911b72c3847f8 ] The ivrs_acpihid command-line parser appends entries to a fixed four-element early_acpihid_map array. Unlike the sibling IOAPIC and HPET parsers, it does not reject a fifth entry before incrementing the map size. Check the capacity at the common found label before parsing the HID and UID or writing the entry. Fixes: ca3bf5d47cec ("iommu/amd: Introduces ivrs_acpihid kernel parameter") Signed-off-by: Pengpeng Hou Reviewed-by: Ankit Soni Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/iommu/amd/init.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 32175daa3dd8a..69170146d4421 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -3860,6 +3860,12 @@ static int __init parse_ivrs_acpihid(char *str) return 1; found: + if (early_acpihid_map_size == EARLY_MAP_SIZE) { + pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n", + str); + return 1; + } + p = acpiid; hid = strsep(&p, ":"); uid = p; From d06fea9b85f038690f55e72fe0c45e113715a85a Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Tue, 21 Jul 2026 17:34:10 +0800 Subject: [PATCH 2457/3297] iommu/intel: Fix out-of-bounds memset in dmar_latency_disable() [ Upstream commit 754f8efe45f87e3a9c6871b645b2f9d46d1b407b ] dmar_latency_disable() intends to zero out only the single latency_statistic entry for the given type, but the memset size was computed as sizeof(*lstat) * DMAR_LATENCY_NUM, which clears the entire array starting from &lstat[type]. When type > 0, this writes beyond the end of the allocated array, corrupting adjacent memory. Fix by using sizeof(*lstat) to clear only the target entry. Fixes: 55ee5e67a59a ("iommu/vt-d: Add common code for dmar latency performance monitors") Signed-off-by: Li RongQing Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- drivers/iommu/intel/perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/intel/perf.c b/drivers/iommu/intel/perf.c index dceeadc3ee7cd..6afb55073e9f1 100644 --- a/drivers/iommu/intel/perf.c +++ b/drivers/iommu/intel/perf.c @@ -63,7 +63,7 @@ void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type) return; spin_lock_irqsave(&latency_lock, flags); - memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM); + memset(&lstat[type], 0, sizeof(*lstat)); spin_unlock_irqrestore(&latency_lock, flags); } From b5fcd1da05622d63ad33808525f334a2797c356a Mon Sep 17 00:00:00 2001 From: Andrew Pope Date: Fri, 17 Jul 2026 11:17:51 +1000 Subject: [PATCH 2458/3297] wifi: mac80211: recalculate TIM when a station enters power save [ Upstream commit a007a384c9eb17610f53a53e2f59944c31f1565a ] When an AP buffers frames for a station on its per-station TXQs and the station subsequently enters power save, sta_ps_start() records the buffered TIDs in txq_buffered_tids but does not update the TIM. The station's TIM bit is only ever set when a further frame is buffered while the station is already asleep (ieee80211_tx_h_unicast_ps_buf() -> sta_info_recalc_tim()). If no further downlink frame arrives for that station the beacon TIM never advertises the buffered traffic. A station relying on the TIM then remains in doze indefinitely on top of a non-empty queue. Its TXQs were removed from the scheduler's active list at PS entry, nothing pages it, and the flow deadlocks until an unrelated event wakes the station. Recalculate the TIM at the end of sta_ps_start(), so traffic already buffered at PS entry is advertised immediately. sta_info_recalc_tim() already consults txq_buffered_tids, which is updated above, and is safe in this context (it is already called from equivalent paths such as the tx handlers and ieee80211_handle_filtered_frame()). Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation") Signed-off-by: Andrew Pope Link: https://patch.msgid.link/20260717011751.79524-1-andrew.pope@morsemicro.com [add wifi: subject prefix] Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/rx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0a986607e6592..13b7432d732f8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1612,6 +1612,8 @@ static void sta_ps_start(struct sta_info *sta) else clear_bit(tid, &sta->txq_buffered_tids); } + + sta_info_recalc_tim(sta); } static void sta_ps_end(struct sta_info *sta) From f5b8b8ccf9a4a4386eba68155b17c45e441dff9c Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Wed, 8 Jul 2026 16:36:49 +0000 Subject: [PATCH 2459/3297] pds_core: reject component parameter in legacy firmware update [ Upstream commit 7be2552e601c247a328a5aba6fc06ac844b94a16 ] The legacy firmware update path does not support per-component updates. If a user specifies a component parameter with devlink flash, reject the request with -EOPNOTSUPP rather than silently ignoring the component parameter and flashing the entire firmware image. Fixes: 49ce92fbee0b ("pds_core: add FW update feature to devlink") Signed-off-by: Nikhil P. Rao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708163649.128620-1-nikhil.rao@amd.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index 621791a3c543b..e35572f099c88 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -89,6 +89,12 @@ int pdsc_dl_flash_update(struct devlink *dl, { struct pdsc *pdsc = devlink_priv(dl); + if (params->component) { + NL_SET_ERR_MSG_MOD(extack, + "Component update not supported by this device"); + return -EOPNOTSUPP; + } + return pdsc_firmware_update(pdsc, params->fw, extack); } From e695cb9becbbb6fc6e6bf334c828f3fee37e69a6 Mon Sep 17 00:00:00 2001 From: Steven Price Date: Fri, 3 Jul 2026 14:48:35 +0100 Subject: [PATCH 2460/3297] arm64: Correct value returned by ESR_ELx_FSC_ADDRSZ_nL() [ Upstream commit b877075d0baa22c225842c2f19e3ea0a9cbcbe39 ] Address size fault, level -1 is encoded as 0b101001 or 0x29 according to the Arm ARM. Correct the value to match the spec. This also matches the offset of "level -1 address size fault" in the fault_info array in fault.c. Fixes: fb8a3eba9c81 ("KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid") Signed-off-by: Steven Price Reviewed-by: Marc Zyngier Signed-off-by: Will Deacon Signed-off-by: Sasha Levin --- arch/arm64/include/asm/esr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index e1deed8244645..31058567eee46 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -130,7 +130,7 @@ * Annoyingly, the negative levels for Address size faults aren't laid out * contiguously (or in the desired order) */ -#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x25 : 0x2C) +#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x29 : 0x2C) #define ESR_ELx_FSC_ADDRSZ_L(n) ((n) < 0 ? ESR_ELx_FSC_ADDRSZ_nL(n) : \ (ESR_ELx_FSC_ADDRSZ + (n))) From f97d199287689d9af903488a9f9f989e28a63fa8 Mon Sep 17 00:00:00 2001 From: Prashanth Kumar KR Date: Thu, 9 Jul 2026 15:20:06 +0530 Subject: [PATCH 2461/3297] amd-xgbe: fix MAC_AUTO_SW handling in CL37 AN [ Upstream commit 4bf22afe53a1de4b44b04cf677fd5199089cbdff ] MAC_AUTO_SW (VR_MII_DIG_CTRL1 bit 9) enables automatic XPCS speed mode switching after CL37 auto-negotiation and is only meaningful in SGMII MAC mode. The original code unconditionally set this bit on every call to xgbe_an37_set(), including when called from xgbe_an37_disable() with enable=false. This left MAC_AUTO_SW=1 after AN was disabled, causing the XPCS to autonomously switch speed from stale AN state during subsequent mode changes, breaking SGMII speed negotiation on 1G copper SFP modules. Patrick: This was breaking negotiation for all 1G SFP modules, not just copper modules. Fixes: 42fd432fe6d3 ("amd-xgbe: align CL37 AN sequence as per databook") Reported-by: Patrick Oppenlander Link: https://lore.kernel.org/netdev/CAEg67GmFS0Q4oSZkz8zWdOzckSth9_vBPiOy6a7-d697C2w2Xg@mail.gmail.com Signed-off-by: Prashanth Kumar KR Tested-by: Patrick Oppenlander Link: https://patch.msgid.link/20260709095006.3683940-1-prashanthkumar.k.r@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 7675bb98f0295..c2d22fdb6c182 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -267,9 +267,14 @@ static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable, XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg); - reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL); - reg |= XGBE_VEND2_MAC_AUTO_SW; - XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg); + if (pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) { + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL); + if (enable) + reg |= XGBE_VEND2_MAC_AUTO_SW; + else + reg &= ~XGBE_VEND2_MAC_AUTO_SW; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg); + } } static void xgbe_an37_restart(struct xgbe_prv_data *pdata) From 4122792923312ee6cfda32f26f30597b85113181 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 9 Jul 2026 18:31:39 +0000 Subject: [PATCH 2462/3297] soreuseport: Clear sk_reuseport_cb before failure in sk_clone(). [ Upstream commit 98da8ce87dd561f08fbe44f75865edc5d9b2ba5f ] When sk_clone() fails, sk_destruct() is called for the new socket. If the parent socket has sk->sk_reuseport_cb, the child will call reuseport_detach_sock() for the reuseport group. Let's clear sk->sk_reuseport_cb before any failure path in sk_clone(). Note that this was not a problem before the cited commit because reuseport_detach_sock() did nothing if the socket was not found in the reuseport array. Fixes: 5dc4c4b7d4e8 ("bpf: Introduce BPF_MAP_TYPE_REUSEPORT_SOCKARRAY") Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260709032007.9E4D61F000E9@smtp.kernel.org/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Willem de Bruijn Reviewed-by: Jason Xing Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709183315.965751-2-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/sock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/sock.c b/net/core/sock.c index c52cec0bf942c..9803926942c55 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2525,6 +2525,8 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) cgroup_sk_clone(&newsk->sk_cgrp_data); + RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); + rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter != NULL) @@ -2547,8 +2549,6 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) goto free; } - RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); - if (bpf_sk_storage_clone(sk, newsk)) goto free; From 245bfe72a5ad489f4ea4bb87425b661b0f306564 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 9 Jul 2026 18:31:40 +0000 Subject: [PATCH 2463/3297] net: Call net_enable_timestamp() before failure in sk_clone(). [ Upstream commit d50557779257a00162411e3048d82971ff1f644c ] When sk_clone() fails, sk_destruct() is called for the new socket. If the parent socket has SK_FLAGS_TIMESTAMP in sk->sk_flags, net_disable_timestamp() is called for the child socket even though net_enable_timestamp() is not called for it. Let's call net_enable_timestamp() before any failure path in sk_clone(). Fixes: 704da560c0a0 ("tcp: update the netstamp_needed counter when cloning sockets") Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260709032007.9E4D61F000E9@smtp.kernel.org/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Willem de Bruijn Reviewed-by: Jason Xing Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709183315.965751-3-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/sock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/core/sock.c b/net/core/sock.c index 9803926942c55..5a658606c50e8 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2527,6 +2527,9 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); + if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP) + net_enable_timestamp(); + rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter != NULL) @@ -2575,9 +2578,6 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) if (newsk->sk_prot->sockets_allocated) sk_sockets_allocated_inc(newsk); - - if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP) - net_enable_timestamp(); out: return newsk; free: From 2d34421bfa261f7e83bea2f2f75fa75e0c3037d1 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Mon, 13 Jul 2026 17:19:11 +0800 Subject: [PATCH 2464/3297] net: txgbe: fix FDIR filter leak on remove [ Upstream commit ecaa37826340520664a4e5522f803ff48fc3f564 ] Perfect FDIR filters can be added while the interface is down and are kept on the software list for later restore. unregister_netdev() only calls ndo_stop when the device is up, so txgbe_fdir_filter_exit() in txgbe_close() is skipped in that case and the filters are leaked on driver remove. Free the filter list from txgbe_remove() as well. Fixes: 4bdb441105dc ("net: txgbe: support Flow Director perfect filters") Signed-off-by: Chenguang Zhao Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260713091911.1614795-1-chenguang.zhao@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 4d20b178af236..76d2940a49fae 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -956,6 +956,7 @@ static void txgbe_remove(struct pci_dev *pdev) netdev = wx->netdev; wx_disable_sriov(wx); unregister_netdev(netdev); + txgbe_fdir_filter_exit(wx); txgbe_remove_phy(txgbe); wx_free_isb_resources(wx); From 11092d79eb2b7c0068382f72fc2416d1786bb2e0 Mon Sep 17 00:00:00 2001 From: HanQuan Date: Mon, 13 Jul 2026 03:20:21 +0000 Subject: [PATCH 2465/3297] sctp: fix auth_chunk_list capacity check in sctp_auth_ep_add_chunkid [ Upstream commit ff04b26794a16a8a879eb4fd2c02c2d6b03850e9 ] sctp_auth_ep_add_chunkid() uses SCTP_NUM_CHUNK_TYPES (20) as the capacity limit for ep->auth_chunk_list, allowing it to hold up to 20 chunk entries (param_hdr.length up to 24). However, the copy destination asoc->c.auth_chunks in struct sctp_cookie is only SCTP_AUTH_MAX_CHUNKS (16) entries (20 bytes). When more than 16 chunks are added, sctp_association_init() memcpy overflows the destination by up to 4 bytes. Fix by using SCTP_AUTH_MAX_CHUNKS as the capacity limit, matching the destination capacity. Fixes: 1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals") Signed-off-by: HanQuan Acked-by: Xin Long Link: https://patch.msgid.link/20260713032021.3491702-1-zhoujian.zja@antgroup.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 82aad477590e2..fbbfa0c279552 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -672,7 +672,7 @@ int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id) /* Check if we can add this chunk to the array */ param_len = ntohs(p->param_hdr.length); nchunks = param_len - sizeof(struct sctp_paramhdr); - if (nchunks == SCTP_NUM_CHUNK_TYPES) + if (nchunks == SCTP_AUTH_MAX_CHUNKS) return -EINVAL; p->chunks[nchunks] = chunk_id; From 19ef775c91c6bf4bd2b60f6616f4e28b621cdd6a Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 18:02:22 +0000 Subject: [PATCH 2466/3297] pds_core: fix deadlock between reset thread and remove [ Upstream commit ab0eec0ff0a421737a37f510ceab5c6ea59cd05a ] pci_reset_function() acquires device_lock before performing the reset. pdsc_remove() is called by the PCI core with device_lock already held. If pdsc_pci_reset_thread() is running when pdsc_remove() is called, destroy_workqueue() will block waiting for the work to complete, while the work is blocked waiting for device_lock - deadlock. Use pci_try_reset_function() which uses pci_dev_trylock() internally. This acquires both the device lock and the PCI config access lock without blocking - if either lock is contended, it returns -EAGAIN immediately. This avoids the deadlock while also ensuring proper config space access serialization during the reset. The pci_dev_get/put calls are also removed as they were unnecessary - the driver-owned workqueue is destroyed in pdsc_remove(), guaranteeing the work completes before remove returns. The PCI core holds its reference to pci_dev throughout the entire unbind sequence. Fixes: 81665adf25d2 ("pds_core: Fix pdsc_check_pci_health function to use work thread") Reported-by: sashiko-bot Closes: https://patchwork.kernel.org/comment/27002369/ Signed-off-by: Nikhil P. Rao Reviewed-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260714180223.1642792-2-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 076dfe2910c77..1978287cf275f 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -603,9 +603,10 @@ void pdsc_pci_reset_thread(struct work_struct *work) struct pdsc *pdsc = container_of(work, struct pdsc, pci_reset_work); struct pci_dev *pdev = pdsc->pdev; - pci_dev_get(pdev); - pci_reset_function(pdev); - pci_dev_put(pdev); + /* Use try variant to avoid deadlock with pdsc_remove(). + * If lock is contended, the watchdog timer will retry. + */ + pci_try_reset_function(pdev); } static void pdsc_check_pci_health(struct pdsc *pdsc) From 9e0f80fac50ab95dd75537c8ecaf5051d01f19b5 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 18:02:23 +0000 Subject: [PATCH 2467/3297] pds_core: fix use-after-free on workqueue during remove [ Upstream commit 0ad134881508c36b65c1a8864f8bec53adbd3327 ] In pdsc_remove(), the workqueue is destroyed before pdsc_teardown() is called. This ordering allows two paths to queue work on the destroyed workqueue: 1. If pdsc_teardown() -> pdsc_devcmd_reset() times out, the error path in pdsc_devcmd_locked() queues health_work. 2. A NotifyQ event can trigger the ISR and queue work before free_irq() is called in pdsc_teardown(). Fix by moving destroy_workqueue() after pdsc_teardown() so the workqueue outlives every queuer; destroy_workqueue() then flushes any work still pending. Draining the queued work also requires ordering the teardown so the resources that work touches are freed last: - In pdsc_qcq_free(), after freeing the interrupt, cancel_work_sync() the queue's work and only then clear qcq->intx, so pdsc_process_adminq()'s read of qcq->intx for interrupt-credit return cannot race with the clear. - Free adminqcq before notifyqcq: the shared adminq ISR is released when adminqcq is freed, and the adminq work accesses notifyqcq, so both must be stopped before notifyqcq is freed. Fixes: 01ba61b55b20 ("pds_core: Add adminq processing and commands") Reported-by: sashiko-bot Closes: https://patchwork.kernel.org/comment/27002369/ Signed-off-by: Nikhil P. Rao Link: https://patch.msgid.link/20260714180223.1642792-3-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/core.c | 14 ++++++++++---- drivers/net/ethernet/amd/pds_core/main.c | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 1978287cf275f..d64e30a204dfc 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -110,7 +110,6 @@ static void pdsc_qcq_intr_free(struct pdsc *pdsc, struct pdsc_qcq *qcq) return; pdsc_intr_free(pdsc, qcq->intx); - qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED; } static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq) @@ -145,6 +144,12 @@ void pdsc_qcq_free(struct pdsc *pdsc, struct pdsc_qcq *qcq) pdsc_qcq_intr_free(pdsc, qcq); + /* Drain any work queued by ISR before it was freed above */ + if (qcq->work.func) + cancel_work_sync(&qcq->work); + + qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED; + if (qcq->q_base) dma_free_coherent(dev, qcq->q_size, qcq->q_base, qcq->q_base_pa); @@ -304,8 +309,11 @@ int pdsc_qcq_alloc(struct pdsc *pdsc, unsigned int type, unsigned int index, static void pdsc_core_uninit(struct pdsc *pdsc) { - pdsc_qcq_free(pdsc, &pdsc->notifyqcq); + /* Free adminqcq first: its work accesses notifyqcq, so we must + * disable its IRQ and drain its work before freeing notifyqcq. + */ pdsc_qcq_free(pdsc, &pdsc->adminqcq); + pdsc_qcq_free(pdsc, &pdsc->notifyqcq); if (pdsc->kern_dbpage) { iounmap(pdsc->kern_dbpage); @@ -478,8 +486,6 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing) { if (!pdsc->pdev->is_virtfn) pdsc_devcmd_reset(pdsc); - if (pdsc->adminqcq.work.func) - cancel_work_sync(&pdsc->adminqcq.work); pdsc_core_uninit(pdsc); diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index c7a2eff576325..accf3a3fe9445 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -436,8 +436,6 @@ static void pdsc_remove(struct pci_dev *pdev) pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev); timer_shutdown_sync(&pdsc->wdtimer); - if (pdsc->wq) - destroy_workqueue(pdsc->wq); mutex_lock(&pdsc->config_lock); set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state); @@ -445,6 +443,9 @@ static void pdsc_remove(struct pci_dev *pdev) pdsc_stop(pdsc); pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); mutex_unlock(&pdsc->config_lock); + + if (pdsc->wq) + destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); From 3a831f40e88d35cc099f28e4b2dc6f0bc502500a Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 20:14:56 +0000 Subject: [PATCH 2468/3297] pds_core: yield the CPU while waiting for the adminq to drain [ Upstream commit a11f0b8a204296fe7db9eaec53441012222cb004 ] pdsc_adminq_wait_and_dec_once_unused() busy-waits for adminq_refcnt to drop to one: while (!refcount_dec_if_one(&pdsc->adminq_refcnt)) cpu_relax(); The refcount is held by pdsc_adminq_post() for the duration of an in-flight command, which can wait up to devcmd_timeout seconds (PDS_CORE_DEVCMD_TIMEOUT is 5) for the hardware to complete. cpu_relax() is not a reschedule point, so on a non-preemptible kernel this loop can spin on the CPU for several seconds, starving other tasks on that core. Add cond_resched() to the loop so the waiter yields to other runnable tasks while it polls, keeping cpu_relax() as the busy-wait hint between checks. Fixes: 7e82a8745b95 ("pds_core: Prevent race issues involving the adminq") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Eric Joyner Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260714201456.1776153-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index d64e30a204dfc..d02e096a2c5fb 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -536,6 +536,7 @@ static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc) dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n", __func__); cpu_relax(); + cond_resched(); } } From 0e87fe52b560fd8a747fc60a4a7e5c27d5fea2b4 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 20:41:45 +0000 Subject: [PATCH 2469/3297] pds_core: order completion reads after the ownership check [ Upstream commit dd6b1cc748cd28147c113f9daa76393916ad9494 ] pdsc_process_adminq() and pdsc_process_notifyq() decide a completion is valid from its ownership field - the color bit for the adminq, the event id for the notifyq - then read the rest of the descriptor, with no barrier in between. On a weakly ordered architecture the CPU may read the payload first. Add dma_rmb() between the ownership read and the payload reads. Fixes: 7e82a8745b95 ("pds_core: Prevent race issues involving the adminq") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Eric Joyner Link: https://patch.msgid.link/20260714204145.1782390-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/adminq.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amd/pds_core/adminq.c b/drivers/net/ethernet/amd/pds_core/adminq.c index 097bb092bdb8c..eadb4b604fbe3 100644 --- a/drivers/net/ethernet/amd/pds_core/adminq.c +++ b/drivers/net/ethernet/amd/pds_core/adminq.c @@ -18,7 +18,13 @@ static int pdsc_process_notifyq(struct pdsc_qcq *qcq) comp = cq_info->comp; eid = le64_to_cpu(comp->event.eid); while (eid > pdsc->last_eid) { - u16 ecode = le16_to_cpu(comp->event.ecode); + u16 ecode; + + /* Order the payload read after the event id, the field the + * driver uses to detect a new completion. + */ + dma_rmb(); + ecode = le16_to_cpu(comp->event.ecode); switch (ecode) { case PDS_EVENT_LINK_CHANGE: @@ -101,6 +107,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq) spin_lock_irqsave(&pdsc->adminq_lock, irqflags); comp = cq->info[cq->tail_idx].comp; while (pdsc_color_match(comp->color, cq->done_color)) { + /* Order the payload reads after the color bit, the field the + * driver uses to detect a new completion. + */ + dma_rmb(); q_info = &q->info[q->tail_idx]; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); From cf0ed2ba202f5c3b300ec1bf7ff0b5d555f7d518 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 21:07:45 +0000 Subject: [PATCH 2470/3297] pds_core: fix auxiliary device add/del races [ Upstream commit bfa33cd513c7ceb93c5a4c30e5662acd73c0a916 ] Two paths add or delete the same slot (pf->vfs[vf_id].padev): a VF's pdsc_reset_done() and the PF's devlink enable_vnet/disable_vnet handler. They serialize on config_lock, but neither guards the slot under it correctly. add() registers and stores a new auxiliary device without first checking the slot, so a second add of an already-populated slot leaks the first device. del() makes that check outside config_lock, so two concurrent dels can both pass it; the first clears the slot, and the second dereferences a NULL pointer. Check and update the slot under config_lock in both paths. Fixes: b699bdc720c0 ("pds_core: specify auxiliary_device to be created") Reported-by: sashiko-bot@kernel.org # Running on a local machine Signed-off-by: Nikhil P. Rao Reviewed-by: Brett Creeley Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260714210745.1785625-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/auxbus.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/auxbus.c b/drivers/net/ethernet/amd/pds_core/auxbus.c index 92f359f2b4492..874812bafc1d3 100644 --- a/drivers/net/ethernet/amd/pds_core/auxbus.c +++ b/drivers/net/ethernet/amd/pds_core/auxbus.c @@ -177,17 +177,21 @@ void pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf, { struct pds_auxiliary_dev *padev; - if (!*pd_ptr) - return; - mutex_lock(&pf->config_lock); + /* A concurrent del may have already torn this device down and + * cleared it. + */ padev = *pd_ptr; + if (!padev) + goto out_unlock; + pds_client_unregister(pf, padev->client_id); auxiliary_device_delete(&padev->aux_dev); auxiliary_device_uninit(&padev->aux_dev); *pd_ptr = NULL; +out_unlock: mutex_unlock(&pf->config_lock); } @@ -210,6 +214,13 @@ int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf, mutex_lock(&pf->config_lock); + /* Nothing to do if the aux device is already present. This also + * guards against a second add overwriting *pd_ptr and leaking the + * first, symmetric with the check in pdsc_auxbus_dev_del(). + */ + if (*pd_ptr) + goto out_unlock; + mask = BIT_ULL(PDSC_S_FW_DEAD) | BIT_ULL(PDSC_S_STOPPING_DRIVER); if (cf->state & mask) { From c984a4184f81046622672f2a18d58df3b07a4e97 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 21:27:13 +0000 Subject: [PATCH 2471/3297] pds_core: check for workqueue allocation failure [ Upstream commit 3a660ca49e2c3807bffe0519db3cff677a5906e0 ] pdsc_init_pf() does not check whether create_singlethread_workqueue() succeeded. Fail probe on failure. The workqueue is set up before the timer and mutexes, so its failure path must unwind only the earlier setup. Fixes: c2dbb0904310 ("pds_core: health timer and workqueue") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Brett Creeley Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260714212713.1788438-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index accf3a3fe9445..a4962dba3140b 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -239,6 +239,10 @@ static int pdsc_init_pf(struct pdsc *pdsc) /* General workqueue and timer, but don't start timer yet */ snprintf(wq_name, sizeof(wq_name), "%s.%d", PDS_CORE_DRV_NAME, pdsc->uid); pdsc->wq = create_singlethread_workqueue(wq_name); + if (!pdsc->wq) { + err = -ENOMEM; + goto err_out_unmap_bars; + } INIT_WORK(&pdsc->health_work, pdsc_health_thread); INIT_WORK(&pdsc->pci_reset_work, pdsc_pci_reset_thread); timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0); @@ -254,7 +258,7 @@ static int pdsc_init_pf(struct pdsc *pdsc) err = pdsc_setup(pdsc, PDSC_SETUP_INIT); if (err) { mutex_unlock(&pdsc->config_lock); - goto err_out_unmap_bars; + goto err_out_shutdown_timer; } err = pdsc_start(pdsc); @@ -306,13 +310,14 @@ static int pdsc_init_pf(struct pdsc *pdsc) pdsc_stop(pdsc); err_out_teardown: pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); -err_out_unmap_bars: +err_out_shutdown_timer: timer_shutdown_sync(&pdsc->wdtimer); if (pdsc->wq) destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); pci_free_irq_vectors(pdsc->pdev); +err_out_unmap_bars: pdsc_unmap_bars(pdsc); err_out_release_regions: pci_release_regions(pdsc->pdev); From 1a10fe1aa9c01f41b389a31906a77d538637c9d9 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Thu, 9 Jul 2026 21:07:18 -0400 Subject: [PATCH 2472/3297] sctp: validate stream count in sctp_process_strreset_inreq() [ Upstream commit 18ae07691d43183d270de8be9dc8e027906015d9 ] When processing a RESET_IN_REQUEST from a peer, sctp_process_strreset_inreq() derives the stream count from the parameter length but does not check whether the resulting RESET_OUT_REQUEST would exceed SCTP_MAX_CHUNK_LEN. The OUT request header (sctp_strreset_outreq, 16 bytes) is 8 bytes larger than the IN request header (sctp_strreset_inreq, 8 bytes). Generally, the IP payload is bounded to 65535 bytes, so the stream list cannot be large enough to trigger the overflow. However, on interfaces with MTU > 65535 (e.g., loopback with IPv6 jumbograms), a stream list that fits within the incoming IN parameter can cause a __u16 overflow in sctp_make_strreset_req() when computing the OUT request size, leading to an undersized skb allocation and a kernel BUG: net/core/skbuff.c:207 skb_panic net/core/skbuff.c:2625 skb_put net/sctp/sm_make_chunk.c:1535 sctp_addto_chunk net/sctp/sm_make_chunk.c:3695 sctp_make_strreset_req net/sctp/stream.c:655 sctp_process_strreset_inreq The local setsockopt path validates the generated reset request size. However, for an incoming-only reset, it accounts for the smaller IN request even though the peer must generate an OUT request with the same stream list. Such a request cannot be completed successfully by the peer. Reject peer IN requests whose corresponding OUT request would exceed SCTP_MAX_CHUNK_LEN. Also tighten the local check so it does not send an IN request that would require an oversized OUT request from the peer. Fixes: 7f9d68ac944e ("sctp: implement sender-side procedures for SSN Reset Request Parameter") Reported-by: AutonomousCodeSecurity@microsoft.com Closes: https://lore.kernel.org/all/20260707203215.2752-1-blbllhy@gmail.com/ Suggested-by: Xin Long Signed-off-by: Cen Zhang (Microsoft) Acked-by: Xin Long Link: https://patch.msgid.link/20260710010718.20318-1-blbllhy@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/stream.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 39b8f5e8ce353..9addcfad467f9 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -308,7 +308,8 @@ int sctp_send_reset_streams(struct sctp_association *asoc, goto out; param_len += str_nums * sizeof(__u16) + - sizeof(struct sctp_strreset_inreq); + (out ? sizeof(struct sctp_strreset_inreq) + : sizeof(struct sctp_strreset_outreq)); } if (param_len > SCTP_MAX_CHUNK_LEN - @@ -639,6 +640,9 @@ struct sctp_chunk *sctp_process_strreset_inreq( nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); str_p = inreq->list_of_streams; + if (nums * sizeof(__u16) + sizeof(struct sctp_strreset_outreq) > + SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_reconf_chunk)) + goto out; for (i = 0; i < nums; i++) { if (ntohs(str_p[i]) >= stream->outcnt) { result = SCTP_STRRESET_ERR_WRONG_SSN; From a8bd8c109da5a87f0c5db0c23cf550d039fde77c Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 15 Jul 2026 16:25:17 +0900 Subject: [PATCH 2473/3297] net: mctp i3c: clean up notifier and buses if driver register fails [ Upstream commit 03d1057305ef17ac3f5936ac1580bc9a1a826e14 ] mctp_i3c_mod_init() registers the I3C bus notifier and then walks the existing buses with i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL) before registering the I3C device driver. If i3c_driver_register() fails, the function returns the error directly, leaving the notifier registered and every mctp_i3c_bus object created for the existing buses allocated. The notifier is left pointing into the module that failed to load and the bus list is leaked. Mirror the module exit path on this failure: unregister the notifier and tear down the buses that were added before returning the error. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: c8755b29b58e ("mctp i3c: MCTP I3C driver") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Acked-by: Jeremy Kerr Link: https://patch.msgid.link/20260715072517.13216-1-mhun512@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/mctp/mctp-i3c.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c index c678f79aa3561..c5d0bc517fea9 100644 --- a/drivers/net/mctp/mctp-i3c.c +++ b/drivers/net/mctp/mctp-i3c.c @@ -731,18 +731,21 @@ static __init int mctp_i3c_mod_init(void) int rc; rc = i3c_register_notifier(&mctp_i3c_notifier); - if (rc < 0) { - i3c_driver_unregister(&mctp_i3c_driver); + if (rc < 0) return rc; - } i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL); rc = i3c_driver_register(&mctp_i3c_driver); if (rc < 0) - return rc; + goto err_unregister_notifier; return 0; + +err_unregister_notifier: + i3c_unregister_notifier(&mctp_i3c_notifier); + mctp_i3c_bus_remove_all(); + return rc; } static __exit void mctp_i3c_mod_exit(void) From fbd91910c5025dd99553fed1313fefd0d2e1c465 Mon Sep 17 00:00:00 2001 From: Rishikesh Jethwani Date: Thu, 9 Jul 2026 16:44:36 -0600 Subject: [PATCH 2474/3297] tls: device: push pending open record on splice EOF [ Upstream commit eaa39f9f8ac8c1d032cd26b9cd572804e9d7683f ] On kTLS device-offload sockets, sendfile() with count > EOF can reach ->splice_eof() with a fully assembled but still-open TLS record left pending. tls_device_splice_eof() only flushes partially sent records, so an abrupt close() can drop the final record and the peer receives a short file. Fix tls_device_splice_eof() to also push pending open records. This matches the software path, where splice EOF already flushes pending open records. Fixes: d4c1e80b0d1b ("tls/device: Use splice_eof() to flush") Link: https://lore.kernel.org/netdev/CAMPsyauZ+jzG9AysO0FWv6ZY0kvCUpjX_U7o=oOjCuOQ87BCgg@mail.gmail.com/ Reported-by: Nils Juenemann Signed-off-by: Rishikesh Jethwani Tested-by: Nils Juenemann Link: https://patch.msgid.link/20260709224436.1608993-2-rjethwani@purestorage.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tls/tls_device.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 71734411ff4c3..d2e3870590c89 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -594,13 +594,15 @@ void tls_device_splice_eof(struct socket *sock) struct tls_context *tls_ctx = tls_get_ctx(sk); struct iov_iter iter = {}; - if (!tls_is_partially_sent_record(tls_ctx)) + if (!tls_is_partially_sent_record(tls_ctx) && + !tls_is_pending_open_record(tls_ctx)) return; mutex_lock(&tls_ctx->tx_lock); lock_sock(sk); - if (tls_is_partially_sent_record(tls_ctx)) { + if (tls_is_partially_sent_record(tls_ctx) || + tls_is_pending_open_record(tls_ctx)) { iov_iter_bvec(&iter, ITER_SOURCE, NULL, 0, 0); tls_push_data(sk, &iter, 0, 0, TLS_RECORD_TYPE_DATA); } From 340c389fd3d5dd32f22f1413ba49d60450a60b53 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:41 +0200 Subject: [PATCH 2475/3297] selftests: af_unix: add USER_NS config [ Upstream commit f8b1abed736111f914b2c567d9a3db1f71e788e8 ] This is required to use unshare(CLONE_NEWUSER). This has not been seen on NIPA before, because the 'af_unix' tests are executed with the 'net' ones, merging their config files. USER_NS is present in tools/testing/selftests/net/config. This issue is visible when only the af_unix config is used on top of the default one. This is the recommended way to execute selftest targets. Fixes: ac011361bd4f ("af_unix: Add test for sock_diag and UDIAG_SHOW_UID.") Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-1-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/af_unix/config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/net/af_unix/config b/tools/testing/selftests/net/af_unix/config index b5429c15a53c7..41dbb03c747eb 100644 --- a/tools/testing/selftests/net/af_unix/config +++ b/tools/testing/selftests/net/af_unix/config @@ -1,3 +1,4 @@ CONFIG_AF_UNIX_OOB=y CONFIG_UNIX=y CONFIG_UNIX_DIAG=m +CONFIG_USER_NS=y From 69eab5c4d9aa6c5b6ebff9679c2e75728ccb0700 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:42 +0200 Subject: [PATCH 2476/3297] selftests: openvswitch: add config file [ Upstream commit 441a820ccef9af80a9ac5a4c85b9c396e595967c ] The kselftests doc mentions that a config file should be present "if a test needs specific kernel config options enabled". This selftest requires some kernel config, but no config file was provided. We could say that a sub-target could use the parent's config file, but the kselftests doc doesn't mention anything about that. Plus the net/openvswitch target is the only net target without a config file. Here is a new config file, which is a trimmed version of the net one, with hopefully the minimal required kconfig on top of 'make defconfig'. The Fixes tag points to the introduction of the net/openvswitch target, just to help validating this target on stable kernels. Fixes: 25f16c873fb1 ("selftests: add openvswitch selftest suite") Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Eelco Chaudron Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-2-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/openvswitch/config | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tools/testing/selftests/net/openvswitch/config diff --git a/tools/testing/selftests/net/openvswitch/config b/tools/testing/selftests/net/openvswitch/config new file mode 100644 index 0000000000000..c659749cd086c --- /dev/null +++ b/tools/testing/selftests/net/openvswitch/config @@ -0,0 +1,16 @@ +CONFIG_GENEVE=m +CONFIG_INET_DIAG=y +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_OVS=y +CONFIG_OPENVSWITCH=m +CONFIG_OPENVSWITCH_GENEVE=m +CONFIG_OPENVSWITCH_GRE=m +CONFIG_OPENVSWITCH_VXLAN=m +CONFIG_PSAMPLE=m +CONFIG_VETH=y +CONFIG_VLAN_8021Q=y +CONFIG_VXLAN=m From e2b3d426c7ee59f068afc0f13b6105fc7978c73a Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:43 +0200 Subject: [PATCH 2477/3297] selftests: ovpn: add IPV6 and VETH configs [ Upstream commit 90c792681a3732caaf7bf5bc435877736baf591a ] They are required to run the selftests: - Tests are executed in v4 and v6. - Virtual Ethernet are used between the different netns. This has not been seen on NIPA before, because the 'ovpn' tests are executed with the 'tcp_ao' ones, merging their config files. These two kernel config are present in tools/testing/selftests/net/tcp_ao/config. This issue is visible when only the ovpn config is used on top of the default one. This is the recommended way to execute selftest targets. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: Matthieu Baerts (NGI0) Acked-by: Antonio Quartulli Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-3-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/ovpn/config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config index 42699740936db..c0a21a43018a2 100644 --- a/tools/testing/selftests/net/ovpn/config +++ b/tools/testing/selftests/net/ovpn/config @@ -4,7 +4,9 @@ CONFIG_CRYPTO_CHACHA20POLY1305=y CONFIG_CRYPTO_GCM=y CONFIG_DST_CACHE=y CONFIG_INET=y +CONFIG_IPV6=y CONFIG_NET=y CONFIG_NET_UDP_TUNNEL=y CONFIG_OVPN=m CONFIG_STREAM_PARSER=y +CONFIG_VETH=y From fa065685c8a9138aeadf87a013abdd27eed0df6d Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:44 +0200 Subject: [PATCH 2478/3297] selftests: ovpn: increase timeout [ Upstream commit 61ac7049aaa86ae044e8a5b94d852218163d5bf8 ] The default timeout is 45 seconds, that's too low for a few ovpn tests. Indeed, these tests can take up to 50 seconds with some debug kernel config on NIPA. Set a timeout to 90 seconds, just to be on the safe side. Note that the Fixes tag here points to the introduction of the ovpn tests because I don't know when they started to take more than 45 seconds. That's OK because a timeout of 1.5 minutes is not exaggerated. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: Matthieu Baerts (NGI0) Acked-by: Antonio Quartulli Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-4-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/net/ovpn/settings | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/net/ovpn/settings diff --git a/tools/testing/selftests/net/ovpn/settings b/tools/testing/selftests/net/ovpn/settings new file mode 100644 index 0000000000000..ba4d85f74cd6b --- /dev/null +++ b/tools/testing/selftests/net/ovpn/settings @@ -0,0 +1 @@ +timeout=90 From 023c5e0d0294a542ebe9e0224273d7efefad4884 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:45 +0200 Subject: [PATCH 2479/3297] selftests: drv-net: increase timeout [ Upstream commit 3529d75d67411497341cd804a045185d6035dff2 ] The default timeout is 45 seconds, that's too low for the xdp.py test. Indeed, this test can take up to 3 minutes with some debug kernel config on NIPA. Set a timeout to 6 minutes, just to be on the safe side. Note that the Fixes tag here points to the introduction of the xdp.py test because I don't know when this test started to take more than 45 seconds. That's OK because a timeout of 6 minutes is not exaggerated. Fixes: 1cbcb1b28b26 ("selftests: drv-net: Test XDP_PASS/DROP support") Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-5-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- tools/testing/selftests/drivers/net/settings | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/drivers/net/settings diff --git a/tools/testing/selftests/drivers/net/settings b/tools/testing/selftests/drivers/net/settings new file mode 100644 index 0000000000000..eef533824a3c1 --- /dev/null +++ b/tools/testing/selftests/drivers/net/settings @@ -0,0 +1 @@ +timeout=360 From 961e9b1e33445f8e42859ecc020c9f60d8b69a8b Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Fri, 10 Jul 2026 23:07:24 +0000 Subject: [PATCH 2480/3297] gtp: check skb_pull_data() return in gtp1u_send_echo_resp() [ Upstream commit cd170f051dba9ac146fabcd1b91726487c0cb9fa ] gtp1u_send_echo_resp() ignores skb_pull_data()'s return value. Its caller gtp1u_udp_encap_recv() only guarantees 16 bytes (udphdr + gtp1_header), but the pull requests 20 (gtp1_header_long + udphdr). For a 16-19 byte echo request the pull fails and returns NULL without advancing skb->data; execution continues, and the following skb_push() plus the IP header pushed by iptunnel_xmit() move skb->data below skb->head, tripping skb_under_panic(). Fix it by dropping the packet when skb_pull_data() fails. skbuff: skb_under_panic: ... kernel BUG at net/core/skbuff.c:214! Call Trace: skb_push (net/core/skbuff.c:2648) iptunnel_xmit (net/ipv4/ip_tunnel_core.c:82) gtp_encap_recv (drivers/net/gtp.c:701 drivers/net/gtp.c:808 drivers/net/gtp.c:920) udp_queue_rcv_one_skb (net/ipv4/udp.c:2388) ... Kernel panic - not syncing: Fatal exception in interrupt Fixes: 9af41cc33471 ("gtp: Implement GTP echo response") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Link: https://patch.msgid.link/20260710230724.942574-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/gtp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 937d913ce5c33..98614d0b390b8 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -669,8 +669,9 @@ static int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) return -1; /* pull GTP and UDP headers */ - skb_pull_data(skb, - sizeof(struct gtp1_header_long) + sizeof(struct udphdr)); + if (!skb_pull_data(skb, sizeof(struct gtp1_header_long) + + sizeof(struct udphdr))) + return -1; gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet)); memset(gtp_pkt, 0, sizeof(struct gtp1u_packet)); From d536bf205c71f700f6de2086038c3e1d77724715 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Mon, 13 Jul 2026 22:15:51 +0000 Subject: [PATCH 2481/3297] nexthop: initialize extack in nh_res_bucket_migrate() [ Upstream commit 6347c5314cee49f364aaf2e40ff15415a57a116e ] nh_res_bucket_migrate() passes an uninitialized netlink_ext_ack to call_nexthop_res_bucket_notifiers(). When nh_notifier_res_bucket_info_init() fails (e.g. the kzalloc returns -ENOMEM), the error is propagated back before any notifier sets extack._msg, and the error path formats the stale pointer with pr_err_ratelimited("%s\n", extack._msg). With CONFIG_INIT_STACK_NONE this dereferences uninitialized stack memory: Oops: general protection fault, probably for non-canonical address ... KASAN: maybe wild-memory-access in range [...] RIP: 0010:string (lib/vsprintf.c:730) vsnprintf (lib/vsprintf.c:2945) _printk (kernel/printk/printk.c:2504) nh_res_bucket_migrate (net/ipv4/nexthop.c:1816) nh_res_table_upkeep (net/ipv4/nexthop.c:1866) rtm_new_nexthop (net/ipv4/nexthop.c:3323) rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) netlink_sendmsg (net/netlink/af_netlink.c:1900) Kernel panic - not syncing: Fatal exception Zero-initialize extack so _msg is NULL on error paths that never set it. Fixes: 7c37c7e00411 ("nexthop: Implement notifiers for resilient nexthop groups") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260713221551.3344650-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/nexthop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 5a95b64b61c59..a8bf1ec548995 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -1791,8 +1791,8 @@ static bool nh_res_bucket_migrate(struct nh_res_table *res_table, bool notify_nl, bool force) { struct nh_res_bucket *bucket = &res_table->nh_buckets[bucket_index]; + struct netlink_ext_ack extack = {}; struct nh_grp_entry *new_nhge; - struct netlink_ext_ack extack; int err; new_nhge = list_first_entry_or_null(&res_table->uw_nh_entries, From e740e90ca8e7f70d9eac1aa31a8b3e0e4d32b2ef Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Mon, 13 Jul 2026 17:49:35 -0300 Subject: [PATCH 2482/3297] tipc: fix infinite loop in __tipc_nl_compat_dumpit [ Upstream commit 22f8aa35964e8f2ab026578f45befc9605fd1b28 ] cmd->dumpit callback can return a negative errno, causing an infinite loop due to the while(len) condition. As the loop never terminates, genl_mutex is never released, and other tasks waiting on it starve in D state. Check dumpit's return value, propagate it and jump to err_out on error. Reported-by: syzbot+85d0bec020d805014a3a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=85d0bec020d805014a3a Fixes: d0796d1ef63d ("tipc: convert legacy nl bearer dump to nl compat") Signed-off-by: Helen Koike Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260713204940.647668-1-koike@igalia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tipc/netlink_compat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 079aebb16ed8f..30e37c05a3f33 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -222,6 +222,10 @@ static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, int rem; len = (*cmd->dumpit)(buf, &cb); + if (len < 0) { + err = len; + goto err_out; + } nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) { err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN, From cb9d3e0b55699979b4f4cc333d641edc764056ad Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Thu, 29 Jan 2026 09:29:02 +0800 Subject: [PATCH 2483/3297] ppp: enable TX scatter-gather [ Upstream commit 42fcb213e58a7da33d5d2d7517b4e521025c68c3 ] PPP channels using chan->direct_xmit prepend the PPP header to a skb and call dev_queue_xmit() directly. In this mode the skb does not need to be linear, but the PPP netdevice currently does not advertise scatter-gather features, causing unnecessary linearization and preventing GSO. Enable NETIF_F_SG and NETIF_F_FRAGLIST on PPP devices. In case a linear buffer is required (PPP compression, multilink, and channels without direct_xmit), call skb_linearize() explicitly. Signed-off-by: Qingfang Deng Link: https://patch.msgid.link/20260129012902.941-1-dqfext@gmail.com Signed-off-by: Paolo Abeni Stable-dep-of: ba712ecfd942 ("ppp: annotate concurrent dev->stats accesses") Signed-off-by: Sasha Levin --- drivers/net/ppp/ppp_generic.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 72e98286fba2f..6d001fb1462d3 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1645,6 +1645,8 @@ static void ppp_setup(struct net_device *dev) dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->priv_destructor = ppp_dev_priv_destructor; dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST; + dev->hw_features = dev->features; netif_keep_dst(dev); } @@ -1714,6 +1716,10 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb) ppp->xcomp->comp_extra + ppp->dev->hard_header_len; int compressor_skb_size = ppp->dev->mtu + ppp->xcomp->comp_extra + PPP_HDRLEN; + + if (skb_linearize(skb)) + return NULL; + new_skb = alloc_skb(new_skb_size, GFP_ATOMIC); if (!new_skb) { if (net_ratelimit()) @@ -1801,6 +1807,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) case PPP_IP: if (!ppp->vj || (ppp->flags & SC_COMP_TCP) == 0) break; + + if (skb_linearize(skb)) + goto drop; + /* try to do VJ TCP header compression */ new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2, GFP_ATOMIC); @@ -1898,19 +1908,26 @@ ppp_push(struct ppp *ppp) } if ((ppp->flags & SC_MULTILINK) == 0) { + struct ppp_channel *chan; /* not doing multilink: send it down the first channel */ list = list->next; pch = list_entry(list, struct channel, clist); spin_lock(&pch->downl); - if (pch->chan) { - if (pch->chan->ops->start_xmit(pch->chan, skb)) - ppp->xmit_pending = NULL; - } else { - /* channel got unregistered */ + chan = pch->chan; + if (unlikely(!chan || (!chan->direct_xmit && skb_linearize(skb)))) { + /* channel got unregistered, or it requires a linear + * skb but linearization failed + */ kfree_skb(skb); ppp->xmit_pending = NULL; + goto out; } + + if (chan->ops->start_xmit(chan, skb)) + ppp->xmit_pending = NULL; + +out: spin_unlock(&pch->downl); return; } @@ -1995,6 +2012,8 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) return 0; /* can't take now, leave it in xmit_pending */ /* Do protocol field compression */ + if (skb_linearize(skb)) + goto err_linearize; p = skb->data; len = skb->len; if (*p == 0 && mp_protocol_compress) { @@ -2153,6 +2172,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) noskb: spin_unlock(&pch->downl); + err_linearize: if (ppp->debug & 1) netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); ++ppp->dev->stats.tx_errors; From b52ff80948d1cfdd7b736f829bf80f6a4e3077ea Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Tue, 3 Mar 2026 17:32:19 +0800 Subject: [PATCH 2484/3297] ppp: don't store tx skb in the fastpath [ Upstream commit 70836c8d0fe046400de8cdcf0613b2f1f6bddde3 ] Currently, ppp->xmit_pending is used in ppp_send_frame() to pass a skb to ppp_push(), and holds the skb when a PPP channel cannot immediately transmit it. This state is redundant because the transmit queue (ppp->file.xq) can already handle the backlog. Furthermore, during normal operation, an skb is queued in file.xq only to be immediately dequeued, causing unnecessary overhead. Refactor the transmit path to avoid stashing the skb when possible: - Remove ppp->xmit_pending. - Rename ppp_send_frame() to ppp_prepare_tx_skb(), and don't call ppp_push() in it. It returns 1 if the skb is consumed (dropped/handled) or 0 if it can be passed to ppp_push(). - Update ppp_push() to accept the skb. It returns 1 if the skb is consumed, or 0 if the channel is busy. - Optimize __ppp_xmit_process(): - Fastpath: If the queue is empty, attempt to send the skb directly via ppp_push(). If busy, queue it. - Slowpath: If the queue is not empty, process the backlog in file.xq. Split dequeuing loop into a separate function ppp_xmit_flush() so ppp_channel_push() uses that directly instead of passing a NULL skb to __ppp_xmit_process(). This simplifies the states and reduces locking in the fastpath. Signed-off-by: Qingfang Deng Link: https://patch.msgid.link/20260303093219.234403-1-dqfext@gmail.com Signed-off-by: Paolo Abeni Stable-dep-of: ba712ecfd942 ("ppp: annotate concurrent dev->stats accesses") Signed-off-by: Sasha Levin --- drivers/net/ppp/ppp_generic.c | 111 ++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 6d001fb1462d3..e09a916356a1d 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -134,7 +134,6 @@ struct ppp { int debug; /* debug flags 70 */ struct slcompress *vj; /* state for VJ header compression */ enum NPmode npmode[NUM_NP]; /* what to do with each net proto 78 */ - struct sk_buff *xmit_pending; /* a packet ready to go out 88 */ struct compressor *xcomp; /* transmit packet compressor 8c */ void *xc_state; /* its internal state 90 */ struct compressor *rcomp; /* receive decompressor 94 */ @@ -265,8 +264,8 @@ struct ppp_net { static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, struct file *file, unsigned int cmd, unsigned long arg); static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb); -static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb); -static void ppp_push(struct ppp *ppp); +static int ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb); +static int ppp_push(struct ppp *ppp, struct sk_buff *skb); static void ppp_channel_push(struct channel *pch); static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch); @@ -1655,26 +1654,44 @@ static void ppp_setup(struct net_device *dev) */ /* Called to do any work queued up on the transmit side that can now be done */ +static void ppp_xmit_flush(struct ppp *ppp) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&ppp->file.xq))) { + if (unlikely(!ppp_push(ppp, skb))) { + skb_queue_head(&ppp->file.xq, skb); + return; + } + } + /* If there's no work left to do, tell the core net code that we can + * accept some more. + */ + netif_wake_queue(ppp->dev); +} + static void __ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb) { ppp_xmit_lock(ppp); - if (!ppp->closing) { - ppp_push(ppp); - - if (skb) + if (unlikely(ppp->closing)) { + kfree_skb(skb); + goto out; + } + if (unlikely(ppp_prepare_tx_skb(ppp, &skb))) + goto out; + /* Fastpath: No backlog, just send the new skb. */ + if (likely(skb_queue_empty(&ppp->file.xq))) { + if (unlikely(!ppp_push(ppp, skb))) { skb_queue_tail(&ppp->file.xq, skb); - while (!ppp->xmit_pending && - (skb = skb_dequeue(&ppp->file.xq))) - ppp_send_frame(ppp, skb); - /* If there's no work left to do, tell the core net - code that we can accept some more. */ - if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq)) - netif_wake_queue(ppp->dev); - else netif_stop_queue(ppp->dev); - } else { - kfree_skb(skb); + } + goto out; } + + /* Slowpath: Enqueue the new skb and process backlog */ + skb_queue_tail(&ppp->file.xq, skb); + ppp_xmit_flush(ppp); +out: ppp_xmit_unlock(ppp); } @@ -1761,13 +1778,15 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb) } /* - * Compress and send a frame. - * The caller should have locked the xmit path, - * and xmit_pending should be 0. + * Compress and prepare to send a frame. + * The caller should have locked the xmit path. + * Returns 1 if the skb was consumed, 0 if it can be passed to ppp_push(). + * @pskb is updated if a compressor is in use. */ -static void -ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) +static int +ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) { + struct sk_buff *skb = *pskb; int proto = PPP_PROTO(skb); struct sk_buff *new_skb; int len; @@ -1788,7 +1807,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) "PPP: outbound frame " "not passed\n"); kfree_skb(skb); - return; + return 1; } /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && @@ -1836,6 +1855,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) } consume_skb(skb); skb = new_skb; + *pskb = skb; cp = skb_put(skb, len + 2); cp[0] = 0; cp[1] = proto; @@ -1862,6 +1882,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) if (!new_skb) goto drop; skb = new_skb; + *pskb = skb; } /* @@ -1873,42 +1894,38 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) goto drop; skb_queue_tail(&ppp->file.rq, skb); wake_up_interruptible(&ppp->file.rwait); - return; + return 1; } - ppp->xmit_pending = skb; - ppp_push(ppp); - return; + return 0; drop: kfree_skb(skb); ++ppp->dev->stats.tx_errors; + return 1; } /* - * Try to send the frame in xmit_pending. + * Try to send the frame. * The caller should have the xmit path locked. + * Returns 1 if the skb was consumed, 0 if not. */ -static void -ppp_push(struct ppp *ppp) +static int +ppp_push(struct ppp *ppp, struct sk_buff *skb) { struct list_head *list; struct channel *pch; - struct sk_buff *skb = ppp->xmit_pending; - - if (!skb) - return; list = &ppp->channels; if (list_empty(list)) { /* nowhere to send the packet, just drop it */ - ppp->xmit_pending = NULL; kfree_skb(skb); - return; + return 1; } if ((ppp->flags & SC_MULTILINK) == 0) { struct ppp_channel *chan; + int ret; /* not doing multilink: send it down the first channel */ list = list->next; pch = list_entry(list, struct channel, clist); @@ -1920,27 +1937,26 @@ ppp_push(struct ppp *ppp) * skb but linearization failed */ kfree_skb(skb); - ppp->xmit_pending = NULL; + ret = 1; goto out; } - if (chan->ops->start_xmit(chan, skb)) - ppp->xmit_pending = NULL; + ret = chan->ops->start_xmit(chan, skb); out: spin_unlock(&pch->downl); - return; + return ret; } #ifdef CONFIG_PPP_MULTILINK /* Multilink: fragment the packet over as many links as can take the packet at the moment. */ if (!ppp_mp_explode(ppp, skb)) - return; + return 0; #endif /* CONFIG_PPP_MULTILINK */ - ppp->xmit_pending = NULL; kfree_skb(skb); + return 1; } #ifdef CONFIG_PPP_MULTILINK @@ -2009,7 +2025,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) * performance if we have a lot of channels. */ if (nfree == 0 || nfree < navail / 2) - return 0; /* can't take now, leave it in xmit_pending */ + return 0; /* can't take now, leave it in transmit queue */ /* Do protocol field compression */ if (skb_linearize(skb)) @@ -2203,8 +2219,12 @@ static void __ppp_channel_push(struct channel *pch, struct ppp *ppp) spin_unlock(&pch->downl); /* see if there is anything from the attached unit to be sent */ if (skb_queue_empty(&pch->file.xq)) { - if (ppp) - __ppp_xmit_process(ppp, NULL); + if (ppp) { + ppp_xmit_lock(ppp); + if (!ppp->closing) + ppp_xmit_flush(ppp); + ppp_xmit_unlock(ppp); + } } } @@ -3464,7 +3484,6 @@ static void ppp_destroy_interface(struct ppp *ppp) } #endif /* CONFIG_PPP_FILTER */ - kfree_skb(ppp->xmit_pending); free_percpu(ppp->xmit_recursion); free_netdev(ppp->dev); From cc4d2f8b984c2b4feae199c496b5b63ed507dd3d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 15 Jul 2026 05:55:41 +0000 Subject: [PATCH 2485/3297] ppp: annotate concurrent dev->stats accesses [ Upstream commit ba712ecfd942b68b21a4b0a5daaf72f6616cc66d ] dev->stats fields can be updated concurrently from multiple CPUs without synchronization. Use DEV_STATS_INC() for stats increments and DEV_STATS_READ() when reading dev->stats in ppp_get_stats64() and ppp_get_stats() to avoid data races. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Qingfang Deng Link: https://patch.msgid.link/20260715055541.1147542-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ppp/ppp_generic.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index e09a916356a1d..a2fe0fe2f3389 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1490,7 +1490,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) outf: kfree_skb(skb); - ++dev->stats.tx_dropped; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } @@ -1540,11 +1540,11 @@ ppp_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr, static void ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) { - stats64->rx_errors = dev->stats.rx_errors; - stats64->tx_errors = dev->stats.tx_errors; - stats64->rx_dropped = dev->stats.rx_dropped; - stats64->tx_dropped = dev->stats.tx_dropped; - stats64->rx_length_errors = dev->stats.rx_length_errors; + stats64->rx_errors = DEV_STATS_READ(dev, rx_errors); + stats64->tx_errors = DEV_STATS_READ(dev, tx_errors); + stats64->rx_dropped = DEV_STATS_READ(dev, rx_dropped); + stats64->tx_dropped = DEV_STATS_READ(dev, tx_dropped); + stats64->rx_length_errors = DEV_STATS_READ(dev, rx_length_errors); dev_fetch_sw_netstats(stats64, dev->tstats); } @@ -1901,7 +1901,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) drop: kfree_skb(skb); - ++ppp->dev->stats.tx_errors; + DEV_STATS_INC(ppp->dev, tx_errors); return 1; } @@ -2191,7 +2191,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) err_linearize: if (ppp->debug & 1) netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); - ++ppp->dev->stats.tx_errors; + DEV_STATS_INC(ppp->dev, tx_errors); ++ppp->nxseq; return 1; /* abandon the frame */ } @@ -2364,7 +2364,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) if (!ppp_decompress_proto(skb)) { kfree_skb(skb); if (ppp) { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); ppp_receive_error(ppp); } goto done; @@ -2437,7 +2437,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) static void ppp_receive_error(struct ppp *ppp) { - ++ppp->dev->stats.rx_errors; + DEV_STATS_INC(ppp->dev, rx_errors); if (ppp->vj) slhc_toss(ppp->vj); } @@ -2704,7 +2704,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) */ if (seq_before(seq, ppp->nextseq)) { kfree_skb(skb); - ++ppp->dev->stats.rx_dropped; + DEV_STATS_INC(ppp->dev, rx_dropped); ppp_receive_error(ppp); return; } @@ -2740,7 +2740,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) if (pskb_may_pull(skb, 2)) ppp_receive_nonmp_frame(ppp, skb); else { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); kfree_skb(skb); ppp_receive_error(ppp); } @@ -2846,7 +2846,7 @@ ppp_mp_reconstruct(struct ppp *ppp) if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) && (PPP_MP_CB(head)->BEbits & B)) { if (len > ppp->mrru + 2) { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); netdev_printk(KERN_DEBUG, ppp->dev, "PPP: reconstructed packet" " is too long (%d)\n", len); @@ -2901,7 +2901,7 @@ ppp_mp_reconstruct(struct ppp *ppp) " missed pkts %u..%u\n", ppp->nextseq, PPP_MP_CB(head)->sequence-1); - ++ppp->dev->stats.rx_dropped; + DEV_STATS_INC(ppp->dev, rx_dropped); ppp_receive_error(ppp); } @@ -3370,8 +3370,8 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) st->p.ppp_opackets += tx_packets; st->p.ppp_obytes += tx_bytes; } - st->p.ppp_ierrors = ppp->dev->stats.rx_errors; - st->p.ppp_oerrors = ppp->dev->stats.tx_errors; + st->p.ppp_ierrors = DEV_STATS_READ(ppp->dev, rx_errors); + st->p.ppp_oerrors = DEV_STATS_READ(ppp->dev, tx_errors); if (!vj) return; st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; From f1ee53e08fdd2906e90c6a6d71e1368fcd52bfc3 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 15:55:39 +0800 Subject: [PATCH 2486/3297] wifi: mt76: mt7925: guard link STA in decap offload [ Upstream commit 96ea44f2269f30364cffa054ee3a87e595bef0d4 ] mt7925_sta_set_decap_offload() iterates over the vif valid_links mask when updating decap offload state for an MLO station. The station may not have a link STA for every valid link of the vif, so mt792x_sta_to_link() can return NULL for a link that belongs to the vif but not to the station. The function currently dereferences mlink before checking whether the link WCID is ready. If mlink is NULL, setting or clearing MT_WCID_FLAG_HDR_TRANS dereferences a NULL pointer. Skip links without a station link before touching mlink->wcid. Fixes: b859ad65309a ("wifi: mt76: mt7925: add link handling in mt7925_sta_set_decap_offload") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260708075539.726200-1-lgs201920130244@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 6c0dc72efc392..910713e62e14a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1573,6 +1573,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw, mlink = mt792x_sta_to_link(msta, i); + if (!mlink) + continue; + if (enabled) set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags); else From 871549814eb4da081f1e93cc0c7ea626a310a966 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sat, 20 Jun 2026 23:53:32 +0800 Subject: [PATCH 2487/3297] wifi: mt76: mt7915: guard HE capability lookups [ Upstream commit 8e9db062654a388d0fa587acbeeae68dd33eba41 ] mt7915_mcu_bss_he_tlv() and mt7915_mcu_sta_bfer_tlv() both run after checking HE support, then dereference the HE PHY capability returned by mt76_connac_get_he_phy_cap(). That helper can return NULL when no capability entry matches the vif type. Fetch the capability before appending the TLV and skip the HE-specific setup when no matching capability is available. Fixes: e6d557a78b60 ("mt76: mt7915: rely on mt76_connac_get_phy utilities") Signed-off-by: Ruoyu Wang Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260620155332.81120-1-ruoyuw560@gmail.com Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 79e021ac0bdbc..f60e2eaea2af4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -595,6 +595,8 @@ mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he)); @@ -1177,13 +1179,12 @@ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy, } static void -mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, - struct mt7915_phy *phy, struct sta_rec_bf *bf) +mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, + const struct ieee80211_sta_he_cap *vc, + struct sta_rec_bf *bf) { struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; - const struct ieee80211_sta_he_cap *vc = - mt76_connac_get_he_phy_cap(phy->mt76, vif); const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); @@ -1242,6 +1243,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_phy *phy = mvif->phy; + const struct ieee80211_sta_he_cap *vc = NULL; int tx_ant = hweight8(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; @@ -1260,6 +1262,12 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, if (!ebf && !dev->ibf) return; + if (sta->deflink.he_cap.has_he && ebf) { + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!vc) + return; + } + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); bf = (struct sta_rec_bf *)tlv; @@ -1268,7 +1276,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, * ht: iBF only, since mac80211 lacks of eBF support */ if (sta->deflink.he_cap.has_he && ebf) - mt7915_mcu_sta_bfer_he(sta, vif, phy, bf); + mt7915_mcu_sta_bfer_he(sta, vc, bf); else if (sta->deflink.vht_cap.vht_supported) mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf); else if (sta->deflink.ht_cap.ht_supported) From c058786b09cfab080125bc3ee7928a181dcbd37a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:24:59 +0200 Subject: [PATCH 2488/3297] wifi: mt76: connac: fix possible NULL-pointer deref in mt76_connac_mcu_uni_bss_he_tlv() [ Upstream commit 2c1fb2335f5e3afb34f91bc07ecb63517c328090 ] mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: d0e274af2f2e4 ("mt76: mt76_connac: create mcu library") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-1-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index fc3e6728fcfbf..2aa7b711c774e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1441,6 +1441,8 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif, struct bss_info_uni_he *he; cap = mt76_connac_get_he_phy_cap(phy, vif); + if (!cap) + return; he = (struct bss_info_uni_he *)tlv; he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; From 313343ab8cab7417973e7bdd43d3c3e93044b447 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:25:00 +0200 Subject: [PATCH 2489/3297] wifi: mt76: mt7925: fix possible NULL-pointer deref in mt7925_mcu_bss_he_tlv() [ Upstream commit 8d1b6738c1ab48c086b17e7994034aca94258931 ] mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: c948b5da6bbec ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-2-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index d887aa9a3dff7..647efa963db48 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2706,6 +2706,8 @@ mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif); + if (!cap) + return; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); From d14238523ca4c6f5fcb54d1920eb2f8525a7711f Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:25:01 +0200 Subject: [PATCH 2490/3297] wifi: mt76: mt7996: check pointer returned by mt76_connac_get_he_phy_cap() [ Upstream commit e858cf6bf99880343348ff1e8c942aaff1d9d592 ] mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: 98686cd21624c ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-3-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index a3f813be107df..87ec7e90dfeeb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -860,6 +860,8 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); @@ -1659,17 +1661,18 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta, { struct ieee80211_sta_he_cap *pc = &link_sta->he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; - const struct ieee80211_sta_he_cap *vc = - mt76_connac_get_he_phy_cap(phy->mt76, vif); - const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + const struct ieee80211_he_cap_elem *ve; + const struct ieee80211_sta_he_cap *vc; u8 snd_dim, sts; + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); if (!vc) return; bf->tx_mode = MT_PHY_TYPE_HE_SU; + ve = &vc->he_cap_elem; mt7996_mcu_sta_sounding_rate(bf, phy); From 95b0cf02731c74e073ef8937f5526bd4442a0326 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 16 Jun 2026 11:10:16 -0500 Subject: [PATCH 2491/3297] wifi: mt76: mt7925: fix crash in reset link replay [ Upstream commit bd8b2ec838184236c3fcbf738a926328836adf12 ] During reset recovery, mt7925_vif_connect_iter() replays firmware state for links tracked in mvif->valid_links. After MLO link changes or MCU timeout recovery, the driver bitmap can temporarily contain a link whose mac80211 bss_conf has already gone away. This can pass a NULL bss_conf to mt76_connac_mcu_uni_add_dev(), matching the crash where x1, the second argument, is NULL: pc : mt76_connac_mcu_uni_add_dev+0x8c/0x1f8 [mt76_connac_lib] lr : mt7925_vif_connect_iter+0x9c/0x168 [mt7925_common] x2 : ffffff80a77f6018 x1 : 0000000000000000 x0 : ffffff8099402080 Call trace: mt76_connac_mcu_uni_add_dev+0x8c/0x1f8 [mt76_connac_lib] mt7925_vif_connect_iter+0x9c/0x168 [mt7925_common] mt7925_mac_reset_work+0x264/0x2f8 [mt7925_common] Skip missing bss_conf entries before replaying the link. Non-MLO AP/STA reset replay is unchanged because the helper still returns &vif->bss_conf for the legacy link. Fixes: 14061994184d ("wifi: mt76: mt7925: add link handling in mt7925_vif_connect_iter") Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260616161016.19346-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index d951a46e9d48c..2ff74d0bef9d0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1274,6 +1274,9 @@ mt7925_vif_connect_iter(void *priv, u8 *mac, for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { bss_conf = mt792x_vif_to_bss_conf(vif, i); + if (!bss_conf) + continue; + mconf = mt792x_vif_to_link(mvif, i); mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76, From d5628f39fccc107dca00b98a491d9848898599f7 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:25:02 +0200 Subject: [PATCH 2492/3297] wifi: mt76: mt7996: fix possible NULL-pointer deref in mt7996_mcu_sta_bfer_eht() [ Upstream commit 2fffc472bec490c8357defcee9c075ca74467352 ] mt76_connac_get_eht_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: ba01944adee9f ("wifi: mt76: mt7996: add EHT beamforming support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-4-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau Signed-off-by: Sasha Levin --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 87ec7e90dfeeb..72851399575f9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1728,14 +1728,18 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta, struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp; - const struct ieee80211_sta_eht_cap *vc = - mt76_connac_get_eht_phy_cap(phy->mt76, vif); - const struct ieee80211_eht_cap_elem_fixed *ve = &vc->eht_cap_elem; u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss, IEEE80211_EHT_MCS_NSS_RX) - 1; + const struct ieee80211_eht_cap_elem_fixed *ve; + const struct ieee80211_sta_eht_cap *vc; u8 snd_dim, sts; + vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif); + if (!vc) + return; + bf->tx_mode = MT_PHY_TYPE_EHT_MU; + ve = &vc->eht_cap_elem; mt7996_mcu_sta_sounding_rate(bf, phy); From 00ebbf030d8c4a1cb89cbbae15e28332373649db Mon Sep 17 00:00:00 2001 From: Shelley Yang Date: Mon, 25 May 2026 16:38:59 +0800 Subject: [PATCH 2493/3297] wifi: brcmfmac: fix 802.1X-SHA256 call trace warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7cb34f6c4fe8a68af621d870abe63bfca2275dd6 ] Based on wpa_auth as 1x_256 mode, need to set up "use_fwsup" with BRCMF_PROFILE_FWSUP_1X. Or it will happen trace warning when call brcmf_cfg80211_set_pmk(). [ 4481.831101] ------------[ cut here ]------------ [ 4481.831102] WARNING: CPU: 1 PID: 2997 at drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c:7242 brcmf_cfg80211_set_pmk+0x77/0xd0 [brcmfmac] [...] [ 4481.831202] Call Trace: [ 4481.831204]   [ 4481.831205]  nl80211_set_pmk+0x183/0x250 [cfg80211] [ 4481.831233]  genl_family_rcv_msg_doit+0xea/0x150 [ 4481.831237]  genl_rcv_msg+0x104/0x240 [ 4481.831239]  ? cfg80211_probe_status+0x2c0/0x2c0 [cfg80211] [ 4481.831257]  ? genl_family_rcv_msg_doit+0x150/0x150 [ 4481.831259]  netlink_rcv_skb+0x4e/0x100 [ 4481.831261]  genl_rcv+0x24/0x40 [ 4481.831262]  netlink_unicast+0x236/0x380 [ 4481.831264]  netlink_sendmsg+0x250/0x4b0 [ 4481.831266]  sock_sendmsg+0x5c/0x70 [ 4481.831269]  ____sys_sendmsg+0x236/0x2b0 [ 4481.831271]  ? copy_msghdr_from_user+0x6d/0xa0 [ 4481.831272]  ___sys_sendmsg+0x86/0xd0 [ 4481.831274]  ? avc_has_perm+0x8c/0x1a0 [ 4481.831276]  ? preempt_count_add+0x6a/0xa0 [ 4481.831279]  ? sock_has_perm+0x82/0xa0 [ 4481.831280]  __sys_sendmsg+0x57/0xa0 [ 4481.831282]  do_syscall_64+0x38/0x90 [ 4481.831284]  entry_SYSCALL_64_after_hwframe+0x63/0xcd [ 4481.831286] RIP: 0033:0x7fd270d369b4 Fixes: 2526ff21aa77 ("brcmfmac: support 4-way handshake offloading for 802.1X") Signed-off-by: Shelley Yang Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260525083859.581246-1-shelley.yang@infineon.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index bb96b87b2a6e5..1fc9ecd86d5d9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2146,7 +2146,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) sme->crypto.akm_suites[0]); return -EINVAL; } - } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_1X_SHA256)) { switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA2_AUTH_UNSPECIFIED; From 790da254031cc56f55d97996b010b6e049e20425 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 8 Jul 2026 16:22:21 +0800 Subject: [PATCH 2494/3297] ovl: fix trusted xattr escape prefix matching [ Upstream commit a8e72879cd0d8422c0b47d6d3c1802274fe73b98 ] In the trusted.* xattr namespace, ovl_is_escaped_xattr() compares one byte less than the escaped overlay xattr prefix length. This makes it match "trusted.overlay.overlay" without requiring the trailing dot. As a result, an xattr such as "trusted.overlay.overlayfoo" is incorrectly treated as an escaped overlay xattr. This can be reproduced by setting "trusted.overlay.overlayfoo" on a lower file and listing xattrs through an overlay mount. listxattr() then exposes it as "trusted.overlay.oo", and a following getxattr() on that listed name fails with ENODATA. Compare the full escaped prefix, including the trailing dot, so similarly-prefixed private xattrs are not misclassified. Fixes: dad02fad84cbc ("ovl: Support escaped overlay.* xattrs") Signed-off-by: Yichong Chen Link: https://patch.msgid.link/20260708082221.633602-1-chenyichong@uniontech.com Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/overlayfs/xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c index 88055deca9360..c86f1a6c8b937 100644 --- a/fs/overlayfs/xattrs.c +++ b/fs/overlayfs/xattrs.c @@ -13,7 +13,7 @@ static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name) OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0; else return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX, - OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0; + OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN) == 0; } static bool ovl_is_own_xattr(struct super_block *sb, const char *name) From 3139b806923b18988c063c957bd311956b67d241 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 01:15:14 +0100 Subject: [PATCH 2495/3297] drm/panel: s6e3ha8: fix unmet dependency on DRM_DISPLAY_HELPER [ Upstream commit 3667bc164849fee4f1b18b182bdfe643f758ca17 ] Currently, DRM_PANEL_SAMSUNG_S6E3HA8 selects DRM_DISPLAY_DSC_HELPER without ensuring its dependency, DRM_DISPLAY_HELPER, is enabled, causing an unmet dependency. Let's select DRM_DISPLAY_HELPER as other similar options do. This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig. Fixes: fd3b2c5f40a1 ("drm/panel: s6e3ha8: select CONFIG_DRM_DISPLAY_DSC_HELPER") Signed-off-by: Julian Braha Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260712001514.2318597-1-julianbraha@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/panel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 1516c6fa265db..7354dc85a6aa7 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -814,6 +814,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER help Say Y or M here if you want to enable support for the From 35f0b504394e0a24a421fc2c6d31dd77d2d623d3 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 3 Nov 2025 00:12:43 +0100 Subject: [PATCH 2496/3297] cred: add scoped_with_kernel_creds() [ Upstream commit ae40e6c65791f47c76cc14d0cce2707fe6053f72 ] Add a new cleanup class for override creds. We can make use of this in a bunch of places going forward. Based on this scoped_with_kernel_creds() that can be used to temporarily assume kernel credentials for specific tasks such as firmware loading, or coredump socket connections. At no point will the caller interact with the kernel credentials directly. Link: https://patch.msgid.link/20251103-work-creds-init_cred-v1-4-cb3ec8711a6a@kernel.org Reviewed-by: Jens Axboe Signed-off-by: Christian Brauner Stable-dep-of: a1e0eb8f55cf ("ovl: check access to copy_file_range source with src mounter creds") Signed-off-by: Sasha Levin --- include/linux/cred.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/cred.h b/include/linux/cred.h index 89ae50ad2acea..6225c0cef3d77 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -180,6 +180,14 @@ static inline const struct cred *revert_creds(const struct cred *revert_cred) return rcu_replace_pointer(current->cred, revert_cred, 1); } +DEFINE_CLASS(override_creds, + const struct cred *, + revert_creds(_T), + override_creds(override_cred), const struct cred *override_cred) + +#define scoped_with_kernel_creds() \ + scoped_class(override_creds, __UNIQUE_ID(cred), kernel_cred()) + /** * get_cred_many - Get references on a set of credentials * @cred: The credentials to reference From cde234a493f6db1b2b0959c40e1fceeda4b0ed9c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:32 +0100 Subject: [PATCH 2497/3297] ovl: add override_creds cleanup guard extension for overlayfs [ Upstream commit 6f5c84162a30514a795eab3495a12c19306d6f6c ] Overlayfs plucks the relevant creds from the superblock. Extend the override_creds cleanup class I added to override_creds_ovl which uses the ovl_override_creds() function as initialization helper. Add with_ovl_creds() based on this new class. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-1-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner Stable-dep-of: a1e0eb8f55cf ("ovl: check access to copy_file_range source with src mounter creds") Signed-off-by: Sasha Levin --- fs/overlayfs/overlayfs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index d1eb1cbe7a456..696b13d9e1f10 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -445,6 +445,11 @@ struct dentry *ovl_workdir(struct dentry *dentry); const struct cred *ovl_override_creds(struct super_block *sb); void ovl_revert_creds(const struct cred *old_cred); +EXTEND_CLASS(override_creds, _ovl, ovl_override_creds(sb), struct super_block *sb) + +#define with_ovl_creds(sb) \ + scoped_class(override_creds_ovl, __UNIQUE_ID(label), sb) + static inline const struct cred *ovl_creds(struct super_block *sb) { return OVL_FS(sb)->creator_cred; From 31f5f7c959c3ed2f2bb677a70a3205fe78fab0c1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:07 +0100 Subject: [PATCH 2498/3297] ovl: port ovl_copyfile() to cred guard [ Upstream commit 14d35fda5b1139bacefb47c69f083fe2cfad211b ] Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-36-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner Stable-dep-of: a1e0eb8f55cf ("ovl: check access to copy_file_range source with src mounter creds") Signed-off-by: Sasha Levin --- fs/overlayfs/file.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 3fedfdddfa758..4374fbad3fc43 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -543,7 +543,6 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, { struct inode *inode_out = file_inode(file_out); struct file *realfile_in, *realfile_out; - const struct cred *old_cred; loff_t ret; inode_lock(inode_out); @@ -565,25 +564,25 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, if (IS_ERR(realfile_in)) goto out_unlock; - old_cred = ovl_override_creds(file_inode(file_out)->i_sb); - switch (op) { - case OVL_COPY: - ret = vfs_copy_file_range(realfile_in, pos_in, - realfile_out, pos_out, len, flags); - break; - - case OVL_CLONE: - ret = vfs_clone_file_range(realfile_in, pos_in, - realfile_out, pos_out, len, flags); - break; - - case OVL_DEDUPE: - ret = vfs_dedupe_file_range_one(realfile_in, pos_in, - realfile_out, pos_out, len, - flags); - break; + with_ovl_creds(file_inode(file_out)->i_sb) { + switch (op) { + case OVL_COPY: + ret = vfs_copy_file_range(realfile_in, pos_in, + realfile_out, pos_out, len, flags); + break; + + case OVL_CLONE: + ret = vfs_clone_file_range(realfile_in, pos_in, + realfile_out, pos_out, len, flags); + break; + + case OVL_DEDUPE: + ret = vfs_dedupe_file_range_one(realfile_in, pos_in, + realfile_out, pos_out, len, + flags); + break; + } } - ovl_revert_creds(old_cred); /* Update size */ ovl_file_modified(file_out); From 9ec22c8113d8cf72ed7197bb61037dcad09e50d8 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 12 Jul 2026 14:24:21 +0200 Subject: [PATCH 2499/3297] ovl: check access to copy_file_range source with src mounter creds [ Upstream commit a1e0eb8f55cfe09bb31a202a388babc411292656 ] Commit 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices") allowed filesystems that implement the copy_file_range() f_op to decide if they want to access cross-sb copy from/to the same fs type. The same commit added checks to verify same sb copy for filesystems that implement ->copy_file_range() and do not support cross-sb copy at the time, namely, to ceph, fuse and nfs. The two remaining fs which implement ->copy_file_range(), cifs and overlayfs started to support cross-sb copy from this time. While overlayfs does support cross-sb copy when the two underlying files are on the same base fs, the copy operation on the two real files from two different overalyfs filesystems is performed with the mounter creds of the destination overlayfs and the read permission access hook for the source file was called with the wrong creds. This could cause either deny of access to copy which would otherwise be allowed (e.g. with splice) or allow read access to file which would otherwise be denied. Fix the latter case by explicitly verifying read access to source file with the source overlayfs mounter creds. The former case remains a quirk of cross-sb overlayfs copy, but userspace could fall back to regular copy so no harm done. Fixes: 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices") Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260712122421.203113-1-amir73il@gmail.com Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/overlayfs/file.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 4374fbad3fc43..a857ce377b0cf 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -541,6 +541,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, loff_t len, unsigned int flags, enum ovl_copyop op) { + struct inode *inode_in = file_inode(file_in); struct inode *inode_out = file_inode(file_out); struct file *realfile_in, *realfile_out; loff_t ret; @@ -564,7 +565,20 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, if (IS_ERR(realfile_in)) goto out_unlock; - with_ovl_creds(file_inode(file_out)->i_sb) { + /* + * For cross-sb copy, vfs_copy_file_range() will verify read access with + * the mounter creds of the dest fs mounter, so we need to explicitly + * verify read access with the source mounter creds. + */ + if (unlikely(inode_in->i_sb != inode_out->i_sb)) { + with_ovl_creds(inode_in->i_sb) { + ret = rw_verify_area(READ, realfile_in, &pos_in, len); + if (unlikely(ret)) + goto out_unlock; + } + } + + with_ovl_creds(inode_out->i_sb) { switch (op) { case OVL_COPY: ret = vfs_copy_file_range(realfile_in, pos_in, From ca0e8b661957f777591efe874cd9d9a63619cd99 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 11 Jul 2026 11:19:33 -0400 Subject: [PATCH 2500/3297] amt: re-read skb header pointers after every pull [ Upstream commit 3656a79f94c471827a08f2cacce5f94ad5e52c24 ] Several AMT receive and transmit paths cache a pointer into the skb head (ip_hdr(), ipv6_hdr(), eth_hdr() or the AMT message header) and then call a helper that can reallocate that head before the cached pointer is used again. pskb_may_pull(), ip_mc_may_pull(), ipv6_mc_may_pull(), iptunnel_pull_header(), ip_mc_check_igmp() and ipv6_mc_check_mld() can all free the old head and move the data, so a pointer taken before the call dangles afterwards and the later access is a use-after-free of the freed head. The affected sites are: amt_rcv() caches ip_hdr() before amt_parse_type() pulls, then reads iph->saddr. amt_dev_xmit() caches ip_hdr()/ipv6_hdr() before ip_mc_check_igmp()/ ipv6_mc_check_mld() and pskb_may_pull(), then reads the group address. amt_multicast_data_handler() caches eth_hdr() before pskb_may_pull(), then writes the L2 header. amt_membership_query_handler() caches the AMT header, the outer and inner eth_hdr() and ip_hdr() before iptunnel_pull_header() and several pulls, then reads and writes them. amt_igmpv3_report_handler() and amt_mldv2_report_handler() cache ip_hdr()/ipv6_hdr() and the current group record and read the record count from the report header inside the record loop, across the *_mc_may_pull() calls. amt_update_handler() caches ip_hdr() and the AMT membership-update header before pskb_may_pull(), iptunnel_pull_header(), ip_mc_check_igmp() and the report handler, then reads iph->daddr and amtmu->nonce / amtmu->response_mac. Fix each site by either snapshotting the scalar that is used after the pull before the first pull runs, or re-deriving the header pointer from the skb after the last pull that can move the head. Values that are stable across the pull (source and group address, the response MAC and nonce, the record count, the outer source MAC) are snapshotted; pointers that are written through or read repeatedly are re-derived. Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface") Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Reviewed-by: Taehee Yoo Link: https://patch.msgid.link/20260711151934.2955226-2-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/amt.c | 79 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index e4d1aa26d7bbd..76e0ab70d8273 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -1211,7 +1211,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) data = true; } v6 = false; - group.ip4 = iph->daddr; + group.ip4 = ip_hdr(skb)->daddr; #if IS_ENABLED(CONFIG_IPV6) } else if (iph->version == 6) { ip6h = ipv6_hdr(skb); @@ -1235,7 +1235,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) data = true; } v6 = true; - group.ip6 = ip6h->daddr; + group.ip6 = ipv6_hdr(skb)->daddr; #endif } else { dev->stats.tx_errors++; @@ -1278,12 +1278,12 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) hlist_for_each_entry_rcu(gnode, &tunnel->groups[hash], node) { if (!v6) { - if (gnode->group_addr.ip4 == iph->daddr) + if (gnode->group_addr.ip4 == group.ip4) goto found; #if IS_ENABLED(CONFIG_IPV6) } else { if (ipv6_addr_equal(&gnode->group_addr.ip6, - &ip6h->daddr)) + &group.ip6)) goto found; #endif } @@ -2000,14 +2000,18 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb, struct igmpv3_report *ihrv3 = igmpv3_report_hdr(skb); int len = skb_transport_offset(skb) + sizeof(*ihrv3); void *zero_grec = (void *)&igmpv3_zero_grec; - struct iphdr *iph = ip_hdr(skb); struct amt_group_node *gnode; union amt_addr group, host; struct igmpv3_grec *grec; + __be32 saddr; u16 nsrcs; + u16 ngrec; int i; - for (i = 0; i < ntohs(ihrv3->ngrec); i++) { + saddr = ip_hdr(skb)->saddr; + ngrec = ntohs(ihrv3->ngrec); + + for (i = 0; i < ngrec; i++) { len += sizeof(*grec); if (!ip_mc_may_pull(skb, len)) break; @@ -2019,10 +2023,13 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb, if (!ip_mc_may_pull(skb, len)) break; + grec = (void *)(skb->data + len - sizeof(*grec) - + nsrcs * sizeof(__be32)); + memset(&group, 0, sizeof(union amt_addr)); group.ip4 = grec->grec_mca; memset(&host, 0, sizeof(union amt_addr)); - host.ip4 = iph->saddr; + host.ip4 = saddr; gnode = amt_lookup_group(tunnel, &group, &host, false); if (!gnode) { gnode = amt_add_group(amt, tunnel, &group, &host, @@ -2162,14 +2169,18 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb, struct mld2_report *mld2r = (struct mld2_report *)icmp6_hdr(skb); int len = skb_transport_offset(skb) + sizeof(*mld2r); void *zero_grec = (void *)&mldv2_zero_grec; - struct ipv6hdr *ip6h = ipv6_hdr(skb); struct amt_group_node *gnode; union amt_addr group, host; struct mld2_grec *grec; + struct in6_addr saddr; u16 nsrcs; + u16 ngrec; int i; - for (i = 0; i < ntohs(mld2r->mld2r_ngrec); i++) { + saddr = ipv6_hdr(skb)->saddr; + ngrec = ntohs(mld2r->mld2r_ngrec); + + for (i = 0; i < ngrec; i++) { len += sizeof(*grec); if (!ipv6_mc_may_pull(skb, len)) break; @@ -2181,10 +2192,13 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb, if (!ipv6_mc_may_pull(skb, len)) break; + grec = (void *)(skb->data + len - sizeof(*grec) - + nsrcs * sizeof(struct in6_addr)); + memset(&group, 0, sizeof(union amt_addr)); group.ip6 = grec->grec_mca; memset(&host, 0, sizeof(union amt_addr)); - host.ip6 = ip6h->saddr; + host.ip6 = saddr; gnode = amt_lookup_group(tunnel, &group, &host, true); if (!gnode) { gnode = amt_add_group(amt, tunnel, &group, &host, @@ -2305,7 +2319,6 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) skb_push(skb, sizeof(*eth)); skb_reset_mac_header(skb); skb_pull(skb, sizeof(*eth)); - eth = eth_hdr(skb); if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2315,6 +2328,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) if (!ipv4_is_multicast(iph->daddr)) return true; skb->protocol = htons(ETH_P_IP); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IP); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) @@ -2328,6 +2342,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) if (!ipv6_addr_is_multicast(&ip6h->daddr)) return true; skb->protocol = htons(ETH_P_IPV6); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IPV6); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif @@ -2351,10 +2366,12 @@ static bool amt_membership_query_handler(struct amt_dev *amt, struct sk_buff *skb) { struct amt_header_membership_query *amtmq; - struct igmpv3_query *ihv3; struct ethhdr *eth, *oeth; + struct igmpv3_query *ihv3; + u8 h_source[ETH_ALEN]; struct iphdr *iph; int hdr_size, len; + u64 response_mac; hdr_size = sizeof(*amtmq) + sizeof(struct udphdr); if (!pskb_may_pull(skb, hdr_size)) @@ -2367,6 +2384,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt, if (amtmq->nonce != amt->nonce) return true; + response_mac = amtmq->response_mac; + hdr_size -= sizeof(*eth); if (iptunnel_pull_header(skb, hdr_size, htons(ETH_P_TEB), false)) return true; @@ -2376,6 +2395,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_pull(skb, sizeof(*eth)); skb_reset_network_header(skb); eth = eth_hdr(skb); + ether_addr_copy(h_source, oeth->h_source); if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2388,6 +2408,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, sizeof(*ihv3))) return true; + iph = ip_hdr(skb); if (!ipv4_is_multicast(iph->daddr)) return true; @@ -2395,10 +2416,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_transport_header(skb); skb_push(skb, sizeof(*iph) + AMT_IPHDR_OPTS); WRITE_ONCE(amt->ready4, true); - amt->mac = amtmq->response_mac; + amt->mac = response_mac; amt->req_cnt = 0; amt->qi = ihv3->qqic; skb->protocol = htons(ETH_P_IP); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IP); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) @@ -2421,10 +2443,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_transport_header(skb); skb_push(skb, sizeof(*ip6h) + AMT_IP6HDR_OPTS); WRITE_ONCE(amt->ready6, true); - amt->mac = amtmq->response_mac; + amt->mac = response_mac; amt->req_cnt = 0; amt->qi = mld2q->mld2q_qqic; skb->protocol = htons(ETH_P_IPV6); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IPV6); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif @@ -2432,7 +2455,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, return true; } - ether_addr_copy(eth->h_source, oeth->h_source); + ether_addr_copy(eth->h_source, h_source); skb->pkt_type = PACKET_MULTICAST; skb->ip_summed = CHECKSUM_NONE; len = skb->len; @@ -2455,8 +2478,11 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) struct ethhdr *eth; struct iphdr *iph; int len, hdr_size; + u64 response_mac; + __be32 saddr; + __be32 nonce; - iph = ip_hdr(skb); + saddr = ip_hdr(skb)->saddr; hdr_size = sizeof(*amtmu) + sizeof(struct udphdr); if (!pskb_may_pull(skb, hdr_size)) @@ -2466,15 +2492,18 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) if (amtmu->reserved || amtmu->version) return true; + nonce = amtmu->nonce; + response_mac = amtmu->response_mac; + if (iptunnel_pull_header(skb, hdr_size, skb->protocol, false)) return true; skb_reset_network_header(skb); list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) { - if (tunnel->ip4 == iph->saddr) { - if ((amtmu->nonce == tunnel->nonce && - amtmu->response_mac == tunnel->mac)) { + if (tunnel->ip4 == saddr) { + if ((nonce == tunnel->nonce && + response_mac == tunnel->mac)) { mod_delayed_work(amt_wq, &tunnel->gc_wq, msecs_to_jiffies(amt_gmi(amt)) * 3); @@ -2508,6 +2537,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) eth = eth_hdr(skb); skb->protocol = htons(ETH_P_IP); eth->h_proto = htons(ETH_P_IP); + iph = ip_hdr(skb); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) } else if (iph->version == 6) { @@ -2527,6 +2557,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) eth = eth_hdr(skb); skb->protocol = htons(ETH_P_IPV6); eth->h_proto = htons(ETH_P_IPV6); + ip6h = ipv6_hdr(skb); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif } else { @@ -2772,7 +2803,7 @@ static void amt_gw_rcv(struct amt_dev *amt, struct sk_buff *skb) static int amt_rcv(struct sock *sk, struct sk_buff *skb) { struct amt_dev *amt; - struct iphdr *iph; + __be32 saddr; int type; bool err; @@ -2785,7 +2816,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) } skb->dev = amt->dev; - iph = ip_hdr(skb); + saddr = ip_hdr(skb)->saddr; type = amt_parse_type(skb); if (type == -1) { err = true; @@ -2795,7 +2826,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) if (amt->mode == AMT_MODE_GATEWAY) { switch (type) { case AMT_MSG_ADVERTISEMENT: - if (iph->saddr != amt->discovery_ip) { + if (saddr != amt->discovery_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; @@ -2807,7 +2838,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) } goto out; case AMT_MSG_MULTICAST_DATA: - if (iph->saddr != amt->remote_ip) { + if (saddr != amt->remote_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; @@ -2818,7 +2849,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) else goto out; case AMT_MSG_MEMBERSHIP_QUERY: - if (iph->saddr != amt->remote_ip) { + if (saddr != amt->remote_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; From 0a347ca1d6a2e3700b13c2df7af498ba5698eeb7 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 11 Jul 2026 11:19:34 -0400 Subject: [PATCH 2501/3297] amt: make the head writable before rewriting the L2 header [ Upstream commit 53969d704fa5b7c1751e277fac96bfc22b435eac ] amt_multicast_data_handler(), amt_membership_query_handler() and amt_update_handler() rewrite the ethernet header of the decapsulated skb in place (eth->h_proto, eth->h_dest and, for the query, also eth->h_source) before handing it up the stack. The skb head may be shared, for example when a packet tap has cloned it on the underlay interface, so writing through it corrupts the other reader's copy. Call skb_cow_head() before the rewrite so the head is private. It is placed before the pointers into the head are (re-)derived, so a reallocation caused by the copy is picked up by those derivations. Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface") Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Reviewed-by: Taehee Yoo Link: https://patch.msgid.link/20260711151934.2955226-3-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/amt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 76e0ab70d8273..cddcad172bbce 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -2320,6 +2320,9 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) skb_reset_mac_header(skb); skb_pull(skb, sizeof(*eth)); + if (skb_cow_head(skb, 0)) + return true; + if (!pskb_may_pull(skb, sizeof(*iph))) return true; iph = ip_hdr(skb); @@ -2396,6 +2399,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_network_header(skb); eth = eth_hdr(skb); ether_addr_copy(h_source, oeth->h_source); + if (skb_cow_head(skb, 0)) + return true; if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2521,6 +2526,9 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(*iph))) return true; + if (skb_cow_head(skb, 0)) + return true; + iph = ip_hdr(skb); if (iph->version == 4) { if (ip_mc_check_igmp(skb)) { From 17bb59682b8e6a5ab57a0a859884d1208d57405c Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 21 Jul 2026 17:09:21 +0300 Subject: [PATCH 2502/3297] net: bridge: vlan: fix vlan range dumps starting with pvid [ Upstream commit 43171c97e4714bf601b468401b37732244639c21 ] There is a bug in all range dumps that rely on br_vlan_can_enter_range() when the PVID is a range starting VLAN, all following VLANs that match its flags can enter the range, but when the range is filled in only the PVID VLAN is dumped and the rest of the range is discarded because br_vlan_fill_vids() checks for the PVID flag. Since the PVID VLAN can be only one, we need to break ranges around it, the best way to do that consistently for all is to alter br_vlan_can_enter_range() to take into account the PVID and return false to break the range when it's matched. Before the fix: $ ip l add br0 type bridge vlan_filtering 1 $ ip l add dumdum type dummy $ ip l set dumdum master br0 $ ip l set br0 up $ ip l set dumdum up $ bridge vlan add dev dumdum vid 1 pvid untagged master $ bridge vlan add dev dumdum vid 2 untagged master $ bridge vlan show dev dumdum # use legacy dump to show all vlans port vlan-id dumdum 1 PVID Egress Untagged 2 Egress Untagged $ bridge -d vlan show dev dumdum # use the new dump (RTM_GETVLAN) port vlan-id dumdum 1 PVID Egress Untagged state forwarding mcast_router 1 VLAN 2 is missing, and if there are more matching VLANs afterwards they'd be missing too. After the fix: [ same setup steps ] $ bridge vlan show dev dumdum port vlan-id dumdum 1 PVID Egress Untagged 2 Egress Untagged $ bridge -d vlan show dev dumdum # use the new dump (RTM_GETVLAN) port vlan-id dumdum 1 PVID Egress Untagged state forwarding mcast_router 1 2 Egress Untagged state forwarding mcast_router 1 Fixes: 0ab558795184 ("net: bridge: vlan: add rtm range support") Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260721140922.682265-2-razor@blackwall.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_netlink_tunnel.c | 3 ++- net/bridge/br_private.h | 6 ++++-- net/bridge/br_vlan.c | 10 ++++++---- net/bridge/br_vlan_options.c | 3 +-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c index 71a12da30004c..a713668ea34f0 100644 --- a/net/bridge/br_netlink_tunnel.c +++ b/net/bridge/br_netlink_tunnel.c @@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p, if (!*v_start) goto out_init; - if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) { + if (v && curr_change && + br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) { *v_end = v; return; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index b9b2981c48414..2b451f3f72a6a 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1613,7 +1613,8 @@ void br_vlan_notify(const struct net_bridge *br, u16 vid, u16 vid_range, int cmd); bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end); + const struct net_bridge_vlan *range_end, + u16 pvid); void br_vlan_fill_forward_path_pvid(struct net_bridge *br, struct net_device_path_ctx *ctx, @@ -1860,7 +1861,8 @@ static inline void br_vlan_notify(const struct net_bridge *br, } static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end) + const struct net_bridge_vlan *range_end, + u16 pvid) { return true; } diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index ce72b837ff8ee..a4ba725ad1dff 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -1977,9 +1977,11 @@ void br_vlan_notify(const struct net_bridge *br, /* check if v_curr can enter a range ending in range_end */ bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end) + const struct net_bridge_vlan *range_end, + u16 pvid) { - return v_curr->vid - range_end->vid == 1 && + return v_curr->vid != pvid && range_end->vid != pvid && + v_curr->vid - range_end->vid == 1 && range_end->flags == v_curr->flags && br_vlan_opts_eq_range(v_curr, range_end); } @@ -2061,8 +2063,8 @@ static int br_vlan_dump_dev(const struct net_device *dev, idx += range_end->vid - range_start->vid + 1; range_start = v; - } else if (dump_stats || v->vid == pvid || - !br_vlan_can_enter_range(v, range_end)) { + } else if (dump_stats || + !br_vlan_can_enter_range(v, range_end, pvid)) { u16 vlan_flags = br_vlan_flags(range_start, pvid); if (!br_vlan_fill_vids(skb, range_start->vid, diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index 8fa89b04ee942..4a736e005bad7 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -310,8 +310,7 @@ int br_vlan_process_options(const struct net_bridge *br, continue; } - if (v->vid == pvid || - !br_vlan_can_enter_range(v, curr_end)) { + if (!br_vlan_can_enter_range(v, curr_end, pvid)) { br_vlan_notify(br, p, curr_start->vid, curr_end->vid, RTM_NEWVLAN); curr_start = v; From b5ded444621b6180df9f3d4e07045fc1fc1e8cd9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 21 Jul 2026 10:12:40 +0000 Subject: [PATCH 2503/3297] net: hsr: fix memory leak on slave unregistration by removing synced VLANs [ Upstream commit dcf15eaf5641812f1cfc5e96537380132a7da89d ] When an HSR master device is brought UP, it auto-adds VLAN 0 via vlan_vid0_add(), which propagates VID 0 to its slave devices (slave A and B). If a slave device is later unregistered while HSR is active (e.g., during netns cleanup or interface destruction), hsr_del_port() is called to detach the slave port from the HSR master. However, hsr_del_port() currently does not delete the VLAN IDs that were synced to the slave device by HSR. As a result, the slave device retains a refcount on VID 0 (and any other synced VLANs). When the slave device is destroyed, its vlan_info / vlan_vid_info structure remains allocated, leading to a memory leak. Fix this by calling vlan_vids_del_by_dev(port->dev, master->dev) in hsr_del_port() before unlinking slave A or slave B ports, matching the propagation logic in hsr_ndo_vlan_rx_add_vid() / hsr_ndo_vlan_rx_kill_vid() and the cleanup behavior in bonding and team drivers. Fixes: 1a8a63a5305e ("net: hsr: Add VLAN CTAG filter support") Reported-by: syzbot+456957213f32970c0762@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a4cb6ca.57639fcc.86d58.000b.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Fernando Fernandez Mancera Reviewed-by: Felix Maurer Link: https://patch.msgid.link/20260721101240.995597-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/hsr/hsr_slave.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index afe06ba00ea44..474f44f1ee88c 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -242,6 +242,8 @@ void hsr_del_port(struct hsr_port *port) netdev_rx_handler_unregister(port->dev); if (!port->hsr->fwd_offloaded) dev_set_promiscuity(port->dev, -1); + if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) + vlan_vids_del_by_dev(port->dev, master->dev); netdev_upper_dev_unlink(port->dev, master->dev); eth_hw_addr_set(port->dev, port->original_macaddress); } From ae0ec759865e02ef39574dc8e9ea5f295f94d403 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 17 Jul 2026 15:20:29 +0200 Subject: [PATCH 2504/3297] net: dpaa: fix mode setting [ Upstream commit da2c6bcc5e30b1496ac587785dcacf6e849eb6ef ] Before converting to the phylink interface, the init function would have set a non-reserved I/F mode in the maccfg2 register. After converting to phylink, 0 is written as mode, which is a reserved value (although it's the hardware default). Without a valid mode, a SGMII link is never established between the MAC and the PHY and thus .link_up() is never called which could set the correct mode according to the actual speed. Fix it by setting the maximum speed of the phy_interface_t in use in .mac_config() - just like the driver did before the phylink conversion. Fixes: 5d93cfcf7360 ("net: dpaa: Convert to phylink") Suggested-by: Sean Anderson Signed-off-by: Michael Walle Reviewed-by: Sean Anderson Reviewed-by: Sean Anderson Link: https://patch.msgid.link/20260717132401.2653252-1-mwalle@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../net/ethernet/freescale/fman/fman_dtsec.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 51402dff72c5f..52f13b61bc699 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -900,22 +900,28 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, { struct mac_device *mac_dev = fman_config_to_mac(config); struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs; - u32 tmp; + u32 ecntrl, maccfg2; + + maccfg2 = ioread32be(®s->maccfg2); + maccfg2 &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); switch (state->interface) { case PHY_INTERFACE_MODE_RMII: - tmp = DTSEC_ECNTRL_RMM; + ecntrl = DTSEC_ECNTRL_RMM; + maccfg2 |= MACCFG2_NIBBLE_MODE; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; + ecntrl = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; + maccfg2 |= MACCFG2_BYTE_MODE; break; case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: - tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; + ecntrl = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; + maccfg2 |= MACCFG2_BYTE_MODE; break; default: dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n", @@ -923,7 +929,8 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, return; } - iowrite32be(tmp, ®s->ecntrl); + iowrite32be(ecntrl, ®s->ecntrl); + iowrite32be(maccfg2, ®s->maccfg2); } static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy, From 18957373920caf5cdaf5cf32e5d1d7a99ca7700a Mon Sep 17 00:00:00 2001 From: Qing Luo Date: Tue, 21 Jul 2026 09:55:32 +0800 Subject: [PATCH 2505/3297] sctp: auth: verify auth requirement when auth_chunk is NULL [ Upstream commit 8e04823c120b376ef7dab14b60ebf6823aa16c14 ] sctp_auth_chunk_verify() returns true unconditionally when chunk->auth_chunk is NULL, silently skipping authentication. This is incorrect when: 1. skb_clone() failed in the BH receive path, leaving auth_chunk NULL. In sctp_endpoint_bh_rcv() asoc is NULL for new connections, so the early sctp_auth_recv_cid() check cannot catch this. 2. No AUTH chunk precedes COOKIE-ECHO, so skb_clone() is never called and auth_chunk remains NULL. Fix by checking sctp_auth_recv_cid() when auth_chunk is NULL: if authentication is required, return false to drop the chunk; otherwise continue normally. Fixes: bbd0d59809f9 ("[SCTP]: Implement the receive and verification of AUTH chunk") Signed-off-by: Qing Luo Acked-by: Xin Long Link: https://patch.msgid.link/20260721015532.120157-2-l1138897701@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/sm_statefuns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index abfd72d1f20c6..4b8d48b6706c8 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -641,7 +641,7 @@ static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk, struct sctp_chunk auth; if (!chunk->auth_chunk) - return true; + return !sctp_auth_recv_cid(chunk->chunk_hdr->type, asoc); /* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo * is supposed to be authenticated and we have to do delayed From 4fdb0f162ccdbe9626863b10003855703253fa29 Mon Sep 17 00:00:00 2001 From: Harshaka Narayana Date: Mon, 13 Jul 2026 07:09:15 -0700 Subject: [PATCH 2506/3297] vmxnet3: fix BUG_ON in vmxnet3_get_hdr_len() for Geneve packets [ Upstream commit 34a71f5361fc3adb5b7138da78750b0d535a8252 ] vmxnet3_get_hdr_len() assumes gdesc->rcd.v4/v6/tcp always describe the outer header, but for a Geneve-encapsulated packet the device can set them based on the inner header instead, signalled by the VMXNET3_RCD_HDR_INNER_SHIFT bit in the completion descriptor. Since the function never skips the outer encapsulation, this mismatch triggers: - BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP), because the outer protocol is UDP (Geneve), not TCP. - BUG_ON(hdr.eth->h_proto != ...), when the tunnel's outer and inner IP versions differ (e.g. outer IPv6/inner IPv4 or vice versa). Check VMXNET3_RCD_HDR_INNER_SHIFT up front and bail out, since the function cannot locate the inner header it would need to parse. Also convert the remaining BUG_ON()s in this function to return 0 defensively. Fixes: 45dac1d6ea04 ("vmxnet3: Changes for vmxnet3 adapter version 2 (fwd)") Signed-off-by: Harshaka Narayana Reviewed-by: Ronak Doshi Reviewed-by: Sankararaman Jayaraman Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260713140915.3381715-1-harshaka.narayana@broadcom.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/vmxnet3/vmxnet3_drv.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 0572f6a9bdb62..679208d587f85 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1530,7 +1530,11 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, struct ipv6hdr *ipv6; struct tcphdr *tcp; } hdr; - BUG_ON(gdesc->rcd.tcp == 0); + + /* v4/v6/tcp then describe the inner header, which we can't locate. */ + if ((le32_to_cpu(gdesc->dword[0]) & (1UL << VMXNET3_RCD_HDR_INNER_SHIFT)) || + gdesc->rcd.tcp == 0) + return 0; maplen = skb_headlen(skb); if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen)) @@ -1544,15 +1548,21 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, hdr.eth = eth_hdr(skb); if (gdesc->rcd.v4) { - BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) && - hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP)); + if (hdr.eth->h_proto != htons(ETH_P_IP) && + hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP)) + return 0; + hdr.ptr += hlen; - BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP); + if (hdr.ipv4->protocol != IPPROTO_TCP) + return 0; + hlen = hdr.ipv4->ihl << 2; hdr.ptr += hdr.ipv4->ihl << 2; } else if (gdesc->rcd.v6) { - BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6) && - hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6)); + if (hdr.eth->h_proto != htons(ETH_P_IPV6) && + hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6)) + return 0; + hdr.ptr += hlen; /* Use an estimated value, since we also need to handle * TSO case. From d18d9b2c29a124f924cc9ce75ab23b51aeecfe5f Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Tue, 21 Jul 2026 17:04:38 +0530 Subject: [PATCH 2507/3297] drm/xe/i2c: Allow per domain unique id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5d8ed6b64220ad629aade5f174e3f690c37435f9 ] PCI bus, device and function can be same for devices existing across different domains. Allow per domain unique identifier while registering platform device to prevent name conflict. Fixes: f0e53aadd702 ("drm/xe: Support for I2C attached MCUs") Signed-off-by: Raag Jadav Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260721113438.651100-1-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit a79f6abc8b516b5bd906e2eca8121e3549ee163f) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 48dfcb41fa08c..2788dc6510d6a 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -94,18 +94,21 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c) struct platform_device *pdev; struct fwnode_handle *fwnode; int ret; + u32 id; fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL); if (IS_ERR(fwnode)) return PTR_ERR(fwnode); + id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci); + /* * Not using platform_device_register_full() here because we don't have * a handle to the platform_device before it returns. xe_i2c_notifier() * uses that handle, but it may be called before * platform_device_register_full() is done. */ - pdev = platform_device_alloc(adapter_name, pci_dev_id(pci)); + pdev = platform_device_alloc(adapter_name, id); if (!pdev) { ret = -ENOMEM; goto err_fwnode_remove; From 279339aa8bdcf9db40094cf2bcbd495c53dbe817 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Tue, 21 Jul 2026 20:55:14 +0000 Subject: [PATCH 2508/3297] drm/xe/vm: Fix SVM leak on resv obj alloc failure in xe_vm_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d2c6800ad1802bed72a6de1416536737f114f1d6 ] Commit 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") made xe_svm_init() unconditional in xe_vm_create() and extended it to also initialize a "simple" gpusvm state for non-fault-mode VMs. The matching xe_svm_fini() call in xe_vm_close_and_put() was updated to run unconditionally, but the error unwind path in xe_vm_create() was not. On the drm_gpuvm_resv_object_alloc() failure path, xe_svm_init() has already succeeded but xe_svm_fini() is only called when XE_VM_FLAG_FAULT_MODE is set. For non-fault-mode VMs this leaves vm->svm.gpusvm partially initialized and leaks the resources allocated by drm_gpusvm_init(). For fault-mode VMs, xe_svm_init() additionally acquires the pagemap owner via drm_pagemap_acquire_owner() and the pagemaps via xe_svm_get_pagemaps(). Those resources are released by xe_svm_close(), not xe_svm_fini(). On the same error path, xe_svm_close() is not called either, so fault-mode VMs leak the pagemap owner and pagemaps. Fix both leaks: - Call xe_svm_fini() unconditionally on the err_svm_fini path, matching the unconditional xe_svm_init() call. Move the vm->size = 0 assignment out of the conditional so the xe_vm_is_closed() assert in xe_svm_fini() (and xe_svm_close()) holds for both modes. - Call xe_svm_close() for fault-mode VMs before xe_svm_fini(), matching the ordering used in xe_vm_close_and_put(). Fixes: 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") Cc: Matthew Auld Assisted-by: Claude:claude-opus-4.7 Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260721205516.4058959-2-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit ca2a3587d577ba764e0fe628fb676244fc33ddd4) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_vm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 8f7b8f2da06ba..b00da90e39913 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1626,10 +1626,10 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef) return ERR_PTR(err); err_svm_fini: - if (flags & XE_VM_FLAG_FAULT_MODE) { - vm->size = 0; /* close the vm */ - xe_svm_fini(vm); - } + vm->size = 0; /* close the vm */ + if (flags & XE_VM_FLAG_FAULT_MODE) + xe_svm_close(vm); + xe_svm_fini(vm); err_no_resv: mutex_destroy(&vm->snap_mutex); for_each_tile(tile, xe, id) From c8e4b2a567efb6e794c3e0af517f22b09cde0ab2 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 14 Jul 2026 16:23:21 +0800 Subject: [PATCH 2509/3297] iomap: correct the range of a partial dirty clear [ Upstream commit 88c26515313169806a412a362b32a1eca53d21bd ] The block range calculation in ifs_clear_range_dirty() is incorrect when partially clearing a range in a folio. We cannot clear the dirty bit of the first block or the last block if the start or end offset is not blocksize-aligned. This has not yet caused any issues since we always clear a whole folio in iomap_writeback_folio(). Fix this by rounding up the first block to blocksize alignment, and calculate the last block by rounding down (using truncation). Correct the nr_blks calculation accordingly. Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance") Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260714082325.325163-2-yi.zhang@huaweicloud.com Reviewed-by: Joanne Koong Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/iomap/buffered-io.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index c0fa6acf19375..734854f864fd1 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -127,13 +127,17 @@ static void ifs_clear_range_dirty(struct folio *folio, { struct inode *inode = folio->mapping->host; unsigned int blks_per_folio = i_blocks_per_folio(inode, folio); - unsigned int first_blk = (off >> inode->i_blkbits); - unsigned int last_blk = (off + len - 1) >> inode->i_blkbits; - unsigned int nr_blks = last_blk - first_blk + 1; + unsigned int first_blk = round_up(off, i_blocksize(inode)) >> + inode->i_blkbits; + unsigned int last_blk = (off + len) >> inode->i_blkbits; unsigned long flags; + if (first_blk >= last_blk) + return; + spin_lock_irqsave(&ifs->state_lock, flags); - bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks); + bitmap_clear(ifs->state, first_blk + blks_per_folio, + last_blk - first_blk); spin_unlock_irqrestore(&ifs->state_lock, flags); } From 1b8fb5a20508bfb0db854e01214888c761b3a911 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Tue, 14 Jul 2026 00:15:41 -0400 Subject: [PATCH 2510/3297] tipc: fix u16 MTU truncation in media and bearer MTU validation [ Upstream commit 9f29cd8a8e7901a2617c8064ce9f50fc67b97cb8 ] Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied MTU values but only enforce a minimum bound, not a maximum. When a user sets the MTU to a value exceeding U16_MAX (65535), it passes validation but is silently truncated when assigned to u16 fields l->mtu and l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000) truncate to 0, causing a division by zero in tipc_link_set_queue_limits() which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing values (e.g. 65537-131071) produce small incorrect MTU values, resulting in link malfunction behaviors. Crash stack (triggered as unprivileged user via user namespace): tipc_link_set_queue_limits net/tipc/link.c:2531 tipc_link_create net/tipc/link.c:520 tipc_node_check_dest net/tipc/node.c:1279 tipc_disc_rcv net/tipc/discover.c:252 tipc_rcv net/tipc/node.c:2129 tipc_udp_recv net/tipc/udp_media.c:392 Two independent paths lack the upper bound check: 1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET) 2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET) Fix both by rejecting MTU values above U16_MAX. Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU") Reported-by: AutonomousCodeSecurity@microsoft.com Closes: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com Reviewed-by: Vadim Fedorenko Signed-off-by: Cen Zhang (Microsoft) Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260714041541.307702-1-blbllhy@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/tipc/netlink.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 8336a9664703f..1307dd1a96134 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { }; /* Properties valid for media, bearer and link */ +static const struct netlink_range_validation tipc_nl_mtu_range = { + .max = U16_MAX, +}; + const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC }, [TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 }, [TIPC_NLA_PROP_TOL] = { .type = NLA_U32 }, [TIPC_NLA_PROP_WIN] = { .type = NLA_U32 }, - [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 }, + [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range), [TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 }, [TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 } }; From b43bb9ab600357d229a8721ddcf4cc8238067a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Exp=C3=B3sito?= Date: Fri, 3 Jul 2026 17:07:43 +0200 Subject: [PATCH 2511/3297] drm/tests: shmem: Set DMA mask to 64-bit in drm_gem_shmem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b04a248cfa6cfa1e7dc9ce91cb1eb88b1a70dd69 ] drm_gem_shmem_test_purge [1] and drm_gem_shmem_test_get_pages_sgt [2] intermittently fail on ppc64le and s390x CI systems with a DMA address overflow: DMA addr 0x0000000100307000+4096 overflow (mask ffffffff, bus limit 0) WARNING: kernel/dma/direct.h:114 dma_direct_map_sg+0x778/0x920 drm_gem_shmem_test_purge: ASSERTION FAILED at drivers/gpu/drm/tests/drm_gem_shmem_test.c:330 Expected sgt is not error, but is: -5 The call chain leading to the failure is: drm_gem_shmem_test_purge() / drm_gem_shmem_test_get_pages_sgt() drm_gem_shmem_get_pages_sgt() drm_gem_shmem_get_pages_sgt_locked() [drm_gem_shmem_helper.c] dma_map_sgtable() [mapping.c] __dma_map_sg_attrs() dma_direct_map_sg() [direct.c] dma_direct_map_phys() [kernel/dma/direct.h] dma_capable() Checks addr against DMA mask -> FAILS: addr > 0xFFFFFFFF The root cause is that KUnit devices are initialized with a 32-bit DMA mask (DMA_BIT_MASK(32)) in lib/kunit/device.c. On ppc64le and s390x systems with physical memory above 4GB, page allocations can land at addresses that exceed this mask. When drm_gem_shmem_get_pages_sgt() attempts to DMA-map these pages via dma_map_sgtable(), the DMA layer rejects the mapping because the physical address overflows the 32-bit mask. The failure is intermittent because pages may or may not be allocated above 4GB on any given run depend on memory pressure. Fix by setting a 64-bit DMA mask on the device before calling drm_gem_shmem_get_pages_sgt() for all tests, following the same pattern already used in drm_gem_shmem_test_obj_create_private(). [1] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_s390x/15128551935/artifacts/jobwatch/logs/recipes/21561049/tasks/220716793/results/1014626315/logs/dmesg.log [2] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_ppc64le/15128551933/artifacts/jobwatch/logs/recipes/21561041/tasks/220716705/results/1014628163/logs/dmesg.log Fixes: 93032ae634d4 ("drm/test: add a test suite for GEM objects backed by shmem") Closes: https://datawarehouse.cki-project.org/issue/5345 Closes: https://datawarehouse.cki-project.org/issue/3184 Assisted-by: Claude:claude-4.6-opus Reviewed-by: Thomas Zimmermann Signed-off-by: José Expósito Link: https://patch.msgid.link/20260703150808.3832-1-jose.exposito89@gmail.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/tests/drm_gem_shmem_test.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tests/drm_gem_shmem_test.c b/drivers/gpu/drm/tests/drm_gem_shmem_test.c index 4b459f21acfd9..b1fa287892fb8 100644 --- a/drivers/gpu/drm/tests/drm_gem_shmem_test.c +++ b/drivers/gpu/drm/tests/drm_gem_shmem_test.c @@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test) sg_init_one(sgt->sgl, buf, TEST_SIZE); /* - * Set the DMA mask to 64-bits and map the sgtables - * otherwise drm_gem_shmem_free will cause a warning - * on debug kernels. + * Map the sgtables otherwise drm_gem_shmem_free will cause a warning on + * debug kernels. */ - ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64)); - KUNIT_ASSERT_EQ(test, ret, 0); - ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -352,11 +348,19 @@ static int drm_gem_shmem_test_init(struct kunit *test) { struct device *dev; struct drm_device *drm_dev; + int ret; /* Allocate a parent device */ dev = drm_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + /* + * Set the DMA mask to 64-bits to avoid intermittent failures calling + * drm_gem_shmem_get_pages_sgt(). + */ + ret = dma_set_mask(dev, DMA_BIT_MASK(64)); + KUNIT_ASSERT_EQ(test, ret, 0); + /* * The DRM core will automatically initialize the GEM core and create * a DRM Memory Manager object which provides an address space pool From 9f27c4f0ae35b5390ce4f7a54d3501144e41a54d Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Mon, 13 Jul 2026 23:32:30 +0000 Subject: [PATCH 2512/3297] bpf: tcp: fix double sock release on batch realloc [ Upstream commit 980a813452754f8001704744e92f7aa697c53dd3 ] bpf_iter_tcp_batch() releases the current batch via bpf_iter_tcp_put_batch(), which drops the socket refs and rewrites each slot with the socket cookie, then grows the batch. cur_sk/end_sk are kept for bpf_iter_tcp_resume(), but on realloc failure the function returns ERR_PTR() before resume runs, leaving cur_sk < end_sk over slots that now hold cookies rather than sock pointers. bpf_iter_tcp_seq_stop() then calls bpf_iter_tcp_put_batch() again and dereferences a cookie as a struct sock. Empty the batch on the failure path so stop() does not release it again. The sockets were already freed by the first bpf_iter_tcp_put_batch(), so nothing leaks, and a later read() rescans the bucket from the start instead of skipping it. The sibling GFP_NOWAIT failure path still holds real socket references and is left for stop() to release. BUG: KASAN: null-ptr-deref in __sock_gen_cookie Read of size 8 at addr 0000000000000059 by task exploit ... __sock_gen_cookie (net/core/sock_diag.c:28) bpf_iter_tcp_put_batch (net/ipv4/tcp_ipv4.c:2918) bpf_iter_tcp_seq_stop (net/ipv4/tcp_ipv4.c:3270) bpf_seq_read (kernel/bpf/bpf_iter.c:205) vfs_read (fs/read_write.c:572) ksys_read (fs/read_write.c:716) do_syscall_64 entry_SYSCALL_64_after_hwframe Kernel panic - not syncing: Fatal exception Fixes: cdec67a489d4 ("bpf: tcp: Make sure iter->batch always contains a full bucket snapshot") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Eric Dumazet Reviewed-by: Jordan Rife Link: https://patch.msgid.link/20260713233230.3553593-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/tcp_ipv4.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a1ed4fa264836..7efb49f9e5b3b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -3284,8 +3284,11 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) bpf_iter_tcp_put_batch(iter); err = bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2, GFP_USER); - if (err) + if (err) { + iter->cur_sk = 0; + iter->end_sk = 0; return ERR_PTR(err); + } sk = bpf_iter_tcp_resume(seq); if (!sk) From d67136a931e5f76f476b72b685a3ef18b1da623f Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 16 Oct 2025 15:36:51 +0100 Subject: [PATCH 2513/3297] net: stmmac: remove xstats.pcs_* members [ Upstream commit 14f74bc6dc699f63b5a6dfa9b22229f0caea89f3 ] As a result of the previous commit, the pcs_link, pcs_duplex and pcs_speed members are not used outside of the interrupt handling code, and are only used to print their status using the misleading "Link is" messages that bear no relation to the actual status of the link. Remove the printing of these messages, these members, and the code that decodes them from the hardware. Reviewed-by: Andrew Lunn Signed-off-by: Russell King (Oracle) Tested-by: Maxime Chevallier Tested-by: Lad Prabhakar Link: https://patch.msgid.link/E1v9P63-0000000AolI-23Kf@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski Stable-dep-of: 9fcf274d93af ("net: stmmac: xgmac: fix l4 filter port overwrite on register update") Signed-off-by: Sasha Levin --- drivers/net/ethernet/stmicro/stmmac/common.h | 3 -- .../ethernet/stmicro/stmmac/dwmac1000_core.c | 28 +------------------ .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 28 +------------------ 3 files changed, 2 insertions(+), 57 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index acd7719506b61..83929477b058b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -197,9 +197,6 @@ struct stmmac_extra_stats { unsigned long irq_pcs_ane_n; unsigned long irq_pcs_link_n; unsigned long irq_rgmii_n; - unsigned long pcs_link; - unsigned long pcs_duplex; - unsigned long pcs_speed; /* debug register */ unsigned long mtl_tx_status_fifo_full; unsigned long mtl_tx_fifo_not_empty; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index fe776ddf68895..2c5ee59c32086 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -266,34 +266,8 @@ static void dwmac1000_pmt(struct mac_device_info *hw, unsigned long mode) /* RGMII or SMII interface */ static void dwmac1000_rgsmii(void __iomem *ioaddr, struct stmmac_extra_stats *x) { - u32 status; - - status = readl(ioaddr + GMAC_RGSMIIIS); + readl(ioaddr + GMAC_RGSMIIIS); x->irq_rgmii_n++; - - /* Check the link status */ - if (status & GMAC_RGSMIIIS_LNKSTS) { - int speed_value; - - x->pcs_link = 1; - - speed_value = ((status & GMAC_RGSMIIIS_SPEED) >> - GMAC_RGSMIIIS_SPEED_SHIFT); - if (speed_value == GMAC_RGSMIIIS_SPEED_125) - x->pcs_speed = SPEED_1000; - else if (speed_value == GMAC_RGSMIIIS_SPEED_25) - x->pcs_speed = SPEED_100; - else - x->pcs_speed = SPEED_10; - - x->pcs_duplex = (status & GMAC_RGSMIIIS_LNKMOD_MASK); - - pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed, - x->pcs_duplex ? "Full" : "Half"); - } else { - x->pcs_link = 0; - pr_info("Link is Down\n"); - } } static int dwmac1000_irq_status(struct mac_device_info *hw, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index d85bc0bb5c3c0..8a19df7b05775 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -592,34 +592,8 @@ static void dwmac4_ctrl_ane(struct stmmac_priv *priv, bool ane, bool srgmi_ral, /* RGMII or SMII interface */ static void dwmac4_phystatus(void __iomem *ioaddr, struct stmmac_extra_stats *x) { - u32 status; - - status = readl(ioaddr + GMAC_PHYIF_CONTROL_STATUS); + readl(ioaddr + GMAC_PHYIF_CONTROL_STATUS); x->irq_rgmii_n++; - - /* Check the link status */ - if (status & GMAC_PHYIF_CTRLSTATUS_LNKSTS) { - int speed_value; - - x->pcs_link = 1; - - speed_value = ((status & GMAC_PHYIF_CTRLSTATUS_SPEED) >> - GMAC_PHYIF_CTRLSTATUS_SPEED_SHIFT); - if (speed_value == GMAC_PHYIF_CTRLSTATUS_SPEED_125) - x->pcs_speed = SPEED_1000; - else if (speed_value == GMAC_PHYIF_CTRLSTATUS_SPEED_25) - x->pcs_speed = SPEED_100; - else - x->pcs_speed = SPEED_10; - - x->pcs_duplex = (status & GMAC_PHYIF_CTRLSTATUS_LNKMOD); - - pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed, - x->pcs_duplex ? "Full" : "Half"); - } else { - x->pcs_link = 0; - pr_info("Link is Down\n"); - } } static int dwmac4_irq_mtl_status(struct stmmac_priv *priv, From dac8c2ab943a2698dc8a2d59cb7bfa14c6345238 Mon Sep 17 00:00:00 2001 From: Rohan G Thomas Date: Sat, 1 Nov 2025 01:27:07 +0800 Subject: [PATCH 2514/3297] net: stmmac: socfpga: Agilex5 EMAC platform configuration [ Upstream commit 93d46ea3e984323fae0e5d2919cf5817e1297d41 ] Agilex5 HPS EMAC uses the dwxgmac-3.10a IP, unlike previous socfpga platforms which use dwmac1000 IP. Due to differences in platform configuration, Agilex5 requires a distinct setup. Introduce a setup_plat_dat() callback in socfpga_dwmac_ops to handle platform-specific setup. This callback is invoked before stmmac_dvr_probe() to ensure the platform data is correctly configured. Also, implemented separate setup_plat_dat() callback for current socfpga platforms and Agilex5. Signed-off-by: Rohan G Thomas Reviewed-by: Maxime Chevallier Tested-by: Maxime Chevallier Link: https://patch.msgid.link/20251101-agilex5_ext-v2-1-a6b51b4dca4d@altera.com Signed-off-by: Jakub Kicinski Stable-dep-of: 9fcf274d93af ("net: stmmac: xgmac: fix l4 filter port overwrite on register update") Signed-off-by: Sasha Levin --- .../ethernet/stmicro/stmmac/dwmac-socfpga.c | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 2ff5db6d41ca0..5666b01723643 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -44,6 +44,7 @@ struct socfpga_dwmac; struct socfpga_dwmac_ops { int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv); + void (*setup_plat_dat)(struct socfpga_dwmac *dwmac_priv); }; struct socfpga_dwmac { @@ -441,6 +442,23 @@ static int socfpga_dwmac_init(struct platform_device *pdev, void *bsp_priv) return dwmac->ops->set_phy_mode(dwmac); } +static void socfpga_gen5_setup_plat_dat(struct socfpga_dwmac *dwmac) +{ + struct plat_stmmacenet_data *plat_dat = dwmac->plat_dat; + + plat_dat->core_type = DWMAC_CORE_GMAC; + + /* Rx watchdog timer in dwmac is buggy in this hw */ + plat_dat->riwt_off = 1; +} + +static void socfpga_agilex5_setup_plat_dat(struct socfpga_dwmac *dwmac) +{ + struct plat_stmmacenet_data *plat_dat = dwmac->plat_dat; + + plat_dat->core_type = DWMAC_CORE_XGMAC; +} + static int socfpga_dwmac_probe(struct platform_device *pdev) { struct plat_stmmacenet_data *plat_dat; @@ -497,25 +515,31 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) plat_dat->pcs_init = socfpga_dwmac_pcs_init; plat_dat->pcs_exit = socfpga_dwmac_pcs_exit; plat_dat->select_pcs = socfpga_dwmac_select_pcs; - plat_dat->core_type = DWMAC_CORE_GMAC; - plat_dat->riwt_off = 1; + ops->setup_plat_dat(dwmac); return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); } static const struct socfpga_dwmac_ops socfpga_gen5_ops = { .set_phy_mode = socfpga_gen5_set_phy_mode, + .setup_plat_dat = socfpga_gen5_setup_plat_dat, }; static const struct socfpga_dwmac_ops socfpga_gen10_ops = { .set_phy_mode = socfpga_gen10_set_phy_mode, + .setup_plat_dat = socfpga_gen5_setup_plat_dat, +}; + +static const struct socfpga_dwmac_ops socfpga_agilex5_ops = { + .set_phy_mode = socfpga_gen10_set_phy_mode, + .setup_plat_dat = socfpga_agilex5_setup_plat_dat, }; static const struct of_device_id socfpga_dwmac_match[] = { { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gen5_ops }, { .compatible = "altr,socfpga-stmmac-a10-s10", .data = &socfpga_gen10_ops }, - { .compatible = "altr,socfpga-stmmac-agilex5", .data = &socfpga_gen10_ops }, + { .compatible = "altr,socfpga-stmmac-agilex5", .data = &socfpga_agilex5_ops }, { } }; MODULE_DEVICE_TABLE(of, socfpga_dwmac_match); From 01d45e6b2c500a611bfe0e197b8a9d62253875de Mon Sep 17 00:00:00 2001 From: Rohan G Thomas Date: Sat, 1 Nov 2025 01:27:08 +0800 Subject: [PATCH 2515/3297] net: stmmac: socfpga: Enable TBS support for Agilex5 [ Upstream commit 4c00476d44804db3c16838299b87a11741cd0dbd ] Agilex5 supports Time-Based Scheduling(TBS) for Tx queue 6 and Tx queue 7. This commit enables TBS support for these queues. Signed-off-by: Rohan G Thomas Link: https://patch.msgid.link/20251101-agilex5_ext-v2-2-a6b51b4dca4d@altera.com Signed-off-by: Jakub Kicinski Stable-dep-of: 9fcf274d93af ("net: stmmac: xgmac: fix l4 filter port overwrite on register update") Signed-off-by: Sasha Levin --- drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 5666b01723643..4f256f0ae05c1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -457,6 +457,19 @@ static void socfpga_agilex5_setup_plat_dat(struct socfpga_dwmac *dwmac) struct plat_stmmacenet_data *plat_dat = dwmac->plat_dat; plat_dat->core_type = DWMAC_CORE_XGMAC; + + /* Enable TBS */ + switch (plat_dat->tx_queues_to_use) { + case 8: + plat_dat->tx_queues_cfg[7].tbs_en = true; + fallthrough; + case 7: + plat_dat->tx_queues_cfg[6].tbs_en = true; + break; + default: + /* Tx Queues 0 - 5 doesn't support TBS on Agilex5 */ + break; + } } static int socfpga_dwmac_probe(struct platform_device *pdev) From 4a3eea468a04103605c74c953065023379f78ac3 Mon Sep 17 00:00:00 2001 From: Rohan G Thomas Date: Sat, 1 Nov 2025 01:27:10 +0800 Subject: [PATCH 2516/3297] net: stmmac: socfpga: Add hardware supported cross-timestamp [ Upstream commit fd8c4f6454963aa7ea895657472aa57f33779d57 ] Cross timestamping is supported on Agilex5 platform with Synchronized Multidrop Timestamp Gathering(SMTG) IP. The hardware cross-timestamp result is made available the applications through the ioctl call PTP_SYS_OFFSET_PRECISE, which inturn calls stmmac_getcrosststamp(). Device time is stored in the MAC Auxiliary register. The 64-bit System time (ARM_ARCH_COUNTER) is stored in SMTG IP. SMTG IP is an MDIO device with 0xC - 0xF MDIO register space holds 64-bit system time. This commit is similar to following commit for Intel platforms: Commit 341f67e424e5 ("net: stmmac: Add hardware supported cross-timestamp") Signed-off-by: Rohan G Thomas Link: https://patch.msgid.link/20251101-agilex5_ext-v2-4-a6b51b4dca4d@altera.com Signed-off-by: Jakub Kicinski Stable-dep-of: 9fcf274d93af ("net: stmmac: xgmac: fix l4 filter port overwrite on register update") Signed-off-by: Sasha Levin --- .../ethernet/stmicro/stmmac/dwmac-socfpga.c | 120 ++++++++++++++++++ .../net/ethernet/stmicro/stmmac/dwxgmac2.h | 5 + 2 files changed, 125 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 4f256f0ae05c1..d8c49083ada21 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -15,8 +16,10 @@ #include #include +#include "dwxgmac2.h" #include "stmmac.h" #include "stmmac_platform.h" +#include "stmmac_ptp.h" #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 @@ -41,6 +44,13 @@ #define SGMII_ADAPTER_ENABLE 0x0000 #define SGMII_ADAPTER_DISABLE 0x0001 +#define SMTG_MDIO_ADDR 0x15 +#define SMTG_TSC_WORD0 0xC +#define SMTG_TSC_WORD1 0xD +#define SMTG_TSC_WORD2 0xE +#define SMTG_TSC_WORD3 0xF +#define SMTG_TSC_SHIFT 16 + struct socfpga_dwmac; struct socfpga_dwmac_ops { int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv); @@ -269,6 +279,112 @@ static int socfpga_set_phy_mode_common(int phymode, u32 *val) return 0; } +static void get_smtgtime(struct mii_bus *mii, int smtg_addr, u64 *smtg_time) +{ + u64 ns; + + ns = mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD3); + ns <<= SMTG_TSC_SHIFT; + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD2); + ns <<= SMTG_TSC_SHIFT; + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD1); + ns <<= SMTG_TSC_SHIFT; + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD0); + + *smtg_time = ns; +} + +static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system, + void *ctx) +{ + struct stmmac_priv *priv = (struct stmmac_priv *)ctx; + u32 num_snapshot, gpio_value, acr_value; + void __iomem *ptpaddr = priv->ptpaddr; + void __iomem *ioaddr = priv->hw->pcsr; + unsigned long flags; + u64 smtg_time = 0; + u64 ptp_time = 0; + int i, ret; + u32 v; + + /* Both internal crosstimestamping and external triggered event + * timestamping cannot be run concurrently. + */ + if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN) + return -EBUSY; + + mutex_lock(&priv->aux_ts_lock); + /* Enable Internal snapshot trigger */ + acr_value = readl(ptpaddr + PTP_ACR); + acr_value &= ~PTP_ACR_MASK; + switch (priv->plat->int_snapshot_num) { + case AUX_SNAPSHOT0: + acr_value |= PTP_ACR_ATSEN0; + break; + case AUX_SNAPSHOT1: + acr_value |= PTP_ACR_ATSEN1; + break; + case AUX_SNAPSHOT2: + acr_value |= PTP_ACR_ATSEN2; + break; + case AUX_SNAPSHOT3: + acr_value |= PTP_ACR_ATSEN3; + break; + default: + mutex_unlock(&priv->aux_ts_lock); + return -EINVAL; + } + writel(acr_value, ptpaddr + PTP_ACR); + + /* Clear FIFO */ + acr_value = readl(ptpaddr + PTP_ACR); + acr_value |= PTP_ACR_ATSFC; + writel(acr_value, ptpaddr + PTP_ACR); + /* Release the mutex */ + mutex_unlock(&priv->aux_ts_lock); + + /* Trigger Internal snapshot signal. Create a rising edge by just toggle + * the GPO0 to low and back to high. + */ + gpio_value = readl(ioaddr + XGMAC_GPIO_STATUS); + gpio_value &= ~XGMAC_GPIO_GPO0; + writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS); + gpio_value |= XGMAC_GPIO_GPO0; + writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS); + + /* Poll for time sync operation done */ + ret = readl_poll_timeout(priv->ioaddr + XGMAC_INT_STATUS, v, + (v & XGMAC_INT_TSIS), 100, 10000); + if (ret) { + netdev_err(priv->dev, "%s: Wait for time sync operation timeout\n", + __func__); + return ret; + } + + *system = (struct system_counterval_t) { + .cycles = 0, + .cs_id = CSID_ARM_ARCH_COUNTER, + .use_nsecs = false, + }; + + num_snapshot = (readl(ioaddr + XGMAC_TIMESTAMP_STATUS) & + XGMAC_TIMESTAMP_ATSNS_MASK) >> + XGMAC_TIMESTAMP_ATSNS_SHIFT; + + /* Repeat until the timestamps are from the FIFO last segment */ + for (i = 0; i < num_snapshot; i++) { + read_lock_irqsave(&priv->ptp_lock, flags); + stmmac_get_ptptime(priv, ptpaddr, &ptp_time); + *device = ns_to_ktime(ptp_time); + read_unlock_irqrestore(&priv->ptp_lock, flags); + } + + get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time); + system->cycles = smtg_time; + + return 0; +} + static int socfpga_gen5_set_phy_mode(struct socfpga_dwmac *dwmac) { struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr; @@ -470,6 +586,10 @@ static void socfpga_agilex5_setup_plat_dat(struct socfpga_dwmac *dwmac) /* Tx Queues 0 - 5 doesn't support TBS on Agilex5 */ break; } + + /* Hw supported cross-timestamp */ + plat_dat->int_snapshot_num = AUX_SNAPSHOT0; + plat_dat->crosststamp = smtg_crosststamp; } static int socfpga_dwmac_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 0d408ee17f337..e48cfa05000c0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -79,6 +79,7 @@ #define XGMAC_PSRQ(x) GENMASK((x) * 8 + 7, (x) * 8) #define XGMAC_PSRQ_SHIFT(x) ((x) * 8) #define XGMAC_INT_STATUS 0x000000b0 +#define XGMAC_INT_TSIS BIT(12) #define XGMAC_LPIIS BIT(5) #define XGMAC_PMTIS BIT(4) #define XGMAC_INT_EN 0x000000b4 @@ -173,6 +174,8 @@ #define XGMAC_MDIO_ADDR 0x00000200 #define XGMAC_MDIO_DATA 0x00000204 #define XGMAC_MDIO_C22P 0x00000220 +#define XGMAC_GPIO_STATUS 0x0000027c +#define XGMAC_GPIO_GPO0 BIT(16) #define XGMAC_ADDRx_HIGH(x) (0x00000300 + (x) * 0x8) #define XGMAC_ADDR_MAX 32 #define XGMAC_AE BIT(31) @@ -220,6 +223,8 @@ #define XGMAC_OB BIT(0) #define XGMAC_RSS_DATA 0x00000c8c #define XGMAC_TIMESTAMP_STATUS 0x00000d20 +#define XGMAC_TIMESTAMP_ATSNS_MASK GENMASK(29, 25) +#define XGMAC_TIMESTAMP_ATSNS_SHIFT 25 #define XGMAC_TXTSC BIT(15) #define XGMAC_TXTIMESTAMP_NSEC 0x00000d30 #define XGMAC_TXTSSTSLO GENMASK(30, 0) From 40413da850363fe9cefcac9a2242927effe7b766 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 8 Jan 2026 17:36:40 +0000 Subject: [PATCH 2517/3297] net: stmmac: cores: remove many xxx_SHIFT definitions [ Upstream commit 8409495bf6c907a5bc9632464dbdd8fb619f9ceb ] We have many xxx_SHIFT definitions along side their corresponding xxx_MASK definitions for the various cores. Manually using the shift and mask can be error prone, as shown with the dwmac4 RXFSTS fix patch. Convert sites that use xxx_SHIFT and xxx_MASK directly to use FIELD_GET(), FIELD_PREP(), and u32_replace_bits() as appropriate. Signed-off-by: Russell King (Oracle) Link: https://patch.msgid.link/E1vdtw8-00000002Gtu-0Hyu@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski Stable-dep-of: 9fcf274d93af ("net: stmmac: xgmac: fix l4 filter port overwrite on register update") Signed-off-by: Sasha Levin --- .../ethernet/stmicro/stmmac/dwmac-loongson.c | 5 +- .../ethernet/stmicro/stmmac/dwmac-socfpga.c | 5 +- .../net/ethernet/stmicro/stmmac/dwmac100.h | 9 +-- .../net/ethernet/stmicro/stmmac/dwmac1000.h | 16 +--- .../ethernet/stmicro/stmmac/dwmac1000_core.c | 21 +++--- .../ethernet/stmicro/stmmac/dwmac1000_dma.c | 16 ++-- .../ethernet/stmicro/stmmac/dwmac100_core.c | 2 +- .../ethernet/stmicro/stmmac/dwmac100_dma.c | 3 +- drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 49 ++++-------- .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 25 +++---- .../net/ethernet/stmicro/stmmac/dwmac4_dma.c | 40 +++++----- .../net/ethernet/stmicro/stmmac/dwmac4_dma.h | 11 +-- .../net/ethernet/stmicro/stmmac/dwmac4_lib.c | 2 +- .../net/ethernet/stmicro/stmmac/dwmac_dma.h | 10 +-- .../net/ethernet/stmicro/stmmac/dwmac_lib.c | 10 +-- .../net/ethernet/stmicro/stmmac/dwxgmac2.h | 31 ++------ .../ethernet/stmicro/stmmac/dwxgmac2_core.c | 21 +++--- .../ethernet/stmicro/stmmac/dwxgmac2_dma.c | 75 ++++++++----------- 18 files changed, 129 insertions(+), 222 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c index ab431bf9b25f2..e6bce3a433a78 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c @@ -209,9 +209,8 @@ static void loongson_dwmac_dma_init_channel(struct stmmac_priv *priv, value |= DMA_BUS_MODE_MAXPBL; value |= DMA_BUS_MODE_USP; - value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK); - value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT); - value |= (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); + value = u32_replace_bits(value, txpbl, DMA_BUS_MODE_PBL_MASK); + value = u32_replace_bits(value, rxpbl, DMA_BUS_MODE_RPBL_MASK); /* Set the Fixed burst mode */ if (dma_cfg->fixed_burst) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index d8c49083ada21..e40d0d6963037 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -367,9 +367,8 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system, .use_nsecs = false, }; - num_snapshot = (readl(ioaddr + XGMAC_TIMESTAMP_STATUS) & - XGMAC_TIMESTAMP_ATSNS_MASK) >> - XGMAC_TIMESTAMP_ATSNS_SHIFT; + num_snapshot = FIELD_GET(XGMAC_TIMESTAMP_ATSNS_MASK, + readl(ioaddr + XGMAC_TIMESTAMP_STATUS)); /* Repeat until the timestamps are from the FIFO last segment */ for (i = 0; i < num_snapshot; i++) { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h index 7ab791c8d355f..eae929955ad78 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h @@ -59,8 +59,7 @@ #define MAC_CORE_INIT (MAC_CONTROL_HBD) /* MAC FLOW CTRL defines */ -#define MAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ -#define MAC_FLOW_CTRL_PT_SHIFT 16 +#define MAC_FLOW_CTRL_PT_MASK GENMASK(31, 16) /* Pause Time Mask */ #define MAC_FLOW_CTRL_PASS 0x00000004 /* Pass Control Frames */ #define MAC_FLOW_CTRL_ENABLE 0x00000002 /* Flow Control Enable */ #define MAC_FLOW_CTRL_PAUSE 0x00000001 /* Flow Control Busy ... */ @@ -76,10 +75,8 @@ /* DMA Bus Mode register defines */ #define DMA_BUS_MODE_DBO 0x00100000 /* Descriptor Byte Ordering */ #define DMA_BUS_MODE_BLE 0x00000080 /* Big Endian/Little Endian */ -#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ -#define DMA_BUS_MODE_PBL_SHIFT 8 -#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ -#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ +#define DMA_BUS_MODE_PBL_MASK GENMASK(13, 8) /* Programmable Burst Len */ +#define DMA_BUS_MODE_DSL_MASK GENMASK(6, 2) /* Descriptor Skip Length */ #define DMA_BUS_MODE_BAR_BUS 0x00000002 /* Bar-Bus Arbitration */ #define DMA_BUS_MODE_DEFAULT 0x00000000 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 0c011a47d5a3e..12c82235c1d7e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -77,7 +77,6 @@ enum power_event { /* SGMII/RGMII status register */ #define GMAC_RGSMIIIS_LNKMODE BIT(0) #define GMAC_RGSMIIIS_SPEED GENMASK(2, 1) -#define GMAC_RGSMIIIS_SPEED_SHIFT 1 #define GMAC_RGSMIIIS_LNKSTS BIT(3) #define GMAC_RGSMIIIS_JABTO BIT(4) #define GMAC_RGSMIIIS_FALSECARDET BIT(5) @@ -134,8 +133,7 @@ enum inter_frame_gap { #define GMAC_MII_ADDR_WRITE 0x00000002 /* MII Write */ #define GMAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */ /* GMAC FLOW CTRL defines */ -#define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ -#define GMAC_FLOW_CTRL_PT_SHIFT 16 +#define GMAC_FLOW_CTRL_PT_MASK GENMASK(31, 16) /* Pause Time Mask */ #define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */ #define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ #define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ @@ -148,7 +146,6 @@ enum inter_frame_gap { #define GMAC_DEBUG_TWCSTS BIT(22) /* MTL Tx FIFO Write Controller */ /* MTL Tx FIFO Read Controller Status */ #define GMAC_DEBUG_TRCSTS_MASK GENMASK(21, 20) -#define GMAC_DEBUG_TRCSTS_SHIFT 20 #define GMAC_DEBUG_TRCSTS_IDLE 0 #define GMAC_DEBUG_TRCSTS_READ 1 #define GMAC_DEBUG_TRCSTS_TXW 2 @@ -156,7 +153,6 @@ enum inter_frame_gap { #define GMAC_DEBUG_TXPAUSED BIT(19) /* MAC Transmitter in PAUSE */ /* MAC Transmit Frame Controller Status */ #define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17) -#define GMAC_DEBUG_TFCSTS_SHIFT 17 #define GMAC_DEBUG_TFCSTS_IDLE 0 #define GMAC_DEBUG_TFCSTS_WAIT 1 #define GMAC_DEBUG_TFCSTS_GEN_PAUSE 2 @@ -164,13 +160,11 @@ enum inter_frame_gap { /* MAC GMII or MII Transmit Protocol Engine Status */ #define GMAC_DEBUG_TPESTS BIT(16) #define GMAC_DEBUG_RXFSTS_MASK GENMASK(9, 8) /* MTL Rx FIFO Fill-level */ -#define GMAC_DEBUG_RXFSTS_SHIFT 8 #define GMAC_DEBUG_RXFSTS_EMPTY 0 #define GMAC_DEBUG_RXFSTS_BT 1 #define GMAC_DEBUG_RXFSTS_AT 2 #define GMAC_DEBUG_RXFSTS_FULL 3 #define GMAC_DEBUG_RRCSTS_MASK GENMASK(6, 5) /* MTL Rx FIFO Read Controller */ -#define GMAC_DEBUG_RRCSTS_SHIFT 5 #define GMAC_DEBUG_RRCSTS_IDLE 0 #define GMAC_DEBUG_RRCSTS_RDATA 1 #define GMAC_DEBUG_RRCSTS_RSTAT 2 @@ -178,7 +172,6 @@ enum inter_frame_gap { #define GMAC_DEBUG_RWCSTS BIT(4) /* MTL Rx FIFO Write Controller Active */ /* MAC Receive Frame Controller FIFO Status */ #define GMAC_DEBUG_RFCFCSTS_MASK GENMASK(2, 1) -#define GMAC_DEBUG_RFCFCSTS_SHIFT 1 /* MAC GMII or MII Receive Protocol Engine Status */ #define GMAC_DEBUG_RPESTS BIT(0) @@ -188,8 +181,7 @@ enum inter_frame_gap { #define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ #define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ /* Programmable burst length (passed thorugh platform)*/ -#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ -#define DMA_BUS_MODE_PBL_SHIFT 8 +#define DMA_BUS_MODE_PBL_MASK GENMASK(13, 8) /* Programmable Burst Len */ #define DMA_BUS_MODE_ATDS 0x00000080 /* Alternate Descriptor Size */ enum rx_tx_priority_ratio { @@ -200,8 +192,7 @@ enum rx_tx_priority_ratio { #define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */ #define DMA_BUS_MODE_MB 0x04000000 /* Mixed burst */ -#define DMA_BUS_MODE_RPBL_MASK 0x007e0000 /* Rx-Programmable Burst Len */ -#define DMA_BUS_MODE_RPBL_SHIFT 17 +#define DMA_BUS_MODE_RPBL_MASK GENMASK(22, 17) /* Rx-Programmable Burst Len */ #define DMA_BUS_MODE_USP 0x00800000 #define DMA_BUS_MODE_MAXPBL 0x01000000 #define DMA_BUS_MODE_AAL 0x02000000 @@ -321,7 +312,6 @@ enum rtc_control { /* PTP and timestamping registers */ #define GMAC3_X_ATSNS GENMASK(29, 25) -#define GMAC3_X_ATSNS_SHIFT 25 #define GMAC_PTP_TCR_ATSFC BIT(24) #define GMAC_PTP_TCR_ATSEN0 BIT(25) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 2c5ee59c32086..5bf717188c92d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -240,7 +240,7 @@ static void dwmac1000_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, if (duplex) { pr_debug("\tduplex mode: PAUSE %d\n", pause_time); - flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT); + flow |= FIELD_PREP(GMAC_FLOW_CTRL_PT_MASK, pause_time); } writel(flow, ioaddr + GMAC_FLOW_CTRL); @@ -386,8 +386,8 @@ static void dwmac1000_debug(struct stmmac_priv *priv, void __iomem *ioaddr, if (value & GMAC_DEBUG_TWCSTS) x->mmtl_fifo_ctrl++; if (value & GMAC_DEBUG_TRCSTS_MASK) { - u32 trcsts = (value & GMAC_DEBUG_TRCSTS_MASK) - >> GMAC_DEBUG_TRCSTS_SHIFT; + u32 trcsts = FIELD_GET(GMAC_DEBUG_TRCSTS_MASK, value); + if (trcsts == GMAC_DEBUG_TRCSTS_WRITE) x->mtl_tx_fifo_read_ctrl_write++; else if (trcsts == GMAC_DEBUG_TRCSTS_TXW) @@ -400,8 +400,7 @@ static void dwmac1000_debug(struct stmmac_priv *priv, void __iomem *ioaddr, if (value & GMAC_DEBUG_TXPAUSED) x->mac_tx_in_pause++; if (value & GMAC_DEBUG_TFCSTS_MASK) { - u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK) - >> GMAC_DEBUG_TFCSTS_SHIFT; + u32 tfcsts = FIELD_GET(GMAC_DEBUG_TFCSTS_MASK, value); if (tfcsts == GMAC_DEBUG_TFCSTS_XFER) x->mac_tx_frame_ctrl_xfer++; @@ -415,8 +414,7 @@ static void dwmac1000_debug(struct stmmac_priv *priv, void __iomem *ioaddr, if (value & GMAC_DEBUG_TPESTS) x->mac_gmii_tx_proto_engine++; if (value & GMAC_DEBUG_RXFSTS_MASK) { - u32 rxfsts = (value & GMAC_DEBUG_RXFSTS_MASK) - >> GMAC_DEBUG_RRCSTS_SHIFT; + u32 rxfsts = FIELD_GET(GMAC_DEBUG_RXFSTS_MASK, value); if (rxfsts == GMAC_DEBUG_RXFSTS_FULL) x->mtl_rx_fifo_fill_level_full++; @@ -428,8 +426,7 @@ static void dwmac1000_debug(struct stmmac_priv *priv, void __iomem *ioaddr, x->mtl_rx_fifo_fill_level_empty++; } if (value & GMAC_DEBUG_RRCSTS_MASK) { - u32 rrcsts = (value & GMAC_DEBUG_RRCSTS_MASK) >> - GMAC_DEBUG_RRCSTS_SHIFT; + u32 rrcsts = FIELD_GET(GMAC_DEBUG_RRCSTS_MASK, value); if (rrcsts == GMAC_DEBUG_RRCSTS_FLUSH) x->mtl_rx_fifo_read_ctrl_flush++; @@ -443,8 +440,8 @@ static void dwmac1000_debug(struct stmmac_priv *priv, void __iomem *ioaddr, if (value & GMAC_DEBUG_RWCSTS) x->mtl_rx_fifo_ctrl_active++; if (value & GMAC_DEBUG_RFCFCSTS_MASK) - x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK) - >> GMAC_DEBUG_RFCFCSTS_SHIFT; + x->mac_rx_frame_ctrl_fifo = FIELD_GET(GMAC_DEBUG_RFCFCSTS_MASK, + value); if (value & GMAC_DEBUG_RPESTS) x->mac_gmii_rx_proto_engine++; } @@ -540,7 +537,7 @@ void dwmac1000_timestamp_interrupt(struct stmmac_priv *priv) if (!(priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN)) return; - num_snapshot = (ts_status & GMAC3_X_ATSNS) >> GMAC3_X_ATSNS_SHIFT; + num_snapshot = FIELD_GET(GMAC3_X_ATSNS, ts_status); for (i = 0; i < num_snapshot; i++) { read_lock_irqsave(&priv->ptp_lock, flags); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 118a22406a2e9..cacbf6d4365c7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -29,13 +29,10 @@ static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi) if (axi->axi_xit_frm) value |= DMA_AXI_LPI_XIT_FRM; - value &= ~DMA_AXI_WR_OSR_LMT; - value |= (axi->axi_wr_osr_lmt & DMA_AXI_WR_OSR_LMT_MASK) << - DMA_AXI_WR_OSR_LMT_SHIFT; - - value &= ~DMA_AXI_RD_OSR_LMT; - value |= (axi->axi_rd_osr_lmt & DMA_AXI_RD_OSR_LMT_MASK) << - DMA_AXI_RD_OSR_LMT_SHIFT; + value = u32_replace_bits(value, axi->axi_wr_osr_lmt, + DMA_AXI_WR_OSR_LMT); + value = u32_replace_bits(value, axi->axi_rd_osr_lmt, + DMA_AXI_RD_OSR_LMT); /* Depending on the UNDEF bit the Master AXI will perform any burst * length according to the BLEN programmed (by default all BLEN are @@ -88,9 +85,8 @@ static void dwmac1000_dma_init_channel(struct stmmac_priv *priv, if (dma_cfg->pblx8) value |= DMA_BUS_MODE_MAXPBL; value |= DMA_BUS_MODE_USP; - value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK); - value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT); - value |= (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); + value = u32_replace_bits(value, txpbl, DMA_BUS_MODE_PBL_MASK); + value = u32_replace_bits(value, rxpbl, DMA_BUS_MODE_RPBL_MASK); /* Set the Fixed burst mode */ if (dma_cfg->fixed_burst) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 14e847c0e1a91..dbc0c1019ed5e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -132,7 +132,7 @@ static void dwmac100_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, unsigned int flow = MAC_FLOW_CTRL_ENABLE; if (duplex) - flow |= (pause_time << MAC_FLOW_CTRL_PT_SHIFT); + flow |= FIELD_PREP(MAC_FLOW_CTRL_PT_MASK, pause_time); writel(flow, ioaddr + MAC_FLOW_CTRL); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index 82957db47c991..12b2bf2d739ab 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -22,7 +22,8 @@ static void dwmac100_dma_init(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg) { /* Enable Application Access by writing to DMA CSR0 */ - writel(DMA_BUS_MODE_DEFAULT | (dma_cfg->pbl << DMA_BUS_MODE_PBL_SHIFT), + writel(DMA_BUS_MODE_DEFAULT | + FIELD_PREP(DMA_BUS_MODE_PBL_MASK, dma_cfg->pbl), ioaddr + DMA_BUS_MODE); /* Mask interrupts by writing to CSR7 */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 3dec1a264cf60..7a6609185fd0c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -95,7 +95,7 @@ /* MAC Flow Control TX */ #define GMAC_TX_FLOW_CTRL_TFE BIT(1) -#define GMAC_TX_FLOW_CTRL_PT_SHIFT 16 +#define GMAC_TX_FLOW_CTRL_PT_MASK GENMASK(31, 16) /* MAC Interrupt bitmap*/ #define GMAC_INT_RGSMIIS BIT(0) @@ -145,23 +145,19 @@ enum power_event { /* MAC Debug bitmap */ #define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17) -#define GMAC_DEBUG_TFCSTS_SHIFT 17 #define GMAC_DEBUG_TFCSTS_IDLE 0 #define GMAC_DEBUG_TFCSTS_WAIT 1 #define GMAC_DEBUG_TFCSTS_GEN_PAUSE 2 #define GMAC_DEBUG_TFCSTS_XFER 3 #define GMAC_DEBUG_TPESTS BIT(16) #define GMAC_DEBUG_RFCFCSTS_MASK GENMASK(2, 1) -#define GMAC_DEBUG_RFCFCSTS_SHIFT 1 #define GMAC_DEBUG_RPESTS BIT(0) /* MAC config */ #define GMAC_CONFIG_ARPEN BIT(31) #define GMAC_CONFIG_SARC GENMASK(30, 28) -#define GMAC_CONFIG_SARC_SHIFT 28 #define GMAC_CONFIG_IPC BIT(27) #define GMAC_CONFIG_IPG GENMASK(26, 24) -#define GMAC_CONFIG_IPG_SHIFT 24 #define GMAC_CONFIG_2K BIT(22) #define GMAC_CONFIG_ACS BIT(20) #define GMAC_CONFIG_BE BIT(18) @@ -169,7 +165,6 @@ enum power_event { #define GMAC_CONFIG_JE BIT(16) #define GMAC_CONFIG_PS BIT(15) #define GMAC_CONFIG_FES BIT(14) -#define GMAC_CONFIG_FES_SHIFT 14 #define GMAC_CONFIG_DM BIT(13) #define GMAC_CONFIG_LM BIT(12) #define GMAC_CONFIG_DCRS BIT(9) @@ -178,11 +173,9 @@ enum power_event { /* MAC extended config */ #define GMAC_CONFIG_EIPG GENMASK(29, 25) -#define GMAC_CONFIG_EIPG_SHIFT 25 #define GMAC_CONFIG_EIPG_EN BIT(24) #define GMAC_CONFIG_HDSMS GENMASK(22, 20) -#define GMAC_CONFIG_HDSMS_SHIFT 20 -#define GMAC_CONFIG_HDSMS_256 (0x2 << GMAC_CONFIG_HDSMS_SHIFT) +#define GMAC_CONFIG_HDSMS_256 FIELD_PREP_CONST(GMAC_CONFIG_HDSMS, 0x2) /* MAC HW features0 bitmap */ #define GMAC_HW_FEAT_SAVLANINS BIT(27) @@ -245,7 +238,6 @@ enum power_event { /* MAC HW ADDR regs */ #define GMAC_HI_DCS GENMASK(18, 16) -#define GMAC_HI_DCS_SHIFT 16 #define GMAC_HI_REG_AE BIT(31) /* L3/L4 Filters regs */ @@ -260,7 +252,6 @@ enum power_event { #define GMAC_L3SAM0 BIT(2) #define GMAC_L3PEN0 BIT(0) #define GMAC_L4DP0 GENMASK(31, 16) -#define GMAC_L4DP0_SHIFT 16 #define GMAC_L4SP0 GENMASK(15, 0) /* MAC Timestamp Status */ @@ -317,39 +308,32 @@ static inline u32 mtl_chanx_base_addr(const struct dwmac4_addrs *addrs, #define MTL_OP_MODE_TSF BIT(1) #define MTL_OP_MODE_TQS_MASK GENMASK(24, 16) -#define MTL_OP_MODE_TQS_SHIFT 16 -#define MTL_OP_MODE_TTC_MASK 0x70 -#define MTL_OP_MODE_TTC_SHIFT 4 - -#define MTL_OP_MODE_TTC_32 0 -#define MTL_OP_MODE_TTC_64 (1 << MTL_OP_MODE_TTC_SHIFT) -#define MTL_OP_MODE_TTC_96 (2 << MTL_OP_MODE_TTC_SHIFT) -#define MTL_OP_MODE_TTC_128 (3 << MTL_OP_MODE_TTC_SHIFT) -#define MTL_OP_MODE_TTC_192 (4 << MTL_OP_MODE_TTC_SHIFT) -#define MTL_OP_MODE_TTC_256 (5 << MTL_OP_MODE_TTC_SHIFT) -#define MTL_OP_MODE_TTC_384 (6 << MTL_OP_MODE_TTC_SHIFT) -#define MTL_OP_MODE_TTC_512 (7 << MTL_OP_MODE_TTC_SHIFT) +#define MTL_OP_MODE_TTC_MASK GENMASK(6, 4) +#define MTL_OP_MODE_TTC_32 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 0) +#define MTL_OP_MODE_TTC_64 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 1) +#define MTL_OP_MODE_TTC_96 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 2) +#define MTL_OP_MODE_TTC_128 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 3) +#define MTL_OP_MODE_TTC_192 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 4) +#define MTL_OP_MODE_TTC_256 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 5) +#define MTL_OP_MODE_TTC_384 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 6) +#define MTL_OP_MODE_TTC_512 FIELD_PREP(MTL_OP_MODE_TTC_MASK, 7) #define MTL_OP_MODE_RQS_MASK GENMASK(29, 20) -#define MTL_OP_MODE_RQS_SHIFT 20 #define MTL_OP_MODE_RFD_MASK GENMASK(19, 14) -#define MTL_OP_MODE_RFD_SHIFT 14 #define MTL_OP_MODE_RFA_MASK GENMASK(13, 8) -#define MTL_OP_MODE_RFA_SHIFT 8 #define MTL_OP_MODE_EHFC BIT(7) #define MTL_OP_MODE_DIS_TCP_EF BIT(6) #define MTL_OP_MODE_RTC_MASK GENMASK(1, 0) -#define MTL_OP_MODE_RTC_SHIFT 0 -#define MTL_OP_MODE_RTC_32 (1 << MTL_OP_MODE_RTC_SHIFT) -#define MTL_OP_MODE_RTC_64 0 -#define MTL_OP_MODE_RTC_96 (2 << MTL_OP_MODE_RTC_SHIFT) -#define MTL_OP_MODE_RTC_128 (3 << MTL_OP_MODE_RTC_SHIFT) +#define MTL_OP_MODE_RTC_32 FIELD_PREP(MTL_OP_MODE_RTC_MASK, 1) +#define MTL_OP_MODE_RTC_64 FIELD_PREP(MTL_OP_MODE_RTC_MASK, 0) +#define MTL_OP_MODE_RTC_96 FIELD_PREP(MTL_OP_MODE_RTC_MASK, 2) +#define MTL_OP_MODE_RTC_128 FIELD_PREP(MTL_OP_MODE_RTC_MASK, 3) /* MTL ETS Control register */ #define MTL_ETS_CTRL_BASE_ADDR 0x00000d10 @@ -454,7 +438,6 @@ static inline u32 mtl_low_credx_base_addr(const struct dwmac4_addrs *addrs, /* MTL debug: Tx FIFO Read Controller Status */ #define MTL_DEBUG_TRCSTS_MASK GENMASK(2, 1) -#define MTL_DEBUG_TRCSTS_SHIFT 1 #define MTL_DEBUG_TRCSTS_IDLE 0 #define MTL_DEBUG_TRCSTS_READ 1 #define MTL_DEBUG_TRCSTS_TXW 2 @@ -469,7 +452,6 @@ static inline u32 mtl_low_credx_base_addr(const struct dwmac4_addrs *addrs, #define MTL_DEBUG_RXFSTS_AT 2 #define MTL_DEBUG_RXFSTS_FULL 3 #define MTL_DEBUG_RRCSTS_MASK GENMASK(2, 1) -#define MTL_DEBUG_RRCSTS_SHIFT 1 #define MTL_DEBUG_RRCSTS_IDLE 0 #define MTL_DEBUG_RRCSTS_RDATA 1 #define MTL_DEBUG_RRCSTS_RSTAT 2 @@ -523,7 +505,6 @@ static inline u32 mtl_low_credx_base_addr(const struct dwmac4_addrs *addrs, #define GMAC_PHYIF_CTRLSTATUS_SMIDRXS BIT(4) #define GMAC_PHYIF_CTRLSTATUS_LNKMOD BIT(16) #define GMAC_PHYIF_CTRLSTATUS_SPEED GENMASK(18, 17) -#define GMAC_PHYIF_CTRLSTATUS_SPEED_SHIFT 17 #define GMAC_PHYIF_CTRLSTATUS_LNKSTS BIT(19) #define GMAC_PHYIF_CTRLSTATUS_JABTO BIT(20) #define GMAC_PHYIF_CTRLSTATUS_FALSECARDET BIT(21) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 8a19df7b05775..1f61177ece453 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -572,8 +572,8 @@ static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, flow = GMAC_TX_FLOW_CTRL_TFE; if (duplex) - flow |= - (pause_time << GMAC_TX_FLOW_CTRL_PT_SHIFT); + flow |= FIELD_PREP(GMAC_TX_FLOW_CTRL_PT_MASK, + pause_time); writel(flow, ioaddr + GMAC_QX_TX_FLOW_CTRL(queue)); } @@ -691,8 +691,8 @@ static void dwmac4_debug(struct stmmac_priv *priv, void __iomem *ioaddr, if (value & MTL_DEBUG_TWCSTS) x->mmtl_fifo_ctrl++; if (value & MTL_DEBUG_TRCSTS_MASK) { - u32 trcsts = (value & MTL_DEBUG_TRCSTS_MASK) - >> MTL_DEBUG_TRCSTS_SHIFT; + u32 trcsts = FIELD_GET(MTL_DEBUG_TRCSTS_MASK, value); + if (trcsts == MTL_DEBUG_TRCSTS_WRITE) x->mtl_tx_fifo_read_ctrl_write++; else if (trcsts == MTL_DEBUG_TRCSTS_TXW) @@ -723,8 +723,7 @@ static void dwmac4_debug(struct stmmac_priv *priv, void __iomem *ioaddr, x->mtl_rx_fifo_fill_level_empty++; } if (value & MTL_DEBUG_RRCSTS_MASK) { - u32 rrcsts = (value & MTL_DEBUG_RRCSTS_MASK) >> - MTL_DEBUG_RRCSTS_SHIFT; + u32 rrcsts = FIELD_GET(MTL_DEBUG_RRCSTS_MASK, value); if (rrcsts == MTL_DEBUG_RRCSTS_FLUSH) x->mtl_rx_fifo_read_ctrl_flush++; @@ -743,8 +742,7 @@ static void dwmac4_debug(struct stmmac_priv *priv, void __iomem *ioaddr, value = readl(ioaddr + GMAC_DEBUG); if (value & GMAC_DEBUG_TFCSTS_MASK) { - u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK) - >> GMAC_DEBUG_TFCSTS_SHIFT; + u32 tfcsts = FIELD_GET(GMAC_DEBUG_TFCSTS_MASK, value); if (tfcsts == GMAC_DEBUG_TFCSTS_XFER) x->mac_tx_frame_ctrl_xfer++; @@ -758,8 +756,8 @@ static void dwmac4_debug(struct stmmac_priv *priv, void __iomem *ioaddr, if (value & GMAC_DEBUG_TPESTS) x->mac_gmii_tx_proto_engine++; if (value & GMAC_DEBUG_RFCFCSTS_MASK) - x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK) - >> GMAC_DEBUG_RFCFCSTS_SHIFT; + x->mac_rx_frame_ctrl_fifo = FIELD_GET(GMAC_DEBUG_RFCFCSTS_MASK, + value); if (value & GMAC_DEBUG_RPESTS) x->mac_gmii_rx_proto_engine++; } @@ -780,8 +778,7 @@ static void dwmac4_sarc_configure(void __iomem *ioaddr, int val) { u32 value = readl(ioaddr + GMAC_CONFIG); - value &= ~GMAC_CONFIG_SARC; - value |= val << GMAC_CONFIG_SARC_SHIFT; + value = u32_replace_bits(value, val, GMAC_CONFIG_SARC); writel(value, ioaddr + GMAC_CONFIG); } @@ -889,9 +886,9 @@ static int dwmac4_config_l4_filter(struct mac_device_info *hw, u32 filter_no, writel(value, ioaddr + GMAC_L3L4_CTRL(filter_no)); if (sa) { - value = match & GMAC_L4SP0; + value = FIELD_PREP(GMAC_L4SP0, match); } else { - value = (match << GMAC_L4DP0_SHIFT) & GMAC_L4DP0; + value = FIELD_PREP(GMAC_L4DP0, match); } writel(value, ioaddr + GMAC_L4_ADDR(filter_no)); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index d87a8b595e6a3..d17bcc31a89c9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -28,13 +28,10 @@ static void dwmac4_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi) if (axi->axi_xit_frm) value |= DMA_AXI_LPI_XIT_FRM; - value &= ~DMA_AXI_WR_OSR_LMT; - value |= (axi->axi_wr_osr_lmt & DMA_AXI_OSR_MAX) << - DMA_AXI_WR_OSR_LMT_SHIFT; - - value &= ~DMA_AXI_RD_OSR_LMT; - value |= (axi->axi_rd_osr_lmt & DMA_AXI_OSR_MAX) << - DMA_AXI_RD_OSR_LMT_SHIFT; + value = u32_replace_bits(value, axi->axi_wr_osr_lmt, + DMA_AXI_WR_OSR_LMT); + value = u32_replace_bits(value, axi->axi_rd_osr_lmt, + DMA_AXI_RD_OSR_LMT); /* Depending on the UNDEF bit the Master AXI will perform any burst * length according to the BLEN programmed (by default all BLEN are @@ -79,7 +76,7 @@ static void dwmac4_dma_init_rx_chan(struct stmmac_priv *priv, u32 rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; value = readl(ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan)); - value = value | (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); + value = value | FIELD_PREP(DMA_BUS_MODE_RPBL_MASK, rxpbl); writel(value, ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan)); if (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) && likely(dma_cfg->eame)) @@ -100,7 +97,7 @@ static void dwmac4_dma_init_tx_chan(struct stmmac_priv *priv, u32 txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; value = readl(ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan)); - value = value | (txpbl << DMA_BUS_MODE_PBL_SHIFT); + value = value | FIELD_PREP(DMA_BUS_MODE_PBL, txpbl); /* Enable OSP to get best performance */ value |= DMA_CONTROL_OSP; @@ -175,10 +172,9 @@ static void dwmac4_dma_init(void __iomem *ioaddr, value = readl(ioaddr + DMA_BUS_MODE); - if (dma_cfg->multi_msi_en) { - value &= ~DMA_BUS_MODE_INTM_MASK; - value |= (DMA_BUS_MODE_INTM_MODE1 << DMA_BUS_MODE_INTM_SHIFT); - } + if (dma_cfg->multi_msi_en) + value = u32_replace_bits(value, DMA_BUS_MODE_INTM_MODE1, + DMA_BUS_MODE_INTM_MASK); if (dma_cfg->dche) value |= DMA_BUS_MODE_DCHE; @@ -288,7 +284,7 @@ static void dwmac4_dma_rx_chan_op_mode(struct stmmac_priv *priv, } mtl_rx_op &= ~MTL_OP_MODE_RQS_MASK; - mtl_rx_op |= rqs << MTL_OP_MODE_RQS_SHIFT; + mtl_rx_op |= FIELD_PREP(MTL_OP_MODE_RQS_MASK, rqs); /* Enable flow control only if each channel gets 4 KiB or more FIFO and * only if channel is not an AVB channel. @@ -319,11 +315,10 @@ static void dwmac4_dma_rx_chan_op_mode(struct stmmac_priv *priv, break; } - mtl_rx_op &= ~MTL_OP_MODE_RFD_MASK; - mtl_rx_op |= rfd << MTL_OP_MODE_RFD_SHIFT; - - mtl_rx_op &= ~MTL_OP_MODE_RFA_MASK; - mtl_rx_op |= rfa << MTL_OP_MODE_RFA_SHIFT; + mtl_rx_op = u32_replace_bits(mtl_rx_op, rfd, + MTL_OP_MODE_RFD_MASK); + mtl_rx_op = u32_replace_bits(mtl_rx_op, rfa, + MTL_OP_MODE_RFA_MASK); } writel(mtl_rx_op, ioaddr + MTL_CHAN_RX_OP_MODE(dwmac4_addrs, channel)); @@ -378,8 +373,8 @@ static void dwmac4_dma_tx_chan_op_mode(struct stmmac_priv *priv, mtl_tx_op |= MTL_OP_MODE_TXQEN; else mtl_tx_op |= MTL_OP_MODE_TXQEN_AV; - mtl_tx_op &= ~MTL_OP_MODE_TQS_MASK; - mtl_tx_op |= tqs << MTL_OP_MODE_TQS_SHIFT; + + mtl_tx_op = u32_replace_bits(mtl_tx_op, tqs, MTL_OP_MODE_TQS_MASK); writel(mtl_tx_op, ioaddr + MTL_CHAN_TX_OP_MODE(dwmac4_addrs, channel)); } @@ -520,8 +515,7 @@ static void dwmac4_set_bfsize(struct stmmac_priv *priv, void __iomem *ioaddr, const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; u32 value = readl(ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan)); - value &= ~DMA_RBSZ_MASK; - value |= (bfsize << DMA_RBSZ_SHIFT) & DMA_RBSZ_MASK; + value = u32_replace_bits(value, bfsize, DMA_RBSZ_MASK); writel(value, ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan)); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 4f980dcd39582..7573ca2db352d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -27,15 +27,13 @@ /* DMA Bus Mode bitmap */ #define DMA_BUS_MODE_DCHE BIT(19) #define DMA_BUS_MODE_INTM_MASK GENMASK(17, 16) -#define DMA_BUS_MODE_INTM_SHIFT 16 #define DMA_BUS_MODE_INTM_MODE1 0x1 #define DMA_BUS_MODE_SFT_RESET BIT(0) /* DMA SYS Bus Mode bitmap */ #define DMA_BUS_MODE_SPH BIT(24) #define DMA_BUS_MODE_PBL BIT(16) -#define DMA_BUS_MODE_PBL_SHIFT 16 -#define DMA_BUS_MODE_RPBL_SHIFT 16 +#define DMA_BUS_MODE_RPBL_MASK GENMASK(21, 16) #define DMA_BUS_MODE_MB BIT(14) #define DMA_BUS_MODE_FB BIT(0) @@ -59,13 +57,7 @@ #define DMA_AXI_EN_LPI BIT(31) #define DMA_AXI_LPI_XIT_FRM BIT(30) #define DMA_AXI_WR_OSR_LMT GENMASK(27, 24) -#define DMA_AXI_WR_OSR_LMT_SHIFT 24 #define DMA_AXI_RD_OSR_LMT GENMASK(19, 16) -#define DMA_AXI_RD_OSR_LMT_SHIFT 16 - -#define DMA_AXI_OSR_MAX 0xf -#define DMA_AXI_MAX_OSR_LIMIT ((DMA_AXI_OSR_MAX << DMA_AXI_WR_OSR_LMT_SHIFT) | \ - (DMA_AXI_OSR_MAX << DMA_AXI_RD_OSR_LMT_SHIFT)) #define DMA_SYS_BUS_MB BIT(14) #define DMA_AXI_1KBBE BIT(13) @@ -146,7 +138,6 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs, /* DMA Rx Channel X Control register defines */ #define DMA_CONTROL_SR BIT(0) #define DMA_RBSZ_MASK GENMASK(14, 1) -#define DMA_RBSZ_SHIFT 1 /* Interrupt status per channel */ #define DMA_CHAN_STATUS_REB GENMASK(21, 19) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index 57c03d4917744..c098047a3bff8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -234,7 +234,7 @@ void stmmac_dwmac4_set_mac_addr(void __iomem *ioaddr, const u8 addr[6], * bit that has no effect on the High Reg 0 where the bit 31 (MO) * is RO. */ - data |= (STMMAC_CHAN0 << GMAC_HI_DCS_SHIFT); + data |= FIELD_PREP(GMAC_HI_DCS, STMMAC_CHAN0); writel(data | GMAC_HI_REG_AE, ioaddr + high); data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; writel(data, ioaddr + low); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 5d9c18f5bbf58..14cfe0de3327a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -59,11 +59,7 @@ static inline u32 dma_chan_base_addr(u32 base, u32 chan) #define DMA_AXI_EN_LPI BIT(31) #define DMA_AXI_LPI_XIT_FRM BIT(30) #define DMA_AXI_WR_OSR_LMT GENMASK(23, 20) -#define DMA_AXI_WR_OSR_LMT_SHIFT 20 -#define DMA_AXI_WR_OSR_LMT_MASK 0xf #define DMA_AXI_RD_OSR_LMT GENMASK(19, 16) -#define DMA_AXI_RD_OSR_LMT_SHIFT 16 -#define DMA_AXI_RD_OSR_LMT_MASK 0xf #define DMA_AXI_OSR_MAX 0xf #define DMA_AXI_MAX_OSR_LIMIT ((DMA_AXI_OSR_MAX << DMA_AXI_WR_OSR_LMT_SHIFT) | \ @@ -132,10 +128,8 @@ static inline u32 dma_chan_base_addr(u32 base, u32 chan) #define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */ #define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ #define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ -#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */ -#define DMA_STATUS_TS_SHIFT 20 -#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */ -#define DMA_STATUS_RS_SHIFT 17 +#define DMA_STATUS_TS_MASK GENMASK(22, 20) /* Transmit Process State */ +#define DMA_STATUS_RS_MASK GENMASK(19, 17) /* Receive Process State */ #define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */ #define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */ #define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 467f1a05747ec..2e979f0756650 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -92,10 +92,7 @@ void dwmac_dma_stop_rx(struct stmmac_priv *priv, void __iomem *ioaddr, u32 chan) #ifdef DWMAC_DMA_DEBUG static void show_tx_process_state(unsigned int status) { - unsigned int state; - state = (status & DMA_STATUS_TS_MASK) >> DMA_STATUS_TS_SHIFT; - - switch (state) { + switch (FIELD_GET(DMA_STATUS_TS_MASK, status)) { case 0: pr_debug("- TX (Stopped): Reset or Stop command\n"); break; @@ -123,10 +120,7 @@ static void show_tx_process_state(unsigned int status) static void show_rx_process_state(unsigned int status) { - unsigned int state; - state = (status & DMA_STATUS_RS_MASK) >> DMA_STATUS_RS_SHIFT; - - switch (state) { + switch (FIELD_GET(DMA_STATUS_RS_MASK, status)) { case 0: pr_debug("- RX (Stopped): Reset or Stop command\n"); break; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index e48cfa05000c0..67e2d539c3385 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -24,17 +24,15 @@ #define XGMAC_CONFIG_SS_2500 (0x6 << XGMAC_CONFIG_SS_OFF) #define XGMAC_CONFIG_SS_10_MII (0x7 << XGMAC_CONFIG_SS_OFF) #define XGMAC_CONFIG_SARC GENMASK(22, 20) -#define XGMAC_CONFIG_SARC_SHIFT 20 #define XGMAC_CONFIG_JD BIT(16) #define XGMAC_CONFIG_TE BIT(0) #define XGMAC_CORE_INIT_TX (XGMAC_CONFIG_JD) #define XGMAC_RX_CONFIG 0x00000004 #define XGMAC_CONFIG_ARPEN BIT(31) #define XGMAC_CONFIG_GPSL GENMASK(29, 16) -#define XGMAC_CONFIG_GPSL_SHIFT 16 #define XGMAC_CONFIG_HDSMS GENMASK(14, 12) #define XGMAC_CONFIG_HDSMS_SHIFT 12 -#define XGMAC_CONFIG_HDSMS_256 (0x2 << XGMAC_CONFIG_HDSMS_SHIFT) +#define XGMAC_CONFIG_HDSMS_256 FIELD_PREP(XGMAC_CONFIG_HDSMS, 0x2) #define XGMAC_CONFIG_S2KP BIT(11) #define XGMAC_CONFIG_LM BIT(10) #define XGMAC_CONFIG_IPC BIT(9) @@ -44,8 +42,10 @@ #define XGMAC_CONFIG_CST BIT(2) #define XGMAC_CONFIG_ACS BIT(1) #define XGMAC_CONFIG_RE BIT(0) -#define XGMAC_CORE_INIT_RX (XGMAC_CONFIG_GPSLCE | XGMAC_CONFIG_WD | \ - (XGMAC_JUMBO_LEN << XGMAC_CONFIG_GPSL_SHIFT)) +#define XGMAC_CORE_INIT_RX (XGMAC_CONFIG_GPSLCE | \ + XGMAC_CONFIG_WD | \ + FIELD_PREP(XGMAC_CONFIG_GPSL, \ + XGMAC_JUMBO_LEN)) #define XGMAC_PACKET_FILTER 0x00000008 #define XGMAC_FILTER_RA BIT(31) #define XGMAC_FILTER_IPFE BIT(20) @@ -90,7 +90,6 @@ #define XGMAC_INT_DEFAULT_EN (XGMAC_LPIIE | XGMAC_PMTIE) #define XGMAC_Qx_TX_FLOW_CTRL(x) (0x00000070 + (x) * 4) #define XGMAC_PT GENMASK(31, 16) -#define XGMAC_PT_SHIFT 16 #define XGMAC_TFE BIT(1) #define XGMAC_RX_FLOW_CTRL 0x00000090 #define XGMAC_RFE BIT(0) @@ -180,12 +179,11 @@ #define XGMAC_ADDR_MAX 32 #define XGMAC_AE BIT(31) #define XGMAC_DCS GENMASK(19, 16) -#define XGMAC_DCS_SHIFT 16 #define XGMAC_ADDRx_LOW(x) (0x00000304 + (x) * 0x8) #define XGMAC_L3L4_ADDR_CTRL 0x00000c00 #define XGMAC_IDDR GENMASK(16, 8) -#define XGMAC_IDDR_SHIFT 8 -#define XGMAC_IDDR_FNUM 4 +#define XGMAC_IDDR_FNUM_MASK GENMASK(7, 4) /* FNUM within IDDR */ +#define XGMAC_IDDR_REG_MASK GENMASK(3, 0) /* REG within IDDR */ #define XGMAC_TT BIT(1) #define XGMAC_XB BIT(0) #define XGMAC_L3L4_DATA 0x00000c04 @@ -204,7 +202,6 @@ #define XGMAC_L3PEN0 BIT(0) #define XGMAC_L4_ADDR 0x1 #define XGMAC_L4DP0 GENMASK(31, 16) -#define XGMAC_L4DP0_SHIFT 16 #define XGMAC_L4SP0 GENMASK(15, 0) #define XGMAC_L3_ADDR0 0x4 #define XGMAC_L3_ADDR1 0x5 @@ -224,7 +221,6 @@ #define XGMAC_RSS_DATA 0x00000c8c #define XGMAC_TIMESTAMP_STATUS 0x00000d20 #define XGMAC_TIMESTAMP_ATSNS_MASK GENMASK(29, 25) -#define XGMAC_TIMESTAMP_ATSNS_SHIFT 25 #define XGMAC_TXTSC BIT(15) #define XGMAC_TXTIMESTAMP_NSEC 0x00000d30 #define XGMAC_TXTSSTSLO GENMASK(30, 0) @@ -290,13 +286,9 @@ #define XGMAC_DPP_DISABLE BIT(0) #define XGMAC_MTL_TXQ_OPMODE(x) (0x00001100 + (0x80 * (x))) #define XGMAC_TQS GENMASK(25, 16) -#define XGMAC_TQS_SHIFT 16 #define XGMAC_Q2TCMAP GENMASK(10, 8) -#define XGMAC_Q2TCMAP_SHIFT 8 #define XGMAC_TTC GENMASK(6, 4) -#define XGMAC_TTC_SHIFT 4 #define XGMAC_TXQEN GENMASK(3, 2) -#define XGMAC_TXQEN_SHIFT 2 #define XGMAC_TSF BIT(1) #define XGMAC_MTL_TCx_ETS_CONTROL(x) (0x00001110 + (0x80 * (x))) #define XGMAC_MTL_TCx_QUANTUM_WEIGHT(x) (0x00001118 + (0x80 * (x))) @@ -310,16 +302,12 @@ #define XGMAC_ETS (0x2 << 0) #define XGMAC_MTL_RXQ_OPMODE(x) (0x00001140 + (0x80 * (x))) #define XGMAC_RQS GENMASK(25, 16) -#define XGMAC_RQS_SHIFT 16 #define XGMAC_EHFC BIT(7) #define XGMAC_RSF BIT(5) #define XGMAC_RTC GENMASK(1, 0) -#define XGMAC_RTC_SHIFT 0 #define XGMAC_MTL_RXQ_FLOW_CONTROL(x) (0x00001150 + (0x80 * (x))) #define XGMAC_RFD GENMASK(31, 17) -#define XGMAC_RFD_SHIFT 17 #define XGMAC_RFA GENMASK(15, 1) -#define XGMAC_RFA_SHIFT 1 #define XGMAC_MTL_QINTEN(x) (0x00001170 + (0x80 * (x))) #define XGMAC_RXOIE BIT(16) #define XGMAC_MTL_QINT_STATUS(x) (0x00001174 + (0x80 * (x))) @@ -333,9 +321,7 @@ #define XGMAC_SWR BIT(0) #define XGMAC_DMA_SYSBUS_MODE 0x00003004 #define XGMAC_WR_OSR_LMT GENMASK(29, 24) -#define XGMAC_WR_OSR_LMT_SHIFT 24 #define XGMAC_RD_OSR_LMT GENMASK(21, 16) -#define XGMAC_RD_OSR_LMT_SHIFT 16 #define XGMAC_EN_LPI BIT(15) #define XGMAC_LPI_XIT_PKT BIT(14) #define XGMAC_AAL BIT(12) @@ -377,15 +363,12 @@ #define XGMAC_DMA_CH_TX_CONTROL(x) (0x00003104 + (0x80 * (x))) #define XGMAC_EDSE BIT(28) #define XGMAC_TxPBL GENMASK(21, 16) -#define XGMAC_TxPBL_SHIFT 16 #define XGMAC_TSE BIT(12) #define XGMAC_OSP BIT(4) #define XGMAC_TXST BIT(0) #define XGMAC_DMA_CH_RX_CONTROL(x) (0x00003108 + (0x80 * (x))) #define XGMAC_RxPBL GENMASK(21, 16) -#define XGMAC_RxPBL_SHIFT 16 #define XGMAC_RBSZ GENMASK(14, 1) -#define XGMAC_RBSZ_SHIFT 1 #define XGMAC_RXST BIT(0) #define XGMAC_DMA_CH_TxDESC_HADDR(x) (0x00003110 + (0x80 * (x))) #define XGMAC_DMA_CH_TxDESC_LADDR(x) (0x00003114 + (0x80 * (x))) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 00e929bf280ba..2c10302f53abe 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -376,7 +376,7 @@ static void dwxgmac2_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, u32 value = XGMAC_TFE; if (duplex) - value |= pause_time << XGMAC_PT_SHIFT; + value |= FIELD_PREP(XGMAC_PT, pause_time); writel(value, ioaddr + XGMAC_Qx_TX_FLOW_CTRL(i)); } @@ -1233,8 +1233,7 @@ static void dwxgmac2_sarc_configure(void __iomem *ioaddr, int val) { u32 value = readl(ioaddr + XGMAC_TX_CONFIG); - value &= ~XGMAC_CONFIG_SARC; - value |= val << XGMAC_CONFIG_SARC_SHIFT; + value = u32_replace_bits(value, val, XGMAC_CONFIG_SARC); writel(value, ioaddr + XGMAC_TX_CONFIG); } @@ -1254,14 +1253,16 @@ static int dwxgmac2_filter_read(struct mac_device_info *hw, u32 filter_no, u8 reg, u32 *data) { void __iomem *ioaddr = hw->pcsr; - u32 value; + u32 value, iddr; int ret; ret = dwxgmac2_filter_wait(hw); if (ret) return ret; - value = ((filter_no << XGMAC_IDDR_FNUM) | reg) << XGMAC_IDDR_SHIFT; + iddr = FIELD_PREP(XGMAC_IDDR_FNUM_MASK, filter_no) | + FIELD_PREP(XGMAC_IDDR_REG_MASK, reg); + value = FIELD_PREP(XGMAC_IDDR, iddr); value |= XGMAC_TT | XGMAC_XB; writel(value, ioaddr + XGMAC_L3L4_ADDR_CTRL); @@ -1277,7 +1278,7 @@ static int dwxgmac2_filter_write(struct mac_device_info *hw, u32 filter_no, u8 reg, u32 data) { void __iomem *ioaddr = hw->pcsr; - u32 value; + u32 value, iddr; int ret; ret = dwxgmac2_filter_wait(hw); @@ -1286,7 +1287,9 @@ static int dwxgmac2_filter_write(struct mac_device_info *hw, u32 filter_no, writel(data, ioaddr + XGMAC_L3L4_DATA); - value = ((filter_no << XGMAC_IDDR_FNUM) | reg) << XGMAC_IDDR_SHIFT; + iddr = FIELD_PREP(XGMAC_IDDR_FNUM_MASK, filter_no) | + FIELD_PREP(XGMAC_IDDR_REG_MASK, reg); + value = FIELD_PREP(XGMAC_IDDR, iddr); value |= XGMAC_XB; writel(value, ioaddr + XGMAC_L3L4_ADDR_CTRL); @@ -1395,13 +1398,13 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no, return ret; if (sa) { - value = match & XGMAC_L4SP0; + value = FIELD_PREP(XGMAC_L4SP0, match); ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); if (ret) return ret; } else { - value = (match << XGMAC_L4DP0_SHIFT) & XGMAC_L4DP0; + value = FIELD_PREP(XGMAC_L4DP0, match); ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); if (ret) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index 4d6bb995d8d84..964ce2d733169 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -55,8 +55,7 @@ static void dwxgmac2_dma_init_rx_chan(struct stmmac_priv *priv, u32 value; value = readl(ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan)); - value &= ~XGMAC_RxPBL; - value |= (rxpbl << XGMAC_RxPBL_SHIFT) & XGMAC_RxPBL; + value = u32_replace_bits(value, rxpbl, XGMAC_RxPBL); writel(value, ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan)); writel(upper_32_bits(phy), ioaddr + XGMAC_DMA_CH_RxDESC_HADDR(chan)); @@ -72,9 +71,7 @@ static void dwxgmac2_dma_init_tx_chan(struct stmmac_priv *priv, u32 value; value = readl(ioaddr + XGMAC_DMA_CH_TX_CONTROL(chan)); - value &= ~XGMAC_TxPBL; - value |= (txpbl << XGMAC_TxPBL_SHIFT) & XGMAC_TxPBL; - value |= XGMAC_OSP; + value = u32_replace_bits(value, txpbl, XGMAC_TxPBL); writel(value, ioaddr + XGMAC_DMA_CH_TX_CONTROL(chan)); writel(upper_32_bits(phy), ioaddr + XGMAC_DMA_CH_TxDESC_HADDR(chan)); @@ -91,13 +88,8 @@ static void dwxgmac2_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi) if (axi->axi_xit_frm) value |= XGMAC_LPI_XIT_PKT; - value &= ~XGMAC_WR_OSR_LMT; - value |= (axi->axi_wr_osr_lmt << XGMAC_WR_OSR_LMT_SHIFT) & - XGMAC_WR_OSR_LMT; - - value &= ~XGMAC_RD_OSR_LMT; - value |= (axi->axi_rd_osr_lmt << XGMAC_RD_OSR_LMT_SHIFT) & - XGMAC_RD_OSR_LMT; + value = u32_replace_bits(value, axi->axi_wr_osr_lmt, XGMAC_WR_OSR_LMT); + value = u32_replace_bits(value, axi->axi_rd_osr_lmt, XGMAC_RD_OSR_LMT); if (!axi->axi_fb) value |= XGMAC_UNDEF; @@ -148,23 +140,24 @@ static void dwxgmac2_dma_rx_mode(struct stmmac_priv *priv, void __iomem *ioaddr, { u32 value = readl(ioaddr + XGMAC_MTL_RXQ_OPMODE(channel)); unsigned int rqs = fifosz / 256 - 1; + unsigned int rtc; if (mode == SF_DMA_MODE) { value |= XGMAC_RSF; } else { value &= ~XGMAC_RSF; - value &= ~XGMAC_RTC; if (mode <= 64) - value |= 0x0 << XGMAC_RTC_SHIFT; + rtc = 0x0; else if (mode <= 96) - value |= 0x2 << XGMAC_RTC_SHIFT; + rtc = 0x2; else - value |= 0x3 << XGMAC_RTC_SHIFT; + rtc = 0x3; + + value = u32_replace_bits(value, rtc, XGMAC_RTC); } - value &= ~XGMAC_RQS; - value |= (rqs << XGMAC_RQS_SHIFT) & XGMAC_RQS; + value = u32_replace_bits(value, rqs, XGMAC_RQS); if ((fifosz >= 4096) && (qmode != MTL_QUEUE_AVB)) { u32 flow = readl(ioaddr + XGMAC_MTL_RXQ_FLOW_CONTROL(channel)); @@ -193,11 +186,8 @@ static void dwxgmac2_dma_rx_mode(struct stmmac_priv *priv, void __iomem *ioaddr, break; } - flow &= ~XGMAC_RFD; - flow |= rfd << XGMAC_RFD_SHIFT; - - flow &= ~XGMAC_RFA; - flow |= rfa << XGMAC_RFA_SHIFT; + flow = u32_replace_bits(flow, rfd, XGMAC_RFD); + flow = u32_replace_bits(flow, rfa, XGMAC_RFA); writel(flow, ioaddr + XGMAC_MTL_RXQ_FLOW_CONTROL(channel)); } @@ -210,40 +200,41 @@ static void dwxgmac2_dma_tx_mode(struct stmmac_priv *priv, void __iomem *ioaddr, { u32 value = readl(ioaddr + XGMAC_MTL_TXQ_OPMODE(channel)); unsigned int tqs = fifosz / 256 - 1; + unsigned int ttc, txqen; if (mode == SF_DMA_MODE) { value |= XGMAC_TSF; } else { value &= ~XGMAC_TSF; - value &= ~XGMAC_TTC; if (mode <= 64) - value |= 0x0 << XGMAC_TTC_SHIFT; + ttc = 0x0; else if (mode <= 96) - value |= 0x2 << XGMAC_TTC_SHIFT; + ttc = 0x2; else if (mode <= 128) - value |= 0x3 << XGMAC_TTC_SHIFT; + ttc = 0x3; else if (mode <= 192) - value |= 0x4 << XGMAC_TTC_SHIFT; + ttc = 0x4; else if (mode <= 256) - value |= 0x5 << XGMAC_TTC_SHIFT; + ttc = 0x5; else if (mode <= 384) - value |= 0x6 << XGMAC_TTC_SHIFT; + ttc = 0x6; else - value |= 0x7 << XGMAC_TTC_SHIFT; + ttc = 0x7; + + value = u32_replace_bits(value, ttc, XGMAC_TTC); } /* Use static TC to Queue mapping */ - value |= (channel << XGMAC_Q2TCMAP_SHIFT) & XGMAC_Q2TCMAP; + value |= FIELD_PREP(XGMAC_Q2TCMAP, channel); - value &= ~XGMAC_TXQEN; if (qmode != MTL_QUEUE_AVB) - value |= 0x2 << XGMAC_TXQEN_SHIFT; + txqen = 0x2; else - value |= 0x1 << XGMAC_TXQEN_SHIFT; + txqen = 0x1; - value &= ~XGMAC_TQS; - value |= (tqs << XGMAC_TQS_SHIFT) & XGMAC_TQS; + value = u32_replace_bits(value, txqen, XGMAC_TXQEN); + value = u32_replace_bits(value, tqs, XGMAC_TQS); writel(value, ioaddr + XGMAC_MTL_TXQ_OPMODE(channel)); } @@ -547,16 +538,17 @@ static void dwxgmac2_qmode(struct stmmac_priv *priv, void __iomem *ioaddr, { u32 value = readl(ioaddr + XGMAC_MTL_TXQ_OPMODE(channel)); u32 flow = readl(ioaddr + XGMAC_RX_FLOW_CTRL); + unsigned int txqen; - value &= ~XGMAC_TXQEN; if (qmode != MTL_QUEUE_AVB) { - value |= 0x2 << XGMAC_TXQEN_SHIFT; + txqen = 0x2; writel(0, ioaddr + XGMAC_MTL_TCx_ETS_CONTROL(channel)); } else { - value |= 0x1 << XGMAC_TXQEN_SHIFT; + txqen = 0x1; writel(flow & (~XGMAC_RFE), ioaddr + XGMAC_RX_FLOW_CTRL); } + value = u32_replace_bits(value, txqen, XGMAC_TXQEN); writel(value, ioaddr + XGMAC_MTL_TXQ_OPMODE(channel)); } @@ -566,8 +558,7 @@ static void dwxgmac2_set_bfsize(struct stmmac_priv *priv, void __iomem *ioaddr, u32 value; value = readl(ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan)); - value &= ~XGMAC_RBSZ; - value |= bfsize << XGMAC_RBSZ_SHIFT; + value = u32_replace_bits(value, bfsize, XGMAC_RBSZ); writel(value, ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan)); } From 55d2a8d184e24239b4e934b3379c39e7e201682c Mon Sep 17 00:00:00 2001 From: Nazim Amirul Date: Mon, 13 Jul 2026 19:37:14 -0700 Subject: [PATCH 2518/3297] net: stmmac: xgmac: fix l4 filter port overwrite on register update [ Upstream commit 9fcf274d93af17396f20cccb63f1d4c17492a000 ] The XGMAC_L4_ADDR register holds both source and destination port match values. The current implementation overwrites the entire register when configuring either port, so setting one silently erases the other. Fix this by reading the register first, then masking and updating only the relevant field before writing back. Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower") Signed-off-by: Rohan G Thomas Signed-off-by: Nazim Amirul Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260714023716.29865-3-muhammad.nazim.amirul.nazle.asmade@altera.com Reviewed-by: Jakub Raczynski Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../ethernet/stmicro/stmmac/dwxgmac2_core.c | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 2c10302f53abe..ee41c77426f1a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -1381,36 +1381,40 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no, value &= ~XGMAC_L4PEN0; } - value &= ~(XGMAC_L4SPM0 | XGMAC_L4SPIM0); - value &= ~(XGMAC_L4DPM0 | XGMAC_L4DPIM0); if (sa) { value |= XGMAC_L4SPM0; if (inv) value |= XGMAC_L4SPIM0; + else + value &= ~XGMAC_L4SPIM0; } else { value |= XGMAC_L4DPM0; if (inv) value |= XGMAC_L4DPIM0; + else + value &= ~XGMAC_L4DPIM0; } ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, value); if (ret) return ret; - if (sa) { - value = FIELD_PREP(XGMAC_L4SP0, match); + ret = dwxgmac2_filter_read(hw, filter_no, XGMAC_L4_ADDR, &value); + if (ret) + return ret; - ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); - if (ret) - return ret; + if (sa) { + value &= ~XGMAC_L4SP0; + value |= FIELD_PREP(XGMAC_L4SP0, match); } else { - value = FIELD_PREP(XGMAC_L4DP0, match); - - ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); - if (ret) - return ret; + value &= ~XGMAC_L4DP0; + value |= FIELD_PREP(XGMAC_L4DP0, match); } + ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); + if (ret) + return ret; + if (!en) return dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, 0); From 370dde2b70e58d53cdb7222d4f4cda6e036ee372 Mon Sep 17 00:00:00 2001 From: Nazim Amirul Date: Mon, 13 Jul 2026 19:37:15 -0700 Subject: [PATCH 2519/3297] net: stmmac: fix l3l4 filter rejecting unsupported offload requests [ Upstream commit 5536d7c843637e9430279b94935fcf7df98babb3 ] The basic flow parser in tc_add_basic_flow() does not validate match keys before proceeding. Unsupported offload configurations such as partial protocol masks, non-IPv4 network proto, or non-TCP/UDP transport proto are silently accepted instead of returning -EOPNOTSUPP. Add validation to return -EOPNOTSUPP early for: - No network or transport proto present in the key - Partial protocol mask (only full mask supported) - Network proto is not IPv4 - Transport proto is not TCP or UDP Each rejection includes an extack message so the user knows which part of the match is unsupported. Also propagate -EOPNOTSUPP from tc_add_basic_flow() in tc_add_flow() by returning it directly rather than using break. The break was silently discarding the error for FLOW_CLS_REPLACE operations where entry->in_use is already true, causing tc_add_flow() to return 0 (success) for unsupported replace requests. Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower") Signed-off-by: Rohan G Thomas Signed-off-by: Nazim Amirul Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260714023716.29865-4-muhammad.nazim.amirul.nazle.asmade@altera.com Reviewed-by: Jakub Raczynski Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 3b4d4696afe96..2050f1fa9f44c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -446,6 +446,7 @@ static int tc_parse_flow_actions(struct stmmac_priv *priv, } #define ETHER_TYPE_FULL_MASK cpu_to_be16(~0) +#define IP_PROTO_FULL_MASK 0xFF static int tc_add_basic_flow(struct stmmac_priv *priv, struct flow_cls_offload *cls, @@ -461,6 +462,37 @@ static int tc_add_basic_flow(struct stmmac_priv *priv, flow_rule_match_basic(rule, &match); + /* Both network proto and transport proto not present in the key */ + if (!match.mask || !(match.mask->n_proto || match.mask->ip_proto)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "filter must specify network or transport protocol"); + return -EOPNOTSUPP; + } + + /* If the proto is present in the key and is not full mask */ + if ((match.mask->n_proto && match.mask->n_proto != ETHER_TYPE_FULL_MASK) || + (match.mask->ip_proto && match.mask->ip_proto != IP_PROTO_FULL_MASK)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only full protocol mask is supported"); + return -EOPNOTSUPP; + } + + /* Network proto is present in the key and is not IPv4 */ + if (match.mask->n_proto && match.key->n_proto != cpu_to_be16(ETH_P_IP)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only IPv4 network protocol is supported"); + return -EOPNOTSUPP; + } + + /* Transport proto is present in the key and is not TCP or UDP */ + if (match.mask->ip_proto && + match.key->ip_proto != IPPROTO_TCP && + match.key->ip_proto != IPPROTO_UDP) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only TCP and UDP transport protocols are supported"); + return -EOPNOTSUPP; + } + entry->ip_proto = match.key->ip_proto; return 0; } @@ -598,6 +630,8 @@ static int tc_add_flow(struct stmmac_priv *priv, ret = tc_flow_parsers[i].fn(priv, cls, entry); if (!ret) entry->in_use = true; + else if (ret == -EOPNOTSUPP) + return ret; } if (!entry->in_use) From 1bcb737fb884dae99300e33613174e35bd132f79 Mon Sep 17 00:00:00 2001 From: Nazim Amirul Date: Mon, 13 Jul 2026 19:37:16 -0700 Subject: [PATCH 2520/3297] net: stmmac: reset residual action in L3L4 filters on delete [ Upstream commit a448f821289934b961dd9d8d0beb006cc8937ba2 ] When deleting an L3/L4 flower filter entry, the action field is not reset. If a filter was previously configured with a drop action, that action may persist and affect subsequent filter configurations unintentionally. Clear the action field when the filter entry is deleted. Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower") Signed-off-by: Rohan G Thomas Signed-off-by: Nazim Amirul Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260714023716.29865-5-muhammad.nazim.amirul.nazle.asmade@altera.com Reviewed-by: Jakub Raczynski Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 2050f1fa9f44c..0119b4b89fc0a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -661,6 +661,7 @@ static int tc_del_flow(struct stmmac_priv *priv, entry->in_use = false; entry->cookie = 0; entry->is_l4 = false; + entry->action = 0; return ret; } From 55515c1b1285a119d004492ee6c46dd2aa7f8cd3 Mon Sep 17 00:00:00 2001 From: vadik likholetov Date: Mon, 13 Jul 2026 10:49:11 +0300 Subject: [PATCH 2521/3297] net: stmmac: enable the MAC on link up for all supported speeds [ Upstream commit 9c99db3a2080b8c2cbbb1100369586a9bea43321 ] stmmac_mac_link_down() clears the MAC's transmit and receive enable bits. stmmac_mac_link_up() is expected to set them again through stmmac_mac_set(..., true), but it first switches on the negotiated speed and returns early for a speed the switch does not list. The MAC is then left gated off. The speed selection is split into three switches, keyed on the interface. The generic branch -- taken for everything that is neither USXGMII nor XLGMII, so including PHY_INTERFACE_MODE_10GBASER -- lists only SPEED_2500, SPEED_1000, SPEED_100 and SPEED_10. MGBE on Tegra234 runs 10GBASE-R into an Aquantia AQR113C. That PHY does rate matching, so phylink_link_up() replaces the media speed with the MAC-side interface speed before calling into the MAC: case RATE_MATCH_PAUSE: speed = phylink_interface_max_speed(link_state.interface); duplex = DUPLEX_FULL; The driver is therefore called as stmmac_mac_link_up(interface=10GBASER, speed=10000, duplex=1) which falls through to "default: return;". The interface stops passing traffic after the first link flap. The failure is easy to misread. The link still comes up, because the PHY is polled over MDIO and needs no MAC, so the interface reports carrier 1 at the media speed. The DMA is untouched, so its start bits stay set and descriptors are still consumed. Only the MAC itself is gated off: the receiver counts nothing (mmc_rx_framecount_gb stops advancing, RE is 0) and nothing reaches the wire (TE is 0). The interface survives boot only because stmmac_hw_setup(), called from ndo_open, enables the MAC unconditionally -- so the problem appears only once the cable has been unplugged and plugged back in, and "ip link set dev down && ip link set dev up" appears to fix it. The interface is not what the speed bits depend on: with the single exception of 2.5G, which is selected through the XGMII block on USXGMII and through the regular speed bits otherwise, each speed maps to one field of struct mac_link. The per-interface switches are speed validation, and phylink already validates the speed against priv->hw->link.caps. So collapse the three switches into one keyed on the speed alone, keeping the interface test only for the 2.5G case. This covers 10G on 10GBASE-R, and equally 5G, and 1G/100/10 on USXGMII, all of which hit "default: return;" today. A core that does not support a speed leaves the corresponding mac_link field at 0, and phylink will not offer it that speed in the first place. For dwxgmac2 at 10G, link.xgmii.speed10000 is XGMAC_CONFIG_SS_10000, which is 0 and is the correct speed selection for a 10GBASE-R MAC: ctrl then equals old_ctrl, the register write is skipped, and execution reaches stmmac_mac_set(..., true). Log an error in the default case, since a speed with no entry here leaves the MAC disabled and the symptom does not point at the cause. Fixes: d8ca113724e7 ("net: stmmac: tegra: Add MGBE support") Suggested-by: Maxime Chevallier Signed-off-by: vadik likholetov Reviewed-by: Jacob Keller Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260713074911.30090-1-vadikas@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 92 ++++++++----------- 1 file changed, 37 insertions(+), 55 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 1ceedd74e4290..3be0b79532496 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -892,63 +892,45 @@ static void stmmac_mac_link_up(struct phylink_config *config, old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl = old_ctrl & ~priv->hw->link.speed_mask; - if (interface == PHY_INTERFACE_MODE_USXGMII) { - switch (speed) { - case SPEED_10000: - ctrl |= priv->hw->link.xgmii.speed10000; - break; - case SPEED_5000: - ctrl |= priv->hw->link.xgmii.speed5000; - break; - case SPEED_2500: + switch (speed) { + case SPEED_100000: + ctrl |= priv->hw->link.xlgmii.speed100000; + break; + case SPEED_50000: + ctrl |= priv->hw->link.xlgmii.speed50000; + break; + case SPEED_40000: + ctrl |= priv->hw->link.xlgmii.speed40000; + break; + case SPEED_25000: + ctrl |= priv->hw->link.xlgmii.speed25000; + break; + case SPEED_10000: + ctrl |= priv->hw->link.xgmii.speed10000; + break; + case SPEED_5000: + ctrl |= priv->hw->link.xgmii.speed5000; + break; + case SPEED_2500: + if (interface == PHY_INTERFACE_MODE_USXGMII) ctrl |= priv->hw->link.xgmii.speed2500; - break; - default: - return; - } - } else if (interface == PHY_INTERFACE_MODE_XLGMII) { - switch (speed) { - case SPEED_100000: - ctrl |= priv->hw->link.xlgmii.speed100000; - break; - case SPEED_50000: - ctrl |= priv->hw->link.xlgmii.speed50000; - break; - case SPEED_40000: - ctrl |= priv->hw->link.xlgmii.speed40000; - break; - case SPEED_25000: - ctrl |= priv->hw->link.xlgmii.speed25000; - break; - case SPEED_10000: - ctrl |= priv->hw->link.xgmii.speed10000; - break; - case SPEED_2500: - ctrl |= priv->hw->link.speed2500; - break; - case SPEED_1000: - ctrl |= priv->hw->link.speed1000; - break; - default: - return; - } - } else { - switch (speed) { - case SPEED_2500: + else ctrl |= priv->hw->link.speed2500; - break; - case SPEED_1000: - ctrl |= priv->hw->link.speed1000; - break; - case SPEED_100: - ctrl |= priv->hw->link.speed100; - break; - case SPEED_10: - ctrl |= priv->hw->link.speed10; - break; - default: - return; - } + break; + case SPEED_1000: + ctrl |= priv->hw->link.speed1000; + break; + case SPEED_100: + ctrl |= priv->hw->link.speed100; + break; + case SPEED_10: + ctrl |= priv->hw->link.speed10; + break; + default: + netdev_err(priv->dev, + "unsupported speed %s on %s, leaving the MAC disabled\n", + phy_speed_to_str(speed), phy_modes(interface)); + return; } if (priv->plat->fix_mac_speed) From 15a1c5f2ed2eeb3daad8d5766fd506aeda4710f3 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Mon, 13 Jul 2026 23:09:45 +0800 Subject: [PATCH 2522/3297] net: gre: fix lltx regression for GRE tunnels with SEQ/CSUM [ Upstream commit 675ed582c1aa4d919dd535490de08c015005c653 ] Before commit 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to dev->lltx"), NETIF_F_LLTX was set unconditionally in both __gre_tunnel_init() and ip6gre_tnl_init_features() alongside GRE_FEATURES: dev->features |= GRE_FEATURES | NETIF_F_LLTX; When that commit converted NETIF_F_LLTX to the dev->lltx flag, it placed 'dev->lltx = true' after the SEQ/CSUM early returns instead of before them. This causes GRE/GRETAP/ip6gre tunnels with SEQ or CSUM+encap to lose lockless TX, reintroducing _xmit_lock acquisition around their ndo_start_xmit. Since GRE xmit re-enters the stack via ip_tunnel_xmit(), holding _xmit_lock risks ABBA deadlock with the underlay device. CPU0 CPU1 ---- ---- lock(&qdisc_xmit_lock_key#6); lock(&qdisc_xmit_lock_key#3); lock(&qdisc_xmit_lock_key#6); lock(&qdisc_xmit_lock_key#3); Fix by moving dev->lltx = true before the early returns in both functions, restoring the original unconditional behavior. Fixes: 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to dev->lltx") Signed-off-by: Yun Zhou Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260713150945.1779628-1-yun.zhou@windriver.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/ip_gre.c | 4 ++-- net/ipv6/ip6_gre.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 879d37c557faf..300b802125db2 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1017,6 +1017,8 @@ static void __gre_tunnel_init(struct net_device *dev) dev->features |= GRE_FEATURES; dev->hw_features |= GRE_FEATURES; + dev->lltx = true; + /* TCP offload with GRE SEQ is not supported, nor can we support 2 * levels of outer headers requiring an update. */ @@ -1028,8 +1030,6 @@ static void __gre_tunnel_init(struct net_device *dev) dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - - dev->lltx = true; } static int ipgre_tunnel_init(struct net_device *dev) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 75961c4ebcdd2..58b473c157c82 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1454,6 +1454,8 @@ static void ip6gre_tnl_init_features(struct net_device *dev) dev->features |= GRE6_FEATURES; dev->hw_features |= GRE6_FEATURES; + dev->lltx = true; + /* TCP offload with GRE SEQ is not supported, nor can we support 2 * levels of outer headers requiring an update. */ @@ -1465,8 +1467,6 @@ static void ip6gre_tnl_init_features(struct net_device *dev) dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - - dev->lltx = true; } static int ip6gre_tunnel_init_common(struct net_device *dev) From e037a41938c69e60acfa6362eeffd8649ea5e4c7 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Wed, 15 Jul 2026 10:50:07 +0530 Subject: [PATCH 2523/3297] octeontx2-vf: set TC flower flag on MCAM entry allocation [ Upstream commit 0d4d31e3cc5dd6204fa1495c4107f5075acce5ed ] When MCAM entries are allocated for a VF netdev via the devlink mcam_count parameter, only OTX2_FLAG_NTUPLE_SUPPORT was set. That enabled ethtool ntuple filters but not tc flower offload. Also set OTX2_FLAG_TC_FLOWER_SUPPORT when entries are successfully allocated. Fixes: 2da489432747 ("octeontx2-pf: devlink params support to set mcam entry count") Signed-off-by: Suman Ghosh Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260715052007.2099851-1-rkannoth@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 64c6d9162ef64..17e8f6e51808b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -146,6 +146,7 @@ int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count) if (allocated) { pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC; pfvf->flags |= OTX2_FLAG_NTUPLE_SUPPORT; + pfvf->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT; } if (allocated != count) From 19fe119dfa93fd7e50c05bafc88e4e4a00929231 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 Jul 2026 10:42:36 +0000 Subject: [PATCH 2524/3297] ipv4: icmp: fill flow parameters in icmp_route_lookup decoy lookup [ Upstream commit 853e164c2b321f0711361bc23505aaeb7dc432c3 ] When Linux forwards a packet and needs to generate an ICMP error, icmp_route_lookup() performs a reverse-path relookup. For non-local destinations, it performs a decoy lookup to find the expected egress interface (rt2->dst.dev) before validating the path with ip_route_input(). Currently, the decoy flow structure (fl4_2) only sets .daddr = fl4_dec.saddr, leaving .saddr, .flowi4_dscp, .flowi4_proto, .flowi4_mark, .flowi4_oif, .fl4_sport, .fl4_dport, and .flowi4_uid zeroed out. When policy routing rules (such as ip rule add from $SRC lookup 100, or dscp/fwmark/ipproto/port rules, or VRF bindings) are configured: 1. The decoy lookup fails to match the policy rule because saddr and other key flow selectors are missing in fl4_2. 2. It resolves a route using the default table instead, returning an incorrect egress netdev. 3. Passing the wrong netdev to ip_route_input() causes strict reverse-path filtering (rp_filter=1) to fail, logging false-positive "martian source" warnings and causing the relookup to fail. Fix this by initializing fl4_2 from fl4_dec and: - Swapping source/destination IP addresses. - Swapping L4 ports for transport protocols with ports (TCP, UDP, SCTP, DCCP) so port-based policy routing matches correctly. Non-port protocols (such as ICMP or GRE) leave the flowi_uli union fields intact to prevent corruption. - Setting .flowi4_oif = l3mdev_master_ifindex(route_lookup_dev) to ensure VRF routing tables are respected. - Setting .flowi4_flags |= FLOWI_FLAG_ANYSRC to allow output route lookups for non-local source IP addresses. - Using __ip_route_output_key() instead of ip_route_output_key() for fl4_2 so that raw FIB routing is used without triggering spurious XFRM policy lookups on the decoy flow (the actual XFRM lookup is performed later using fl4_dec). Fixes: 415b3334a21a ("icmp: Fix regression in nexthop resolution during replies.") Reported-by: Muhammad Ziad Closes: https://lore.kernel.org/netdev/CAOAwikA60AYKdFr_UDLyja3oU4hqyAE7uFZWqum5uRdaQsgRYg@mail.gmail.com/ Signed-off-by: Eric Dumazet Reviewed-by: David Ahern Link: https://patch.msgid.link/20260722104236.2938082-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/icmp.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index fc0a93f43313d..691d7b076c620 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -539,11 +539,23 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, if (IS_ERR(rt2)) err = PTR_ERR(rt2); } else { - struct flowi4 fl4_2 = {}; + struct flowi4 fl4_2 = fl4_dec; unsigned long orefdst; - fl4_2.daddr = fl4_dec.saddr; - rt2 = ip_route_output_key(net, &fl4_2); + swap(fl4_2.daddr, fl4_2.saddr); + switch (fl4_2.flowi4_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + swap(fl4_2.fl4_sport, fl4_2.fl4_dport); + break; + } + + fl4_2.flowi4_oif = l3mdev_master_ifindex(route_lookup_dev); + fl4_2.flowi4_flags |= FLOWI_FLAG_ANYSRC; + + rt2 = __ip_route_output_key(net, &fl4_2); if (IS_ERR(rt2)) { err = PTR_ERR(rt2); goto relookup_failed; From 8fc45a2a7cc24a27ccf1e3ac538600ef38235a36 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 Jul 2026 10:16:05 +0000 Subject: [PATCH 2525/3297] ppp: annotate data races in ppp_generic [ Upstream commit 543adf072165aaf2e3b635c0476204f9658ed3bf ] Several fields in struct ppp can be read or updated concurrently from multiple CPUs without synchronization, causing data races: 1. ppp->mru is read concurrently in ppp_receive_nonmp_frame() while being updated via PPPIOCSMRU ioctl. Protect ppp->mru updates in PPPIOCSMRU with ppp_recv_lock(ppp). 2. PPPIOCGFLAGS reads ppp->flags, ppp->xstate, and ppp->rstate unlocked. Wrap the read in ppp_lock(ppp) to get a consistent snapshot. 3. ppp->debug is updated via PPPIOCSDEBUG and read concurrently on fast paths. Annotate reads with READ_ONCE() and writes with WRITE_ONCE(). 4. ppp->last_xmit and ppp->last_recv are updated on TX/RX data paths and read via PPPIOCGIDLE32 / PPPIOCGIDLE64 ioctls. Annotate with WRITE_ONCE() / READ_ONCE() and use max() to handle jiffies subtraction. 5. ppp->npmode[] is updated via PPPIOCSNPMODE and read on TX/RX paths. Annotate with WRITE_ONCE() / READ_ONCE(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Qingfang Deng Link: https://patch.msgid.link/20260722101605.2868548-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ppp/ppp_generic.c | 50 ++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index a2fe0fe2f3389..14fa87a8e0834 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -818,7 +818,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCSMRU: if (get_user(val, p)) break; + ppp_recv_lock(ppp); ppp->mru = val; + ppp_recv_unlock(ppp); err = 0; break; @@ -839,7 +841,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCGFLAGS: + ppp_lock(ppp); val = ppp->flags | ppp->xstate | ppp->rstate; + ppp_unlock(ppp); if (put_user(val, p)) break; err = 0; @@ -863,7 +867,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCSDEBUG: if (get_user(val, p)) break; - ppp->debug = val; + WRITE_ONCE(ppp->debug, val); err = 0; break; @@ -874,16 +878,16 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCGIDLE32: - idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle32.recv_idle = (jiffies - ppp->last_recv) / HZ; - if (copy_to_user(argp, &idle32, sizeof(idle32))) + idle32.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ; + idle32.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ; + if (copy_to_user(argp, &idle32, sizeof(idle32))) break; err = 0; break; case PPPIOCGIDLE64: - idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle64.recv_idle = (jiffies - ppp->last_recv) / HZ; + idle64.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ; + idle64.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ; if (copy_to_user(argp, &idle64, sizeof(idle64))) break; err = 0; @@ -924,7 +928,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_to_user(argp, &npi, sizeof(npi))) break; } else { - ppp->npmode[i] = npi.mode; + WRITE_ONCE(ppp->npmode[i], npi.mode); /* we may be able to transmit more packets now (??) */ netif_wake_queue(ppp->dev); } @@ -1462,7 +1466,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) goto outf; /* Drop, accept or reject the packet */ - switch (ppp->npmode[npi]) { + switch (READ_ONCE(ppp->npmode[npi])) { case NPMODE_PASS: break; case NPMODE_QUEUE: @@ -1802,7 +1806,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_OUTBOUND_TAG); if (ppp->pass_filter && bpf_prog_run(ppp->pass_filter, skb) == 0) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: outbound frame " "not passed\n"); @@ -1812,11 +1816,11 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && bpf_prog_run(ppp->active_filter, skb) == 0)) - ppp->last_xmit = jiffies; + WRITE_ONCE(ppp->last_xmit, jiffies); skb_pull(skb, 2); #else /* for data packets, record the time */ - ppp->last_xmit = jiffies; + WRITE_ONCE(ppp->last_xmit, jiffies); #endif /* CONFIG_PPP_FILTER */ } @@ -2189,7 +2193,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) noskb: spin_unlock(&pch->downl); err_linearize: - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); DEV_STATS_INC(ppp->dev, tx_errors); ++ppp->nxseq; @@ -2548,7 +2552,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_INBOUND_TAG); if (ppp->pass_filter && bpf_prog_run(ppp->pass_filter, skb) == 0) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: inbound frame " "not passed\n"); @@ -2557,14 +2561,14 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) } if (!(ppp->active_filter && bpf_prog_run(ppp->active_filter, skb) == 0)) - ppp->last_recv = jiffies; + WRITE_ONCE(ppp->last_recv, jiffies); __skb_pull(skb, 2); } else #endif /* CONFIG_PPP_FILTER */ - ppp->last_recv = jiffies; + WRITE_ONCE(ppp->last_recv, jiffies); if ((ppp->dev->flags & IFF_UP) == 0 || - ppp->npmode[npi] != NPMODE_PASS) { + READ_ONCE(ppp->npmode[npi]) != NPMODE_PASS) { kfree_skb(skb); } else { /* chop off protocol */ @@ -2817,7 +2821,7 @@ ppp_mp_reconstruct(struct ppp *ppp) seq = seq_before(minseq, PPP_MP_CB(p)->sequence)? minseq + 1: PPP_MP_CB(p)->sequence; - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "lost frag %u..%u\n", oldseq, seq-1); @@ -2866,7 +2870,7 @@ ppp_mp_reconstruct(struct ppp *ppp) struct sk_buff *tmp2; skb_queue_reverse_walk_from_safe(list, p, tmp2) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "discarding frag %u\n", PPP_MP_CB(p)->sequence); @@ -2888,7 +2892,7 @@ ppp_mp_reconstruct(struct ppp *ppp) skb_queue_walk_safe(list, p, tmp) { if (p == head) break; - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "discarding frag %u\n", PPP_MP_CB(p)->sequence); @@ -2896,7 +2900,7 @@ ppp_mp_reconstruct(struct ppp *ppp) kfree_skb(p); } - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, " missed pkts %u..%u\n", ppp->nextseq, @@ -3209,7 +3213,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (!ppp->rc_state) break; if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len, - ppp->file.index, 0, ppp->mru, ppp->debug)) { + ppp->file.index, 0, ppp->mru, + READ_ONCE(ppp->debug))) { ppp->rstate |= SC_DECOMP_RUN; ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR); } @@ -3218,7 +3223,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (!ppp->xc_state) break; if (ppp->xcomp->comp_init(ppp->xc_state, dp, len, - ppp->file.index, 0, ppp->debug)) + ppp->file.index, 0, + READ_ONCE(ppp->debug))) ppp->xstate |= SC_COMP_RUN; } break; From 0d57d43d7c4c67b26f414e67959eb54de479377d Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Wed, 22 Jul 2026 10:53:53 +0800 Subject: [PATCH 2526/3297] hinic: remove unused ethtool RSS user configuration buffers [ Upstream commit fe0c002928c6749b7f4a726f6f600f6dd70280ea ] rss_indir_user and rss_hkey_user are allocated and filled in __set_rss_rxfh() when the user configures RSS via ethtool, but nothing ever reads them. hinic_get_rxfh() fetches the state from the device, and the hardware is programmed from the original indir/key arguments. These buffers only leaked on driver unload. Drop the unused allocations, memcpys, and struct fields. Fixes: 4fdc51bb4e92 ("hinic: add support for rss parameters with ethtool") Signed-off-by: Chenguang Zhao Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260722025353.328179-1-chenguang.zhao@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 2 -- .../net/ethernet/huawei/hinic/hinic_ethtool.c | 21 ------------------- 2 files changed, 23 deletions(-) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 52ea97c818b8e..d9ab94910a2a7 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -104,8 +104,6 @@ struct hinic_dev { u16 num_rss; u16 rss_limit; struct hinic_rss_type rss_type; - u8 *rss_hkey_user; - s32 *rss_indir_user; struct hinic_intr_coal_info *rx_intr_coalesce; struct hinic_intr_coal_info *tx_intr_coalesce; struct hinic_sriov_info sriov_info; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index e9f338e9dbe7a..cd4c295ec7aeb 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -1064,17 +1064,6 @@ static int __set_rss_rxfh(struct net_device *netdev, int err; if (indir) { - if (!nic_dev->rss_indir_user) { - nic_dev->rss_indir_user = - kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, - GFP_KERNEL); - if (!nic_dev->rss_indir_user) - return -ENOMEM; - } - - memcpy(nic_dev->rss_indir_user, indir, - sizeof(u32) * HINIC_RSS_INDIR_SIZE); - err = hinic_rss_set_indir_tbl(nic_dev, nic_dev->rss_tmpl_idx, indir); if (err) @@ -1082,16 +1071,6 @@ static int __set_rss_rxfh(struct net_device *netdev, } if (key) { - if (!nic_dev->rss_hkey_user) { - nic_dev->rss_hkey_user = - kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL); - - if (!nic_dev->rss_hkey_user) - return -ENOMEM; - } - - memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE); - err = hinic_rss_set_template_tbl(nic_dev, nic_dev->rss_tmpl_idx, key); if (err) From 8150c48fb978e01689f94ed80148f8a7499ae571 Mon Sep 17 00:00:00 2001 From: Aldo Ariel Panzardo Date: Thu, 16 Jul 2026 12:43:19 -0300 Subject: [PATCH 2527/3297] net: qrtr: restrict socket creation to the initial network namespace [ Upstream commit 3b536db8fb32da9e9c62f2bb45e2e319331f0426 ] QRTR keeps its entire port and node state in module-global variables that are not partitioned per network namespace: qrtr_local_nid is a single global node id (always 1) and qrtr_ports is a single global xarray. qrtr_port_lookup() and qrtr_local_enqueue() operate on that global state with no network-namespace check, and qrtr_create() places no restriction on the namespace a socket is created in. As a result an unprivileged process that creates an AF_QIPCRTR socket in a separate network namespace, e.g. via unshare(CLONE_NEWUSER | CLONE_NEWNET), can send QRTR datagrams - including control-plane messages such as QRTR_TYPE_NEW_SERVER - to QRTR sockets owned by another namespace, and vice versa. The receiving socket sees such a message as coming from node id 1, indistinguishable from a legitimate local client, breaking the isolation that network namespaces are expected to provide. QRTR is a transport to global hardware endpoints (the modem and other remote processors) and has no per-namespace semantics; its in-kernel name service already creates its socket in init_net only. Confine the socket family to the initial network namespace, as other non-namespace-aware socket families do (see llc_ui_create() and the ieee802154 socket code). Fixes: bdabad3e363d ("net: Add Qualcomm IPC router") Signed-off-by: Aldo Ariel Panzardo Link: https://patch.msgid.link/20260716154319.3297699-1-qwe.aldo@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/qrtr/af_qrtr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index 305523cebe3ba..fcd24a7df3f00 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -1261,6 +1261,14 @@ static int qrtr_create(struct net *net, struct socket *sock, if (sock->type != SOCK_DGRAM) return -EPROTOTYPE; + /* QRTR keeps its port and node state in module-global variables that + * are not partitioned per network namespace, and the in-kernel name + * service only operates in init_net. Confine the family to init_net so + * a socket in another namespace cannot reach the global control plane. + */ + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern); if (!sk) return -ENOMEM; From 953d47cfe529a88ae321de5b74cc6e3046e31c43 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 16 Jul 2026 22:29:58 +0800 Subject: [PATCH 2528/3297] raw: annotate lockless match fields in raw_v4_match() [ Upstream commit 18f116931f52e3c3303ad4b15ff41eb89b0e4239 ] raw_v4_match() is a lockless match helper under sk_for_each_rcu(). It still reads inet->inet_daddr, inet->inet_rcv_saddr and sk->sk_bound_dev_if with plain loads while bind, connect and bind-to-device paths can update the same match fields concurrently. Annotate only those mutable match fields in raw_v4_match(), and do so at the point of use instead of hoisting the bound-device read before the earlier short-circuit tests. Also annotate the raw bind writer and the shared IPv4 datagram connect writer used by raw sockets, so the address fields updated on bind and connect match explicit WRITE_ONCE() updates. This version intentionally leaves the shared disconnect-side IPv4 writers to follow-up cleanup and limits the writer changes here to the raw bind path and the datagram connect path directly exercised by raw sockets. Fixes: 0daf07e52709 ("raw: convert raw sockets to RCU") Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260716142958.3064224-1-runyu.xiao@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/datagram.c | 4 ++-- net/ipv4/raw.c | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index c2b2cda1a7e50..7861615b34fc6 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -63,12 +63,12 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len } /* Update addresses before rehashing */ - inet->inet_daddr = fl4->daddr; + WRITE_ONCE(inet->inet_daddr, fl4->daddr); inet->inet_dport = usin->sin_port; if (!inet->inet_saddr) inet->inet_saddr = fl4->saddr; if (!inet->inet_rcv_saddr) { - inet->inet_rcv_saddr = fl4->saddr; + WRITE_ONCE(inet->inet_rcv_saddr, fl4->saddr); if (sk->sk_prot->rehash) sk->sk_prot->rehash(sk); } diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 26468a2ec63db..c10cf41a365dc 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -120,13 +120,21 @@ bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num, __be32 raddr, __be32 laddr, int dif, int sdif) { const struct inet_sock *inet = inet_sk(sk); + __be32 daddr, rcv_saddr; - if (net_eq(sock_net(sk), net) && inet->inet_num == num && - !(inet->inet_daddr && inet->inet_daddr != raddr) && - !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) && - raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif)) - return true; - return false; + if (!net_eq(sock_net(sk), net) || inet->inet_num != num) + return false; + + daddr = READ_ONCE(inet->inet_daddr); + if (daddr && daddr != raddr) + return false; + + rcv_saddr = READ_ONCE(inet->inet_rcv_saddr); + if (rcv_saddr && rcv_saddr != laddr) + return false; + + return raw_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if), + dif, sdif); } EXPORT_SYMBOL_GPL(raw_v4_match); @@ -721,7 +729,8 @@ static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) chk_addr_ret)) goto out; - inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; + inet->inet_saddr = addr->sin_addr.s_addr; + WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr); if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) inet->inet_saddr = 0; /* Use device */ sk_dst_reset(sk); From 5f2ef3d53d37489a35005e43c7c37f98c900ace9 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 17 Nov 2025 23:42:05 +0200 Subject: [PATCH 2529/3297] net/mlx5: Refactor EEPROM query error handling to return status separately [ Upstream commit 2e4c44b12f4da60d3e8dcbc1ccf38bb28a878050 ] Matthew and Jakub reported [1] issues where inventory automation tools are calling EEPROM query repeatedly on a port that doesn't have an SFP connected, resulting in millions of error prints. Move MCIA register status extraction from the query functions to the callers, allowing use of extack reporting instead of a dmesg print when using the netlink API. [1] https://lore.kernel.org/netdev/20251028194011.39877-1-mattc@purestorage.com/ Cc: Matthew W Carlis Signed-off-by: Gal Pressman Reviewed-by: Jianbo Liu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/1763415729-1238421-2-git-send-email-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 11c057d23465 ("net/mlx5: Fix MCIA register buffer overflow on 32 dword reads") Signed-off-by: Sasha Levin --- .../ethernet/mellanox/mlx5/core/en_ethtool.c | 19 +++++----- .../ethernet/mellanox/mlx5/core/mlx5_core.h | 4 +-- .../net/ethernet/mellanox/mlx5/core/port.c | 35 +++++++++---------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 893e1380a7c97..8a07b9e8dc386 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -2027,7 +2027,7 @@ static int mlx5e_get_module_info(struct net_device *netdev, int size_read = 0; u8 data[4] = {0}; - size_read = mlx5_query_module_eeprom(dev, 0, 2, data); + size_read = mlx5_query_module_eeprom(dev, 0, 2, data, NULL); if (size_read < 2) return -EIO; @@ -2069,6 +2069,7 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev, struct mlx5_core_dev *mdev = priv->mdev; int offset = ee->offset; int size_read; + u8 status = 0; int i = 0; if (!ee->len) @@ -2078,15 +2079,15 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev, while (i < ee->len) { size_read = mlx5_query_module_eeprom(mdev, offset, ee->len - i, - data + i); - + data + i, &status); if (!size_read) /* Done reading */ return 0; if (size_read < 0) { - netdev_err(priv->netdev, "%s: mlx5_query_eeprom failed:0x%x\n", - __func__, size_read); + netdev_err(netdev, + "%s: mlx5_query_eeprom failed:0x%x, status %u\n", + __func__, size_read, status); return size_read; } @@ -2106,6 +2107,7 @@ static int mlx5e_get_module_eeprom_by_page(struct net_device *netdev, struct mlx5_core_dev *mdev = priv->mdev; u8 *data = page_data->data; int size_read; + u8 status = 0; int i = 0; if (!page_data->length) @@ -2119,7 +2121,8 @@ static int mlx5e_get_module_eeprom_by_page(struct net_device *netdev, query.page = page_data->page; while (i < page_data->length) { query.size = page_data->length - i; - size_read = mlx5_query_module_eeprom_by_page(mdev, &query, data + i); + size_read = mlx5_query_module_eeprom_by_page(mdev, &query, + data + i, &status); /* Done reading, return how many bytes was read */ if (!size_read) @@ -2128,8 +2131,8 @@ static int mlx5e_get_module_eeprom_by_page(struct net_device *netdev, if (size_read < 0) { NL_SET_ERR_MSG_FMT_MOD( extack, - "Query module eeprom by page failed, read %u bytes, err %d", - i, size_read); + "Query module eeprom by page failed, read %u bytes, err %d, status %u", + i, size_read, status); return size_read; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 09c544bdf70da..e08903d002a10 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -358,11 +358,11 @@ int mlx5_set_port_fcs(struct mlx5_core_dev *mdev, u8 enable); void mlx5_query_port_fcs(struct mlx5_core_dev *mdev, bool *supported, bool *enabled); int mlx5_query_module_eeprom(struct mlx5_core_dev *dev, - u16 offset, u16 size, u8 *data); + u16 offset, u16 size, u8 *data, u8 *status); int mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev, struct mlx5_module_eeprom_query_params *params, - u8 *data); + u8 *data, u8 *status); int mlx5_query_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *out); int mlx5_set_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *in); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 876e648c91ba8..5fa06f69943df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -289,11 +289,11 @@ int mlx5_query_module_num(struct mlx5_core_dev *dev, int *module_num) } static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num, - u8 *module_id) + u8 *module_id, u8 *status) { u32 in[MLX5_ST_SZ_DW(mcia_reg)] = {}; u32 out[MLX5_ST_SZ_DW(mcia_reg)]; - int err, status; + int err; u8 *ptr; MLX5_SET(mcia_reg, in, i2c_device_address, MLX5_I2C_ADDR_LOW); @@ -308,12 +308,12 @@ static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num, if (err) return err; - status = MLX5_GET(mcia_reg, out, status); - if (status) { - mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n", - status); + if (MLX5_GET(mcia_reg, out, status)) { + if (status) + *status = MLX5_GET(mcia_reg, out, status); return -EIO; } + ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); *module_id = ptr[0]; @@ -370,13 +370,14 @@ static int mlx5_mcia_max_bytes(struct mlx5_core_dev *dev) } static int mlx5_query_mcia(struct mlx5_core_dev *dev, - struct mlx5_module_eeprom_query_params *params, u8 *data) + struct mlx5_module_eeprom_query_params *params, + u8 *data, u8 *status) { u32 in[MLX5_ST_SZ_DW(mcia_reg)] = {}; u32 out[MLX5_ST_SZ_DW(mcia_reg)]; - int status, err; void *ptr; u16 size; + int err; size = min_t(int, params->size, mlx5_mcia_max_bytes(dev)); @@ -392,12 +393,9 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev, if (err) return err; - status = MLX5_GET(mcia_reg, out, status); - if (status) { - mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n", - status); + *status = MLX5_GET(mcia_reg, out, status); + if (*status) return -EIO; - } ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); memcpy(data, ptr, size); @@ -406,7 +404,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev, } int mlx5_query_module_eeprom(struct mlx5_core_dev *dev, - u16 offset, u16 size, u8 *data) + u16 offset, u16 size, u8 *data, u8 *status) { struct mlx5_module_eeprom_query_params query = {0}; u8 module_id; @@ -416,7 +414,8 @@ int mlx5_query_module_eeprom(struct mlx5_core_dev *dev, if (err) return err; - err = mlx5_query_module_id(dev, query.module_number, &module_id); + err = mlx5_query_module_id(dev, query.module_number, &module_id, + status); if (err) return err; @@ -442,12 +441,12 @@ int mlx5_query_module_eeprom(struct mlx5_core_dev *dev, query.size = size; query.offset = offset; - return mlx5_query_mcia(dev, &query, data); + return mlx5_query_mcia(dev, &query, data, status); } int mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev, struct mlx5_module_eeprom_query_params *params, - u8 *data) + u8 *data, u8 *status) { int err; @@ -461,7 +460,7 @@ int mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev, return -EINVAL; } - return mlx5_query_mcia(dev, params, data); + return mlx5_query_mcia(dev, params, data, status); } static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc, From 87b39a8c875ca744b7de69af0a8ef8874cffccf1 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Fri, 17 Jul 2026 10:23:38 +0300 Subject: [PATCH 2530/3297] net/mlx5: Fix MCIA register buffer overflow on 32 dword reads [ Upstream commit 11c057d23465c7a5817a7284c896d19d54c0b616 ] The MCIA register can return up to 32 dwords (128 bytes) when the device advertises the mcia_32dwords capability, but struct mlx5_ifc_mcia_reg_bits only defines dword_0..11, leaving room for just 12 dwords (48 bytes) of data. mlx5_query_mcia() clamps the read size to mlx5_mcia_max_bytes() and then memcpy()s that many bytes out of the register, potentially reading past the end of the 'out' buffer. On kernels built with FORTIFY_SOURCE this is caught as a buffer overflow while reading the module EEPROM via ethtool: detected buffer overflow in memcpy kernel BUG at lib/string_helpers.c:1048! RIP: 0010:fortify_panic+0x13/0x20 Call Trace: mlx5_query_mcia.isra.0+0x200/0x210 [mlx5_core] mlx5_query_module_eeprom_by_page+0x4a/0xa0 [mlx5_core] mlx5e_get_module_eeprom_by_page+0xbb/0x120 [mlx5_core] eeprom_prepare_data+0xf3/0x170 ethnl_default_doit+0xf1/0x3b0 Extend the mcia_reg layout to 32 dwords. Fixes: 271907ee2f29 ("net/mlx5: Query the maximum MCIA register read size from firmware") Signed-off-by: Gal Pressman Reviewed-by: Alex Lazar Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260717072338.1240582-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/port.c | 4 ++-- include/linux/mlx5/mlx5_ifc.h | 13 +------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 5fa06f69943df..d25713ce65b6d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -314,7 +314,7 @@ static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num, return -EIO; } - ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); + ptr = MLX5_ADDR_OF(mcia_reg, out, dwords); *module_id = ptr[0]; @@ -397,7 +397,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev, if (*status) return -EIO; - ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); + ptr = MLX5_ADDR_OF(mcia_reg, out, dwords); memcpy(data, ptr, size); return size; diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 1b0b36aa2a767..61257b866fb24 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -12141,18 +12141,7 @@ struct mlx5_ifc_mcia_reg_bits { u8 reserved_at_60[0x20]; - u8 dword_0[0x20]; - u8 dword_1[0x20]; - u8 dword_2[0x20]; - u8 dword_3[0x20]; - u8 dword_4[0x20]; - u8 dword_5[0x20]; - u8 dword_6[0x20]; - u8 dword_7[0x20]; - u8 dword_8[0x20]; - u8 dword_9[0x20]; - u8 dword_10[0x20]; - u8 dword_11[0x20]; + u8 dwords[0x400]; }; struct mlx5_ifc_dcbx_param_bits { From cdddc8188db46a472b7cfca94be2da15a9c4d72e Mon Sep 17 00:00:00 2001 From: Yael Chemla Date: Fri, 17 Jul 2026 10:33:06 +0300 Subject: [PATCH 2531/3297] net/mlx5: E-Switch, fix zero num_dest in prio_tag egress vlan rule [ Upstream commit d12956d083eb70f2c6d72711aebaf8c2ce21e170 ] esw_egress_acl_vlan_create() hardcodes num_dest=0 in its mlx5_add_flow_rules() call. When invoked from the non-bond path fwd_dest is NULL and num_dest=0 is correct. When invoked from esw_acl_egress_ofld_rules_create() during a bond event, fwd_dest is non-NULL and flow_act.action carries MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, but _mlx5_add_flow_rules() rejects a non-NULL dest pointer paired with dest_num<=0 and returns -EINVAL. The error propagates as "configure slave vport egress fwd, err(-22)". The passive vport's egress ACL table ends up with its flow groups allocated but no FTEs, so prio-tagged packets are not popped and bond failover is broken on prio_tag_required devices. Fix by passing fwd_dest ? 1 : 0 as num_dest to match the actual number of destinations supplied. Fixes: bf773dc0e6d5 ("net/mlx5: E-Switch, Introduce APIs to enable egress acl forward-to-vport rule") Signed-off-by: Yael Chemla Reviewed-by: Cosmin Ratiu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260717073306.1242399-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c index 3ce455c2535c4..53f064ebd1c18 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c @@ -71,7 +71,7 @@ int esw_egress_acl_vlan_create(struct mlx5_eswitch *esw, flow_act.action = flow_action; vport->egress.allowed_vlan = mlx5_add_flow_rules(vport->egress.acl, spec, - &flow_act, fwd_dest, 0); + &flow_act, fwd_dest, fwd_dest ? 1 : 0); if (IS_ERR(vport->egress.allowed_vlan)) { err = PTR_ERR(vport->egress.allowed_vlan); esw_warn(esw->dev, From a7e430349fc5ee9750731b384e8e742c471924a7 Mon Sep 17 00:00:00 2001 From: Alexei Lazar Date: Fri, 17 Jul 2026 10:51:24 +0300 Subject: [PATCH 2532/3297] net/mlx5e: Report zero bandwidth for non-ETS traffic classes [ Upstream commit ffb1873b2df11945b8c395e859169248675c91c5 ] The IEEE 802.1Qaz standard defines that bandwidth allocation percentages only apply to Enhanced Transmission Selection (ETS) traffic classes. For STRICT and VENDOR transmission selection algorithms, bandwidth percentage values are not applicable. Currently for non-ETS 100 bandwidth is being reported for all traffic classes in the get operation due to hardware limitation, regardless of their TSA type. Fix this by reporting 0 for non-ETS traffic classes. Fixes: 820c2c5e773d ("net/mlx5e: Read ETS settings directly from firmware") Signed-off-by: Alexei Lazar Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260717075125.1244877-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index cf8f14ce4cd50..fbd84ab8d61e7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -158,6 +158,13 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, } memcpy(ets->tc_tsa, priv->dcbx.tc_tsa, sizeof(ets->tc_tsa)); + /* Report 0 for non ETS TSA */ + for (i = 0; i < ets->ets_cap; i++) { + if (ets->tc_tx_bw[i] == MLX5E_MAX_BW_ALLOC && + priv->dcbx.tc_tsa[i] != IEEE_8021QAZ_TSA_ETS) + ets->tc_tx_bw[i] = 0; + } + return err; } From d612754a515cb1f46e159f13be1b3dcdf5d6a5bd Mon Sep 17 00:00:00 2001 From: Alexei Lazar Date: Fri, 17 Jul 2026 10:51:25 +0300 Subject: [PATCH 2533/3297] net/mlx5e: Reject unsupported CB Shaper TSA in ETS validation [ Upstream commit 9173e1d3c7c7d49a71eee813091f9e834ec7cee5 ] Credit Based (CB) TSA is not supported by the mlx5 driver, so reject any configurations that specify it. Fixes: 08fb1dacdd76 ("net/mlx5e: Support DCBNL IEEE ETS") Signed-off-by: Alexei Lazar Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260717075125.1244877-3-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index fbd84ab8d61e7..20db77552545e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -309,6 +309,14 @@ static int mlx5e_dbcnl_validate_ets(struct net_device *netdev, } } + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_CB_SHAPER) { + netdev_err(netdev, + "Failed to validate ETS: CB Shaper is not supported\n"); + return -EOPNOTSUPP; + } + } + /* Validate Bandwidth Sum */ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) { From e60e6009d3baed030b7622481b5067beb158453b Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Fri, 17 Jul 2026 14:13:49 +0530 Subject: [PATCH 2534/3297] octeontx2-pf: tc: fix egress ratelimiting [ Upstream commit bb0d96ebe5f4d1acccf4dc36ca7f01f9a8fa1ba1 ] The egress rate calculation computes an incorrect mantissa and exponent, causing up to ~50% deviation from the configured rate at lower speeds. Rework the computation to follow the hardware rate formula: rate = 2 * (1 + mantissa/256) * 2^exp / (1 << div_exp) Keep div_exp = 0 and derive exp and mantissa from half of the requested rate. Rates below 2 Mbps are floored to the smallest encodable step (exp = 0, mantissa = 0). Fixes: e638a83f167e ("octeontx2-pf: TC_MATCHALL egress ratelimiting offload") Signed-off-by: Hariprasad Kelam Signed-off-by: Nitin Shetty J Link: https://patch.msgid.link/20260717084349.2227796-1-nshettyj@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../ethernet/marvell/octeontx2/nic/otx2_tc.c | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 26a08d2cfbb1b..f4906c67d5eee 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -30,6 +30,7 @@ #define OTX2_UNSUPP_LSE_DEPTH GENMASK(6, 4) #define MCAST_INVALID_GRP (-1U) +#define RATE_MANTISSA_BITS 8 struct otx2_tc_flow_stats { u64 bytes; @@ -90,28 +91,30 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp, u32 *mantissa, u32 *div_exp) { - u64 tmp; - /* Rate calculation by hardware * * PIR_ADD = ((256 + mantissa) << exp) / 256 * rate = (2 * PIR_ADD) / ( 1 << div_exp) * The resultant rate is in Mbps. + * + * Use div_exp = 0 and compute exp/mantissa for maxrate / 2; the + * leading factor of two yields the full rate. Rates below 2 Mbps + * are floored to the smallest step (exp = 0, mantissa = 0). */ - /* 2Mbps to 100Gbps can be expressed with div_exp = 0. - * Setting this to '0' will ease the calculation of - * exponent and mantissa. - */ *div_exp = 0; - if (maxrate) { - *exp = ilog2(maxrate) ? ilog2(maxrate) - 1 : 0; - tmp = maxrate - rounddown_pow_of_two(maxrate); - if (maxrate < MAX_RATE_MANTISSA) - *mantissa = tmp * 2; - else - *mantissa = tmp / (1ULL << (*exp - 7)); + maxrate = maxrate / 2; + if (!maxrate) { + /* Rates below 2 Mbps map to the smallest step */ + *exp = 0; + *mantissa = 0; + } else { + *exp = ilog2(maxrate); + /* Clear MSB and derive fractional bits */ + maxrate &= ~BIT(*exp); + *mantissa = (maxrate << RATE_MANTISSA_BITS) >> *exp; + } } else { /* Instead of disabling rate limiting, set all values to max */ *exp = MAX_RATE_EXPONENT; From 5d54d603cf3e96b92c66501d758abbbcd4f406c4 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 17 Jul 2026 22:32:30 +0800 Subject: [PATCH 2535/3297] net: ipv6: fix dif and sdif mismatch in raw6_icmp_error [ Upstream commit 440e274da4d1b93c7df2cb0ce893c3009dd4db55 ] In raw6_icmp_error(), raw_v6_match() is called with inet6_iif(skb) passed to both the 'dif' and 'sdif' arguments. This is a copy-paste or typo error, as the last argument should represent the secondary interface index (sdif). This mismatch breaks ICMPv6 error handling for IPv6 raw sockets in VRF (Virtual Routing and Forwarding) environments. When a raw socket is bound to a VRF master device, raw_v6_match() fails to find a match because it is not given the correct sdif value, causing the socket to miss relevant ICMPv6 error notifications. Fix this by properly passing inet6_sdif(skb) as the last argument to raw_v6_match(). Fixes: 5108ab4bf446fa ("net: ipv6: add second dif to raw socket lookups") Signed-off-by: Li RongQing Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260717143230.1836-1-lirongqing@baidu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index c534848933ee6..6cb8e32a3061f 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -348,7 +348,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr, const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr, - inet6_iif(skb), inet6_iif(skb))) + inet6_iif(skb), inet6_sdif(skb))) continue; rawv6_err(sk, skb, type, code, inner_offset, info); } From 3a81345467674f5ca84cd04a9efe3ec8a8dbdbf8 Mon Sep 17 00:00:00 2001 From: Vincent Chen Date: Fri, 17 Jul 2026 11:53:23 -0700 Subject: [PATCH 2536/3297] ice: allow creating VFs when !CONFIG_ICE_SWITCHDEV [ Upstream commit 99d0f42b0e5c57e4c02070a908aaff082881293a ] Currently ice_eswitch_attach_vf() is called unconditionally in ice_start_vfs(), which causes VF creation to fail when CONFIG_ICE_SWITCHDEV is not defined. Fix this by adding switchdev mode checks at the call sites before calling ice_eswitch_attach_vf(), consistent with how ice_eswitch_attach_sf() is already handled in ice_devlink_port_new(). This is similar to commit aacca7a83b97 ("ice: allow creating VFs for !CONFIG_NET_SWITCHDEV") which fixed the same issue for the previous ice_eswitch_configure() API. Fixes: 415db8399d06 ("ice: make representor code generic") Signed-off-by: Vincent Chen Reviewed-by: Aleksandr Loktionov Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-2-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_eswitch.c | 3 --- drivers/net/ethernet/intel/ice/ice_sriov.c | 14 ++++++++------ drivers/net/ethernet/intel/ice/ice_vf_lib.c | 3 ++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index c30e27bbfe6e2..b069e6c514fb1 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -512,9 +512,6 @@ int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf) struct ice_repr *repr; int err; - if (!ice_is_eswitch_mode_switchdev(pf)) - return 0; - repr = ice_repr_create_vf(vf); if (IS_ERR(repr)) return PTR_ERR(repr); diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 843e82fd3bf93..6a0b724e46f9a 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -484,12 +484,14 @@ static int ice_start_vfs(struct ice_pf *pf) goto teardown; } - retval = ice_eswitch_attach_vf(pf, vf); - if (retval) { - dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d", - vf->vf_id, retval); - ice_vf_vsi_release(vf); - goto teardown; + if (ice_is_eswitch_mode_switchdev(pf)) { + retval = ice_eswitch_attach_vf(pf, vf); + if (retval) { + dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d", + vf->vf_id, retval); + ice_vf_vsi_release(vf); + goto teardown; + } } set_bit(ICE_VF_STATE_INIT, vf->vf_states); diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 4f86412a9c0c3..bab968b05540b 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -812,7 +812,8 @@ void ice_reset_all_vfs(struct ice_pf *pf) } ice_vf_post_vsi_rebuild(vf); - ice_eswitch_attach_vf(pf, vf); + if (ice_is_eswitch_mode_switchdev(pf)) + ice_eswitch_attach_vf(pf, vf); mutex_unlock(&vf->cfg_lock); } From a32604e8d9e2ca058e2605845f8d793cee641ce2 Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Fri, 17 Jul 2026 11:53:28 -0700 Subject: [PATCH 2537/3297] ice: fix LAG recipe to profile association [ Upstream commit d6da9b7d48599db078aea6144997a381f8d90d45 ] ice_init_lag() associates recipes to profiles, assuming that Link Aggregation-related profiles will always have profile ID lower than 70 (ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER). This value seems arbitrary and might not always be valid for some versions of DDP package, i.e. LAG profiles may have profile ID greater than 70. This would lead to misconfigured switch and LAG not working properly. Fix it by checking up to maximum profile ID. Fixes: 1e0f9881ef79 ("ice: Flesh out implementation of support for SRIOV on bonded interface") Signed-off-by: Marcin Szycik Reviewed-by: Michal Swiatkowski Reviewed-by: Aleksandr Loktionov Reviewed-by: Dave Ertman Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-7-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_lag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c index aebf8e08a297b..e8ab36d0f11d8 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.c +++ b/drivers/net/ethernet/intel/ice/ice_lag.c @@ -2624,7 +2624,7 @@ int ice_init_lag(struct ice_pf *pf) goto free_lport_res; /* associate recipes to profiles */ - for (n = 0; n < ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER; n++) { + for (n = 0; n < ICE_MAX_NUM_PROFILES; n++) { err = ice_aq_get_recipe_to_profile(&pf->hw, n, &recipe_bits, NULL); if (err) From 684d4d0bda95a3fb21b3e29ff0f668f657707b54 Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Fri, 17 Jul 2026 11:53:32 -0700 Subject: [PATCH 2538/3297] ice: prevent tstamp ring allocation for non-PF VSI types [ Upstream commit 144539bbfd3cea1ab0fb6f5216d6004c1f4f029b ] The pf->txtime_txqs bitmap tracks which Tx queues have ETF (Earliest TxTime First) offload enabled. This bitmap is indexed by queue number and is set by ice_offload_txtime(), which only operates on PF VSI queues. However, ice_is_txtime_ena() does not check the VSI type before consulting the bitmap. When ETF offload is enabled on PF Tx queue 0, bit 0 is set in pf->txtime_txqs. During a subsequent PCI reset rebuild, the CTRL VSI's Tx queue 0 is reconfigured and ice_is_txtime_ena() is called for that ring. Since it only checks pf->txtime_txqs by queue index without distinguishing VSI type, it finds bit 0 set and returns true, matching the PF VSI's ETF queue, not the CTRL VSI's. This causes ice_vsi_cfg_txq() to spuriously allocate a tstamp_ring for the CTRL VSI ring. Since CTRL VSI rings have no associated netdev, ice_clean_tx_ring() takes an early return at the !netdev check before reaching ice_free_tx_tstamp_ring(), leaking the allocation. Each PCI reset leaks one 64-byte tstamp_ring. Fix this by restricting ice_is_txtime_ena() to return true only for PF VSI rings, since txtime_txqs is only meaningful for PF VSI queues. Fixes: ccde82e90946 ("ice: add E830 Earliest TxTime First Offload support") Signed-off-by: Paul Greenwalt Reviewed-by: Przemek Kitszel Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-11-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 6886188043764..fc4eae0a00cd3 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -765,6 +765,9 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring) struct ice_vsi *vsi = ring->vsi; struct ice_pf *pf = vsi->back; + if (vsi->type != ICE_VSI_PF) + return false; + return test_bit(ring->q_index, pf->txtime_txqs); } From 1db34097998cc97b665ba1727cdfd4e6559cbd37 Mon Sep 17 00:00:00 2001 From: "Nikola Z. Ivanov" Date: Sun, 19 Jul 2026 13:57:59 +0300 Subject: [PATCH 2539/3297] ipv6: Change allocation flags to match rcu_read_lock section requirements [ Upstream commit 313a123e1fca8827bb463db1f4bb211309764563 ] Since the call to __ip6_del_rt_siblings has been converted under rcu read lock and it only has one call point we should no longer block or yield. Our stack trace from the syzbot reproducer looks as follows: __ip6_del_rt_siblings rtnl_notify (Here we pass gfp_any() -> GFP_KERNEL) nlmsg_notify nlmsg_multicast nlmsg_multicast_filtered netlink_broadcast_filtered (GFP_KERNEL passed from earlier) netlink_broadcast_filtered can yield if GFP_KERNEL is passed, which we do not want to happen. Fix this by changing the allocation flag of rtnl_notify. Also change the flag passed to nlmsg_new. Even though it is not related to the syzbot generated bug it still falls under the same requirements. Reported-by: syzbot+84d4a405ed798b40c96d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=84d4a405ed798b40c96d Fixes: bd11ff421d36 ("ipv6: Get rid of RTNL for SIOCDELRT and RTM_DELROUTE.") Signed-off-by: Nikola Z. Ivanov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260719105759.558050-1-zlatistiv@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/route.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ef66a3c86febb..a45747bfb31a0 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -4011,7 +4011,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) struct fib6_node *fn; /* prefer to send a single notification with all hops */ - skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); + skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC); if (skb) { u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; @@ -4067,7 +4067,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) if (skb) { rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, - info->nlh, gfp_any()); + info->nlh, GFP_ATOMIC); } return err; } From 16df2d154ec82e2f7e7585b4fa154751ba37729a Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Sun, 19 Jul 2026 17:03:57 -0400 Subject: [PATCH 2540/3297] rds: tcp: unregister sysctl before tearing down listen socket [ Upstream commit 167e54c703ccd4fa028feb568b0d1002020cff86 ] rds_tcp_exit_net() frees the per-netns RDS TCP listen socket via rds_tcp_kill_sock() before unregistering the per-netns sysctl table. Since rds_tcp_skbuf_handler() derives the netns from rtn->rds_tcp_listen_sock->sk, a concurrent sysctl write can race with netns teardown and dereference the freed socket/sk. KASAN reports the race as: BUG: KASAN: slab-use-after-free in rds_tcp_skbuf_handler+0x2aa/0x2e0 rds_tcp_skbuf_handler net/rds/tcp.c:721 proc_sys_call_handler fs/proc/proc_sysctl.c vfs_write fs/read_write.c __x64_sys_pwrite64 fs/read_write.c Fix this by unregistering the RDS TCP sysctl table before calling rds_tcp_kill_sock(). unregister_net_sysctl_table() prevents new sysctl handlers from starting and waits for in-flight handlers to finish, so the listen socket can then be released safely. The fix was tested against the linked reproducer. Fixes: 7f5611cbc487 ("rds: sysctl: rds_tcp_{rcv,snd}buf: avoid using current->nsproxy") Reported-by: AutonomousCodeSecurity@microsoft.com Link: https://lore.kernel.org/all/20260719203718.9680-1-blbllhy@gmail.com Reviewed-by: Allison Henderson Signed-off-by: Cen Zhang (Microsoft) Link: https://patch.msgid.link/20260719210357.10179-1-blbllhy@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/rds/tcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/rds/tcp.c b/net/rds/tcp.c index b66dfcc3efaa0..06a2d8d48bbac 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -637,13 +637,13 @@ static void __net_exit rds_tcp_exit_net(struct net *net) { struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid); - rds_tcp_kill_sock(net); - if (rtn->rds_tcp_sysctl) unregister_net_sysctl_table(rtn->rds_tcp_sysctl); if (net != &init_net) kfree(rtn->ctl_table); + + rds_tcp_kill_sock(net); } static struct pernet_operations rds_tcp_net_ops = { From 74ecebfbf1555605c5c512f6ce565806a459022d Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Mon, 20 Jul 2026 09:25:08 +0800 Subject: [PATCH 2541/3297] ptp: netc: explicitly clear TMR_OFF during initialization [ Upstream commit c3f2fc231a39e29fe9f0adc14a3ecc3c1260d3c5 ] The NETC timer does not support function level reset, so TMR_OFF_L/H registers are not cleared by pcie_flr(). If TMR_OFF was set to a non-zero value in a previous binding, it will persist across driver rebind and cause inaccurate PTP time. There is also a hardware issue: after a warm reset or soft reset, TMR_OFF_L/H registers appear to be cleared to zero, but the timer clock domain internally retains the stale value. When the timer is re-enabled, TMR_CUR_TIME continues to track the old offset until TMR_OFF is written explicitly. This can cause incorrect PTP timestamps and even PTP clock synchronization failures. Per the recommendation from the IP team, explicitly write 0 to TMR_OFF in netc_timer_init() to flush the internally cached value and ensure TMR_CUR_TIME follows the freshly initialized counter. Fixes: 87a201d59963 ("ptp: netc: add NETC V4 Timer PTP driver support") Signed-off-by: Clark Wang Signed-off-by: Wei Fang Reviewed-by: Vadim Fedorenko Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260720012508.23227-1-wei.fang@oss.nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/ptp/ptp_netc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c index 94e952ee69902..5e381c354d746 100644 --- a/drivers/ptp/ptp_netc.c +++ b/drivers/ptp/ptp_netc.c @@ -779,6 +779,7 @@ static void netc_timer_init(struct netc_timer *priv) netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); netc_timer_wr(priv, NETC_TMR_ECTRL, NETC_TMR_DEFAULT_ETTF_THR); + netc_timer_offset_write(priv, 0); ktime_get_real_ts64(&now); ns = timespec64_to_ns(&now); netc_timer_cnt_write(priv, ns); From 391a23c503856323300b26f7d2cf70048b48d1a5 Mon Sep 17 00:00:00 2001 From: Minhong He Date: Mon, 20 Jul 2026 15:25:18 +0800 Subject: [PATCH 2542/3297] mctp: check register_netdevice_notifier() error in mctp_device_init() [ Upstream commit d9a33cadc70a94c1582f65e6042e81027cd200c6 ] mctp_device_init() handles errors from rtnl_af_register() and rtnl_register_many(), but ignores the return value of register_netdevice_notifier(). If notifier registration fails, init can still return success while the module is only partially initialized. Check the notifier registration error and fail module init early. Fixes: 583be982d934 ("mctp: Add device handling and netlink interface") Signed-off-by: Minhong He Link: https://patch.msgid.link/20260720072518.112614-1-heminhong@kylinos.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/mctp/device.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mctp/device.c b/net/mctp/device.c index 04c5570bacff6..7861d83aaad26 100644 --- a/net/mctp/device.c +++ b/net/mctp/device.c @@ -536,7 +536,9 @@ int __init mctp_device_init(void) { int err; - register_netdevice_notifier(&mctp_dev_nb); + err = register_netdevice_notifier(&mctp_dev_nb); + if (err) + return err; err = rtnl_af_register(&mctp_af_ops); if (err) From 3a924139cf526b9b3524e01f2eaeb09faac8f564 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 20 Jul 2026 13:22:28 +0200 Subject: [PATCH 2543/3297] net: airoha: fix ETS channel derivation in airoha_tc_setup_qdisc_ets() [ Upstream commit 649ea07fc25a17aa51bff710baac1ab161022a7c ] Derive the hardware QoS channel from opt->parent instead of opt->handle in airoha_tc_setup_qdisc_ets(). The ETS qdisc handle is either user-specified or auto-allocated by qdisc_alloc_handle() and bears no relation to the HTB leaf classid that identifies the hardware channel. HTB derives the channel from TC_H_MIN(opt->classid), and ETS is always attached as a child of an HTB leaf, so its opt->parent matches that classid. Using opt->handle instead can cause two ETS qdiscs on different HTB leaves to collide on the same hardware channel, corrupting scheduler configuration and stats. Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support") Reviewed-by: Simon Horman Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260720-airoha-ets-handle-fix-v2-1-6f7129ddc06f@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 5efd03c2deea7..64ab34e37c36f 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2326,8 +2326,7 @@ static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, if (opt->parent == TC_H_ROOT) return -EINVAL; - channel = TC_H_MAJ(opt->handle) >> 16; - channel = channel % AIROHA_NUM_QOS_CHANNELS; + channel = TC_H_MIN(opt->parent) % AIROHA_NUM_QOS_CHANNELS; switch (opt->command) { case TC_ETS_REPLACE: From 786d690257ec7a0c839f8710456e444ce3f1348b Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Fri, 24 Jul 2026 18:38:56 +0800 Subject: [PATCH 2544/3297] bpf, sockmap: Fix cork use-after-free in tcp_bpf_sendmsg() [ Upstream commit 2d66a033864e27ab8d5e44cb36f31d9d2413bee4 ] tcp_bpf_sendmsg() keeps msg_tx across sk_stream_wait_memory(), which drops and reacquires the socket lock. Its error path tries to decide whether msg_tx names the local temporary message by comparing it with the current value of psock->cork. This comparison is unsafe when two threads send on the same socket: Thread A Thread B msg_tx = psock->cork sk_msg_alloc() fails sk_stream_wait_memory() releases the socket lock acquires the socket lock completes the cork psock->cork = NULL frees the cork reacquires the socket lock msg_tx != psock->cork sk_msg_free(msg_tx) The stale cork is therefore mistaken for the local temporary message and freed again. KASAN reported: BUG: KASAN: slab-use-after-free in sk_msg_free+0x49/0x50 Read of size 4 at addr ffff88810c908800 by task poc/90 Call Trace: sk_msg_free+0x49/0x50 tcp_bpf_sendmsg+0x14f5/0x1cc0 __sys_sendto+0x32c/0x3a0 __x64_sys_sendto+0xdb/0x1b0 Allocated by task 89: __kasan_kmalloc+0x8f/0xa0 tcp_bpf_sendmsg+0x16b3/0x1cc0 Freed by task 91: __kasan_slab_free+0x43/0x70 kfree+0x131/0x3c0 tcp_bpf_sendmsg+0xec3/0x1cc0 msg_tx can only name the stack-local tmp or the shared cork. Check for tmp directly so a changed psock->cork cannot turn a shared message into an apparent local one. Fixes: 604326b41a6f ("bpf, sockmap: convert to generic sk_msg interface") Signed-off-by: Chengfeng Ye Reviewed-by: Emil Tsalapatis Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/87fr18lmzo.fsf%40cloudflare.com/ Link: https://lore.kernel.org/netdev/20260719161630.2901208-1-nicoyip.dev%40gmail.com/ [v1] Link: https://patch.msgid.link/20260724103856.3399001-1-nicoyip.dev@gmail.com Signed-off-by: Eduard Zingerman Signed-off-by: Sasha Levin --- net/ipv4/tcp_bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 7a3f0b244c8fc..c27e797eff275 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -589,7 +589,7 @@ static int tcp_bpf_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) wait_for_memory: err = sk_stream_wait_memory(sk, &timeo); if (err) { - if (msg_tx && msg_tx != psock->cork) + if (msg_tx == &tmp) sk_msg_free(sk, msg_tx); goto out_err; } From aa8ad3e0d1fe9c80560751920f0fff7809f46c7e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 11:44:45 +0100 Subject: [PATCH 2545/3297] drm: renesas: rzg2l_mipi_dsi: Increase reset deassertion delay commit 7cbba8a8ba0219a267844d3116dbc77cecb4fcf8 upstream. The RZ/G2L hardware manual (Rev. 1.50, May 2025), Section 34.4.2.1, requires waiting at least 1 msec after deasserting the CMN_RSTB signal before the DSI-Tx module is ready. Increase the delay from 1 usec to 1 msec by replacing udelay(1) with fsleep(1000) for RZ/G2L SoCs. Fixes: 7a043f978ed1 ("drm: rcar-du: Add RZ/G2L DSI driver") Cc: stable@vger.kernel.org Reviewed-by: Tommaso Merciai Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260330104450.128512-3-biju.das.jz@bp.renesas.com Signed-off-by: Biju Das Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index cbd898a182b56..5d83540c1453a 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -272,7 +272,7 @@ static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi, if (ret < 0) return ret; - udelay(1); + fsleep(1000); return 0; } From 300a2d970a535d9be35884fbf06c63397496441a Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 11:44:44 +0100 Subject: [PATCH 2546/3297] drm: renesas: rzg2l_mipi_dsi: Move rzg2l_mipi_dsi_set_display_timing() commit 5bfa858d53bb252d7a012c2e0a97ae18182edfb1 upstream. The RZ/G2L hardware manual (Rev. 1.50, May 2025), Section 34.4.2.1, requires display timings to be set after the HS clock is started. Move rzg2l_mipi_dsi_set_display_timing() from rzg2l_mipi_dsi_atomic_pre_enable() to rzg2l_mipi_dsi_atomic_enable(), placing it after rzg2l_mipi_dsi_start_hs_clock(). Drop the unused ret variable from rzg2l_mipi_dsi_atomic_pre_enable(). Fixes: 5ce16c169a4c ("drm: renesas: rz-du: Add atomic_pre_enable") Fixes: 7a043f978ed1 ("drm: rcar-du: Add RZ/G2L DSI driver") Cc: stable@vger.kernel.org Reviewed-by: Tommaso Merciai Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260330104450.128512-2-biju.das.jz@bp.renesas.com Signed-off-by: Biju Das Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index 5d83540c1453a..04a24ff9dde15 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -604,29 +604,33 @@ static void rzg2l_mipi_dsi_atomic_pre_enable(struct drm_bridge *bridge, const struct drm_display_mode *mode; struct drm_connector *connector; struct drm_crtc *crtc; - int ret; connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode; - ret = rzg2l_mipi_dsi_startup(dsi, mode); - if (ret < 0) - return; - - rzg2l_mipi_dsi_set_display_timing(dsi, mode); + rzg2l_mipi_dsi_startup(dsi, mode); } static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge); + const struct drm_display_mode *mode; + struct drm_connector *connector; + struct drm_crtc *crtc; int ret; ret = rzg2l_mipi_dsi_start_hs_clock(dsi); if (ret < 0) goto err_stop; + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; + mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode; + + rzg2l_mipi_dsi_set_display_timing(dsi, mode); + ret = rzg2l_mipi_dsi_start_video(dsi); if (ret < 0) goto err_stop_clock; From 86ab4d93b3d478378db84f4a6738e33c19fc009e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 11 Mar 2026 11:14:44 +0200 Subject: [PATCH 2547/3297] drm/tidss: Fix missing drm_bridge_add() call commit 83084bb36847cbd2b13743f7333aaca12613a72b upstream. tidss encoder-bridge is not added with drm_bridge_add() call, which leads to: [drm] Missing drm_bridge_add() before attach Add the missing call, using devm_drm_bridge_add() variant to get the drm_bridge_remove() handled automatically. The commit marked with the Fixes tag (from v6.6) is the commit that added the encoder bridge without drm_bridge_add(). However, this fix is not directly applicable there as devm_drm_bridge_alloc() was not used to alloc the bridge, so using devm version for drm_bridge_add() wouldn't be safe. Instead, drm_bridge_add() and drm_bridge_remove() would be needed there, but that would require new plumbing code as we don't have a separate cleanup function in the tidss_encoder.c, not in the tidss_kms.c from which the encoder is created. Also, there has been no reported bugs caused by the missing drm_bridge_add(). The drm_bridge_add() initializes the bridge's hpd_mutex, but HPD is not used for the encoder bridge. drm_bridge_add() also adds the bridge to the global bridge_list, which is only used in of_drm_find_bridge(), and again that is not used for the encoder bridge. Thus, while the original commit is not right, there should be no bugs caused by it, and for the time being I'm not sending a patch for the stable kernels for the original commit. This fix applies on top of commit 66cdf05f8548 ("drm/tidss: encoder: convert to devm_drm_bridge_alloc()"), which changes the tidss_encoder.c to use the devm variant (added in v6.17). The warning print was added in v6.19, so applying this fix to v6.17+ gets rid of the warning for all kernel versions. Cc: stable@vger.kernel.org # v6.17+ Fixes: c932ced6b585 ("drm/tidss: Update encoder/bridge chain connect model") Acked-by: Maxime Ripard Link: https://patch.msgid.link/20260311-tidss-minor-fixes-v2-2-cb4479784458@ideasonboard.com Signed-off-by: Tomi Valkeinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/tidss/tidss_encoder.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c index 81a04f7677701..db467bbcdb776 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.c +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -106,6 +106,8 @@ int tidss_encoder_create(struct tidss_device *tidss, enc = &t_enc->encoder; enc->possible_crtcs = possible_crtcs; + devm_drm_bridge_add(tidss->dev, &t_enc->bridge); + /* Attaching first bridge to the encoder */ ret = drm_bridge_attach(enc, &t_enc->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); From 50cd8a7e98dd21c67a235934ebf3460560c3eb93 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Fri, 30 Jan 2026 23:35:42 +0300 Subject: [PATCH 2548/3297] drm/rockchip: cdn-dp: add missing check in cdn_dp_config_video() commit 46c31e1604d121221167cb09380de8c7d53290b9 upstream. The result of cdn_dp_reg_write() is checked everywhere (with the error being logged by the callers) except one place in cdn_dp_config_video(). Add the missing result check, bailing out early on error... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: 1a0f7ed3abe2 ("drm/rockchip: cdn-dp: add cdn DP support for rk3399") Signed-off-by: Sergey Shtylyov Cc: stable@vger.kernel.org Reviewed-by: Chaoyi Chen Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/adf6b313-f7db-4d8f-9000-8c65446ba041@auroraos.dev Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index 924fb1d3ece28..c0866ca105435 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -683,6 +683,8 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) val = div_u64(8 * (symbol + 1), bit_per_pix) - val; val += 2; ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val); + if (ret) + goto err_config_video; switch (video->color_depth) { case 6: From 6ab29a86835721566f0c26bd7bebcdcdcb0cb093 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Mon, 9 Feb 2026 11:31:23 +0800 Subject: [PATCH 2549/3297] drm/rockchip: analogix_dp: Add missing error check for platform_get_resource() commit 45895f4d4d5f222d07412f90664f88b059627859 upstream. Add missing error check for platform_get_resource() return value to prevent NULL pointer dereference when memory resource is not available. Fixes: 718b3bb9c0ab ("drm/rockchip: analogix_dp: Expand device data to support multiple edp display") Cc: stable@vger.kernel.org Signed-off-by: Chen Ni Reviewed-by: Dragan Simic Signed-off-by: Heiko Stuebner Link: https://patch.msgid.link/20260209033123.1089370-1-nichen@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index d30f0983a53a6..b905e28afa074 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -469,6 +469,8 @@ static int rockchip_dp_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; i = 0; while (dp_data[i].reg) { From 943fa73ea0efa335d9c1800fcfac47915de4ff89 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:36 +0100 Subject: [PATCH 2550/3297] drm/imagination: Count paired job fence as dependency in prepare_job() commit 9cd74f935306cd857f46686975c43383e1d95f94 upstream. The DRM scheduler's prepare_job() callback counts the remaining non-signaled native dependencies for a job, preventing job submission until those (plus job data and fence update) can fit in the job queue's CCCB. This means checking which dependencies can be waited upon in the firmware, i.e. whether they are backed by a UFO object, i.e. whether their drm_sched_fence::parent has been assigned to a pvr_queue_fence::base fence. That happens when the job owning the fence is submitted to the firmware. Paired geometry and fragment jobs are submitted at the same time, which means the dependency between them can't be checked this way before submission. Update job_count_remaining_native_deps() to take into account the dependency between paired jobs. This fixes cases where prepare_job() underestimated the space left in an almost full fragment CCCB, wrongly unblocking run_job(), which then returned early without writing the full sequence of commands to the CCCB. The above lead to kernel warnings such as the following and potentially job timeouts (depending on waiters on the missing commands): [ 375.702979] WARNING: drivers/gpu/drm/imagination/pvr_cccb.c:178 at pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr], CPU#1: kworker/u16:3/47 [ 375.703160] Modules linked in: [ 375.703571] CPU: 1 UID: 0 PID: 47 Comm: kworker/u16:3 Tainted: G W 7.0.0-rc2-g817eb6b11ad5 #40 PREEMPT [ 375.703613] Tainted: [W]=WARN [ 375.703627] Hardware name: Texas Instruments AM625 SK (DT) [ 375.703645] Workqueue: powervr-sched drm_sched_run_job_work [gpu_sched] [ 375.703741] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 375.703764] pc : pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] [ 375.703847] lr : pvr_queue_submit_job_to_cccb+0x578/0xa70 [powervr] [ 375.703921] sp : ffff800084a97650 [ 375.703934] x29: ffff800084a97740 x28: 0000000000000958 x27: ffff80008565d000 [ 375.703979] x26: 0000000000000030 x25: ffff800084a97680 x24: 0000000000001000 [ 375.704017] x23: ffff800084a97820 x22: 1ffff00010952ecc x21: 0000000000000008 [ 375.704056] x20: 00000000000006a8 x19: ffff00002ff7da88 x18: 0000000000000000 [ 375.704093] x17: 0000000020020000 x16: 0000000000020000 x15: 0000000000000000 [ 375.704132] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 375.704168] x11: 000000000000f2f2 x10: 00000000f3000000 x9 : 00000000f3f3f3f3 [ 375.704206] x8 : 00000000f2f2f200 x7 : ffff700010952ecc x6 : 0000000000000008 [ 375.704243] x5 : 0000000000000000 x4 : 1ffff00010acba00 x3 : 0000000000000000 [ 375.704279] x2 : 0000000000000007 x1 : 0000000000000fff x0 : 000000000000002f [ 375.704317] Call trace: [ 375.704331] pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] (P) [ 375.704411] pvr_queue_submit_job_to_cccb+0x578/0xa70 [powervr] [ 375.704487] pvr_queue_run_job+0x3a4/0x990 [powervr] [ 375.704562] drm_sched_run_job_work+0x580/0xd48 [gpu_sched] [ 375.704623] process_one_work+0x520/0x1288 [ 375.704658] worker_thread+0x3f0/0xb3c [ 375.704680] kthread+0x334/0x3d8 [ 375.704706] ret_from_fork+0x10/0x20 [ 375.704736] ---[ end trace 0000000000000000 ]--- Fixes: eaf01ee5ba28 ("drm/imagination: Implement job submission and scheduling") Cc: stable@vger.kernel.org Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-1-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/imagination/pvr_queue.c | 27 +++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index fc415dd0d7a73..8829c65747b6b 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -179,7 +179,7 @@ static const struct dma_fence_ops pvr_queue_job_fence_ops = { /** * to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is - * backed by a UFO. + * already backed by a UFO. * @f: The dma_fence to turn into a pvr_queue_fence. * * Return: @@ -356,6 +356,15 @@ static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count) pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len); } +static bool +is_paired_job_fence(struct dma_fence *fence, struct pvr_job *job) +{ + /* This assumes "fence" is one of "job"'s drm_sched_job::dependencies */ + return job->type == DRM_PVR_JOB_TYPE_FRAGMENT && + job->paired_job && + &job->paired_job->base.s_fence->scheduled == fence; +} + /** * job_count_remaining_native_deps() - Count the number of non-signaled native dependencies. * @job: Job to operate on. @@ -371,6 +380,17 @@ static unsigned long job_count_remaining_native_deps(struct pvr_job *job) xa_for_each(&job->base.dependencies, index, fence) { struct pvr_queue_fence *jfence; + if (is_paired_job_fence(fence, job)) { + /* + * A fence between paired jobs won't resolve to a pvr_queue_fence (i.e. + * be backed by a UFO) until the jobs have been submitted, together. + * The submitting code will insert a partial render fence command for this. + */ + WARN_ON(dma_fence_is_signaled(fence)); + remaining_count++; + continue; + } + jfence = to_pvr_queue_job_fence(fence); if (!jfence) continue; @@ -630,9 +650,8 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) if (!jfence) continue; - /* Skip the partial render fence, we will place it at the end. */ - if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job && - &job->paired_job->base.s_fence->scheduled == fence) + /* This fence will be placed last, as partial render fence. */ + if (is_paired_job_fence(fence, job)) continue; if (dma_fence_is_signaled(&jfence->base)) From c0384d6872f4dc2701960048a0be1a12a8d2dc6e Mon Sep 17 00:00:00 2001 From: Vitor Soares Date: Tue, 5 May 2026 14:47:05 +0100 Subject: [PATCH 2551/3297] drm/bridge: cdns-dsi: Replace deprecated UNIVERSAL_DEV_PM_OPS() commit 2d8b08844c0ecc6f2002fa68711e779aa18c8585 upstream. The deprecated UNIVERSAL_DEV_PM_OPS() macro uses the provided callbacks for both runtime PM and system sleep. This causes the DSI clocks to be disabled twice: once during runtime suspend and again during system suspend, resulting in a WARN message from the clock framework when attempting to disable already-disabled clocks. [ 84.384540] clk:231:5 already disabled [ 84.388314] WARNING: CPU: 2 PID: 531 at /drivers/clk/clk.c:1181 clk_core_disable+0xa4/0xac ... [ 84.579183] Call trace: [ 84.581624] clk_core_disable+0xa4/0xac [ 84.585457] clk_disable+0x30/0x4c [ 84.588857] cdns_dsi_suspend+0x20/0x58 [cdns_dsi] [ 84.593651] pm_generic_suspend+0x2c/0x44 [ 84.597661] ti_sci_pd_suspend+0xbc/0x15c [ 84.601670] dpm_run_callback+0x8c/0x14c [ 84.605588] __device_suspend+0x1a0/0x56c [ 84.609594] dpm_suspend+0x17c/0x21c [ 84.613165] dpm_suspend_start+0xa0/0xa8 [ 84.617083] suspend_devices_and_enter+0x12c/0x634 [ 84.621872] pm_suspend+0x1fc/0x368 To address this issue, replace UNIVERSAL_DEV_PM_OPS() with RUNTIME_PM_OPS(). Bridge and panel drivers should only deal with runtime PM, as the DRM framework manages system-wide power transitions through the bridge enable() and disable() hooks. Link: https://lore.kernel.org/all/fbde0659-78f3-46e4-98cf-d832f765a18b@ideasonboard.com/ Cc: stable@vger.kernel.org # 6.1.x Fixes: e19233955d9e ("drm/bridge: Add Cadence DSI driver") Reviewed-by: Tomi Valkeinen Signed-off-by: Vitor Soares Reviewed-by: Luca Ceresoli Link: https://patch.msgid.link/20260505134705.188661-2-ivitro@gmail.com Signed-off-by: Tomi Valkeinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c index 09b289f0fcbf5..de2a32112913e 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -1230,7 +1230,7 @@ static const struct mipi_dsi_host_ops cdns_dsi_ops = { .transfer = cdns_dsi_transfer, }; -static int __maybe_unused cdns_dsi_resume(struct device *dev) +static int cdns_dsi_resume(struct device *dev) { struct cdns_dsi *dsi = dev_get_drvdata(dev); @@ -1241,7 +1241,7 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev) return 0; } -static int __maybe_unused cdns_dsi_suspend(struct device *dev) +static int cdns_dsi_suspend(struct device *dev) { struct cdns_dsi *dsi = dev_get_drvdata(dev); @@ -1251,8 +1251,9 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev) return 0; } -static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume, - NULL); +static const struct dev_pm_ops cdns_dsi_pm_ops = { + RUNTIME_PM_OPS(cdns_dsi_suspend, cdns_dsi_resume, NULL) +}; static int cdns_dsi_drm_probe(struct platform_device *pdev) { @@ -1399,7 +1400,7 @@ static struct platform_driver cdns_dsi_platform_driver = { .driver = { .name = "cdns-dsi", .of_match_table = cdns_dsi_of_match, - .pm = &cdns_dsi_pm_ops, + .pm = pm_ptr(&cdns_dsi_pm_ops), }, }; module_platform_driver(cdns_dsi_platform_driver); From 533d9e2bede4aeefdc2a0561d7071cfede95958f Mon Sep 17 00:00:00 2001 From: Ashutosh Desai Date: Sun, 10 May 2026 20:17:33 +0000 Subject: [PATCH 2552/3297] drm/dp/mst: fix OOB reads in remote DPCD/I2C sideband reply parsers commit 1a8f537f5a1eeac941f262fe73078d6b08ba83c0 upstream. drm_dp_sideband_parse_remote_dpcd_read() reads num_bytes from the raw message and then unconditionally does: memcpy(bytes, &raw->msg[idx], num_bytes); without checking that idx + num_bytes <= raw->curlen. raw->msg[] is 256 bytes; if a malicious or misbehaving MST hub sets num_bytes larger than the remaining payload, the memcpy reads past the received data into whatever follows in raw->msg[]. drm_dp_sideband_parse_remote_i2c_read_ack() has the same flaw (noted with a /* TODO check */ comment since the code was introduced). Fix both functions by using a single combined check (idx + num_bytes > curlen) before each memcpy. Since num_bytes is u8, it is always >= 0, so this strictly subsumes the simpler idx > curlen form and no separate step is needed. Fixes: ad7f8a1f9ced ("drm/helper: add Displayport multi-stream helper (v0.6)") Cc: # v3.17+ Signed-off-by: Ashutosh Desai Reviewed-by: Lyude Paul [added missing fixes tag] Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260510201733.2882224-1-ashutoshdesai993@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index be749dcad3b58..be7c104e4a80e 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -871,7 +871,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx goto fail_len; repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; idx++; - if (idx > raw->curlen) + if (idx + repmsg->u.remote_dpcd_read_ack.num_bytes > raw->curlen) goto fail_len; memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes); @@ -907,7 +907,9 @@ static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg goto fail_len; repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx]; idx++; - /* TODO check */ + if (idx + repmsg->u.remote_i2c_read_ack.num_bytes > raw->curlen) + goto fail_len; + memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes); return true; fail_len: From 1e5827839ad0ceb0079d1560c321fa3656b54f21 Mon Sep 17 00:00:00 2001 From: Ashutosh Desai Date: Fri, 10 Apr 2026 04:19:01 +0000 Subject: [PATCH 2553/3297] drm/dp/mst: fix buffer overflows in sideband chunk accumulation commit 55bd5e685bda455b9b50c835f8c8442d52a344a3 upstream. drm_dp_sideband_append_payload() has three related bugs when processing device-provided sideband reply data: 1. Zero-length curchunk_len underflow: msg_len is a 6-bit field taken directly from the DP sideband header. If a device sends msg_len=0, curchunk_len is set to zero. The condition (curchunk_idx >= curchunk_len) is immediately true, and curchunk_len-1 wraps to 255 (u8 underflow). drm_dp_msg_data_crc4() reads 255 bytes from chunk[48], then memcpy() writes 255 bytes into msg[], both far out of bounds. 2. chunk[48] overflow: curchunk_len can reach 63 (6-bit field). chunk[] is only 48 bytes. Multi-iteration payload assembly appends 16-byte blocks until curchunk_idx reaches curchunk_len, writing up to 15 bytes past the end of chunk[] into msg[]. 3. msg[256] overflow: each chunk contributes (curchunk_len-1) bytes to msg[]. No check ensures curlen + (curchunk_len-1) stays within msg[256], so the memcpy can spill into adjacent struct fields. All three are reachable from any DP MST device that can forge sideband reply messages on a physical connection. Fixes: ad7f8a1f9ced ("drm/helper: add Displayport multi-stream helper (v0.6)") Cc: # v3.17+ Signed-off-by: Ashutosh Desai Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260410041901.2438960-1-ashutoshdesai993@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index be7c104e4a80e..72850730eb797 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -789,6 +789,12 @@ static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg, { u8 crc4; + /* curchunk_len must be >= 1 (min 1 CRC byte) and fit in chunk[] */ + if (!msg->curchunk_len || + msg->curchunk_len > ARRAY_SIZE(msg->chunk) || + msg->curchunk_idx + replybuflen > ARRAY_SIZE(msg->chunk)) + return false; + memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); msg->curchunk_idx += replybuflen; @@ -799,6 +805,9 @@ static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg, print_hex_dump(KERN_DEBUG, "wrong crc", DUMP_PREFIX_NONE, 16, 1, msg->chunk, msg->curchunk_len, false); + /* Guard against accumulated msg[] overflow */ + if (msg->curlen + msg->curchunk_len - 1 > ARRAY_SIZE(msg->msg)) + return false; /* copy chunk into bigger msg */ memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1); msg->curlen += msg->curchunk_len - 1; From e2c29d51c0f65459ae5bbf7ccc302df4c359c473 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 30 Mar 2026 08:56:37 +0100 Subject: [PATCH 2554/3297] drm/imagination: Fit paired fragment job in the correct CCCB commit 4baf9e70cb756d78dd56419f8baee2978a72d0c3 upstream. For geometry jobs with a paired fragment job, at the moment, the DRM scheduler's prepare_job() callback: - checks for internal (driver) dependencies for the geometry job; - calls into pvr_queue_get_paired_frag_job_dep() to check for external dependencies for the fragment job (the two jobs are submitted together but the common scheduler code doesn't know about it, so this needs to be done at this point in time); - calls into the prepare_job() callback again, but for the fragment job, to check its internal dependencies as well, passing the fragment job's drm_sched_job and the geometry job's drm_sched_entity / pvr_queue. The problem with the last step is that pvr_queue_prepare_job() doesn't always take the mismatched fragment job and geometry queue into account, in particular when checking whether there is space for the fragment command to be submitted, so the code ends up checking for space in the geometry (i.e. wrong) CCCB. The rest of the nested prepare_job() callback happens to work fine at the moment as the other internal dependencies are not relevant for a paired fragment job. Move the initialisation of a paired fragment job's done fence and CCCB fence to pvr_queue_get_paired_frag_job_dep(), inferring the correct queue from the fragment job itself. This fixes cases where prepare_job() wrongly assumed that there was enough space for a paired fragment job in its own CCCB, unblocking run_job(), which then returned early without writing the full sequence of commands to the CCCB. The above lead to kernel warnings such as the following and potentially job timeouts (depending on waiters on the missing commands): [ 552.421075] WARNING: drivers/gpu/drm/imagination/pvr_cccb.c:178 at pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr], CPU#2: kworker/u16:5/63 [ 552.421230] Modules linked in: [ 552.421592] CPU: 2 UID: 0 PID: 63 Comm: kworker/u16:5 Tainted: G W 7.0.0-rc2-gc5d053e4dccb #39 PREEMPT [ 552.421625] Tainted: [W]=WARN [ 552.421637] Hardware name: Texas Instruments AM625 SK (DT) [ 552.421655] Workqueue: powervr-sched drm_sched_run_job_work [gpu_sched] [ 552.421744] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 552.421766] pc : pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] [ 552.421850] lr : pvr_queue_submit_job_to_cccb+0x57c/0xa74 [powervr] [ 552.421923] sp : ffff800084c47650 [ 552.421936] x29: ffff800084c47740 x28: 0000000000000df8 x27: ffff800088a77000 [ 552.421979] x26: 0000000000000030 x25: ffff800084c47680 x24: 0000000000001000 [ 552.422017] x23: ffff800084c47820 x22: 1ffff00010988ecc x21: 0000000000000008 [ 552.422055] x20: 0000000000000208 x19: ffff000006ad5a88 x18: 0000000000000000 [ 552.422093] x17: 0000000020020000 x16: 0000000000020000 x15: 0000000000000000 [ 552.422130] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 552.422167] x11: 000000000000f2f2 x10: 00000000f3000000 x9 : 00000000f3f3f3f3 [ 552.422204] x8 : 00000000f2f2f200 x7 : ffff700010988ecc x6 : 0000000000000008 [ 552.422241] x5 : 0000000000000000 x4 : 1ffff0001114ee00 x3 : 0000000000000000 [ 552.422278] x2 : 0000000000000007 x1 : 0000000000000fff x0 : 000000000000002f [ 552.422316] Call trace: [ 552.422330] pvr_cccb_write_command_with_header+0x2c4/0x330 [powervr] (P) [ 552.422411] pvr_queue_submit_job_to_cccb+0x57c/0xa74 [powervr] [ 552.422486] pvr_queue_run_job+0x3a4/0x990 [powervr] [ 552.422562] drm_sched_run_job_work+0x580/0xd48 [gpu_sched] [ 552.422623] process_one_work+0x520/0x1288 [ 552.422657] worker_thread+0x3f0/0xb3c [ 552.422679] kthread+0x334/0x3d8 [ 552.422706] ret_from_fork+0x10/0x20 Fixes: eaf01ee5ba28 ("drm/imagination: Implement job submission and scheduling") Cc: stable@vger.kernel.org Signed-off-by: Alessio Belle Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260330-job-submission-fixes-cleanup-v1-2-7de8c09cef8c@imgtec.com Signed-off-by: Matt Coster Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/imagination/pvr_queue.c | 32 +++++++++++-------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 8829c65747b6b..7d0d09026b82c 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -488,10 +488,11 @@ pvr_queue_get_job_kccb_fence(struct pvr_queue *queue, struct pvr_job *job) } static struct dma_fence * -pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job) +pvr_queue_get_paired_frag_job_dep(struct pvr_job *job) { struct pvr_job *frag_job = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ? job->paired_job : NULL; + struct pvr_queue *frag_queue = frag_job ? frag_job->ctx->queues.fragment : NULL; struct dma_fence *f; unsigned long index; @@ -510,7 +511,10 @@ pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job) return dma_fence_get(f); } - return frag_job->base.sched->ops->prepare_job(&frag_job->base, &queue->entity); + /* Initialize the paired fragment job's done_fence, so we can signal it. */ + pvr_queue_job_fence_init(frag_job->done_fence, frag_queue); + + return pvr_queue_get_job_cccb_fence(frag_queue, frag_job); } /** @@ -529,11 +533,6 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job, struct pvr_queue *queue = container_of(s_entity, struct pvr_queue, entity); struct dma_fence *internal_dep = NULL; - /* - * Initialize the done_fence, so we can signal it. This must be done - * here because otherwise by the time of run_job() the job will end up - * in the pending list without a valid fence. - */ if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) { /* * This will be called on a paired fragment job after being @@ -543,18 +542,15 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job, */ if (job->paired_job->has_pm_ref) return NULL; - - /* - * In this case we need to use the job's own ctx to initialise - * the done_fence. The other steps are done in the ctx of the - * paired geometry job. - */ - pvr_queue_job_fence_init(job->done_fence, - job->ctx->queues.fragment); - } else { - pvr_queue_job_fence_init(job->done_fence, queue); } + /* + * Initialize the done_fence, so we can signal it. This must be done + * here because otherwise by the time of run_job() the job will end up + * in the pending list without a valid fence. + */ + pvr_queue_job_fence_init(job->done_fence, queue); + /* CCCB fence is used to make sure we have enough space in the CCCB to * submit our commands. */ @@ -575,7 +571,7 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job, /* The paired job fence should come last, when everything else is ready. */ if (!internal_dep) - internal_dep = pvr_queue_get_paired_frag_job_dep(queue, job); + internal_dep = pvr_queue_get_paired_frag_job_dep(job); return internal_dep; } From d5c70523cafa26ad2c7a37b612849abe2683baa8 Mon Sep 17 00:00:00 2001 From: Ashutosh Desai Date: Sun, 10 May 2026 20:31:28 +0000 Subject: [PATCH 2555/3297] drm/dp/mst: fix OOB reads on 2-byte fields in sideband reply parsers commit 6b89ba3dba2f583626fb693e47e951ffb8bf591f upstream. Three sideband reply parsers read 16-bit fields as: val = (raw->msg[idx] << 8) | (raw->msg[idx+1]); and check bounds only after the fact. When idx == raw->curlen, raw->msg[idx+1] reads one byte past the received message data into the following struct fields (curchunk_len, curchunk_idx, curlen). Affected functions: - drm_dp_sideband_parse_enum_path_resources_ack() full_payload_bw_number and avail_payload_bw_number fields - drm_dp_sideband_parse_allocate_payload_ack() allocated_pbn field - drm_dp_sideband_parse_query_payload_ack() allocated_pbn field Fix by using a single combined check (idx + 2 > curlen) before each 2-byte read. Since the check is strictly tighter than idx > curlen, no separate step is needed. Fixes: ad7f8a1f9ced ("drm/helper: add Displayport multi-stream helper (v0.6)") Cc: # v3.17+ Signed-off-by: Ashutosh Desai Reviewed-by: Lyude Paul [added fixes tag] Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260510203128.2884846-1-ashutoshdesai993@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 72850730eb797..56075dde04038 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -934,16 +934,13 @@ static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf; repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1; idx++; - if (idx > raw->curlen) + if (idx + 2 > raw->curlen) goto fail_len; repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); idx += 2; - if (idx > raw->curlen) + if (idx + 2 > raw->curlen) goto fail_len; repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); - idx += 2; - if (idx > raw->curlen) - goto fail_len; return true; fail_len: DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen); @@ -961,12 +958,9 @@ static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_ms goto fail_len; repmsg->u.allocate_payload.vcpi = raw->msg[idx]; idx++; - if (idx > raw->curlen) + if (idx + 2 > raw->curlen) goto fail_len; repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]); - idx += 2; - if (idx > raw->curlen) - goto fail_len; return true; fail_len: DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen); @@ -980,12 +974,9 @@ static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_r repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf; idx++; - if (idx > raw->curlen) + if (idx + 2 > raw->curlen) goto fail_len; repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]); - idx += 2; - if (idx > raw->curlen) - goto fail_len; return true; fail_len: DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen); From e28420e36542ae8b66a5bdcec419525f521bddf8 Mon Sep 17 00:00:00 2001 From: Asad Kamal Date: Fri, 5 Jun 2026 23:44:08 +0800 Subject: [PATCH 2556/3297] drm/amdgpu/gfx: fix cleaner shader IB buffer overflow commit 3e864bf2a32a1cbdf1e0f9c5a5a4176e8575f4a3 upstream. The cleaner shader sysfs path allocates a 16-dword (64 byte) IB but incorrectly fills (align_mask + 1) dwords. On GFX rings align_mask is 0xff, so the loop wrote 256 dwords into a 64-byte buffer, causing a kernel page fault. The IB only needs to be a minimal NOP shell to schedule the job; the cleaner shader itself is emitted on the ring via emit_cleaner_shader(). Fill 16 dwords to match the allocation. v2: Use ib_size_dw variable (Lijo) Fixes: d361ad5d2fc0 ("drm/amdgpu: Add sysfs interface for running cleaner shader") Suggested-by: Lijo Lazar Signed-off-by: Asad Kamal Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit bf21af331ebf72d0935fd70c73192414a422c03a) CC: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index ebe2b4c68b0f3..40e7482980692 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1456,12 +1456,13 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) struct amdgpu_device *adev = ring->adev; struct drm_gpu_scheduler *sched = &ring->sched; struct drm_sched_entity entity; + unsigned int ib_size_dw = 16; static atomic_t counter; struct dma_fence *f; struct amdgpu_job *job; struct amdgpu_ib *ib; void *owner; - int i, r; + int r; /* Initialize the scheduler entity */ r = drm_sched_entity_init(&entity, DRM_SCHED_PRIORITY_NORMAL, @@ -1479,7 +1480,7 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) owner = (void *)(unsigned long)atomic_inc_return(&counter); r = amdgpu_job_alloc_with_ib(ring->adev, &entity, owner, - 64, 0, &job, + ib_size_dw * sizeof(uint32_t), 0, &job, AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER); if (r) goto err; @@ -1489,9 +1490,8 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) job->run_cleaner_shader = true; ib = &job->ibs[0]; - for (i = 0; i <= ring->funcs->align_mask; ++i) - ib->ptr[i] = ring->funcs->nop; - ib->length_dw = ring->funcs->align_mask + 1; + memset32(ib->ptr, ring->funcs->nop, ib_size_dw); + ib->length_dw = ib_size_dw; f = amdgpu_job_submit(job); From b3a01cda0ae169cb1f128a16d13c478488c50c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:33:20 +0200 Subject: [PATCH 2557/3297] drm/amdgpu/uvd: Fix forcing MSG, FB BOs into VCPU segment when it isn't at 0 (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 32bd35f068a3507a1b3922cd12ea2985fc58c85b upstream. UVD 4.x and older can only access MSG, FEEDBACK buffers from a specific 256M VRAM segment that the VCPU BO is also located in. We already modify all placements of the given BO to ensure the BO is placed within this segment. Previously, it always assumed that the VCPU segment is the first 256M of VRAM, even though under some conditions the VCPU BO could be allocated outside this segment, which made UVD non-functional as the BOs were not inside the same segment as the UVD VCPU BO. Solve that by using the segment where the VCPU BO actually is. This fixes an issue with UVD failing to initialize on SI/CIK when resizable BAR is enabled and the VCPU BO is allocated in a different segment. v2: - For other BOs, keep using the same UVD segment as before. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/3851 Reviewed-by: Christian König Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit cbfd4d3fc2061a1ec8e9d36e65973ac3e813358a) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 5c38f0d30c87a..26f49690d2118 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -135,7 +135,7 @@ MODULE_FIRMWARE(FIRMWARE_VEGA12); MODULE_FIRMWARE(FIRMWARE_VEGA20); static void amdgpu_uvd_idle_work_handler(struct work_struct *work); -static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo); +static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *abo); static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev, uint32_t size, @@ -158,7 +158,7 @@ static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev, amdgpu_bo_kunmap(bo); amdgpu_bo_unpin(bo); amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_VRAM); - amdgpu_uvd_force_into_uvd_segment(bo); + amdgpu_uvd_force_into_vcpu_segment(bo); r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); if (r) goto err; @@ -544,6 +544,24 @@ void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp) } } +static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *bo) +{ + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + struct amdgpu_bo *vcpu_bo = adev->uvd.inst[0].vcpu_bo; + struct amdgpu_res_cursor vcpu_cur; + + amdgpu_res_first(vcpu_bo->tbo.resource, 0, + amdgpu_bo_size(vcpu_bo), &vcpu_cur); + + bo->placement.num_placement = 1; + bo->placement.placement = &bo->placements[0]; + bo->placements[0].fpfn = ALIGN_DOWN(vcpu_cur.start, SZ_256M) >> PAGE_SHIFT; + bo->placements[0].lpfn = bo->placements[0].fpfn + (SZ_256M >> PAGE_SHIFT); + bo->placements[0].mem_type = vcpu_bo->tbo.resource->mem_type; + if (bo->placements[0].mem_type == TTM_PL_VRAM) + bo->placements[0].flags |= TTM_PL_FLAG_CONTIGUOUS; +} + static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo) { int i; @@ -594,13 +612,10 @@ static int amdgpu_uvd_cs_pass1(struct amdgpu_uvd_cs_ctx *ctx) if (!ctx->parser->adev->uvd.address_64_bit) { /* check if it's a message or feedback command */ cmd = amdgpu_ib_get_value(ctx->ib, ctx->idx) >> 1; - if (cmd == 0x0 || cmd == 0x3) { - /* yes, force it into VRAM */ - uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM; - - amdgpu_bo_placement_from_domain(bo, domain); - } - amdgpu_uvd_force_into_uvd_segment(bo); + if (cmd == 0x0 || cmd == 0x3) + amdgpu_uvd_force_into_vcpu_segment(bo); + else + amdgpu_uvd_force_into_uvd_segment(bo); r = ttm_bo_validate(&bo->tbo, &bo->placement, &tctx); } From d068a2f53afc8310a1b9e8196287a59fb0adbf66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:33:19 +0200 Subject: [PATCH 2558/3297] drm/amdgpu/uvd: Place VCPU BO only in VRAM for UVD 4.x and older MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8002b744ad70055ef11ff7d0a7d685bfe8ffe6e4 upstream. These UVD versions don't fully support GPUVM and are only validated to work when their VCPU BO is placed in VRAM. Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 01b8dfc0660db5d6cdd62c22dc20f774a26ce853) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 26f49690d2118..c6804063b39d9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -188,6 +188,7 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) const struct common_firmware_header *hdr; unsigned int family_id; int i, j, r; + u32 vcpu_bo_domain; INIT_DELAYED_WORK(&adev->uvd.idle_work, amdgpu_uvd_idle_work_handler); @@ -319,12 +320,20 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) bo_size += AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8); + /* UVD 5.0 and newer HW can use 64 bit addressing. */ + adev->uvd.address_64_bit = + !amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0); + + vcpu_bo_domain = AMDGPU_GEM_DOMAIN_VRAM; + if (adev->uvd.address_64_bit) + vcpu_bo_domain |= AMDGPU_GEM_DOMAIN_GTT; + for (j = 0; j < adev->uvd.num_uvd_inst; j++) { if (adev->uvd.harvest_config & (1 << j)) continue; + r = amdgpu_bo_create_kernel(adev, bo_size, PAGE_SIZE, - AMDGPU_GEM_DOMAIN_VRAM | - AMDGPU_GEM_DOMAIN_GTT, + vcpu_bo_domain, &adev->uvd.inst[j].vcpu_bo, &adev->uvd.inst[j].gpu_addr, &adev->uvd.inst[j].cpu_addr); @@ -339,10 +348,6 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) adev->uvd.filp[i] = NULL; } - /* from uvd v5.0 HW addressing capacity increased to 64 bits */ - if (!amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0)) - adev->uvd.address_64_bit = true; - r = amdgpu_uvd_create_msg_bo_helper(adev, 128 << 10, &adev->uvd.ib_bo); if (r) return r; From ddba17b3dfa0efc80d6c98621c2fb7af66adb622 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 Jun 2026 21:11:53 -0500 Subject: [PATCH 2559/3297] drm/amdgpu: check amdgpu_vm_bo_find() result in GET_MAPPING_INFO commit 93475c34111916df71c63e510fc52db01351f809 upstream. The AMDGPU_GEM_OP_GET_MAPPING_INFO path of amdgpu_gem_op_ioctl() looks up the bo_va for the buffer object in the caller's VM via amdgpu_vm_bo_find(), but uses the returned pointer without checking it. amdgpu_vm_bo_find() returns NULL when the BO has no bo_va in that VM, which is the normal case for a BO that has never been mapped. The result is fed straight into amdgpu_vm_bo_va_for_each_valid_mapping(), which expands to list_for_each_entry(mapping, &(bo_va)->valids, list) and dereferences bo_va, causing a NULL pointer dereference. This is reachable by any process able to issue the ioctl (render group) simply by requesting mapping info for an unmapped BO. Return -ENOENT when no bo_va is found, jumping to out_exec so the drm_exec context and GEM object reference are released. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 528b19377affc1cc7362a70a254c1dda793595f9) 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 cd52ddd27c58b..25bb984fa89ef 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1075,6 +1075,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 (!bo_va) { + r = -ENOENT; + goto out_exec; + } + if (args->num_entries > INT_MAX / sizeof(*vm_entries)) { r = -EINVAL; goto out_exec; From 7daefc6d519524fbbe2ebc8cc635e6dfd0464e65 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:41:57 +0200 Subject: [PATCH 2560/3297] drm/sysfb: Do not page-align visible size of the framebuffer commit 134844856c399bfa9462a159dcf860bfdb748055 upstream. Only return the actually visible size of the system framebuffer in drm_sysfb_get_visible_size_si(). Drivers use this size value for reserving access to framebuffer memory. Increasing the value can make later attempts to do so fail. Signed-off-by: Thomas Zimmermann Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Reviewed-by: Javier Martinez Canillas Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Link: https://patch.msgid.link/20260618084327.46567-2-tzimmermann@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index 885864168c54f..ebcb28f2b3359 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -67,7 +67,7 @@ EXPORT_SYMBOL(drm_sysfb_get_stride_si); u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size) { - u64 vsize = PAGE_ALIGN(height * stride); + u64 vsize = height * stride; return drm_sysfb_get_validated_size0(dev, "visible size", vsize, size); } From f835eda74cf6565a4f2554152cb5e1a4d7c70a5a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:42:00 +0200 Subject: [PATCH 2561/3297] drm/sysfb: Avoid truncating maximum stride commit 9206b22fb959f4a9cf1921f34aed0df1dcb1ab04 upstream. Passing a maximum as 64-bit type to drm_sysfb_get_validated_int0() can truncate the value to 32 bits. Use drm_sysfb_get_validated_size0(), which uses 64-bit arithmetics. Then test the returned stride against the limits of int to avoid truncations in the returned value. A valid stride is in the range of [1, INT_MAX] inclusive. Signed-off-by: Thomas Zimmermann Reported-by: Sashiko Closes: https://lore.kernel.org/dri-devel/20260617114016.5A5991F000E9@smtp.kernel.org/ Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Reviewed-by: Javier Martinez Canillas Link: https://patch.msgid.link/20260618084327.46567-5-tzimmermann@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index ebcb28f2b3359..e9b5fa73567df 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -56,11 +56,17 @@ int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si unsigned int width, unsigned int height, u64 size) { u64 lfb_linelength = si->lfb_linelength; + s64 stride; if (!lfb_linelength) lfb_linelength = drm_format_info_min_pitch(format, 0, width); - return drm_sysfb_get_validated_int0(dev, "stride", lfb_linelength, div64_u64(size, height)); + stride = drm_sysfb_get_validated_size0(dev, "stride", lfb_linelength, + div64_u64(size, height)); + if (stride < INT_MIN || stride > INT_MAX) + return -EINVAL; + + return (int)stride; /* stride or negative errno code */ } EXPORT_SYMBOL(drm_sysfb_get_stride_si); From 9c2b01508831b7e74ed48e4bc6a4382298dd9b10 Mon Sep 17 00:00:00 2001 From: Jiqian Chen Date: Thu, 4 Jun 2026 18:30:23 +0800 Subject: [PATCH 2562/3297] drm/amdgpu/gfx9: Fix Ring and IB test fail after mode2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 85ed06d990ff73212b5a91a406671cabd962e521 upstream. For Renior APU with gfx9, in some test scenarios with disabling ring_reset, like accessing an unmapped invalid address, it can trigger a gpu job timeout event, then driver uses Mode2 reset to reset GPU, but after Mode2 compute Ring test and IB test fail randomly. It because the HQDs of MECs are always active before or after Mode2, that causes MECs use stale HQDs when MECs are unhalted before driver restore MQDs, and causes CPC and CPF are still stuck after Mode2, then causes compute Ring and IB tests fail. So, add sequences to deactivate HQDs of MECs in suspend IP function of the resetting process. v2: Move all sequences into a new function gfx_v9_0_cp_mode2_clear_state (Ray Huang) To check reset Mode2 method in the if condition (Ray Huang) v3: Move all sequences before Mode2 instead of after Mode2 (Timur Kristóf) v4: Call amdgpu_gfx_rlc_enter/exit_safe_mode int the begin and end of gfx_v9_0_deactivate_kcq_hqd (Alex Deucher) Signed-off-by: Jiqian Chen Reviewed-by: Huang Rui Reviewed-by: Timur Kristóf Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit c3988a7ad4799514447294f04f063b422e0551df) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index f589457f60187..2c1108848fe7b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -4045,6 +4045,41 @@ static int gfx_v9_0_hw_init(struct amdgpu_ip_block *ip_block) return r; } +static void gfx_v9_0_deactivate_kcq_hqd(struct amdgpu_device *adev) +{ + amdgpu_gfx_rlc_enter_safe_mode(adev, 0); + for (int i = 0; i < adev->gfx.num_compute_rings; i++) { + u32 tmp; + struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; + + mutex_lock(&adev->srbm_mutex); + soc15_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0, 0); + tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE); + /* disable the queue if it's active */ + if (tmp & CP_HQD_ACTIVE__ACTIVE_MASK) { + int j; + + WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 1); + for (j = 0; j < adev->usec_timeout; j++) { + tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE); + if (!(tmp & CP_HQD_ACTIVE__ACTIVE_MASK)) + break; + udelay(1); + } + if (j == AMDGPU_MAX_USEC_TIMEOUT) { + DRM_DEBUG("comp_%u_%u_%u dequeue request failed.\n", + ring->me, ring->pipe, ring->queue); + /* Manual disable if dequeue request times out */ + WREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE, 0); + } + WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 0); + } + soc15_grbm_select(adev, 0, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + } + amdgpu_gfx_rlc_exit_safe_mode(adev, 0); +} + static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -4071,6 +4106,10 @@ static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block) return 0; } + if ((adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev) && + amdgpu_asic_reset_method(adev) == AMD_RESET_METHOD_MODE2) + gfx_v9_0_deactivate_kcq_hqd(adev); + /* Use deinitialize sequence from CAIL when unbinding device from driver, * otherwise KIQ is hanging when binding back */ From 3fb10ec43c25a6b2e9b8335bd162ca7688e01e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 25 May 2026 13:33:18 +0200 Subject: [PATCH 2563/3297] drm/amdgpu: Fix amdgpu_bo_move() when old_mem and new_mem are both GTT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ee94a65f192c05c543b4d3ad7137cd696b5c18fc upstream. The UVD code relies on GTT to GTT moves in order to ensure that its BOs don't cross 256M segments. Fixes: bfe5e585b44f ("drm/ttm: move last binding into the drivers.") Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 21fd45e5e2628d00b478590bcc3d14d3de5d45b6) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index d629c5f73bf59..944e0feb674ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -505,6 +505,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, if (new_mem->mem_type == TTM_PL_TT || new_mem->mem_type == AMDGPU_PL_PREEMPT) { + if (old_mem && (old_mem->mem_type == TTM_PL_TT || + old_mem->mem_type == AMDGPU_PL_PREEMPT)) { + r = ttm_bo_wait_ctx(bo, ctx); + if (r) + return r; + + amdgpu_ttm_backend_unbind(bo->bdev, bo->ttm); + } + r = amdgpu_ttm_backend_bind(bo->bdev, bo->ttm, new_mem); if (r) return r; @@ -539,6 +548,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, ttm_bo_assign_mem(bo, new_mem); return 0; } + if ((old_mem->mem_type == TTM_PL_TT || + old_mem->mem_type == AMDGPU_PL_PREEMPT) && + (new_mem->mem_type == TTM_PL_TT || + new_mem->mem_type == AMDGPU_PL_PREEMPT)) { + amdgpu_bo_move_notify(bo, evict, new_mem); + ttm_resource_free(bo, &bo->resource); + ttm_bo_assign_mem(bo, new_mem); + return 0; + } if (old_mem->mem_type == AMDGPU_PL_GDS || old_mem->mem_type == AMDGPU_PL_GWS || From 315d2e5741a81b0be763e80413a2677e22b7e596 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 12 Jun 2026 21:07:24 -0500 Subject: [PATCH 2564/3297] drm/amdgpu: validate CP_GFX_SHADOW chunk size in CS pass1 commit 84c4c36acd5c4b2558b5069f869a165b2c655c84 upstream. Add a minimum-length check for the AMDGPU_CHUNK_ID_CP_GFX_SHADOW chunk in amdgpu_cs_pass1(), matching the gate already present for the IB, FENCE and BO_HANDLES chunk types. The CP_GFX_SHADOW case previously shared a bare break with the dependency and syncobj chunk types, which do not dereference a fixed-size struct. When userspace submits this chunk with length_dw == 0, vmemdup_array_user() is called with size 0 and returns ZERO_SIZE_PTR, which passes the IS_ERR() check. amdgpu_cs_p2_shadow() then dereferences chunk->kdata as a struct drm_amdgpu_cs_chunk_cp_gfx_shadow (reading shadow->flags), faulting on the ZERO_SIZE_PTR and causing a NULL-pointer dereference. This is reachable by an unprivileged process in the render group. Reject undersized chunks with -EINVAL during pass1 so the bad submission is rejected before pass2 ever dereferences the data. Fixes: ac9287055ff1 ("drm/amdgpu: add gfx shadow CS IOCTL support") Reviewed-by: Alex Deucher Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 7f61b2eef7415eccdb40850aca0de94211948657) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 8669e2fd6eff6..4b449b58d9073 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -260,13 +260,17 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, goto free_partial_kdata; break; + case AMDGPU_CHUNK_ID_CP_GFX_SHADOW: + if (size < sizeof(struct drm_amdgpu_cs_chunk_cp_gfx_shadow)) + goto free_partial_kdata; + break; + case AMDGPU_CHUNK_ID_DEPENDENCIES: case AMDGPU_CHUNK_ID_SYNCOBJ_IN: case AMDGPU_CHUNK_ID_SYNCOBJ_OUT: case AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES: case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT: case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL: - case AMDGPU_CHUNK_ID_CP_GFX_SHADOW: break; default: From 4e109faa9ea2b6c04cc5a99e76db3126575a59d1 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Wed, 10 Jun 2026 18:01:28 +0800 Subject: [PATCH 2565/3297] drm/nouveau: fix reversed error cleanup order in ucopy functions commit ab99ead646b1b833ecd57fe577a2816f2e848167 upstream. nouveau_uvmm_vm_bind_ucopy() and nouveau_exec_ucopy() place their error cleanup labels in allocation order rather than reverse allocation order. On a u_memcpya() failure for in_sync.s, the goto to err_free_ops (or err_free_pushs) frees the first allocation and then falls through to err_free_ins, which calls u_free() on args->in_sync.s. Since args->in_sync.s still holds the ERR_PTR returned by the failed u_memcpya(), and ERR_PTR values are not caught by ZERO_OR_NULL_PTR(), kvfree() proceeds to dereference it, which can result in a kernel oops. A failure for out_sync.s instead jumps to err_free_ins and skips freeing the first allocation, leading to a memory leak. Fix by swapping the cleanup label order so resources are freed in the correct reverse allocation sequence. Fixes: b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB7881484D91A6F80271415F71AF1A2@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Danilo Krummrich Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/nouveau/nouveau_exec.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_uvmm.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c index c4949e815eb36..6c31093be1c78 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.c +++ b/drivers/gpu/drm/nouveau/nouveau_exec.c @@ -331,10 +331,10 @@ nouveau_exec_ucopy(struct nouveau_exec_job_args *args, return 0; -err_free_pushs: - u_free(args->push.s); err_free_ins: u_free(args->in_sync.s); +err_free_pushs: + u_free(args->push.s); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index 79eefdfd08a26..fd518e3f1809d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -1711,10 +1711,10 @@ nouveau_uvmm_vm_bind_ucopy(struct nouveau_uvmm_bind_job_args *args, return 0; -err_free_ops: - u_free(args->op.s); err_free_ins: u_free(args->in_sync.s); +err_free_ops: + u_free(args->op.s); return ret; } From 154795885e8f0033c918c815aece543168bee670 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:41:58 +0200 Subject: [PATCH 2566/3297] drm/sysfb: Avoid possible truncation with calculating visible size commit b771974988ec7ce077a7246fa0fa588c246fe581 upstream. Calculating the visible size of the system framebuffer can result in truncation of the result. The calculation uses 32-bit arithmetics, which can overflow if the values for height and stride are large. Fix the issue by multiplying with mul_u32_u32(). Signed-off-by: Thomas Zimmermann Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Reported-by: Sashiko Closes: https://lore.kernel.org/dri-devel/20260617114027.1F2A71F000E9@smtp.kernel.org/ Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Reviewed-by: Javier Martinez Canillas Link: https://patch.msgid.link/20260618084327.46567-3-tzimmermann@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index e9b5fa73567df..959b6fe55d731 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -73,7 +74,7 @@ EXPORT_SYMBOL(drm_sysfb_get_stride_si); u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size) { - u64 vsize = height * stride; + u64 vsize = mul_u32_u32(height, stride); return drm_sysfb_get_validated_size0(dev, "visible size", vsize, size); } From 5452eb5c166301d0ae00c71c31a6bc89d786cf78 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 10:41:59 +0200 Subject: [PATCH 2567/3297] drm/sysfb: Return errno code from drm_sysfb_get_visible_size() commit 7bab0f09d753f098977bbba3955d694c2e2c25da upstream. Change the return type of drm_sysfb_get_visible_size() to s64 so that it returns a possible errno code from _get_validated_size0(). Fix callers to handle the errno code. The currently returned unsigned type converts an errno code to a very large size value, which drivers interpret as visible size of the system framebuffer. Later efforts to reserve the framebuffer resource fail. The bug has been present since efidrm and vesadrm got merged. It was then part of each driver. Signed-off-by: Thomas Zimmermann Fixes: 32ae90c66fb6 ("drm/sysfb: Add efidrm for EFI displays") Fixes: a84eb6abe2b6 ("drm/sysfb: Add vesadrm for VESA displays") Reviewed-by: Javier Martinez Canillas Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: dri-devel@lists.freedesktop.org Cc: # v6.16+ Link: https://patch.msgid.link/20260618084327.46567-4-tzimmermann@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/sysfb/drm_sysfb_helper.h | 2 +- drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 2 +- drivers/gpu/drm/sysfb/efidrm.c | 7 ++++--- drivers/gpu/drm/sysfb/vesadrm.c | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h index 265a1b8a93493..6caf87ab1f154 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -39,7 +39,7 @@ struct resource *drm_sysfb_get_memory_si(struct drm_device *dev, int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si, const struct drm_format_info *format, unsigned int width, unsigned int height, u64 size); -u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, +s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size); const struct drm_format_info *drm_sysfb_get_format_si(struct drm_device *dev, const struct drm_sysfb_format *formats, diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index 959b6fe55d731..a250bea7b19ca 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -71,7 +71,7 @@ int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si } EXPORT_SYMBOL(drm_sysfb_get_stride_si); -u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, +s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size) { u64 vsize = mul_u32_u32(height, stride); diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c index 97a3711e79337..844d1e8b0ab3c 100644 --- a/drivers/gpu/drm/sysfb/efidrm.c +++ b/drivers/gpu/drm/sysfb/efidrm.c @@ -143,7 +143,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, const struct screen_info *si; const struct drm_format_info *format; int width, height, stride; - u64 vsize, mem_flags; + s64 vsize; + u64 mem_flags; struct resource resbuf; struct resource *res; struct efidrm_device *efi; @@ -195,8 +196,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, if (stride < 0) return ERR_PTR(stride); vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res)); - if (!vsize) - return ERR_PTR(-EINVAL); + if (vsize < 0) + return ERR_PTR(vsize); drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n", &format->format, width, height, stride); diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c index 16a4b52d45c6c..37123976e1fbf 100644 --- a/drivers/gpu/drm/sysfb/vesadrm.c +++ b/drivers/gpu/drm/sysfb/vesadrm.c @@ -392,7 +392,7 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv, const struct screen_info *si; const struct drm_format_info *format; int width, height, stride; - u64 vsize; + s64 vsize; struct resource resbuf; struct resource *res; struct vesadrm_device *vesa; @@ -445,8 +445,8 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv, if (stride < 0) return ERR_PTR(stride); vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res)); - if (!vsize) - return ERR_PTR(-EINVAL); + if (vsize < 0) + return ERR_PTR(vsize); drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n", &format->format, width, height, stride); From 748d425e53c313d06c36c46e49e4fed5c7efc601 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 10 Jun 2026 17:15:49 +0300 Subject: [PATCH 2568/3297] drm/displayid: fix Tiled Display Topology ID size commit 90c0486a82e27393f9eaf3bb350f51a0bd38cb6b upstream. The Tiled Display Topology ID of a DisplayID Tiled Display Topology Data Block consists of three fields: - Tiled Display Manufacturer/Vendor ID Field (3 bytes) - Tiled Display Product ID Code Field (2 bytes) - Tiled Display Serial Number Field (4 bytes) i.e. a total of 9 bytes, not 8. The DisplayID Tiled Display Topology ID is used as the tile group identifier. Update both struct displayid_tiled_block topology_id member and struct drm_tile_group group_data member to full 9 bytes. The group data was missing the last byte of the serial number. I don't know whether there are known bug reports that might be linked to this, but it's plausible the last byte could be the differentiating part for the tile groups, and fewer tile groups might have been created than intended. Fixes: b49b55bd4fba ("drm/displayid: add displayid defines and edid extension (v2)") Fixes: 138f9ebb9755 ("drm: add tile_group support. (v3)") Cc: Dave Airlie Cc: stable@vger.kernel.org # v3.19+ Reviewed-by: Dave Airlie Link: https://patch.msgid.link/20260610141549.555605-1-jani.nikula@intel.com Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_connector.c | 12 ++++++------ drivers/gpu/drm/drm_displayid_internal.h | 2 +- include/drm/drm_connector.h | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 272d6254ea478..0326b9adb5f12 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -3554,7 +3554,7 @@ EXPORT_SYMBOL(drm_mode_put_tile_group); /** * drm_mode_get_tile_group - get a reference to an existing tile group * @dev: DRM device - * @topology: 8-bytes unique per monitor. + * @topology_id: 9-byte unique ID per monitor. * * Use the unique bytes to get a reference to an existing tile group. * @@ -3562,14 +3562,14 @@ EXPORT_SYMBOL(drm_mode_put_tile_group); * tile group or NULL if not found. */ struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - const char topology[8]) + const char topology_id[9]) { struct drm_tile_group *tg; int id; mutex_lock(&dev->mode_config.idr_mutex); idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { - if (!memcmp(tg->group_data, topology, 8)) { + if (!memcmp(tg->group_data, topology_id, sizeof(tg->group_data))) { if (!kref_get_unless_zero(&tg->refcount)) tg = NULL; mutex_unlock(&dev->mode_config.idr_mutex); @@ -3584,7 +3584,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group); /** * drm_mode_create_tile_group - create a tile group from a displayid description * @dev: DRM device - * @topology: 8-bytes unique per monitor. + * @topology_id: 9-byte unique ID per monitor. * * Create a tile group for the unique monitor, and get a unique * identifier for the tile group. @@ -3593,7 +3593,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group); * new tile group or NULL. */ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - const char topology[8]) + const char topology_id[9]) { struct drm_tile_group *tg; int ret; @@ -3603,7 +3603,7 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, return NULL; kref_init(&tg->refcount); - memcpy(tg->group_data, topology, 8); + memcpy(tg->group_data, topology_id, sizeof(tg->group_data)); tg->dev = dev; mutex_lock(&dev->mode_config.idr_mutex); diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h index 5b1b32f735166..4590d6a3d8215 100644 --- a/drivers/gpu/drm/drm_displayid_internal.h +++ b/drivers/gpu/drm/drm_displayid_internal.h @@ -109,7 +109,7 @@ struct displayid_tiled_block { u8 topo[3]; u8 tile_size[4]; u8 tile_pixel_bezel[5]; - u8 topology_id[8]; + u8 topology_id[9]; } __packed; struct displayid_detailed_timings_1 { diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8f34f4b8183d8..d5e6f4b832a77 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -2502,13 +2502,13 @@ struct drm_tile_group { struct kref refcount; struct drm_device *dev; int id; - u8 group_data[8]; + u8 group_data[9]; }; struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - const char topology[8]); + const char topology_id[9]); struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - const char topology[8]); + const char topology_id[9]); void drm_mode_put_tile_group(struct drm_device *dev, struct drm_tile_group *tg); From 45db277b2e1e34bcc99a0852026791108339ec3e Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Mon, 22 Jun 2026 16:25:39 +0300 Subject: [PATCH 2569/3297] drm/i915/gem: Add missing nospec on parallel submit slot commit 914a76a9f08366434bf595700f62026b7a19a9cc upstream. Add missing Spectre mitigation for userspace controlled parallel submission slot. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: e5e32171a2cf ("drm/i915/guc: Connect UAPI to GuC multi-lrc interface") Cc: Matthew Brost Cc: Tvrtko Ursulin Signed-off-by: Joonas Lahtinen Reviewed-by: Matthew Brost Reviewed-by: Tvrtko Ursulin Cc: # v5.16+ Link: https://patch.msgid.link/20260622132539.165558-1-joonas.lahtinen@linux.intel.com (cherry picked from commit 15b9353deff3cf72331c387780de3cf9c316b643) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/gem/i915_gem_context.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index ed6599694835f..e076344b8da9c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -612,6 +612,7 @@ set_proto_ctx_engines_parallel_submit(struct i915_user_extension __user *base, return -EINVAL; } + slot = array_index_nospec(slot, set->num_engines); if (set->engines[slot].type != I915_GEM_ENGINE_TYPE_INVALID) { drm_dbg(&i915->drm, "Invalid placement[%d], already occupied\n", slot); From fbb9effc81683e3ee75b41468f29f0e93b2954d8 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Sat, 6 Jun 2026 15:56:06 +0000 Subject: [PATCH 2570/3297] drm/nouveau/acr: fix missing nvkm_done() in error path of nvkm_acr_oneinit() commit c3027973f692077a1b66a9fb26d6a7c46c0dc72c upstream. In nvkm_acr_oneinit(), nvkm_kmap(acr->wpr) is invoked unconditionally at line 309 to obtain a mapping reference. Additionally, when both acr->wpr_fw and acr->wpr_comp are present, a second nvkm_kmap() is called inside the conditional block. Both mappings are expected to be released by nvkm_done(acr->wpr) at line 320 before the function returns successfully. However, when a mismatch is detected during the loop within the conditional block, the function returns -EINVAL at line 318 without calling nvkm_done(). This results in a leak of the kmap reference(s) acquired earlier. Fix the issue by invoking nvkm_done(acr->wpr) prior to the early return to ensure proper release of the mapping references. Fixes: 22dcda45a3d1 ("drm/nouveau/acr: implement new subdev to replace "secure boot"") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260606155606.77593-1-vulab@iscas.ac.cn Signed-off-by: Danilo Krummrich Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c index 13d829593180c..8b6274b945b0d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c @@ -315,6 +315,7 @@ nvkm_acr_oneinit(struct nvkm_subdev *subdev) i, us, fw); } } + nvkm_done(acr->wpr); return -EINVAL; } nvkm_done(acr->wpr); From 3e1f909556aa61b72523d0621e17fd1e1717d6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Ondra=C4=8Dka?= Date: Wed, 10 Jun 2026 10:32:45 +0200 Subject: [PATCH 2571/3297] drm/radeon: fix r100_copy_blit for large BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f896e86273dbbebb5eac966b4a201b5c62a02e9a upstream. r100_copy_blit() copies BOs as 1024-pixel-wide ARGB8888 blits, so one GPU page becomes one blit row. Large copies are split into chunks of at most 8191 rows. The kernel register header names the packet coordinate dwords SRC_Y_X and DST_Y_X. In the BITBLT_MULTI description in R5xx_Acceleration_v1.5.pdf docs, these correspond to [SRC_X1 | SRC_Y1] and [DST_X1 | DST_Y1], which are signed 13-bit coordinates in the -8192..8191 range. The old code kept SRC/DST_PITCH_OFFSET at the BO base and used SRC_Y_X/DST_Y_X as the chunk address, so large BO moves could exceed that coordinate range. Compute per-chunk SRC/DST_PITCH_OFFSET bases and emit zero source and destination coordinates. r100_copy_blit() already packs SRC/DST_PITCH_OFFSET as pitch plus base offset, so large chunk addresses belong there rather than in the coordinate fields. This fixes Prison Architect corruption with 4096x4096 mipped textures after they are evicted to GTT under memory pressure on RV530. Closes: https://gitlab.freedesktop.org/mesa/mesa/-/work_items/6716 Acked-by: Christian König Signed-off-by: Pavel Ondračka Signed-off-by: Alex Deucher (cherry picked from commit 87be26aee76239c6da03e599f238a426897f78ad) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/r100.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 07a9c523a17af..59ea0b94932da 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -906,6 +906,7 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, { struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; struct radeon_fence *fence; + uint64_t cur_src_offset, cur_dst_offset; uint32_t cur_pages; uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE; uint32_t pitch; @@ -934,6 +935,10 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, cur_pages = 8191; } num_gpu_pages -= cur_pages; + cur_src_offset = src_offset + + (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE; + cur_dst_offset = dst_offset + + (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE; /* pages are in Y direction - height page width in X direction - width */ @@ -950,13 +955,13 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, RADEON_DP_SRC_SOURCE_MEMORY | RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); - radeon_ring_write(ring, (pitch << 22) | (src_offset >> 10)); - radeon_ring_write(ring, (pitch << 22) | (dst_offset >> 10)); + radeon_ring_write(ring, (pitch << 22) | (cur_src_offset >> 10)); + radeon_ring_write(ring, (pitch << 22) | (cur_dst_offset >> 10)); radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); radeon_ring_write(ring, 0); radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); - radeon_ring_write(ring, num_gpu_pages); - radeon_ring_write(ring, num_gpu_pages); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); radeon_ring_write(ring, cur_pages | (stride_pixels << 16)); } radeon_ring_write(ring, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0)); From 90a8a938e0caecc9ee9dec3eb9eda92d66ba02a3 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 17 Jun 2026 06:51:01 -0700 Subject: [PATCH 2572/3297] drm/xe: Return error on non-migratable faults requiring devmem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 136fb61ba8571076dc5d49350a0e6d002d740b74 upstream. Non-migratable faults that require devmem incorrectly jump to the 'out' label, which squashes the error code intended to be returned to the upper layers. Fix this by returning -EACCES instead. Reported-by: Sashiko Fixes: 4208fac3dce5 ("drm/xe: Add more SVM GT stats") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Francois Dugast Link: https://patch.msgid.link/20260617135101.1245574-1-matthew.brost@intel.com (cherry picked from commit c4508edb2c723de93717272488ea65b165637eac) Signed-off-by: Thomas Hellström Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_svm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index 65452c20609ed..40015e091ed52 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -1074,10 +1074,8 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, xe_svm_range_fault_count_stats_incr(gt, range); - if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) { - err = -EACCES; - goto out; - } + if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) + return -EACCES; if (xe_svm_range_is_valid(range, tile, ctx.devmem_only)) { xe_svm_range_valid_fault_count_stats_incr(gt, range); From 038d0b80ab77884b63593ade2dff01079dbe0bd0 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 1 Jul 2026 18:24:34 -0700 Subject: [PATCH 2573/3297] drm/xe: Fix PTE index in xe_vm_populate_pgtable() for chunked binds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 34a4dd45cf210c04fee773b0dbc350aec285f03c upstream. xe_vm_populate_pgtable() indexed the source PTE array (update->pt_entries) by the per-call loop counter, assuming each call starts at the first entry of the update. That holds for the CPU bind path (xe_migrate_update_pgtables_cpu), which populates a whole update in a single call, but not for the GPU bind path: write_pgtable() splits an update into MAX_PTE_PER_SDI (510) sized MI_STORE_DATA_IMM chunks, invoking the populate callback once per chunk with an advancing qword_ofs but a fresh command- buffer destination pointer. As a result, every chunk after the first re-read pt_entries from index 0 instead of from its true offset, so PTEs beyond the first 510 entries of a single update were programmed with the wrong physical pages, shifting the mapping by exactly MAX_PTE_PER_SDI pages. This stayed latent because a single update only exceeds 510 qwords when a large (e.g. 2M) region is bound as individual 4K PTEs rather than a single huge-page entry, which happens when the backing store is sufficiently fragmented. It was surfaced by the BO defrag path, which deliberately rebinds such fragmented ranges via the GPU bind path, producing deterministic data corruption offset by 510 pages. Index pt_entries by the chunk's absolute offset relative to update->ofs so both the CPU and GPU paths pick the correct entries. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Assisted-by: GitHub_Copilot:claude-opus-4.8 Signed-off-by: Matthew Brost Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260702012434.3861171-1-matthew.brost@intel.com (cherry picked from commit e6f2d0b757c4fb577a513c577140109d1d292a9a) Signed-off-by: Thomas Hellström Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_pt.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 1f0262adc3ae3..de0d104fc8b66 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -999,12 +999,22 @@ xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *t u64 *ptr = data; u32 i; + /* + * @qword_ofs is the absolute entry offset within the page table, while + * @ptes is indexed relative to @update->ofs (its first entry). The GPU + * path (write_pgtable) splits a single update into MAX_PTE_PER_SDI-sized + * chunks, calling this with an advancing @qword_ofs but a fresh @data + * pointer per chunk, so translate back into a @ptes index rather than + * assuming the chunk starts at ptes[0]. + */ for (i = 0; i < num_qwords; i++) { + u32 idx = qword_ofs - update->ofs + i; + if (map) xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) * - sizeof(u64), u64, ptes[i].pte); + sizeof(u64), u64, ptes[idx].pte); else - ptr[i] = ptes[i].pte; + ptr[i] = ptes[idx].pte; } } From c1954c66662de477a8f4309335b775f7b07bd28b Mon Sep 17 00:00:00 2001 From: Nitin Gote Date: Sat, 11 Jul 2026 00:40:28 +0530 Subject: [PATCH 2574/3297] drm/xe: Hold a dma-buf reference for imported BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 62775525a27c3b0d56382e08ba81ee2d322058b6 upstream. An imported dma-buf BO is created as a ttm_bo_type_sg BO whose reservation object is the exporter's dma_buf->resv. The importer, however, only takes a dma-buf reference after a successful dma_buf_dynamic_attach(). Until then nothing keeps the exporter alive, so if the exporter is freed while the BO still references its resv, a later access to that resv is a use-after-free: Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c Workqueue: ttm ttm_bo_delayed_delete [ttm] RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0 This can be reached on two paths: - dma_buf_dynamic_attach() fails, or - ttm_bo_init_reserved() fails during BO creation. In both cases the BO already has bo->base.resv pointing at the exporter resv, and sg BOs are always torn down via ttm_bo_delayed_delete(), which locks bo->base.resv asynchronously - potentially after the exporter has been freed. Take the dma-buf reference in xe_bo_init_locked(), before ttm_bo_init_reserved(), so it also covers a creation failure there, and release it in xe_ttm_bo_destroy(). The reference is held for the whole BO lifetime, keeping the shared resv alive on every path. v2: - Reworked the fix to avoid creating the imported sg BO before dma_buf_dynamic_attach() succeeds. - Attach with importer_priv == NULL and make invalidate_mappings ignore incomplete imports. v3: - Dropped the xe-side reordering approach since importer_priv must be valid when dma_buf_dynamic_attach() publishes the attachment. - Per Christian's suggestion on the v1 thread, keyed the check on import_attach rather than removing the sg guard entirely. - Fixes both xe and amdgpu in a single TTM patch. v4: - Moved import_attach check to after dma_resv_copy_fences() so fences are copied before returning for successful imports (Thomas). - Removed exporter-alive claim from commit message (Thomas). v5: - Add drm/xe patch to keep imported sg BOs off the LRU before attach succeeds; the TTM fix alone is not sufficient for xe if the BO is already LRU-visible. (Thomas) v4 patch: https://patchwork.freedesktop.org/patch/736663/?series=169129&rev=2 - Patch 1 (drm/ttm) carries Christian's Reviewed-by from v4. v6: - Reworked the fix based on Thomas' suggestion. Instead of the TTM resv individualization (v1-v5) plus the xe off-LRU/placement handling (v5), just hold a dma-buf reference for the imported BO lifetime so the shared resv can never be freed while the BO still references it. Single xe patch, no TTM change. (Thomas) - Take the reference in xe_bo_init_locked() before ttm_bo_init_reserved() so a TTM creation failure is covered too (Thomas). - Dropped the v5 series (drm/ttm + drm/xe off-LRU); the off-LRU approach also regressed in CI BAT via ttm_bo_pipeline_gutting() creating a ghost BO that outlived the exporter. Link to v5: https://patchwork.freedesktop.org/series/169984/ v7: - Move changelog above --- so it stays in the commit message. - Reorder changelog entries oldest-to-newest. (Thomas) Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023 Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Cc: Thomas Hellstrom Cc: Christian Konig Cc: Matthew Auld Suggested-by: Thomas Hellstrom Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Reviewed-by: Thomas Hellström Signed-off-by: Nitin Gote Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260710191027.260160-2-nitin.r.gote@intel.com (cherry picked from commit 3516f3fae6be35642f8f06f8a218da6425c0306a) Signed-off-by: Thomas Hellström Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_bo.c | 24 ++++++++++++++++++++---- drivers/gpu/drm/xe/xe_bo.h | 3 ++- drivers/gpu/drm/xe/xe_bo_types.h | 2 ++ drivers/gpu/drm/xe/xe_dma_buf.c | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 1d46b61bf1285..5c531b8d92111 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -1186,7 +1186,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo) backup = xe_bo_init_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo), DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel, XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS | - XE_BO_FLAG_PINNED, &exec); + XE_BO_FLAG_PINNED, NULL, &exec); if (IS_ERR(backup)) { drm_exec_retry_on_contention(&exec); ret = PTR_ERR(backup); @@ -1327,7 +1327,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo) xe_bo_size(bo), DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel, XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS | - XE_BO_FLAG_PINNED, &exec); + XE_BO_FLAG_PINNED, NULL, &exec); if (IS_ERR(backup)) { drm_exec_retry_on_contention(&exec); ret = PTR_ERR(backup); @@ -1675,6 +1675,8 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo) if (bo->ttm.base.import_attach) drm_prime_gem_destroy(&bo->ttm.base, NULL); + if (bo->dma_buf) + dma_buf_put(bo->dma_buf); drm_gem_object_release(&bo->ttm.base); xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list)); @@ -2092,6 +2094,8 @@ void xe_bo_free(struct xe_bo *bo) * @cpu_caching: The cpu caching used for system memory backing store. * @type: The TTM buffer object type. * @flags: XE_BO_FLAG_ flags. + * @dma_buf: The dma-buf to reference for the BO lifetime (imported BOs), + * or NULL. * @exec: The drm_exec transaction to use for exhaustive eviction. * * Initialize or create an xe buffer object. On failure, any allocated buffer @@ -2103,7 +2107,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, struct xe_tile *tile, struct dma_resv *resv, struct ttm_lru_bulk_move *bulk, size_t size, u16 cpu_caching, enum ttm_bo_type type, - u32 flags, struct drm_exec *exec) + u32 flags, struct dma_buf *dma_buf, + struct drm_exec *exec) { struct ttm_operation_ctx ctx = { .interruptible = true, @@ -2189,6 +2194,17 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, placement = (type == ttm_bo_type_sg || bo->flags & XE_BO_FLAG_DEFER_BACKING) ? &sys_placement : &bo->placement; + + /* + * For imported BOs, keep the exporter dma-buf alive for the BO + * lifetime. Taken before ttm_bo_init_reserved() to also cover a + * creation failure there. Released in xe_ttm_bo_destroy(). + */ + if (dma_buf) { + get_dma_buf(dma_buf); + bo->dma_buf = dma_buf; + } + err = ttm_bo_init_reserved(&xe->ttm, &bo->ttm, type, placement, alignment, &ctx, NULL, resv, xe_ttm_bo_destroy); @@ -2303,7 +2319,7 @@ __xe_bo_create_locked(struct xe_device *xe, vm && !xe_vm_in_fault_mode(vm) && flags & XE_BO_FLAG_USER ? &vm->lru_bulk_move : NULL, size, - cpu_caching, type, flags, exec); + cpu_caching, type, flags, NULL, exec); if (IS_ERR(bo)) return bo; diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index a77af42b5f9ea..9c1ba37983d9d 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -93,7 +93,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, struct xe_tile *tile, struct dma_resv *resv, struct ttm_lru_bulk_move *bulk, size_t size, u16 cpu_caching, enum ttm_bo_type type, - u32 flags, struct drm_exec *exec); + u32 flags, struct dma_buf *dma_buf, + struct drm_exec *exec); struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile, struct xe_vm *vm, size_t size, enum ttm_bo_type type, u32 flags, diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h index d4fe3c8dca5be..88e3d8b33c532 100644 --- a/drivers/gpu/drm/xe/xe_bo_types.h +++ b/drivers/gpu/drm/xe/xe_bo_types.h @@ -35,6 +35,8 @@ struct xe_bo { struct xe_bo *backup_obj; /** @parent_obj: Ref to parent bo if this a backup_obj */ struct xe_bo *parent_obj; + /** @dma_buf: Imported dma-buf ref to keep its resv alive. */ + struct dma_buf *dma_buf; /** @flags: flags for this buffer object */ u32 flags; /** @vm: VM this BO is attached to, for extobj this will be NULL */ diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index eeea2a6e15b5c..07010f097e584 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -251,7 +251,7 @@ xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf) bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ - ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); + ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, dma_buf, &exec); drm_exec_retry_on_contention(&exec); if (IS_ERR(bo)) { ret = PTR_ERR(bo); From c88fdbf3da26e0179629530cae7768cd3d4ead85 Mon Sep 17 00:00:00 2001 From: Brajesh Gupta Date: Tue, 30 Jun 2026 21:10:07 +0530 Subject: [PATCH 2575/3297] drm/imagination: Fix double call to drm_sched_entity_fini() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4af24c27a39ba147a613a09e10b9e0f7294524c0 upstream. Call sequence of double call: pvr_context_destroy   pvr_context_kill_queues     pvr_queue_kill       drm_sched_entity_destroy         drm_sched_entity_fini // here   pvr_context_put     kref_put(..., pvr_context_release)       pvr_context_destroy_queues         pvr_queue_destroy           drm_sched_entity_fini // here Call to drm_sched_entity_destroy() from pvr_context_kill_queues() calls drm_sched_entity_flush() + drm_sched_entity_fini(). drm_sched_entity_flush() ensures all pending jobs are completed and drm_sched_entity_fini() ensures no further submission is allowed as per expectation from pvr_context_kill_queues(). Double call to drm_sched_entity_fini() is misuse of the API so keep call only in pvr_context_create() failure path. Stack trace for issue with addition of refcounting for DRM entity stats in commit fd177135f0e6 ("drm/sched: Account entity GPU time"): [ 789.490527] ------------[ cut here ]------------ [ 789.490559] refcount_t: underflow; use-after-free. [ 789.490657] WARNING: lib/refcount.c:28 at refcount_warn_saturate+0xf4/0x144, CPU#0: kworker/u16:1/440 [ 789.490695] Modules linked in: powervr drm_gpuvm drm_exec gpu_sched drm_shmem_helper xhci_plat_hcd xhci_hcd dwc3 usbcore usb_common snd_soc_simple_card snd_soc_simple_card_utils sa2ul sha512 sha256 dwc3_am62 sha1 authenc rti_wdt libsha512 at24 sch_fq_codel fuse dm_mod ipv6 [ 789.490798] CPU: 0 UID: 0 PID: 440 Comm: kworker/u16:1 Not tainted 7.0.0-rc7-02049-g5e2c0700091b #22 PREEMPT [ 789.490809] Hardware name: Texas Instruments AM625 SK (DT) [ 789.490815] Workqueue: powervr-sched pvr_queue_fence_release_work [powervr] [ 789.490868] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 789.490876] pc : refcount_warn_saturate+0xf4/0x144 [ 789.490884] lr : refcount_warn_saturate+0xf4/0x144 [ 789.490892] sp : ffff8000822cbcc0 [ 789.490895] x29: ffff8000822cbcc0 x28: 0000000000000000 x27: 0000000000000000 [ 789.490909] x26: 0000000000000000 x25: ffff800081b1e338 x24: ffff000004541405 [ 789.490922] x23: ffff000004bea950 x22: ffff00000042e400 x21: ffff000007123e30 [ 789.490935] x20: ffff000007123000 x19: ffff000007a80d50 x18: fffffffffffe7768 [ 789.490948] x17: 74736574202c6e6f x16: 697461746e656d65 x15: ffff800081b269f0 [ 789.490962] x14: 0000000000000030 x13: ffff800081b26a70 x12: 0000000000000211 [ 789.490975] x11: 00000000000000c0 x10: 0000000000000b50 x9 : ffff8000822cbb30 [ 789.490988] x8 : ffff0000014e7bb0 x7 : ffff00007725e780 x6 : 0000000372a05f49 [ 789.491001] x5 : 0000000000000000 x4 : 0000000000000001 x3 : 0000000000000010 [ 789.491013] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000014e7000 [ 789.491027] Call trace: [ 789.491032] refcount_warn_saturate+0xf4/0x144 (P) [ 789.491043] drm_sched_entity_fini+0x164/0x18c [gpu_sched] [ 789.491081] pvr_queue_destroy+0x64/0x134 [powervr] [ 789.491110] pvr_context_destroy_queues+0x34/0x64 [powervr] [ 789.491138] pvr_context_release+0x70/0xac [powervr] [ 789.491166] pvr_context_put.part.0+0x5c/0x7c [powervr] [ 789.491193] pvr_context_put+0x14/0x24 [powervr] [ 789.491221] pvr_queue_fence_release_work+0x20/0x38 [powervr] [ 789.491249] process_one_work+0x160/0x4c4 [ 789.491264] worker_thread+0x188/0x310 [ 789.491276] kthread+0x130/0x13c [ 789.491287] ret_from_fork+0x10/0x20 [ 789.491300] ---[ end trace 0000000000000000 ]--- Fixes: eaf01ee5ba28 ("drm/imagination: Implement job submission and scheduling") Cc: stable@vger.kernel.org Signed-off-by: Brajesh Gupta Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260630-b4-sched_fix-v7-1-71aa39c62627@imgtec.com Signed-off-by: Alessio Belle Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/imagination/pvr_context.c | 18 ++++++++++-------- drivers/gpu/drm/imagination/pvr_queue.c | 6 ++++-- drivers/gpu/drm/imagination/pvr_queue.h | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c index 5edc3c01af72b..2bae95e03d2d1 100644 --- a/drivers/gpu/drm/imagination/pvr_context.c +++ b/drivers/gpu/drm/imagination/pvr_context.c @@ -161,22 +161,24 @@ ctx_fw_data_init(void *cpu_ptr, void *priv) /** * pvr_context_destroy_queues() - Destroy all queues attached to a context. * @ctx: Context to destroy queues on. + * @cleanup_queue_entity: Whether to cleanup the queue entity e.g. context + * creation failure path. * * Should be called when the last reference to a context object is dropped. * It releases all resources attached to the queues bound to this context. */ -static void pvr_context_destroy_queues(struct pvr_context *ctx) +static void pvr_context_destroy_queues(struct pvr_context *ctx, bool cleanup_queue_entity) { switch (ctx->type) { case DRM_PVR_CTX_TYPE_RENDER: - pvr_queue_destroy(ctx->queues.fragment); - pvr_queue_destroy(ctx->queues.geometry); + pvr_queue_destroy(ctx->queues.fragment, cleanup_queue_entity); + pvr_queue_destroy(ctx->queues.geometry, cleanup_queue_entity); break; case DRM_PVR_CTX_TYPE_COMPUTE: - pvr_queue_destroy(ctx->queues.compute); + pvr_queue_destroy(ctx->queues.compute, cleanup_queue_entity); break; case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: - pvr_queue_destroy(ctx->queues.transfer); + pvr_queue_destroy(ctx->queues.transfer, cleanup_queue_entity); break; } } @@ -240,7 +242,7 @@ static int pvr_context_create_queues(struct pvr_context *ctx, return -EINVAL; err_destroy_queues: - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, true); return err; } @@ -356,7 +358,7 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co pvr_fw_object_destroy(ctx->fw_obj); err_destroy_queues: - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, true); err_free_ctx_data: kfree(ctx->data); @@ -382,7 +384,7 @@ pvr_context_release(struct kref *ref_count) spin_unlock(&pvr_dev->ctx_list_lock); xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id); - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, false); pvr_fw_object_destroy(ctx->fw_obj); kfree(ctx->data); pvr_vm_context_put(ctx->vm_ctx); diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 7d0d09026b82c..bb5835ede6660 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -1401,11 +1401,12 @@ void pvr_queue_kill(struct pvr_queue *queue) /** * pvr_queue_destroy() - Destroy a queue. * @queue: The queue to destroy. + * @cleanup_queue_entity: Whether to cleanup the queue entity. * * Cleanup the queue and free the resources attached to it. Should be * called from the context release function. */ -void pvr_queue_destroy(struct pvr_queue *queue) +void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity) { if (!queue) return; @@ -1415,7 +1416,8 @@ void pvr_queue_destroy(struct pvr_queue *queue) mutex_unlock(&queue->ctx->pvr_dev->queues.lock); drm_sched_fini(&queue->scheduler); - drm_sched_entity_fini(&queue->entity); + if (cleanup_queue_entity) + drm_sched_entity_fini(&queue->entity); if (WARN_ON(queue->last_queued_job_scheduled_fence)) dma_fence_put(queue->last_queued_job_scheduled_fence); diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h index fc1986d73fc88..a205e29437f4e 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.h +++ b/drivers/gpu/drm/imagination/pvr_queue.h @@ -158,7 +158,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx, void pvr_queue_kill(struct pvr_queue *queue); -void pvr_queue_destroy(struct pvr_queue *queue); +void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity); void pvr_queue_process(struct pvr_queue *queue); From b983a35dad3701399c692d7c6eb57d8b6ffc0929 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Wed, 1 Jul 2026 11:44:34 -0700 Subject: [PATCH 2576/3297] drm/imagination: Fix user array stride in pvr_set_uobj_array() commit 8dc8f3f4c2382fb7d1b1986ba8f33a2466cd3d7a upstream. pvr_set_uobj_array() copies an array of kernel objects to a userspace array whose element size is described by out->stride. When out->stride is different from the kernel object size, the slow path advances the userspace pointer by the kernel object size and the kernel pointer by the userspace stride. This reverses the intended layout. For larger userspace strides, later copies read from the wrong kernel addresses. For smaller userspace strides, later copies are written at the wrong userspace offsets. The padding clear is also done only for the first element instead of the padding area for each element. Advance the userspace pointer by out->stride and the kernel pointer by obj_size, and clear per-element padding while the current userspace pointer is still available. Fixes: f99f5f3ea7ef ("drm/imagination: Add GPU ID parsing and firmware loading") Cc: stable@vger.kernel.org # v6.8+ Reviewed-by: Alessio Belle Signed-off-by: Shuvam Pandey Link: https://patch.msgid.link/6a456012.eb165e5c.113c2a.b71d@mx.google.com Signed-off-by: Alessio Belle Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/imagination/pvr_drv.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index 916b40ced7eb0..e403411cf9639 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -1252,14 +1252,13 @@ pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_ if (copy_to_user(out_ptr, in_ptr, cpy_elem_size)) return -EFAULT; - out_ptr += obj_size; - in_ptr += out->stride; - } + if (out->stride > obj_size && + clear_user(out_ptr + cpy_elem_size, out->stride - obj_size)) { + return -EFAULT; + } - if (out->stride > obj_size && - clear_user(u64_to_user_ptr(out->array + obj_size), - out->stride - obj_size)) { - return -EFAULT; + out_ptr += out->stride; + in_ptr += obj_size; } } From c45fafa69fe3f79e319369cf665da89868e3ef98 Mon Sep 17 00:00:00 2001 From: Luigi Santivetti Date: Tue, 7 Jul 2026 16:17:16 +0100 Subject: [PATCH 2577/3297] drm/imagination: fix error checking of pvr_vm_context_lookup() commit cf385cf6e713eba0720651174dac0b2d2f5bb8f8 upstream. Since pvr_vm_context_lookup() returns either NULL or a pointer, then stop using IS_ERR() for checking the return value. Using IS_ERR() leads to the kernel oops reported below. It can be reproduced by passing an invalid VM context handle from userspace to the DRM_IOCTL_PVR_CREATE_CONTEXT ioctl. [ 92.733119] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000148 [ 92.742042] Mem abort info: [ 92.744890] ESR = 0x0000000096000004 [ 92.748686] EC = 0x25: DABT (current EL), IL = 32 bits [ 92.754020] SET = 0, FnV = 0 [ 92.757154] EA = 0, S1PTW = 0 [ 92.760337] FSC = 0x04: level 0 translation fault [ 92.765243] Data abort info: [ 92.768129] ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 [ 92.773626] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 [ 92.778763] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 92.784098] user pgtable: 4k pages, 48-bit VAs, pgdp=000000088ed23000 [ 92.790550] [0000000000000148] pgd=0000000000000000, p4d=0000000000000000 [ 92.797381] Internal error: Oops: 0000000096000004 [#1] SMP [ 92.803027] Modules linked in: powervr [ 92.852533] CPU: 0 UID: 0 PID: 409 Comm: triangle Not tainted 7.1.0-rc5-g98b46e693b91 #1 PREEMPT [ 92.861385] Hardware name: Texas Instruments AM68 SK (DT) [ 92.866766] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 92.873709] pc : pvr_vm_get_fw_mem_context+0x0/0xc [powervr] [ 92.879376] lr : pvr_queue_create+0x26c/0x440 [powervr] [ 92.884595] sp : ffff8000837fbb00 [ 92.887895] x29: ffff8000837fbb60 x28: 0000000000000000 x27: ffff8000837fbce8 [ 92.895015] x26: ffff000807f61a40 x25: ffff000807f61a00 x24: ffff000807f64400 [ 92.902135] x23: ffff00080a5ab000 x22: ffff800079b24730 x21: ffff000807f61800 [ 92.909254] x20: ffff00080999e680 x19: 0000000000000000 x18: 0000000000000000 [ 92.916373] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000001 [ 92.923492] x14: 0000000000000000 x13: 0000000000000002 x12: ffff80008145b298 [ 92.930611] x11: ffff8000844e5000 x10: ffff80008165a130 x9 : 0000000000000100 [ 92.937730] x8 : 0000000000000001 x7 : ffff0008076b27e0 x6 : ffff00080ec43b7c [ 92.944850] x5 : ffff00080ec43b78 x4 : 0000000000000000 x3 : ffff00080999e680 [ 92.951968] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000 [ 92.959088] Call trace: [ 92.961521] pvr_vm_get_fw_mem_context+0x0/0xc [powervr] (P) [ 92.967173] pvr_context_create+0x190/0x410 [powervr] [ 92.972218] pvr_ioctl_create_context+0x44/0x8c [powervr] [ 92.977608] drm_ioctl_kernel+0xbc/0x124 [drm] [ 92.982127] drm_ioctl+0x1f8/0x4dc [drm] [ 92.986098] __arm64_sys_ioctl+0xac/0x104 [ 92.990102] invoke_syscall+0x54/0x10c [ 92.993842] el0_svc_common.constprop.0+0x40/0xe0 [ 92.998532] do_el0_svc+0x1c/0x28 [ 93.001835] el0_svc+0x38/0x11c [ 93.004969] el0t_64_sync_handler+0xa0/0xe4 [ 93.009139] el0t_64_sync+0x198/0x19c [ 93.012792] Code: aa1703e0 d2800014 95cb0ba4 17ffffe8 (f940a400) [ 93.018869] ---[ end trace 0000000000000000 ]--- Fixes: d2d79d29bb98 ("drm/imagination: Implement context creation/destruction ioctls") Cc: stable@vger.kernel.org Signed-off-by: Luigi Santivetti Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260707-staging-ddkopsrc-2435-v1-1-24e160d44476@imgtec.com Signed-off-by: Alessio Belle Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/imagination/pvr_context.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c index 2bae95e03d2d1..d9066a4fefcf3 100644 --- a/drivers/gpu/drm/imagination/pvr_context.c +++ b/drivers/gpu/drm/imagination/pvr_context.c @@ -309,8 +309,8 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co goto err_free_ctx; ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); - if (IS_ERR(ctx->vm_ctx)) { - err = PTR_ERR(ctx->vm_ctx); + if (!ctx->vm_ctx) { + err = -EINVAL; goto err_free_ctx; } From 6253bb56bb2ebdf317d8b599ce737a2510cc2e17 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 14 Jul 2026 15:36:41 +0800 Subject: [PATCH 2578/3297] drm/imagination: acquire vm_ctx->lock before mapping memory to GPU VM commit 17e2030f37600994440f875dc410615d5c66ee6d upstream. The drm gpuvm code doesn't protect find operation against map operation, and the driver needs to ensure a map operation shouldn't happen when a find operation is in progress. In some cases a find operation will be in progress when doing map/unmap operations, and the find operation will do a NULL pointer dereference. An example of the stack trace of such NULL dereference is shown below: ``` Unable to handle kernel access to user memory without uaccess routines at virtual address 0000000000000010 [] drm_gpuva_find+0x28/0x6c [drm_gpuvm] [] pvr_vm_unmap+0x34/0x68 [powervr] [] pvr_ioctl_vm_unmap+0x2e/0x50 [powervr] [] drm_ioctl_kernel+0x8e/0xdc [] drm_ioctl+0x1be/0x3e0 [] __riscv_sys_ioctl+0xba/0xc4 [] do_trap_ecall_u+0x23e/0x3f4 [] handle_exception+0x168/0x174 ``` As all occurences of drm_gpuva_find*() are already guarded by vm_ctx->lock, make pvr_vm_map() to acquire this lock to prevent disturbing any find operation. This fixes the NULL deference problem in drm_gpuva_find*(). Cc: stable@vger.kernel.org Fixes: ff5f643de0bf ("drm/imagination: Add GEM and VM related code") Fixes: 4bc736f890ce ("drm/imagination: vm: make use of GPUVM's drm_exec helper") Signed-off-by: Icenowy Zheng Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260714073641.1935075-1-zhengxingda@iscas.ac.cn Signed-off-by: Alessio Belle Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/imagination/pvr_vm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c index 3d97990170bf6..b2f8e89dafb70 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -746,6 +746,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, pvr_gem_object_get(pvr_obj); + mutex_lock(&vm_ctx->lock); err = drm_gpuvm_exec_lock(&vm_exec); if (err) goto err_cleanup; @@ -755,6 +756,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, drm_gpuvm_exec_unlock(&vm_exec); err_cleanup: + mutex_unlock(&vm_ctx->lock); pvr_vm_bind_op_fini(&bind_op); return err; From 14a631dca9df00fdbe0e8f1b0a1e4ee07baa3e31 Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 25 Jun 2026 10:09:13 -0400 Subject: [PATCH 2579/3297] drm/amdkfd: Use kvcalloc to allocate arrays commit 9c8b85f95c1d4736b967e17b8eb4a463c055bea3 upstream. There were a few instances in kfd_chardev.c of kvzalloc being used to allocate memory for an array. Switch those to kvcalloc, which - is the standard way of allocating a zero-initialized array - does a check for the mul overflowing Signed-off-by: David Francis Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 60b048c93f7a3add39757ad65fe2bb6e58eeae23) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index c2100fd78b522..c4fd83ae17620 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1790,13 +1790,13 @@ static int criu_checkpoint_devices(struct kfd_process *p, struct kfd_criu_device_bucket *device_buckets = NULL; int ret = 0, i; - device_buckets = kvzalloc(num_devices * sizeof(*device_buckets), GFP_KERNEL); + device_buckets = kvcalloc(num_devices, sizeof(*device_buckets), GFP_KERNEL); if (!device_buckets) { ret = -ENOMEM; goto exit; } - device_priv = kvzalloc(num_devices * sizeof(*device_priv), GFP_KERNEL); + device_priv = kvcalloc(num_devices, sizeof(*device_priv), GFP_KERNEL); if (!device_priv) { ret = -ENOMEM; goto exit; @@ -1916,17 +1916,17 @@ static int criu_checkpoint_bos(struct kfd_process *p, int ret = 0, pdd_index, bo_index = 0, id; void *mem; - bo_buckets = kvzalloc(num_bos * sizeof(*bo_buckets), GFP_KERNEL); + bo_buckets = kvcalloc(num_bos, sizeof(*bo_buckets), GFP_KERNEL); if (!bo_buckets) return -ENOMEM; - bo_privs = kvzalloc(num_bos * sizeof(*bo_privs), GFP_KERNEL); + bo_privs = kvcalloc(num_bos, sizeof(*bo_privs), GFP_KERNEL); if (!bo_privs) { ret = -ENOMEM; goto exit; } - files = kvzalloc(num_bos * sizeof(struct file *), GFP_KERNEL); + files = kvcalloc(num_bos, sizeof(struct file *), GFP_KERNEL); if (!files) { ret = -ENOMEM; goto exit; @@ -2463,7 +2463,7 @@ static int criu_restore_bos(struct kfd_process *p, if (!bo_buckets) return -ENOMEM; - files = kvzalloc(args->num_bos * sizeof(struct file *), GFP_KERNEL); + files = kvcalloc(args->num_bos, sizeof(struct file *), GFP_KERNEL); if (!files) { ret = -ENOMEM; goto exit; From 50319efb865f72db45f191c8709511746d58ee0a Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 21 May 2026 09:18:59 -0400 Subject: [PATCH 2580/3297] drm/amdkfd: Check bounds in allocate_event_notification_slot commit bb52249fbbe948875155ccd45cd8d74bf4ae747b upstream. The valid event ids go from 0 to KFD_SIGNAL_EVENT_LIMIT allocate_event_notification_slot has an option to specify an event id to allocate at, used by CRIU. We weren't checking the bounds on that value. Check them. v2: Lower bounds check is unecessary because of idr_alloc already rejecting negative numbers. Upper bounds check should be KFD_SIGNAL_EVENT_LIMIT since the signal mode mappings might not yet exist Signed-off-by: David Francis Reviewed-by: David Yat Sin Signed-off-by: Alex Deucher (cherry picked from commit 6853f1f6cbbeb3f53ebbbd7286536aeb2c5d5f50) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 63039035b1941..2fdee34ae5432 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -107,6 +107,9 @@ static int allocate_event_notification_slot(struct kfd_process *p, } if (restore_id) { + if (*restore_id >= KFD_SIGNAL_EVENT_LIMIT) + return -EINVAL; + id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1, GFP_KERNEL); } else { From fd1691ec62701c982ea32e749678988c67fd4c21 Mon Sep 17 00:00:00 2001 From: David Francis Date: Mon, 6 Jul 2026 10:19:04 -0400 Subject: [PATCH 2581/3297] drm/amdkfd: Check bounds on CRIU restore queue type and mqd size commit 47ea05f246bebc81c7796f56265cffd812cf0601 upstream. We weren't checking whether the values provided in the private data in kfd CRIU restore were within bounds. For queue type, add a KFD_QUEUE_TYPE_MAX and ensure the provided type is less than it. For mqd_size, add new function mqd_size_from_queue_type and confirm that the provided mqd_size matches expectations. Reviewed-by: David Yat Sin Signed-off-by: David Francis Signed-off-by: Alex Deucher (cherry picked from commit f19d8086f6644083c913d70bfdeee20e1b6f46a5) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../drm/amd/amdkfd/kfd_device_queue_manager.c | 6 +++++ .../drm/amd/amdkfd/kfd_device_queue_manager.h | 2 ++ drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 3 ++- .../amd/amdkfd/kfd_process_queue_manager.c | 24 +++++++++++++------ 4 files changed, 27 insertions(+), 8 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 18d03f33c6202..c650ffc3e66e8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3628,6 +3628,12 @@ bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm, dqm_unlock(dqm); return r; } + +size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, enum kfd_queue_type type) +{ + return dqm->mqd_mgrs[get_mqd_type_from_queue_type(type)]->mqd_size; +} + #if defined(CONFIG_DEBUG_FS) static void seq_reg_dump(struct seq_file *m, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index 74a61b5b2f0b4..46c6849df13f4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -327,6 +327,8 @@ int debug_refresh_runlist(struct device_queue_manager *dqm); bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm, struct qcm_process_device *qpd, int doorbell_off, u32 *queue_format); +size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, + enum kfd_queue_type type); static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 24e85440473a1..d7e9150402428 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -436,7 +436,8 @@ enum kfd_queue_type { KFD_QUEUE_TYPE_HIQ, KFD_QUEUE_TYPE_DIQ, KFD_QUEUE_TYPE_SDMA_XGMI, - KFD_QUEUE_TYPE_SDMA_BY_ENG_ID + KFD_QUEUE_TYPE_SDMA_BY_ENG_ID, + KFD_QUEUE_TYPE_MAX, }; enum kfd_queue_format { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 7bf712032c52c..0bfe64cdabe6a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -1015,6 +1015,23 @@ int kfd_criu_restore_queue(struct kfd_process *p, goto exit; } + pdd = kfd_process_device_data_by_id(p, q_data->gpu_id); + if (!pdd) { + pr_err("Failed to get pdd\n"); + ret = -EINVAL; + goto exit; + } + + if (q_data->type >= KFD_QUEUE_TYPE_MAX) { + ret = -EINVAL; + goto exit; + } + + if (q_data->mqd_size != mqd_size_from_queue_type(pdd->dev->dqm, q_data->type)) { + ret = -EINVAL; + goto exit; + } + *priv_data_offset += sizeof(*q_data); q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size; @@ -1037,13 +1054,6 @@ int kfd_criu_restore_queue(struct kfd_process *p, *priv_data_offset += q_extra_data_size; - pdd = kfd_process_device_data_by_id(p, q_data->gpu_id); - if (!pdd) { - pr_err("Failed to get pdd\n"); - ret = -EINVAL; - goto exit; - } - /* * data stored in this order: * mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]... From abce3276c57e36c955627307469b9f009057a467 Mon Sep 17 00:00:00 2001 From: Yongqiang Sun Date: Mon, 6 Jul 2026 15:15:07 -0400 Subject: [PATCH 2582/3297] drm/amdkfd: fix 32-bit overflow in CWSR total size calculation commit 2b0386d4293920e690c0e017708f999b93cc729b upstream. total_cwsr_size was computed in 32-bit before being used as a BO/SVM allocation size. With large ctx_save_restore_area_size and debug_memory_size multiplied by the XCC count, the product can wrap, yielding an undersized CWSR save area that firmware later overruns. Promote total_cwsr_size to u64 and use check_add_overflow()/ check_mul_overflow() in both kfd_queue_acquire_buffers() and kfd_queue_release_buffers(). Signed-off-by: Yongqiang Sun Reviewed-by: Philip Yang Signed-off-by: Alex Deucher (cherry picked from commit 319f7e13423ae3f486b9aea82f9ad2d6af0ee608) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index b97f4a51db6e3..8e74e2bae5902 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -23,6 +23,7 @@ */ #include +#include #include "kfd_priv.h" #include "kfd_topology.h" #include "kfd_svm.h" @@ -235,7 +236,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope struct kfd_topology_device *topo_dev; u64 expected_queue_size; struct amdgpu_vm *vm; - u32 total_cwsr_size; + u64 total_cwsr_size; int err; topo_dev = kfd_topology_device_by_id(pdd->dev->id); @@ -305,8 +306,14 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope goto out_err_unreserve; } - total_cwsr_size = (properties->ctx_save_restore_area_size + - topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask); + total_cwsr_size = (u64)properties->ctx_save_restore_area_size + + topo_dev->node_props.debug_memory_size; + if (check_mul_overflow(total_cwsr_size, + NUM_XCC(pdd->dev->xcc_mask), + &total_cwsr_size)) { + err = -EINVAL; + goto out_err_unreserve; + } total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE); err = kfd_queue_buffer_get(vm, (void *)properties->ctx_save_restore_area_address, @@ -341,7 +348,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_properties *properties) { struct kfd_topology_device *topo_dev; - u32 total_cwsr_size; + u64 total_cwsr_size; kfd_queue_buffer_put(&properties->wptr_bo); kfd_queue_buffer_put(&properties->rptr_bo); @@ -352,8 +359,12 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope topo_dev = kfd_topology_device_by_id(pdd->dev->id); if (!topo_dev) return -EINVAL; - total_cwsr_size = (properties->ctx_save_restore_area_size + - topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask); + total_cwsr_size = (u64)properties->ctx_save_restore_area_size + + topo_dev->node_props.debug_memory_size; + if (check_mul_overflow(total_cwsr_size, + NUM_XCC(pdd->dev->xcc_mask), + &total_cwsr_size)) + return -EINVAL; total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE); kfd_queue_buffer_svm_put(pdd, properties->ctx_save_restore_area_address, total_cwsr_size); From 8a77ccf9cb9927122d9764b3dffe0565d15bd994 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 10 Jun 2026 17:18:17 +0200 Subject: [PATCH 2583/3297] drm/amd/display: Handle struct drm_plane_state.ignore_damage_clips commit ac11060c6d4959e2d4ceada037d2e1e1bfcf6645 upstream. The mode-setting pipeline can disabled damage clippings for a commit by setting ignore_damage_clips in struct drm_plane_state. The commit will then do a full display update. Test the flag in DCN code and do a full update in DCN code if it has been set. Commit 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips") introduced ignore_damage_clips to selectively ignore damage clipping in certain framebuffer changes. This driver does not do that, but DRM's damage iterator will soon rely on the flag. Therefore supporting it here as well make sense for consistency. Signed-off-by: Thomas Zimmermann Fixes: 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips") Cc: Javier Martinez Canillas Cc: Thomas Zimmermann Cc: Zack Rusin Cc: dri-devel@lists.freedesktop.org Reviewed-by: Javier Martinez Canillas Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher (cherry picked from commit a24019f6480fad5c077b5956eed942c8960323d6) Cc: # v6.8+ Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index a2ca1de6b43cf..66069df71d989 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -6240,8 +6240,8 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, { struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state); struct rect *dirty_rects = flip_addrs->dirty_rects; - u32 num_clips; - struct drm_mode_rect *clips; + u32 num_clips = 0; + struct drm_mode_rect *clips = NULL; bool bb_changed; bool fb_changed; u32 i = 0; @@ -6257,8 +6257,10 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, if (new_plane_state->rotation != DRM_MODE_ROTATE_0) goto ffu; - num_clips = drm_plane_get_damage_clips_count(new_plane_state); - clips = drm_plane_get_damage_clips(new_plane_state); + if (!new_plane_state->ignore_damage_clips) { + num_clips = drm_plane_get_damage_clips_count(new_plane_state); + clips = drm_plane_get_damage_clips(new_plane_state); + } if (num_clips && (!amdgpu_damage_clips || (amdgpu_damage_clips < 0 && is_psr_su))) From 4ee77643e6194f2deb62fe62f04396f9825e27d8 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Fri, 26 Jun 2026 20:45:55 +0800 Subject: [PATCH 2584/3297] drm/amd/display: detect_link_and_local_sink: DP alt mode timeout path leaks prev_sink reference commit a6e14b976be48eebd8769cb5b883a6af7fc5ade1 upstream. prev_sink is unconditionally retained via dc_sink_retain at function entry, but the DP alt mode timeout path inside SIGNAL_TYPE_DISPLAY_PORT returns false without releasing prev_sink. All other return paths in the function correctly call dc_sink_release(prev_sink), making this the only missing cleanup. Fixes: 54618888d1ea ("drm/amd/display: break down dc_link.c") Signed-off-by: WenTao Liang Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260626124555.36910-1-vulab@iscas.ac.cn Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 45510cf662dcf46b5d8926d454f338809f107b9d) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/link/link_detection.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index 31b404e9b9ae0..37ca3bb734c38 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -979,8 +979,11 @@ static bool detect_link_and_local_sink(struct dc_link *link, link->link_enc->features.flags.bits.DP_IS_USB_C == 1) { /* if alt mode times out, return false */ - if (!wait_for_entering_dp_alt_mode(link)) + if (!wait_for_entering_dp_alt_mode(link)) { + if (prev_sink) + dc_sink_release(prev_sink); return false; + } } if (!detect_dp(link, &sink_caps, reason)) { From 35be0e2c6862abcd5e5f5445261f1fd910d4a9b4 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 20 Jun 2026 21:43:34 -0500 Subject: [PATCH 2585/3297] drm/virtio: bound EDID block reads to the response buffer commit 4e1a53892ba7f8a3e1da6bfc53c83ae7c812dccd upstream. virtio_get_edid_block() validates the read offset only against the device-supplied resp->size field, never against the fixed-size resp->edid array. The EDID block index is driven by the device-supplied extension count, so a malicious virtio-gpu backend can advertise a large size together with a high block count and read far past the array into adjacent kernel memory, which is then surfaced in the parsed EDID (an out-of-bounds read / info leak). Also reject any read whose end exceeds the size of the edid array. Conforming EDID responses stay within the array and are unaffected. Fixes: b4b01b4995fb ("drm/virtio: add edid support") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260620-b4-disp-22bba7bf-v1-1-b95924cee742@proton.me Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/virtio/virtgpu_vq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 412384e16daea..83d46a8edf79f 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -893,7 +893,8 @@ static int virtio_get_edid_block(void *data, u8 *buf, struct virtio_gpu_resp_edid *resp = data; size_t start = block * EDID_LENGTH; - if (start + len > le32_to_cpu(resp->size)) + if (start + len > le32_to_cpu(resp->size) || + start + len > sizeof(resp->edid)) return -EINVAL; memcpy(buf, resp->edid + start, len); return 0; From 1f876bd8adc791c8a4b26272354f647ff1df6b3d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 13:44:07 +0300 Subject: [PATCH 2586/3297] drm/i915/hdcp: require monotonically increasing seq_num_v commit db9e64c983dcb07ff256bd455f258c44aa530ff8 upstream. The HDCP 2.2 specification requires the seq_num_v to be monotonically increasing, and repeated seq_num_v needs to be treated as an integrity failure. Make it so. For the first message, seq_num_v must be zero, and is already checked. We can only check for less-than-or-equal for the subsequent messages, where hdcp2_encrypted is true. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: d849178e2c9e ("drm/i915: Implement HDCP2.2 repeater authentication") Cc: stable@vger.kernel.org # v5.2+ Cc: Suraj Kandpal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260625104407.1025614-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit 58a224375c81179b52558c53d8857b93196d2687) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_hdcp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 531ee122bf822..d50cfe092105c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1794,9 +1794,10 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector) return -EINVAL; } - if (seq_num_v < hdcp->seq_num_v) { - /* Roll over of the seq_num_v from repeater. Reauthenticate. */ - drm_dbg_kms(display->drm, "Seq_num_v roll over.\n"); + if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) { + /* Reauthenticate on Seq_num_v repeat or rollover */ + drm_dbg_kms(display->drm, "Seq_num_v %s\n", + seq_num_v == hdcp->seq_num_v ? "repeat" : "rollover"); return -EINVAL; } From 3d2ef8d389495e7889c6062d8bddc46d2a5fbdef Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 20:03:04 +0300 Subject: [PATCH 2587/3297] drm/i915/hdcp: check streams[] bounds before overflow commit bbb15a6b042d02e5508a02b4847e02d2579ee7bc upstream. The data->streams[] overflow check is done after the buffer overflow has already happened. Move the overflow check before the write. Side note, emitting a warning splat with a backtrace might be overkill here, but prefer not changing the behaviour other than not doing the overrun. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: e03187e12cae ("drm/i915/hdcp: MST streams support in hdcp port_data") Cc: stable@vger.kernel.org # v5.12+ Cc: Anshuman Gupta Cc: Suraj Kandpal Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260625170304.1104723-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit 9284ab3b6e776c315883ac2611283d263c9460fd) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_hdcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index d50cfe092105c..68503a3f945e0 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -140,6 +140,9 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, if (conn_dig_port != dig_port) continue; + if (drm_WARN_ON(display->drm, data->k >= INTEL_NUM_PIPES(display))) + return -EINVAL; + data->streams[data->k].stream_id = intel_conn_to_vcpi(state, connector); data->k++; @@ -150,7 +153,7 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, } drm_connector_list_iter_end(&conn_iter); - if (drm_WARN_ON(display->drm, data->k > INTEL_NUM_PIPES(display) || data->k == 0)) + if (drm_WARN_ON(display->drm, !data->k)) return -EINVAL; /* From 4c09483325360373656214cc7a2fd29dc73037a5 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:28:29 -0400 Subject: [PATCH 2588/3297] drm/amdgpu/sdma7.0: replace BUG_ON() with WARN_ON() commit e80e28f398f5d9f6e361ffb56382d2e74fc87556 upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 9723a8bed3aa251a26bee4583bac9d8fb064dd44) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index 2b81344dcd668..d1318fb8060f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -364,7 +364,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -375,7 +375,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From 51fd52087165180967cf7d5ee99badee7e172ea0 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:27:54 -0400 Subject: [PATCH 2589/3297] drm/amdgpu/sdma6.0: replace BUG_ON() with WARN_ON() commit ec42c96c322e5cc48099ab5e67b5cbe236cb1949 upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit c17a508a7d652da3728f8bbc481bfffe96d65a87) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index 6809c6d4be5b1..ef411bd02237f 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -360,7 +360,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -371,7 +371,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From 09da54636bac146c1a3c461c4e7eb08d355bb86e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:27:15 -0400 Subject: [PATCH 2590/3297] drm/amdgpu/sdma5.2: replace BUG_ON() with WARN_ON() commit b9dd618a635d39fbb211454b6e8837b2a7f10fb0 upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit ae658afc7f47f6147371ec42cc6b1a793dfdb5af) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index 5b982cc91af39..48c672c1b74d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -378,7 +378,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -389,7 +389,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From d20b5c139b2906bcd8ab4bfe5b8be500318161d1 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:26:28 -0400 Subject: [PATCH 2591/3297] drm/amdgpu/sdma5.0: replace BUG_ON() with WARN_ON() commit 9e98ed3113943257ad6e5c1e6beddbdb482a70ad upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 8d144a0eb09537055841af48c9e7c2d4cd48e84d) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index 45e2933214a80..e1d9cea98a2d6 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -528,7 +528,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -539,7 +539,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From cbec6a57959ab503e3ad4ad6edd51efb585dce92 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 24 Jun 2026 12:09:40 +0300 Subject: [PATCH 2592/3297] drm/i915: Return NULL on error in active_instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 1e33f0de5fdcd09e51fdec1e5822448970b6420f upstream. Avoid returning &node->base when node is NULL due to OOM during GFP_ATOMIC allocation. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: bfaae47db3c0 ("drm/i915: make lockdep slightly happier about execbuf.") Cc: Maarten Lankhorst Cc: Thomas Hellström Cc: Simona Vetter Cc: # v5.13+ Signed-off-by: Joonas Lahtinen Reviewed-by: Sebastian Brzezinka Reviewed-by: Maarten Lankhorst Link: https://patch.msgid.link/20260624090940.74840-1-joonas.lahtinen@linux.intel.com (cherry picked from commit 6029bc064f0b1bac184203a50fbaaf070fa18832) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_active.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index 6b0c1162505ac..bbcfdf23c0a7d 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -318,7 +318,7 @@ active_instance(struct i915_active *ref, u64 idx) */ node = kmem_cache_alloc(slab_cache, GFP_ATOMIC); if (!node) - goto out; + goto err; __i915_active_fence_init(&node->base, NULL, node_retire); node->ref = ref; @@ -332,6 +332,11 @@ active_instance(struct i915_active *ref, u64 idx) spin_unlock_irq(&ref->tree_lock); return &node->base; + +err: + spin_unlock_irq(&ref->tree_lock); + + return NULL; } void __i915_active_init(struct i915_active *ref, From 8b2da44446f9dce2ae50fee78bac2734d4277143 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 26 Jun 2026 17:01:55 +0300 Subject: [PATCH 2593/3297] drm/i915/bios: range check LFP Data Block panel_type2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2084503f2d087bf956198e7f6eb25b03a7049cb2 upstream. While the panel_type from LFP Data Block is range checked, panel_type2 is not. Add a few helpers for range checking, and use them to not only check panel_type2, but also improve clarity and correctness in the panel type selection. Discovered using AI-assisted static analysis confirmed by Intel Product Security. v2: - Fix commit message typo (Michał) - Add is_panel_type_pnp() (Ville) Reported-by: Martin Hodo Fixes: 6434cf630086 ("drm/i915/bios: calculate panel type as per child device index in VBT") Cc: stable@vger.kernel.org # v6.0+ Cc: Animesh Manna Cc: Ville Syrjälä Reviewed-by: Michał Grzelak # v1 Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260626140155.1389655-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit c9ebe5d2f25729d6cfbbb1235d640bf67f9275df) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_bios.c | 36 ++++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 3596dce84c286..a2b6f40b4b0c0 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -624,6 +624,21 @@ get_lfp_data_tail(const struct bdb_lfp_data *data, return NULL; } +static bool is_panel_type_valid(int panel_type) +{ + return panel_type >= 0 && panel_type < 16; +} + +static bool is_panel_type_pnp(int panel_type) +{ + return panel_type == 0xff; +} + +static bool is_panel_type_valid_or_pnp(int panel_type) +{ + return is_panel_type_valid(panel_type) || is_panel_type_pnp(panel_type); +} + static int opregion_get_panel_type(struct intel_display *display, const struct intel_bios_encoder_data *devdata, const struct drm_edid *drm_edid, bool use_fallback) @@ -641,15 +656,21 @@ static int vbt_get_panel_type(struct intel_display *display, if (!lfp_options) return -1; - if (lfp_options->panel_type > 0xf && - lfp_options->panel_type != 0xff) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type)) { drm_dbg_kms(display->drm, "Invalid VBT panel type 0x%x\n", lfp_options->panel_type); return -1; } - if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) + if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type2)) { + drm_dbg_kms(display->drm, "Invalid VBT panel type 2 0x%x\n", + lfp_options->panel_type2); + return -1; + } + return lfp_options->panel_type2; + } drm_WARN_ON(display->drm, devdata && devdata->child.handle != DEVICE_HANDLE_LFP1); @@ -763,13 +784,12 @@ static int get_panel_type(struct intel_display *display, panel_types[i].name, panel_types[i].panel_type); } - if (panel_types[PANEL_TYPE_OPREGION].panel_type >= 0) + if (is_panel_type_valid(panel_types[PANEL_TYPE_OPREGION].panel_type)) i = PANEL_TYPE_OPREGION; - else if (panel_types[PANEL_TYPE_VBT].panel_type == 0xff && - panel_types[PANEL_TYPE_PNPID].panel_type >= 0) + else if (is_panel_type_pnp(panel_types[PANEL_TYPE_VBT].panel_type) && + is_panel_type_valid(panel_types[PANEL_TYPE_PNPID].panel_type)) i = PANEL_TYPE_PNPID; - else if (panel_types[PANEL_TYPE_VBT].panel_type != 0xff && - panel_types[PANEL_TYPE_VBT].panel_type >= 0) + else if (is_panel_type_valid(panel_types[PANEL_TYPE_VBT].panel_type)) i = PANEL_TYPE_VBT; else i = PANEL_TYPE_FALLBACK; From 5df5a59c32b46fe8e7f292df688c2b248aa9f4f2 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Thu, 25 Jun 2026 17:48:22 +0800 Subject: [PATCH 2594/3297] drm/amd/amdgpu: disable ASPM on VI if pcie dpm is disabled commit 18a7826aea6fd09f2d371c02cec70c7234fc4879 upstream. Disable ASPM on VI if PCIE dpm is disabled. Fixes: bb00bf17328d ("drm/amd/amdgpu: decouple ASPM with pcie dpm") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5370 Signed-off-by: Kenneth Feng Reviewed-by: Yang Wang Signed-off-by: Alex Deucher (cherry picked from commit 873a8d6b3c0a386408c891e4ff1c684fa11783e1) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index d0cb300cd382b..653f98b447b5c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1888,7 +1888,8 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) * It's unclear if this is a platform-specific or GPU-specific issue. * Disable ASPM on SI for the time being. */ - if (adev->family == AMDGPU_FAMILY_SI) + if (adev->family == AMDGPU_FAMILY_SI || + (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK) && adev->family == AMDGPU_FAMILY_VI)) return true; #if IS_ENABLED(CONFIG_X86) From 1173190412fb9d12e7efce76734118d9712ff970 Mon Sep 17 00:00:00 2001 From: Shahyan Soltani Date: Mon, 6 Jul 2026 08:15:21 -0400 Subject: [PATCH 2595/3297] drm/amdgpu: fix lifetime issue of amdgpu_vm_get_task_info_pasid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 04cc4aa3617b0ed67e859f91f09de5d896a46f3a upstream. The vm pointer returned from amdgpu_vm_get_vm_from_pasid() is only valid while the lock is still being held. Once xa_unlock_irqrestore is called and returned, the pointer is no longer under lock and is subject to modification. Since, the caller still dereferences vm->task_info in amdgpu_vm_get_task_info_vm() after the lock is removed, this causes a use after unlock problem. Remove the lifetime issue present in amdgpu_vm_get_task_info_pasid() through removing the amdgpu_vm_get_vm_from_pasid() function from amdgpu_vm.c and making the relevant code inline to hold the lock while it is still in use. Signed-off-by: Shahyan Soltani Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 9d01579f3f868b333acc901815972685989092c7) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index ffbae605caaa5..fd80571a1acea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2453,19 +2453,6 @@ static void amdgpu_vm_destroy_task_info(struct kref *kref) kfree(ti); } -static inline struct amdgpu_vm * -amdgpu_vm_get_vm_from_pasid(struct amdgpu_device *adev, u32 pasid) -{ - struct amdgpu_vm *vm; - unsigned long flags; - - xa_lock_irqsave(&adev->vm_manager.pasids, flags); - vm = xa_load(&adev->vm_manager.pasids, pasid); - xa_unlock_irqrestore(&adev->vm_manager.pasids, flags); - - return vm; -} - /** * amdgpu_vm_put_task_info - reference down the vm task_info ptr * @@ -2512,8 +2499,16 @@ amdgpu_vm_get_task_info_vm(struct amdgpu_vm *vm) struct amdgpu_task_info * amdgpu_vm_get_task_info_pasid(struct amdgpu_device *adev, u32 pasid) { - return amdgpu_vm_get_task_info_vm( - amdgpu_vm_get_vm_from_pasid(adev, pasid)); + struct amdgpu_task_info *ti; + struct amdgpu_vm *vm; + unsigned long flags; + + xa_lock_irqsave(&adev->vm_manager.pasids, flags); + vm = xa_load(&adev->vm_manager.pasids, pasid); + ti = amdgpu_vm_get_task_info_vm(vm); + xa_unlock_irqrestore(&adev->vm_manager.pasids, flags); + + return ti; } static int amdgpu_vm_create_task_info(struct amdgpu_vm *vm) From 37951ce1567ccf8c86c7a1b8fb7d55a32c821b87 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 10:30:30 +0300 Subject: [PATCH 2596/3297] drm/i915/gem: Do not leak siblings[] on proto context error commit eed3de2acf6aa5154d49098b026710b646db67ee upstream. After a successful BALANCE/PARALLEL_SUBMIT extension on context creation, error during processing of next user extension leaks the siblings[] array. Fix that. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") Cc: Faith Ekstrand Cc: Simona Vetter Cc: Tvrtko Ursulin Cc: Maarten Lankhorst Cc: # v5.15+ Signed-off-by: Joonas Lahtinen Reviewed-by: Maarten Lankhorst Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260701073030.44850-1-joonas.lahtinen@linux.intel.com (cherry picked from commit aa65e0a4b51b3b54b53e4142aaa2d997aa1061ff) Signed-off-by: Rodrigo Vivi Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/gem/i915_gem_context.c | 22 +++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index e076344b8da9c..25b190dac4d6b 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -770,8 +770,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv, struct intel_engine_cs *engine; if (copy_from_user(&ci, &user->engines[n], sizeof(ci))) { - kfree(set.engines); - return -EFAULT; + err = -EFAULT; + goto err; } memset(&set.engines[n], 0, sizeof(set.engines[n])); @@ -787,8 +787,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv, drm_dbg(&i915->drm, "Invalid engine[%d]: { class:%d, instance:%d }\n", n, ci.engine_class, ci.engine_instance); - kfree(set.engines); - return -ENOENT; + err = -ENOENT; + goto err; } set.engines[n].type = I915_GEM_ENGINE_TYPE_PHYSICAL; @@ -801,15 +801,21 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv, set_proto_ctx_engines_extensions, ARRAY_SIZE(set_proto_ctx_engines_extensions), &set); - if (err) { - kfree(set.engines); - return err; - } + if (err) + goto err_extensions; pc->num_user_engines = set.num_engines; pc->user_engines = set.engines; return 0; + +err_extensions: + for (n = 0; n < set.num_engines; n++) + kfree(set.engines[n].siblings); +err: + kfree(set.engines); + + return err; } static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv, From 726f27bca93e6c83b263542669132ee1d0eb693e Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 10:55:55 +0300 Subject: [PATCH 2597/3297] drm/i915/gem: Fix NULL deref in I915_CONTEXT_PARAM_SSEU commit 2b56757a9a7456825eb668fde92299e01c5e2721 upstream. Setting context engine slot N into I915_ENGINE_CLASS_INVALID / I915_ENGINE_CLASS_INVALID_NONE and attempting to apply I915_CONTEXT_PARAM_SSEU to the same slot N will deref NULL. Fix that. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: d4433c7600f7 ("drm/i915/gem: Use the proto-context to handle create parameters (v5)") Cc: Faith Ekstrand Cc: Simona Vetter Cc: Tvrtko Ursulin Cc: Maarten Lankhorst Cc: # v5.15+ Signed-off-by: Joonas Lahtinen Reviewed-by: Maarten Lankhorst Reviewed-by: Andi Shyti Link: https://patch.msgid.link/20260701075555.52142-1-joonas.lahtinen@linux.intel.com (cherry picked from commit 36eda5b5c2d40da41cc0a5403c26986237cf9e87) Signed-off-by: Rodrigo Vivi Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/gem/i915_gem_context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index 25b190dac4d6b..ae5194625508f 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -857,7 +857,7 @@ static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv, pe = &pc->user_engines[idx]; /* Only render engine supports RPCS configuration. */ - if (pe->engine->class != RENDER_CLASS) + if (!pe->engine || pe->engine->class != RENDER_CLASS) return -EINVAL; sseu = &pe->sseu; From e3bcd3bf7eeca9570b9fa0b2f8a602c7bcc6b0d0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 17:22:04 +0300 Subject: [PATCH 2598/3297] drm/i915/mst: limit DP MST ESI service loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 005771c18c5b2c98cb4e7517661aea460990fd3f upstream. The loop in intel_dp_check_mst_status() keeps servicing interrupts originating from the sink without bound. Add an upper bound to the new interrupts occurring during interrupt processing to not get stuck on potentially stuck sink devices. Use arbitrary 32 tries to clear incoming interrupts in one go. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Note: The condition likely pre-dates the commit in the Fixes: tag, but this is about as far back as a backport has any chance of succeeding. Before that, the retry had a goto. Reported-by: Martin Hodo Fixes: 3c0ec2c2d594 ("drm/i915: Flatten intel_dp_check_mst_status() a bit") Cc: stable@vger.kernel.org # v5.8+ Cc: Ville Syrjälä Cc: Imre Deak Reviewed-by: Imre Deak Link: https://patch.msgid.link/20260625142204.1078287-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit b4ea5272133059acb493cc36599071a9e852ec2e) Signed-off-by: Rodrigo Vivi Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_dp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c7886b3647706..2a0750acf2053 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5203,8 +5203,9 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) struct intel_encoder *encoder = &dig_port->base; bool link_ok = true; bool reprobe_needed = false; + int tries = 33; - for (;;) { + while (--tries) { u8 esi[4] = {}; u8 ack[4] = {}; @@ -5247,6 +5248,11 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) if (!link_ok || intel_dp->link.force_retrain) intel_encoder_link_check_queue_work(encoder, 0); + if (!tries) { + drm_dbg_kms(display->drm, "DPRX ESI not clearing, device may be stuck\n"); + reprobe_needed = true; + } + return !reprobe_needed; } From e163c5a0946de528d9a83f567153677d31202f12 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 6 Jul 2026 09:29:24 +0800 Subject: [PATCH 2599/3297] drm/amd/pm: fix smu14 power limit range calculation commit e987eabc02646920cd13ab75902693e99735eca0 upstream. SMU14 derives the default PPT limit from SocketPowerLimitAc/Dc, but MsgLimits.Power may expose a different firmware limit for the same PPT0 throttler. Using those values independently as fixed min/max bases can report an incorrect configurable power range. Keep the socket power limit as the default value and as the fallback for current-limit queries. Calculate the reported range from both firmware values instead, using the lower value as the minimum base and the higher value as the maximum base before applying OD percentages. Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit c936b8126b444401318fcbeb1828488cc5312dee) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index 969be5ad06819..9e42cc8dd133e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -1701,19 +1701,23 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu, table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; CustomSkuTable_t *skutable = &pptable->CustomSkuTable; - int16_t od_percent_upper = 0, od_percent_lower = 0; + uint32_t pp_limit = smu->adev->pm.ac_power ? + skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : + skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - uint32_t power_limit; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + int16_t od_percent_upper = 0, od_percent_lower = 0; + int ret; - if (smu_v14_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? - skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : - skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + if (current_power_limit) { + ret = smu_v14_0_get_current_power_limit(smu, current_power_limit); + if (ret) + *current_power_limit = pp_limit; + } - if (current_power_limit) - *current_power_limit = power_limit; if (default_power_limit) - *default_power_limit = power_limit; + *default_power_limit = pp_limit; if (powerplay_table) { if (smu->od_enabled && @@ -1727,15 +1731,15 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu, } dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); + *max_power_limit = max_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = power_limit * (100 + od_percent_lower); + *min_power_limit = min_limit * (100 + od_percent_lower); *min_power_limit /= 100; } From a4a1866d50c49d9fabbc7f3a0eaa2e33b9d632bd Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 26 Jun 2026 16:29:13 -0400 Subject: [PATCH 2600/3297] drm/gfx10: Program DB_RING_CONTROL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f0262c3a3f14d60140f6b826d40d44edf62c36d6 upstream. This is needed to allocate occlusion counters across both gfx pipes. Fixes: b7a1a0ef12b8 ("drm/amd/amdgpu: add pipe1 hardware support") Reviewed-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 6807352cbabb74b61ba42888769283af72191f66) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 234753a10361f..fdd55609c677e 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -5349,6 +5349,15 @@ static void gfx_v10_0_constants_init(struct amdgpu_device *adev) gfx_v10_0_get_tcc_info(adev); adev->gfx.config.pa_sc_tile_steering_override = gfx_v10_0_init_pa_sc_tile_steering_override(adev); + /* Program DB_RING_CONTROL for multiple GFX pipes + * Default power up value is 1. + * Possible values: + * 0 - split occlusion counters between gfx pipes + * 1 - all occlusion counters to pipe 0 + * 2 - all occlusion counters to pipe 1 + */ + WREG32_FIELD15(GC, 0, DB_RING_CONTROL, COUNTER_CONTROL, + (adev->gfx.me.num_pipe_per_me > 1) ? 0 : 1); /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ From b2d8b66c673933ce62bc05a54b047d7354b2e298 Mon Sep 17 00:00:00 2001 From: Jason Macnak Date: Thu, 25 Jun 2026 10:08:28 -0700 Subject: [PATCH 2601/3297] drm/virtio: Don't detach GEM from a non-created context commit d489a5305b9d5480d6fb97d5636f5f4b1e0b3827 upstream. Applies the same treatment as commit 7cf6dd467e87 ("drm/virtio: Don't attach GEM to a non-created context in gem_object_open()") to virtio_gpu_gem_object_close() to avoid trying to detach a resource that was never attached due to a context never being created when context_init is supported. Fixes: 086b9f27f0ab ("drm/virtio: Don't create a context with default param if context_init is supported") Cc: # v6.14+ Signed-off-by: Jason Macnak Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260625170828.3335431-1-natsu@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/virtio/virtgpu_gem.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c index 015b5debd7451..7174086119c51 100644 --- a/drivers/gpu/drm/virtio/virtgpu_gem.c +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj, if (!vgdev->has_virgl_3d) return; - objs = virtio_gpu_array_alloc(1); - if (!objs) - return; - virtio_gpu_array_add_obj(objs, obj); + if (vfpriv->context_created) { + objs = virtio_gpu_array_alloc(1); + if (!objs) + return; + virtio_gpu_array_add_obj(objs, obj); - virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id, - objs); + virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id, + objs); + } virtio_gpu_notify(vgdev); } From 22aa7fb4e7d0b3ab41d1240ed743167980912970 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 2 Jul 2026 14:48:15 -0700 Subject: [PATCH 2602/3297] drm/ttm: Account for NULL and handle pages in ttm_pool_backup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5b7b3b6595ee77d01c7463757baed114786094dd upstream. Pages in ttm_pool_backup can be NULL or backup handles (ttm_backup_page_ptr_is_handle()), neither of which can be passed to set_pages_array_wb() or freed. Add a dedicated WB pass before the dma/purge loop that walks allocations using the same i += num_pages stride, skipping NULL and handle entries, and calls set_pages_array_wb() once per contiguous run of real pages. Apply the same NULL/handle guard to the dma/purge loop. Fixes the following oops: Oops: general protection fault, kernel NULL pointer dereference 0x0: 0000 [#1] SMP NOPTI RIP: 0010:__cpa_process_fault+0xf8/0x770 RSP: 0018:ffffc90000a87718 EFLAGS: 00010287 RAX: 0000000000000000 RBX: ffffc90000a87868 RCX: 0000000000000000 RDX: 0000000000001000 RSI: 0005088000000000 RDI: ffffffff827c5f34 RBP: 0005088000000000 R08: ffffc90000a877cb R09: ffffc90000a877d0 R10: 0000000000000000 R11: 000000000000001b R12: 000ffffffffff000 R13: ffffc90000a87868 R14: ffffc90000a87868 R15: ffff88815b882ae0 FS: 0000000000000000(0000) GS:ffff8884ec840000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f930b844000 CR3: 000000000262e003 CR4: 0000000008f70ef0 PKRU: 55555554 Call Trace: __change_page_attr_set_clr+0x989/0xe90 ? __purge_vmap_area_lazy+0x6c/0x3a0 ? _vm_unmap_aliases+0x250/0x2a0 set_pages_array_wb+0x7f/0x120 ttm_pool_backup+0x4c9/0x5b0 [ttm] ? dma_resv_wait_timeout+0x3b/0xf0 ttm_tt_backup+0x32/0x60 [ttm] ttm_bo_shrink+0x66/0x110 [ttm] xe_bo_shrink_purge+0x12b/0x1b0 [xe] xe_bo_shrink+0xbb/0x270 [xe] __xe_shrinker_walk+0xf7/0x160 [xe] xe_shrinker_walk+0x9d/0xc0 [xe] xe_shrinker_scan+0x11f/0x210 [xe] do_shrink_slab+0x13b/0x270 shrink_slab+0xf1/0x400 shrink_node+0x352/0x8a0 balance_pgdat+0x32c/0x700 kswapd+0x205/0x2f0 ? __pfx_autoremove_wake_function+0x10/0x10 ? __pfx_kswapd+0x10/0x10 kthread+0xd1/0x110 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x1b1/0x200 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Cc: Christian Koenig Cc: Huang Rui Cc: Matthew Auld Cc: Matthew Brost Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: Thomas Hellström Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org Fixes: b63d715b8090 ("drm/ttm/pool, drm/ttm/tt: Provide a helper to shrink pages") Cc: stable@vger.kernel.org Assisted-by: GitHub_Copilot:claude-opus-4.8 Signed-off-by: Matthew Brost Reviewed-by: Thomas Hellström Link: https://patch.msgid.link/20260702214815.4009271-1-matthew.brost@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/ttm/ttm_pool.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index baf27c70a4193..01083720e4e7f 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -981,9 +981,31 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, return -EBUSY; #ifdef CONFIG_X86 - /* Anything returned to the system needs to be cached. */ - if (tt->caching != ttm_cached) - set_pages_array_wb(tt->pages, tt->num_pages); + /* Anything returned to the system needs to be cached. Walk allocations + * skipping NULL pages and issue set_pages_array_wb() per contiguous run. + */ + if (tt->caching != ttm_cached) { + pgoff_t run_start = 0, run_count = 0; + + for (i = 0; i < tt->num_pages; i += num_pages) { + page = tt->pages[i]; + if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) { + if (run_count) { + set_pages_array_wb(&tt->pages[run_start], + run_count); + run_count = 0; + } + num_pages = 1; + continue; + } + num_pages = 1UL << ttm_pool_page_order(pool, page); + if (!run_count) + run_start = i; + run_count += num_pages; + } + if (run_count) + set_pages_array_wb(&tt->pages[run_start], run_count); + } #endif if (tt->dma_address || flags->purge) { @@ -991,7 +1013,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, unsigned int order; page = tt->pages[i]; - if (unlikely(!page)) { + if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) { num_pages = 1; continue; } @@ -1034,6 +1056,10 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (unlikely(!page)) continue; + /* Already-handled entry from a previous attempt. */ + if (unlikely(ttm_backup_page_ptr_is_handle(page))) + continue; + ttm_pool_split_for_swap(pool, page); shandle = ttm_backup_backup_page(backup, page, flags->writeback, i, From e64b2f1e826af697f2b786dc30d166135bec5609 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Tue, 14 Jul 2026 18:30:55 +0200 Subject: [PATCH 2603/3297] drm/panthor: return error on truncated firmware commit 4a2c8cbe9bcba170706fdf08b1c84b6cbcf5b044 upstream. panthor_fw_load() detects truncated firmware images, but jumps to the common cleanup path without setting ret. If no previous error was recorded, the function can return 0 and treat the invalid firmware as successfully loaded. Set ret to -EINVAL before leaving the truncated-image path. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Liviu Dudau Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260714163056.22329-1-osama.abdelkader@gmail.com Signed-off-by: Liviu Dudau Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/panthor/panthor_fw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index df767e82148af..a8a5a8e260242 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -777,6 +777,7 @@ static int panthor_fw_load(struct panthor_device *ptdev) } if (hdr.size > iter.size) { + ret = -EINVAL; drm_err(&ptdev->base, "Firmware image is truncated\n"); goto out; } From 312278b3091912fa56a6a587609f17dcb33465c2 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 8 Jul 2026 14:35:15 -0500 Subject: [PATCH 2604/3297] drm/amdgpu: Release VFCT ACPI table reference commit 65bff26617607c1331283232016c0e89088c5b78 upstream. amdgpu_acpi_vfct_bios() fetches the VFCT table with acpi_get_table() but never releases it. acpi_get_table() takes a reference on the table (incrementing its validation_count and mapping it on the 0->1 transition); without a paired acpi_put_table() the mapping is leaked on every call, whether or not a matching VBIOS image is found. Route all exit paths after the table is acquired through a common acpi_put_table(). The VBIOS image is copied out with kmemdup() before the table is released, so it remains valid for the caller. Reviewed-by: Alex Deucher Link: https://patch.msgid.link/20260708193518.702584-3-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit ca5988682b4cba4cd125a0fa99b2de1239164ae4) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index 00e96419fcdad..57af0291fc47d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -361,13 +361,14 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) acpi_size tbl_size; UEFI_ACPI_VFCT *vfct; unsigned int offset; + bool r = false; if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) return false; tbl_size = hdr->length; if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { dev_info(adev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n"); - return false; + goto out; } vfct = (UEFI_ACPI_VFCT *)hdr; @@ -380,13 +381,13 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) offset += sizeof(VFCT_IMAGE_HEADER); if (offset > tbl_size) { dev_info(adev->dev, "ACPI VFCT image header truncated,skipping\n"); - return false; + goto out; } offset += vhdr->ImageLength; if (offset > tbl_size) { dev_info(adev->dev, "ACPI VFCT image truncated,skipping\n"); - return false; + goto out; } if (vhdr->ImageLength && @@ -401,15 +402,19 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) if (!check_atom_bios(adev, vhdr->ImageLength)) { amdgpu_bios_release(adev); - return false; + goto out; } adev->bios_size = vhdr->ImageLength; - return true; + r = true; + goto out; } } dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n"); - return false; + +out: + acpi_put_table(hdr); + return r; } #else static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) From d8dc3a9e815d6b225616dce4f1eac7261a8ead0f Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 8 Jul 2026 14:35:14 -0500 Subject: [PATCH 2605/3297] drm/amdgpu: Fix VFCT bus number matching with soft filter commit db7e8108809a2245f0a17ba323f027cac0941ffb upstream. On systems where PCI bus renumbering occurs (e.g. pci=realloc, resource conflicts), the runtime bus number may differ from the BIOS POST bus number recorded in the VFCT table. This causes amdgpu_acpi_vfct_bios() to fail finding the VBIOS even though the correct device entry exists. Introduce amdgpu_acpi_vfct_match() which treats the bus number as a soft filter: vendor/device/function identity is the hard requirement, while exact bus match is the preferred path. When bus numbers disagree but device identity matches, accept the VFCT entry and log a dev_notice for diagnostics. Reported-by: Oz Tiram Closes: https://lore.kernel.org/amd-gfx/20260621173211.28443-1-oz@shift-computing.de/ Reviewed-by: Alex Deucher Link: https://patch.msgid.link/20260708193518.702584-2-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 11c141672045ffc0187aa604f2c0f597bc334fb2) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 45 +++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index 57af0291fc47d..4e60753e16e90 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -355,6 +355,45 @@ static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) } #ifdef CONFIG_ACPI +/** + * amdgpu_acpi_vfct_match() - Check if a VFCT entry matches the device + * @adev: AMDGPU device + * @vhdr: VFCT image header to check + * + * VFCT entries contain the PCI bus number as recorded during BIOS POST. + * On systems where the kernel renumbers PCI buses (e.g. pci=realloc or + * resource conflicts), the runtime bus number may differ from the POST + * value. Match by device identity (vendor + device + function) and use + * the bus number as a preference: exact bus match is preferred, but when + * the bus numbers disagree we accept the entry if the device identity + * matches. + * + * Returns: 0 on match, -ENODEV on no match + */ +static int amdgpu_acpi_vfct_match(struct amdgpu_device *adev, + VFCT_IMAGE_HEADER *vhdr) +{ + /* Vendor and device IDs must always match */ + if (vhdr->VendorID != adev->pdev->vendor || + vhdr->DeviceID != adev->pdev->device) + return -ENODEV; + + if (vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) || + vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn)) + return -ENODEV; + + /* Exact bus number match - preferred */ + if (vhdr->PCIBus == adev->pdev->bus->number) + return 0; + + /* Bus mismatch but device identity matches (PCI renumbering case) */ + dev_notice(adev->dev, + "VFCT bus number mismatch: table %u != runtime %u, matching by device identity (vendor 0x%04x device 0x%04x)\n", + vhdr->PCIBus, adev->pdev->bus->number, + adev->pdev->vendor, adev->pdev->device); + return 0; +} + static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) { struct acpi_table_header *hdr; @@ -391,11 +430,7 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) } if (vhdr->ImageLength && - vhdr->PCIBus == adev->pdev->bus->number && - vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) && - vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) && - vhdr->VendorID == adev->pdev->vendor && - vhdr->DeviceID == adev->pdev->device) { + !amdgpu_acpi_vfct_match(adev, vhdr)) { adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); From 123692ebc1ea731c82790f438654427b6e137300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Mon, 13 Jul 2026 08:14:43 +0200 Subject: [PATCH 2606/3297] drm/amd/pm/ci: Don't disable MCLK DPM on Bonaire 0x6658 (R7 260X) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 85371c5ef502d10add72eab38711e191dccea981 upstream. The old radeon driver has a documented workaround in ci_dpm.c which claims that Bonaire 0x6658 with old memory controller firmware is unstable with MCLK DPM, so as a precaution I disabled MCLK DPM on this ASIC in amdgpu. Note that the old MC firmware is not actually used with amdgpu, but in theory it's possible that the VBIOS sets up the ASIC with an old MC firmware that is already running when amdgpu initializes (in which case amdgpu doesn't load its own firmware). What I expected to happen is that the GPU would simply use its maximum memory clock, and indeed this is what seemed to happen according to amdgpu_pm_info which reads the current MCLK value from the SMU. However, some users reported a huge perf regression and upon a closer look it seems that the GPU seems to not actually use the highest MCLK value, despite the SMU reporting that it does. Let's not disable MCLK DPM on Bonaire 0x6658 (R7 260X). Keep MCLK DPM disabled on R9 M380 in the 2015 iMac because that still hangs if we enable it. Fixes: 9851f29cb06c ("drm/amd/pm/ci: Disable MCLK DPM on problematic CI ASICs") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit d34acad064ee7d82bd18f5d87592c422d4d323ac) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c index 1d6e30269d567..4d553be56396f 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c @@ -106,11 +106,8 @@ int hwmgr_early_init(struct pp_hwmgr *hwmgr) hwmgr->od_enabled = false; switch (hwmgr->chip_id) { case CHIP_BONAIRE: - /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM - * R7 260X cards with old MC ucode: MCLK DPM is unstable - */ - if (adev->pdev->subsystem_vendor == 0x106B || - adev->pdev->device == 0x6658) { + /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM */ + if (adev->pdev->subsystem_vendor == 0x106B) { dev_info(adev->dev, "disabling MCLK DPM on quirky ASIC"); adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK; hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK; From 679f23f0a3606afcef1ffabd72222f00a54ad9e3 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Sun, 28 Jun 2026 15:27:40 +0800 Subject: [PATCH 2607/3297] drm/amd/display: set new_stream to NULL after release commit 9fa26b9eed6195bf840f39ac183b9a6237548755 upstream. In dm_update_crtc_state(), the skip_modeset path releases new_stream via dc_stream_release() but does not set the pointer to NULL. If a later error (e.g., color management failure) triggers the fail label, the error path calls dc_stream_release() again on the same dangling pointer, causing a double release and potential use-after-free. Fix this by setting new_stream to NULL after the initial release. Fixes: 9b690ef3c704 ("drm/amd/display: Avoid full modeset when not required") Signed-off-by: WenTao Liang Reviewed-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 99f3af19073b3ddbfd96e789124cce12c4277b28) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 66069df71d989..6f352e1acd819 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -11383,6 +11383,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, /* Release extra reference */ if (new_stream) dc_stream_release(new_stream); + new_stream = NULL; /* * We want to do dc stream updates that do not require a From 0b9fa4272e24bfc28fe075118f246cb09b9770ed Mon Sep 17 00:00:00 2001 From: Leo Li Date: Fri, 12 Jun 2026 13:29:31 -0400 Subject: [PATCH 2608/3297] drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock commit 8382cd234981ae36299bb66a10bac2cd8ff1b99d upstream. [Why] On DCN, vblank events were delivered from VSTARTUP/VUPDATE (dm_crtc_high_irq/dm_vupdate_high_irq) and pageflip completion from GRPH_PFLIP (dm_pflip_high_irq). These signals can be masked by hardware by a few things: * DPG - DCN can Dynamically Power Gate parts of the display pipe when a self-refresh capable eDP is connected. DPG is engaged when there's enough static frames (detected through drm_vblank_off). Once gated, even though the OTG (output timing generator) is still enabled, VSTARTUP and GRPH_FLIP are masked. * GSL - Driver can use the Global Sync Lock to block HW from latching onto double-buffered registers during programming, to prevent HW from latching onto a partially programmed state. This will mask VSTARTUP, GRPH_FLIP, and VUPDATE. See dcn20_pipe_control_lock(). * MALL - A DCN accessible cache introduced in DCN32+ DGPUs that can store fb data to allow for longer DRAM sleep. When scanning out from MALL, VSTARTUP is masked. When masked, events are never delivered, which can show up as flip_done timeouts in the wild. However, there is an interrupt source on DCN that is never masked: VUPDATE_NO_LOCK. It's simply an unmasked variant of VUPDATE, which fires while the OTG is active, at the exact point hardware latches double-buffered registers. It is therefore the natural single signal for delivering both vblank and flip-completion events on DCN, and the correct point to timestamp both VRR and non-VRR vblanks. DCE's interrupt sources are different, it does not have an unmaskable VUPDATE_NO_LOCK. The only unmaskable DCE interrupt is VLINE0, but it can only be programmed as a vline offset from vsync_start, making it unsuitable for VRR. Thus, we keep DCE untouched and use the existing mix of interrupt sources. [How] For DCN1 and newer only: * Factor the body of dm_crtc_high_irq() into dm_crtc_high_irq_handler() and drive it from dm_vupdate_high_irq() (VUPDATE_NO_LOCK). DCE keeps using dm_crtc_high_irq() (VSTARTUP) and dm_pflip_high_irq() (GRPH_PFLIP) unchanged. * Stop registering VSTARTUP (crtc_irq) and GRPH_PFLIP (pageflip_irq) on DCN, and stop enabling them in amdgpu_dm_crtc_set_vblank() / manage_dm_interrupts(). Enable VUPDATE whenever vblank is enabled on DCN (previously only in VRR mode). The secure-display vline0 interrupt is left untouched. * VUPDATE_NO_LOCK does not early-fire on an immediate (tearing / async) flip, since HW latches the new address right away. Deliver the flip completion event immediately after programming such flips in amdgpu_dm_commit_planes(), and clear pflip_status so the next vupdate handler does not double-send. v2: Do not gate VUPDATE_NO_LOCK on DCN in dm_handle_vrr_transition() Also toggle VUPDATE_NO_LOCK on DCN in dm_gpureset_toggle_interrupts() Re-cook vblank event count and timestamp for immediate flips Fixes: 9b47278cec98 ("drm/amd/display: temp w/a for dGPU to enter idle optimizations") Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/3787 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141 Assisted-by: Copilot:claude-opus-4.8 Co-developed-by: Matthew Schwartz Signed-off-by: Matthew Schwartz Tested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Leo Li Signed-off-by: Alex Deucher (cherry picked from commit c87e6635d2db02c88ae8d09529362da672d34770) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 347 ++++++++++-------- .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 70 ++-- 2 files changed, 228 insertions(+), 189 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 6f352e1acd819..add471b150d42 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -575,89 +575,25 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev, queue_work(system_wq, &offload_work->work); } -static void dm_vupdate_high_irq(void *interrupt_params) -{ - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; - struct amdgpu_crtc *acrtc; - struct drm_device *drm_dev; - struct drm_vblank_crtc *vblank; - ktime_t frame_duration_ns, previous_timestamp; - unsigned long flags; - int vrr_active; - - acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE); - - if (acrtc) { - vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); - drm_dev = acrtc->base.dev; - vblank = drm_crtc_vblank_crtc(&acrtc->base); - previous_timestamp = atomic64_read(&irq_params->previous_timestamp); - frame_duration_ns = vblank->time - previous_timestamp; - - if (frame_duration_ns > 0) { - trace_amdgpu_refresh_rate_track(acrtc->base.index, - frame_duration_ns, - ktime_divns(NSEC_PER_SEC, frame_duration_ns)); - atomic64_set(&irq_params->previous_timestamp, vblank->time); - } - - drm_dbg_vbl(drm_dev, - "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, - vrr_active); - - /* Core vblank handling is done here after end of front-porch in - * vrr mode, as vblank timestamping will give valid results - * while now done after front-porch. This will also deliver - * page-flip completion events that have been queued to us - * if a pageflip happened inside front-porch. - */ - if (vrr_active && acrtc->dm_irq_params.stream) { - bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; - bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; - bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state - == VRR_STATE_ACTIVE_VARIABLE; - - amdgpu_dm_crtc_handle_vblank(acrtc); - - /* BTR processing for pre-DCE12 ASICs */ - if (adev->family < AMDGPU_FAMILY_AI) { - spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); - mod_freesync_handle_v_update( - adev->dm.freesync_module, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params); - - if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { - schedule_dc_vmin_vmax(adev, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params.adjust); - } - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); - } - } - } -} - /** - * dm_crtc_high_irq() - Handles CRTC interrupt - * @interrupt_params: used for determining the CRTC instance + * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling + * @adev: amdgpu device + * @acrtc: the CRTC to service * - * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK - * event handler. + * Performs writeback completion, vblank event handling, CRC processing, VRR BTR + * updates and pageflip completion delivery. + * + * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from + * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank + * from dm_crtc_high_irq(). */ -static void dm_crtc_high_irq(void *interrupt_params) +static void dm_crtc_high_irq_handler(struct amdgpu_device *adev, + struct amdgpu_crtc *acrtc) { - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; struct drm_writeback_job *job; - struct amdgpu_crtc *acrtc; unsigned long flags; int vrr_active; - - acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); - if (!acrtc) - return; + bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0; if (acrtc->wb_conn) { spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags); @@ -694,12 +630,17 @@ static void dm_crtc_high_irq(void *interrupt_params) vrr_active, acrtc->dm_irq_params.active_planes); /** - * Core vblank handling at start of front-porch is only possible - * in non-vrr mode, as only there vblank timestamping will give - * valid results while done in front-porch. Otherwise defer it - * to dm_vupdate_high_irq after end of front-porch. + * Core vblank handling. + * + * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch + * point, which is the correct place to timestamp both VRR and non-VRR + * vblanks. + * + * On DCE this handler runs at the start of front-porch, where only + * non-VRR timestamping is valid; VRR vblank is deferred to + * dm_vupdate_high_irq() after end of front-porch. */ - if (!vrr_active) + if (is_dcn || !vrr_active) amdgpu_dm_crtc_handle_vblank(acrtc); /** @@ -732,18 +673,16 @@ static void dm_crtc_high_irq(void *interrupt_params) } /* - * If there aren't any active_planes then DCH HUBP may be clock-gated. - * In that case, pageflip completion interrupts won't fire and pageflip - * completion events won't get delivered. Prevent this by sending - * pending pageflip events from here if a flip is still pending. + * Deliver pageflip completion events (DCN only). + * + * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch + * point. Deliver any pending pageflip completion event from here. * - * If any planes are enabled, use dm_pflip_high_irq() instead, to - * avoid race conditions between flip programming and completion, - * which could cause too early flip completion events. + * NOTE: This can deliver an event for a flip that was armed but not yet + * programmed into HW; that race is closed in a follow-up change by + * checking the programmed flip status. */ - if (adev->family >= AMDGPU_FAMILY_RV && - acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && - acrtc->dm_irq_params.active_planes == 0) { + if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) { if (acrtc->event) { drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); acrtc->event = NULL; @@ -755,6 +694,104 @@ static void dm_crtc_high_irq(void *interrupt_params) spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); } +static void dm_vupdate_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + struct drm_device *drm_dev; + struct drm_vblank_crtc *vblank; + ktime_t frame_duration_ns, previous_timestamp; + unsigned long flags; + int vrr_active; + + acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE); + if (!acrtc) + return; + + vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); + drm_dev = acrtc->base.dev; + vblank = drm_crtc_vblank_crtc(&acrtc->base); + previous_timestamp = atomic64_read(&irq_params->previous_timestamp); + frame_duration_ns = vblank->time - previous_timestamp; + + if (frame_duration_ns > 0) { + trace_amdgpu_refresh_rate_track(acrtc->base.index, + frame_duration_ns, + ktime_divns(NSEC_PER_SEC, frame_duration_ns)); + atomic64_set(&irq_params->previous_timestamp, vblank->time); + } + + drm_dbg_vbl(drm_dev, + "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, + vrr_active); + + /* + * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver + * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are + * not used. Run the full handler here. + */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { + dm_crtc_high_irq_handler(adev, acrtc); + return; + } + + /* DCE only below. */ + + /* Core vblank handling is done here after end of front-porch in + * vrr mode, as vblank timestamping will give valid results + * while now done after front-porch. This will also deliver + * page-flip completion events that have been queued to us + * if a pageflip happened inside front-porch. + */ + if (vrr_active && acrtc->dm_irq_params.stream) { + bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; + bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; + bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state + == VRR_STATE_ACTIVE_VARIABLE; + + amdgpu_dm_crtc_handle_vblank(acrtc); + + /* BTR processing for pre-DCE12 ASICs */ + if (adev->family < AMDGPU_FAMILY_AI) { + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + mod_freesync_handle_v_update( + adev->dm.freesync_module, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params); + + if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { + schedule_dc_vmin_vmax(adev, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params.adjust); + } + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); + } + } +} + +/** + * dm_crtc_high_irq() - Handles CRTC interrupt + * @interrupt_params: used for determining the CRTC instance + * + * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler. + * + * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is + * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq(). + */ +static void dm_crtc_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + + acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); + if (!acrtc) + return; + + dm_crtc_high_irq_handler(adev, acrtc); +} + #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) /** * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for @@ -3059,6 +3096,13 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, */ if (!dc_interrupt_set(adev->dm.dc, irq_source, enable)) drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis"); + + } else if (acrtc && state->stream_status[i].plane_count != 0) { + /* DCN only needs to toggle VUPDATE_NO_LOCK */ + rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, enable); + if (rc) + drm_warn(adev_to_drm(adev), "Failed to %sable vupdate interrupt\n", + enable ? "en" : "dis"); } } @@ -4558,38 +4602,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) * for acknowledging and handling. */ - /* Use VSTARTUP interrupt */ - for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP; - i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1; - i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq); - - if (r) { - drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 || - int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) { - drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_crtc_high_irq, c_irq_params)) - return -ENOMEM; - } - /* Use otg vertical line interrupt */ #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) { @@ -4661,37 +4673,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) return -ENOMEM; } - /* Use GRPH_PFLIP interrupt */ - for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT; - i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1; - i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST || - int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) { - drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_pflip_high_irq, c_irq_params)) - return -ENOMEM; - } - /* HPD */ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT, &adev->hpd_irq); @@ -9118,14 +9099,22 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, drm_crtc_vblank_on_config(&acrtc->base, &config); - /* Allow RX6xxx, RX7700, RX7800 GPUs to call amdgpu_irq_get.*/ + /* + * Since pflip_high_irq is no longer registered for DCN, grab an + * extra reference to vupdate irq instead to workaround this + * issue: + * https://gitlab.freedesktop.org/drm/amd/-/work_items/3936 + * + * The callbacks to drm_vblank_on/off should really take care of + * this though. + */ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { case IP_VERSION(3, 0, 0): case IP_VERSION(3, 0, 2): case IP_VERSION(3, 0, 3): case IP_VERSION(3, 2, 0): - if (amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type)) - drm_err(dev, "DM_IRQ: Cannot get pageflip irq!\n"); + if (amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type)) + drm_err(dev, "DM_IRQ: Cannot get vupdate irq!\n"); #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) if (amdgpu_irq_get(adev, &adev->vline0_irq, irq_type)) drm_err(dev, "DM_IRQ: Cannot get vline0 irq!\n"); @@ -9143,8 +9132,8 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, if (amdgpu_irq_put(adev, &adev->vline0_irq, irq_type)) drm_err(dev, "DM_IRQ: Cannot put vline0 irq!\n"); #endif - if (amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type)) - drm_err(dev, "DM_IRQ: Cannot put pageflip irq!\n"); + if (amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type)) + drm_err(dev, "DM_IRQ: Cannot put vupdate irq!\n"); } drm_crtc_vblank_off(&acrtc->base); @@ -9157,6 +9146,10 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev, int irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id); + /* GRPH_PFLIP is not used on DCN; nothing to reapply. */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) + return; + /** * This reads the current state for the IRQ and force reapplies * the setting to hardware. @@ -9488,9 +9481,13 @@ static void update_stream_irq_parameters( static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state, struct dm_crtc_state *new_state) { + struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev); bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state); bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state); + /* Only DCE gates vupdate on VRR, keep it enabled for DCN */ + bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0; + if (!old_vrr_active && new_vrr_active) { /* Transition VRR inactive -> active: * While VRR is active, we must not disable vblank irq, as a @@ -9500,7 +9497,8 @@ static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state, * We also need vupdate irq for the actual core vblank handling * at end of vblank. */ - WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0); + if (vrr_gates_vupdate) + WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0); WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0); drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n", __func__, new_state->base.crtc->base.id); @@ -9508,7 +9506,8 @@ static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state, /* Transition VRR active -> inactive: * Allow vblank irq disable again for fixed refresh rate. */ - WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0); + if (vrr_gates_vupdate) + WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0); drm_crtc_vblank_put(new_state->base.crtc); drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n", __func__, new_state->base.crtc->base.id); @@ -9683,6 +9682,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state); bool cursor_update = false; bool pflip_present = false; + bool immediate_flip = false; bool dirty_rects_changed = false; bool updated_planes_and_streams = false; struct { @@ -9848,6 +9848,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, acrtc_state->update_type == UPDATE_TYPE_FAST && get_mem_type(old_plane_state->fb) == get_mem_type(fb); + immediate_flip |= bundle->flip_addrs[planes_count].flip_immediate; + timestamp_ns = ktime_get_ns(); bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000); bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count]; @@ -10049,6 +10051,29 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) amdgpu_dm_commit_cursors(state); + /* + * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK. + * However, an immediate (tearing / async) flip is latched by HW right + * away and does not wait for the next vupdate, so deliver its + * completion event here after programming. + * + * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so + * this is DCN-only. + */ + if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) { + spin_lock_irqsave(&pcrtc->dev->event_lock, flags); + if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED && + acrtc_attach->event) { + drm_crtc_accurate_vblank_count(&acrtc_attach->base); + drm_crtc_send_vblank_event(&acrtc_attach->base, + acrtc_attach->event); + acrtc_attach->event = NULL; + drm_crtc_vblank_put(&acrtc_attach->base); + acrtc_attach->pflip_status = AMDGPU_FLIP_NONE; + } + spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); + } + cleanup: kfree(bundle); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 2e7ee77c010e1..fcaf39d1c3ef5 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -327,7 +327,14 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) drm_crtc_vblank_restore(crtc); } - if (dc_supports_vrr(dm->dc->ctx->dce_version)) { + /* + * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver + * vblank and pageflip completion events, so enable it whenever vblank + * is enabled. On DCE, vupdate is only needed in VRR mode. + */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { + rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable); + } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) { if (enable) { /* vblank irq on -> Only need vupdate irq in vrr mode */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) @@ -338,39 +345,46 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) } } - if (rc) - return rc; - - /* crtc vblank or vstartup interrupt */ - if (enable) { - rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc); - } else { - rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc); - } - if (rc) return rc; /* - * hubp surface flip interrupt - * - * We have no guarantee that the frontend index maps to the same - * backend index - some even map to more than one. - * - * TODO: Use a different interrupt or check DC itself for the mapping. + * VLINE0 (crtc_irq) and GRPH_PFLIP (pageflip_irq) are only used on + * DCE. On DCN, vblank and pageflip completion are delivered from + * VUPDATE_NO_LOCK (enabled above), so don't touch them here. */ - if (enable) { - rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc); - } else { - rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc); - } + if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { + /* crtc vblank or vstartup interrupt */ + if (enable) { + rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc); + } - if (rc) - return rc; + if (rc) + return rc; + + /* + * hubp surface flip interrupt + * + * We have no guarantee that the frontend index maps to the same + * backend index - some even map to more than one. + * + * TODO: Use a different interrupt or check DC itself for the mapping. + */ + if (enable) { + rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc); + } + + if (rc) + return rc; + } #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) /* crtc vline0 interrupt, only available on DCN+ */ From 51ea665c30c424c98959101f3bf6f48ab42949c8 Mon Sep 17 00:00:00 2001 From: Andriy Korud Date: Fri, 10 Jul 2026 12:52:26 +0200 Subject: [PATCH 2609/3297] drm/amd/display: dce100: skip non-DP stream encoders for DP MST commit d340cba0df4cf327c7e89c7c1a4e79d4771d7dd5 upstream. On DCE8-class ASICs (e.g. Bonaire), the resource pool contains digital DIG stream encoders plus one analog DAC encoder. When assigning a stream encoder for a second DisplayPort MST stream, if the preferred digital encoder is already acquired, dce100_find_first_free_match_stream_enc_for_link() falls back to the first free pool entry. That entry may be the analog encoder, whose funcs table lacks DP hooks such as dp_set_stream_attribute. The subsequent atomic commit then dereferences NULL function pointers in link_set_dpms_on() and crashes. Skip encoders without dp_set_stream_attribute when the stream uses a DP signal (including MST). Use dc_is_dp_signal(stream->signal) for the MST fallback path instead of checking only the link connector signal. Tested on: - GPU: AMD Radeon R7 260X (Bonaire / DCE8) - Board: Supermicro C9X299-PG300 - Setup: DP MST daisy chain, hotplug second monitor or have it connected on boot - Kernel: 7.1.3 (issue observed since 6.19) - Result: kernel oops without patch; dual monitors stable with patch Signed-off-by: Andriy Korud Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5162 Signed-off-by: Alex Deucher (cherry picked from commit 28ec64943e3ee4d9b8d30cea61e380f1429953a8) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../drm/amd/display/dc/resource/dce100/dce100_resource.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c index ef146f253577a..91fd73f4a96cf 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c @@ -957,6 +957,11 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link( for (i = 0; i < pool->stream_enc_count; i++) { if (!res_ctx->is_stream_enc_acquired[i] && pool->stream_enc[i]) { + /* DP/MST needs a digital encoder; skip analog/no-DP encoders */ + if (dc_is_dp_signal(stream->signal) && + (!pool->stream_enc[i]->funcs || + !pool->stream_enc[i]->funcs->dp_set_stream_attribute)) + continue; /* Store first available for MST second display * in daisy chain use case */ @@ -980,7 +985,7 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link( * required for non DP connectors. */ - if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) + if (j >= 0 && dc_is_dp_signal(stream->signal)) return pool->stream_enc[j]; return NULL; From 9c0432044d34bf629f3c0dec3251128e7c8451fe Mon Sep 17 00:00:00 2001 From: Alessandro Rinaldi Date: Fri, 26 Jun 2026 16:36:00 +0200 Subject: [PATCH 2610/3297] drm/amd/display: Force PWM backlight on Lenovo Legion 5 15ARH05 commit bad177fa75e607e396cd57daaaed881450d7a471 upstream. The Lenovo Legion 5 15ARH05 (Renoir) ships a BOE 0x08DF eDP panel that advertises AUX/DPCD backlight control, so amdgpu's automatic detection (amdgpu_backlight == -1) selects AUX. On this panel the AUX backlight path has no effect: brightness writes are accepted but the panel level never changes, the display is stuck at a fixed brightness and max_brightness is reported as a bogus 511000. As a result neither the desktop brightness slider nor the brightness hotkeys do anything. Forcing PWM backlight (amdgpu.backlight=0) restores working control: max_brightness becomes 65535 and the level tracks writes. This has long been applied by users as a manual kernel-parameter workaround. Extend the generic panel backlight quirk with a force_pwm flag, add an entry for the Legion 5 15ARH05 / BOE 0x08DF panel, and have amdgpu disable AUX backlight (use PWM) when the quirk matches and the user lets the driver auto-select the backlight type. Signed-off-by: Alessandro Rinaldi Tested-by: Alessandro Rinaldi Reviewed-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 81b39f43e7e53589491e2eef6bad5389626b4b9c) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 7 +++++-- drivers/gpu/drm/drm_panel_backlight_quirks.c | 9 +++++++++ include/drm/drm_utils.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index add471b150d42..1b5f4077e3c42 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3724,6 +3724,8 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps; caps->aux_support = false; + panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid); + if (caps->ext_caps->bits.oled == 1 /* * || @@ -3736,6 +3738,9 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) caps->aux_support = false; else if (amdgpu_backlight == 1) caps->aux_support = true; + else if (!IS_ERR_OR_NULL(panel_backlight_quirk) && + panel_backlight_quirk->force_pwm) + caps->aux_support = false; if (caps->aux_support) aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX; @@ -3751,8 +3756,6 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) else caps->aux_min_input_signal = 1; - panel_backlight_quirk = - drm_get_panel_backlight_quirk(aconnector->drm_edid); if (!IS_ERR_OR_NULL(panel_backlight_quirk)) { if (panel_backlight_quirk->min_brightness) { caps->min_input_signal = diff --git a/drivers/gpu/drm/drm_panel_backlight_quirks.c b/drivers/gpu/drm/drm_panel_backlight_quirks.c index 537dc6dd05343..2d0238382ebd5 100644 --- a/drivers/gpu/drm/drm_panel_backlight_quirks.c +++ b/drivers/gpu/drm/drm_panel_backlight_quirks.c @@ -21,6 +21,15 @@ struct drm_get_panel_backlight_quirk { }; static const struct drm_get_panel_backlight_quirk drm_panel_min_backlight_quirks[] = { + /* Lenovo Legion 5 15ARH05, AUX backlight non-functional, force PWM */ + { + .dmi_match.field = DMI_SYS_VENDOR, + .dmi_match.value = "LENOVO", + .dmi_match_other.field = DMI_PRODUCT_VERSION, + .dmi_match_other.value = "Lenovo Legion 5 15ARH05", + .ident.panel_id = drm_edid_encode_panel_id('B', 'O', 'E', 0x08df), + .quirk = { .force_pwm = true, }, + }, /* 13 inch matte panel */ { .dmi_match.field = DMI_BOARD_VENDOR, diff --git a/include/drm/drm_utils.h b/include/drm/drm_utils.h index 6a46f755daba0..7e077484c5bbf 100644 --- a/include/drm/drm_utils.h +++ b/include/drm/drm_utils.h @@ -19,6 +19,7 @@ int drm_get_panel_orientation_quirk(int width, int height); struct drm_panel_backlight_quirk { u16 min_brightness; u32 brightness_mask; + bool force_pwm; }; const struct drm_panel_backlight_quirk * From 490ceacd2162de919a142bcb4eff363bb493b1de Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 29 Jun 2026 15:27:00 -0500 Subject: [PATCH 2611/3297] drm/amd/display: Fix backlight max_brightness to match exported range commit f1b5d8f9cc54ae8a2567ac126867ae488e1bf625 upstream. [Why] FWTS autobrightness fails on eDP panels because actual_brightness can read higher than the advertised max_brightness (e.g. 63576 vs 62451). The conversion helpers expose the firmware PWM range to userspace as [0..max]. But max_brightness is advertised as (max - min), which is smaller. So reading the level can return a value above max_brightness. This regressed in commit 4b61b8a39051 ("drm/amd/display: Add debugging message for brightness caps"), which changed max_brightness to (max - min) and undid commit 8dbd72cb7900 ("drm/amd/display: Export full brightness range to userspace"). [How] Advertise max_brightness as max, and scale the initial AC/DC brightness against max too. Update the KUnit expectations to match. Fixes: 4b61b8a39051 ("drm/amd/display: Add debugging message for brightness caps") Reviewed-by: Alex Hung Signed-off-by: Mario Limonciello Signed-off-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit bd9e2b5b0473c75abc0f4134dfe79ecbfb16610d) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 1b5f4077e3c42..bc68243256f35 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -5204,11 +5204,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector) caps = &dm->backlight_caps[aconnector->bl_idx]; if (get_brightness_range(caps, &min, &max)) { if (power_supply_is_system_supplied() > 0) - props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100); + props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100); else - props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100); + props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100); /* min is zero, so max needs to be adjusted */ - props.max_brightness = max - min; + props.max_brightness = max; drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max, caps->ac_level, caps->dc_level); } else From fd2de80f28a0796254f45a437f26a87d59db8d14 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 8 Jul 2026 22:15:20 -0500 Subject: [PATCH 2612/3297] drm/amdgpu: Disable PCIe dynamic speed switching on Ryzen Pinnacle Ridge commit 0148ac33547b9af1c5a7f3bb6e5baffcb6e9fac2 upstream. AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs have PCI controllers that don't support PCIe dynamic speed switching, causing system freezes during GPU initialization when enabled. Disable dynamic speed switching when this CPU is detected. Assisted-by: Claude:sonnet Fixes: 466a7d115326 ("drm/amd: Use the first non-dGPU PCI device for BW limits") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5436 Reviewed-by: Lijo Lazar Link: https://patch.msgid.link/20260709031520.841611-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 9ceb4e034a327a04155f32f1cd1a5031dfa5fe02) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 653f98b447b5c..c3e9b916f08f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1878,6 +1878,15 @@ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device if (c->x86_vendor == X86_VENDOR_INTEL) return false; + + /* + * AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs don't + * support PCIe dynamic speed switching. + * https://gitlab.freedesktop.org/drm/amd/-/work_items/5436 + */ + if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 0x17 && + c->x86_model == 0x08) + return false; #endif return true; } From ba7b6444097a73ccd3d3ac9e2be4ebb73d226460 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 1 Jul 2026 18:53:21 +0800 Subject: [PATCH 2613/3297] drm/amdgpu: fix bo->pin leaking in amdgpu_bo_create_reserved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a2f895f3c852063258d62e9f74b081de07ca95df upstream. amdgpu_bo_create_reserved() only allocates a new BO when *bo_ptr (struct amdgpu_bo **bo_ptr as input parameter) is NULL, it simply skips creation when *bo_ptr is non-NULL. But it unconditionally reserves, pins, gart allocates and maps the BO afterwards. When the same non-NULL BO pointer is passed in again, for example firmware buffers that live in adev and are re-loaded on every resume / cp_resume / start under AMDGPU_FW_LOAD_DIRECT, amdgpu_bo_pin() just increases pin_count unconditionally, however the matching teardown only unpins once, so pin_count never drops to zero, so TTM is not able to move, swap or evict a BO, causing BO leaks. This commit fixes this issue by only pinning the bo once at creation, and repeated calls no longer take additional pin references. Signed-off-by: Zhu Lingshan Reviewed-by: Alex Deucher Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 3ddc0ae76202c447b6aec61e907b852bc94671cf) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index e08f58de4b17f..cdab2c098cfab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -284,10 +284,12 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev, goto error_free; } - r = amdgpu_bo_pin(*bo_ptr, domain); - if (r) { - dev_err(adev->dev, "(%d) kernel bo pin failed\n", r); - goto error_unreserve; + if (free) { + r = amdgpu_bo_pin(*bo_ptr, domain); + if (r) { + dev_err(adev->dev, "(%d) kernel bo pin failed\n", r); + goto error_unreserve; + } } r = amdgpu_ttm_alloc_gart(&(*bo_ptr)->tbo); @@ -310,7 +312,8 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev, return 0; error_unpin: - amdgpu_bo_unpin(*bo_ptr); + if (free) + amdgpu_bo_unpin(*bo_ptr); error_unreserve: amdgpu_bo_unreserve(*bo_ptr); From ee44ea4f7e309191790875f8bb88e61d91eae370 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 23 Jul 2026 14:01:59 -0400 Subject: [PATCH 2614/3297] drm/amd/display: Fix flip-done timeouts on mode1 reset commit 82730dba0cf9d9524af0ceeb7eb6b5c3ab1bdb87 upstream. The vblank on/off callbacks mixed use of amdgpu_irq_get/put() and amdgpu_dm_crtc_set_vupdate_irq() to enable and disable IRQs. With get/put, base driver will callback into DC to disable IRQs when refcount == 0. With set_vupdate_irq(), DC is called directly to disable IRQs, bypassing base driver's refcount tracking. During gpu reset, base driver can restore IRQs via amdgpu_irq_gpu_reset_resume_helper() > amdgpu_irq_update(). So if get/put() is not used (i.e. refcount == 0), then vupdate_irq will be disabled. This is problematic if DRM requests vblank on before amdgpu_irq_update() is called: drm_vblank_on() > set_vupdate_irq() enables vupdate_irq, but the refcount is still 0. gpu_reset_resume_helper() > irq_update() then immediately disables it, thus leading to flip done timeouts. This is made worse on DCN since VUPDATE_NO_LOCK is the only IRQ enabled. Prior to 8382cd234981, a combination of GRPH_FLIP and VSTARTUP IRQs were used, and they used get/put(). This explains why 8382cd234981 exposed this issue. Fix by using get/put() instead of set_vupdate_irq(). DCE is unchanged, since it relies on unbalanced enable/disable calls based on VRR status, and hence requires direct set_vupdate_irq(). Plus, it also uses GRPH_FLIP and VLINE IRQs, which are properly tracked by get/put(). Cc: stable@vger.kernel.org Fixes: 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock") Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260723180159.52121-1-sunpeng.li@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index fcaf39d1c3ef5..cc23d45815c36 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -333,10 +333,19 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) * is enabled. On DCE, vupdate is only needed in VRR mode. */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { - rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable); + if (enable) { + rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc); + } } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) { if (enable) { - /* vblank irq on -> Only need vupdate irq in vrr mode */ + /* vblank irq on -> Only need vupdate irq in vrr mode + * Not ref-counted since we need explicit enable/disable + * for DCE VRR handling + */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true); } else { From a38f2724eb93a78ba250b01e0caf3468df4d3956 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 20 Jul 2026 16:44:26 +0800 Subject: [PATCH 2615/3297] drm/vc4: Shut down BO cache timer before teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6273dd3ffb54ec581855b82ae77331b66028249c upstream. The BO cache timer callback schedules time_work, and time_work can rearm the timer through vc4_bo_cache_free_old(). vc4_bo_cache_destroy() deletes the timer and then cancels the work, which does not break that cycle: the work being cancelled can rearm the timer, and the timer then queues work again after teardown. Use timer_shutdown_sync() instead, so the timer cannot be rearmed and the cycle ends with cancel_work_sync(). Fixes: c826a6e10644 ("drm/vc4: Add a BO cache.") Cc: stable@vger.kernel.org Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260720084426.1632508-1-lilinmao@kylinos.cn Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vc4/vc4_bo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index a1efda9c39f92..3bb4a988a8125 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -1046,7 +1046,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused) struct vc4_dev *vc4 = to_vc4_dev(dev); int i; - timer_delete(&vc4->bo_cache.time_timer); + timer_shutdown_sync(&vc4->bo_cache.time_timer); cancel_work_sync(&vc4->bo_cache.time_work); vc4_bo_cache_purge(dev); From 2b85e19792be4fda511f23b6b7a69b4fba23bca1 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 23 Jul 2026 09:44:50 -0400 Subject: [PATCH 2616/3297] drm/amd/display: Fix missing DCE check in dm_gpureset_toggle_interrupts() commit fbbaca9e208733652828ea98d00f09f260a7770e upstream. This line was lost when cping from amd-staging-drm-next to drm-fixes. So add it back. Cc: stable@vger.kernel.org Fixes: 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock") Reported-by: Lu Yao Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260723134450.13838-1-sunpeng.li@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index bc68243256f35..99759559cc0f6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3068,7 +3068,8 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, acrtc = get_crtc_by_otg_inst( adev, state->stream_status[i].primary_otg_inst); - if (acrtc && state->stream_status[i].plane_count != 0) { + if (acrtc && state->stream_status[i].plane_count != 0 && + amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; if (rc) From b1379f0c42b88cb60b9f3757eb5d1e73ad460ed8 Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Tue, 23 Jun 2026 14:33:14 -0500 Subject: [PATCH 2617/3297] drm/vmwgfx: Validate vmw_surface_metadata::array_size commit a4f55260f7f7d4dc4d0ee55063dfb0c457b77991 upstream. This field comes from userspace and should be validated against specific limits depending on which Shader Model (SM) is available. Fixes: 504901dbb0b5 ("drm/vmwgfx: Refactor surface_define to use vmw_surface_metadata") Reported-by: Zero Day Initiative Cc: stable@vger.kernel.org Signed-off-by: Ian Forbes Reviewed-by: Maaz Mombasawala Signed-off-by: Zack Rusin Link: https://patch.msgid.link/20260623193314.506257-1-ian.forbes@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 7e281c3c6bc58..8e01e13eebf4d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -77,7 +77,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res, static int vmw_gb_surface_destroy(struct vmw_resource *res); static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv); static int @@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data, */ static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv) { @@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct drm_device *dev, req->base.svga3d_flags); /* array_size must be null for non-GL3 host. */ - if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) { - VMW_DEBUG_USER("SM4 surface not supported.\n"); - return -EINVAL; + if (req->base.array_size > 0) { + if (has_sm5_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else if (has_sm4_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else { + VMW_DEBUG_USER("SM4+ surface not supported.\n"); + return -EINVAL; + } } if (!has_sm4_1_context(dev_priv)) { From 6deaa317201851c644c431b57682e54d06b35838 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Tue, 21 Jul 2026 09:15:58 +0800 Subject: [PATCH 2618/3297] drm/vc4: Prevent shader BO mappings from becoming writable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0c9e6367639548307d3f578f6943ce72c9d39087 upstream. vc4_gem_object_mmap() rejects a writable mapping of a validated shader BO, but leaves VM_MAYWRITE set. Userspace can map the BO read-only and then turn it writable with mprotect(). Validated shader BOs must stay read-only: the validator checks the instructions once and the GPU trusts them afterwards. A writable mapping lets userspace rewrite the code after validation, bypassing the validator. Clear VM_MAYWRITE on the read-only path so the mapping cannot be upgraded, as i915 already does for its read-only objects. Fixes: 463873d57014 ("drm/vc4: Add an API for creating GPU shaders in GEM BOs.") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://lore.kernel.org/dri-devel/20260720085554.B0AF01F000E9@smtp.kernel.org/ Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260721011558.1672477-1-lilinmao@kylinos.cn Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vc4/vc4_bo.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 3bb4a988a8125..cd7996592db37 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -733,9 +733,13 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct { struct vc4_bo *bo = to_vc4_bo(obj); - if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { - DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); - return -EINVAL; + if (bo->validated_shader) { + if (vma->vm_flags & VM_WRITE) { + DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); + return -EINVAL; + } + + vm_flags_clear(vma, VM_MAYWRITE); } mutex_lock(&bo->madv_lock); From a2212fef8e18724e06432fce01fa257296d9f053 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 14 Jul 2026 10:00:25 -0700 Subject: [PATCH 2619/3297] drm/gpusvm: Fix MM reference leak in drm_gpusvm_range_evict commit 847b371debf3c8c72384ab7b9a0c4123a74cc925 upstream. If kvmalloc_array() fails in drm_gpusvm_range_evict(), the MM reference acquired earlier is not released, resulting in a reference leak. Fix this by dropping the MM reference on the kvmalloc_array() failure path. Fixes: 99624bdff867 ("drm/gpusvm: Add support for GPU Shared Virtual Memory") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260714170025.3487974-1-matthew.brost@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_gpusvm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 4cde5bf4aedd8..781cd4e5b704f 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1566,8 +1566,10 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm, return -EFAULT; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); - if (!pfns) + if (!pfns) { + mmput(mm); return -ENOMEM; + } hmm_range.hmm_pfns = pfns; while (!time_after(jiffies, timeout)) { From a096a6aba601152a45d4e272207bd37e03ca3f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Sat, 18 Jul 2026 10:44:36 -0300 Subject: [PATCH 2620/3297] drm/v3d: Reach the GMP through the hub registers on V3D 7.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f7df2da0d1c375c1c4c70e1e0b569592de0d6b73 upstream. v3d_idle_axi() drains the GPU's memory interface for a safe powerdown by using the V3D_GMP_CFG register. It reached both registers with the macros V3D_CORE_READ and V3D_CORE_WRITE. On V3D 7.x the GMP is no longer a per-core block; it lives in the hub register region. Reaching it through the per-core register block addresses the wrong region. Select the hub accessors (V3D_{READ,WRITE}) for the GMP on V3D 7.x and keep the per-core path for earlier generations. Cc: stable@vger.kernel.org Fixes: 0ad5bc1ce463 ("drm/v3d: fix up register addresses for V3D 7.x") Link: https://patch.msgid.link/20260718-v3d-pm-axi-transactions-v1-1-4ecd7729ed70@igalia.com Reviewed-by: Iago Toral Quiroga Signed-off-by: Maíra Canal Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/v3d/v3d_gem.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index ab29dde223618..b499f6635cd24 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -45,6 +45,18 @@ v3d_init_hw_state(struct v3d_dev *v3d) static void v3d_idle_axi(struct v3d_dev *v3d, int core) { + if (v3d->ver >= V3D_GEN_71) { + V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); + + if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) & + (V3D_GMP_STATUS_RD_COUNT_MASK | + V3D_GMP_STATUS_WR_COUNT_MASK | + V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) { + drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n"); + } + return; + } + V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) & From 73bd2779865372b1017d4f555b45270aa2d0d710 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:06 +0300 Subject: [PATCH 2621/3297] media: airspy: Return queued buffers on start_streaming() failure commit 04344d0b4929caa94c0df72f767752aa0935ef5d upstream. The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. airspy_start_streaming() returned -ENODEV early when the USB device had been disconnected (s->udev == NULL) without returning any buffers that buf_queue() had already accepted. Take v4l2_lock first and jump to the existing err_clear_bit label, which already drains s->queued_bufs via vb2_buffer_done(..., VB2_BUF_STATE_QUEUED) before unlocking. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 634fe5033951 ("[media] airspy: AirSpy SDR driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/airspy/airspy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 08f0920cf6ca2..7bad5d8ea186a 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -522,11 +522,13 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(s->dev, "\n"); - if (!s->udev) - return -ENODEV; - mutex_lock(&s->v4l2_lock); + if (!s->udev) { + ret = -ENODEV; + goto err_clear_bit; + } + s->sequence = 0; set_bit(POWER_ON, &s->flags); From 391fe3e36e59f3c6e3d46edfb3a5de51e00cd216 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:11 +0000 Subject: [PATCH 2622/3297] media: amlogic-c3: Add validations for ae and awb config commit 9724164f71974a2a44a5e026614fbcc05bab6d91 upstream. Avoid invalid memory access if the zones_num is bigger than zone_weight. This patch fixes the following smatch errors: drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max Cc: stable@vger.kernel.org Fixes: fb2e135208f3 ("media: platform: Add C3 ISP driver") Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Keke Li Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/amlogic/c3/isp/c3-isp-params.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c index c80667dd76621..ec25299e52abc 100644 --- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c @@ -107,6 +107,8 @@ static void c3_isp_params_awb_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AWB_MAX_ZONES) + zones_num = C3_ISP_AWB_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { @@ -223,6 +225,8 @@ static void c3_isp_params_ae_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AE_MAX_ZONES) + zones_num = C3_ISP_AE_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { From 0459a4304cff8e702a5a2bf6060a9e23061796aa Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 11:23:30 +0000 Subject: [PATCH 2623/3297] media: aspeed: fix missing of_reserved_mem_device_release() on probe failure commit 253c8ef7d57da0c74db251f385324faaa5ae2257 upstream. aspeed_video_init() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. When aspeed_video_setup_video() subsequently fails in aspeed_video_probe(), the error path frees the JPEG buffer and unprepares the clocks but does not release the reserved memory association, leaking the rmem_assigned_device entry on the global list. The normal remove path already calls of_reserved_mem_device_release() correctly; only the probe error path was missing it. Add the missing of_reserved_mem_device_release() call to the aspeed_video_setup_video() failure cleanup. Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/aspeed/aspeed-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index b83e43245277b..88ccd2f374698 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -2327,6 +2327,7 @@ static int aspeed_video_probe(struct platform_device *pdev) rc = aspeed_video_setup_video(video); if (rc) { aspeed_video_free_buf(video, &video->jpeg); + of_reserved_mem_device_release(&pdev->dev); clk_unprepare(video->vclk); clk_unprepare(video->eclk); return rc; From 6efe665356ec81221928cbcca6e89e66f48bc7db Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Fri, 24 Apr 2026 23:36:01 +0900 Subject: [PATCH 2624/3297] media: cec: seco: unregister adapter on IR probe failure commit c3a78691be8245e52ced489f268e413f18061ac2 upstream. If secocec_ir_probe() fails after cec_register_adapter() succeeds, probe returns an error and the driver remove callback is not called. The current unwind path unregisters the notifier and then falls through to cec_delete_adapter(), which violates the CEC adapter lifetime rules after a successful registration. Add a registered-adapter unwind path that unregisters the notifier and the adapter instead. Fixes: daef95769b3a ("media: seco-cec: add Consumer-IR support") Cc: stable@vger.kernel.org Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/cec/platform/seco/seco-cec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index b7bb49f023957..97ed9654c78a1 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -649,7 +649,7 @@ static int secocec_probe(struct platform_device *pdev) ret = secocec_ir_probe(secocec); if (ret) - goto err_notifier; + goto err_unregister_adapter; platform_set_drvdata(pdev, secocec); @@ -657,6 +657,10 @@ static int secocec_probe(struct platform_device *pdev) return ret; +err_unregister_adapter: + cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); + cec_unregister_adapter(secocec->cec_adap); + goto err; err_notifier: cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); err_delete_adapter: From 73504935e4365d931a367f23f9b7c29c2722e750 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 6 May 2026 21:41:16 +0900 Subject: [PATCH 2625/3297] media: cedrus: clean up media device on probe failure commit 2c869b6969f3061cbbdab587f4c0a88bd7fc3cc9 upstream. cedrus_probe() initializes the media device before registering the video device, the media controller, and the media device. If any of those later steps fails, probe returns without calling media_device_cleanup(), so the media device internals initialized by media_device_init() are left behind. Add a media-device cleanup label to the probe unwind path and route video registration failures through it as well. Fixes: 50e761516f2b8c ("media: platform: Add Cedrus VPU decoder driver") Cc: stable@vger.kernel.org Reviewed-by: Paul Kocialkowski Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/sunxi/cedrus/cedrus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 1c7191bb0d8fc..ededa5152decf 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -507,7 +507,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto err_m2m; + goto err_media; } v4l2_info(&dev->v4l2_dev, @@ -533,7 +533,8 @@ static int cedrus_probe(struct platform_device *pdev) v4l2_m2m_unregister_media_controller(dev->m2m_dev); err_video: video_unregister_device(&dev->vfd); -err_m2m: +err_media: + media_device_cleanup(&dev->mdev); v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); From 000e51afb6068bcaf3e51959f030c8002d8110a7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 7 Apr 2026 01:14:02 +0300 Subject: [PATCH 2626/3297] media: cedrus: Fix missing cleanup in error path commit d99732334aaf33b9f93926b70b6a11c2cef3de39 upstream. According to the documentation struct v4l2_fh has to be cleaned up with v4l2_fh_exit() before being freed. [1] Currently there is no actual bug here, when v4l2_fh_exit() isn't called. v4l2_fh_exit() in this case only destroys internal mutex. But it may change in the future, when v4l2_fh_init/v4l2_fh_exit will be enhanced. 1. https://docs.kernel.org/driver-api/media/v4l2-fh.html Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Cc: stable@vger.kernel.org Acked-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/sunxi/cedrus/cedrus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index ededa5152decf..6de345b4ed170 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -391,6 +391,7 @@ static int cedrus_open(struct file *file) err_m2m_release: v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_free: + v4l2_fh_exit(&ctx->fh); kfree(ctx); mutex_unlock(&dev->dev_mutex); From 9924cb548ee7753a6473997949c3ec48092de0b0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 24 Mar 2026 16:08:56 +0800 Subject: [PATCH 2627/3297] media: cedrus: skip invalid H.264 reference list entries commit 10358ea986c3c85516d1c8206486464f79d36e76 upstream. Cedrus consumes H.264 ref_pic_list0/ref_pic_list1 entries from the stateless slice control and later uses their indices to look up decode->dpb[] in _cedrus_write_ref_list(). Rejecting such controls in cedrus_try_ctrl() would break existing userspace, since stateless H.264 reference lists may legitimately carry out-of-range indices for missing references. Instead, guard the actual DPB lookup in Cedrus and skip entries whose indices do not fit the fixed V4L2_H264_NUM_DPB_ENTRIES array. This keeps the fix local to the driver use site and avoids out-of-bounds reads from malformed or unsupported reference list entries. Fixes: e000e1fa4bdbd ("media: uapi: h264: Update reference lists") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Nicolas Dufresne Acked-by: Jernej Skrabec Tested-by: Chen-Yu Tsai Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index 3e2843ef6ccec..fc54d993b11f2 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -210,6 +210,9 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, u8 dpb_idx; dpb_idx = ref_list[i].index; + if (dpb_idx >= V4L2_H264_NUM_DPB_ENTRIES) + continue; + dpb = &decode->dpb[dpb_idx]; if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) From f24ca8b53fe15db40957bdaa40c9aa68e1557bbe Mon Sep 17 00:00:00 2001 From: Brandon Brnich Date: Fri, 20 Mar 2026 13:05:26 -0500 Subject: [PATCH 2628/3297] media: chips-media: wave5: Move src_buf Removal to finish_encode commit b20157147089a9c16a38c7810e2fe6f2df8e3277 upstream. During encoder processing, there is a case where the IRQ response could return the buffer back to userspace via v4l2_m2m_buf_done call. In this time, userspace could queue up this same buffer before start_encode removes the index from the ready queue. This would then lead to a case where the buffer in the ready queue could be a self loop due to the WRITE_ONCE(prev->next, new) call in __list_add. When __list_del is finally called, the loop is already made so nothing points back to ready queue list head and pointers are poisoned. A buffer should not be marked as DONE before the buffer is removed from m2m ready queue. Move removal entirely to finish_encode. Fixes: 9707a6254a8a6 ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Brandon Brnich Tested-by: Jackson Lee Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- .../chips-media/wave5/wave5-vpu-enc.c | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index 24fc0d0d3f4aa..50cba21fdcdce 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -226,13 +226,6 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res) } else { dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n", __func__); - /* - * Remove the source buffer from the ready-queue now and finish - * it in the videobuf2 framework once the index is returned by the - * firmware in finish_encode - */ - if (src_buf) - v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index); } return 0; @@ -259,27 +252,13 @@ static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst) __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index, enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts); - /* - * The source buffer will not be found in the ready-queue as it has been - * dropped after sending of the encode firmware command, locate it in - * the videobuf2 queue directly - */ if (enc_output_info.enc_src_idx >= 0) { - struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx), - enc_output_info.enc_src_idx); - if (vb->state != VB2_BUF_STATE_ACTIVE) - dev_warn(inst->dev->dev, - "%s: encoded buffer (%d) was not in ready queue %i.", - __func__, enc_output_info.enc_src_idx, vb->state); - else - src_buf = to_vb2_v4l2_buffer(vb); - - if (src_buf) { + src_buf = v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, enc_output_info.enc_src_idx); + if (!src_buf) { + dev_warn(inst->dev->dev, "%s: no source buffer found\n", __func__); + } else { inst->timestamp = src_buf->vb2_buf.timestamp; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } else { - dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n", - __func__, enc_output_info.enc_src_idx); } } From f468b7ee5d6332b01e6c538179a4c720e6dae93b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 11:37:27 +0200 Subject: [PATCH 2629/3297] media: cx231xx: fix devres lifetime commit 7d6358ab02866e5b7ed8d3a00805297617bbb0ec upstream. USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes). Fix the driver state lifetime so that it is released on driver unbind. Fixes: 184a82784d50 ("[media] cx231xx: use devm_ functions to allocate memory") Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/cx231xx/cx231xx-cards.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 691f073892b36..d96b9ab276b76 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1575,7 +1575,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->video_mode.end_point_addr, dev->video_mode.num_alt); - dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL); + dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->video_mode.num_alt, GFP_KERNEL); if (dev->video_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1616,7 +1617,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->vbi_mode.num_alt); /* compute alternate max packet sizes for vbi */ - dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL); + dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->vbi_mode.num_alt, GFP_KERNEL); if (dev->vbi_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1658,7 +1660,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, "sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n", dev->sliced_cc_mode.end_point_addr, dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL); + dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->sliced_cc_mode.num_alt, + GFP_KERNEL); if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1722,7 +1726,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); /* allocate memory for our device state and initialize it */ - dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL); + dev = devm_kzalloc(&interface->dev, sizeof(*dev), GFP_KERNEL); if (dev == NULL) { retval = -ENOMEM; goto err_if; @@ -1852,7 +1856,9 @@ static int cx231xx_usb_probe(struct usb_interface *interface, dev->ts1_mode.end_point_addr, dev->ts1_mode.num_alt); - dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL); + dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->ts1_mode.num_alt, + GFP_KERNEL); if (dev->ts1_mode.alt_max_pkt_size == NULL) { retval = -ENOMEM; goto err_video_alt; From c68c4ce72feb6fcccc843eb3baa7af60189ed567 Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Fri, 20 Mar 2026 15:04:53 +0800 Subject: [PATCH 2630/3297] media: cx23885: add ioremap return check and cleanup commit a0701e387b46e2481c05b47f1235b954bfc2af3e upstream. Add a check for the return value of pci_ioremap_bar() in cx23885_dev_setup(). If ioremap for BAR0 fails, release the already allocated PCI memory region, decrement the device count, and return -ENODEV. This prevents a potential null pointer dereference and ensures proper cleanup on memory mapping failure. Fixes: d19770e5178a ("V4L/DVB (6150): Add CX23885/CX23887 PCIe bridge driver") Cc: stable@vger.kernel.org Signed-off-by: Wang Jun <1742789905@qq.com> Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/pci/cx23885/cx23885-core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index a39f445ce22a0..43d658a90f08e 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -990,8 +990,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } /* PCIe stuff */ - dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), - pci_resource_len(dev->pci, 0)); + dev->lmmio = pci_ioremap_bar(dev->pci, 0); + if (!dev->lmmio) { + dev_err(&dev->pci->dev, "CORE %s: can't ioremap MMIO memory\n", + dev->name); + goto err_release_region; + } dev->bmmio = (u8 __iomem *)dev->lmmio; @@ -1096,6 +1100,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } return 0; + +err_release_region: + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); + cx23885_devcount--; + return -ENODEV; } static void cx23885_dev_unregister(struct cx23885_dev *dev) From 7337c88205ed0ffc654f40266be0d3c3eb15fb29 Mon Sep 17 00:00:00 2001 From: Martin Hecht Date: Fri, 8 May 2026 11:59:03 +0200 Subject: [PATCH 2631/3297] media: i2c: alvium: fix critical pointer access in alvium_ctrl_init commit 4f6f28ff24709710c08557c127b3e4c3fb1b4159 upstream. The current implementation of alvium_ctrl_init creates several controls in function alvium_ctrl_init and uses the returned pointer without check. That can cause write access over NULL-pointer for several controls. The reworked code checks the pointers before adding flags. Fixes: 0a7af872915e ("media: i2c: Add support for alvium camera") Cc: stable@vger.kernel.org Signed-off-by: Martin Hecht Signed-off-by: Sakari Ailus Signed-off-by: Greg Kroah-Hartman --- drivers/media/i2c/alvium-csi2.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index 1f088acecf367..8ea5e9951de04 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -2100,20 +2100,21 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_PIXEL_RATE, 0, ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1, ALVIUM_DEFAULT_PIXEL_RATE_MHZ); - ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Link freq is fixed */ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, 0, 0, &alvium->link_freq); - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Auto/manual white balance */ if (alvium->avail_ft.auto_whiteb) { ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + if (ctrls->auto_wb) + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); } ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, @@ -2122,6 +2123,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_bbalance, alvium->inc_bbalance, alvium->dft_bbalance); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, alvium->min_rbalance, @@ -2136,7 +2138,9 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + if (ctrls->auto_exp) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, + V4L2_EXPOSURE_MANUAL, true); } ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, @@ -2145,14 +2149,16 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_exp, alvium->inc_exp, alvium->dft_exp); - ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->exposure) + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; /* Auto/manual gain */ if (alvium->avail_ft.auto_gain) { ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + if (ctrls->auto_gain) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); } if (alvium->avail_ft.gain) { @@ -2162,7 +2168,8 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_gain, alvium->inc_gain, alvium->dft_gain); - ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->gain) + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; } if (alvium->avail_ft.sat) From 00a98fb2a6fb2e3d646f15da6de2de75cbeb17e4 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 21 Feb 2026 01:38:15 +0200 Subject: [PATCH 2632/3297] media: imx219: Fix maximum frame length in lines commit 2c4f1ba7354312ad2d6e34e70a518a51a9344715 upstream. The driver used the maximum frame length in lines value of 0xffff, but the maximum appears to be 0xfffe instead. Fix it. Fixes: 1283b3b8f82b ("media: i2c: Add driver for Sony IMX219 sensor") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Dave Stevenson Reviewed-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 4734c7b4d37b9..00aec91e3d499 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -71,7 +71,7 @@ /* V_TIMING internal */ #define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) -#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_MAX 0xfffe #define IMX219_VBLANK_MIN 32 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_LLP_MIN 0x0d78 From d56044558a7571b03a115cdc21a98f59dd5d80e9 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 18:25:47 +0200 Subject: [PATCH 2633/3297] media: intel/ipu6: Improve DWC PHY HSFREQRANGE band selection for overlapping ranges commit 477620dccf3e9481ed6ae67cb5c747f25751c531 upstream. The get_hsfreq_by_mbps() function searches the freqranges[] table backward (from highest to lowest index). Because adjacent frequency bands overlap, a data rate that falls in the overlap region always lands on the higher-indexed band. For data rates up to 1500 Mbps (index 42) every band uses osc_freq_target 335. Starting at index 43 (1461-1640 Mbps) the osc_freq_target drops to 208. A sensor running at 1498 Mbps sits in the overlap between index 42 (1414-1588, osc 335) and index 43 (1461-1640, osc 208). The backward search picks index 43, programming the lower osc_freq_target of 208 instead of the optimal 335. This causes DDL lock instability and CSI-2 CRC errors on affected configurations, such as the OmniVision OV08X40 sensor on Intel Arrow Lake platforms (Dell Pro Max 16). Rewrite get_hsfreq_by_mbps() to select the optimal band: 1. Among bands whose min/max range covers the data rate, prefer the one with the higher osc_freq_target. 2. If osc_freq_target is equal, prefer the band whose default_mbps is closest to the requested rate. Since the frequency ranges are monotonically increasing, the loop exits early once min exceeds the requested rate. For 1498 Mbps this now correctly selects index 42 (osc_freq_target 335, range 1414-1588) instead of index 43 (osc_freq_target 208, range 1461-1640). Fixes: 1e7eeb301696 ("media: intel/ipu6: add the CSI2 DPHY implementation") Cc: stable@vger.kernel.org Signed-off-by: Marco Nenciarini Signed-off-by: Sakari Ailus Signed-off-by: Greg Kroah-Hartman --- .../media/pci/intel/ipu6/ipu6-isys-dwc-phy.c | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c index db28748434530..237906cb131ac 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c @@ -288,15 +288,27 @@ static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = { static u16 get_hsfreq_by_mbps(u32 mbps) { - unsigned int i = DPHY_FREQ_RANGE_NUM; - - while (i--) { - if (freqranges[i].default_mbps == mbps || - (mbps >= freqranges[i].min && mbps <= freqranges[i].max)) - return i; + u16 best = DPHY_FREQ_RANGE_INVALID_INDEX; + unsigned int i; + + for (i = 0; i < DPHY_FREQ_RANGE_NUM; i++) { + if (mbps > freqranges[i].max) + continue; + + if (mbps < freqranges[i].min) + break; + + if (best == DPHY_FREQ_RANGE_INVALID_INDEX || + freqranges[i].osc_freq_target > + freqranges[best].osc_freq_target || + (freqranges[i].osc_freq_target == + freqranges[best].osc_freq_target && + abs((int)mbps - (int)freqranges[i].default_mbps) < + abs((int)mbps - (int)freqranges[best].default_mbps))) + best = i; } - return DPHY_FREQ_RANGE_INVALID_INDEX; + return best; } static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys, From cf48e9db856529d5d57a0f1806ac0cca81ce0ad5 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 29 May 2026 14:27:10 +0300 Subject: [PATCH 2634/3297] media: iris: Fix use IRQF_NO_AUTOEN when requesting the IRQ commit 45ac2230f929b9f0fae899c4a5c6bf763250fc13 upstream. Requesting the IRQ and then immediately disabling it is fragile as it leaves a window when the IRQ is still enabled although the underlying device might be not completely setup for IRQ handling. Pass IRQF_NO_AUTOEN instead of calling disable_irq_nosync(). Fixes: fb583a214337 ("media: iris: introduce host firmware interface with necessary hooks") Reviewed-by: Konrad Dybcio Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov [bod: Appended Fix to patch title for -stable clarity] [bod: Added cc stable for backporting] Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/qcom/iris/iris_probe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index a197833bac618..cdeb78ba605ae 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -256,12 +256,12 @@ static int iris_probe(struct platform_device *pdev) core->iris_platform_data = of_device_get_match_data(core->dev); ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr, - iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core); + iris_hfi_isr_handler, + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + "iris", core); if (ret) return ret; - disable_irq_nosync(core->irq); - iris_init_ops(core); core->iris_platform_data->init_hfi_command_ops(core); core->iris_platform_data->init_hfi_response_ops(core); From 18e3b838e09d7b7b052febad6987cb72fbd09621 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 17 Apr 2026 14:53:30 +0800 Subject: [PATCH 2635/3297] media: marvell-cam: fix missing pci_disable_device() on remove commit 033ff0420e4c9c240ae5523fff39770298efa964 upstream. During manual code audit, we found that cafe_pci_probe() enables the PCI device with pci_enable_device(), and its probe error path properly calls pci_disable_device() on failure. However, cafe_pci_remove() tears down the controller and frees the driver data without disabling the PCI device, leaving the remove path inconsistent with probe cleanup. Add the missing pci_disable_device() call to cafe_pci_remove(). Fixes: abfa3df36c01 ("[media] marvell-cam: Separate out the Marvell camera core") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/marvell/cafe-driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c index f9796de92aa77..39967ec5e7ee3 100644 --- a/drivers/media/platform/marvell/cafe-driver.c +++ b/drivers/media/platform/marvell/cafe-driver.c @@ -609,6 +609,7 @@ static void cafe_pci_remove(struct pci_dev *pdev) return; } cafe_shutdown(cam); + pci_disable_device(pdev); kfree(cam); } From 1391b75bf0119b5d37f1c1c3078d452a01967f9b Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Wed, 20 May 2026 10:10:41 +0530 Subject: [PATCH 2636/3297] media: meson: vdec: Fix memory leak in error path of vdec_open commit 940f161f734b25f175a95d2684c2021f6323693a upstream. The vdec_open() function previously jumped directly to err_m2m_release when vdec_init_ctrls() failed, skipping release of the m2m context. This caused a resource leak. Fix it by introducing a proper err_m2m_ctx_release label that calls v4l2_m2m_ctx_release(sess->m2m_ctx) before releasing the m2m device. This was identified via kmemleak: unreferenced object 0xffff0000205d6878 (size 8): comm "v4l_id", pid 5289, jiffies 4294938580 hex dump (first 8 bytes): 40 d2 49 18 00 00 ff ff @.I..... backtrace (crc d3204599): kmemleak_alloc+0xc8/0xf0 __kvmalloc_node_noprof+0x60c/0x850 v4l2_ctrl_handler_init_class+0x1b4/0x2e8 [videodev] vdec_open+0x1f4/0x788 [meson_vdec] v4l2_open+0x144/0x460 [videodev] chrdev_open+0x1ac/0x500 do_dentry_open+0x3f0/0xfe8 vfs_open+0x68/0x320 do_open+0x2d8/0x9a8 path_openat+0x1d0/0x4f0 do_filp_open+0x190/0x380 do_sys_openat2+0xf8/0x1b0 __arm64_sys_openat+0x13c/0x1e8 invoke_syscall+0xdc/0x268 el0_svc_common.constprop.0+0x178/0x258 do_el0_svc+0x4c/0x70 Fixes: 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver") Cc: stable@vger.kernel.org Signed-off-by: Anand Moon Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/meson/vdec/vdec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 49e497a32973b..8545d62683eb5 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -889,7 +889,7 @@ static int vdec_open(struct file *file) ret = vdec_init_ctrls(sess); if (ret) - goto err_m2m_release; + goto err_m2m_ctx_release; sess->pixfmt_cap = formats[0].pixfmts_cap[0]; sess->fmt_out = &formats[0]; @@ -913,6 +913,8 @@ static int vdec_open(struct file *file) return 0; +err_m2m_ctx_release: + v4l2_m2m_ctx_release(sess->m2m_ctx); err_m2m_release: v4l2_m2m_release(sess->m2m_dev); err_free_sess: From 264b5380c4f8aa92dbc2983ecd2b627f1d5e0061 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:07 +0300 Subject: [PATCH 2637/3297] media: msi2500: Return queued buffers on start_streaming() failure commit 7201c17786a498497bca57752883b90914d405ac upstream. The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. msi2500_start_streaming() had five error paths that all hit this trap and were further tangled by ret-overwriting between calls: - -ENODEV when the USB device was already disconnected - -ERESTARTSYS when mutex_lock_interruptible() was interrupted - msi2500_set_usb_adc() failure: ret was silently overwritten by the next call (msi2500_isoc_init), so the error was lost entirely - msi2500_isoc_init() failure: cleanup_queued_bufs was called, but the function then fell through to msi2500_ctrl_msg() and again masked the original error by overwriting ret - msi2500_ctrl_msg(CMD_START_STREAMING) failure: no cleanup at all, leaving isoc URBs submitted with no way for the driver to consume them Consolidate the error paths into a small goto chain. Every failure now stops the function, drains the queued-buffer list, and returns the real error code. The ctrl_msg failure path also rolls back the preceding msi2500_isoc_init() via msi2500_isoc_cleanup() before unlocking and draining. The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 977e444f59ad ("[media] Mirics MSi3101 SDR Dongle driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/msi2500/msi2500.c | 32 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 33099f39146af..235b685f85364 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2500_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) +static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev, + enum vb2_buffer_state state) { unsigned long flags; @@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) buf = list_entry(dev->queued_bufs.next, struct msi2500_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -830,25 +831,40 @@ static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dev->dev, "\n"); - if (!dev->udev) - return -ENODEV; + if (!dev->udev) { + ret = -ENODEV; + goto err_cleanup; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) - return -ERESTARTSYS; + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + ret = -ERESTARTSYS; + goto err_cleanup; + } /* wake-up tuner */ v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1); ret = msi2500_set_usb_adc(dev); + if (ret) + goto err_unlock_cleanup; ret = msi2500_isoc_init(dev); if (ret) - msi2500_cleanup_queued_bufs(dev); + goto err_unlock_cleanup; ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0); + if (ret) + goto err_isoc_cleanup; mutex_unlock(&dev->v4l2_lock); + return 0; +err_isoc_cleanup: + msi2500_isoc_cleanup(dev); +err_unlock_cleanup: + mutex_unlock(&dev->v4l2_lock); +err_cleanup: + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return ret; } @@ -863,7 +879,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq) if (dev->udev) msi2500_isoc_cleanup(dev); - msi2500_cleanup_queued_bufs(dev); + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); /* according to tests, at least 700us delay is required */ msleep(20); From 2147acb948a945901d58f5c0e61eb60f19a4d49e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 18:17:49 +0000 Subject: [PATCH 2638/3297] media: nuvoton: npcm-video: fix error handling in npcm_video_init() commit 60ca00792bce46ec170c7ed101f376186d4cf8a9 upstream. npcm_video_init() has two error handling issues after of_reserved_mem_device_init() is called: When dma_set_mask_and_coherent() fails, the function releases the reserved memory but does not return, allowing execution to fall through into npcm_video_ece_init() with a failed DMA configuration. When npcm_video_ece_init() fails, the function returns an error without calling of_reserved_mem_device_release(), leaking the reserved memory association. Fix both by adding the missing return after the DMA mask failure and adding the missing of_reserved_mem_device_release() call on the ECE init error path. Fixes: 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nuvoton/npcm-video.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 44e904e61801e..6dbdb0d0128d4 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1720,10 +1720,12 @@ static int npcm_video_init(struct npcm_video *video) if (rc) { dev_err(dev, "Failed to set DMA mask\n"); of_reserved_mem_device_release(dev); + return rc; } rc = npcm_video_ece_init(video); if (rc) { + of_reserved_mem_device_release(dev); dev_err(dev, "Failed to initialize ECE\n"); return rc; } From 181a0aeefd56f9285325b84789aa348aba0508bf Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 18:18:09 +0000 Subject: [PATCH 2639/3297] media: nuvoton: npcm-video: fix memory leaks in probe and remove commit 50cc0e547da50b887e63dfa1ad203cd5b735d01e upstream. npcm_video_probe() allocates the npcm_video structure with kzalloc_obj() but never frees it on any probe error path or in npcm_video_remove(), leaking the allocation on every failed probe and every normal unbind. Additionally, when npcm_video_setup_video() fails, the reserved memory association established by of_reserved_mem_device_init() in npcm_video_init() is not released, leaking the rmem_assigned_device entry on the global list. Fix both by adding kfree(video) to all probe error paths and to npcm_video_remove(), and adding the missing of_reserved_mem_device_release() call when npcm_video_setup_video() fails. Fixes: 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nuvoton/npcm-video.c | 32 +++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 6dbdb0d0128d4..c4c5c318f7865 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1750,42 +1750,55 @@ static int npcm_video_probe(struct platform_device *pdev) regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { dev_err(&pdev->dev, "Failed to parse VCD reg in DTS\n"); - return PTR_ERR(regs); + rc = PTR_ERR(regs); + goto err_free; } video->vcd_regmap = devm_regmap_init_mmio(&pdev->dev, regs, &npcm_video_regmap_cfg); if (IS_ERR(video->vcd_regmap)) { dev_err(&pdev->dev, "Failed to initialize VCD regmap\n"); - return PTR_ERR(video->vcd_regmap); + rc = PTR_ERR(video->vcd_regmap); + goto err_free; } video->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(video->reset)) { dev_err(&pdev->dev, "Failed to get VCD reset control in DTS\n"); - return PTR_ERR(video->reset); + rc = PTR_ERR(video->reset); + goto err_free; } video->gcr_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgcr"); - if (IS_ERR(video->gcr_regmap)) - return PTR_ERR(video->gcr_regmap); + if (IS_ERR(video->gcr_regmap)) { + rc = PTR_ERR(video->gcr_regmap); + goto err_free; + } video->gfx_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgfxi"); - if (IS_ERR(video->gfx_regmap)) - return PTR_ERR(video->gfx_regmap); + if (IS_ERR(video->gfx_regmap)) { + rc = PTR_ERR(video->gfx_regmap); + goto err_free; + } rc = npcm_video_init(video); if (rc) - return rc; + goto err_free; rc = npcm_video_setup_video(video); if (rc) - return rc; + goto err_release_mem; dev_info(video->dev, "NPCM video driver probed\n"); return 0; + +err_release_mem: + of_reserved_mem_device_release(&pdev->dev); +err_free: + kfree(video); + return rc; } static void npcm_video_remove(struct platform_device *pdev) @@ -1800,6 +1813,7 @@ static void npcm_video_remove(struct platform_device *pdev) v4l2_device_unregister(v4l2_dev); if (video->ece.enable) npcm_video_ece_stop(video); + kfree(video); of_reserved_mem_device_release(dev); } From 9e61258fbc3cfc053e4c2ed72254c2de76772354 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:16 +0800 Subject: [PATCH 2640/3297] media: nxp: imx8-isi: Add missing v4l2_subdev_cleanup() in crossbar and pipe commit 567418eedd25b3d86d489807682030b4b98b73d9 upstream. Both mxc_isi_crossbar_init() and mxc_isi_pipe_init() call v4l2_subdev_init_finalize() which allocates the subdev active state, but neither mxc_isi_crossbar_cleanup() nor mxc_isi_pipe_cleanup() calls v4l2_subdev_cleanup() to free it. This causes a memory leak on every rmmod, reported by kmemleak: unreferenced object 0xffff0000d06fc800 (size 192): comm "(udev-worker)", pid 254, jiffies 4294913455 backtrace (crc 36eeae58): kmemleak_alloc+0x34/0x40 __kvmalloc_node_noprof+0x5f8/0x7d8 __v4l2_subdev_state_alloc+0x1fc/0x30c __v4l2_subdev_init_finalize+0x178/0x368 Add the missing v4l2_subdev_cleanup() calls before media_entity_cleanup() in both crossbar and pipe cleanup paths. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-3-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 1 + drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index ede6cc74c0234..3562c9f5ae2ef 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -492,6 +492,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar) { + v4l2_subdev_cleanup(&xbar->sd); media_entity_cleanup(&xbar->sd.entity); kfree(xbar->pads); kfree(xbar->inputs); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index a41c51dd9ce0f..cb50af2270f6c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -819,6 +819,7 @@ void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe) { struct v4l2_subdev *sd = &pipe->sd; + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); } From 4702afbd56f1d3a26eabae99627673382433f1dc Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:18 +0800 Subject: [PATCH 2641/3297] media: nxp: imx8-isi: Clean up already-initialized pipes on probe failure commit d970b27cc48ec42f8a72bc3a4a4ad2e5c7a36395 upstream. When mxc_isi_pipe_init() fails partway through the channel loop or when mxc_isi_v4l2_init() fails, the already initialized pipes are not cleaned up. Fix this by calling mxc_isi_pipe_cleanup() for each already-initialized pipe in the err_xbar error path. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Link: https://patch.msgid.link/20260507041318.491594-5-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 60fed2fe200dc..a6ed1d124472a 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -517,6 +517,8 @@ static int mxc_isi_probe(struct platform_device *pdev) return 0; err_xbar: + while (i--) + mxc_isi_pipe_cleanup(&isi->pipes[i]); mxc_isi_crossbar_cleanup(&isi->crossbar); return ret; From 659a7cea0be8029df342b0b8965638bbbb7f3d76 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:17 +0800 Subject: [PATCH 2642/3297] media: nxp: imx8-isi: Fix missing v4l2_subdev_cleanup() in pipe init error path commit 8262de0663318124824aaafd97ddb5d7bb53bd77 upstream. After v4l2_subdev_init_finalize() succeeds in mxc_isi_pipe_init(), if platform_get_irq() or devm_request_irq() fails, the error path jumps to a label that only calls media_entity_cleanup() and mutex_destroy(), missing the v4l2_subdev_cleanup() call needed to free the subdev active state allocated by v4l2_subdev_init_finalize(). Add an error_subdev label that calls v4l2_subdev_cleanup() before falling through to the existing error cleanup. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-4-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index cb50af2270f6c..a59b9456b5907 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -796,18 +796,20 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) irq = platform_get_irq(to_platform_device(isi->dev), id); if (irq < 0) { ret = irq; - goto error; + goto error_subdev; } ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler, 0, dev_name(isi->dev), pipe); if (ret < 0) { dev_err(isi->dev, "failed to request IRQ (%d)\n", ret); - goto error; + goto error_subdev; } return 0; +error_subdev: + v4l2_subdev_cleanup(sd); error: media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); From 28ae75dba701d7aa69a36802c398582933d3e0e6 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 23 Mar 2026 16:33:30 +0800 Subject: [PATCH 2643/3297] media: nxp: imx8-isi: Fix potential out-of-bounds issues commit 57a7ec5c9f38ce6c4d6209c4b75c8e57e1fea6cf upstream. The maximum downscaling factor supported by ISI can be up to 16. Add minimum value constraint before applying the setting to hardware. Otherwise, the process will not respond even when Ctrl+C is executed. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260323-isi-v3-1-8df53b24e622@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- .../media/platform/nxp/imx8-isi/imx8-isi-core.h | 16 ++++++++++++++++ .../media/platform/nxp/imx8-isi/imx8-isi-m2m.c | 11 ++++++++--- .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 13 ++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index e84af5127e4e7..681a05045cd28 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -11,6 +11,7 @@ #define __MXC_ISI_CORE_H__ #include +#include #include #include #include @@ -412,4 +413,19 @@ static inline void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi) } #endif +/* + * ISI scaling engine works in two parts: it performs pre-decimation of + * the image followed by bilinear filtering to achieve the desired + * downscaling factor. + * + * The decimation filter provides a maximum downscaling factor of 8, and + * the subsequent bilinear filter provides a maximum downscaling factor + * of 2. Combined, the maximum scaling factor can be up to 16. + */ +static inline unsigned int +mxc_isi_clamp_downscale_16(unsigned int val, unsigned int max_val) +{ + return clamp(val, max(1U, DIV_ROUND_UP(max_val, 16)), max_val); +} + #endif /* __MXC_ISI_CORE_H__ */ diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index 00afcbfbdde48..f2a73f0f1ac77 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -509,9 +509,14 @@ __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx, const enum mxc_isi_video_type type) { if (type == MXC_ISI_VIDEO_M2M_CAP) { - /* Downscaling only */ - pix->width = min(pix->width, ctx->queues.out.format.width); - pix->height = min(pix->height, ctx->queues.out.format.height); + const struct v4l2_pix_format_mplane *format = + &ctx->queues.out.format; + + /* Downscaling only, by up to 16. */ + pix->width = mxc_isi_clamp_downscale_16(pix->width, + format->width); + pix->height = mxc_isi_clamp_downscale_16(pix->height, + format->height); } return mxc_isi_format_try(ctx->m2m->pipe, pix, type); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index a59b9456b5907..2d0843c86534c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -641,16 +641,19 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, /* Composing is supported on the sink only. */ return -EINVAL; - /* The sink crop is bound by the sink format downscaling only). */ + /* + * The ISI supports downscaling only, with a factor up to 16. + * Clamp the compose rectangle size accordingly. + */ format = mxc_isi_pipe_get_pad_format(pipe, state, MXC_ISI_PIPE_PAD_SINK); sel->r.left = 0; sel->r.top = 0; - sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, - format->width); - sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, - format->height); + sel->r.width = mxc_isi_clamp_downscale_16(sel->r.width, + format->width); + sel->r.height = mxc_isi_clamp_downscale_16(sel->r.height, + format->height); rect = mxc_isi_pipe_get_pad_compose(pipe, state, MXC_ISI_PIPE_PAD_SINK); From 4e077bcb5e1f634b28365efc197f4cf71e7911d1 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 23 Mar 2026 16:33:31 +0800 Subject: [PATCH 2644/3297] media: nxp: imx8-isi: Fix scale factor calculation for hardware rounding commit 5eb54da3f874b44149556542d949909898865e29 upstream. The ISI hardware rounds the actual output size up to an integer, as described in i.MX93 Reference Manual section 57.7.8 (Channel 0 Scale Factor). The scale factor must be calculated to ensure the theoretical output value rounds up to exactly the desired size. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260323-isi-v3-2-8df53b24e622@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 9225a7ac1c3ee..0bfd9b8f5f108 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -112,7 +112,14 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to, else *dec = 8; - return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD); + /* + * The ISI rounds output dimensions up to the next integer (i.MX93 RM + * section 57.7.8). Calculate the scale factor such that the theoretical + * output (input / scale_factor) rounds up to exactly the desired + * output. + */ + return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec), + ISI_DOWNSCALE_THRESHOLD); } static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe, From 08ddfd628a2dbd9d385da677afccd893d0ab37e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2026 16:50:08 +0200 Subject: [PATCH 2645/3297] media: pci: dm1105: Free allocated workqueue commit 1a65db225b25bb8c8febf16974c060e0cc242eb9 upstream. Destroy allocated workqueue in remove() callback to free its resources, thus fixing memory leak. Fixes: 519a4bdcf822 ("V4L/DVB (11984): Add support for yet another SDMC DM1105 based DVB-S card.") Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/pci/dm1105/dm1105.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index e1185aa669f48..c1d55b9836744 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -1194,6 +1194,7 @@ static void dm1105_remove(struct pci_dev *pdev) dm1105_hw_exit(dev); free_irq(pdev->irq, dev); + destroy_workqueue(dev->wq); pci_iounmap(pdev, dev->io_mem); pci_release_regions(pdev); pci_disable_device(pdev); From 9afd605dcd96c7a45f338eded1de16679b30e1df Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Wed, 13 May 2026 08:42:44 +0300 Subject: [PATCH 2646/3297] media: pwc: Drain fill_buf on start_streaming() failure commit 906e410dcffbbd99fb4081abab817a830033aa28 upstream. pwc_isoc_init() submits its isochronous URBs with usb_submit_urb(.., GFP_KERNEL) in a loop. After the first URB is submitted, its completion handler pwc_isoc_handler() can run on another CPU before the loop finishes: start_streaming() pwc_isoc_init() usb_submit_urb(urbs[0], GFP_KERNEL) pwc_isoc_handler(urbs[0]) pdev->fill_buf = pwc_get_next_fill_buf(pdev) usb_submit_urb(urbs[i>0], ..) -> fails pwc_isoc_cleanup(pdev) /* kills URBs */ return ret; pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED) pwc_get_next_fill_buf() detaches a buffer from pdev->queued_bufs and stores it in pdev->fill_buf. The error path in start_streaming() only drains pdev->queued_bufs, so the buffer parked in pdev->fill_buf is leaked. vb2_start_streaming() then triggers WARN_ON(owned_by_drv_count). stop_streaming() already handles this since commit 80b0963e1698 ("[media] pwc: fix WARN_ON"), which added the fill_buf drain in the teardown path but not in the start_streaming() error path. Mirror that handling on failure so start_streaming() returns with no buffer owned by the driver. Issue identified by automated review of the INV-003 series at https://sashiko.dev/ Fixes: 885fe18f5542 ("[media] pwc: Replace private buffer management code with videobuf2") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/pwc/pwc-if.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index c6e5d031f068b..b5ac5b36c8103 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -726,6 +726,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) pwc_camera_power(pdev, 0); /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); + if (pdev->fill_buf) { + vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + pdev->fill_buf = NULL; + } } mutex_unlock(&pdev->v4l2_lock); From cb16b79a2be2cec9c3ebe4147490817c4d8b1de3 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:08 +0300 Subject: [PATCH 2647/3297] media: pwc: Return queued buffers on start_streaming() failure commit 975b2ee20e569d47821e4f6c9761b4664d48a6a4 upstream. The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. pwc's start_streaming() had two early returns that hit this trap: -ENODEV when the USB device was already disconnected, and -ERESTARTSYS when mutex_lock_interruptible() was interrupted by a signal. Call the existing pwc_cleanup_queued_bufs() helper with VB2_BUF_STATE_QUEUED before returning (matching the state already used by the pwc_isoc_init() error path in the same function). This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: ceede9fa8939 ("[media] pwc: Fix locking") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/pwc/pwc-if.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index b5ac5b36c8103..4cc278d711aee 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -710,11 +710,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct pwc_device *pdev = vb2_get_drv_priv(vq); int r; - if (!pdev->udev) + if (!pdev->udev) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&pdev->v4l2_lock)) + if (mutex_lock_interruptible(&pdev->v4l2_lock)) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); pwc_set_leds(pdev, leds[0], leds[1]); From 4e451100b35eed78fbf4349e339bdf2c947edbeb Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:51 +0100 Subject: [PATCH 2648/3297] media: qcom: camss: Fix RDI streaming for CSID 680 commit 93ea81d16570442dbca04d2e2563ae8c3e65fa1b upstream. Fix streaming to RDI1 and RDI2. csid->phy.en_vc contains a bitmask of enabled CSID ports not virtual channels. We cycle through the number of available CSID ports and test this value against the vc_en bitmask. We then use the passed value both as an index to the port configuration macros and as a virtual channel index. This is a very broken pattern. Reviewing the initial introduction of VC support it states that you can only map one CSID to one VFE. This is true however each CSID has multiple sources which can sink inside of the VFE - for example there is a "pixel" path for bayer stats which sources @ CSID(x):3 and sinks on VFE(x):pix. That is CSID port # 3 should drive VFE port #3. With our current setup only a sensor which drives virtual channel number #3 could possibly enable that setup. This is deeply wrong the virtual channel has no relevance to hooking CSID to VFE, a fact that is proven after this patch is applied allowing RDI0,RDI1 and RDI2 to function with VC0 whereas before only RDI1 worked. Another way the current model breaks is the DT field. A sensor driving different data-types on the same VC would not be able to separate the VC:DT pair to separate RDI outputs, thus breaking another feature of VCs in the MIPI data-stream. Default the VC back to zero. A follow on series will implement subdev streams to actually enable VCs without breaking CSID source to VFE sink. Fixes: 253314b20408 ("media: qcom: camss: Add CSID 680 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- .../platform/qcom/camss/camss-csid-680.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c index 3ad3a174bcfb8..edf01ba79907d 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-680.c +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -219,9 +219,9 @@ static void __csid_configure_top(struct csid_device *csid) CSID_TOP_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -233,28 +233,28 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 lane_cnt = 4; val = 0; - writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); /* * DT_ID is a two bit bitfield that is concatenated with * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; /* note: for non-RDI path, this should be format->decode_format */ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_TIMESTAMP_STB_FRAME; val |= RDI_CFG1_BYTE_CNTR_EN; @@ -265,23 +265,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_V_EN; val |= RDI_CFG1_PACKING_MIPI; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_ENABLE; else val &= ~RDI_CFG0_ENABLE; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -290,11 +290,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_top(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From c39a1d9fde82b9ec858e5e4cc2746200651bae00 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:53 +0100 Subject: [PATCH 2649/3297] media: qcom: camss: Fix RDI streaming for CSID GEN2 commit 618765634cefbdddafa84f07f82e9dd05b86cb9c upstream. Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID gen2 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: 729fc005c8e2 ("media: qcom: camss: Split testgen, RDI and RX for CSID 170") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- .../platform/qcom/camss/camss-csid-gen2.c | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index 2a1746dcc1c5b..eadcb2f7e3aaa 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -203,10 +203,10 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi)); } -static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct csid_testgen_config *tg = &csid->testgen; - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -253,10 +253,10 @@ static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) writel_relaxed(val, csid->base + CSID_TPG_CTRL); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -267,14 +267,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = 1 << RDI_CFG0_BYTE_CNTR_EN; val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; @@ -284,56 +284,57 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); /* CSID_TIMESTAMP_STB_POST_IRQ */ val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; - writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG1(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CTRL(port)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val = readl_relaxed(csid->base + CSID_RDI_CFG0(port)); val |= enable << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; u8 i; - /* Loop through all enabled VCs and configure stream for each */ + + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { if (tg->enabled) - __csid_configure_testgen(csid, enable, i); + __csid_configure_testgen(csid, enable, i, 0); - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From a58d01a0ed3977e9048b70ba5714fd94220922e9 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:54 +0100 Subject: [PATCH 2650/3297] media: qcom: camss: Fix RDI streaming for CSID GEN3 commit ad136e52634d5a393a9dc93383f8ea1e898faf37 upstream. Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID gen3 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: d96fe1808dcc ("media: qcom: camss: Add CSID 780 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- .../platform/qcom/camss/camss-csid-gen3.c | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index bd059243790ed..ed5c5766efd36 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -145,12 +145,12 @@ static void __csid_configure_wrapper(struct csid_device *csid) writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { u32 val; u8 lane_cnt = csid->phy.lane_cnt; /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -163,14 +163,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = RDI_CFG0_TIMESTAMP_EN; val |= RDI_CFG0_TIMESTAMP_STB_SEL; @@ -180,7 +180,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DT; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_PACKING_FORMAT_MIPI; val |= RDI_CFG1_PIX_STORE; @@ -189,22 +189,22 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_H_EN; val |= RDI_CFG1_CROP_V_EN; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_EN; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -213,11 +213,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_wrapper(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From 64cb15878b35e5574ff4f80a0b613a79e47867ba Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 13 May 2026 16:02:37 +0900 Subject: [PATCH 2651/3297] media: radio-si476x: Unregister v4l2_device on probe failure commit 436a693af04ffb889aaf87cb69ec1f2b21d3569c upstream. si476x_radio_probe() registers radio->v4l2dev before allocating the V4L2 controls and before registering the video device. If any of those later steps fails, probe returns through the exit label after freeing only the control handler. A failed probe does not call si476x_radio_remove(), so the v4l2_device_unregister() there is not reached. This leaves the parent device reference taken by v4l2_device_register() behind on the error path. Unregister the V4L2 device in the probe error path after freeing the controls. Fixes: b879a9c2a755 ("[media] v4l2: Add a V4L2 driver for SI476X MFD") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/radio/radio-si476x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 9980346cb5ea3..bfe89782dce43 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -1493,6 +1493,7 @@ static int si476x_radio_probe(struct platform_device *pdev) return 0; exit: v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + v4l2_device_unregister(&radio->v4l2dev); return rval; } From 2c71bda6edc630a1f8c3c45d8df5fc22d234e042 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Wed, 22 Apr 2026 20:17:34 +0530 Subject: [PATCH 2652/3297] media: rtl2832: fix use-after-free in rtl2832_remove() commit 680daf40a82d483949f87f0d8f98639dc47e610c upstream. cancel_delayed_work_sync() is called before i2c_mux_del_adapters() in rtl2832_remove(). While the cancel waits for any running instance of i2c_gate_work to finish, it does not prevent the timer from being rescheduled by a concurrent thread. During probe, the r820t_attach() call attempts I2C transfers through the mux adapter. These transfers go through i2c_mux_master_xfer(), which calls rtl2832_deselect() after the transfer completes, rescheduling i2c_gate_work via schedule_delayed_work(). If this transfer is still in flight when rtl2832_remove() runs, rtl2832_deselect() can reschedule i2c_gate_work after it has been cancelled, causing a use-after-free when kfree(dev) is called. Fix this by calling i2c_mux_del_adapters() before cancel_delayed_work_sync(). Once the mux adapter is unregistered, no new I2C transfers can go through it, so rtl2832_deselect() can no longer reschedule i2c_gate_work. The subsequent cancel_delayed_work_sync() is then guaranteed to be final. Fixes: cddcc40b1b15 ("[media] rtl2832: convert to use an explicit i2c mux core") Cc: stable@vger.kernel.org Reported-by: syzbot+019ced393ab913002b75@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=019ced393ab913002b75 Signed-off-by: Deepanshu Kartikey Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/dvb-frontends/rtl2832.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 3b4e46dac1bf1..16f5f7e430922 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1115,10 +1115,10 @@ static void rtl2832_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - cancel_delayed_work_sync(&dev->i2c_gate_work); - i2c_mux_del_adapters(dev->muxc); + cancel_delayed_work_sync(&dev->i2c_gate_work); + regmap_exit(dev->regmap); kfree(dev); From 894e83509c66910112b9eaeaa8cd66cd9806db91 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:09 +0300 Subject: [PATCH 2653/3297] media: rtl2832_sdr: Return queued buffers on start_streaming() failure commit 33ca0aab6f4bd90921fc1395478f38f72c4d19af upstream. The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. rtl2832_sdr_start_streaming() had multiple error paths that hit this trap: two direct early returns (-ENODEV, -ERESTARTSYS), plus six `goto err` paths covering subdev s_power, tuner setup, ADC setup, stream-buffer allocation, urb allocation, and urb submission failures. None of them returned the queued buffers. The original function had no distinct success exit and fell straight through into the err label, which previously only did mutex_unlock and "return ret". Adding queued-buffer cleanup at err must therefore be paired with an explicit success return; otherwise every successful start would also drain the buffer queue and kill streaming. Add that success return, then add rtl2832_sdr_cleanup_queued_bufs() at the err label and before each early return. The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). The err label still does not roll back power_ctrl(), frontend_ctrl(), the POWER_ON flag, or stream/URB allocations that may have happened before the failing step. Those are pre-existing leaks of a different class and are not addressed here. Fixes: 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/dvb-frontends/rtl2832_sdr.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 0357624968f1b..787e2ef202a59 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -399,7 +399,8 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev, + enum vb2_buffer_state state) { struct platform_device *pdev = dev->pdev; unsigned long flags; @@ -413,7 +414,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) buf = list_entry(dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -855,11 +856,15 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(&pdev->dev, "\n"); - if (!dev->udev) + if (!dev->udev) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } if (d->props->power_ctrl) d->props->power_ctrl(d, 1); @@ -900,7 +905,11 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; + mutex_unlock(&dev->v4l2_lock); + return 0; + err: + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); return ret; @@ -920,7 +929,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) rtl2832_sdr_kill_urbs(dev); rtl2832_sdr_free_urbs(dev); rtl2832_sdr_free_stream_bufs(dev); - rtl2832_sdr_cleanup_queued_bufs(dev); + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); rtl2832_sdr_unset_adc(dev); /* sleep tuner */ From a7a141e4e93c8bca495c3195989bb6b731d39934 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Tue, 30 Dec 2025 18:09:15 +0100 Subject: [PATCH 2654/3297] media: rzg2l-cru: Skip ICnMC configuration when ICnSVC is used commit cd4ce68875f406b0cf3f5a94c0fd22989689222f upstream. When the CRU is configured to use ICnSVC for virtual channel mapping, as on the RZ/{G3E, V2H/P} SoC, the ICnMC register must not be programmed. Return early after setting up ICnSVC to avoid overriding the ICnMC register, which is not applicable in this mode. This prevents unintended register programming when ICnSVC is enabled. Cc: stable@vger.kernel.org Fixes: 3c5ca0a48bb0 ("media: rzg2l-cru: Drop function pointer to configure CSI") Signed-off-by: Tommaso Merciai [Rework to not break image format programming] Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- .../platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 1 + .../platform/renesas/rzg2l-cru/rzg2l-video.c | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h index a5a57369ef0eb..10e62f2646d08 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -60,6 +60,7 @@ #define ICnMC_CSCTHR BIT(5) #define ICnMC_INF(x) ((x) << 16) #define ICnMC_VCSEL(x) ((x) << 22) +#define ICnMC_VCSEL_MASK GENMASK(23, 22) #define ICnMC_INF_MASK GENMASK(21, 16) #define ICnMS_IA BIT(2) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 162e2ace69318..6aea7c244df1b 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -262,19 +262,24 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, u8 csi_vc) { const struct rzg2l_cru_info *info = cru->info; - u32 icnmc = ICnMC_INF(ip_fmt->datatype); + u32 icnmc = rzg2l_cru_read(cru, info->image_conv) & ~(ICnMC_INF_MASK | + ICnMC_VCSEL_MASK); + icnmc |= ICnMC_INF(ip_fmt->datatype); + /* + * VC filtering goes through SVC register on G3E/V2H. + * + * FIXME: virtual channel filtering is likely broken and only VC=0 + * works. + */ if (cru->info->regs[ICnSVC]) { rzg2l_cru_write(cru, ICnSVCNUM, csi_vc); rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) | ICnSVC_SVC2(2) | ICnSVC_SVC3(3)); + } else { + icnmc |= ICnMC_VCSEL(csi_vc); } - icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; - - /* Set virtual channel CSI2 */ - icnmc |= ICnMC_VCSEL(csi_vc); - rzg2l_cru_write(cru, info->image_conv, icnmc); } From b7936e8cbec1b96b126058eeb005e5b9111df38e Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Thu, 2 Apr 2026 15:35:29 +0800 Subject: [PATCH 2655/3297] media: saa7134: Fix a possible memory leak in saa7134_video_init1 commit f86ed548386e3050e5f8f25b450d09dc009d9a88 upstream. In saa7134_video_init1(), the return value of the first saa7134_pgtable_alloc() is not checked. If it fails, the function continues as if successful, leaving the driver with an invalid page table. Additionally, if vb2_queue_init() for the VBI queue fails after the video queue page table has been allocated, the allocated memory is not freed before returning. The second saa7134_pgtable_alloc() also lacks a return value check. Errors occur during device probing before the device is fully registered, the normal cleanup path in saa7134_finidev() is not executed, leading to memory leaks and potential use of uninitialized DMA resources. Check the return value of both saa7134_pgtable_alloc() calls and propagate errors. On failure of any later step, free allocated page tables to avoid memory leaks. Ensure control handlers are also released on error to prevent further resource leakage. Found by code review. Signed-off-by: Ma Ke Cc: stable@vger.kernel.org Fixes: a00e68888d5d ("[media] saa7134: move saa7134_pgtable to saa7134_dmaqueue") Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/pci/saa7134/saa7134-video.c | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 4a51b873e47ad..2b1672737d841 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1714,8 +1714,10 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->dev = &dev->pci->dev; ret = vb2_queue_init(q); if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + ret = saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + if (ret) + goto err_free_ctrl; q = &dev->vbi_vbq; q->type = V4L2_BUF_TYPE_VBI_CAPTURE; @@ -1732,11 +1734,24 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->lock = &dev->lock; q->dev = &dev->pci->dev; ret = vb2_queue_init(q); - if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } + + ret = saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } return 0; + +err_free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + if (card_has_radio(dev)) + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; } void saa7134_video_fini(struct saa7134_dev *dev) From ed342a86bb2f9c1b44a0fc4f6b08c14073946e4f Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:10 +0300 Subject: [PATCH 2656/3297] media: stm32-dcmipp: Return queued buffers on start_streaming() failure commit ffc8eec06378a340d708c889184ab3e14b57d540 upstream. The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. dcmipp_bytecap_start_streaming() returned -EINVAL when the source subdevice could not be resolved from the media graph, before pm_runtime_resume_and_get() and media_pipeline_start() had been called. The remaining error paths already converge on the err_buffer_done label, which calls dcmipp_bytecap_all_buffers_done(..., VB2_BUF_STATE_QUEUED). Jump to that label directly: the intermediate err_pm_put / err_media_pipeline_stop labels are skipped, which is correct because nothing they would undo has happened yet. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 28e0f3772296 ("media: stm32-dcmipp: STM32 DCMIPP camera interface driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- .../media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c index b18e273ef4a3e..e9cab99775bc9 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -398,8 +398,10 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, */ if (!vcap->s_subdev) { pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) { + ret = -EINVAL; + goto err_buffer_done; + } vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); vcap->s_subdev_pad_nb = pad->index; } From 931abe1deb65b919d23fa203d7f6d6fbd4fccd8e Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 21:43:49 +0900 Subject: [PATCH 2657/3297] media: stm32: dcmi: unregister notifier on probe failure commit 084973ebd67b28f0945c5d45408f86c58b540110 upstream. dcmi_graph_init() registers the async notifier before dcmi_probe() toggles the reset line. If reset_control_assert() or reset_control_deassert() fails afterwards, probe returns through err_cleanup and the driver core will not call dcmi_remove(). Unregister the notifier before cleaning it up on that error path, matching the successful remove path and the V4L2 async notifier lifetime rules. Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil Fixes: d079f94c9046 ("media: platform: Switch to v4l2_async_notifier_add_subdev") Cc: stable@vger.kernel.org [hverkuil: added Fixes tag] Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/st/stm32/stm32-dcmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index 13762861b7694..200b498127919 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2063,6 +2063,7 @@ static int dcmi_probe(struct platform_device *pdev) return 0; err_cleanup: + v4l2_async_nf_unregister(&dcmi->notifier); v4l2_async_nf_cleanup(&dcmi->notifier); err_media_entity_cleanup: media_entity_cleanup(&dcmi->vdev->entity); From b5184b3f0e9d4cc47059ba1138c9a73d43d2493f Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:11 +0300 Subject: [PATCH 2658/3297] media: sun4i-csi: Return queued buffers on start_streaming() failure commit bbba3e260a62810a717b4442a3bb96d0ec0f6309 upstream. The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. sun4i_csi_start_streaming() returned -EINVAL when no matching CSI format could be found, before any setup (scratch buffer allocation, pipeline start) had been performed. The remaining error paths already converge on the err_clear_dma_queue label, which calls return_all_buffers(..., VB2_BUF_STATE_QUEUED) under csi->qlock. Jump to that label directly: the intermediate err_disable_device / err_disable_pipeline / err_free_scratch_buffer labels are skipped, which is correct because nothing they would undo has happened yet. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 577bbf23b758 ("media: sunxi: Add A10 CSI driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index e911c7f7acc52..4781db21c2055 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL); - if (!csi_fmt) - return -EINVAL; + if (!csi_fmt) { + ret = -EINVAL; + goto err_clear_dma_queue; + } dev_dbg(csi->dev, "Starting capture\n"); From 118c2f5d1d3639835917edd5962ac14ea4b08d1f Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 25 Mar 2026 13:57:42 +0300 Subject: [PATCH 2659/3297] media: synopsys: hdmirx: Fix HPD lane hold time commit d1162a5adbb5e95953d460b5bde3a04cd4473fe9 upstream. Increase time of holding HPD lane low by 50ms. This fixes EDID change not detected by source/display side. Fixes: 7b59b132ad43 ("media: platform: synopsys: Add support for HDMI input driver") Cc: stable@vger.kernel.org Reported-by: Ross Cawston Closes: https://lore.kernel.org/linux-rockchip/20260209061654.54757-1-ross@r-sc.ca/ Signed-off-by: Dmitry Osipenko Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index b7d278b3889f2..8e6f452f76c73 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -504,9 +504,9 @@ static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en) hdmirx_writel(hdmirx_dev, CORE_CONFIG, hdmirx_dev->hpd_trigger_level_high ? en : !en); - /* 100ms delay as per HDMI spec */ + /* 100ms delay as per HDMI spec + extra 50ms to cover internal delay */ if (!en) - msleep(100); + msleep(100 + 50); } static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev, From 127fc44e83256e713e8e9e2716c95b0e560a1bad Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 7 May 2026 02:22:13 +0000 Subject: [PATCH 2660/3297] media: tegra-video: vi: fix invalid u32 return value in format lookup commit d5b50055338e131a1a99f923ebb0361974a00f36 upstream. tegra_get_format_fourcc_by_idx() returns a u32 but uses -EINVAL to signal an out-of-bounds index. This results in a large unsigned value being returned, which may be interpreted as a valid fourcc. Returning 0 is not a valid fourcc either. This condition should never happen, so use WARN_ON_ONCE() to catch unexpected out-of-bounds access and return a valid fallback format instead. Suggested-by: Hans Verkuil Fixes: 3d8a97eabef0 ("media: tegra-video: Add Tegra210 Video input driver") Cc: stable@vger.kernel.org Reviewed-by: Luca Ceresoli Signed-off-by: Hungyu Lin Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/staging/media/tegra-video/vi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 14b327afe045e..46f1693467824 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -80,8 +80,8 @@ static int tegra_get_format_idx_by_code(struct tegra_vi *vi, static u32 tegra_get_format_fourcc_by_idx(struct tegra_vi *vi, unsigned int index) { - if (index >= vi->soc->nformats) - return -EINVAL; + if (WARN_ON_ONCE(index >= vi->soc->nformats)) + return vi->soc->video_formats[0].fourcc; return vi->soc->video_formats[index].fourcc; } From 7e6521dd747eca3cb3d4cd3ddcf20f266494f63d Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 22:16:31 +0900 Subject: [PATCH 2661/3297] media: ti: vpe: unwind v4l2 device registration on probe error commit e0f1c9a90ef665f2587c274a8fed59f2dfc575a6 upstream. If the vpe_top resource is missing, vpe_probe() returns -ENODEV after v4l2_device_register() has succeeded. Probe failures do not call the driver's remove callback, so the v4l2 device remains registered on that error path. Route that failure through the existing v4l2_device_unregister() unwind label, matching the other errors after v4l2_device_register(). Fixes: 4d59c7d45585 ("media: ti-vpe: vpe: Add missing null pointer checks") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Yemike Abhilash Chandra Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/ti/vpe/vpe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index 6029d4e8e0bd3..e6c3aee946c16 100644 --- a/drivers/media/platform/ti/vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c @@ -2546,7 +2546,8 @@ static int vpe_probe(struct platform_device *pdev) "vpe_top"); if (!dev->res) { dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); - return -ENODEV; + ret = -ENODEV; + goto v4l2_dev_unreg; } /* From 836cfffb2ddbbf0f703efef8440bad544238172b Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Fri, 1 May 2026 23:28:31 +0300 Subject: [PATCH 2662/3297] media: v4l2-ctrls-request: add NULL check in v4l2_ctrl_request_complete() commit caced3578bf9f104a4aaad8f46c4c719e705d9a6 upstream. If CONFIG_MEDIA_CONTROLLER is undefined, media_request_object_find() will always return NULL, so its 2nd call in v4l2_ctrl_request_complete() would fail as well as the 1st one and thus cause hdl to have a wrong value (at the top of memory) and list_for_each_entry() to iterate over the garbage data located there. Add NULL check for the 2nd call and place the error cleanup at the end of v4l2_ctrl_request_complete()... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: c3bf5129f339 ("media: v4l2-ctrls: always copy the controls on completion") Cc: stable@vger.kernel.org Signed-off-by: Sergey Shtylyov Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/v4l2-core/v4l2-ctrls-request.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c index e77f722b36a48..aa5566a8fb7d3 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-request.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c @@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct media_request *req, ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8); if (!ret) ret = v4l2_ctrl_request_bind(req, hdl, main_hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - kfree(hdl); - return; - } + if (ret) + goto error; hdl->request_is_queued = true; obj = media_request_object_find(req, &req_ops, main_hdl); + if (!obj) + goto error; } hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); @@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct media_request *req, mutex_unlock(main_hdl->lock); media_request_object_complete(obj); media_request_object_put(obj); + return; + +error: + v4l2_ctrl_handler_free(hdl); + kfree(hdl); } EXPORT_SYMBOL(v4l2_ctrl_request_complete); From 3068ab802fc98b121dcb451e1f7f4d338ffc7a19 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 24 Mar 2026 11:13:26 +0800 Subject: [PATCH 2663/3297] media: v4l2-ctrls: validate HEVC active reference counts commit afbe4bc252d90a6f8fad869b06d5430f615f22f9 upstream. HEVC slice parameters are shared stateless V4L2 controls, but the common validation path does not verify the active L0/L1 reference counts before driver-specific code consumes them. The original report came from Cedrus, but the active count bounds are not Cedrus-specific. Validate them in the common HEVC slice control path so stateless HEVC drivers get the same basic guarantees as soon as the control is queued. Do not reject ref_idx_l0/ref_idx_l1 entries here. Existing userspace may use out-of-range sentinel values such as 0xff for missing references, and some hardware can use that information for concealment. Keep this common check limited to the active reference counts. Fixes: d395a78db9eab ("media: hevc: Add decode params control") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/v4l2-core/v4l2-ctrls-core.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 85d07ef44f626..838e9aef703c8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -882,6 +882,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_h264_decode_params *p_h264_dec_params; struct v4l2_ctrl_hevc_sps *p_hevc_sps; struct v4l2_ctrl_hevc_pps *p_hevc_pps; + struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering; struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params; struct v4l2_area *area; @@ -1171,6 +1172,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, break; case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + p_hevc_slice_params = p; + + if (p_hevc_slice_params->num_ref_idx_l0_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; + + if (p_hevc_slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_B) + break; + + if (p_hevc_slice_params->num_ref_idx_l1_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; break; case V4L2_CTRL_TYPE_HDR10_CLL_INFO: From cf9732fd6c4f2f803ccfc46d89489b6635590270 Mon Sep 17 00:00:00 2001 From: Mirela Rabulea Date: Fri, 22 May 2026 17:31:20 +0300 Subject: [PATCH 2664/3297] media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor() commit 06cb687a5132fcffe624c0070576ab852ac6b568 upstream. The v4l2 helper v4l2_async_register_subdev_sensor() calls v4l2_async_register_subdev(), which is a macro that expands to __v4l2_async_register_subdev(sd,THIS_MODULE). Since the macro is expanded inside v4l2-fwnode.c, THIS_MODULE resolves to the v4l2-fwnode module rather than the sensor driver module that originally set sd->owner. When v4l2-fwnode is built-in, THIS_MODULE evaluates to NULL, which then overwrites the sensor driver's owner with NULL. This causes the problem that the sensor module's reference count is never incremented during async registration, so the module can be removed while the subdevice is still in use by a notifier (e.g., a CSI-2 receiver bridge driver). Fix this by renaming v4l2_async_register_subdev_sensor() to __v4l2_async_register_subdev_sensor() with an added explicit module argument and introducing a wrapper macro: #define v4l2_async_register_subdev_sensor(sd) \ __v4l2_async_register_subdev_sensor(sd, THIS_MODULE) This ensures the sensor driver module is properly referenced even when the sensor driver does not init the owner field before calling v4l2_async_register_subdev_sensor() and prevents premature module removal. Fixes: aef69d54755d ("media: v4l: fwnode: Add a convenience function for registering sensors") Cc: stable@vger.kernel.org Suggested-by: Frank Li Link: https://lore.kernel.org/linux-media/20240315073125.275501-2-sakari.ailus@linux.intel.com/ Signed-off-by: Mirela Rabulea Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Signed-off-by: Sakari Ailus Signed-off-by: Greg Kroah-Hartman --- drivers/media/v4l2-core/v4l2-fwnode.c | 6 +++--- include/media/v4l2-async.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index cb153ce42c45d..a8becf88b7536 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1246,7 +1246,7 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev, return 0; } -int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) +int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module) { struct v4l2_async_notifier *notifier; int ret; @@ -1272,7 +1272,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) if (ret < 0) goto out_cleanup; - ret = v4l2_async_register_subdev(sd); + ret = __v4l2_async_register_subdev(sd, module); if (ret < 0) goto out_unregister; @@ -1290,7 +1290,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) return ret; } -EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor); +EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor); MODULE_DESCRIPTION("V4L2 fwnode binding parsing library"); MODULE_LICENSE("GPL"); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index f26c323e9c963..54a2d9620ed5b 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module); * An error is returned if the module is no longer loaded on any attempts * to register it. */ +#define v4l2_async_register_subdev_sensor(sd) \ + __v4l2_async_register_subdev_sensor(sd, THIS_MODULE) int __must_check -v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd); +__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module); /** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous From 072a883061a461ff1d1b6a5de171ce8a85e0c9f2 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 21 Mar 2026 23:41:50 +0200 Subject: [PATCH 2665/3297] media: v4l2-subdev: Fail {enable,disable}_streams and s_streaming nicely commit 0bcbfd1c1142d85faef8df5cb679d37f71394c5f upstream. If a sub-device does not set enable_streams() and disable_streams() pad ops while it sets the s_stream() video op to v4l2_subdev_s_stream_helper(), enabling or disabling streaming either way on the sub-device will result calling v4l2_subdev_s_stream_helper() and v4l2_subdev_{enable,disable}_streams() recursively, exhausting the stack. Return -ENOIOCTLCMD in this case to handle the situation gracefully. Fixes: b62949ddaa52 ("media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams()") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/media/v4l2-core/v4l2-subdev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 25e66bf18f5fc..349022153b18e 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2506,6 +2506,10 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) u64 source_mask = 0; int pad_index = -1; + if (WARN_ON(!v4l2_subdev_has_op(sd, pad, enable_streams) || + !v4l2_subdev_has_op(sd, pad, disable_streams))) + return -ENOIOCTLCMD; + /* * Find the source pad. This helper is meant for subdevs that have a * single source pad, so failures shouldn't happen, but catch them From b88c929188e3c4e4ea7b9310419ada411189e9a3 Mon Sep 17 00:00:00 2001 From: Zile Xiong Date: Fri, 20 Mar 2026 14:54:45 +0800 Subject: [PATCH 2666/3297] media: vb2: use ssize_t for vb2_read/vb2_write commit a562d6dc86bdfdd299e1b4734977a8d63e803583 upstream. vb2_read() and vb2_write() return size_t, but propagate negative errno values from __vb2_perform_fileio(). This relies on implicit signed/unsigned conversions in callers (e.g. vb2_fop_read()) to recover error codes: __vb2_perform_fileio() -> -EINVAL vb2_read() -> (size_t)-EINVAL vb2_fop_read() -> -EINVAL This relies on implicit conversions that are not obvious. These helpers are exported (EXPORT_SYMBOL_GPL) and part of the vb2 API, so changing their return type may affect existing users. However, they conceptually follow read/write semantics, where ssize_t is typically used to return either a byte count or a negative error code. Switch vb2_read() and vb2_write() to ssize_t, and update __vb2_perform_fileio() accordingly. Signed-off-by: Zile Xiong Acked-by: Marek Szyprowski Fixes: b25748fe6126 ("[media] v4l: videobuf2: add read() and write() emulator") Cc: stable@vger.kernel.org Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/common/videobuf2/videobuf2-core.c | 12 ++++++------ include/media/videobuf2-core.h | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 2df566f409b65..28313e284bb7d 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -3006,8 +3006,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) * @read: access mode selector (1 means read, 0 means write) */ -static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock, int read) +static ssize_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock, int read) { struct vb2_fileio_data *fileio; struct vb2_fileio_buf *buf; @@ -3170,15 +3170,15 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ return ret; } -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); } EXPORT_SYMBOL_GPL(vb2_read); -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, (char __user *) data, count, ppos, nonblocking, 0); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 9b02aeba41089..b86258b98bd21 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -1106,8 +1106,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * vb2_write() - implements write() syscall logic. * @q: pointer to &struct vb2_queue with videobuf2 queue. @@ -1116,8 +1116,8 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * typedef vb2_thread_fnc - callback function for use with vb2_thread. From 16ae8c166e78757a46d7b7884f810a348b3d13c9 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 24 Jun 2025 14:29:38 +0200 Subject: [PATCH 2667/3297] media: verisilicon: Export only needed pixels formats commit e0f5d6ae76423ec5b6f97c7c3e1f02187b988afd upstream. Some pixel formats can only be produced if the decoder outputs reference pictures directly. In some cases, such as AV1 film-grain, the use of the post-processor is strictly required. In this case, only enumerate the post-processor supported formats. The exception is when V4L2_FMTDESC_FLAG_ENUM_ALL is set, in this case, we enumerate everything regardless of the state. Signed-off-by: Benjamin Gaignard Fixes: bcd4f091cf1e ("media: verisilicon: Use V4L2_FMTDESC_FLAG_ENUM_ALL flag") Cc: stable@vger.kernel.org Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/verisilicon/hantro_v4l2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index fcf3bd9bcda2d..83af9fa1ce949 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -222,6 +222,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, unsigned int num_fmts, i, j = 0; bool skip_mode_none, enum_all_formats; u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL; + bool need_postproc = ctx->need_postproc; /* * If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all @@ -230,6 +231,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv, enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL); f->index = index; + if (enum_all_formats) + need_postproc = HANTRO_AUTO_POSTPROC; + /* * When dealing with an encoder: * - on the capture side we want to filter out all MODE_NONE formats. @@ -242,7 +246,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, */ skip_mode_none = capture == ctx->is_encoder; - formats = hantro_get_formats(ctx, &num_fmts, HANTRO_AUTO_POSTPROC); + formats = hantro_get_formats(ctx, &num_fmts, need_postproc); for (i = 0; i < num_fmts; i++) { bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE; fmt = &formats[i]; From 86ece01fba2d5555c421ea94777fab592d2a0962 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Apr 2026 23:28:26 +0800 Subject: [PATCH 2668/3297] media: vidtv: fix reference leak on failed device registration commit 9aa21e1549db8882ff77b691e7714153df21dff0 upstream. When platform_device_register() fails in vidtv_bridge_init(), the embedded struct device in vidtv_bridge_dev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device: vidtv_bridge_init() -> platform_device_register(&vidtv_bridge_dev) -> device_initialize(&vidtv_bridge_dev.dev) -> setup_pdev_dma_masks(&vidtv_bridge_dev) -> platform_device_add(&vidtv_bridge_dev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: f90cf6079bf67 ("media: vidtv: add a bridge driver") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index 52b2abe16dcf5..8a8e097d2ac96 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -594,8 +594,10 @@ static int __init vidtv_bridge_init(void) int ret; ret = platform_device_register(&vidtv_bridge_dev); - if (ret) + if (ret) { + platform_device_put(&vidtv_bridge_dev); return ret; + } ret = platform_driver_register(&vidtv_bridge_driver); if (ret) From 3780ad3810718bf749151e8187bbc3c374619085 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Apr 2026 23:45:37 +0800 Subject: [PATCH 2669/3297] media: vimc: fix reference leak on failed device registration commit 33e2b833c66b890a0d71c4fa82d4c97143f7f75f upstream. When platform_device_register() fails in vimc_init(), the embedded struct device in vimc_pdev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device: vimc_init() -> platform_device_register(&vimc_pdev) -> device_initialize(&vimc_pdev.dev) -> setup_pdev_dma_masks(&vimc_pdev) -> platform_device_add(&vimc_pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: 4babf057c143f ("media: vimc: allocate vimc_device dynamically") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/test-drivers/vimc/vimc-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index f632c77e52f5d..bc0474d8059ae 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -422,6 +422,7 @@ static int __init vimc_init(void) if (ret) { dev_err(&vimc_pdev.dev, "platform device registration failed (err=%d)\n", ret); + platform_device_put(&vimc_pdev); return ret; } From 26e7a8ac286f3a5ab326dd7ed23b60c4fde20e81 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 May 2026 09:30:44 +0200 Subject: [PATCH 2670/3297] media: vivid: add vivid_update_reduced_fps() commit 1d793a29efb4260f90913f5287939bf95573b073 upstream. Don't call vivid_update_format_cap() when switching to/from reduced fps for HDMI inputs: that will also reset the format, which is overkill for this. Make a new vivid_update_reduced_fps() function that just updates the dev->timeperframe_vid_cap. Reviewed-by: Nicolas Dufresne Fixes: c79aa6aeadb0 ("[media] vivid-capture: add control for reduced frame rate") Cc: stable@vger.kernel.org Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- .../media/test-drivers/vivid/vivid-ctrls.c | 3 +- .../media/test-drivers/vivid/vivid-vid-cap.c | 32 +++++++++++-------- .../media/test-drivers/vivid/vivid-vid-cap.h | 1 + 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f94c15ff84f78..1077445f57726 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -609,7 +609,8 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) break; case VIVID_CID_REDUCED_FPS: dev->reduced_fps = ctrl->val; - vivid_update_format_cap(dev, true); + if (dev->input_type[dev->input] == HDMI) + vivid_update_reduced_fps(dev); break; case VIVID_CID_HAS_CROP_CAP: dev->has_crop_cap = ctrl->val; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 8b3162e820327..77d80217a3b8c 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -362,6 +362,24 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) return TPG_PIXEL_ASPECT_SQUARE; } +void vivid_update_reduced_fps(struct vivid_dev *dev) +{ + struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; + unsigned int size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); + u64 pixelclock; + + if (dev->reduced_fps && can_reduce_fps(bt)) { + pixelclock = div_u64(bt->pixelclock * 1000, 1001); + bt->flags |= V4L2_DV_FL_REDUCED_FPS; + } else { + pixelclock = bt->pixelclock; + bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; + } + dev->timeperframe_vid_cap = (struct v4l2_fract) { + size / 100, (u32)pixelclock / 100 + }; +} + /* * Called whenever the format has to be reset which can occur when * changing inputs, standard, timings, etc. @@ -370,8 +388,6 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) { struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; u32 dims[V4L2_CTRL_MAX_DIMS] = {}; - unsigned size; - u64 pixelclock; switch (dev->input_type[dev->input]) { case WEBCAM: @@ -400,17 +416,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) case HDMI: dev->src_rect.width = bt->width; dev->src_rect.height = bt->height; - size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); - if (dev->reduced_fps && can_reduce_fps(bt)) { - pixelclock = div_u64(bt->pixelclock * 1000, 1001); - bt->flags |= V4L2_DV_FL_REDUCED_FPS; - } else { - pixelclock = bt->pixelclock; - bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; - } - dev->timeperframe_vid_cap = (struct v4l2_fract) { - size / 100, (u32)pixelclock / 100 - }; + vivid_update_reduced_fps(dev); if (bt->interlaced) dev->field_cap = V4L2_FIELD_ALTERNATE; else diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 38a99f7e038ea..d08a85927510f 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h @@ -9,6 +9,7 @@ #define _VIVID_VID_CAP_H_ void vivid_update_quality(struct vivid_dev *dev); +void vivid_update_reduced_fps(struct vivid_dev *dev); void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); void vivid_update_outputs(struct vivid_dev *dev); void vivid_update_connected_outputs(struct vivid_dev *dev); From 492c97cb50feaa60ccd7792d3d6b904ed8ec61bf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 May 2026 09:22:41 +0200 Subject: [PATCH 2671/3297] media: vivid: check for vb2_is_busy() when toggling caps commit c2d1a2130c93f6d758af58590b86b2254c7a1dec upstream. The vivid_update_format_cap/out() functions must only be called if the capture/output queue are not busy. But for the controls that select the CROP/COMPOSE/SCALE capability that is not checked. Only when streaming starts will they be set to 'grabbed' and it is impossible to change the control, but between REQBUFS and STREAMON you are still allowed to set these controls. Since vivid_update_format_cap/out will change the format, this can cause unexpected results. Besides adding these checks, also add a WARN_ON in vivid_update_format_cap/out() if the queue is busy. I'm 90% certain that this is the cause of this syzbot bug: https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89 But since we never have reproducers, it is hard to be certain. In any case, these checks are needed regardless. Reviewed-by: Nicolas Dufresne Fixes: 73c3f48230cd ("[media] vivid: add the control handling code") Cc: stable@vger.kernel.org Reported-by: syzbot+dac8f5eaa46837e97b89@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89 Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/test-drivers/vivid/vivid-ctrls.c | 12 ++++++++++++ drivers/media/test-drivers/vivid/vivid-vid-cap.c | 6 ++++++ drivers/media/test-drivers/vivid/vivid-vid-out.c | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 1077445f57726..a8a134b36720e 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -613,14 +613,20 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) vivid_update_reduced_fps(dev); break; case VIVID_CID_HAS_CROP_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_crop_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_COMPOSE_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_compose_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_SCALER_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_scaler_cap = ctrl->val; vivid_update_format_cap(dev, true); break; @@ -1117,14 +1123,20 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_crop_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_COMPOSE_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_compose_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_SCALER_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_scaler_out = ctrl->val; vivid_update_format_out(dev); break; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 77d80217a3b8c..d1c09bdb2f3b0 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -389,6 +389,12 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; u32 dims[V4L2_CTRL_MAX_DIMS] = {}; + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_cap_q))) + return; + switch (dev->input_type[dev->input]) { case WEBCAM: default: diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 8c037b90833e7..23e1d5a189eed 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -214,6 +214,12 @@ void vivid_update_format_out(struct vivid_dev *dev) unsigned size, p; u64 pixelclock; + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_out_q))) + return; + switch (dev->output_type[dev->output]) { case SVID: default: From 1349af7f87df57940619f5b87990b799dac9ed8a Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 16 Apr 2026 00:20:58 +0800 Subject: [PATCH 2672/3297] media: vivid: fix cleanup bugs in vivid_init() commit a07c179a92e949172ca52f6d4a13202ea88cd4b7 upstream. When platform_device_register() fails in vivid_init(), the embedded struct device in vivid_pdev has already been initialized by device_initialize(), but the failure path jumps to free_output_strings without dropping the device reference for the current platform device: vivid_init() -> platform_device_register(&vivid_pdev) -> device_initialize(&vivid_pdev.dev) -> setup_pdev_dma_masks(&vivid_pdev) -> platform_device_add(&vivid_pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before jumping to the common cleanup path. Also, the unreg_driver label incorrectly calls platform_driver_register() instead of platform_driver_unregister(), which breaks cleanup when workqueue creation fails after successful driver registration. Fix that as well. The reference leak was identified by a static analysis tool I developed and confirmed by manual review. The incorrect cleanup call was found during code inspection. Fixes: f46d740fb0258 ("[media] vivid: turn this into a platform_device") Fixes: d7c969f37515d ("media: vivid: Add 'Is Connected To' menu controls") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/test-drivers/vivid/vivid-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 86506be36acb0..f44930502bdaf 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -2289,8 +2289,10 @@ static int __init vivid_init(void) } } ret = platform_device_register(&vivid_pdev); - if (ret) + if (ret) { + platform_device_put(&vivid_pdev); goto free_output_strings; + } ret = platform_driver_register(&vivid_pdrv); if (ret) goto unreg_device; @@ -2311,7 +2313,7 @@ static int __init vivid_init(void) destroy_hdmi_wq: destroy_workqueue(update_hdmi_ctrls_workqueue); unreg_driver: - platform_driver_register(&vivid_pdrv); + platform_driver_unregister(&vivid_pdrv); unreg_device: platform_device_unregister(&vivid_pdev); free_output_strings: From 4091b216d11b3ed787b18786e1fe9156a9ad6cbb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 12:08:31 +0200 Subject: [PATCH 2673/3297] media: vpif_capture: fix OF node reference imbalance commit 2282f979560af6bbc8ee2c1ee8663197312cee5b upstream. The driver reuses the OF node of the parent device but fails to take another reference to balance the one dropped by the platform bus code when unbinding the parent and releasing the child devices. Fix this by using the intended helper for reusing OF nodes. Fixes: 4a5f8ae50b66 ("[media] davinci: vpif_capture: get subdevs from DT when available") Cc: stable@vger.kernel.org # 4.13 Cc: Kevin Hilman Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/ti/davinci/vpif_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 243c6196b0247..9fbef24025744 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1499,7 +1499,7 @@ vpif_capture_get_pdata(struct platform_device *pdev, * video ports & endpoints data. */ if (pdev->dev.parent && pdev->dev.parent->of_node) - pdev->dev.of_node = pdev->dev.parent->of_node; + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) return pdev->dev.platform_data; From 2ec8f95a08fed5882a74f8f98169ab20d3fcf873 Mon Sep 17 00:00:00 2001 From: Nikita Maksimov Date: Mon, 20 Jul 2026 21:02:14 +0300 Subject: [PATCH 2674/3297] ALSA: hda/realtek: Fix speakers on Lunnen Ground 14 commit 5c3f8dac531b454bf67b6ee3c2aac89f0aaaef74 upstream. The firmware on the Lunnen Ground 14 marks pin 0x1b as unused even though the internal speakers are connected to it. As a result, the speakers are not detected. Add a pin configuration quirk for PCI subsystem ID 2782:a212 to configure pin 0x1b as an internal speaker. The pin configuration was tested on a Lunnen Ground 14 (DMI product LL4FA) with an ALC269VC codec. The internal speakers and microphone work as expected. Cc: stable@vger.kernel.org Signed-off-by: Nikita Maksimov Link: https://patch.msgid.link/20260720180214.73770-1-nickstogramm@yandex.ru Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/realtek/alc269.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 01483eb545cda..40a75d72f7125 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3742,6 +3742,7 @@ enum { ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, ALC269VC_FIXUP_INFINIX_Y4_MAX, + ALC269VC_FIXUP_LUNNEN_GROUND_14, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -4183,6 +4184,13 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC269VC_FIXUP_LUNNEN_GROUND_14] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* internal speaker */ + { } + }, + }, [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -7573,6 +7581,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), + SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), From 6a10025c7fd09a7d2af37a3ae1da188569fce470 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Tue, 14 Jul 2026 10:29:23 +0200 Subject: [PATCH 2675/3297] ALSA: seq: close a re-opened queue timer in the destructor commit 2c4dc0ed50b05cd847a4b34b8cebf0775f19aeb9 upstream. queue_delete() closes the queue timer, then frees it. snd_seq_timer_close() clears q->timer->timeri. snd_use_lock_sync() then drains borrowers, and snd_seq_timer_delete() frees q->timer. A borrower can re-open the timer inside that window. A SET_QUEUE_CLIENT that took a queueptr() use_lock reference before the queue was unlinked runs snd_seq_timer_open() after the close. Open refuses re-open only while timeri is set, and the close just cleared it, so it re-opens timeri. snd_seq_timer_delete() does not close that instance. Its snd_seq_timer_stop() is a no-op, because running was cleared first. So it frees q->timer with the instance still live. The queue is freed next. The instance stays on the global timer with callback_data pointing at the freed queue. A non-owner START on the unlocked queue arms it. The next tick derefs the freed queue in snd_seq_timer_interrupt(). Reachable by an unprivileged user with access to /dev/snd/seq. No CAP and no queue ownership required. Close any lingering instance in the destructor. There, ->timeri can no longer change: the queue is unlinked and all use_lock borrowers have drained, so no snd_seq_queue_use() can re-open it. Close it before clearing q->timer. snd_timer_close() waits for any in-flight snd_seq_timer_interrupt() to finish, and that callback still reads q->timer (via snd_seq_check_queue()), so q->timer must stay valid until it drains. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/422FDB81-2A68-47C7-A22D-2D3301E2E86D@doyensec.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_timer.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 29b018a212fc3..0256caa61c294 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -61,12 +61,23 @@ struct snd_seq_timer *snd_seq_timer_new(void) void snd_seq_timer_delete(struct snd_seq_timer **tmr) { struct snd_seq_timer *t = *tmr; - *tmr = NULL; + struct snd_timer_instance *ti; if (t == NULL) { pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n"); return; } + + scoped_guard(spinlock_irq, &t->lock) { + ti = t->timeri; + t->timeri = NULL; + } + if (ti) { + snd_timer_close(ti); + snd_timer_instance_free(ti); + } + + *tmr = NULL; t->running = 0; /* reset time */ From 3bc4de57fc7d11371f95fff652bfb0e5ce9f4386 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 15 Jul 2026 21:06:10 +0300 Subject: [PATCH 2676/3297] ALSA: hda: codecs: hdmi: disable keep-alive before audio format change commit a3d6d3cedfe87bbd5a677d52b22ac20d28e59cf8 upstream. When a keep-alive (KAE) silent stream is active on an Intel HDMI/DP codec, opening a real PCM stream reprograms the converter format and the audio infoframe in snd_hda_hdmi_generic_pcm_prepare(). Part of that reprogramming - the converter channel count and the channel mapping in snd_hda_hdmi_setup_audio_infoframe() - is not safe to do while a keep-alive stream is active. This is most visible when switching to a multichannel PCM configuration, where the active channel count actually changes. In that case the newly opened PCM stream plays no sound. Add an optional hdmi_ops .prepare hook, called at the start of the PCM prepare sequence (before the format and infoframe are touched), and implement it for HSW+ to release keep-alive. Keep-alive is then re-enabled as before once the new stream has been set up, in the setup_stream op. Fixes: 15175a4f2bbb ("ALSA: hda/hdmi: add keep-alive support for ADL-P and DG2") Reported-by: Alexander Kaplan Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8412 Tested-by: Alexander Kaplan Cc: Signed-off-by: Kai Vehmanen Link: https://patch.msgid.link/20260715180610.1371243-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/hdmi/hdmi.c | 3 +++ sound/hda/codecs/hdmi/hdmi_local.h | 9 ++++++++ sound/hda/codecs/hdmi/intelhdmi.c | 36 +++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 1036d08d1431a..f427d04176e92 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -1688,6 +1688,9 @@ int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, per_pin->channels = substream->runtime->channels; per_pin->setup = true; + if (spec->ops.prepare) + spec->ops.prepare(codec, per_pin); + if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, substream); diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h index 548241ad3fa99..dce9a335cff28 100644 --- a/sound/hda/codecs/hdmi/hdmi_local.h +++ b/sound/hda/codecs/hdmi/hdmi_local.h @@ -74,6 +74,15 @@ struct hdmi_ops { hda_nid_t pin_nid, int dev_id, u32 stream_tag, int format); + /* + * Optional hook invoked at the beginning of the PCM prepare + * sequence, before the audio infoframe and stream format are + * (re)programmed. Used to disable keep-alive / silent stream so + * that the format change is not done while keep-alive is active. + */ + void (*prepare)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin); + void (*pin_cvt_fixup)(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid); diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index 23237d5274302..3a942343bc989 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -418,6 +418,28 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); } +/* + * prepare ops override for HSW+ + * + * Disable keep-alive before the converter format and audio infoframe are + * reprogrammed by the PCM prepare sequence. Changing the audio format (e.g. + * the channel count when switching to multichannel PCM) while a keep-alive + * stream is active is not safe, so release keep-alive here, early in the + * sequence. It is re-enabled once the new stream has been set up, in + * i915_hsw_setup_stream(). + */ +static void i915_hsw_prepare(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin->silent_stream) { + silent_stream_set_kae(codec, per_pin, false); + /* wait for pending transfers in codec to clear */ + usleep_range(100, 200); + } +} + /* setup_stream ops override for HSW+ */ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, int dev_id, u32 stream_tag, @@ -435,15 +457,16 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, haswell_verify_D0(codec, cvt_nid, pin_nid); - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - silent_stream_set_kae(codec, per_pin, false); - /* wait for pending transfers in codec to clear */ - usleep_range(100, 200); - } - res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, stream_tag, format); + /* + * Keep-alive was disabled in i915_hsw_prepare(), re-enable it now. + * The pin lookup above resolves to the same per_pin that prepare + * used (pin_nid comes from that per_pin), so this stays balanced; a + * NULL per_pin only occurs on a lookup failure that also implies no + * active keep-alive stream to restore. + */ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { usleep_range(100, 200); silent_stream_set_kae(codec, per_pin, true); @@ -607,6 +630,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, codec->depop_delay = 0; codec->auto_runtime_pm = 1; + spec->ops.prepare = i915_hsw_prepare; spec->ops.setup_stream = i915_hsw_setup_stream; spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; spec->ops.silent_stream = i915_set_silent_stream; From 426c0ff1c433d6030610ad4f9375746dfe931caa Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 20 Jul 2026 09:14:12 +0200 Subject: [PATCH 2677/3297] ALSA: timer: drain a slave's callback before its master detaches it commit bdefe1346a8e6b8dc8593406dc2617e985fcbcab upstream. snd_timer_close_locked() drains the closing instance's own in-flight callback (IFLG_CALLBACK) before freeing it, but not its slaves'. When a master instance is closed, remove_slave_links() clears each slave's ->timer; the slave's own close then reads timer == NULL and takes the branch that skips the drain entirely (snd_timer_stop_slave() also no-ops on a NULL timer). So a slave whose callback is still running when the master is closed is freed underneath the live callback, leading to use-after-free. Drain the slaves too before remove_slave_links() severs them. snd_timer_stop() has already taken this instance off the active list, so no new slave callback can be queued. Take the slaves off the ack list so a pending one can't fire either, then wait for any that is already in flight. Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/D26598EB-DBF7-4D76-9F71-8E4BD59822D4@doyensec.com Signed-off-by: Greg Kroah-Hartman --- sound/core/timer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 35651c273b2f1..384baf24c866f 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -437,10 +437,20 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri, snd_timer_stop(timeri); if (timer) { + struct snd_timer_instance *slave; + bool busy; + timer->num_instances--; - /* wait, until the active callback is finished */ + /* unqueue then drain the slaves' callbacks before remove_slave_links() severs them */ spin_lock_irq(&timer->lock); - while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { + list_for_each_entry(slave, &timeri->slave_list_head, open_list) + list_del_init(&slave->ack_list); + for (;;) { + busy = timeri->flags & SNDRV_TIMER_IFLG_CALLBACK; + list_for_each_entry(slave, &timeri->slave_list_head, open_list) + busy |= slave->flags & SNDRV_TIMER_IFLG_CALLBACK; + if (!busy) + break; spin_unlock_irq(&timer->lock); udelay(10); spin_lock_irq(&timer->lock); From 1395327a96614885552bae5fbb650e6dd182d49b Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 20 Jul 2026 09:09:55 +0200 Subject: [PATCH 2678/3297] ALSA: timer: don't re-enter an instance callback that is still running commit 70d28bfcd6224eed75986b3b987b997e59643fa4 upstream. The userspace-driven timer (utimer) TRIGGER ioctl calls snd_timer_interrupt() directly with no serialization, so two threads triggering the same utimer can run snd_timer_interrupt() on one snd_timer concurrently. snd_timer_process_callbacks() drops timer->lock around each instance callback and marks the in-flight callback with the single SNDRV_TIMER_IFLG_CALLBACK bit; snd_timer_close_locked() waits on that bit to drain an in-flight callback before freeing the instance. The bit cannot represent two concurrent callbacks: when a second interrupt re-queues an instance whose callback is still running, both run at once, the first to finish clears the bit, and the close-path drain then frees the instance (and its callback_data) while the other callback is still live - a use-after-free reachable by any user able to open /dev/snd/timer, both via a user timer instance and via a sequencer queue timer bound to the utimer. snd_timer_interrupt() sets IFLG_CALLBACK before dropping timer->lock, so a concurrent interrupt already observes it under the lock. Skip re-queuing an instance (and its slaves) to the ack/sack list while its callback is in flight; the accumulated pticks are delivered on the next tick, so no event is lost. Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Cc: stable@vger.kernel.org Suggested-by: Takashi Iwai Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/6F9B6501-8E65-4265-B02C-7EFB240D1664@doyensec.com Signed-off-by: Greg Kroah-Hartman --- sound/core/timer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 384baf24c866f..a2c344b4cc73a 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -883,12 +883,15 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ack_list_head = &timer->ack_list_head; else ack_list_head = &timer->sack_list_head; - if (list_empty(&ti->ack_list)) + /* don't requeue an instance whose callback is still running */ + if (list_empty(&ti->ack_list) && + !(ti->flags & SNDRV_TIMER_IFLG_CALLBACK)) list_add_tail(&ti->ack_list, ack_list_head); list_for_each_entry(ts, &ti->slave_active_head, active_list) { ts->pticks = ti->pticks; ts->resolution = resolution; - if (list_empty(&ts->ack_list)) + if (list_empty(&ts->ack_list) && + !(ts->flags & SNDRV_TIMER_IFLG_CALLBACK)) list_add_tail(&ts->ack_list, ack_list_head); } } From 58c6c8dc2e022e1b4f3dc58725a1ca49ff470f9c Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 2 Jul 2026 00:50:20 +0000 Subject: [PATCH 2679/3297] wifi: ath6kl: fix OOB access from firmware ADDBA window size commit 44126b6994eeb28f2103b638e698f40a1244f327 upstream. aggr_recv_addba_req_evt() logs a debug message when the firmware-supplied win_sz is outside [AGGR_WIN_SZ_MIN, AGGR_WIN_SZ_MAX] but does not return. The out-of-range win_sz is then used in TID_WINDOW_SZ() to compute a kzalloc size and stored in rxtid->hold_q_sz, leading to zero-size or overflowed allocations and subsequent out-of-bounds access. Clean up any previously active aggregation session for the TID first, then return early when win_sz is out of the valid range, instead of proceeding with a broken allocation size. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Cc: stable@vger.kernel.org Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Tristan Madani Link: https://patch.msgid.link/20260702005020.708717-1-tristmd@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath6kl/txrx.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index c3b06b515c4f4..312b5a7164c05 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1723,13 +1723,15 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no, rxtid = &aggr_conn->rx_tid[tid]; - if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) - ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", - __func__, win_sz, tid); - if (rxtid->aggr) aggr_delete_tid_state(aggr_conn, tid); + if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", + __func__, win_sz, tid); + return; + } + rxtid->seq_next = seq_no; hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q); rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL); From 18965470d41e69d3fc10eb62afae29d10f4cdfd1 Mon Sep 17 00:00:00 2001 From: Daniel Hodges Date: Fri, 6 Feb 2026 13:52:07 -0500 Subject: [PATCH 2680/3297] wifi: ath6kl: fix use-after-free in aggr_reset_state() commit ba7debb4dd6427386862220e8335a53a4bfc235d upstream. The aggr_reset_state() function uses timer_delete() (non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers like aggr_module_destroy(). If the timer callback (aggr_timeout) is executing when aggr_reset_state() is called, the callback will continue to access aggr_conn fields like rx_tid[] and stat[] which may be freed immediately after by kfree(aggr_info->aggr_conn) in aggr_module_destroy(). Additionally, the timer callback can re-arm itself via mod_timer() while aggr_reset_state() is running, creating a more complex race condition. Use timer_delete_sync() instead to ensure any running timer callback has completed before returning. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Cc: stable@vger.kernel.org Signed-off-by: Daniel Hodges Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260206185207.30098-1-git@danielhodges.dev Signed-off-by: Jeff Johnson Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 312b5a7164c05..5fd9cfdb56717 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1830,7 +1830,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn) return; if (aggr_conn->timer_scheduled) { - timer_delete(&aggr_conn->timer); + timer_delete_sync(&aggr_conn->timer); aggr_conn->timer_scheduled = false; } From 9375a4ea4121625ef27a46b74781cda66a5cc61b Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 12:30:42 +0200 Subject: [PATCH 2681/3297] wifi: mwifiex: fix NULL dereference when the AP has HT-cap but no HT-oper commit c3d68e294cbb6a4090bb219d3dcaca85a011809b upstream. mwifiex_tdls_add_ht_oper() gates its follow-the-AP-bandwidth path on bss_desc->bcn_ht_cap being present, but then dereferences a different pointer, bss_desc->bcn_ht_oper: if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && bss_desc->bcn_ht_cap && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) bcn_ht_cap and bcn_ht_oper are populated independently while parsing the associated AP's beacon in mwifiex_update_bss_desc_with_ie(): an AP that advertises an HT Capabilities element but no HT Operation element leaves bcn_ht_cap non-NULL and bcn_ht_oper NULL. Setting up a TDLS link to a peer while associated to such an AP then dereferences the NULL bcn_ht_oper and crashes the kernel. Every other bcn_ht_oper user in the driver NULL-checks it first. Guard on the pointer that is actually dereferenced. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: 396939f94084 ("mwifiex: add HT operation IE in TDLS setup confirm") Cc: stable@vger.kernel.org Assisted-by: 0sec:multi-model Signed-off-by: Doruk Tan Ozturk Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260716103042.88469-1-doruk@0sec.ai Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/mwifiex/tdls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 77a9a6de636d1..b94ca13086d67 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -215,7 +215,7 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, /* follow AP's channel bandwidth */ if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && - bss_desc->bcn_ht_cap && + bss_desc->bcn_ht_oper && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; From e511e93abd6eeedcd5b3c55516241f414fbde64a Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Tue, 14 Jul 2026 17:17:58 +0800 Subject: [PATCH 2682/3297] wifi: wilc1000: validate assoc response length before subtracting header commit 4c4c97b60a5e978121d9ee8cb0ab3916e5d6a8de upstream. wilc_parse_assoc_resp_info() computes the trailing IE length as ies_len = buffer_len - sizeof(*res); without first checking that buffer_len is at least sizeof(struct wilc_assoc_resp) (6 bytes). buffer_len is the length reported for a received association response (host_int_parse_assoc_resp_info() passes hif_drv->assoc_resp / assoc_resp_info_len straight in) and must be validated before the driver accesses the fixed header. For a frame shorter than the 6-byte fixed header, the subtraction wraps. For a four-byte response the result is truncated to a u16 ies_len of 65534, so kmemdup() then attempts to copy 65534 bytes starting at buffer + sizeof(*res), beyond the valid association-response data (CWE-125). A response shorter than four bytes can also cause an out-of-bounds read of res->status_code at offsets 2 and 3. Reject frames too short to hold the fixed header before touching the header or computing ies_len. Also set the connection status to a failure on this path: the caller falls through to a "conn_info->status == WLAN_STATUS_SUCCESS" check after the parser returns, so leaving the status untouched could let a malformed short response be treated as a successful association. Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Huihui Huang Link: https://patch.msgid.link/20260714091811.3596126-1-hhhuang@smu.edu.sg Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/microchip/wilc1000/hif.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index d795ca05729ab..f08426ca51135 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -600,6 +600,11 @@ static s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len, u16 ies_len; struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer; + if (buffer_len < sizeof(*res)) { + ret_conn_info->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + return -EINVAL; + } + ret_conn_info->status = le16_to_cpu(res->status_code); if (ret_conn_info->status == WLAN_STATUS_SUCCESS) { ies = &buffer[sizeof(*res)]; From ab4d213393e846baa6437497f94dda7553cbeda7 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Sat, 27 Jun 2026 12:13:36 -0700 Subject: [PATCH 2683/3297] wifi: mt76: mt7615: drop TXRX_NOTIFY on non-mmio buses commit 39afc46c0243d10b7795e6e6cf4ae91f41732120 upstream. PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7615_rx_check() and mt7615_queue_rx_skb() dispatch it to mt7615_mac_tx_free() on every bus. mt7615_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on the mt7663 USB and SDIO buses it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker. Same defect as the mt7921 and mt7925 patches in this series. Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices"). Fixes: eb99cc95c3b6 ("mt76: mt7615: introduce mt7663u support") Cc: stable@vger.kernel.org Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260627191336.20223-4-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 13c9dbeaee84e..6c7debe439b6c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1601,6 +1601,8 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) + return false; mt7615_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: @@ -1634,6 +1636,10 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, dev_kfree_skb(skb); break; case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) { + dev_kfree_skb(skb); + break; + } mt7615_mac_tx_free(dev, skb->data, skb->len); dev_kfree_skb(skb); break; From 263816e92e8d66c81c98ccab2b5d2191ed08ec71 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Sat, 27 Jun 2026 12:13:34 -0700 Subject: [PATCH 2684/3297] wifi: mt76: mt7921: drop TXRX_NOTIFY on non-mmio buses commit da4082e91acabc1498611ed8ccc53f0610baefc6 upstream. PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7921_rx_check() and mt7921_queue_rx_skb() dispatch it to mt7921_mac_tx_free() on every bus. mt7921_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on USB and SDIO it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Call Trace: mt7921_mac_tx_free+0x64/0x310 [mt7921_common] mt7921_rx_check+0x5f/0xf0 [mt7921_common] mt76u_rx_worker+0x1b9/0x620 [mt76_usb] Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices"). Fixes: 48fab5bbef40 ("mt76: mt7921: introduce mt7921s support") Cc: stable@vger.kernel.org Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260627191336.20223-2-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index d2731e9953bf8..251a9a1f664d5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -569,8 +569,9 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ - mt7921_mac_tx_free(dev, data, len); /* mmio */ + if (!mt76_is_mmio(mdev)) + return false; + mt7921_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: for (rxd += 2; rxd + 8 <= end; rxd += 8) @@ -599,7 +600,10 @@ void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } mt7921_mac_tx_free(dev, skb->data, skb->len); napi_consume_skb(skb, 1); break; From 9cb72f67e1502aabba51aab9ac04ae7c386ee194 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Sat, 27 Jun 2026 12:13:35 -0700 Subject: [PATCH 2685/3297] wifi: mt76: mt7925: drop TXRX_NOTIFY on non-mmio buses commit feeff151c83e7f0ffcdedcad5343852d23d1f6e1 upstream. PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7925_rx_check() and mt7925_queue_rx_skb() dispatch it to mt7925_mac_tx_free() on every bus. mt7925_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on USB it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Call Trace: mt7925_mac_tx_free+0x58/0x350 [mt7925_common] mt7925_rx_check+0xe2/0x130 [mt7925_common] mt76u_rx_worker+0x1b9/0x620 [mt76_usb] Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices"). Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Cc: stable@vger.kernel.org Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260627191336.20223-3-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 2ff74d0bef9d0..6a74779430916 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1193,8 +1193,9 @@ bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ - mt7925_mac_tx_free(dev, data, len); /* mmio */ + if (!mt76_is_mmio(mdev)) + return false; + mt7925_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: for (rxd += 4; rxd + 12 <= end; rxd += 12) @@ -1230,7 +1231,10 @@ void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } mt7925_mac_tx_free(dev, skb->data, skb->len); napi_consume_skb(skb, 1); break; From 044fca8f45ba9ab6ca526163155234cf88287ff5 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Sat, 18 Jul 2026 02:43:52 +0000 Subject: [PATCH 2686/3297] wifi: brcmfmac: make release_scratchbuffers idempotent commit 538c51e9d124cf656f2dd0c0394a8545efc7102d upstream. brcmf_pcie_release_scratchbuffers() frees the shared.scratch and shared.ringupd DMA buffers with dma_free_coherent() but does not clear the pointers afterwards, unlike the sibling release_ringbuffers() which NULLs commonrings/flowrings/idxbuf on release. Both the bus_reset .reset callback (brcmf_pcie_reset) and brcmf_pcie_remove() call release_scratchbuffers. When reset teardown has run before removal, remove's own teardown would call dma_free_coherent() a second time on the already-freed DMA allocation. NULL the pointers after free, matching release_ringbuffers(), so a later release observes that the allocation has already been released. This patch makes repeated sequential release safe; the reset-work lifetime is handled separately by the following patch. This issue was found by an in-house static analysis tool. Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Assisted-by: Codex:gpt-5.6 Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260718024353.3147201-2-fanwu01@zju.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 6327f4eca5007..fe4f23decc705 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1383,16 +1383,20 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) static void brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) { - if (devinfo->shared.scratch) + if (devinfo->shared.scratch) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_SCRATCH_BUF_LEN, devinfo->shared.scratch, devinfo->shared.scratch_dmahandle); - if (devinfo->shared.ringupd) + devinfo->shared.scratch = NULL; + } + if (devinfo->shared.ringupd) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_RINGUPD_BUF_LEN, devinfo->shared.ringupd, devinfo->shared.ringupd_dmahandle); + devinfo->shared.ringupd = NULL; + } } static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) From efe9de178e4b965eed6da080588294f4ad1d07b7 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Wed, 15 Jul 2026 14:49:38 +0800 Subject: [PATCH 2687/3297] wifi: brcmfmac: set F2 blocksize to 256 for BCM43752 commit 29ab31f3f27157648f2f7e6d5e1fd9792fdf0614 upstream. The BCM43752 is not reliable with the default 512-byte SDIO function 2 block size: on an i.MX8MP board with an AMPAK AP6275S module at SDR104 / 200 MHz, an iperf TX stress test kills WLAN within seconds: mmc_submit_one: CMD53 sg block write failed -84 brcmf_sdio_dpc: failed backplane access over SDIO, halting operation Commit d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") set up the 43752 like the 4373 for the F2 watermark but missed the F2 block size, which the 4373 limits to 256 bytes. The vendor driver (bcmdhd) also programs a 256-byte F2 block size for this chip and runs the same hardware without errors. Group the 43752 with the 4373, matching the F2 watermark handling. With this change a 10-minute bidirectional iperf3 soak completes with zero SDIO errors at ~270 Mbit/s in each direction. Backporting note: kernels before v6.18 name this id SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, so on those trees the case label added by this patch must be adjusted to that name. Cherry-picking the rename commit 74e2ef72bd4b ("wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW)") first is not a clean alternative: on trees before v6.17 its context collides with the 43751 additions, and trees before v6.2 lack the FWVID framework it touches. Fixes: d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") Cc: stable@vger.kernel.org # see patch description, needs adjustments for <= 6.17 Signed-off-by: LiangCheng Wang Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260715-b43752-f2-blksz-v2-1-f9be49856050@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 13952dfeb3e30..0beb38047df51 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -911,6 +911,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) return ret; } switch (sdiodev->func2->device) { + case SDIO_DEVICE_ID_BROADCOM_43752: case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373: f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE; break; From 0dbaff14fd6a8b7bbfe7e1379b17be0ffe4ffaf8 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 09:25:28 +0000 Subject: [PATCH 2688/3297] wifi: ath11k: fix refcount leak in ath11k_ahb_fw_resources_init() commit 0e120ee0822b7cc650bd7b29682a34e137cec10d upstream. of_get_child_by_name() returns a node pointer with refcount incremented, but the error path when ath11k_ahb_setup_msa_resources() fails does not release it. Add the missing of_node_put() to avoid leaking the reference. Cc: stable@vger.kernel.org Fixes: 095cb947490c ("wifi: ath11k: allow missing memory-regions") Signed-off-by: Wentao Liang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260609092528.220547-1-vulab@iscas.ac.cn Signed-off-by: Jeff Johnson Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath11k/ahb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 8dfe9b40c1262..b8437e23e5192 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -998,6 +998,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) ret = ath11k_ahb_setup_msa_resources(ab); if (ret) { ath11k_err(ab, "failed to setup msa resources\n"); + of_node_put(node); return ret; } From 875479f18835ac11e21a83e88f3d4dc7ccdcd0c4 Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Thu, 25 Jun 2026 20:29:11 +0000 Subject: [PATCH 2689/3297] staging: rtl8723bs: fix OOB reads in rtw_get_wps_ie() commit 0e95ff792ae0aa6fbad9455943e9e1e4062670e9 upstream. rtw_get_wps_ie() iterates over IE data from network frames without validating that the IE header and payload fit within the remaining buffer before reading them. Specifically: - in_ie[cnt + 1] is read without checking cnt + 1 < in_len - memcmp(&in_ie[cnt + 2], ...) accesses cnt + 2 without bounds check - in_ie[cnt + 1] is used as length without verifying payload fits Add bounds checks at the top of the loop body to break early if fewer than 2 bytes remain for the IE header, or if the declared payload extends past the end of the buffer. Also require at least 4 bytes of payload before comparing the WPS OUI. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Moksh Panicker Link: https://patch.msgid.link/20260625202911.26782-1-mokshpanicker.7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 01ab7fb5e5bc6..e61bc9c6bb21e 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -679,7 +679,14 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) while (cnt < in_len) { eid = in_ie[cnt]; - if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { + if (cnt + 2 > in_len) + break; + + if (in_ie[cnt + 1] + 2 > in_len - cnt) + break; + + if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (in_ie[cnt + 1] >= 4) && + (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { wpsie_ptr = &in_ie[cnt]; if (wps_ie) From 91eff666c9078921e88d518b517cbfcf948bd85e Mon Sep 17 00:00:00 2001 From: MinJea Kim Date: Tue, 14 Jul 2026 22:14:21 +0900 Subject: [PATCH 2690/3297] staging: rtl8723bs: fix inverted HT40 secondary channel offset commit 30d49cba27f8905bc288cef5846963f0004f644c upstream. rtw_get_chan_type() maps the driver's channel offset to nl80211 channel types the wrong way around. In this driver HAL_PRIME_CHNL_OFFSET_LOWER means the primary channel is the lower 20 MHz half of the 40 MHz pair, i.e. the secondary channel is above the primary one: rtw_get_center_ch() computes the center channel as "channel + 2" for OFFSET_LOWER, and bwmode_update_check() sets OFFSET_LOWER when the AP's HT operation IE announces SCA (secondary channel above). In nl80211 terms that is NL80211_CHAN_HT40PLUS, not HT40MINUS. Because of the inversion, cfg80211_rtw_get_channel() reports an HT40+ association as HT40-. For an HT40+ AP on a low channel (e.g. channel 3) the resulting chandef spans below the 2.4 GHz band edge and is invalid, so the regulatory core tears the connection down 60 seconds (REG_ENFORCE_GRACE_MS) after the AP's country IE triggers a regdomain change: reg_check_chans_work() considers the reported chandef unusable and calls cfg80211_leave(). The supplicant then reconnects, the country IE changes the regdomain again, and the cycle repeats, causing a disconnect/reconnect loop every ~65 seconds for as long as the link is up. Observed on a TECLAST X80 Power tablet (RTL8723BS) associated to an HT40+ AP on channel 3 with a KR country IE; a kprobe trace showed cfg80211_disconnect() being invoked from reg_check_chans_work(). With the mapping fixed, "iw dev wlan0 info" reports the correct "width: 40 MHz, center1: 2432 MHz" and the periodic disconnects stop. Fixes: 5402cc178c5d ("staging: rtl8723bs: add get_channel cfg80211 implementation") Cc: stable@vger.kernel.org Assisted-by: Claude-Code:claude-fable-5 bpftrace Signed-off-by: MinJea Kim Link: https://patch.msgid.link/20260714131421.3980-1-qndkdrnl@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 73fc1d4f92e6c..1bc60be309dd6 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -1958,7 +1958,7 @@ static u8 rtw_get_chan_type(struct adapter *adapter) else return NL80211_CHAN_NO_HT; case CHANNEL_WIDTH_40: - if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER) + if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER) return NL80211_CHAN_HT40PLUS; else return NL80211_CHAN_HT40MINUS; From a42f5536ea9c00e13f0c0fbb330feed95e2365ca Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Mon, 20 Jul 2026 00:24:27 +0800 Subject: [PATCH 2691/3297] Bluetooth: hci_sync: Protect UUID list traversal commit e9027ffbf5a0f3c12ca8900822e884eae9f0821b upstream. The hci_sync conversion moved class-of-device and EIR generation from an HCI request built under hdev->lock to asynchronous command sync work. The worker holds hdev->req_lock, but that lock does not serialize access to hdev->uuids against add_uuid() and remove_uuid(), which update the list under hdev->lock. The following interleaving can therefore occur: CPU0 (command sync work) CPU1 (management socket) fetch uuid from the list list_del(&uuid->list) kfree(uuid) read uuid->size KASAN reports the resulting use-after-free: BUG: KASAN: slab-use-after-free in eir_create+0xb8f/0xee0 Read of size 1 at addr ffff88810dbd8620 by task kworker/u17:0/87 Workqueue: hci0 hci_cmd_sync_work Call Trace: eir_create+0xb8f/0xee0 hci_update_eir_sync+0x1c0/0x330 hci_cmd_sync_work+0x13c/0x290 process_one_work+0x63a/0x1070 worker_thread+0x45b/0xd10 Allocated by task 86: __kasan_kmalloc+0x8f/0xa0 add_uuid+0x18a/0x4b0 hci_sock_sendmsg+0x1033/0x1ea0 Freed by task 92: __kasan_slab_free+0x43/0x70 kfree+0x131/0x3c0 remove_uuid+0x25e/0x560 hci_sock_sendmsg+0x1033/0x1ea0 Hold hdev->lock while generating and committing the class-of-device and EIR snapshots. Release it before sending an HCI command, so controller waits do not happen under the device lock. This protects all UUID list walks in these paths and restores the serialization lost in the command sync conversion. Fixes: 161510ccf91c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 1") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hci_sync.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 651caa4a05d36..65c8a490c5553 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -929,12 +929,16 @@ int hci_update_eir_sync(struct hci_dev *hdev) memset(&cp, 0, sizeof(cp)); + hci_dev_lock(hdev); eir_create(hdev, cp.data); - if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) + if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) { + hci_dev_unlock(hdev); return 0; + } memcpy(hdev->eir, cp.data, sizeof(cp.data)); + hci_dev_unlock(hdev); return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp, HCI_CMD_TIMEOUT); @@ -966,6 +970,7 @@ int hci_update_class_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) return 0; + hci_dev_lock(hdev); cod[0] = hdev->minor_class; cod[1] = hdev->major_class; cod[2] = get_service_classes(hdev); @@ -973,8 +978,12 @@ int hci_update_class_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) cod[1] |= 0x20; - if (memcmp(cod, hdev->dev_class, 3) == 0) + if (memcmp(cod, hdev->dev_class, 3) == 0) { + hci_dev_unlock(hdev); return 0; + } + + hci_dev_unlock(hdev); return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod, HCI_CMD_TIMEOUT); From 780b04d09c941262ee2a2b4a09906451b69df8a6 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Mon, 20 Jul 2026 00:03:11 +0800 Subject: [PATCH 2692/3297] Bluetooth: RFCOMM: Fix session UAF in set_termios commit c783399efc22d035443f1dfbf2a09bf9562aaa5e upstream. rfcomm_tty_set_termios() tests dlc->session without rfcomm_mutex and later passes the pointer to rfcomm_send_rpn(). The latter dereferences both session->initiator and session->sock. Meanwhile, krfcommd can unlink the DLC and free the session while holding rfcomm_mutex. The race can proceed as follows: TTY ioctl task krfcommd -------------- -------- load dlc->session enter rfcomm_send_rpn() lock rfcomm_mutex clear dlc->session free session unlock rfcomm_mutex read session->initiator KASAN reported: BUG: KASAN: slab-use-after-free in rfcomm_send_rpn+0x297/0x2a0 Read of size 4 at addr ffff88810012a850 by task poc/92 Call Trace: rfcomm_send_rpn+0x297/0x2a0 rfcomm_tty_set_termios+0x50d/0x850 tty_set_termios+0x596/0x950 set_termios+0x46a/0x6e0 tty_mode_ioctl+0x152/0xbd0 tty_ioctl+0x915/0x1240 __x64_sys_ioctl+0x134/0x1c0 Allocated by task 92: rfcomm_session_add+0x9e/0x2e0 rfcomm_dlc_open+0x8b1/0xe00 rfcomm_dev_activate+0x85/0x1a0 rfcomm_tty_open+0x90/0x280 Freed by task 68: kfree+0x131/0x3c0 rfcomm_session_del+0x119/0x180 rfcomm_run+0x737/0x4710 Add rfcomm_dlc_send_rpn(), which holds rfcomm_mutex while it verifies that the DLC is still attached and sends the RPN frame. Have the TTY path use the helper and drop its unlocked session check. This keeps the session valid through both the frame construction and socket send. Fixes: 3a5e903c09ae ("[Bluetooth]: Implement RFCOMM remote port negotiation") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/rfcomm.h | 3 +++ net/bluetooth/rfcomm/core.c | 17 +++++++++++++++++ net/bluetooth/rfcomm/tty.c | 7 +++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index c058824769002..7e40d3c5f5a97 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -229,6 +229,9 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, u8 bit_rate, u8 data_bits, u8 stop_bits, u8 parity, u8 flow_ctrl_settings, u8 xon_char, u8 xoff_char, u16 param_mask); +int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits, + u8 stop_bits, u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask); /* ---- RFCOMM DLCs (channels) ---- */ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index dfade1933fa73..d2c869e8286b2 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1031,6 +1031,23 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, return rfcomm_send_frame(s, buf, ptr - buf); } +int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits, + u8 stop_bits, u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask) +{ + int err = -ENOTCONN; + + rfcomm_lock(); + if (d->session) + err = rfcomm_send_rpn(d->session, 1, d->dlci, bit_rate, + data_bits, stop_bits, parity, + flow_ctrl_settings, xon_char, xoff_char, + param_mask); + rfcomm_unlock(); + + return err; +} + static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) { struct rfcomm_hdr *hdr; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index b783526ab5884..63f208abbd514 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -861,7 +861,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, BT_DBG("tty %p termios %p", tty, old); - if (!dev || !dev->dlc || !dev->dlc->session) + if (!dev || !dev->dlc) return; /* Handle turning off CRTSCTS */ @@ -982,9 +982,8 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, } if (changes) - rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud, - data_bits, stop_bits, parity, - RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); + rfcomm_dlc_send_rpn(dev->dlc, baud, data_bits, stop_bits, parity, + RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); } static void rfcomm_tty_throttle(struct tty_struct *tty) From 2bc6bf70d41055377f390d06f0f3521deb62fd3b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 21 Jul 2026 12:08:49 +0200 Subject: [PATCH 2693/3297] exec: fix unsigned loop counter wrap in transfer_args_to_stack() commit 16cc4f5c1c4b9e45eca7f7deefa5410a292db599 upstream. The stop value is derived from bprm->p >> PAGE_SHIFT. The index variable is an unsigned long. If bprm->p drops below PAGE_SIZE and stop becomes zero the loop condition index >= stop is always true. After the index == 0 iteration the decrement wraps to ULONG_MAX and bprm->page[ULONG_MAX] reads sizeof(void *) bytes in front of the array. The pointer has wrapped to -1. That garbage pointer is then passed to kmap_local_page() and PAGE_SIZE bytes are copied from wherever that lands into the stack of the process being created. And the loop doesn't terminate either... Getting there only requires bprm->p < PAGE_SIZE. On !MMU bprm_set_stack_limit() and bprm_hit_stack_limit() are empty. So the only constraint on how far bprm->p is pushed down is valid_arg_len(), i.e. that each individual string still fits in what is left. bprm->p starts at PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *) so a single argument or environment string of a little over 31 pages leaves it in the first page: Oops - load access fault [#1] CPU: 0 UID: 0 PID: 1 Comm: victim Not tainted 7.2.0-rc4 #1 epc : __memcpy+0xd4/0xf8 ra : transfer_args_to_stack+0xaa/0xae s4 : ffffffffffffffff s2 : 0000000000000000 a1 : ffffffdc98000000 a2 : 0000000000001000 status: 0000000a00001880 badaddr: ffffffdc98000000 cause: 0000000000000005 [<801a5324>] __memcpy+0xd4/0xf8 [<800d5f6a>] load_flat_binary+0x43a/0x65e [<800a2de4>] bprm_execve+0x1d4/0x316 [<800a351a>] do_execveat_common+0x12e/0x138 [<800a3d44>] __riscv_sys_execve+0x38/0x4e Kernel panic - not syncing: Fatal exception in interrupt This is an arcane bug but we should still fix it. Count down from MAX_ARG_PAGES so the loop ends when index reaches stop, stop == 0 included. The iterations performed are unchanged for every other value of stop. Only CONFIG_MMU=n builds are affected, transfer_args_to_stack() is used by binfmt_flat and binfmt_elf_fdpic on nommu only. The loop predates git history. commit 7e7ec6a93434 ("elf_fdpic_transfer_args_to_stack(): make it generic") only moved it from binfmt_elf_fdpic.c into fs/exec.c and narrowed the copy to the used part of the first page. The condition and the decrement are unchanged from 2.6.12-rc2. Link: https://patch.msgid.link/20260721-hochachtung-staumauer-pigmente-15d71f7d7d04@brauner Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: David Hildenbrand (Arm) Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/exec.c b/fs/exec.c index 4298e7e08d5d7..6eb1c8da2c7db 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -736,7 +736,7 @@ int transfer_args_to_stack(struct linux_binprm *bprm, stop = bprm->p >> PAGE_SHIFT; sp = *sp_location; - for (index = MAX_ARG_PAGES - 1; index >= stop; index--) { + for (index = MAX_ARG_PAGES; index-- > stop; ) { unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK : 0; char *src = kmap_local_page(bprm->page[index]) + offset; sp -= PAGE_SIZE - offset; From 1cd4e9b7967dab48c9f79a00b06ffff7208c0993 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 20 Jul 2026 14:36:49 +0200 Subject: [PATCH 2694/3297] binfmt_misc: set have_execfd only once the interpreter is opened commit bbf5f639918dc011aaf60aab8480218758ee68c5 upstream. load_misc_binary() raises bprm->have_execfd as soon as it sees the 'O' (or 'C') flag. This happens well before it opens the interpreter. If that open fails the flag stays set on the bprm. binfmt_misc is at the head of the format list so an interpreter open failure that returns -ENOEXEC lets the search fall through to a later format. This means it runs the matched binary directly having never staged an interpreter. So bprm->executable is NULL while have_execfd falsely claims a descriptor is present. Consequently, begin_new_exec() dereferences the missing executable: would_dump(bprm, bprm->executable); and NULL derefs. Had it not, the hand-off later in the same function would have failed anyway. FD_ADD(0, bprm->executable) rejects a NULL file with -ENOMEM. Both sites are past the point of no return so the exec cannot be unwound either way. This can be reached by unprivileged users as binfmt_misc can be mounted in user namespaces. So a user can register an 'O' entry whose interpreter lives on a FUSE mount, have the FUSE server fail the open with -ENOEXEC and execute a native ELF file that matches the entry. have_execfd only means anything alongside the executable it describes which is not set until the interpreter has been opened and staged. So lets raise it there, next to execfd_creds, which is already set at that point. An open failure now leaves it clear, so the fallback format derives credentials from the binary and emits no AT_EXECFD, as it would for any native exec. The argv rewrite load_misc_binary() performs before the open is still not undone. This means the binary sees the interpreter path in argv[0] and its own path in argv[1] but that predates this change and only became observable once the exec stopped faulting. Link: https://patch.msgid.link/20260720-beglichen-kognitiv-organismus-5e1e55326c56@brauner Fixes: bc2bf338d54b ("exec: Remove recursion from search_binary_handler") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_misc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index a8b1d79e4af07..d44529d5ecf5f 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -227,9 +227,6 @@ static int load_misc_binary(struct linux_binprm *bprm) goto ret; } - if (fmt->flags & MISC_FMT_OPEN_BINARY) - bprm->have_execfd = 1; - /* make argv[1] be the path to the binary */ retval = copy_string_kernel(bprm->interp, bprm); if (retval < 0) @@ -259,6 +256,8 @@ static int load_misc_binary(struct linux_binprm *bprm) goto ret; bprm->interpreter = interp_file; + if (fmt->flags & MISC_FMT_OPEN_BINARY) + bprm->have_execfd = 1; if (fmt->flags & MISC_FMT_CREDENTIALS) bprm->execfd_creds = 1; From cc3bbff10b1a73121ada1cb06d5a5f66ecee6c13 Mon Sep 17 00:00:00 2001 From: Zixing Liu Date: Fri, 24 Jul 2026 16:33:14 +0800 Subject: [PATCH 2695/3297] platform/loongarch: laptop: Explicitly reset bl_powered state when suspend commit 91a70492c03040d51b36f595530d6491d5d6c541 upstream. On EAECIS NL60R with EC firmware version 1.11, resuming from S3 has a very high chance (>90%) of causing the EC to lose the previous backlight power state. When this happens, the laptop resumes normally from S3, but the backlight remains off (when shining on the screen with a flash light, we can see the screen contents are updating normally). Since there is no generic way to query the EC's backlight state on Loongson laptop platforms, assume the worst-case scenario and restart the backlight power inside the kernel each time the system resumes. Cc: stable@vger.kernel.org Fixes: 53c762b47f72 ("platform/loongarch: laptop: Add backlight power control support") Tested-by: Yao Zi Tested-by: Xi Ruoyao Signed-off-by: Zixing Liu Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- drivers/platform/loongarch/loongson-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/loongarch/loongson-laptop.c b/drivers/platform/loongarch/loongson-laptop.c index 61b18ac206c9e..dc8142f4fa954 100644 --- a/drivers/platform/loongarch/loongson-laptop.c +++ b/drivers/platform/loongarch/loongson-laptop.c @@ -189,6 +189,7 @@ static int __init setup_acpi_notify(struct generic_sub_driver *sub_driver) static int loongson_hotkey_suspend(struct device *dev) { + bl_powered = false; return 0; } From 12be1d75e9b29b387088aa729f8b79563ada400d Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 8 Jul 2026 12:00:26 +0000 Subject: [PATCH 2696/3297] rust_binder: only print failure if error has source commit bb66b1a3452534adb8b72abf2f761375970fe472 upstream. The commit that fixes BINDER_GET_EXTENDED_ERROR changed the condition for printing transaction failures so errors are printed even if the cause is a dead or frozen process. Undo this change so that the error is only printed if the failure has an errno associated with it. Cc: stable@kernel.org Fixes: 77bfebf11077 ("rust_binder: fix BINDER_GET_EXTENDED_ERROR") Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260708-get-extended-error-fix-printing-v1-1-6e293b213b70@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/thread.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index ad8bdf762ddc6..93b550e6b60e7 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1268,14 +1268,14 @@ impl Thread { inner.extended_error = ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); } - } - pr_warn!( - "{}:{} transaction to {} failed: {err:?}", - info.from_pid, - info.from_tid, - info.to_pid - ); + pr_warn!( + "{}:{} transaction to {} failed: {err:?}", + info.from_pid, + info.from_tid, + info.to_pid + ); + } } } From 6db0c42c87c46e15f381a124bb553e3b4878fbdd Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 14 Jul 2026 07:52:35 +0900 Subject: [PATCH 2697/3297] rust: time: fix as_micros_ceil() to round correctly for negative Delta commit 880c43b185ca52239e75bc546cc4f4d9154d0fed upstream. The ceiling-division idiom `(n + d - 1) / d` only produces the correct result when `n` is non-negative. For example, if n = -1000 (exactly -1us), the old code computed (-1000 + 999) / 1000 == 0 instead of -1. For negative n, truncating division already rounds towards positive infinity, so no bias is needed in that case. Fixes: fae0cdc12340 ("rust: time: Introduce Delta type") Signed-off-by: FUJITA Tomonori Acked-by: Andreas Hindborg Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260713225235.3243480-1-tomo@flapping.org Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- rust/kernel/time.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 6ea98dfcd0278..c1e83aed2b46d 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -421,15 +421,22 @@ impl Delta { /// to the value in the [`Delta`]. #[inline] pub fn as_micros_ceil(self) -> i64 { + let n = self.as_nanos(); + let n = if n >= 0 { + n.saturating_add(NSEC_PER_USEC - 1) + } else { + n + }; + #[cfg(CONFIG_64BIT)] { - self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC + n / NSEC_PER_USEC } #[cfg(not(CONFIG_64BIT))] // SAFETY: It is always safe to call `ktime_to_us()` with any value. unsafe { - bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1)) + bindings::ktime_to_us(n) } } From 8ccfb3b315a0edb4383347d77b5e9d11df404837 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 8 Jul 2026 19:49:26 +0900 Subject: [PATCH 2698/3297] rust: allow `clippy::unwrap_or_default` globally commit 4688cf884b3abcd12498e03b625d1916bf49a1e4 upstream. Starting with rustc 1.88, the `clippy::unwrap_or_default` lint triggers on `rust/kernel/soc.rs` if `CONFIG_CC_OPTIMIZE_FOR_SIZE=y`: warning: use of `unwrap_or` to construct default value --> ../rust/kernel/soc.rs:66:10 | 66 | .unwrap_or(core::ptr::null()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` This is a clippy bug [1]: the lint decides whether an expression is equivalent to `Default::default()` by inspecting the optimized MIR of `<*const T as Default>::default` exported by `core`, so its outcome depends on the optimization level `core` was built with. Moreover, its suggestion ignores our MSRV of 1.85 (`Default` for `*const T` is only stable since Rust 1.88), so we could not apply it anyway. Disable the lint globally rather than working around this single occurrence; it can be re-enabled conditionally using `rustc-min-version` once clippy is fixed. Link: https://github.com/rust-lang/rust-clippy/issues/17379 [1] Suggested-by: Miguel Ojeda Signed-off-by: Alexandre Courbot Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Link: https://patch.msgid.link/20260708-soc_unwrap_or-v2-1-007ed724cc7b@nvidia.com [ Moved to non-versioned group. - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index f3c450a0a2fbb..51ba8fb199dee 100644 --- a/Makefile +++ b/Makefile @@ -470,6 +470,10 @@ KBUILD_USERLDFLAGS := $(USERLDFLAGS) # These flags apply to all Rust code in the tree, including the kernel and # host programs. +# +# `-Aclippy::unwrap_or_default`: the lint is buggy [1] and ignores our +# MSRV. It can trigger depending on the optimization level. +# [1] https://github.com/rust-lang/rust-clippy/issues/17379 export rust_common_flags := --edition=2021 \ -Zbinary_dep_depinfo=y \ -Astable_features \ @@ -497,6 +501,7 @@ export rust_common_flags := --edition=2021 \ -Aclippy::uninlined_format_args \ -Wclippy::unnecessary_safety_comment \ -Wclippy::unnecessary_safety_doc \ + -Aclippy::unwrap_or_default \ -Wrustdoc::missing_crate_level_docs \ -Wrustdoc::unescaped_backticks From c404b1f30b252f26eb2ae940f15e706235414dec Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 10 Jul 2026 19:32:52 +0200 Subject: [PATCH 2699/3297] objtool/rust: add one more `noreturn` Rust function for Rust 1.99.0 commit 5a81c35c3b18cd59ded56171a6a9f643b92a6759 upstream. Starting with Rust 1.99.0 (expected 2026-10-01), under `CONFIG_RUST_DEBUG_ASSERTIONS=y`, `objtool` may report: rust/kernel.o: warning: objtool: _R..._6kernel12module_param9set_paramaEB4_() falls through to next function _R..._6kernel12module_param9set_paramhEB4_() (and many others) due to calls to the `noreturn` symbol [1]: core::panicking::panic_null_reference_constructed Thus add the mangled one to the list so that `objtool` knows it is actually `noreturn`. See commit 56d680dd23c3 ("objtool/rust: list `noreturn` Rust functions") for more details. Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Cc: Josh Poimboeuf Cc: Peter Zijlstra Cc: Petr Pavlu Link: https://github.com/rust-lang/rust/pull/158796 [1] Reported-by: Alice Ryhl Closes: https://lore.kernel.org/rust-for-linux/alEBInX9gD1M5NAr@google.com/ Reviewed-by: Alice Ryhl Tested-by: Alice Ryhl Link: https://patch.msgid.link/20260710173252.191781-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- tools/objtool/check.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 75b6592afcc29..c7d64ab2ea4c4 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -228,6 +228,7 @@ static bool is_rust_noreturn(const struct symbol *func) str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt") || str_ends_with(func->name, "_4core9panicking19assert_failed_inner") || str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference") || + str_ends_with(func->name, "_4core9panicking32panic_null_reference_constructed") || str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference") || str_ends_with(func->name, "_7___rustc17rust_begin_unwind") || strstr(func->name, "_4core9panicking13assert_failed") || From a94d6726ec8680a2b0c453fc782a543f68a1ea06 Mon Sep 17 00:00:00 2001 From: George Guo Date: Thu, 23 Jul 2026 22:27:30 +0800 Subject: [PATCH 2700/3297] LoongArch: Fix address space mismatch in kexec command line lookup commit 485ed44db5694d8d2e5027f63ad608e705286f30 upstream. When searching the loaded segments for the "kexec" command line marker, the kexec_load(2) path (file_mode == 0) passes the user-space segment buffer straight to strncmp() through a bogus (char __user *) cast. This dereferences a user pointer in kernel context, which is wrong and is flagged by sparse: arch/loongarch/kernel/machine_kexec.c:84:51: sparse: incorrect type in argument 2 (different address spaces) @@ expected char const * @@ got char [noderef] __user * Here copy the marker-sized prefix of each segment into a small on-stack buffer with copy_from_user() before comparing, and skip segments that fault. The subsequent copy_from_user() that stages the full command line into the safe area is left unchanged. Cc: stable@vger.kernel.org Fixes: 4a03b2ac06a5 ("LoongArch: Add kexec support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605051639.aEPioXdD-lkp@intel.com/ Co-developed-by: Kexin Liu Signed-off-by: Kexin Liu Signed-off-by: George Guo Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/machine_kexec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kernel/machine_kexec.c b/arch/loongarch/kernel/machine_kexec.c index d7fafda1d5417..1883cae93bc31 100644 --- a/arch/loongarch/kernel/machine_kexec.c +++ b/arch/loongarch/kernel/machine_kexec.c @@ -42,6 +42,7 @@ static unsigned long first_ind_entry; int machine_kexec_prepare(struct kimage *kimage) { int i; + char head[8]; char *bootloader = "kexec"; void *cmdline_ptr = (void *)KEXEC_CMDLINE_ADDR; @@ -59,7 +60,9 @@ int machine_kexec_prepare(struct kimage *kimage) } else { /* Find the command line */ for (i = 0; i < kimage->nr_segments; i++) { - if (!strncmp(bootloader, (char __user *)kimage->segment[i].buf, strlen(bootloader))) { + if (copy_from_user(head, kimage->segment[i].buf, strlen(bootloader))) + continue; + if (!strncmp(bootloader, head, strlen(bootloader))) { if (!copy_from_user(cmdline_ptr, kimage->segment[i].buf, COMMAND_LINE_SIZE)) kimage->arch.cmdline_ptr = (unsigned long)cmdline_ptr; break; From 6ab0abb5a2e0c2493817f8cc446c1a033c222768 Mon Sep 17 00:00:00 2001 From: Haoran Jiang Date: Thu, 23 Jul 2026 22:27:30 +0800 Subject: [PATCH 2701/3297] LoongArch: Fix oops during single-step debugging commit 73555fdab5e1e4f24ca000c41a616b34edf4b55d upstream. When entering KDB via a breakpoint and then performing single-step debugging, an oops is triggered. Now during single-step debugging, kdb_local() expects the reason to be KDB_REASON_SSTEP, but it is actually KDB_REASON_OOPS. In kdb_stub(), when determining the reason, the ex_vector for single-step should be 0, as already implemented on other architectures such as arm64 and riscv. Before the patch: [112]kdb> ss Entering kdb (current=0x900020009f520000, pid 10661) on processor 112 Oops: (null) due to oops @ 0x90000000005b57a4 Cc: stable@vger.kernel.org Signed-off-by: Haoran Jiang Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/kgdb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kernel/kgdb.c b/arch/loongarch/kernel/kgdb.c index 7be5b4c0c9002..6728b2d67dd32 100644 --- a/arch/loongarch/kernel/kgdb.c +++ b/arch/loongarch/kernel/kgdb.c @@ -252,7 +252,8 @@ static int kgdb_loongarch_notify(struct notifier_block *self, unsigned long cmd, if (atomic_read(&kgdb_active) != -1) kgdb_nmicallback(smp_processor_id(), regs); - if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs)) + if (kgdb_handle_exception(regs->csr_era == stepped_address ? 0 : args->trapnr, + args->signr, cmd, regs)) return NOTIFY_DONE; if (atomic_read(&kgdb_setting_breakpoint)) From 38b025fcdc45bdf5140a5726a1fbb2e694ea047b Mon Sep 17 00:00:00 2001 From: Kanglong Wang Date: Thu, 23 Jul 2026 22:27:29 +0800 Subject: [PATCH 2702/3297] LoongArch: Move jump_label_init() before parse_early_param() commit ea68d444a658783234a06f05414e41cf93a18fb2 upstream. When enabling both CONFIG_MEM_ALLOC_PROFILING=y and CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=y, then diabling memory profiling by adding the boot parameter 'sysctl.vm.mem_profiling=0' will cause the kernel failed to boot. After analysis, this is because jump_label_init() must be called before parse_early_param(), the early param handlers may modify static keys by static_branch_enable/disable(). Fix this by moving jump_label_init() to before parse_early_param(). The solution is similar to other architectures. Cc: Signed-off-by: Kanglong Wang Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/setup.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 75e3dfa060553..c0b59b813f8e9 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -610,6 +610,7 @@ void __init setup_arch(char **cmdline_p) memblock_init(); pagetable_init(); bootcmdline_init(cmdline_p); + jump_label_init(); /* Initialise the static keys for early params */ parse_early_param(); reserve_initrd_mem(); @@ -617,8 +618,6 @@ void __init setup_arch(char **cmdline_p) arch_mem_init(cmdline_p); resource_init(); - jump_label_init(); /* Initialise the static keys for paravirtualization */ - #ifdef CONFIG_SMP plat_smp_setup(); prefill_possible_map(); From 4427a33faabb4b81511b99dfee18ce0a38b5b5df Mon Sep 17 00:00:00 2001 From: Rong Bao Date: Thu, 23 Jul 2026 22:27:29 +0800 Subject: [PATCH 2703/3297] LoongArch: Retrieve CPU package ID from PPTT when available commit 4e8f58620f6717f72f3d88a2c8f25c0c656d0ba7 upstream. Currently, the LoongArch CPU topology initialization code calculates each core's package ID by dividing its physical ID by loongson_sysconf. cores_per_package. This relies on the assumption that cores_per_package counts in the same domain as physical IDs. On Loongson-3B6000 (XB612B0V_1.2), cores_per_package matches the visible core count -- 24 in this case. However, the physical IDs range from 0 to 31 in a noncontinuous fashion: $ cat /proc/cpuinfo | grep -i -F 'global_id' global_id : 0 global_id : 1 global_id : 4 global_id : 5 global_id : 6 global_id : 7 global_id : 8 global_id : 9 global_id : 10 global_id : 11 global_id : 14 global_id : 15 global_id : 16 global_id : 17 global_id : 20 global_id : 21 global_id : 22 global_id : 23 global_id : 26 global_id : 27 global_id : 28 global_id : 29 global_id : 30 global_id : 31 Retrieve the exact package ID from ACPI PPTT when available, in the same style as retrieving the core ID and thread ID in parse_acpi_topology(). Use this information in loongson_init_secondary() when the PPTT readout is successful. The original division logic is kept as a fallback. Meanwhile, since some existing code paths like loongson3_cpufreq expect a continuous integer sequence of package IDs in [0, MAX_PACKAGES) when retrieving from cpu_data[], here we also canonicalize the package ID to be filled in parse_acpi_topology() to meet such an expectation. Cc: stable@vger.kernel.org Tested-by: Mingcong Bai Co-developed-by: Xi Ruoyao Signed-off-by: Xi Ruoyao Signed-off-by: Rong Bao Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/acpi.c | 27 ++++++++++++++++++++++++++- arch/loongarch/kernel/smp.c | 4 ++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 88ad24d1e729a..fb9a167ee7a06 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -201,10 +201,12 @@ static void __init acpi_process_madt(void) } int pptt_enabled; +static int acpi_nr_packages; +static int acpi_package_ids[MAX_PACKAGES]; int __init parse_acpi_topology(void) { - int cpu, topology_id; + int i, cpu, topology_id; for_each_possible_cpu(cpu) { topology_id = find_acpi_cpu_topology(cpu, 0); @@ -222,6 +224,29 @@ int __init parse_acpi_topology(void) cpu_data[cpu].core = topology_id; } + + topology_id = find_acpi_cpu_topology_package(cpu); + if (topology_id < 0) { + pr_warn("Invalid BIOS PPTT\n"); + return -ENOENT; + } + + for (i = 0; i < acpi_nr_packages; i++) + if (acpi_package_ids[i] == topology_id) + break; + + if (i == acpi_nr_packages) + acpi_package_ids[acpi_nr_packages++] = topology_id; + + cpu_data[cpu].package = topology_id; + } + + for_each_possible_cpu(cpu) { + for (i = 0; i < acpi_nr_packages; i++) + if (cpu_data[cpu].package == acpi_package_ids[i]) { + cpu_data[cpu].package = i; /* Canonicalize */ + break; + } } pptt_enabled = 1; diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 5d6a60294fb2d..fc3817bd1919c 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -412,10 +412,10 @@ void loongson_init_secondary(void) numa_add_cpu(cpu); #endif per_cpu(cpu_state, cpu) = CPU_ONLINE; - cpu_data[cpu].package = - cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; cpu_data[cpu].core = pptt_enabled ? cpu_data[cpu].core : cpu_logical_map(cpu) % loongson_sysconf.cores_per_package; + cpu_data[cpu].package = pptt_enabled ? cpu_data[cpu].package : + cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; cpu_data[cpu].global_id = cpu_logical_map(cpu); } From d43c5c0c935522deae7339e0c2399365f3bf0016 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 20 Jul 2026 20:44:21 +0100 Subject: [PATCH 2704/3297] cdrom: fix stack out-of-bounds read in CDROMVOLCTRL commit b27e195d4db8dea263050bdbeb11881b2999c9c6 upstream. mmc_ioctl_cdrom_volume() first reads the audio control mode page into a 32-byte stack buffer with cgc->buflen set to 24. If the device reports a block descriptor, the function increases cgc->buflen to include that descriptor and reads the page again. For CDROMVOLCTRL, the function then builds a MODE SELECT parameter list by moving cgc->buffer forward by offset - 8 bytes. This drops the block descriptor from the outgoing payload and leaves a new 8-byte mode parameter header in front of the audio control page. However, cgc->buflen is left unchanged. With a standard 8-byte block descriptor, cgc->buffer points at buffer + 8 but cgc->buflen remains 32. cdrom_mode_select() therefore asks the low level packet path to write 32 bytes from that adjusted pointer, reading 8 bytes past the end of the 32-byte stack buffer. This is not hit by CDROMVOLREAD, and CDROMVOLCTRL only triggers it on drives that return a non-zero block descriptor length, which helps explain why it has gone unnoticed. The overread is also sent to the device as extra MODE SELECT payload, so it may not produce an obvious local failure. Reduce cgc->buflen by the same amount as the buffer pointer adjustment so the MODE SELECT transfer covers only the intended parameter list. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Phillip Potter Link: https://patch.msgid.link/20260720194421.1497-2-phil@philpotter.co.uk Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/cdrom/cdrom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index e30414f0d4560..ce2de543bac32 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -3187,6 +3187,7 @@ static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi, /* set volume */ cgc->buffer = buffer + offset - 8; + cgc->buflen -= offset - 8; memset(cgc->buffer, 0, 8); return cdrom_mode_select(cdi, cgc); } From 3ff7c1dbf722cf3fa538672452ba182318e0fcc3 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Tue, 7 Jul 2026 12:41:15 -0400 Subject: [PATCH 2705/3297] rhashtable: clear stale iter->p on table restart commit 8173f7e2ce67e6ca1d4763f3da14e5b01ce77456 upstream. rhashtable_walk_start_check() has two restart paths when resuming a walk. When iter->walker.tbl is valid, it re-validates iter->p against the table and sets iter->p = NULL if the object is gone. When iter->walker.tbl is NULL (table was freed during resize), it resets slot and skip but forgets to clear iter->p. rhashtable_walk_next() then dereferences the stale iter->p, reading freed memory. This is a use-after-free. Any caller that does multi-fragment rhashtable walks across walk_stop/walk_start boundaries is affected. Concrete cases include netlink_diag (__netlink_diag_dump in net/netlink/diag.c) and TIPC (tipc_nl_sk_walk in net/tipc/socket.c). Crash stack (netlink_diag): BUG: KASAN: slab-use-after-free in rhashtable_walk_next+0x365/0x3c0 Read of size 8 at addr ffff88801a9d2438 (freed kmalloc-2k, offset 1080) Call Trace: rhashtable_walk_next+0x365/0x3c0 (lib/rhashtable.c:1016) __netlink_diag_dump+0x160/0x760 (net/netlink/diag.c:122) netlink_diag_dump+0xc2/0x240 netlink_dump+0x5bc/0x1270 netlink_recvmsg+0x7a3/0x980 sock_recvmsg+0x1bc/0x200 __sys_recvfrom+0x1d4/0x2c0 Fixes: 5d240a8936f6 ("rhashtable: improve rhashtable_walk stability when stop/start used.") Cc: Reported-by: AutonomousCodeSecurity@microsoft.com Reported-by: Yuan Tan Closes: https://lore.kernel.org/linux-crypto/CAB8m9Wh559e+=n8z51gB8DrbEyCc2mc0MgGjrRR6_VXBmU=2AQ@mail.gmail.com Signed-off-by: Cen Zhang (Microsoft) Reviewed-by: NeilBrown Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- lib/rhashtable.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rhashtable.c b/lib/rhashtable.c index fde0f0e556f87..fcc7bd22d86ef 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -742,6 +742,7 @@ int rhashtable_walk_start_check(struct rhashtable_iter *iter) iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht); iter->slot = 0; iter->skip = 0; + iter->p = NULL; return -EAGAIN; } From 4f2db41a09eba7a45abd140bb86ffc519c191886 Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Wed, 24 Jun 2026 03:06:35 -0700 Subject: [PATCH 2706/3297] firmware: stratix10-svc: fix memory leaks and list corruption bugs commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c upstream. Fix a memory leak when gen_pool_alloc() fails by freeing pmem on the error path. Switch pmem allocation from devm_kzalloc() to kzalloc() with explicit kfree() in the free path to match its list-managed lifetime. Remove the erroneous list_del(&svc_data_mem) which corrupted the list head on failed lookups. Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver") Cc: stable@vger.kernel.org # 5.0+ Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/stratix10-svc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 1d24e82f3d09d..ba715c6eb32bf 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1080,14 +1080,16 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan, struct gen_pool *genpool = chan->ctrl->genpool; size_t s = roundup(size, 1 << genpool->min_alloc_order); - pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL); + pmem = kzalloc_obj(*pmem); if (!pmem) return ERR_PTR(-ENOMEM); guard(mutex)(&svc_mem_lock); va = gen_pool_alloc(genpool, s); - if (!va) + if (!va) { + kfree(pmem); return ERR_PTR(-ENOMEM); + } memset((void *)va, 0, s); pa = gen_pool_virt_to_phys(genpool, va); @@ -1113,6 +1115,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_allocate_memory); void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) { struct stratix10_svc_data_mem *pmem; + guard(mutex)(&svc_mem_lock); list_for_each_entry(pmem, &svc_data_mem, node) @@ -1121,10 +1124,9 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) (unsigned long)kaddr, pmem->size); pmem->vaddr = NULL; list_del(&pmem->node); + kfree(pmem); return; } - - list_del(&svc_data_mem); } EXPORT_SYMBOL_GPL(stratix10_svc_free_memory); From 9bb71b59e0aa385c3efcb565acc5cb4af749ea2d Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 22 Jul 2026 17:09:43 -0700 Subject: [PATCH 2707/3297] x86/boot/compressed: Disable jump tables commit 4a9ec5ec9555ad62dc5b81a37ac946025c2ea002 upstream. After a recent upstream LLVM change to start generating jump and lookup tables in switch statements in more instances [1], linking the compressed x86 boot image when CONFIG_KERNEL_ZSTD is enabled fails with: ld.lld: error: Unexpected run-time relocations (.rela) detected! Dumping the relocations in misc.o, which is the only file influenced by CONFIG_KERNEL_ZSTD in the decompressor, shows dynamic relocations to some string constants, which correspond to the string literals in the switch statement in handle_zstd_error(): Relocation section '.rela.data.rel.ro' at offset 0x277b0 contains 31 entries: Offset Info Type Symbol's Value Symbol's Name + Addend 0000000000000000 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 73a 0000000000000008 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000010 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000018 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e ... This optimization is problematic for the decompressor environment, as it is built as -fPIE without any explicit absolute references (as described at the top of misc.c) while not applying any dynamic relocations, hence the linker assertion. To opt out of this optimization, which is of little value in this special early boot code, and to mirror the other x86 startup code in arch/x86/boot/startup, disable jump tables in the decompressor. Signed-off-by: Nathan Chancellor Signed-off-by: Ingo Molnar Acked-by: Ard Biesheuvel Cc: Bill Wendling Cc: Justin Stitt Cc: Nick Desaulniers Cc: "H. Peter Anvin" Cc: Peter Zijlstra Cc: stable@vger.kernel.org Link: https://github.com/llvm/llvm-project/commit/fa02a6ed66b1700c996b49c96c6bc0eb014c9518 [1] Link: https://patch.msgid.link/20260722-x86-boot-compressed-disable-jt-clang-v2-1-7373d38482fb@kernel.org Closes: https://github.com/ClangBuiltLinux/linux/issues/2165 Signed-off-by: Greg Kroah-Hartman --- arch/x86/boot/compressed/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 2013840d6318f..99d85503e5093 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -27,6 +27,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \ KBUILD_CFLAGS := -m$(BITS) -O2 $(CLANG_FLAGS) KBUILD_CFLAGS += -std=gnu11 KBUILD_CFLAGS += -fno-strict-aliasing -fPIE +KBUILD_CFLAGS += -fno-jump-tables KBUILD_CFLAGS += -Wundef KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING cflags-$(CONFIG_X86_32) := -march=i386 From cf26dd2d841583c54a87005c4934b92fddb930c3 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 27 May 2026 13:51:03 +0100 Subject: [PATCH 2708/3297] comedi: comedi_parport: deal with premature interrupt commit 17221216ae8ce6a24e8a4e787382e3ebc81b88a8 upstream. Syzbot reported a general protection fault in `comedi_get_is_subdevice_running()`, which was called from the interrupt handler `parport_interrupt()` in the "comedi_parport" driver, but it does not currently have a C reproducer for the problem. It's probably due to a premature interrupt for one of two reasons: 1. The driver sets up the interrupt handler before the comedi subdevices used by the interrupt handler have been allocated, but does not disable the interrupt in the parallel port's CTRL register first. 2. The driver uses a user-supplied I/O port base address which Syzbot would have supplied, but it might not be backed by real parallel port hardware. Change the initialization order in the driver's comedi "attach" handler (`parport_attach()`) so that the hardware registers are initialized before the interrupt handler is requested. This should prevent premature interrupts occurring for real hardware. Also add a test to the interrupt handler to ensure the comedi device is fully attached and return early if it isn't. Fixes: 241ab6ad7108e ("Staging: comedi: add comedi_parport driver") Reported-by: syzbot+f24c3d5d316011bacc70@syzkaller.appspotmail.com Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260527125104.96596-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_parport.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/comedi/drivers/comedi_parport.c b/drivers/comedi/drivers/comedi_parport.c index 098738a688fe6..db9f58792ab91 100644 --- a/drivers/comedi/drivers/comedi_parport.c +++ b/drivers/comedi/drivers/comedi_parport.c @@ -211,6 +211,13 @@ static irqreturn_t parport_interrupt(int irq, void *d) unsigned int ctrl; unsigned short val = 0; + /* + * Check device is fully attached. Device interrupts should have + * been disabled, but do this in case of bad hardware. + */ + if (!dev->attached) + return IRQ_NONE; + ctrl = inb(dev->iobase + PARPORT_CTRL_REG); if (!(ctrl & PARPORT_CTRL_IRQ_ENA)) return IRQ_NONE; @@ -231,6 +238,9 @@ static int parport_attach(struct comedi_device *dev, if (ret) return ret; + outb(0, dev->iobase + PARPORT_DATA_REG); + outb(0, dev->iobase + PARPORT_CTRL_REG); + if (it->options[1]) { ret = request_irq(it->options[1], parport_interrupt, 0, dev->board_name, dev); @@ -286,9 +296,6 @@ static int parport_attach(struct comedi_device *dev, s->cancel = parport_intr_cancel; } - outb(0, dev->iobase + PARPORT_DATA_REG); - outb(0, dev->iobase + PARPORT_CTRL_REG); - return 0; } From 635be8b097f0c5851083ff4287dbd5d756c093d9 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 25 May 2026 14:04:22 +0200 Subject: [PATCH 2709/3297] uio_hv_generic: Bind to FCopy device by default commit 87d3621ccc63b3999d756bb59f0cedd738c28eb3 upstream. The Hyper-V kernel-mode fcopy driver was removed in 6.10 and the new fcopy daemon requires this uio driver to function. However, by default the driver does not bind to any devices, and must be configured through the sysfs "new_id" file. Since the FCopy device is now only usable through this driver, add its ID to the driver's ID table so that the daemon will work "out of the box". Signed-off-by: Ben Hutchings Fixes: ec314f61e4fc ("Drivers: hv: Remove fcopy driver") Cc: stable Link: https://patch.msgid.link/ahQ6xuhSReidmN-3@decadent.org.uk Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_hv_generic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index 3f8e2e27697fb..c47d46334a7c0 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -395,9 +395,15 @@ hv_uio_remove(struct hv_device *dev) vmbus_free_ring(dev->channel); } +static const struct hv_vmbus_device_id hv_uio_id_table[] = { + { HV_FCOPY_GUID }, + {} +}; +MODULE_DEVICE_TABLE(vmbus, hv_uio_id_table); + static struct hv_driver hv_uio_drv = { .name = "uio_hv_generic", - .id_table = NULL, /* only dynamic id's */ + .id_table = hv_uio_id_table, .probe = hv_uio_probe, .remove = hv_uio_remove, }; From 4fae473b856b49fb1c166da1052a66d126d80166 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 16 Jul 2026 17:08:09 -0400 Subject: [PATCH 2710/3297] serial: sc16is7xx: implement gpio get_direction() callback commit af071d9e07e57cfff239e8d09d2f3b05ebc9c667 upstream. It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio. Fixes: dfeae619d781 ("serial: sc16is7xx") Cc: stable Signed-off-by: Hugo Villeneuve Acked-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260716210813.2582826-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index c7435595dce13..13a86f0c2d9b4 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1328,6 +1328,17 @@ static int sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned int offset, return 0; } +static int sc16is7xx_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct sc16is7xx_port *s = gpiochip_get_data(chip); + struct uart_port *port = &s->p[0].port; + unsigned int val; + + val = sc16is7xx_port_read(port, SC16IS7XX_IODIR_REG); + + return val & BIT(offset) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { @@ -1405,6 +1416,7 @@ static int sc16is7xx_setup_gpio_chip(struct sc16is7xx_port *s) s->gpio.parent = dev; s->gpio.label = dev_name(dev); s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask; + s->gpio.get_direction = sc16is7xx_gpio_get_direction; s->gpio.direction_input = sc16is7xx_gpio_direction_input; s->gpio.get = sc16is7xx_gpio_get; s->gpio.direction_output = sc16is7xx_gpio_direction_output; From b2a3eeb57ba24f8a0e34a69d40adcc63e5b9ca56 Mon Sep 17 00:00:00 2001 From: Jiangshan Yi Date: Wed, 15 Jul 2026 15:35:46 +0800 Subject: [PATCH 2711/3297] serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms commit 7fb13fd7e9a59a37cd911efff83abe19e3ee029d upstream. Commit b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") replaced the dnv_board setup and exit callbacks with PTR_IF(false, ...), which evaluates to NULL. However, the three call sites in mid8250_probe() and mid8250_remove() unconditionally dereference these function pointers without NULL checks, causing a NULL pointer dereference (kernel oops) on any Denverton (DNV), Ice Lake Xeon D (ICX-D/CDF), or Snowridge (SNR) platform. Fix this by adding the missing NULL checks before calling the setup and exit callbacks. Fixes: b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") Cc: stable Reviewed-by: Andy Shevchenko Signed-off-by: Jiangshan Yi Link: https://patch.msgid.link/20260715073546.1875083-1-yijiangshan@kylinos.cn Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index f88809ff370b7..82656645b8a64 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -318,9 +318,11 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!uart.port.membase) return -ENOMEM; - ret = mid->board->setup(mid, &uart.port); - if (ret) - return ret; + if (mid->board->setup) { + ret = mid->board->setup(mid, &uart.port); + if (ret) + return ret; + } ret = mid8250_dma_setup(mid, &uart); if (ret) @@ -336,7 +338,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err: - mid->board->exit(mid); + if (mid->board->exit) + mid->board->exit(mid); return ret; } @@ -346,7 +349,8 @@ static void mid8250_remove(struct pci_dev *pdev) serial8250_unregister_port(mid->line); - mid->board->exit(mid); + if (mid->board->exit) + mid->board->exit(mid); } static const struct mid8250_board pnw_board = { From 5118872357279cd2a83d5435bc062831a41d0f52 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 10:55:04 -0700 Subject: [PATCH 2712/3297] selftests: ntsync: correct CONFIG_NTSYNC name commit f97752cfe511c1ed9933057455c73aaac07d6517 upstream. The config fragment for these tests defines CONFIG_WINESYNC, which refers to an earlier name for the ntsync driver before it was merged [1]. Correct it to define CONFIG_NTSYNC instead. [1] https://lore.kernel.org/all/f4cc1a38-1441-62f8-47e4-0c67f5ad1d43@codeweavers.com/ Fixes: 7f853a252cde ("selftests: ntsync: Add some tests for semaphore state.") Cc: stable Signed-off-by: Ethan Nelson-Moore Reviewed-by: Elizabeth Figura Link: https://patch.msgid.link/20260609175505.19632-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/drivers/ntsync/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/ntsync/config b/tools/testing/selftests/drivers/ntsync/config index 60539c826d062..0aa68de147af1 100644 --- a/tools/testing/selftests/drivers/ntsync/config +++ b/tools/testing/selftests/drivers/ntsync/config @@ -1 +1 @@ -CONFIG_WINESYNC=y +CONFIG_NTSYNC=y From 59dd34854202d9a3faaa87a85205e553fe7150e1 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 5 Jul 2026 18:12:59 +0300 Subject: [PATCH 2713/3297] mei: bus: access mei_device under device_lock on cleanup commit f112ea910e554d58b4b39a4492b7d302f0f4204f upstream. Fix couple of problems in mei_cl_bus_dev_release(): mei_cl_flush_queues() is running without lock. bus->file_list access after mei_dev_bus_put(bus) can become a use-after-free if this was the last reference to bus. Protect queues cleanup and WARN traversal by device lock there to avoid the concurrent access problems. Move WARN traversal before mei_dev_bus_put(bus). This file uses bus variable name for mei_device, adjust code of mei_cl_bus_dev_release() to use bus variable too. Cc: stable Fixes: 35e8a426b16a ("mei: bus: Check for still connected devices in mei_cl_bus_dev_release()") Reviewed-by: Menachem Adin Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260705151259.3054795-1-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 2c810ab12e620..a6f0bc150d770 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -4,6 +4,7 @@ * Intel Management Engine Interface (Intel MEI) Linux driver */ +#include #include #include #include @@ -1317,15 +1318,16 @@ static void mei_dev_bus_put(struct mei_device *bus) static void mei_cl_bus_dev_release(struct device *dev) { struct mei_cl_device *cldev = to_mei_cl_device(dev); - struct mei_device *mdev = cldev->cl->dev; + struct mei_device *bus = cldev->bus; struct mei_cl *cl; - mei_cl_flush_queues(cldev->cl, NULL); - mei_me_cl_put(cldev->me_cl); - mei_dev_bus_put(cldev->bus); - - list_for_each_entry(cl, &mdev->file_list, link) - WARN_ON(cl == cldev->cl); + scoped_guard(mutex, &bus->device_lock) { + mei_cl_flush_queues(cldev->cl, NULL); + mei_me_cl_put(cldev->me_cl); + list_for_each_entry(cl, &bus->file_list, link) + WARN_ON(cl == cldev->cl); + } + mei_dev_bus_put(bus); kfree(cldev->cl); kfree(cldev); From caba30eb8bd321c465ecfc7d850ee85f5b353496 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Jul 2026 15:08:51 +0800 Subject: [PATCH 2714/3297] intel_th: fix MSC output device reference leak commit 761b785a0cfbce43761227bc42a7f984f31f8921 upstream. intel_th_output_open() looks up the output device with bus_find_device_by_devt(), which returns the device with a reference that must be dropped after use. commit 95fc36a234da ("intel_th: fix device leak on output open()") attempted to drop the reference from intel_th_output_release(). However, a successful open replaces file->f_op with the output driver file operations before returning, so close runs the output driver release callback instead. For MSC outputs, close runs intel_th_msc_release(), which only removes the per-file iterator and does not drop the device reference taken by intel_th_output_open(). Consequently, every successful MSC output open leaks one device reference. Drop the device reference from intel_th_msc_release(), which is the release path actually used for MSC output files. Remove the now-unused intel_th_output_release() callback from intel_th_output_fops. Fixes: 95fc36a234da ("intel_th: fix device leak on output open()") Cc: stable Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold Link: https://patch.msgid.link/20260715070851.2077965-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/core.c | 10 ---------- drivers/hwtracing/intel_th/msu.c | 2 ++ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index e3a7ab112ea95..dc888a11c7a09 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -843,18 +843,8 @@ static int intel_th_output_open(struct inode *inode, struct file *file) return err; } -static int intel_th_output_release(struct inode *inode, struct file *file) -{ - struct intel_th_device *thdev = file->private_data; - - put_device(&thdev->dev); - - return 0; -} - static const struct file_operations intel_th_output_fops = { .open = intel_th_output_open, - .release = intel_th_output_release, .llseek = noop_llseek, }; diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index f3a13b300835d..d3170b1476de2 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -1490,8 +1490,10 @@ static int intel_th_msc_release(struct inode *inode, struct file *file) { struct msc_iter *iter = file->private_data; struct msc *msc = iter->msc; + struct intel_th_device *thdev = msc->thdev; msc_iter_remove(iter, msc); + put_device(&thdev->dev); return 0; } From 8f068342096b027181b168d91fef7ac7a2c64b25 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 22:53:50 +0800 Subject: [PATCH 2715/3297] misc: nsm: only unlock nsm_dev on post-lock error paths commit ce1fed11d18e163baf7f875152a33bf80f625c1a upstream. nsm_dev_ioctl() jumps to the common out label even when the initial copy_from_user() fails before nsm->lock has been taken. The error path then blindly unlocks a mutex that was never acquired. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the miscdevice ioctl entry and the pre-lock copy_from_user(&raw, argp, _IOC_SIZE(cmd)) failure path by issuing NSM_IOCTL_RAW with an invalid user pointer. That failure reaches the shared out label before mutex_lock(&nsm->lock). Lockdep reported: WARNING: bad unlock balance detected! exploit/193 is trying to release lock (&global_nsm.lock) at: nsm_dev_ioctl+0x5f/0xcf [vuln_msv] but there are no more locks to release! no locks held by exploit/193. Return immediately on the pre-lock copy_from_user() failure and keep the common unlock label for the post-lock paths only. Fixes: b9873755a6c8 ("misc: Add Nitro Secure Module driver") Cc: stable Signed-off-by: Runyu Xiao Reviewed-by: Alexander Graf Link: https://patch.msgid.link/20260617145350.513875-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/nsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index ef7b327423409..185900cdad4a7 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -367,7 +367,7 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd, /* Copy user argument struct to kernel argument struct */ r = -EFAULT; if (copy_from_user(&raw, argp, _IOC_SIZE(cmd))) - goto out; + return r; mutex_lock(&nsm->lock); From 1da310b94504d42001e9c32c43c5dc105b777e5f Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 13 Jul 2026 13:55:23 +0800 Subject: [PATCH 2716/3297] misc: nsm: pin the module while the device is open commit 3b231f1e9990f4c21220d0a69733ce2105891ff9 upstream. misc_open() installs a misc driver's file operations with fops_get(), which pins file_operations::owner before replacing the file's f_op. The NSM misc device leaves nsm_dev_fops.owner unset, so opening /dev/nsm does not take a module reference on the nsm driver. If the driver is built as a module, an open file descriptor can therefore survive rmmod of the module that provides its ioctl callbacks. A later ioctl through that descriptor can call into unloaded module text. Set nsm_dev_fops.owner to THIS_MODULE so the misc core holds the module while any /dev/nsm file descriptor is open, matching the lifetime expectation for the installed file operations. Fixes: b9873755a6c8 ("misc: Add Nitro Secure Module driver") Cc: stable Signed-off-by: Xu Rao Link: https://patch.msgid.link/BE6951D13B5E5513+20260713055523.3193089-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/nsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index 185900cdad4a7..3960506eb7ab3 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -413,6 +413,7 @@ static int nsm_device_init_vq(struct virtio_device *vdev) } static const struct file_operations nsm_dev_fops = { + .owner = THIS_MODULE, .unlocked_ioctl = nsm_dev_ioctl, .compat_ioctl = compat_ptr_ioctl, }; From 1f2e7cd0ff9766983650dfa9b3e4d231ccd1ce37 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Fri, 17 Jul 2026 10:32:52 -0700 Subject: [PATCH 2717/3297] tracing: Fix context switch counter truncation commit 8f76afb9b114bee1c1251e0e52553e9dc7c59f20 upstream. trace_user_fault_read() samples nr_context_switches_cpu() before enabling preemption and retries the user copy if the counter changes. The helper returns unsigned long long because rq->nr_switches is u64, but the saved value is unsigned int. Once a CPU has performed 2^32 context switches, assigning the counter to cnt discards its upper bits. The comparison after the copy promotes cnt back to unsigned long long, but the lost bits remain zero, so it reports a change even when the task was never scheduled out. Every retry then fails the same way until the 100-try guard warns and the user copy is abandoned. This affects long-running systems and workloads with high context-switch rates. A CPU switching 1,000 times per second takes about 50 days. Store the sampled count in unsigned long long so the full value is preserved. Cc: stable@vger.kernel.org Fixes: 64cf7d058a00 ("tracing: Have trace_marker use per-cpu data to read user space") Link: https://patch.msgid.link/20260717173252.3431565-1-usama.arif@linux.dev Reported-by: Breno Leitao Signed-off-by: Usama Arif Acked-by: Masami Hiramatsu (Google) Reviewed-by: Breno Leitao Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 5b1693d58af06..914d6baa1bf9c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7325,7 +7325,7 @@ static char *trace_user_fault_read(struct trace_user_buf_info *tinfo, { int cpu = smp_processor_id(); char *buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf; - unsigned int cnt; + unsigned long long cnt; int trys = 0; int ret; From 8464427e1c177809a9488a97dfa2807d9dcf323b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 21 Jul 2026 21:11:43 -0400 Subject: [PATCH 2718/3297] tracing: Fix mmiotrace possible NULL dereferencing of hiter->dev commit 144f29e85702234b23d2a62abf723e6a17eb5427 upstream. If the mmio_pipe_open() fails to find a PCI device, the hiter->dev will be assigned to NULL. The mmiotrace read() function dereferences the hiter->dev if hiter exists. Change the test of the read to not only check hiter being NULL, but also the hiter->dev before dereferencing it. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260721211143.36dbd559@gandalf.local.home Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260715143604.14481-1-gaikwad.dcg%40gmail.com Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index c706544be60cc..d5b4e70df0f9e 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -146,7 +146,7 @@ static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp, goto print_out; } - if (!hiter) + if (!hiter || !hiter->dev) return 0; mmio_print_pcidev(s, hiter->dev); From cf5a82bef623b969a609f2b7e392d06dbae34aa6 Mon Sep 17 00:00:00 2001 From: deepakraog Date: Wed, 15 Jul 2026 20:06:04 +0530 Subject: [PATCH 2719/3297] tracing: Fix resource leak on mmiotrace trace_pipe close commit c1d87e724ae55e781b7cc7ccafb34d9e668582b2 upstream. The mmiotrace tracer was added May 12th 2008. At that time, resources created in pipe_open() could not be freed because there was not pipe_close function pointer of the tracer. The pipe_close function pointer was added in December 7th, 2009, but the mmiotrace tracer was not updated. mmio_pipe_open() allocates a header_iter and takes a pci_dev reference when trace_pipe is opened. mmio_close() frees them, but it was only wired to the tracer's .close callback. tracing_release_pipe() invokes .pipe_close, not .close, when the trace_pipe file is released. As a result, closing trace_pipe with the mmiotrace tracer active leaked the header_iter allocation and left a stale pci_dev reference. Set .pipe_close to mmio_close, matching how function_graph wires both callbacks to the same handler. Note, if the trace_pipe is read to completion, it will clean up the resources, but if one were to run: # head -n 1 /sys/kernel/tracing/trace_pipe VERSION 20070824 Over and over again, it would trigger a massive leak. Cc: stable@vger.kernel.org Fixes: c521efd1700a8 ("tracing: Add pipe_close interface) Link: https://patch.msgid.link/20260715143604.14481-1-gaikwad.dcg@gmail.com Signed-off-by: deepakraog Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index d5b4e70df0f9e..dc8a1eafbe574 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -109,7 +109,6 @@ static void mmio_pipe_open(struct trace_iterator *iter) iter->private = hiter; } -/* XXX: This is not called when the pipe is closed! */ static void mmio_close(struct trace_iterator *iter) { struct header_iter *hiter = iter->private; @@ -279,6 +278,7 @@ static struct tracer mmio_tracer __read_mostly = .start = mmio_trace_start, .pipe_open = mmio_pipe_open, .close = mmio_close, + .pipe_close = mmio_close, .read = mmio_read, .print_line = mmio_print_line, .noboot = true, From b6a4575f22925da7e6aa00171e9fc0e5029c0bc9 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 17 Jul 2026 11:51:49 +0900 Subject: [PATCH 2720/3297] tracing: Fix union collision of module and refcnt for dynamic events commit b4eb07bde606c2096b24252be589e735eff6d413 upstream. In 'struct trace_event_call', the 'module' pointer and the 'refcnt' atomic variable share the same memory space in a union. For dynamic events, the union member is 'refcnt', which acts as an active reference counter. When a dynamic event (such as kprobe, uprobe, fprobe, eprobe, or wprobe) has a non-zero reference count (e.g. due to active event triggers or perf attachments), its 'call->module' evaluates to a small non-zero integer instead of NULL. When filtering or setting events for a specific module (e.g., writing ':mod:' to 'set_event'), the code in '__ftrace_set_clr_event_nolock()' and 'update_event_fields()' reads 'call->module' directly without checking whether the event is dynamic. This causes the kernel to treat the small integer (refcnt) as a 'struct module' pointer, leading to a NULL/invalid pointer dereference (Oops) when dereferencing the module name. Fix this by ensuring that the 'TRACE_EVENT_FL_DYNAMIC' flag is checked before treating 'call->module' as a valid pointer in these code paths. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178425670947.84440.11344393611899824907.stgit@devnote2 Fixes: 4c86bc531e60 ("tracing: Add :mod: command to enabled module events") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_events.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index b86f128ff99fb..b0842df4d6e90 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1333,7 +1333,9 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match, call = file->event_call; /* If a module is specified, skip events that are not that module */ - if (module && (!call->module || strcmp(module_name(call->module), module))) + if (module && + ((call->flags & TRACE_EVENT_FL_DYNAMIC) || + !call->module || strcmp(module_name(call->module), module))) continue; name = trace_event_name(call); From 6b5098d7458117fb67b188fbac07a5ce6546ffe2 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:38 +0900 Subject: [PATCH 2721/3297] tracing/eprobe: Fix exact system name matching in eprobe_dyn_event_match() commit f418d68d71fd4a0a9cef92377bc8c4c3334b5b53 upstream. eprobe_dyn_event_match() checks if the target event system in argv[0] matches ep->event_system using strncmp(ep->event_system, argv[0], len). However, if ep->event_system is longer than len (e.g. "eprobes" vs "ep/event"), strncmp() still returns 0 because the first len characters match. Check that ep->event_system[len] is '\0' to ensure exact system name matching. Link: https://lore.kernel.org/all/178454235856.290363.14872590900774231133.stgit@devnote2/ Fixes: 7d5fda1c841f ("tracing: Fix event probe removal from dynamic events") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_eprobe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index 7697ce22337b6..0a79606419475 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -169,7 +169,8 @@ static bool eprobe_dyn_event_match(const char *system, const char *event, if (!slash) return false; - if (strncmp(ep->event_system, argv[0], slash - argv[0])) + if (strncmp(ep->event_system, argv[0], slash - argv[0]) || + ep->event_system[slash - argv[0]] != '\0') return false; if (strcmp(ep->event_name, slash + 1)) return false; From 949ac1aeb37b87e93660dcf89459f018f0c62ee2 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:10 +0900 Subject: [PATCH 2722/3297] tracing/probes: Avoid temporary buffer truncation in trace_probe_match_command_args() commit 15f197856d68882af9416fc97516bb55079b7677 upstream. In trace_probe_match_command_args(), a stack buffer buf[MAX_ARGSTR_LEN + 1] (256 bytes) is used to format "=". However, since name can be up to 32 bytes (MAX_ARG_NAME_LEN) and comm up to 255 bytes (MAX_ARGSTR_LEN), the formatted string can exceed 256 bytes and get truncated by snprintf(), causing spurious argument matching failures. Instead of formatting into a temporary buffer on stack, compare the argument name, the '=' delimiter, and the comm expression directly. Link: https://lore.kernel.org/all/178454233010.290363.10428767141343428804.stgit@devnote2/ Fixes: eb5bf81330a7 ("tracing/kprobe: Add per-probe delete from event") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_probe.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 2333e5446c9fa..282eb0f4a4f3b 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -2344,16 +2344,17 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b) bool trace_probe_match_command_args(struct trace_probe *tp, int argc, const char **argv) { - char buf[MAX_ARGSTR_LEN + 1]; int i; if (tp->nr_args < argc) return false; for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "%s=%s", - tp->args[i].name, tp->args[i].comm); - if (strcmp(buf, argv[i])) + int len = strlen(tp->args[i].name); + + if (strncmp(argv[i], tp->args[i].name, len) || + argv[i][len] != '=' || + strcmp(argv[i] + len + 1, tp->args[i].comm)) return false; } return true; From 3b3be8653c594d394c0c2763140d865477c4bd8b Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:29 +0900 Subject: [PATCH 2723/3297] tracing/probes: Fix potential underflow in LEN_OR_ZERO macro commit 8ce20bfba48902e1382187cd1a852f7cf3a1e739 upstream. In __set_print_fmt(), LEN_OR_ZERO is defined as (len ? len - pos : 0). If len is non-zero but smaller than pos, len - pos evaluates to a negative integer. When passed as a size argument to snprintf(), this negative value is cast to a large unsigned size_t, bypassing buffer size limits. Ensure len > pos before subtracting to avoid integer underflow. Link: https://lore.kernel.org/all/178454234934.290363.15247317871499514139.stgit@devnote2/ Fixes: 5bf652aaf46c ("tracing/probes: Integrate duplicate set_print_fmt()") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 282eb0f4a4f3b..672acf45ce98f 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -2018,7 +2018,7 @@ int traceprobe_update_arg(struct probe_arg *arg) } /* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) +#define LEN_OR_ZERO (len > pos ? len - pos : 0) static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, enum probe_print_type ptype) { From 9025946adec9a97d6cd599c718be0056fa182020 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:20 +0900 Subject: [PATCH 2724/3297] tracing/probes: Prevent out-of-bounds write in __trace_probe_log_err() commit a9d6fb284039a5d3858a1d9f9a0d7e46cfb7c2d4 upstream. If trace_probe_log.argc is 0 in __trace_probe_log_err(), the loop constructing the command string will not execute and p will remain equal to command. Writing to *(p - 1) will cause an out-of-bounds access before command. This should not happen, but better to be treated. Reject if trace_probe_log.argc is 0. Link: https://lore.kernel.org/all/178454233992.290363.18323091580600697731.stgit@devnote2/ Fixes: ab105a4fb894 ("tracing: Use tracing error_log with probe events") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 672acf45ce98f..918fafc5d45c5 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -187,7 +187,7 @@ void __trace_probe_log_err(int offset, int err_type) lockdep_assert_held(&dyn_event_ops_mutex); - if (!trace_probe_log.argv) + if (!trace_probe_log.argv || !trace_probe_log.argc) return; /* Recalculate the length and allocate buffer */ From 9cd4b1a52eff330798d668c1775f8bc450776280 Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Fri, 3 Jul 2026 11:41:54 +0000 Subject: [PATCH 2725/3297] arm64: make huge_ptep_get handled unaligned addresses commit f73a8edc2ccc6ec72c37d5c578e7592d2e1f9922 upstream. huge_ptep_get() can be handed a virtual address pointing to the middle of a contpmd/contpte mapped hugetlb folio (examples of callers are pagemap_hugetlb_range, page_mapped_in_vma). The arm64 helper rewalks the pgtables in find_num_contig to answer whether the huge pte we have maps a contpmd or a contpte hugetlb folio, and returns CONT_PMDS or CONT_PTES, so that it can collect a/d bits over the contiguous ptes. We can falsely return CONT_PTES instead of CONT_PMDS if the addr is not aligned. On systems where CONT_PTES != CONT_PMDS (meaning page size is 16K), we could collect excess A/D bit state, meaning extra work for the kernel. Even worse, we may iterate beyond the PTE table and dereference a garbage ptep pointer to access physical memory we don't own. Since the ptep pointer is a linear map address, we may run off the end of the linear map or into a hole, dereference a VA not mapped into the kernel pgtables and cause kernel panic. Fix this by aligning the pmdp pointer down to a contpmd base before checking equality with the passed huge pte pointer, to correctly answer whether the huge pte is the base of a contpmd block. Fixes: 29cb80519689 ("arm64: hugetlb: Cleanup huge_pte size discovery mechanisms") Cc: stable@vger.kernel.org Acked-by: David Hildenbrand (Arm) Signed-off-by: Dev Jain Acked-by: Muchun Song Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/mm/hugetlbpage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index 1d90a7e753336..fa63c96dbf79d 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -91,7 +91,7 @@ static int find_num_contig(struct mm_struct *mm, unsigned long addr, p4dp = p4d_offset(pgdp, addr); pudp = pud_offset(p4dp, addr); pmdp = pmd_offset(pudp, addr); - if ((pte_t *)pmdp == ptep) { + if ((pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) { *pgsize = PMD_SIZE; return CONT_PMDS; } From 64ab0964c7db949abbd3c56268a220e2b77f7b9e Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 16 Jul 2026 13:06:39 +0100 Subject: [PATCH 2726/3297] arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates commit e057b94772328221405b067c3a85fe479b915dc8 upstream. When seccomp support was originally added to arm64 in a1ae65b21941 ("arm64: add seccomp support"), seccomp was erroneously called _before_ the ptrace syscall-enter-stop and therefore the tracer could trivially manipulate the syscall register state after the seccomp check had passed. This was subsequently fixed in a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace") by moving the seccomp check after the tracer has run. Unfortunately, a decade later, that fix has been reported to be incomplete. On arm64, both the first argument to a syscall and its eventual return value are allocated to register x0. In order to facilitate syscall restarting and querying of syscall arguments on the syscall exit path, the original value of x0 is stashed in 'struct pt_regs::orig_x0' early during the syscall entry path and is returned for the first argument by syscall_get_arguments(). Unlike 32-bit Arm, this stashed value is not directly exposed via ptrace() and so changes to register x0 made by the tracer on a syscall-enter-stop are not reflected in 'orig_x0'. This means that seccomp, syscall tracepoints and audit can observe a stale value for the register compared to the argument that will be observed by the actual syscall. Re-sync 'orig_x0' from x0 on the syscall entry path following a potential ptrace stop (i.e. PTRACE_EVENTMSG_SYSCALL_ENTRY or SECCOMP_RET_TRACE). This behaviour is limited to native tasks (because compat tasks expose 'orig_r0' to ptrace) where the syscall is not being skipped (because x0 is updated to hold the return value of -ENOSYS in that case). Cc: Kees Cook Cc: Jinjie Ruan Cc: Mark Rutland Cc: stable@vger.kernel.org Reported-by: Yiqi Sun Link: https://lore.kernel.org/all/20260529065444.1336608-1-sunyiqixm@gmail.com/ Suggested-by: Catalin Marinas Fixes: a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace") Reviewed-by: Jinjie Ruan Tested-by: Jinjie Ruan Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/ptrace.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 2acc2c3dd033e..4caece24083d9 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2362,6 +2362,21 @@ static void report_syscall(struct pt_regs *regs, enum ptrace_syscall_dir dir) } } +static void update_syscall_orig_x0_after_ptrace(struct pt_regs *regs) +{ + /* + * Keep orig_x0 authoritative so that seccomp (via + * syscall_get_arguments()), audit and the restart path all see the same + * first argument the syscall is dispatched with, even if it has been + * updated by a tracer. Skip this for NO_SYSCALL (set either by the user + * or the tracer), as regs[0] holds the return value (see the comment in + * el0_svc_common()) and can be unwound using syscall_rollback(). + * For compat tasks, orig_r0 is provided directly through GPR index 17. + */ + if (!is_compat_task() && regs->syscallno != NO_SYSCALL) + regs->orig_x0 = regs->regs[0]; +} + int syscall_trace_enter(struct pt_regs *regs) { unsigned long flags = read_thread_flags(); @@ -2370,12 +2385,26 @@ int syscall_trace_enter(struct pt_regs *regs) report_syscall(regs, PTRACE_SYSCALL_ENTER); if (flags & _TIF_SYSCALL_EMU) return NO_SYSCALL; + + /* + * Ensure ptrace changes to x0 during a regular + * syscall-enter-stop (PTRACE_SYSCALL) are visible to + * subsequent seccomp checks, tracepoints and audit. + */ + update_syscall_orig_x0_after_ptrace(regs); } /* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing() == -1) return NO_SYSCALL; + /* + * Ensure tracer changes to x0 during seccomp ptrace exit + * processing (SECCOMP_RET_TRACE) are visible to tracepoints and + * audit. + */ + update_syscall_orig_x0_after_ptrace(regs); + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, regs->syscallno); From 35c4b274d4cc4e3c953c5a58e4a3b66951d0a1ce Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 17 Jul 2026 17:25:58 +0100 Subject: [PATCH 2727/3297] Revert "arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates" commit 26b483d52417253d88a3a01262ac85914a7aec8e upstream. This reverts commit e057b94772328221405b067c3a85fe479b915dc8. Sashiko points out that updating 'orig_x0' after secure_computing() has returned is too late to handle the case where a seccomp filter is re-evaluated after initially returning SECCOMP_RET_TRACE. This means that a tracer can manipulate the first argument of the syscall behind seccomp's back. For now, revert the initial fix and we'll have another crack at it soon. Since the incorrect fix was cc'd to stable, do the same here with an appropriate fixes tag. Cc: stable@vger.kernel.org Fixes: e057b9477232 ("arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates") Link: https://sashiko.dev/#/patchset/20260716120640.6590-1-will@kernel.org Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/ptrace.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 4caece24083d9..2acc2c3dd033e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2362,21 +2362,6 @@ static void report_syscall(struct pt_regs *regs, enum ptrace_syscall_dir dir) } } -static void update_syscall_orig_x0_after_ptrace(struct pt_regs *regs) -{ - /* - * Keep orig_x0 authoritative so that seccomp (via - * syscall_get_arguments()), audit and the restart path all see the same - * first argument the syscall is dispatched with, even if it has been - * updated by a tracer. Skip this for NO_SYSCALL (set either by the user - * or the tracer), as regs[0] holds the return value (see the comment in - * el0_svc_common()) and can be unwound using syscall_rollback(). - * For compat tasks, orig_r0 is provided directly through GPR index 17. - */ - if (!is_compat_task() && regs->syscallno != NO_SYSCALL) - regs->orig_x0 = regs->regs[0]; -} - int syscall_trace_enter(struct pt_regs *regs) { unsigned long flags = read_thread_flags(); @@ -2385,26 +2370,12 @@ int syscall_trace_enter(struct pt_regs *regs) report_syscall(regs, PTRACE_SYSCALL_ENTER); if (flags & _TIF_SYSCALL_EMU) return NO_SYSCALL; - - /* - * Ensure ptrace changes to x0 during a regular - * syscall-enter-stop (PTRACE_SYSCALL) are visible to - * subsequent seccomp checks, tracepoints and audit. - */ - update_syscall_orig_x0_after_ptrace(regs); } /* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing() == -1) return NO_SYSCALL; - /* - * Ensure tracer changes to x0 during seccomp ptrace exit - * processing (SECCOMP_RET_TRACE) are visible to tracepoints and - * audit. - */ - update_syscall_orig_x0_after_ptrace(regs); - if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, regs->syscallno); From 6c936b5ad557e55cdae9869a2c7bd9e1324ac38b Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Wed, 22 Jul 2026 00:14:38 +0200 Subject: [PATCH 2728/3297] mptcp: decrement subflows counter on failed passive join commit f3ca0ee2cc308e33896536789cbc5f3a12ca7b30 upstream. mptcp_pm_allow_new_subflow() increments extra_subflows before __mptcp_finish_join() on the passive MP_JOIN path. In case of race conditions, the subflow is dropped without calling mptcp_close_ssk(), so the counter is not rolled back. Call mptcp_pm_close_subflow() when the join completion fails to decrement the subflows counter. Fixes: 10f6d46c943d ("mptcp: fix race between MP_JOIN and close") Cc: stable@vger.kernel.org Signed-off-by: Chenguang Zhao Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-1-6fb595bc86ef@kernel.org Signed-off-by: Jakub Kicinski 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 72a909bc0fca4..9e381408d58aa 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3758,6 +3758,7 @@ bool mptcp_finish_join(struct sock *ssk) mptcp_data_unlock(parent); if (!ret) { + mptcp_pm_close_subflow(msk); err_prohibited: subflow->reset_reason = MPTCP_RST_EPROHIBIT; return false; From 6cd3c3d631555efee8831b7e0f95ea5a67f99fbe Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 9 Jul 2026 15:19:25 -0400 Subject: [PATCH 2729/3297] mptcp: only set DATA_FIN when a mapping is present commit b2ff91b752b0d85e8815e7f44fd85205c4268094 upstream. mptcp_get_options() clears only the status group of struct mptcp_options_received; data_seq, subflow_seq and data_len are filled in by mptcp_parse_option() exclusively inside the DSS mapping block, which runs only when the DSS M (mapping present) bit is set. A peer can send a DSS option with the DATA_FIN flag set but the mapping bit clear. The parser then records mp_opt->data_fin while leaving data_len and data_seq uninitialized. For a zero-length segment mptcp_incoming_options() evaluates if (mp_opt.data_fin && mp_opt.data_len == 1 && mptcp_update_rcv_data_fin(msk, mp_opt.data_seq, mp_opt.dsn64)) which reads the uninitialized data_len and data_seq; KMSAN reports an uninit-value in mptcp_incoming_options(). The stale data_seq can also be fed into the receive-side DATA_FIN sequence tracking. Record the DATA_FIN flag only when the DSS option carries a mapping, so data_fin is never set without data_seq and data_len also being present. data_fin is part of the status group that mptcp_get_options() clears up front, so on the no-map path it stays zero and the zero-length DATA_FIN branch is simply skipped. A DATA_FIN is always transmitted together with a mapping (mptcp_write_data_fin() sets use_map along with data_seq and data_len), so legitimate DATA_FIN handling is unaffected. Move the pr_debug() that logs the parsed DSS flags below the mapping block, so it reports the final data_fin value instead of the stale one it would otherwise print before the assignment. Fixes: 43b54c6ee382 ("mptcp: Use full MPTCP-level disconnect state machine") Suggested-by: Paolo Abeni Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260709191925.2811195-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/options.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 845860311ea67..cbece69dc5f01 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -157,17 +157,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, ptr++; flags = (*ptr++) & MPTCP_DSS_FLAG_MASK; - mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0; mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0; mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0; mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0; mp_opt->use_ack = (flags & MPTCP_DSS_HAS_ACK); - pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n", - mp_opt->data_fin, mp_opt->dsn64, - mp_opt->use_map, mp_opt->ack64, - mp_opt->use_ack); - expected_opsize = TCPOLEN_MPTCP_DSS_BASE; if (mp_opt->use_ack) { @@ -178,12 +172,18 @@ static void mptcp_parse_option(const struct sk_buff *skb, } if (mp_opt->use_map) { + mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0; if (mp_opt->dsn64) expected_opsize += TCPOLEN_MPTCP_DSS_MAP64; else expected_opsize += TCPOLEN_MPTCP_DSS_MAP32; } + pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n", + mp_opt->data_fin, mp_opt->dsn64, + mp_opt->use_map, mp_opt->ack64, + mp_opt->use_ack); + /* Always parse any csum presence combination, we will enforce * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */ From d64f6c02495f3fad674038cfa7ec049671b59e7b Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 22 Jul 2026 00:14:39 +0200 Subject: [PATCH 2730/3297] mptcp: pm: userspace: fix use-after-free in get_local_id commit 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9 upstream. In mptcp_pm_userspace_get_local_id(), the address entry is looked up under spinlock, but its id is read after dropping the lock. A concurrent deletion can free the entry between the unlock and the read, leading to UAF. The race window is narrow. It was reproduced only with a locally constructed stress test that repeatedly overlaps an MP_JOIN SYN with a MPTCP_PM_CMD_SUBFLOW_DESTROY request. However, the KASAN report below confirms that the race is reachable: [ 666.319376] BUG: KASAN: slab-use-after-free in mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319386] Read of size 1 at addr ffff888124845610 by task swapper/0/0 ... [ 666.319401] Call Trace: [ 666.319405] [ 666.319408] dump_stack_lvl+0x53/0x70 [ 666.319412] print_address_description.constprop.0+0x2c/0x3b0 [ 666.319418] print_report+0xbe/0x2b0 [ 666.319421] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319423] kasan_report+0xce/0x100 [ 666.319426] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319429] mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319433] mptcp_pm_get_local_id+0x371/0x440 ... [ 666.319821] Allocated by task 45539: [ 666.319844] kasan_save_stack+0x33/0x60 [ 666.319855] kasan_save_track+0x14/0x30 [ 666.319858] __kasan_kmalloc+0x8f/0xa0 [ 666.319863] __kmalloc_noprof+0x1e7/0x520 [ 666.319867] sock_kmalloc+0xdf/0x130 [ 666.319885] sock_kmemdup+0x1b/0x40 [ 666.319888] mptcp_userspace_pm_append_new_local_addr+0x261/0x500 [ 666.319910] mptcp_pm_nl_announce_doit+0x16a/0x610 ... [ 666.319967] Freed by task 45560: [ 666.319988] kasan_save_stack+0x33/0x60 [ 666.319991] kasan_save_track+0x14/0x30 [ 666.319994] kasan_save_free_info+0x3b/0x60 [ 666.319998] __kasan_slab_free+0x43/0x70 [ 666.320000] kfree+0x166/0x440 [ 666.320003] sock_kfree_s+0x1d/0x50 [ 666.320007] mptcp_userspace_pm_delete_local_addr.isra.0+0x157/0x200 [ 666.320011] mptcp_pm_nl_subflow_destroy_doit+0x51d/0xea0 Fix by copying the id into a local variable while still holding the lock, and use -1 as a "not found" sentinel. Fixes: f012d796a6de ("mptcp: check addrs list in userspace_pm_get_local_id") Cc: stable@vger.kernel.org Signed-off-by: Geliang Tang Tested-by: Xuanqiang Luo Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-2-6fb595bc86ef@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mptcp/pm_userspace.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 0d3a95e676f17..db5693262c8c6 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -132,12 +132,15 @@ int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, __be16 msk_sport = ((struct inet_sock *) inet_sk((struct sock *)msk))->inet_sport; struct mptcp_pm_addr_entry *entry; + int id; spin_lock_bh(&msk->pm.lock); entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr); + id = entry ? entry->addr.id : -1; spin_unlock_bh(&msk->pm.lock); - if (entry) - return entry->addr.id; + + if (id != -1) + return id; if (skc->addr.port == msk_sport) skc->addr.port = 0; From f2b293359924117736218701ebd8b40618d73e6f Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 13 Jul 2026 16:22:55 +0100 Subject: [PATCH 2731/3297] afs: Fix afs_edit_dir_remove() to get, not find, block 0 commit 62d9853aa4ce6e9797b6949804891be14b219752 upstream. Fix afs_edit_dir_remove() to use afs_dir_get_block() to get block 0 rather than afs_dir_find_block() as the latter caches the found block in the afs_dir_iter and may[*] switch out the page it's on if another afs_dir_find_block() is done. This parallels what afs_edit_dir_add() does. [*] There's more than one block per page. Fixes: a5b5beebcf96 ("afs: Use the contained hashtable to search a directory") Closes: https://sashiko.dev/#/patchset/20260706153408.1231650-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/2380759.1783956175@warthog.procyon.org.uk cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/dir_edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c index fd3aa9f97ce64..3ead36a070487 100644 --- a/fs/afs/dir_edit.c +++ b/fs/afs/dir_edit.c @@ -415,7 +415,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, if (!afs_dir_init_iter(&iter, name)) return; - meta = afs_dir_find_block(&iter, 0); + meta = afs_dir_get_block(&iter, 0); if (!meta) return; From ac7a6f61f56fe99ca9a53ef0754403a9e46622e4 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 3 Jul 2026 09:17:24 -0700 Subject: [PATCH 2732/3297] mm/kmemleak: fix checksum computation for per-cpu objects commit 79c37ae3733e93d9d8ea12ecb44f717e61439024 upstream. The per-cpu object checksum folds each CPU's CRC together with XOR and seeds every CRC with 0. Both choices make update_checksum() miss content changes: - XOR is self-cancelling, so equal contents on two CPUs cancel out and simultaneous identical changes leave the checksum unchanged. - crc32(0, ...) over all-zero content is 0, so a freshly allocated, zeroed per-cpu area checksums to 0, matching the initial value, and the object is never seen to change. See discussions at [0]. When update_checksum() wrongly reports an actively modified object as unchanged, kmemleak stops greying it for an extra scan and can report a live per-cpu object as a leak. Fold the per-cpu CRC as a single rolling checksum across all CPUs and initialise the object checksum to ~0 so the first computed value always registers as a change, even for content that hashes to 0. reset_checksum() is seeded the same way. Link: https://lore.kernel.org/all/akfYImSNDh3OjIfR@gmail.com [0] Link: https://lore.kernel.org/20260703-kmemleak_checksum-v1-1-5e0ab7d6966f@debian.org Fixes: 6c99d4eb7c5e ("kmemleak: enable tracking for percpu pointers") Signed-off-by: Breno Leitao Co-developed-by: Catalin Marinas Signed-off-by: Catalin Marinas Reviewed-by: Pavel Tikhomirov Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/kmemleak.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 1ac56ceb29b6b..415b154fdf38e 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -676,7 +676,7 @@ static struct kmemleak_object *__alloc_object(gfp_t gfp) atomic_set(&object->use_count, 1); object->excess_ref = 0; object->count = 0; /* white color initially */ - object->checksum = 0; + object->checksum = ~0; object->del_state = 0; /* task information */ @@ -972,7 +972,7 @@ static void reset_checksum(unsigned long ptr) } raw_spin_lock_irqsave(&object->lock, flags); - object->checksum = 0; + object->checksum = ~0; raw_spin_unlock_irqrestore(&object->lock, flags); put_object(object); } @@ -1401,7 +1401,8 @@ static bool update_checksum(struct kmemleak_object *object) for_each_possible_cpu(cpu) { void *ptr = per_cpu_ptr((void __percpu *)object->pointer, cpu); - object->checksum ^= crc32(0, kasan_reset_tag((void *)ptr), object->size); + object->checksum = crc32(object->checksum, + kasan_reset_tag((void *)ptr), object->size); } } else { object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size); From 3db217a4c2bdc87642b314a527e06f12e67608f6 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Wed, 1 Jul 2026 13:42:34 -0400 Subject: [PATCH 2733/3297] mm/huge_memory: set PG_has_hwpoisoned only after new folio head is established commit e1cd30eceb6908fc13bebce41283b885d71ee8d6 upstream. __split_folio_to_order() copies the hwpoison state onto each new sub-folio while splitting a folio to a non-zero order. It does so via if (handle_hwpoison && page_range_has_hwpoisoned(new_head, new_nr_pages)) folio_set_has_hwpoisoned(new_folio); *before* clear_compound_head(new_head)/prep_compound_page(new_head, ...) turns @new_head from a tail page into a proper folio head. PG_has_hwpoisoned is a FOLIO_SECOND_PAGE flag, so folio_set_has_hwpoisoned() resolves to folio_flags(folio, 1). With the new compound_info-based page-flags layout, folio_flags() asserts the page is not a tail: VM_BUG_ON_PGFLAGS(page->compound_info & 1, page); VM_BUG_ON_PGFLAGS(n > 0 && !test_bit(PG_head, &page->flags.f), page); At the current call site @new_head still has the tail marker (compound_info bit 0 set, PG_head clear), so on CONFIG_DEBUG_VM kernels this hits: kernel BUG at include/linux/page-flags.h:354 folio_flags+0x82 folio_set_has_hwpoisoned __split_folio_to_order __split_unmapped_folio __folio_split truncate_inode_partial_folio (shmem hole-punch / MADV_REMOVE) Reproduced by syzkaller: hwpoison-inject a few subpages of a large shmem folio, then MADV_REMOVE (fallocate punch hole) on the same range, which splits the partial folio to a non-zero order. memory_failure() tries to split the poisoned folio to order 0 first, but that split is best-effort; when it fails the folio is left large with PG_has_hwpoisoned set, the case fa5a06170036 added this hwpoison copying for. Move the folio_set_has_hwpoisoned() call to after clear_compound_head()/prep_compound_page(), where @new_folio is a real order-new_order head folio (handle_hwpoison implies new_order != 0, so a second page always exists). The flag still lands on the same struct page (page[1] of the new folio); only the ordering relative to compound-head setup changes, satisfying the FOLIO_SECOND_PAGE precondition. Link: https://lore.kernel.org/20260701174235.3173401-1-riel@surriel.com Fixes: fa5a06170036 ("mm/huge_memory: preserve PG_has_hwpoisoned if a folio is split to >0 order") Signed-off-by: Rik van Riel Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Lance Yang Reviewed-by: Lorenzo Stoakes Reviewed-by: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Yang Shi Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/huge_memory.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index b90be9f16b49b..c08aa3db96431 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3344,10 +3344,6 @@ static void __split_folio_to_order(struct folio *folio, int old_order, (1L << PG_dirty) | LRU_GEN_MASK | LRU_REFS_MASK)); - if (handle_hwpoison && - page_range_has_hwpoisoned(new_head, new_nr_pages)) - folio_set_has_hwpoisoned(new_folio); - new_folio->mapping = folio->mapping; new_folio->index = folio->index + i; @@ -3378,6 +3374,14 @@ static void __split_folio_to_order(struct folio *folio, int old_order, folio_set_large_rmappable(new_folio); } + /* + * PG_has_hwpoisoned is on the 2nd page, so set it after + * the compound head is prepped. + */ + if (handle_hwpoison && + page_range_has_hwpoisoned(new_head, new_nr_pages)) + folio_set_has_hwpoisoned(new_folio); + if (folio_test_young(folio)) folio_set_young(new_folio); if (folio_test_idle(folio)) From 85aca407c560aba81b5ce9d3d6cf94c74077d19b Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Tue, 21 Jul 2026 21:14:05 +0800 Subject: [PATCH 2734/3297] sctp: don't free the ASCONF's own transport in DEL-IP processing commit 9b2854f86f0b56e9027d68e7a3fc909d1a9b566f upstream. sctp_process_asconf() caches the transport the ASCONF chunk is processed against in asconf->transport (== chunk->transport, set once in sctp_rcv()). For an ASCONF located through its Address Parameter by __sctp_rcv_asconf_lookup(), that cached transport corresponds to the Address Parameter, which need not be the packet's source address. sctp_process_asconf_param() rejects a DEL-IP for the packet source address (ADDIP D8, SCTP_ERROR_DEL_SRC_IP), but nothing protects asconf->transport. A single ASCONF can therefore carry, in order: [Address Parameter L] [DEL-IP L] [DEL-IP 0.0.0.0] where L differs from the source. The DEL-IP for L passes the D8 check and calls sctp_assoc_rm_peer() on the transport that asconf->transport still points at, freeing it (RCU-deferred). The following wildcard DEL-IP then reuses the now-dangling asconf->transport in sctp_assoc_set_primary() and sctp_assoc_del_nonprimary_peers(): set_primary() dereferences the freed transport (->ipaddr, ->state) and plants the dangling pointer into asoc->peer.primary_path / active_path, and del_nonprimary_peers(), keeping only the pointer that is no longer on the list, removes every real transport, leaving the association with a transport_count of 0 and primary_path/active_path pointing at freed memory. Reject a DEL-IP that targets the transport the ASCONF is being processed against, mirroring the existing source-address guard, so the wildcard branch can never reuse a freed transport. Fixes: 42e30bf3463c ("[SCTP]: Handle the wildcard ADD-IP Address parameter") Cc: stable@kernel.org Signed-off-by: Jun Yang Acked-by: Xin Long Link: https://patch.msgid.link/tencent_73762ED1DF08CC9D5F5F61954B01350CFE0A@qq.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index a7804e47d3827..1f8a1e6069b3c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3153,6 +3153,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, if (!peer) return SCTP_ERROR_DNS_FAILED; + /* Don't free asconf->transport; a later wildcard DEL-IP + * parameter reuses it. + */ + if (peer == asconf->transport) + return SCTP_ERROR_REQ_REFUSED; + sctp_assoc_rm_peer(asoc, peer); break; case SCTP_PARAM_SET_PRIMARY: From be6aae9d1b91c603adb35872d37d40e83daf8758 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Wed, 15 Jul 2026 09:50:10 +0800 Subject: [PATCH 2735/3297] sctp: avoid auth_enable sysctl UAF during netns teardown commit f8d5e7846025f4ab15a461235f8ebae9094a361a upstream. proc_sctp_do_auth() updates the SCTP control socket after changing net.sctp.auth_enable. The handler gets the per-net SCTP state from ctl->data, so an already opened sysctl file can still target a network namespace while that namespace is being torn down. SCTP previously registered its per-net sysctls from sctp_defaults_init(), while the control socket is created later from sctp_ctrlsock_init(). This exposed a window during initialization where auth_enable was writable before net->sctp.ctl_sock existed, and a teardown window where auth_enable stayed writable after inet_ctl_sock_destroy() had released the control socket. Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after sctp_ctl_sock_init() succeeds, and unregister the sysctl table before destroying the control socket in sctp_ctrlsock_exit(). If sysctl registration fails after the control socket was created, destroy the control socket in the same init path. Make sctp_sysctl_net_unregister() tolerate a missing header and clear the saved pointer so init-error and exit paths can safely share the unregister helper. Fixes: 15649fd5415e ("sctp: sysctl: auth_enable: avoid using current->nsproxy") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Co-developed-by: Qi Tang Signed-off-by: Qi Tang Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Xin Long Link: https://patch.msgid.link/390cd5e91ed60eea27b0b64d0468301a9e73b808.1784033357.git.roxy520tt@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sctp/protocol.c | 20 ++++++++++++-------- net/sctp/sysctl.c | 9 +++++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index bbeff25185b17..3762d1cfda5f2 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1410,10 +1410,6 @@ static int __net_init sctp_defaults_init(struct net *net) net->sctp.l3mdev_accept = 1; #endif - status = sctp_sysctl_net_register(net); - if (status) - goto err_sysctl_register; - /* Allocate and initialise sctp mibs. */ status = init_sctp_mibs(net); if (status) @@ -1447,8 +1443,6 @@ static int __net_init sctp_defaults_init(struct net *net) cleanup_sctp_mibs(net); #endif err_init_mibs: - sctp_sysctl_net_unregister(net); -err_sysctl_register: return status; } @@ -1463,7 +1457,6 @@ static void __net_exit sctp_defaults_exit(struct net *net) net->sctp.proc_net_sctp = NULL; #endif cleanup_sctp_mibs(net); - sctp_sysctl_net_unregister(net); } static struct pernet_operations sctp_defaults_ops = { @@ -1477,16 +1470,27 @@ static int __net_init sctp_ctrlsock_init(struct net *net) /* Initialize the control inode/socket for handling OOTB packets. */ status = sctp_ctl_sock_init(net); - if (status) + if (status) { pr_err("Failed to initialize the SCTP control sock\n"); + return status; + } + + status = sctp_sysctl_net_register(net); + if (status) { + inet_ctl_sock_destroy(net->sctp.ctl_sock); + net->sctp.ctl_sock = NULL; + } return status; } static void __net_exit sctp_ctrlsock_exit(struct net *net) { + sctp_sysctl_net_unregister(net); + /* Free the control endpoint. */ inet_ctl_sock_destroy(net->sctp.ctl_sock); + net->sctp.ctl_sock = NULL; } static struct pernet_operations sctp_ctrlsock_ops = { diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 15e7db9a3ab2e..fca840484ebf7 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -615,11 +615,16 @@ int sctp_sysctl_net_register(struct net *net) void sctp_sysctl_net_unregister(struct net *net) { + struct ctl_table_header *header = net->sctp.sysctl_header; const struct ctl_table *table; - table = net->sctp.sysctl_header->ctl_table_arg; - unregister_net_sysctl_table(net->sctp.sysctl_header); + if (!header) + return; + + table = header->ctl_table_arg; + unregister_net_sysctl_table(header); kfree(table); + net->sctp.sysctl_header = NULL; } static struct ctl_table_header *sctp_sysctl_header; From 3bf0e349cbb4f975f35eb22753acc346b89c66a0 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Wed, 15 Jul 2026 09:50:11 +0800 Subject: [PATCH 2736/3297] sctp: close UDP tunnel sockets during netns teardown commit ffb2bd7ade36ec4da32c46a6eddbf4515316d08c upstream. proc_sctp_do_udp_port() starts per-net SCTP UDP tunneling sockets when net.sctp.udp_port is set, and stops/restarts them when the sysctl value changes. The netns exit path does not stop these sockets, so a namespace can be torn down while its SCTP UDP tunnel sockets are still installed. Close the UDP tunnel sockets from sctp_ctrlsock_exit() after unregistering the per-net sysctl table. This prevents new sysctl writes from racing in while the sockets are being released, and closes the sockets before the control socket is destroyed. Fixes: 046c052b475e ("sctp: enable udp tunneling socks") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/b9f1f02b0780ad6a719e2413f5f0bb8eb7702d94.1782585631.git.roxy520tt%40gmail.com Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Xin Long Link: https://patch.msgid.link/6dab75f22855cb219e2e30a5497cab03b970ab91.1784033357.git.roxy520tt@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sctp/protocol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 3762d1cfda5f2..5718328cb8a79 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1487,6 +1487,7 @@ static int __net_init sctp_ctrlsock_init(struct net *net) static void __net_exit sctp_ctrlsock_exit(struct net *net) { sctp_sysctl_net_unregister(net); + sctp_udp_sock_stop(net); /* Free the control endpoint. */ inet_ctl_sock_destroy(net->sctp.ctl_sock); From a4228b93706fb74a484e6ffb271c1cc2af3a2ddb Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 29 May 2026 00:37:24 +0000 Subject: [PATCH 2737/3297] ceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps() commit 4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02 upstream. ceph_handle_caps() reads snap_trace_len from the wire-format ceph_mds_caps header and uses it unconditionally to build a fake end pointer (snaptrace + snaptrace_len) that is later handed to ceph_update_snap_trace() in the CEPH_CAP_OP_IMPORT case: snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); p = snaptrace + snaptrace_len; ... case CEPH_CAP_OP_IMPORT: if (snaptrace_len) { ... if (ceph_update_snap_trace(mdsc, snaptrace, snaptrace + snaptrace_len, false, &realm)) { ... } ceph_update_snap_trace() then decodes a struct ceph_mds_snap_realm from snaptrace using ceph_decode_need(&p, e, sizeof(*ri), bad) with the attacker-supplied fake end e == snaptrace + snaptrace_len. With snaptrace_len == 0xFFFFFFFF the bound check is trivially satisfied, ri = p reads sizeof(struct ceph_mds_snap_realm) past the legitimate msg->front buffer, and ri->num_snaps / ri->num_prior_parent_snaps then drive further out-of-bounds reads of the encoded snap arrays. The eleven msg_version >= 2 .. msg_version >= 12 decoder blocks above the op switch each catch this OOB through their ceph_decode_*_safe() / ceph_decode_need() helpers, but they sit behind a hdr.version-gated if, so a malicious or compromised MDS that sets msg->hdr.version = 1 reaches the IMPORT path with no version-gated decoder having validated snap_trace_len. The shape has been present since ceph_handle_caps() was introduced. Validate snap_trace_len against the message front buffer before consuming it, using the canonical ceph_decode_need() / ceph_has_room() helper. The helper bounds the length with subtraction (n <= end - p, guarded by end >= p) rather than pointer addition, so it is wrap-safe for the attacker-controlled u32 length on 32-bit builds where p + snap_trace_len could overflow the address space. This matches the rest of the ceph decode path (e.g. the pool_ns_len check a few lines below), and the existing goto bad cleanup already covers this exit path. Cc: stable@vger.kernel.org Fixes: a8599bd821d0 ("ceph: capability management") Signed-off-by: Bryam Vargas Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- fs/ceph/caps.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index b1a8ff612c41d..d9924ef55f4a2 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -4364,6 +4364,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); + ceph_decode_need(&snaptrace, end, snaptrace_len, bad); p = snaptrace + snaptrace_len; if (msg_version >= 2) { From a7c2dfa610a12ac725d51290b550133a89a0c466 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 22:40:07 +0800 Subject: [PATCH 2738/3297] ceph: fix refcount leak in ceph_readdir() commit c3e64079d8b9663e3998d0caac9aba915b6b93ae upstream. The ceph_readdir() function allocates a ceph_mds_request via ceph_mdsc_create_request() and stores it in dfi->last_readdir. In the directory entry processing loop, if the entry's offset is less than ctx->pos or if the inode pointer is unexpectedly NULL, the function returns -EIO without releasing the reference held by dfi->last_readdir, causing a refcount leak. Fix this by adding ceph_mdsc_put_request(dfi->last_readdir) before returning on these error paths. Also set dfi->last_readdir to NULL for safety, matching the cleanup done at the normal exit. Cc: stable@vger.kernel.org Fixes: af9ffa6df7e3 ("ceph: add support to readdir for encrypted names") Signed-off-by: WenTao Liang Reviewed-by: Viacheslav Dubeyko Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- fs/ceph/dir.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 66e592c47e57a..c30510bdae3a5 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -546,11 +546,16 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) pr_warn_client(cl, "%p %llx.%llx rde->offset 0x%llx ctx->pos 0x%llx\n", inode, ceph_vinop(inode), rde->offset, ctx->pos); + ceph_mdsc_put_request(dfi->last_readdir); + dfi->last_readdir = NULL; return -EIO; } - if (WARN_ON_ONCE(!rde->inode.in)) + if (WARN_ON_ONCE(!rde->inode.in)) { + ceph_mdsc_put_request(dfi->last_readdir); + dfi->last_readdir = NULL; return -EIO; + } ctx->pos = rde->offset; doutc(cl, "%p %llx.%llx (%d/%d) -> %llx '%.*s' %p\n", inode, From 7d03e08b763fd67cf796da3026b2597ce0035bba Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 4 Jun 2026 02:19:51 +0000 Subject: [PATCH 2739/3297] ceph: fix writeback_count leak in write_folio_nounlock() commit cbf59617cd715219e84c50d106a3d0e1e8ba054e upstream. write_folio_nounlock() increments fsc->writeback_count to track in-flight writeback operations. On several error paths where the function returns early (folio lookup failure, snapshot context allocation failure, and writepages submission failure), the function returns without calling atomic_long_dec_return() to decrement the counter. Each leaked increment keeps the counter above zero, which can prevent the filesystem from cleanly unmounting or suspending writes. Add atomic_long_dec_return() calls on all error paths that currently return without decrementing the counter. Cc: stable@vger.kernel.org Fixes: d55207717ded ("ceph: add encryption support to writepage and writepages") Signed-off-by: Wentao Liang Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- fs/ceph/addr.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 3af6795cb3c15..ea31c892a1fb1 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -785,6 +785,9 @@ static int write_folio_nounlock(struct folio *folio, ceph_wbc.truncate_size, true); if (IS_ERR(req)) { folio_redirty_for_writepage(wbc, folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(req); } @@ -804,6 +807,9 @@ static int write_folio_nounlock(struct folio *folio, folio_redirty_for_writepage(wbc, folio); folio_end_writeback(folio); ceph_osdc_put_request(req); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(bounce_page); } } @@ -838,6 +844,9 @@ static int write_folio_nounlock(struct folio *folio, ceph_vinop(inode), folio); folio_redirty_for_writepage(wbc, folio); folio_end_writeback(folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return err; } if (err == -EBLOCKLISTED) From 4e7ebfaa0d14cf50e44041bfde38070d6dbc019f Mon Sep 17 00:00:00 2001 From: Douya Le Date: Sun, 7 Jun 2026 17:35:49 +0800 Subject: [PATCH 2740/3297] libceph: bound get_version reply decode to front len commit d3c32939fa0e3ee9b883b9a0fd1972c5c444e3d0 upstream. handle_get_version_reply() uses msg->front_alloc_len as the decode boundary for MON_GET_VERSION_REPLY. That is the size of the reused reply buffer, not the number of bytes actually received. A truncated reply can therefore pass ceph_decode_need() and decode the second u64 from stale tail bytes left in the buffer by an earlier message, causing an uninitialized memory read. Use msg->front.iov_len as the receive-side decode boundary, matching other libceph reply handlers and limiting decoding to the bytes that were actually read from the wire. Cc: stable@vger.kernel.org Fixes: 513a8243d67f ("libceph: mon_get_version request infrastructure") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Douya Le Signed-off-by: Ren Wei Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index ff4311d5764bd..1d7662e13e087 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -821,7 +821,7 @@ static void handle_get_version_reply(struct ceph_mon_client *monc, struct ceph_mon_generic_request *req; u64 tid = le64_to_cpu(msg->hdr.tid); void *p = msg->front.iov_base; - void *end = p + msg->front_alloc_len; + void *const end = p + msg->front.iov_len; u64 handle; dout("%s msg %p tid %llu\n", __func__, msg, tid); From 1732d89dfcd74f6fde9ce70900d316c4a151c153 Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Wed, 27 May 2026 16:06:17 +0200 Subject: [PATCH 2741/3297] libceph: Fix multiplication overflow in decode_new_up_state_weight() commit 98917a499ec7064c14fc56d180a4fd636fc2784c upstream. If a message of type CEPH_MSG_OSD_MAP contains a (maliciously) corrupted osdmap, out-of-bounds memory accesses may occur in decode_new_up_state_weight(). This happens because the bounds check for the new_state part is based on calculating its length depending on a len value read from the incoming message. This calculation may overflow leading to an incorrect bounds check. Subsequently, out-of-bounds reads may occur when decoding this part. This patch switches the multiplication to use check_mul_overflow() to abort processing the osdmap if an overflow occurred. Therefore, osdmaps/messages containing large values for len that result in a multiplication overflow are treated as invalid. [ idryomov: rename new_state_len -> new_state_item_size, formatting ] Cc: stable@vger.kernel.org Fixes: 930c53286977 ("libceph: apply new_state before new_up_client on incrementals") Signed-off-by: Raphael Zimmer Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index c34a5bf86831b..d16bcc6929869 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1844,6 +1844,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, void *new_up_client; void *new_state; void *new_weight_end; + const u32 new_state_item_size = + sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8)); u32 len; int ret; int i; @@ -1864,7 +1866,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, new_state = *p; ceph_decode_32_safe(p, end, len, e_inval); - len *= sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8)); + if (check_mul_overflow(len, new_state_item_size, &len)) + goto e_inval; ceph_decode_need(p, end, len, e_inval); *p += len; From 4716a64b7cc2797741f7be4e283ace78a9dff37d Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Fri, 19 Jun 2026 15:40:03 +0800 Subject: [PATCH 2742/3297] libceph: guard missing CRUSH type name lookup commit bbeae12fda3384a90fbebc8a19ba9d33f85b5361 upstream. Localized read selection can walk a parent bucket whose name exists in the CRUSH map while its type has no matching entry in type_names. get_immediate_parent() then dereferences a NULL type_cn and passes an invalid pointer into strcmp(), causing a null-ptr-deref. Skip such malformed parent buckets unless both the bucket name and type name metadata are present. This keeps malformed hierarchy data from crashing locality lookup and safely falls back to "not local". [ idryomov: add WARN_ON_ONCE ] Cc: stable@vger.kernel.org Fixes: 117d96a04f00 ("libceph: support for balanced and localized reads") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index d16bcc6929869..0aa4b106a1578 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -3060,8 +3060,11 @@ static int get_immediate_parent(struct crush_map *c, int id, if (b->items[j] != id) continue; - *parent_type_id = b->type; type_cn = lookup_crush_name(&c->type_names, b->type); + if (WARN_ON_ONCE(!type_cn)) + continue; + + *parent_type_id = b->type; parent_loc->cl_type_name = type_cn->cn_name; parent_loc->cl_name = cn->cn_name; return b->id; From 0060ec912292a550198d8d18ac95b433c92a7091 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Mon, 29 Jun 2026 13:14:22 -0400 Subject: [PATCH 2743/3297] libceph: refresh auth->authorizer_buf{,_len} after authorizer update commit 937d61f86d377a3aa578adae7a3dfcecdddf9d89 upstream. ceph_x_create_authorizer() caches au->buf->vec.iov_base and au->buf->vec.iov_len in struct ceph_auth_handshake. These cached values are then used by the messenger connect code when sending the authorizer. ceph_x_update_authorizer() can rebuild the authorizer when a newer service ticket is available. If the rebuilt authorizer no longer fits in the existing buffer, ceph_x_build_authorizer() drops its reference to au->buf and allocates a new one. If this is the final reference, ceph_buffer_put() frees the old ceph_buffer and its vec.iov_base, but auth->authorizer_buf still points at that freed memory. A subsequent msgr1 reconnect can therefore queue the stale pointer and trigger a KASAN slab-use-after-free in _copy_from_iter() while tcp_sendmsg() copies the authorizer. Refresh auth->authorizer_buf and auth->authorizer_buf_len after a successful authorizer rebuild so the messenger sends the current buffer. Cc: stable@vger.kernel.org Fixes: 0bed9b5c523d ("libceph: add update_authorizer auth method") Closes: https://lore.kernel.org/all/E378850E-106C-427B-A241-970EB2D054D7@gmail.com/ Signed-off-by: Shuangpeng Bai Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- net/ceph/auth_x.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c index a21c157daf7dd..c83559f015e46 100644 --- a/net/ceph/auth_x.c +++ b/net/ceph/auth_x.c @@ -781,9 +781,16 @@ static int ceph_x_update_authorizer( au = (struct ceph_x_authorizer *)auth->authorizer; if (au->secret_id < th->secret_id) { + int ret; + dout("ceph_x_update_authorizer service %u secret %llu < %llu\n", au->service, au->secret_id, th->secret_id); - return ceph_x_build_authorizer(ac, th, au); + ret = ceph_x_build_authorizer(ac, th, au); + if (ret) + return ret; + + auth->authorizer_buf = au->buf->vec.iov_base; + auth->authorizer_buf_len = au->buf->vec.iov_len; } return 0; } From e67e8b694872c9bc66996040f9de9242f6236ed9 Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Fri, 29 May 2026 09:42:57 +0200 Subject: [PATCH 2744/3297] libceph: Reject monmaps advertising zero monitors commit 40480eee361ed9676b3f844d532ac28b47251634 upstream. A message of type CEPH_MSG_MON_MAP contains a monmap that is sent from a monitor to the client. This monmap contains information about the existing monitors in the cluster. Currently, a monmap indicating that there are zero monitors in the cluster is treated as valid. However, it is impossible to have zero monitors in the cluster and still receive a valid monmap from a monitor. Therefore, such a monmap must be corrupted and should be treated as invalid. Furthermore, a monmap with a monitor count of zero can subsequently crash the client when attempting to open a session with a monitor in __open_session(). This happens because the "BUG_ON(monc->monmap->num_mon < 1)" assertion in pick_new_mon() is triggered. This patch extends a check in ceph_monmap_decode() to also reject arriving mon_maps with num_mon == 0 rather than only with num_mon > CEPH_MAX_MON. [ idryomov: drop "log output for unusual values of num_mon" part ] Cc: stable@vger.kernel.org Signed-off-by: Raphael Zimmer Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 1d7662e13e087..96c9c4d0a375e 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_decode(void **p, void *end, bool msgr2) dout("%s fsid %pU epoch %u num_mon %u\n", __func__, &fsid, epoch, num_mon); - if (num_mon > CEPH_MAX_MON) + if (num_mon == 0 || num_mon > CEPH_MAX_MON) goto e_inval; monmap = kmalloc(struct_size(monmap, mon_inst, num_mon), GFP_NOIO); From 3b2f1937f5fce8b7dd5432e7693e3cc8b5eece56 Mon Sep 17 00:00:00 2001 From: Douya Le Date: Fri, 29 May 2026 16:11:44 +0800 Subject: [PATCH 2745/3297] libceph: reject zero bucket types in crush_decode commit 05f90284223381005d6bcddab3fda4a97f9c3401 upstream. CRUSH bucket type 0 is reserved for devices. The mapper relies on that invariant and uses type 0 to identify leaf devices. If crush_decode() accepts a bucket with type 0, a malformed CRUSH map can make the mapper treat a negative bucket ID as a device and pass it to is_out(), which then indexes the OSD weight array with a negative value. Reject zero bucket types while decoding the CRUSH map so the invalid state never reaches the mapper. Cc: stable@vger.kernel.org Fixes: f24e9980eb86 ("ceph: OSD client") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Douya Le Signed-off-by: Ren Wei Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- net/ceph/osdmap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 0aa4b106a1578..30d75970be449 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -520,6 +520,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end) ceph_decode_need(p, end, 4*sizeof(u32), bad); b->id = ceph_decode_32(p); b->type = ceph_decode_16(p); + if (b->type == 0) + goto bad; b->alg = ceph_decode_8(p); if (b->alg != alg) { b->alg = 0; From 8f5a3abc54ba24dbceb14cc3a719908c4f688091 Mon Sep 17 00:00:00 2001 From: Douya Le Date: Mon, 15 Jun 2026 14:31:06 +0800 Subject: [PATCH 2746/3297] libceph: remove debugfs files before client teardown commit e4c804726c4afce3ba648b982d564f6af2cfa328 upstream. ceph_destroy_client() tears down the monitor client before removing the per-client debugfs files. A concurrent read of the monmap debugfs file can enter monmap_show() after ceph_monc_stop() has freed monc->monmap, triggering a use-after-free. Remove the debugfs files before stopping the OSD and monitor clients. debugfs_remove() drains active handlers and prevents new accesses, so the debugfs callbacks can no longer race the rest of client teardown. Cc: stable@vger.kernel.org Fixes: 76aa844d5b2f ("ceph: debugfs") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Douya Le Signed-off-by: Ren Wei Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- net/ceph/ceph_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index e734e57be083a..5974c23347dc3 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -763,13 +763,13 @@ void ceph_destroy_client(struct ceph_client *client) atomic_set(&client->msgr.stopping, 1); + ceph_debugfs_client_cleanup(client); + /* unmount */ ceph_osdc_stop(&client->osdc); ceph_monc_stop(&client->monc); ceph_messenger_fini(&client->msgr); - ceph_debugfs_client_cleanup(client); - ceph_destroy_options(client->options); kfree(client); From 1a644db2cf59f164cdf3c75995bab5aadc097528 Mon Sep 17 00:00:00 2001 From: Shihuang Liu Date: Wed, 22 Jul 2026 19:39:19 +0800 Subject: [PATCH 2747/3297] amt: fix use-after-free in AMT delayed works commit ea20c44935d6142daecfa9b39d635033a7553e1b upstream. When an AMT device is removed, pending delayed works can still access the freed amt_dev structure, which may result in kernel crashes or memory corruption. amt_dev_stop() cancels req_wq and discovery_wq with cancel_delayed_work_sync(), but these works can be scheduled again from event_wq after the cancellation. This allows delayed works to access the freed amt_dev structure after the netdev has been released. The following is a simple race scenario: CPU0 CPU1 amt_dev_stop() cancel_delayed_work_sync() amt_event_work() mod_delayed_work(req_wq) free netdev req_wq accesses freed amt_dev Use disable_delayed_work_sync() in amt_dev_stop() to prevent req_wq and discovery_wq from being queued again and wait for running work items to complete. The delayed works are disabled after initialization in amt_newlink() and enabled only when the device is successfully opened. This keeps the delayed work lifecycle synchronized with the lifetime of the AMT device. Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface") Cc: stable@vger.kernel.org Signed-off-by: Shihuang Liu Reviewed-by: Simon Horman Reviewed-by: Taehee Yoo Link: https://patch.msgid.link/20260722113919.7723-1-shlomojune6@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/amt.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index cddcad172bbce..2bc8781f07c2d 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3034,9 +3034,15 @@ static int amt_dev_open(struct net_device *dev) amt->event_idx = 0; amt->nr_events = 0; + enable_delayed_work(&amt->discovery_wq); + enable_delayed_work(&amt->req_wq); + err = amt_socket_create(amt); - if (err) + if (err) { + disable_delayed_work(&amt->req_wq); + disable_delayed_work(&amt->discovery_wq); return err; + } amt->req_cnt = 0; amt->remote_ip = 0; @@ -3062,8 +3068,8 @@ static int amt_dev_stop(struct net_device *dev) struct sk_buff *skb; int i; - cancel_delayed_work_sync(&amt->req_wq); - cancel_delayed_work_sync(&amt->discovery_wq); + disable_delayed_work_sync(&amt->req_wq); + disable_delayed_work_sync(&amt->discovery_wq); cancel_delayed_work_sync(&amt->secret_wq); /* shutdown */ @@ -3317,6 +3323,8 @@ static int amt_newlink(struct net_device *dev, INIT_DELAYED_WORK(&amt->req_wq, amt_req_work); INIT_DELAYED_WORK(&amt->secret_wq, amt_secret_work); INIT_WORK(&amt->event_wq, amt_event_work); + disable_delayed_work(&amt->req_wq); + disable_delayed_work(&amt->discovery_wq); INIT_LIST_HEAD(&amt->tunnel_list); return 0; err: From c4d77740eca2198dd2c9651db7c6e1dcd3e572eb Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Fri, 10 Jul 2026 12:13:33 +0900 Subject: [PATCH 2748/3297] ASoC: fsl: imx-card: Skip sysclk reset for active DAIs in shutdown commit 9f86aea992568c2b4db78c80ff9508af9e050ff7 upstream. In a full-duplex setup, when one direction (playback or capture) is closed while the other is still running, imx_aif_shutdown() was unconditionally calling snd_soc_dai_set_sysclk() with rate=0 for all cpu/codec DAIs, which would disable the clock still needed by the active stream. Add snd_soc_dai_active() checks before clearing sysclk so that only truly inactive DAIs have their clocks reset. Fixes: 2260bc6ea8bd ("ASoC: imx-card: Add WM8524 support") Cc: stable@vger.kernel.org Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260710031333.3491445-1-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/fsl/imx-card.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index a4518fefad690..43438af1e1c68 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -496,11 +496,15 @@ static void imx_aif_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai; int i; - for_each_rtd_cpu_dais(rtd, i, cpu_dai) - snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + if (!snd_soc_dai_active(cpu_dai)) + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + } - for_each_rtd_codec_dais(rtd, i, codec_dai) - snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (!snd_soc_dai_active(codec_dai)) + snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); + } } static const struct snd_soc_ops imx_aif_ops = { From d309f8b52b34ca481600e83359d3fac570b7183a Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Fri, 10 Jul 2026 16:08:35 +0900 Subject: [PATCH 2749/3297] ASoC: fsl_sai: Fix spurious BCLK on resume by clearing BYP commit d091132889c1378dd0944a72f86eae3e4da1e4fa upstream. When the BCLK divider ratio is 1:1, fsl_sai_set_bclk() enables bypass mode by setting BYP, but never clears the bit. The BYP=1 value remains in the regcache, and is restored by regcache_sync() on the next runtime resume. Since BYP=1 combined with BCD=1 immediately outputs the ungated MCLK as BCLK without waiting for BCE/TE/RE to be enabled, the clock is driven prematurely before the stream is fully configured, causing noise on some codecs. Fix this by clearing BYP and BCI in fsl_sai_hw_free() taking into account sync mode and the opposite stream's state, so that the regcache holds BYP=0 before runtime suspend and regcache_sync() on resume will not restore bypass mode prematurely. Fixes: a50b7926d015 ("ASoC: fsl_sai: implement 1:1 bclk:mclk ratio support") Cc: stable@vger.kernel.org Signed-off-by: Chancel Liu Reviewed-by: Shengjiu Wang Link: https://patch.msgid.link/20260710070835.3749817-1-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/fsl/fsl_sai.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index d48f72e5d49b6..03107feb9ecaa 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -757,6 +757,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; unsigned int ofs = sai->soc_data->reg_offset; + int adir = tx ? RX : TX; + int dir = tx ? TX : RX; /* Clear xMR to avoid channel swap with mclk_with_tere enabled case */ regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0); @@ -764,10 +766,29 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), FSL_SAI_CR3_TRCE_MASK, 0); - if (!sai->is_consumer_mode[tx] && - sai->mclk_streams & BIT(substream->stream)) { - clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); - sai->mclk_streams &= ~BIT(substream->stream); + if (!sai->is_consumer_mode[tx]) { + bool adir_active = !!(sai->mclk_streams & BIT(!substream->stream)); + /* + * If opposite stream provides clocks for synchronous mode and + * it is inactive, Clear BYP and BCI + */ + if (fsl_sai_dir_is_synced(sai, adir) && !adir_active) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), + FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0); + /* + * Clear BYP and BCI of current stream if either of: + * 1. current stream doesn't provide clocks for synchronous mode + * 2. current stream provides clocks for synchronous mode but no + * more stream is active. + */ + if (!fsl_sai_dir_is_synced(sai, dir) || !adir_active) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), + FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0); + + if (sai->mclk_streams & BIT(substream->stream)) { + clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); + sai->mclk_streams &= ~BIT(substream->stream); + } } return 0; From 89b9121c3b0162655fc2f190b714ae64f1aa8cae Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 21 Jul 2026 13:20:45 +0200 Subject: [PATCH 2750/3297] binfmt_elf_fdpic: only honour the first PT_INTERP commit 3349ef6a366a61d631f6a263d12cea240957719d upstream. The program header scan handles PT_INTERP from a switch nested in the scan loop, so its break leaves the switch and not the loop. A binary carrying more than one PT_INTERP runs the case again and overwrites both interpreter_name and interpreter. The previous name allocation leaks and so does the previous interpreter reference, along with the write denial open_exec() took on it. The denial is never released, so the file stays unwritable for as long as the system runs. An unprivileged caller reaches this with a crafted binary and repeats it at will. binfmt_elf stops at the first PT_INTERP. Do the same here. The flaw dates back to the driver's introduction in the pre-git history tree introduced in v2.6.11 by 91808d6ebe39 ("[PATCH] FRV: Add FDPIC ELF binary format driver"). Link: https://patch.msgid.link/20260721-gezittert-medium-kreide-b41fc1f0277e@brauner Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Jori Koolstra Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_elf_fdpic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index a3d4e6973b299..7834ca77877db 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -231,6 +231,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) { switch (phdr->p_type) { case PT_INTERP: + /* elf ABI allows only one interpreter */ + if (interpreter_name) + continue; + retval = -ENOMEM; if (phdr->p_filesz > PATH_MAX) goto error; From c78e38745ff1b0457c4551e7f75ea15842df1169 Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Tue, 21 Jul 2026 14:41:40 +0800 Subject: [PATCH 2751/3297] fs/super: fix emergency thaw double-unlock of s_umount commit 503d67fbaec6fdeaba391cb497675071db9d16ea upstream. do_thaw_all() iterates over all superblocks via __iterate_supers() with SUPER_ITER_EXCL, which acquires s_umount exclusively before calling the callback and releases it afterwards. However, the callback do_thaw_all_callback() calls thaw_super_locked() which unconditionally releases s_umount on every code path. This results in a second unlock attempt in __iterate_supers() that corrupts the rwsem state, triggering a DEBUG_RWSEMS warning: [ 182.601148] sysrq: Emergency Thaw of all frozen filesystems [ 182.601865] ------------[ cut here ]------------ [ 182.602375] DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) && !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE)): count = 0x0, magic = 0xffff99b1011e5870, owner = 0x0, curr 0xffff99b101b06c80, list not empty [ 182.603817] WARNING: kernel/locking/rwsem.c:1412 at up_write+0xa3/0x170, CPU#2: kworker/2:1/53 [ 182.604578] Modules linked in: [ 182.604864] CPU: 2 UID: 0 PID: 53 Comm: kworker/2:1 Not tainted 7.2.0-rc4-00001-gbd3bd93ea98a-dirty #4 PREEMPT(lazy) [ 182.605711] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1kylin1 04/01/2014 [ 182.606417] Workqueue: events do_thaw_all [ 182.606750] RIP: 0010:up_write+0xaf/0x170 [ 182.607076] Code: 19 3a 92 48 0f 44 c2 48 8b 55 08 48 8b 55 00 4c 8b 45 08 48 8b 55 00 48 8d 3d ad 91 e0 01 48 8b 4d 20 50 48 c7 c6 f0 8c 26 92 <67> 48 0f b9 3a e8 d7 93 4e 00 58 eb 81 48 83 7f 18 00 48 c7 c2 8d [ 182.608563] RSP: 0018:ffffb670001d7e08 EFLAGS: 00010246 [ 182.609007] RAX: ffffffff92349e8d RBX: 0000000000000000 RCX: ffff99b1011e5870 [ 182.609595] RDX: 0000000000000000 RSI: ffffffff92268cf0 RDI: ffffffff92914d10 [ 182.610283] RBP: ffff99b1011e5870 R08: 0000000000000000 R09: ffff99b101b06c80 [ 182.610847] R10: ffff99b10139a808 R11: fefefefefefefeff R12: 0000000000000000 [ 182.611414] R13: ffffffff90cf74d0 R14: 0000000000000000 R15: ffff99b1011e5800 [ 182.612009] FS: 0000000000000000(0000) GS:ffff99b1eaaee000(0000) knlGS:0000000000000000 [ 182.612670] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 182.613146] CR2: 00000000005c631c CR3: 00000000013ee000 CR4: 00000000000006f0 [ 182.613722] Call Trace: [ 182.613946] [ 182.614130] __iterate_supers+0x128/0x150 [ 182.614463] do_thaw_all+0x1b/0x30 [ 182.614759] process_scheduled_works+0xbb/0x3f0 [ 182.615150] ? __pfx_worker_thread+0x10/0x10 [ 182.615499] worker_thread+0x129/0x270 [ 182.615816] ? __pfx_worker_thread+0x10/0x10 [ 182.616201] kthread+0xe2/0x120 [ 182.616469] ? __pfx_kthread+0x10/0x10 [ 182.616792] ret_from_fork+0x15b/0x240 [ 182.617115] ? __pfx_kthread+0x10/0x10 [ 182.617426] ret_from_fork_asm+0x1a/0x30 [ 182.617761] [ 182.617968] ---[ end trace 0000000000000000 ]--- [ 182.618412] Emergency Thaw complete Fix this by switching to SUPER_ITER_UNLOCKED and acquiring s_umount in the callback via super_lock_excl() before calling thaw_super_locked(). This matches the locking pattern expected by thaw_super_locked() and eliminates the double unlock. While at it, remove the dead 'return;' at the end of do_thaw_all_callback(). Fixes: 2992476528ae ("super: use a common iterator (Part 1)") Cc: stable@vger.kernel.org Signed-off-by: Chen Changcheng Link: https://patch.msgid.link/20260721064140.152305-1-chenchangcheng@kylinos.cn Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/super.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/super.c b/fs/super.c index ce6e5b4ff3c5f..1d3f4fe0a1050 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1141,16 +1141,19 @@ void emergency_remount(void) static void do_thaw_all_callback(struct super_block *sb, void *unused) { + if (!super_lock_excl(sb)) + return; + if (IS_ENABLED(CONFIG_BLOCK)) while (sb->s_bdev && !bdev_thaw(sb->s_bdev)) pr_warn("Emergency Thaw on %pg\n", sb->s_bdev); + thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL); - return; } static void do_thaw_all(struct work_struct *work) { - __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_EXCL); + __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_UNLOCKED); kfree(work); printk(KERN_WARNING "Emergency Thaw complete\n"); } From a019b074903b3ad0a9726087efd0e8291452023b Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 14 Jul 2026 00:09:31 +0200 Subject: [PATCH 2752/3297] fs: preserve ACL_DONT_CACHE state in forget_cached_acl() commit 4b9a5458d02e214ef2b384124ca626e3e381d778 upstream. The ACL_DONT_CACHE state is meant to be a constant state for the inode for filesystems that want to opt out of posix acl caching. Commit facd61053cff1 ("fuse: fixes after adapting to new posix acl api") used this facility to opt out of posix acl caching for fuse inodes with fuse server that does not negotiate FUSE_POSIX_ACL (fc->posix_acl). The commit also takes care to gate the forget_all_cached_acls() call in fuse_set_acl() on fc->posix_acl because there is no need for it, but there are other placed in fuse code which call forget_all_cached_acls() unconditional to fc->posix_acl and those cause the loss of the ACL_DONT_CACHE state. This is not only a functional bug. Properly timed, a get_acl() from this fuse filesystem can return a stale cached value, as was observed in tests, because set_acl() does not invalidate the unintentional acl cache. We could fix this in fuse, but it actually makes no sense for the vfs helper forget_cached_acl() to invalidate the ACL_DONT_CACHE state, so let it not do that to fix fuse and future users of ACL_DONT_CACHE. Fixes: facd61053cff1 ("fuse: fixes after adapting to new posix acl api") Cc: stable@vger.kernel.org Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260713220932.413004-2-amir73il@gmail.com Reviewed-by: Luis Henriques Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/posix_acl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 4050942ab52f9..0d92d6a631e1d 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -93,6 +93,13 @@ static void __forget_cached_acl(struct posix_acl **p) { struct posix_acl *old; + /* + * ACL_DONT_CACHE is expected to be a "const" value and xchg it with + * ACL_NOT_CACHED would enable acl caching for the inode - + * clearly not what the caller has intended. + */ + if (READ_ONCE(*p) == ACL_DONT_CACHE) + return; old = xchg(p, ACL_NOT_CACHED); if (!is_uncached_acl(old)) posix_acl_release(old); From 95376fe9c145be35566991df99c53134943d992f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 20:31:20 -0700 Subject: [PATCH 2753/3297] fscrypt: Add missing superblock check in find_or_insert_direct_key() commit b5fa40226e71c17847b9ff2816c6ca4133d0d994 upstream. The legacy 'fscrypt_direct_keys' table caches master keys that are used by v1 encryption policies that have FSCRYPT_POLICY_FLAG_DIRECT_KEY. It's just a global table for all filesystems (since the keys can be provided by the legacy process-subscribed keyrings mechanism, which makes it difficult to reuse super_block::s_master_keys). The entries in it ('struct fscrypt_direct_key') do contain a super_block pointer, though, for passing to fscrypt_destroy_inline_crypt_key() when the last inode that references the key is evicted. However, when finding the fscrypt_direct_key for an inode, we weren't actually comparing the super_block pointer. As a result, inodes with different super_blocks could point to the same fscrypt_direct_key. That could extend the lifetime of a fscrypt_direct_key beyond the super_block it points to, causing a use-after-free later. Fix this by creating distinct fscrypt_direct_key structs for distinct super_block structs. Note that this problem doesn't exist in the v2 policy equivalent ("per-mode keys"), since the data structures there are per super_block. Fixes: 22e9947a4b2b ("fscrypt: stop holding extra request_queue references") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260717044303.425265-1-ebiggers%40kernel.org Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260719033120.122120-1-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- fs/crypto/keysetup_v1.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index c4d05168522b0..7b2ee3c14d31c 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -190,13 +190,19 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert, if (memcmp(ci->ci_policy.v1.master_key_descriptor, dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0) continue; + /* The sb is used at eviction time, so it must be the same. */ + if (ci->ci_inode->i_sb != dk->dk_sb) + continue; if (ci->ci_mode != dk->dk_mode) continue; if (!fscrypt_is_key_prepared(&dk->dk_key, ci)) continue; if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize)) continue; - /* using existing tfm with same (descriptor, mode, raw_key) */ + /* + * Use an existing prepared key with the same (descriptor, sb, + * mode, inlinecrypt, raw_key) combination. + */ refcount_inc(&dk->dk_refcount); spin_unlock(&fscrypt_direct_keys_lock); free_direct_key(to_insert); From 65bf73bee1a4f3722208ae46afc0fa5de76b9a0a Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Sat, 25 Jul 2026 02:47:21 +0000 Subject: [PATCH 2754/3297] ftrace: Add global mutex to serialize trace_parser access commit 7720b63bcef3f54c7fe288774b720a227d54a306 upstream. In ftrace, the trace_parser structure is allocated and initialized when a trace file is opened, and is subsequently used across write and release handlers to parse user input. The affected handler paths and their specific functions are: - Open paths: ftrace_regex_open(), ftrace_graph_open() - Write paths: ftrace_regex_write(), ftrace_graph_write() - Release paths: ftrace_regex_release(), ftrace_graph_release() If userspace opens a trace file descriptor and shares it across multiple threads, concurrent write calls will race on the parser's internal state, specifically the 'idx', 'cont', and 'buffer' fields, leading to corrupted input or undefined behavior. Fix this by adding a global mutex, parser_lock, to serialize all access to trace_parser across write and release paths, preventing concurrent corruption of parser state. Fixes: e704eff3ff51 ("ftrace: Have set_graph_function handle multiple functions in one write") Fixes: 689fd8b65d66 ("tracing: trace parser support for function and graph") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260725024721.1983675-1-wutengda@huaweicloud.com Signed-off-by: Tengda Wu Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ftrace.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 905f4d1679559..d451926bc55df 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1072,6 +1072,12 @@ struct ftrace_ops global_ops = { FTRACE_OPS_FL_PID, }; +/* + * parser_lock - Protects trace_parser state against concurrent operations. + * Held across trace_get_user() and subsequent buffer parsing to prevent races. + */ +static DEFINE_MUTEX(parser_lock); + /* * Used by the stack unwinder to know about dynamic ftrace trampolines. */ @@ -5783,6 +5789,8 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, /* iter->hash is a local copy, so we don't need regex_lock */ parser = &iter->parser; + + guard(mutex)(&parser_lock); read = trace_get_user(parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) && @@ -6551,12 +6559,14 @@ int ftrace_regex_release(struct inode *inode, struct file *file) iter = file->private_data; parser = &iter->parser; + mutex_lock(&parser_lock); if (trace_parser_loaded(parser)) { int enable = !(iter->flags & FTRACE_ITER_NOTRACE); ftrace_process_regex(iter, parser->buffer, parser->idx, enable); } + mutex_unlock(&parser_lock); trace_parser_put(parser); @@ -6888,10 +6898,12 @@ ftrace_graph_release(struct inode *inode, struct file *file) parser = &fgd->parser; + mutex_lock(&parser_lock); if (trace_parser_loaded((parser))) { ret = ftrace_graph_set_hash(fgd->new_hash, parser->buffer); } + mutex_unlock(&parser_lock); trace_parser_put(parser); @@ -7004,6 +7016,7 @@ ftrace_graph_write(struct file *file, const char __user *ubuf, parser = &fgd->parser; + guard(mutex)(&parser_lock); read = trace_get_user(parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) && From f139498c5ebdd50a2148d4989125846330e2f257 Mon Sep 17 00:00:00 2001 From: Yitang Yang Date: Wed, 22 Jul 2026 20:45:51 +0800 Subject: [PATCH 2755/3297] io_uring/rw: fix missing ERESTARTSYS conversion in read paths commit ab05caca123c6d0b41850b7c05b246e4dca4a770 upstream. Both read and write may receive internal restart error codes from the filesystem layer and should be converted to -EINTR. However, when multishot read support was added, the error code normalization was lost for both io_read() and io_read_mshot(). Extract the conversion into io_fixup_restart_res() and apply it in all three locations: io_rw_done(), io_read(), and io_read_mshot(). Fixes: a08d195b586a ("io_uring/rw: split io_read() into a helper") Cc: stable@vger.kernel.org Signed-off-by: Yitang Yang Link: https://patch.msgid.link/20260722124551.130563-1-yi1tang.yang@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/rw.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/io_uring/rw.c b/io_uring/rw.c index 1a5dee8fe619f..3285a58409dfe 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -625,6 +625,24 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res) smp_store_release(&req->iopoll_completed, 1); } +static inline ssize_t io_fixup_restart_res(ssize_t ret) +{ + switch (ret) { + case -ERESTARTSYS: + case -ERESTARTNOINTR: + case -ERESTARTNOHAND: + case -ERESTART_RESTARTBLOCK: + /* + * We can't just restart the syscall, since previously + * submitted sqes may already be in progress. Just fail + * this IO with EINTR. + */ + return -EINTR; + default: + return ret; + } +} + static inline void io_rw_done(struct io_kiocb *req, ssize_t ret) { struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); @@ -634,21 +652,8 @@ static inline void io_rw_done(struct io_kiocb *req, ssize_t ret) return; /* transform internal restart error codes */ - if (unlikely(ret < 0)) { - switch (ret) { - case -ERESTARTSYS: - case -ERESTARTNOINTR: - case -ERESTARTNOHAND: - case -ERESTART_RESTARTBLOCK: - /* - * We can't just restart the syscall, since previously - * submitted sqes may already be in progress. Just fail - * this IO with EINTR. - */ - ret = -EINTR; - break; - } - } + if (unlikely(ret < 0)) + ret = io_fixup_restart_res(ret); if (req->ctx->flags & IORING_SETUP_IOPOLL) io_complete_rw_iopoll(&rw->kiocb, ret); @@ -1044,7 +1049,8 @@ int io_read(struct io_kiocb *req, unsigned int issue_flags) if (req->flags & REQ_F_BUFFERS_COMMIT) io_kbuf_recycle(req, sel.buf_list, issue_flags); - return ret; + + return io_fixup_restart_res(ret); } int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags) @@ -1078,8 +1084,10 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags) return IOU_RETRY; } else if (ret <= 0) { io_kbuf_recycle(req, sel.buf_list, issue_flags); - if (ret < 0) + if (ret < 0) { + ret = io_fixup_restart_res(ret); req_set_fail(req); + } } else if (!(req->flags & REQ_F_APOLL_MULTISHOT)) { cflags = io_put_kbuf(req, ret, sel.buf_list); } else { From 7037e7bdcd26f46c080b8ce307dee5cb471c4b7c Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 14 Jul 2026 16:23:24 +0800 Subject: [PATCH 2756/3297] iomap: fix out-of-bounds bitmap_set() with zero-length range commit 9c7d8f7c8994c790fca501dc45ce66e7356cbe05 upstream. ifs_set_range_dirty() and ifs_set_range_uptodate() compute last_blk as (off + len - 1) >> i_blkbits. When off is 0 and len is 0, the unsigned subtraction underflows to SIZE_MAX, producing a huge last_blk and nr_blks value that causes bitmap_set() to write far beyond the ifs->state allocation. Regarding ifs_set_range_uptodate(), it is temporarily safe because len cannot be passed in as 0. However, for ifs_set_range_dirty() this is reachable from __iomap_write_end(): when copy_folio_from_iter_atomic() returns 0 (e.g. user buffer fault) and the folio is already uptodate, the guard at the top of __iomap_write_end() does not trigger because !folio_test_uptodate() is false, and iomap_set_range_dirty() is called with copied == 0. Add a !len guard to both functions before the computation, so that a zero-length range is a no-op. Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance") Cc: stable@vger.kernel.org # v6.6 Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260714082325.325163-5-yi.zhang@huaweicloud.com Reviewed-by: Joanne Koong Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/iomap/buffered-io.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 734854f864fd1..c9ece378687b9 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -47,11 +47,13 @@ static bool ifs_set_range_uptodate(struct folio *folio, struct iomap_folio_state *ifs, size_t off, size_t len) { struct inode *inode = folio->mapping->host; - unsigned int first_blk = off >> inode->i_blkbits; - unsigned int last_blk = (off + len - 1) >> inode->i_blkbits; - unsigned int nr_blks = last_blk - first_blk + 1; + unsigned int first_blk, last_blk; - bitmap_set(ifs->state, first_blk, nr_blks); + if (len) { + first_blk = off >> inode->i_blkbits; + last_blk = (off + len - 1) >> inode->i_blkbits; + bitmap_set(ifs->state, first_blk, last_blk - first_blk + 1); + } return ifs_is_fully_uptodate(folio, ifs); } @@ -154,13 +156,17 @@ static void ifs_set_range_dirty(struct folio *folio, { struct inode *inode = folio->mapping->host; unsigned int blks_per_folio = i_blocks_per_folio(inode, folio); - unsigned int first_blk = (off >> inode->i_blkbits); - unsigned int last_blk = (off + len - 1) >> inode->i_blkbits; - unsigned int nr_blks = last_blk - first_blk + 1; + unsigned int first_blk, last_blk; unsigned long flags; + if (!len) + return; + + first_blk = off >> inode->i_blkbits; + last_blk = (off + len - 1) >> inode->i_blkbits; spin_lock_irqsave(&ifs->state_lock, flags); - bitmap_set(ifs->state, first_blk + blks_per_folio, nr_blks); + bitmap_set(ifs->state, first_blk + blks_per_folio, + last_blk - first_blk + 1); spin_unlock_irqrestore(&ifs->state_lock, flags); } From 1b44a5f584bffa7cde0d531920ecf981342a2906 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Thu, 16 Jul 2026 13:35:53 +0800 Subject: [PATCH 2757/3297] iommu/vt-d: Disallow SVA if page walk is not coherent commit 780dfed688622ea01be3c9c2c55eec2207f05e04 upstream. Hardware implementations report Scalable-Mode Page-walk Coherency Support via the SMPWCS field in the extended capability register. If the hardware does not support page-walk coherency, a clflush is required every time the page table entries (which are walked by the IOMMU hardware) are updated. In the SVA case, page tables are managed by the CPU mm core, not by the IOMMU driver. Because the IOMMU driver has no way of knowing whether the CPU page table management code has ensured coherency via clflush, the driver must deny SVA if the hardware does not support coherent paging. Fixes: ff3dc6521f78 ("iommu/vt-d: Fix CPU and IOMMU SVM feature matching checks") Cc: stable@vger.kernel.org Signed-off-by: Lu Baolu Reviewed-by: Kevin Tian Reviewed-by: Samiullah Khawaja Reviewed-by: Jason Gunthorpe Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/intel/svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index e147f71f91b72..6c275c7679459 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -27,7 +27,7 @@ void intel_svm_check(struct intel_iommu *iommu) { - if (!pasid_supported(iommu)) + if (!pasid_supported(iommu) || !ecap_smpwc(iommu->ecap)) return; if (cpu_feature_enabled(X86_FEATURE_GBPAGES) && From 03157872da5ed83c048f7ff686396adb277e257c Mon Sep 17 00:00:00 2001 From: Markus Breitenberger Date: Mon, 13 Jul 2026 19:16:19 +0200 Subject: [PATCH 2758/3297] net: stmmac: intel: skip SerDes reconfig when rate is unchanged commit 0ab78ead2481adb52f9eb5b403865c529f6f2348 upstream. intel_mac_finish() is registered as the phylink mac_finish() callback for the Elkhart Lake SGMII ports. phylink calls it at the end of every major link reconfiguration, including the initial one during probe. The callback selects the PMC ModPHY LCPLL programming for the requested MAC-side interface and then power-cycles the SerDes. On Elkhart Lake that ModPHY is also used by the on-die AHCI SATA PHY. Reapplying the programming during the initial boot-time link-up disturbs the shared analog block while it is still driving SATA, so the SATA link fails to train: ata1: SATA link down (SStatus 1 SControl 300) The disk carrying the root filesystem is never detected and the system hangs at rootwait. Ethernet itself comes up normally, which makes the failure look unrelated to the network driver. Before mac_finish() runs, the legacy SerDes power-up path has already programmed SERDES_GCR0 for the current interface. The 1G and 2.5G ModPHY tables selected by mac_finish() correspond to the SerDes lane rate, so read that rate back from SERDES_GCR0 and skip the PMC reprogramming and SerDes power-cycle when it already matches the selected interface. This keeps the disruptive reprogramming out of the boot path when the SerDes is configured correctly, while preserving the previous behavior when a real SGMII/1000BASE-X to 2500BASE-X rate change is needed. If the register read fails, reconfigure as before. Fixes: a42f6b3f1cc1 ("net: stmmac: configure SerDes according to the interface mode") Cc: stable@vger.kernel.org Signed-off-by: Markus Breitenberger Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260713171619.192452-1-bre@breiti.cc Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- .../net/ethernet/stmicro/stmmac/dwmac-intel.c | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 47fda982d6b15..a54bd924032c1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -524,6 +524,32 @@ static int intel_set_reg_access(const struct pmc_serdes_regs *regs, int max_regs return ret; } +/* + * Return true if the SerDes lane rate must change to serve @interface. + * If the current rate cannot be determined, reconfigure as before. + */ +static bool intel_serdes_needs_reconfig(struct stmmac_priv *priv, + struct intel_priv_data *intel_priv, + phy_interface_t interface) +{ + u32 cur_rate, want_rate; + int data; + + if (!intel_priv->mdio_adhoc_addr) + return true; + + data = mdiobus_read(priv->mii, intel_priv->mdio_adhoc_addr, + SERDES_GCR0); + if (data < 0) + return true; + + cur_rate = (data & SERDES_RATE_MASK) >> SERDES_RATE_PCIE_SHIFT; + want_rate = interface == PHY_INTERFACE_MODE_2500BASEX ? + SERDES_RATE_PCIE_GEN2 : SERDES_RATE_PCIE_GEN1; + + return cur_rate != want_rate; +} + static int intel_mac_finish(struct net_device *ndev, void *intel_data, unsigned int mode, @@ -535,6 +561,11 @@ static int intel_mac_finish(struct net_device *ndev, int max_regs = 0; int ret = 0; + if (!intel_serdes_needs_reconfig(priv, intel_priv, interface)) { + priv->plat->phy_interface = interface; + return 0; + } + ret = intel_tsn_lane_is_available(ndev, intel_priv); if (ret < 0) { netdev_info(priv->dev, "No TSN lane available to set the registers.\n"); From a48a889b60f73edb0399a8b08284a2ab0bd0295f Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 21 Jul 2026 01:58:45 -0700 Subject: [PATCH 2759/3297] phonet: pep: fix use-after-free in pep_get_sb() commit 0f71f852a96af9685858ce59fda34ecbf85c283d upstream. pep_get_sb() doesn't consider that pskb_may_pull() might have relocated the skb data, and continue to access the older pointer, causing UAF. Reproduced under KASAN: BUG: KASAN: slab-use-after-free in pep_get_sb+0x234/0x3b0 Read of size 1 at addr ff11000105510f50 by task repro/157 pep_get_sb+0x234/0x3b0 pipe_handler_do_rcv+0x5f7/0xa10 pep_do_rcv+0x203/0x410 __sk_receive_skb+0x471/0x4a0 phonet_rcv+0x5b3/0x6c0 __netif_receive_skb+0xcc/0x1d0 Refetch the header with skb_header_pointer() after pskb_may_pull(), so the possibly stale pointer is no longer dereferenced. There are better ways to solve this, but, this is the less instrusive one. Fixes: 9641458d3ec4 ("Phonet: Pipe End Point for Phonet Pipes protocol") Cc: stable@vger.kernel.org Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20260721-phonet_get_sb_uaf-v1-1-95fd7881cc4e@debian.org Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/phonet/pep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 7c695f3afc285..5910dcd26555e 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -55,6 +55,8 @@ static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, ph = skb_header_pointer(skb, 0, 2, &h); if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) return NULL; + /* pskb_may_pull() may have reallocated the head; refetch ph. */ + ph = skb_header_pointer(skb, 0, 2, &h); ph->sb_len -= 2; *ptype = ph->sb_type; *plen = ph->sb_len; From 730c7e5fea7f06e0cdf21c547222ec93234fd1d6 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 22:34:59 +0200 Subject: [PATCH 2760/3297] vxlan: require CAP_NET_ADMIN in the device netns for changelink commit 3a61bd9637f3d929aa846e4eb3d98b48c26fcb0e upstream. A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns vxlan->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in vxlan->net can rewrite a vxlan device whose underlay lives in vxlan->net. vxlan_changelink() validates and applies the new configuration against vxlan->net (vxlan_config_validate(vxlan->net, ...)) and can reopen the underlay socket in that netns, so the same reasoning as the tunnel changelink series applies here. Gate vxlan_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: 8bcdc4f3a20b ("vxlan: add changelink support") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Fernando Fernandez Mancera Link: https://patch.msgid.link/20260716203500.70573-2-doruk@0sec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/vxlan/vxlan_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index b706e1777354c..cb7b01e492fa1 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4415,6 +4415,9 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], struct vxlan_rdst *dst; int err; + if (!rtnl_dev_link_net_capable(dev, vxlan->net)) + return -EPERM; + dst = &vxlan->default_dst; err = vxlan_nl2conf(tb, data, dev, &conf, true, extack); if (err) From 5d07b178bef511d69558cfc89fe1129258dc39f8 Mon Sep 17 00:00:00 2001 From: Sungmin Kang <726ksm@gmail.com> Date: Sat, 18 Jul 2026 16:36:30 +0900 Subject: [PATCH 2761/3297] net: slip: serialize receive against buffer reallocation commit ee7f9bb9320add61f7b367d7e6cd55e3a3a4d65d upstream. sl_realloc_bufs() replaces rbuff and updates buffsize while holding sl->lock. slip_receive_buf() reads those fields and writes through rbuff without holding the lock. An MTU change can therefore race with receive processing. An MTU shrink can expose the new smaller rbuff with the old larger bound, causing an out-of-bounds write. A receive callback which already loaded the old rbuff can instead continue writing after that buffer has been freed. Serialize receive processing with sl_realloc_bufs() by holding sl->lock while consuming each receive batch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Sungmin Kang <726ksm@gmail.com> Link: https://patch.msgid.link/20260718073631.1674-1-726ksm@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/slip/slip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index c889fb3747030..bc84e2cc05022 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -693,6 +693,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) return; + spin_lock_bh(&sl->lock); + /* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { @@ -708,6 +710,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, #endif slip_unesc(sl, *cp++); } + + spin_unlock_bh(&sl->lock); } /************************************ From f8c498585d2a08aa623748353c3e61467b7e9fd2 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 22:35:00 +0200 Subject: [PATCH 2762/3297] geneve: require CAP_NET_ADMIN in the device netns for changelink commit 8efb8f8bbb353b8f2fdf4f37534c6d96c9f69e01 upstream. A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns geneve->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in geneve->net can rewrite a geneve device whose underlay lives in geneve->net. geneve_changelink() applies the new configuration against geneve->net: geneve_link_config() and the geneve_quiesce()/geneve_unquiesce() pair reopen the underlay sockets in that netns (geneve_sock_add() uses geneve->net), so the same reasoning as the tunnel changelink series applies here. Gate geneve_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: 5b861f6baa3a ("geneve: add rtnl changelink support") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Fernando Fernandez Mancera Link: https://patch.msgid.link/20260716203500.70573-3-doruk@0sec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/geneve.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 77b0c3d520414..73e020270b989 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1736,6 +1736,9 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], struct geneve_config cfg; int err; + if (!rtnl_dev_link_net_capable(dev, geneve->net)) + return -EPERM; + /* If the geneve device is configured for metadata (or externally * controlled, for example, OVS), then nothing can be changed. */ From 33736ff5e7c97d3348ce812e8bd2e125d840743c Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Thu, 9 Jul 2026 21:17:32 +0200 Subject: [PATCH 2763/3297] net/af_iucv: fix NULL deref in afiucv_hs_callback_syn() commit 47a5116e56a6b6fe1e909f244e39cd0fc26ceee4 upstream. afiucv_hs_callback_syn() allocates the child socket with GFP_ATOMIC. If the allocation fails, nsk is NULL. The connection-refused path is entered when the listen state check fails, the accept backlog is full, or nsk is NULL. The code unconditionally calls iucv_sock_kill(nsk) in that path. iucv_sock_kill() does not accept a NULL socket pointer and immediately dereferences sk via sock_flag(sk, SOCK_ZAPPED). When nsk is NULL, calling iucv_sock_kill(nsk) results in a NULL pointer dereference. Only call iucv_sock_kill() when a child socket was successfully allocated. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Cc: stable@vger.kernel.org Reviewed-by: Alexandra Winter Signed-off-by: Hidayath Khan Link: https://patch.msgid.link/20260709191732.124092-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/iucv/af_iucv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index bd0405373c5e9..08cd759ecf057 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1872,7 +1872,8 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb) afiucv_swap_src_dest(skb); trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN; err = dev_queue_xmit(skb); - iucv_sock_kill(nsk); + if (nsk) + iucv_sock_kill(nsk); bh_unlock_sock(sk); goto out; } From 900cd6d8119b7f3ae5c4bf82f922ff5957df43db Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Tue, 7 Jul 2026 02:00:54 -0500 Subject: [PATCH 2764/3297] net/iucv: fix use-after-free of a severed iucv_path commit be7cc4656eb1f54029610e82d1f0fdd3f9b5ec0a upstream. af_iucv queues not-yet-received message notifications on iucv->message_q, each holding a raw pointer to the connection's iucv_path. When the peer severs the connection, iucv_sever_path() frees that path with iucv_path_free() but leaves the notifications queued. A later recvmsg() drains message_q via iucv_process_message_q() and hands the stale path to message_receive() -- a use-after-free of the freed iucv_path. Drop the queued notifications when the path is severed; once the path is gone they can no longer be received. This also frees the notifications leaked when a socket is closed with messages still queued. Fixes: f0703c80e515 ("[AF_IUCV]: postpone receival of iucv-packets") Closes: https://sashiko.dev/#/patchset/20260705-b4-disp-fc79c0dc-v1-1-d2cdcb57afa9@proton.me?part=1 Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260707-b4-disp-783fedbb-v1-1-463b9dbda2ea@proton.me Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/iucv/af_iucv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 08cd759ecf057..7f5a5aa0cfdc0 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -335,6 +335,7 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) unsigned char user_data[16]; struct iucv_sock *iucv = iucv_sk(sk); struct iucv_path *path = iucv->path; + struct sock_msg_q *p, *n; /* Whoever resets the path pointer, must sever and free it. */ if (xchg(&iucv->path, NULL)) { @@ -346,6 +347,19 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) } else pr_iucv->path_sever(path, NULL); iucv_path_free(path); + + /* + * Message notifications queued on message_q still reference + * the now freed path; drop them, otherwise a later recvmsg() + * would pass the freed iucv_path to message_receive() via + * iucv_process_message_q(). + */ + spin_lock_bh(&iucv->message_q.lock); + list_for_each_entry_safe(p, n, &iucv->message_q.list, list) { + list_del(&p->list); + kfree(p); + } + spin_unlock_bh(&iucv->message_q.lock); } } From 40f9a124ebbe0d60fe165fb3f87515c35b2d72f5 Mon Sep 17 00:00:00 2001 From: Manjunath Patil Date: Tue, 7 Jul 2026 16:39:11 -0700 Subject: [PATCH 2765/3297] net/mlx5e: Use sender devcom for MPV master-up commit e32649b4bad90a6216d8e93cd7dd050af8ac9740 upstream. After PCIe DPC recovery, mlx5 reloads the affected functions and replays multiport affiliation events. In the reported failure, the first relevant device error was: pcieport 0000:10:01.1: DPC: containment event pcieport 0000:10:01.1: PCIe Bus Error: severity=Uncorrected (Fatal) pcieport 0000:10:01.1: [ 5] SDES (First) mlx5 recovered the PCI functions and resumed 0000:11:00.1. During that resume, RDMA multiport binding replayed MLX5_DRIVER_EVENT_AFFILIATION_DONE and mlx5e sent MPV_DEVCOM_MASTER_UP. The host then panicked with: BUG: kernel NULL pointer dereference, address: 0000000000000010 RIP: mlx5_devcom_comp_set_ready+0x5/0x40 [mlx5_core] RDI: 0000000000000000 Call trace included: mlx5_devcom_comp_set_ready mlx5e_devcom_event_mpv mlx5_devcom_send_event mlx5_ib_bind_slave_port mlx5r_mp_probe mlx5_pci_resume MPV devcom registration publishes mlx5e private data to the component peer list before mlx5e_devcom_init_mpv() stores the returned component device in priv->devcom. A concurrent master-up event can therefore reach a peer whose private data is visible but whose priv->devcom backpointer is still NULL. MPV_DEVCOM_MASTER_UP already carries the sender/master mlx5e private data as event_data. The ready bit is stored on the shared devcom component, not on an individual peer. Use the sender devcom when marking the MPV component ready. This preserves the readiness transition while avoiding a NULL dereference of the peer devcom pointer during affiliation replay after PCI error recovery. Fixes: bf11485f8419 ("net/mlx5: Register mlx5e priv to devcom in MPV mode") Assisted-by: Codex:gpt-5 Signed-off-by: Manjunath Patil Cc: stable@vger.kernel.org # 6.7+ Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260707233911.3651139-1-manjunath.b.patil@oracle.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index f660ef37b0a63..7e754b72f889f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -211,11 +211,11 @@ static void mlx5e_disable_async_events(struct mlx5e_priv *priv) static int mlx5e_devcom_event_mpv(int event, void *my_data, void *event_data) { - struct mlx5e_priv *slave_priv = my_data; + struct mlx5e_priv *master_priv = event_data; switch (event) { case MPV_DEVCOM_MASTER_UP: - mlx5_devcom_comp_set_ready(slave_priv->devcom, true); + mlx5_devcom_comp_set_ready(master_priv->devcom, true); break; case MPV_DEVCOM_MASTER_DOWN: /* no need for comp set ready false since we unregister after From ec6d91a1bf2ebd767d3d43f6d249ee0ed3f4558a Mon Sep 17 00:00:00 2001 From: David Lee Date: Mon, 13 Jul 2026 10:47:50 +0000 Subject: [PATCH 2766/3297] net/x25: fix use-after-free in x25_kill_by_neigh() commit 5499e0602d2faafd42c580d25f615903c3fbe11b upstream. x25_kill_by_neigh() walks the global X.25 socket list looking for sockets attached to a terminating neighbour. x25_list_lock protects list membership while the lookup is in progress, but it does not pin a socket's lifetime after the lock is dropped. The function currently drops x25_list_lock before calling lock_sock(s). A concurrent close can run x25_release(), remove the same socket from x25_list, and drop the last socket reference in that window. The neighbour teardown path can then lock or inspect a freed struct sock/struct x25_sock. Take sock_hold(s) while x25_list_lock still proves that the list entry is live, then drop the temporary reference after the socket has been locked, rechecked, and released. Recheck x25_sk(s)->neighbour after lock_sock(), because another path may have disconnected the socket before this path acquired the socket lock. Restart the list walk after each disconnect because the list lock was dropped and the previous iterator state may no longer be valid. A QEMU/KASAN run against origin/master reproduced a slab-use-after-free in x25_kill_by_neigh(). Fixes: 7781607938c8 ("net/x25: Fix null-ptr-deref caused by x25_disconnect") Cc: stable@vger.kernel.org Signed-off-by: David Lee Assisted-by: Codex:gpt-5.5 Acked-by: Martin Schiller Link: https://patch.msgid.link/20260713104752.241175-1-david.lee@trailofbits.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/x25/af_x25.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 655d1e0ae25f7..a1b093308ccb2 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1772,15 +1772,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb) { struct sock *s; +again: write_lock_bh(&x25_list_lock); sk_for_each(s, &x25_list) { if (x25_sk(s)->neighbour == nb) { + sock_hold(s); write_unlock_bh(&x25_list_lock); lock_sock(s); - x25_disconnect(s, ENETUNREACH, 0, 0); + if (x25_sk(s)->neighbour == nb) + x25_disconnect(s, ENETUNREACH, 0, 0); release_sock(s); - write_lock_bh(&x25_list_lock); + sock_put(s); + goto again; } } write_unlock_bh(&x25_list_lock); From a4dfd46cc8f08a29c6183794790547d0945f3d45 Mon Sep 17 00:00:00 2001 From: Shiming Cheng Date: Thu, 9 Jul 2026 09:46:39 +0800 Subject: [PATCH 2767/3297] net: gro: fix double aggregation of flush-marked skbs commit e751256486d0ded20f5a9f9863467f1dce65142f upstream. Commit 0ab03f353d36 ("net-gro: Fix GRO flush when receiving a GSO packet.") added a flush check to skb_gro_receive(), but skb_gro_receive_list() lacks the same validation. As a result, packets marked with NAPI_GRO_CB(skb)->flush may still be re-aggregated. This allows already-GRO'd packets with existing frag_list to be re-aggregated into a new GRO session, corrupting the frag_list chain structure. When skb_segment() attempts to unpack these malformed packets, it encounters invalid state and triggers a kernel panic. Scenario (Tethering/Device forwarding): 1. Driver: Generated aggregated packet P1 via LRO with frag_list 2. Dev A: Receives aggregated fraglist packet and flush flag set 3. Dev A: Re-enters GRO, skb_gro_receive_list() is called 4. Missing flush check allows re-aggregation despite flush flag 5. Frag_list chain becomes corrupted (loops or dangling refs) 6. Dev B: TX path calls skb_segment(), crashes on corrupted frag_list Root cause in skb_segment(): The check at line ~4891: if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) && (skb_headlen(list_skb) == len || sg)) { When frag_list is corrupted by double aggregation, when list_skb is a NULL pointer from skb->next, skb_headlen(list_skb) dereference NULL/corrupted pointers occurs. Call Trace: skb_headlen(NULL skb) skb_segment tcp_gso_segment tcp4_gso_segment inet_gso_segment skb_mac_gso_segment __skb_gso_segment skb_gso_segment validate_xmit_skb validate_xmit_skb_list sch_direct_xmit qdisc_restart __qdisc_run qdisc_run net_tx_action Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return check in skb_gro_receive_list(), matching the defensive programming pattern of skb_gro_receive(). Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.") Cc: stable@vger.kernel.org Signed-off-by: Shiming Cheng Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260709014704.3625-1-shiming.cheng@mediatek.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/gro.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/gro.c b/net/core/gro.c index 9ec8a46b30bb9..70770f9f9c45b 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -231,7 +231,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) { - if (unlikely(p->len + skb->len >= 65536)) + /* make sure to check flush flag and to not merge */ + if (unlikely(p->len + skb->len >= 65536 || + NAPI_GRO_CB(skb)->flush)) return -E2BIG; if (!pskb_may_pull(skb, skb_gro_offset(skb))) { From 80d977f280b4eccd4ac5369871d0ecb2b9c9a49d Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Sun, 12 Jul 2026 14:27:29 +0000 Subject: [PATCH 2768/3297] net: hip04: fix RX buffer leak on build_skb failure commit 14fa65d10f5696b063a7d8d26e8291ea84a2c6ed upstream. When build_skb() fails in hip04_rx_poll(), the driver jumps to the refill path without releasing the current RX buffer and its DMA mapping. Installing a replacement buffer then overwrites the slot references and leaks both resources. Keep the current slot intact and return budget so NAPI retries the same buffer. Also free a newly allocated RX fragment when dma_map_single() fails. This issue was found by an in-house static analysis tool. Fixes: 701a0fd52318 ("hip04_eth: fix missing error handle for build_skb failed") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260712142729.2057636-1-fanwu01@zju.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/hisilicon/hip04_eth.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index 18376bcc718a2..fc2c47dcfaabe 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -594,7 +594,11 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) skb = build_skb(buf, priv->rx_buf_size); if (unlikely(!skb)) { net_dbg_ratelimited("build_skb failed\n"); - goto refill; + /* Retain the slot; return budget so NAPI retries this + * buffer. Refill would overwrite rx_buf[]/rx_phys[] + * and leak them. + */ + return budget; } dma_unmap_single(priv->dev, priv->rx_phys[priv->rx_head], @@ -622,14 +626,15 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) rx++; } -refill: buf = netdev_alloc_frag(priv->rx_buf_size); if (!buf) goto done; phys = dma_map_single(priv->dev, buf, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(priv->dev, phys)) + if (dma_mapping_error(priv->dev, phys)) { + skb_free_frag(buf); goto done; + } priv->rx_buf[priv->rx_head] = buf; priv->rx_phys[priv->rx_head] = phys; hip04_set_recv_desc(priv, phys); From 4056cc19071a3268c2b1ad89fef990f4ca0aab69 Mon Sep 17 00:00:00 2001 From: Coia Prant Date: Fri, 17 Jul 2026 15:43:25 +0800 Subject: [PATCH 2769/3297] net: pcs: xpcs: fix SGMII state reading commit def9a4745e105145133e442dd8a1c126caf0f553 upstream. Commit 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode") added a path in xpcs_get_state_c37_sgmii() that reads speed/duplex from BMCR after AN completes. However, BMCR does not reflect the negotiated result on the hardware where this has been tested: - On RK3568 (MAC side SGMII), BMCR returns a fixed hardware reset value - Wangxun engineer Jiawen Wu confirmed that on their side, "BMCR looks like it only wants to be return as 0" [0] The correct information is available in CL37_ANSGM_STS, which contains the actual link status and negotiated speed/duplex. This bug was previously masked by phylink core, which overrides the PCS link state with the PHY state when a PHY is present: /* If we have a phy, the "up" state is the union of both the * PHY and the MAC */ if (phy) link_state.link &= pl->phy_state.link; Thus, when the link is down, the PHY's link_down state is applied on top of whatever the PCS reports, hiding the broken PCS state reading path. Modify xpcs_get_state_c37_sgmii() to: 1. Read link state from CL37_ANSGM_STS 2. If link is up, report speed/duplex from CL37_ANSGM_STS 3. Remove the broken BMCR reading path entirely Also properly set state->an_complete to reflect the AN completion status, and clear CL37_ANCMPLT_INTR when link is down to avoid stale state. [0] https://lore.kernel.org/all/000c01dd1593$2ac0b0f0$804212d0$@trustnetic.com/ Fixes: 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode") Cc: stable@vger.kernel.org Tested-by: Jiawen Wu Signed-off-by: Coia Prant Tested-by: Maxime Chevallier Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260717074324.3250043-2-coiaprant@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/pcs/pcs-xpcs.c | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 3d1bd5aac0937..14c4b10fee74c 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -958,6 +958,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, /* Reset link_state */ state->link = false; + state->an_complete = false; state->speed = SPEED_UNKNOWN; state->duplex = DUPLEX_UNKNOWN; state->pause = 0; @@ -969,6 +970,8 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, if (ret < 0) return ret; + state->an_complete = ret & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR; + if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { int speed_value; @@ -986,34 +989,13 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, state->duplex = DUPLEX_FULL; else state->duplex = DUPLEX_HALF; - } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { - int speed, duplex; - - state->link = true; - - speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMCR); - if (speed < 0) - return speed; - - speed &= BMCR_SPEED100 | BMCR_SPEED1000; - if (speed == BMCR_SPEED1000) - state->speed = SPEED_1000; - else if (speed == BMCR_SPEED100) - state->speed = SPEED_100; - else if (speed == 0) - state->speed = SPEED_10; - - duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE); - if (duplex < 0) - return duplex; - if (duplex & ADVERTISE_1000XFULL) - state->duplex = DUPLEX_FULL; - else if (duplex & ADVERTISE_1000XHALF) - state->duplex = DUPLEX_HALF; + return 0; + } + /* Clear AN complete status or interrupt */ + if (state->an_complete) xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); - } return 0; } From 4afc58ea75b966c3b8a94225b9f9b335958d0482 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 6 Jul 2026 20:22:42 +0200 Subject: [PATCH 2770/3297] proc: Fix broken error paths for namespace links commit 425224c2d700391729be7fe6929a88ef4e2d7a4e upstream. Don't return the return value of down_read_killable() (0) when a ptrace access check fails, return -EACCES as intended. Reported-by: Magnus Lindholm Closes: https://lore.kernel.org/r/20260706170735.2941493-1-linmag7@gmail.com Fixes: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260706-procfs-ns-eacces-fix-v1-1-a69ab14c02e6@google.com Tested-by: Magnus Lindholm Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/proc/namespaces.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 2f46f13967445..ea6ec61a0430b 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -46,7 +46,7 @@ static const char *proc_ns_get_link(struct dentry *dentry, const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; struct task_struct *task; struct path ns_path; - int error = -EACCES; + int error; if (!dentry) return ERR_PTR(-ECHILD); @@ -59,6 +59,7 @@ static const char *proc_ns_get_link(struct dentry *dentry, if (error) goto out_put_task; + error = -EACCES; if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) goto out; @@ -90,6 +91,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl if (res) goto out_put_task; + res = -EACCES; if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { res = ns_get_name(name, sizeof(name), task, ns_ops); if (res >= 0) From 9a8a247f0f17b68b90615c694f151f9724940f76 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 14 Jul 2026 15:03:41 +0200 Subject: [PATCH 2771/3297] s390/ptff: Export ptff_function_mask[] commit 9de445d8296a7f2b011ebb5834fdc94dcda5c778 upstream. Export the ptff_function_mask to make ptff_query() usable in modules. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Link: https://patch.msgid.link/20260714130342.1971700-2-svens@linux.ibm.com Signed-off-by: Jakub Kicinski Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/time.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 63517b85f4c93..246fc5b936fa6 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -66,6 +66,7 @@ ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); EXPORT_SYMBOL(s390_epoch_delta_notifier); unsigned char ptff_function_mask[16]; +EXPORT_SYMBOL(ptff_function_mask); static unsigned long lpar_offset; static unsigned long initial_leap_seconds; From b3efb4744abf493c9782eae713b861a80d9bbeca Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 14 Jul 2026 15:03:42 +0200 Subject: [PATCH 2772/3297] ptp: ptp_s390: Add missing facility check commit e78f1ac37afcb16cb6fef8a2c92591eab6558956 upstream. Only register the physical clock when facility 28 is installed and PTFF QAF returns that PTFF QPT is available. Fixes: 2d7de7a3010d ("s390/time: Add PtP driver") Signed-off-by: Sven Schnelle Cc: stable@kernel.org Reviewed-by: Heiko Carstens Link: https://patch.msgid.link/20260714130342.1971700-3-svens@linux.ibm.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/ptp/ptp_s390.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_s390.c b/drivers/ptp/ptp_s390.c index 29618eb9bf442..02d624d89a0ab 100644 --- a/drivers/ptp/ptp_s390.c +++ b/drivers/ptp/ptp_s390.c @@ -107,6 +107,9 @@ static __init int ptp_s390_init(void) if (IS_ERR(ptp_stcke_clock)) return PTR_ERR(ptp_stcke_clock); + if (!test_facility(28) || !ptff_query(PTFF_QPT)) + return 0; + ptp_qpt_clock = ptp_clock_register(&ptp_s390_qpt_info, NULL); if (IS_ERR(ptp_qpt_clock)) { ptp_clock_unregister(ptp_stcke_clock); @@ -117,7 +120,8 @@ static __init int ptp_s390_init(void) static __exit void ptp_s390_exit(void) { - ptp_clock_unregister(ptp_qpt_clock); + if (ptp_qpt_clock) + ptp_clock_unregister(ptp_qpt_clock); ptp_clock_unregister(ptp_stcke_clock); } From e4406cbdd915f702d2ed9ee8b30683a16b06c6ac Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Fri, 17 Jul 2026 11:53:31 -0700 Subject: [PATCH 2773/3297] ice: fix PTP Call Trace during PTP release commit f6a7e00b81e35ef1325234925f2fe1e53b466f92 upstream. If a PF reset occurs when the PTP state is ICE_PTP_UNINIT, then ice_ptp_rebuild() will update the state to ICE_PTP_ERROR. This will result in the following PTP release call trace during driver unload: kernel BUG at lib/list_debug.c:52! ice_ptp_release+0x332/0x3c0 [ice] ice_deinit_features.part.0+0x10e/0x120 [ice] ice_remove+0x100/0x220 [ice] This was observed when passing PF1 through to a VM. ice_ptp_init() fails because ctrl_pf is NULL and sets the state to ICE_PTP_UNINIT. Fix by detecting the ICE_PTP_UNINIT state in ice_ptp_rebuild() and returning without error, preventing the invalid state transition to ICE_PTP_ERROR. The only valid path to ICE_PTP_ERROR is from ICE_PTP_RESETTING after a failed rebuild. Fixes: 8293e4cb2ff5 ("ice: introduce PTP state machine") Cc: stable@vger.kernel.org Signed-off-by: Paul Greenwalt 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/20260717185340.3595286-10-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_ptp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 86eb3d0315a27..9607c1555b39e 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -2994,6 +2994,11 @@ void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) struct ice_ptp *ptp = &pf->ptp; int err; + if (ptp->state == ICE_PTP_UNINIT) { + dev_dbg(ice_pf_to_dev(pf), "PTP was not initialized, skipping rebuild\n"); + return; + } + if (ptp->state == ICE_PTP_READY) { ice_ptp_prepare_for_reset(pf, reset_type); } else if (ptp->state != ICE_PTP_RESETTING) { From 63d78b546eefc38ad9898dc839bfc94811ede547 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 23 Jul 2026 11:37:05 +0200 Subject: [PATCH 2774/3297] super: fix emergency thaw deadlock on frozen block devices commit 749d7aa0377aae32af8c0a4ad43371e7bf830ab5 upstream. do_thaw_all_callback() calls bdev_thaw() while holding sb->s_umount exclusively. If the block device was frozen via bdev_freeze() dropping the last block layer freeze reference calls fs_bdev_thaw() which reacquires s_umount: do_thaw_all_callback(sb) super_lock_excl(sb) # holds sb->s_umount bdev_thaw(sb->s_bdev) mutex_lock(&bdev->bd_fsfreeze_mutex) # bd_fsfreeze_count drops 1 -> 0 bd_holder_ops->thaw == fs_bdev_thaw get_bdev_super(bdev) bdev_super_lock(bdev, true) super_lock(sb, true) down_write(&sb->s_umount) # same task: deadlock The emergency thaw worker deadlocks against itself holding both s_umount and bd_fsfreeze_mutex. That fscks any subsequent unmount, freeze, or thaw of that filesystem and block device. [ 81.878470] sysrq: Show Blocked State [ 81.880140] task:kworker/0:1 state:D stack:0 pid:11 tgid:11 ppid:2 task_flags:0x4208060 flags:0x00080000 [ 81.884876] Workqueue: events do_thaw_all [ 81.886656] Call Trace: [ 81.887759] [ 81.888763] __schedule+0x579/0x1420 [ 81.890372] schedule+0x3a/0x100 [ 81.891794] schedule_preempt_disabled+0x15/0x30 [ 81.893848] rwsem_down_write_slowpath+0x1ea/0x900 [ 81.895191] ? __pfx_do_thaw_all_callback+0x10/0x10 [ 81.896528] down_write+0xbd/0xc0 [ 81.897505] super_lock+0x91/0x180 [ 81.898457] ? __mutex_lock+0xa99/0x1140 [ 81.900748] ? __mutex_unlock_slowpath+0x1f/0x400 [ 81.902069] bdev_super_lock+0x5b/0x150 [ 81.903132] get_bdev_super+0x10/0x60 [ 81.904042] fs_bdev_thaw+0x23/0xf0 [ 81.904755] bdev_thaw+0x82/0x100 [ 81.905484] do_thaw_all_callback+0x2c/0x50 [ 81.906298] __iterate_supers+0x5d/0x130 [ 81.907067] do_thaw_all+0x20/0x40 [ 81.907739] process_one_work+0x206/0x5e0 [ 81.908545] worker_thread+0x1e2/0x3c0 [ 81.909339] ? __pfx_worker_thread+0x10/0x10 [ 81.910171] kthread+0xf4/0x130 [ 81.910799] ? __pfx_kthread+0x10/0x10 [ 81.911528] ret_from_fork+0x2e2/0x3b0 [ 81.912259] ? __pfx_kthread+0x10/0x10 [ 81.913010] ret_from_fork_asm+0x1a/0x30 [ 81.913806] bdev_super_lock() even documents the violated requirement with lockdep_assert_not_held(&sb->s_umount). Acquiring bd_fsfreeze_mutex under s_umount also inverts the bd_fsfreeze_mutex vs. s_umount ordering established by bdev_{freeze,thaw}() and can thus ABBA against a concurrent block-layer freeze even when the recursive path isn't hit. Fix this by not holding s_umount around the bdev_thaw() loop at all. Pin the superblock with an active reference instead as filesystems_freeze_callback() does. The active reference keeps the superblock from being shut down and so ->s_bdev stays valid without holding s_umount. The block-layer-held freeze is dropped by fs_bdev_thaw() with FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE exactly as a regular unfreeze would and thaw_super_locked() handles filesystem-level freezes as before. The emergency thaw path has deadlocked like this in one form or another for a long long time but the current exclusively-held shape dates back to commit [1] where thaw_bdev() already ended in thaw_super() with s_umount held by do_thaw_all_callback(). Fixes: 08fdc8a0138a ("buffer.c: call thaw_super during emergency thaw") [1] Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260723-work-super-emergency_thaw-v1-1-7c315c600245@kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/super.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/super.c b/fs/super.c index 1d3f4fe0a1050..8af2ee9395bcc 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1139,16 +1139,30 @@ void emergency_remount(void) } } +static inline bool get_active_super(struct super_block *sb) +{ + bool active = false; + + if (super_lock_excl(sb)) { + active = atomic_inc_not_zero(&sb->s_active); + super_unlock_excl(sb); + } + return active; +} + static void do_thaw_all_callback(struct super_block *sb, void *unused) { - if (!super_lock_excl(sb)) + if (!get_active_super(sb)) return; + /* fs_bdev_thaw() acquires s_umount so it must not be held here */ if (IS_ENABLED(CONFIG_BLOCK)) while (sb->s_bdev && !bdev_thaw(sb->s_bdev)) pr_warn("Emergency Thaw on %pg\n", sb->s_bdev); - thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL); + if (super_lock_excl(sb)) + thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL); + deactivate_super(sb); } static void do_thaw_all(struct work_struct *work) @@ -1174,17 +1188,6 @@ void emergency_thaw_all(void) } } -static inline bool get_active_super(struct super_block *sb) -{ - bool active = false; - - if (super_lock_excl(sb)) { - active = atomic_inc_not_zero(&sb->s_active); - super_unlock_excl(sb); - } - return active; -} - static const char *filesystems_freeze_ptr = "filesystems_freeze"; static void filesystems_freeze_callback(struct super_block *sb, void *freeze_all_ptr) From 34f2a2f32af570dfcc532ad70c080629ee1c32b0 Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Thu, 9 Jul 2026 13:26:20 +0200 Subject: [PATCH 2775/3297] rbd: Reset positive result codes to zero in object map update path commit a6c4250b81bd30beae94e1b7a4b26fa1193ad2e4 upstream. In a reply message to an RBD request, a positive result code indicates a data payload, which is not allowed for writes. While rbd_osd_req_callback() already resets a positive result code for writes to zero, rbd_object_map_callback() does not. This allows a corrupted reply to an object map update to trigger the rbd_assert(*result < 0) in __rbd_obj_handle_request(). This happens, because rbd_object_map_callback() calls rbd_obj_handle_request() -> __rbd_obj_handle_request() and passes this positive result code. From __rbd_obj_handle_request(), rbd_obj_advance_write() is called, which leaves the positive result code unchanged and returns true. Therefore, the if(done && *result) branch is executed in __rbd_obj_handle_request() and the assertion triggers. This patch fixes the issue by adjusting the logic in the rbd_object_map_callback() path. A positive result code for an object map update is now reset to zero (similar to rbd_osd_req_callback()), and the message is subsequently handled the same way as if the result code was zero from the beginning. Additionally, a WARN_ON_ONCE() is added for this case. Cc: stable@vger.kernel.org Fixes: 22e8bd51bb04 ("rbd: support for object-map and fast-diff") Signed-off-by: Raphael Zimmer Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- drivers/block/rbd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 97cd8cf7bad30..6c802f029f57d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1957,9 +1957,14 @@ static int rbd_object_map_update_finish(struct rbd_obj_request *obj_req, bool has_current_state; void *p; - if (osd_req->r_result) + if (osd_req->r_result < 0) return osd_req->r_result; + /* + * Writes aren't allowed to return a data payload. + */ + WARN_ON_ONCE(osd_req->r_result > 0); + /* * Nothing to do for a snapshot object map. */ From 6098b55f6a0cfb7e574d8f3eea1a72ba49aca0b5 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Tue, 21 Jul 2026 13:27:55 -0400 Subject: [PATCH 2776/3297] smb: client: handle STATUS_STOPPED_ON_SYMLINK responses without a symlink target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2eb74eef4b7eda8df593d22fb48e94ef959ec8a5 upstream. The macOS built-in SMB server returns STATUS_STOPPED_ON_SYMLINK for a CREATE on a path whose final component is a symlink, but it does not include a Symbolic Link Error Response in the error data: both ErrorContextCount and ByteCount are zero, so the symlink target is not present in the response at all. Per [MS-SMB2] section 2.2.2 such a response should carry a valid Symbolic Link Error Response, so this is a server bug, but the target can still be retrieved with FSCTL_GET_REPARSE_POINT. Frame from a capture against macOS 26.5.2 (build 25F84): SMB2 hdr : Status=0x8000002d STATUS_STOPPED_ON_SYMLINK, Cmd=Create Error Rsp: StructureSize=0x0009 Error Context Count: 0 Byte Count: 0 Error Data: 00 symlink_data() cannot find a struct smb2_symlink_err_rsp in such a response and returns -EINVAL, which parse_create_response() propagates, so smb2_query_path_info() bails out at if (rc || !data->reparse_point) goto out; before it can retry with SMB2_OP_GET_REPARSE. stat(), readlink() and ls of any server-side symlink then fail with -EINVAL: $ ls -la Config l????????? ? ? ? ? ? Config.json $ stat Config/Config.json stat: cannot statx 'Config/Config.json': Invalid argument A 5.10 client resolves these symlinks correctly against the same server and share, so this is a regression for Apple SMB servers. Handle it in several places: - symlink_data() detects the empty response (ErrorContextCount and ByteCount both zero) and returns a distinct -ENODATA, so that "server did not send the target" can be told apart from a genuinely malformed response and only this case is worked around. - parse_create_response() treats -ENODATA like STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not carry the target either: leave the reparse tag unset and clear rc, so the existing SMB2_OP_GET_REPARSE path retrieves the target. - smb2_query_path_info() only fixes up the symlink target type when the target is already known. SMB2_OP_GET_REPARSE sets data->reparse.tag but does not parse the target out of the reparse buffer; that happens later, in reparse_info_to_fattr(). Without this check smb2_fix_symlink_target_type() is called with a NULL target and returns -EIO. This could not happen with servers that send the target inline and therefore skip SMB2_OP_GET_REPARSE. - smb2_open_file() maps -ENODATA to -EIO, matching STATUS_IO_REPARSE_TAG_NOT_HANDLED, so its callers retrieve the target with SMB2_OP_GET_REPARSE as well. Tested on Debian 13, kernel 6.18.38 (armv7), against macOS 26.5.2: symlinks now resolve, including relative, parent-traversing and directory symlinks, and reads through symlinks succeed. Cc: stable@vger.kernel.org Co-developed-by: Pali Rohár Signed-off-by: Pali Rohár Signed-off-by: Carl Johnson Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2file.c | 21 +++++++++++++++++++++ fs/smb/client/smb2inode.c | 23 ++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index cc1ff2461b003..9bab3c57b3a4f 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -30,6 +30,19 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) u8 *end = (u8 *)err + iov->iov_len; u32 len; + /* + * Per [MS-SMB2] section 2.2.2, a STATUS_STOPPED_ON_SYMLINK response has to + * carry a Symbolic Link Error Response, so ByteCount cannot be zero. Some + * servers (e.g. the macOS built-in SMB server) violate this and return an + * empty error response, with both ErrorContextCount and ByteCount set to + * zero, i.e. without the symlink target. Detect this and return -ENODATA + * so that callers can tell "server did not send the target" apart from a + * malformed response, and retrieve the target with FSCTL_GET_REPARSE_POINT + * instead. + */ + if (!err->ErrorContextCount && !le32_to_cpu(err->ByteCount)) + return ERR_PTR(-ENODATA); + if (err->ErrorContextCount) { struct smb2_error_context_rsp *p; @@ -200,6 +213,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, oparms->path, &data->symlink_target); + /* + * If smb2_parse_symlink_response returned -ENODATA then the + * symlink_target was not sent. Treat this as if the SMB2_open() + * failed with STATUS_IO_REPARSE_TAG_NOT_HANDLED status, which is + * indicated by the -EIO errno. + */ + if (rc == -ENODATA) + rc = -EIO; if (!rc) { memset(smb2_data, 0, sizeof(*smb2_data)); oparms->create_options |= OPEN_REPARSE_POINT; diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index e13bc8a97c449..f1de84990aedb 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -896,9 +896,19 @@ static int parse_create_response(struct cifs_open_info_data *data, rc = smb2_parse_symlink_response(cifs_sb, iov, full_path, &data->symlink_target); - if (rc) + if (rc != 0 && rc != -ENODATA) return rc; - tag = IO_REPARSE_TAG_SYMLINK; + /* + * -ENODATA means that the response was parsed but did not contain + * the symlink target at all (see symlink_data()). Treat it like + * STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not contain it + * either: leave the tag unset and clear rc, so that the caller + * retrieves the target with SMB2_OP_GET_REPARSE. + */ + if (rc == -ENODATA) + rc = 0; + else + tag = IO_REPARSE_TAG_SYMLINK; reparse_point = true; break; case STATUS_SUCCESS: @@ -1091,7 +1101,14 @@ int smb2_query_path_info(const unsigned int xid, rc = -EOPNOTSUPP; } - if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) { + /* + * If the symlink was already parsed in create response then it is needed to fix + * its type now (after the second call with OPEN_REPARSE_POINT which filled the + * data->fi.Attributes). If the symlink was not parsed in create response then + * the data->symlink_target was not filled yet and then the type will be fixed + * later after data->symlink_target is filled. + */ + if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) { bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY; rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb); } From 18705cace0619fd2123737dcd028147774f38181 Mon Sep 17 00:00:00 2001 From: James Montgomery Date: Fri, 3 Jul 2026 15:26:41 -0400 Subject: [PATCH 2777/3297] ksmbd: defer destroy_previous_session() until after NTLM authentication commit c74801ee524f477c174a1899782b6c3b6918d407 upstream. In ntlm_authenticate(), destroy_previous_session() is called using a user pointer resolved from the client-supplied NTLM blob username field before the NTLMv2 response is validated. An authenticated attacker can set the NTLM blob username to match a victim account and set PreviousSessionId to the victim's session ID; destroy_previous_session() destroys the victim's session while ksmbd_decode_ntlmssp_auth_blob() subsequently rejects the request with -EPERM. Move destroy_previous_session() and the prev_id assignment to after ksmbd_decode_ntlmssp_auth_blob() returns success and use sess->user rather than the pre-authentication lookup result. This matches the ordering already used by krb5_authenticate(), where destroy_previous_session() is called only after ksmbd_krb5_authenticate() returns success. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-cifs/20260702155449.3639773-1-james_montgomery@disroot.org/ Signed-off-by: James Montgomery Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smb2pdu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a69bc75c85ad0..64a1cfc4e8099 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1492,11 +1492,6 @@ static int ntlm_authenticate(struct ksmbd_work *work, return -EPERM; } - /* Check for previous session */ - prev_id = le64_to_cpu(req->PreviousSessionId); - if (prev_id && prev_id != sess->id) - destroy_previous_session(conn, user, prev_id); - if (sess->state == SMB2_SESSION_VALID) { /* * Reuse session if anonymous try to connect @@ -1534,6 +1529,10 @@ static int ntlm_authenticate(struct ksmbd_work *work, } } + prev_id = le64_to_cpu(req->PreviousSessionId); + if (prev_id && prev_id != sess->id) + destroy_previous_session(conn, sess->user, prev_id); + /* * If session state is SMB2_SESSION_VALID, We can assume * that it is reauthentication. And the user/password From 91e0249f3ef62b75fe8c9c9372eaba32876e4b3a Mon Sep 17 00:00:00 2001 From: Eddie Phillips Date: Thu, 9 Jul 2026 21:19:06 +0000 Subject: [PATCH 2778/3297] gve: fix Rx queue stall on alloc failure commit b65352a1bac64442ad95e64f385b40ccb9f1b0db upstream. When the system is under extreme memory pressure, page allocations can fail during the Rx buffer refill loop. If the number of buffers posted to hardware falls below a critical low threshold and the refill loop exits due to allocation failures, the queue can stall: 1. The device drops incoming packets because there are no descriptors. 2. Since no packets are processed, no Rx completions are generated. 3. Because no completions occur, NAPI is never scheduled, preventing the refill loop from running again even after memory is freed. This results in a permanent queue stall. Resolve this by introducing a starvation recovery timer for each Rx queue. If the number of buffers posted to hardware falls below a critical low threshold, start a timer to periodically reschedule NAPI. Once NAPI runs and successfully refills the queue above the threshold, the timer is not rescheduled. The threshold is set to 32 because a single maximum-sized Receive Segment Coalescing (RSC) packet can consume up to 19 descriptors in the Rx path. Lower thresholds (such as 8 or 16) would be insufficient to process a complete maximum-sized RSC packet, risking packet drops or unexpected hardware behavior under memory pressure. Setting the threshold to 32 guarantees a safe margin to handle at least one full RSC packet. Cc: stable@vger.kernel.org Fixes: 9b8dd5e5ea48 ("gve: DQO: Add RX path") Reviewed-by: Jordan Rhee Signed-off-by: Eddie Phillips Signed-off-by: Harshitha Ramamurthy Reviewed-by: Przemek Kitszel Link: https://patch.msgid.link/20260709211906.3322883-1-hramamurthy@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/google/gve/gve.h | 3 ++ drivers/net/ethernet/google/gve/gve_rx_dqo.c | 34 ++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 4cc6dcbfd367b..9e7d6860b83fa 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ /* Interval to schedule a stats report update, 20000ms. */ #define GVE_STATS_REPORT_TIMER_PERIOD 20000 +#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */ /* Numbers of NIC tx/rx stats in stats report. */ #define NIC_TX_STATS_REPORT_NUM 0 @@ -338,6 +340,7 @@ struct gve_rx_ring { struct xdp_rxq_info xdp_rxq; struct xsk_buff_pool *xsk_pool; struct page_frag_cache page_cache; /* Page cache to allocate XDP frames */ + struct timer_list starvation_timer; /* for queue starvation recovery */ }; /* A TX desc ring entry */ diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 6a0430a93ed75..fc0c7991c3a25 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -18,6 +18,16 @@ #include #include +static void gve_rx_starvation_timer(struct timer_list *t) +{ + struct gve_rx_ring *rx = timer_container_of(rx, t, starvation_timer); + struct gve_priv *priv = rx->gve; + struct gve_notify_block *block; + + block = &priv->ntfy_blocks[rx->ntfy_id]; + napi_schedule(&block->napi); +} + static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx) { struct device *hdev = &priv->pdev->dev; @@ -120,6 +130,7 @@ void gve_rx_stop_ring_dqo(struct gve_priv *priv, int idx) if (rx->dqo.page_pool) page_pool_disable_direct_recycling(rx->dqo.page_pool); + timer_shutdown_sync(&rx->starvation_timer); gve_remove_napi(priv, ntfy_idx); gve_rx_remove_from_block(priv, idx); gve_rx_reset_ring_dqo(priv, idx); @@ -208,8 +219,10 @@ static int gve_rx_alloc_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx, void gve_rx_start_ring_dqo(struct gve_priv *priv, int idx) { int ntfy_idx = gve_rx_idx_to_ntfy(priv, idx); + struct gve_rx_ring *rx = &priv->rx[idx]; gve_rx_add_to_block(priv, idx); + timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0); gve_add_napi(priv, ntfy_idx, gve_napi_poll_dqo); } @@ -363,6 +376,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq; struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq; struct gve_priv *priv = rx->gve; + u32 num_bufs_avail_to_hw; u32 num_avail_slots; u32 num_full_slots; u32 num_posted = 0; @@ -398,6 +412,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) } rx->fill_cnt += num_posted; + + /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors + * visible to the hardware, the hardware is in danger of starving + * and cannot trigger interrupts. + * + * We use a threshold of 32 because a single maximum-sized RSC + * packet can consume up to 19 descriptors in the Rx path. Lower + * thresholds (e.g., 8 or 16) would be unsafe as they could cause + * the device to drop/stall on a maximum-sized RSC packet. + * + * Start the timer to periodically reschedule NAPI and recover. + */ + num_bufs_avail_to_hw = + ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) - + bufq->head) & bufq->mask; + + if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) { + mod_timer(&rx->starvation_timer, + jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS)); + } } static void gve_rx_skb_csum(struct sk_buff *skb, From 5e496f2b615cec4b45537cfb5b54f36a51dc8753 Mon Sep 17 00:00:00 2001 From: Aleksandr Loktionov Date: Fri, 17 Jul 2026 11:53:33 -0700 Subject: [PATCH 2779/3297] ice: reject out-of-range ptype in ice_parser_profile_init commit 59abb87159c53605c063f6e2ceb215b5eba43ee6 upstream. set_bit(rslt->ptype, prof->ptypes) operates on a DECLARE_BITMAP of ICE_FLOW_PTYPE_MAX (1024) bits. Nothing prevents a malicious VF from providing ptype >= 1024 through VIRTCHNL, resulting in a write past the end of the bitmap and a kernel page fault. Reproduced with a custom kernel module injecting a crafted VIRTCHNL_OP_ADD_RSS_CFG on E810-C QSFP (8086:1592), FW 4.91 0x800214af 1.3909.0, ICE COMMS DDP 1.3.53.0, kernel 7.1.0-rc1. crash_parser: ice_parser_profile_init @ ffffffffc0d61b60 crash_parser: setting ptype=0xffff (max valid=1023) crash_parser: calling ice_parser_profile_init -- expect OOB crash! BUG: kernel NULL pointer dereference, address: 0000000000000000 Oops: Oops: 0002 [#1] SMP NOPTI CPU: 56 UID: 0 PID: 165011 Comm: insmod Kdump: loaded Tainted: G S U OE 7.1.0-rc1 #1 Hardware name: Intel Corporation S2600BPB/S2600BPB RIP: 0010:ice_parser_profile_init+0x2d/0x1d0 [ice] Call Trace: ? __pfx_ice_parser_profile_init+0x10/0x10 [ice] crash_init+0x127/0xff0 [crash_parser] do_one_initcall+0x45/0x310 do_init_module+0x64/0x270 init_module_from_file+0xcc/0xf0 idempotent_init_module+0x17b/0x280 __x64_sys_finit_module+0x6e/0xe0 Bail out early with -EINVAL when ptype is out of range. Fixes: e312b3a1e209 ("ice: add API for parser profile initialization") Cc: stable@vger.kernel.org Signed-off-by: Aleksandr Loktionov Reviewed-by: Marcin Szycik Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-12-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_parser.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_parser.c b/drivers/net/ethernet/intel/ice/ice_parser.c index 664beb64f5570..0c240e90a54e1 100644 --- a/drivers/net/ethernet/intel/ice/ice_parser.c +++ b/drivers/net/ethernet/intel/ice/ice_parser.c @@ -2368,6 +2368,9 @@ int ice_parser_profile_init(struct ice_parser_result *rslt, u16 proto_off = 0; u16 off; + if (rslt->ptype >= ICE_FLOW_PTYPE_MAX) + return -EINVAL; + memset(prof, 0, sizeof(*prof)); set_bit(rslt->ptype, prof->ptypes); if (blk == ICE_BLK_SW) { From 919d0accf2600b5e5f4c44aa5733b318b5324588 Mon Sep 17 00:00:00 2001 From: Sergey Temerkhanov Date: Fri, 17 Jul 2026 11:53:30 -0700 Subject: [PATCH 2780/3297] ice: use READ_ONCE() to access cached PHC time commit 2915681b89f817677ab9f1166d95b595bc144f5f upstream. ptp.cached_phc_time is a 64-bit value updated by a periodic work item on one CPU and read locklessly on another. On 32-bit or non-atomic architectures this can result in a torn read. Use READ_ONCE() to enforce a single atomic load. Fixes: 77a781155a65 ("ice: enable receive hardware timestamping") Cc: stable@vger.kernel.org Signed-off-by: Sergey Temerkhanov 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/20260717185340.3595286-9-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 9607c1555b39e..30a34ed2ae60a 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -350,7 +350,7 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) return 0; } - return ice_ptp_extend_32b_ts(pf->ptp.cached_phc_time, + return ice_ptp_extend_32b_ts(READ_ONCE(pf->ptp.cached_phc_time), (in_tstamp >> 8) & mask); } From 472aba2603ca74c4f7722cb0c0296942b0776b8d Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 14 Jul 2026 07:49:03 -0400 Subject: [PATCH 2781/3297] ila: reload IPv6 header after pskb_may_pull in checksum adjust commit 92d3817649df2b0b6a008a686c8275c88d7ef594 upstream. ila_csum_adjust_transport() caches ip6h = ipv6_hdr(skb) before calling pskb_may_pull(). On a non-linear skb whose transport header sits in a page fragment, pskb_may_pull() can call __pskb_pull_tail() / pskb_expand_head() and free the old skb head, leaving ip6h dangling; the following get_csum_diff(ip6h, p) then reads freed memory. ila_update_ipv6_locator() uses ip6h (and the iaddr derived from it) again after the csum-adjust call and additionally writes the new locator through that pointer. Impact: a remote IPv6 packet routed through a configured ILA csum-adjust-transport route or receive-side mapping triggers a slab-use-after-free in ila_update_ipv6_locator() (KASAN). The route or mapping requires CAP_NET_ADMIN to configure, but trigger packets are unauthenticated once it exists. Reload ip6h after each pskb_may_pull() in ila_csum_adjust_transport() before the csum-diff read. In ila_update_ipv6_locator() only the ILA_CSUM_ADJUST_TRANSPORT case pulls the skb, so reload ip6h and iaddr in that case alone before the destination-address write; the neutral-map modes never pull and keep their cached pointers. Fixes: 33f11d16142b ("ila: Create net/ipv6/ila directory") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Reviewed-by: Antoine Tenart Link: https://patch.msgid.link/20260714114903.3763420-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ila/ila_common.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c index b8d43ed4689db..ca54a227fc2f6 100644 --- a/net/ipv6/ila/ila_common.c +++ b/net/ipv6/ila/ila_common.c @@ -84,6 +84,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, struct tcphdr *th = (struct tcphdr *) (skb_network_header(skb) + nhoff); + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&th->check, skb, diff, true, true); @@ -95,6 +96,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, (skb_network_header(skb) + nhoff); if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&uh->check, skb, diff, true, true); @@ -109,6 +111,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, struct icmp6hdr *ih = (struct icmp6hdr *) (skb_network_header(skb) + nhoff); + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb, diff, true, true); @@ -126,6 +129,15 @@ void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, switch (p->csum_mode) { case ILA_CSUM_ADJUST_TRANSPORT: ila_csum_adjust_transport(skb, p); + /* + * ila_csum_adjust_transport() calls pskb_may_pull(), which can + * reallocate the skb head and leave ip6h (and the iaddr derived + * from it) dangling; reload both before the write below. The + * other csum modes do not pull, so their cached pointers stay + * valid. + */ + ip6h = ipv6_hdr(skb); + iaddr = ila_a2i(&ip6h->daddr); break; case ILA_CSUM_NEUTRAL_MAP: if (sir2ila) { From 59c1d5463b7bc5a2cdaae27108d1dfd67edc7d1b Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Tue, 21 Jul 2026 23:12:28 +0200 Subject: [PATCH 2782/3297] mac802154: hold an interface reference across the scan worker commit 234e5e898b713bc0b3a631b6f002897f43d046c8 upstream. mac802154_scan_worker() captures the scanning sub-interface under RCU and then keeps dereferencing sdata->dev after rcu_read_unlock() and outside the rtnl -- in the failure traces, in mac802154_transmit_beacon_req() (skb->dev = sdata->dev), and in the end_scan cleanup. Nothing keeps that netdev alive across the worker iteration. A concurrent DEL_INTERFACE or PHY removal can unregister the interface once the worker drops the rtnl between its two drv_set_channel() sections. unregister_netdevice() frees the netdev asynchronously from netdev_run_todo() with the rtnl already dropped, so neither holding the rtnl nor the per-PHY IEEE802154_IS_SCANNING flag prevents a stale worker iteration from dereferencing the freed netdev -- a KASAN slab-use-after-free, reachable by racing TRIGGER_SCAN against DEL_INTERFACE (both CAP_NET_ADMIN). Pin the netdev with netdev_hold() while the RCU read lock is still held, and release it at every worker exit. Fixes: 57588c71177f ("mac802154: Handle passive scanning") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Link: https://patch.msgid.link/20260721211228.34578-1-security@auditcode.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mac802154/scan.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 0eef3b4f5b9b3..775809ee02067 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -179,6 +179,7 @@ void mac802154_scan_worker(struct work_struct *work) enum nl802154_scan_types scan_req_type; struct ieee802154_sub_if_data *sdata; unsigned int scan_duration = 0; + netdevice_tracker dev_tracker; struct wpan_phy *wpan_phy; u8 scan_req_duration; u8 page, channel; @@ -209,6 +210,14 @@ void mac802154_scan_worker(struct work_struct *work) return; } + /* + * sdata->dev is dereferenced below after rcu_read_unlock() and outside + * the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it + * asynchronously from netdev_run_todo(). Pin it with a reference taken + * while the RCU read lock is still held, and drop it at every exit. + */ + netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC); + wpan_phy = scan_req->wpan_phy; scan_req_type = scan_req->type; scan_req_duration = scan_req->duration; @@ -262,12 +271,14 @@ void mac802154_scan_worker(struct work_struct *work) "Scan page %u channel %u for %ums\n", page, channel, jiffies_to_msecs(scan_duration)); queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration); + netdev_put(sdata->dev, &dev_tracker); return; end_scan: rtnl_lock(); mac802154_scan_cleanup_locked(local, sdata, false); rtnl_unlock(); + netdev_put(sdata->dev, &dev_tracker); } int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata, From f20dedce0429b293d4bad604e0d3f65d8ac96c83 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 21:34:23 +0200 Subject: [PATCH 2783/3297] mac802154: llsec: reject frames shorter than the authentication tag commit fd3a3f28ed60c6af4b2a39933b151d6b27842c3b upstream. llsec_do_decrypt_auth() computes the associated-data length for the AEAD request as assoclen += datalen - authlen; where datalen is the number of bytes after the MAC header and authlen (4, 8 or 16) is the length of the authentication tag. Nothing verifies that the frame actually carries at least authlen payload bytes. A secured frame whose payload is shorter than the tag makes datalen - authlen negative; assoclen is then passed to aead_request_set_ad() as an unsigned value close to 4 GiB, so crypto_aead_decrypt() walks far off the end of the scatterlist that only spans the real frame. The frame is fully attacker-controlled and reaches this path from any IEEE 802.15.4 peer in radio range. Reject frames whose payload is shorter than the authentication tag before the subtraction. Dynamically reproduced on a KASAN kernel as a general-protection-fault in the AEAD scatterwalk, and the fix confirmed. Fixes: 4c14a2fb5d14 ("mac802154: add llsec decryption method") Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Signed-off-by: Doruk Tan Ozturk Link: https://patch.msgid.link/20260716193423.32498-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/mac802154/llsec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 09a47104b5772..a55fa5017e930 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -891,6 +891,11 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, data = skb_mac_header(skb) + skb->mac_len; datalen = skb_tail_pointer(skb) - data; + if (datalen < authlen) { + kfree_sensitive(req); + return -EBADMSG; + } + sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen); if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) { From f80ba170d7b3a44e3d244a2c8e06031d61bf3b23 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Wed, 15 Jul 2026 10:20:21 +0200 Subject: [PATCH 2784/3297] mctp: serial: handle zero-length frames to prevent rx buffer overflow commit 793b9b729f1e8de57be8c8daf1a9838be96cabed upstream. The MCTP serial receive state machine reads a frame length byte in mctp_serial_push_header() case 2 and validates it upper-bound-only: if (c > MCTP_SERIAL_FRAME_MTU) { dev->rxstate = STATE_ERR; } else { dev->rxlen = c; dev->rxpos = 0; dev->rxstate = STATE_DATA; ... } A length of zero passes this check, so rxlen is set to 0 and the state machine advances to STATE_DATA. In mctp_serial_push() STATE_DATA, the incoming byte is stored and rxpos incremented before the terminator is tested: dev->rxbuf[dev->rxpos] = c; dev->rxpos++; dev->rxstate = STATE_DATA; if (dev->rxpos == dev->rxlen) { dev->rxpos = 0; dev->rxstate = STATE_TRAILER; } With rxlen == 0 the "rxpos == rxlen" terminator can never fire (rxpos is already 1 on the first data byte), so subsequent bytes are written past the end of the fixed 74-byte rxbuf, which is the last member of the netdev private area. Every following data byte is an attacker-controlled 1-byte out-of-bounds heap write, and the overflow continues until a frame (0x7e) or escape byte resets the parser -- effectively unbounded. Reaching this requires CAP_NET_ADMIN to attach the N_MCTP line discipline and bring the resulting mctpserialN netdev up, after which the bytes arrive via the tty receive path. Route a zero-length frame straight to STATE_TRAILER instead of STATE_DATA. The trailer/framing bytes are still consumed, and the frame resolves to a zero-length skb that the MCTP core rejects; the parser never enters STATE_DATA with rxlen == 0, so the out-of-bounds write can no longer occur. KASAN, on a frame of 0x7e 0x01 0x00 followed by data bytes (before this change): UBSAN: array-index-out-of-bounds in drivers/net/mctp/mctp-serial.c:370 index 74 is out of range for type 'u8 [74]' BUG: KASAN: slab-out-of-bounds in mctp_serial_tty_receive_buf Write of size 1 at addr ... by task kworker/u16:0 mctp_serial_tty_receive_buf tty_ldisc_receive_buf flush_to_ldisc Allocated by task 152: alloc_netdev_mqs mctp_serial_open v2: route zero-length frames to STATE_TRAILER instead of STATE_ERR so the trailer/framing bytes are still consumed (Jeremy Kerr). Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: a0c2ccd9b5ad ("mctp: Add MCTP-over-serial transport binding") Cc: stable@vger.kernel.org Suggested-by: Jeremy Kerr Assisted-by: 0sec:multi-model Signed-off-by: Doruk Tan Ozturk Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260715082021.46315-1-doruk@0sec.ai Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- drivers/net/mctp/mctp-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/mctp/mctp-serial.c b/drivers/net/mctp/mctp-serial.c index 26c9a33fd6364..a5070ffa9a955 100644 --- a/drivers/net/mctp/mctp-serial.c +++ b/drivers/net/mctp/mctp-serial.c @@ -318,7 +318,7 @@ static void mctp_serial_push_header(struct mctp_serial *dev, u8 c) } else { dev->rxlen = c; dev->rxpos = 0; - dev->rxstate = STATE_DATA; + dev->rxstate = c > 0 ? STATE_DATA : STATE_TRAILER; dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c); } break; From 100a23b1613e9218e0af654ef102352c713f0263 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Tue, 7 Jul 2026 15:16:35 -0700 Subject: [PATCH 2785/3297] openvswitch: fix GSO userspace truncation underflow commit 4032f8ed10fcb84d41c508dfb04be96589f78dfe upstream. OVS_ACTION_ATTR_TRUNC currently stores a delta from the original skb length in OVS_CB(skb)->cutlen. When a later userspace action segments a GSO skb, queue_gso_packets() reuses that delta for each smaller segment. A segment can then reach queue_userspace_packet() with cutlen greater than skb->len, underflowing the length passed to skb_zerocopy(). Store the maximum preserved length instead and bound each consumer against the current skb length. Use U32_MAX as the no-truncation sentinel so the value remains valid if skb geometry changes before a consumer handles it. Fixes: f2a4d086ed4c ("openvswitch: Add packet truncation support.") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.5 Signed-off-by: Kyle Zeng Reviewed-by: Ilya Maximets Reviewed-by: Aaron Conole Link: https://patch.msgid.link/20260707221635.27489-1-kylebot@openai.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/openvswitch/actions.c | 19 +++++++------------ net/openvswitch/datapath.c | 25 ++++++++++++++----------- net/openvswitch/datapath.h | 2 +- net/openvswitch/vport.c | 2 +- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 792ca44a461da..56ea40d23e280 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -836,12 +836,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, u16 mru = OVS_CB(skb)->mru; u32 cutlen = OVS_CB(skb)->cutlen; - if (unlikely(cutlen > 0)) { - if (skb->len - cutlen > ovs_mac_header_len(key)) - pskb_trim(skb, skb->len - cutlen); - else - pskb_trim(skb, ovs_mac_header_len(key)); - } + if (unlikely(cutlen < skb->len)) + pskb_trim(skb, max(cutlen, ovs_mac_header_len(key))); if (likely(!mru || (skb->len <= mru + vport->dev->hard_header_len))) { @@ -1233,7 +1229,7 @@ static void execute_psample(struct datapath *dp, struct sk_buff *skb, psample_group.net = ovs_dp_get_net(dp); md.in_ifindex = OVS_CB(skb)->input_vport->dev->ifindex; - md.trunc_size = skb->len - OVS_CB(skb)->cutlen; + md.trunc_size = min(skb->len, OVS_CB(skb)->cutlen); md.rate_as_probability = 1; rate = OVS_CB(skb)->probability ? OVS_CB(skb)->probability : U32_MAX; @@ -1283,22 +1279,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, clone = skb_clone(skb, GFP_ATOMIC); if (clone) do_output(dp, clone, port, key); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; break; } case OVS_ACTION_ATTR_TRUNC: { struct ovs_action_trunc *trunc = nla_data(a); - if (skb->len > trunc->max_len) - OVS_CB(skb)->cutlen = skb->len - trunc->max_len; + OVS_CB(skb)->cutlen = trunc->max_len; break; } case OVS_ACTION_ATTR_USERSPACE: output_userspace(dp, skb, key, a, attr, len, OVS_CB(skb)->cutlen); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; if (nla_is_last(a, rem)) { consume_skb(skb); return 0; @@ -1452,7 +1447,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_PSAMPLE: execute_psample(dp, skb, a); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; if (nla_is_last(a, rem)) { consume_skb(skb); return 0; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 56c744e1e14c5..e3d89ce7df9f2 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -276,7 +276,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) upcall.portid = ovs_vport_find_upcall_portid(p, skb); upcall.mru = OVS_CB(skb)->mru; - error = ovs_dp_upcall(dp, skb, key, &upcall, 0); + error = ovs_dp_upcall(dp, skb, key, &upcall, U32_MAX); switch (error) { case 0: case -EAGAIN: @@ -457,7 +457,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, struct sk_buff *nskb = NULL; struct sk_buff *user_skb = NULL; /* to be queued to userspace */ struct nlattr *nla; - size_t len; + size_t msg_size; + size_t skb_len; unsigned int hlen; int err, dp_ifindex; u64 hash; @@ -478,7 +479,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, skb = nskb; } - if (nla_attr_size(skb->len) > USHRT_MAX) { + skb_len = min(skb->len, cutlen); + if (nla_attr_size(skb_len) > USHRT_MAX) { err = -EFBIG; goto out; } @@ -493,13 +495,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, * padding logic. Only perform zerocopy if padding is not required. */ if (dp->user_features & OVS_DP_F_UNALIGNED) - hlen = skb_zerocopy_headlen(skb); + hlen = min(skb_zerocopy_headlen(skb), cutlen); else - hlen = skb->len; + hlen = skb_len; - len = upcall_msg_size(upcall_info, hlen - cutlen, - OVS_CB(skb)->acts_origlen); - user_skb = genlmsg_new(len, GFP_ATOMIC); + msg_size = upcall_msg_size(upcall_info, hlen, + OVS_CB(skb)->acts_origlen); + user_skb = genlmsg_new(msg_size, GFP_ATOMIC); if (!user_skb) { err = -ENOMEM; goto out; @@ -560,7 +562,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, } /* Add OVS_PACKET_ATTR_LEN when packet is truncated */ - if (cutlen > 0 && + if (skb_len < skb->len && nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN, skb->len)) { err = -ENOBUFS; goto out; @@ -585,9 +587,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, err = -ENOBUFS; goto out; } - nla->nla_len = nla_attr_size(skb->len - cutlen); + nla->nla_len = nla_attr_size(skb_len); - err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen); + err = skb_zerocopy(user_skb, skb, skb_len, hlen); if (err) goto out; @@ -644,6 +646,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) packet->ignore_df = 1; } OVS_CB(packet)->mru = mru; + OVS_CB(packet)->cutlen = U32_MAX; if (a[OVS_PACKET_ATTR_HASH]) { hash = nla_get_u64(a[OVS_PACKET_ATTR_HASH]); diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index db0c3e69d66ca..696640e88fa7e 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -118,7 +118,7 @@ struct datapath { * @mru: The maximum received fragement size; 0 if the packet is not * fragmented. * @acts_origlen: The netlink size of the flow actions applied to this skb. - * @cutlen: The number of bytes from the packet end to be removed. + * @cutlen: The number of bytes in the packet to preserve on output. * @probability: The sampling probability that was applied to this skb; 0 means * no sampling has occurred; U32_MAX means 100% probability. * @upcall_pid: Netlink socket PID to use for sending this packet to userspace; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 1f0c86a9f43b0..7470f43d31ea8 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -503,7 +503,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, OVS_CB(skb)->input_vport = vport; OVS_CB(skb)->mru = 0; - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; OVS_CB(skb)->probability = 0; OVS_CB(skb)->upcall_pid = 0; if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { From b08526bf0bbf84ceebd29033783e8e0c9f451286 Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Sat, 23 May 2026 05:02:43 -0400 Subject: [PATCH 2786/3297] ovpn: fix peer refcount leak in TCP error paths commit 63bbe18fc03062f483c627838a566a707b62da79 upstream. When either the TCP RX or TX error path calls ovpn_peer_hold() followed by schedule_work(&peer->tcp.defer_del_work), and the work item is already pending from the other path, schedule_work() returns false and the work runs only once. Since ovpn_tcp_peer_del_work() calls ovpn_peer_put() exactly once, the extra reference taken by the losing path is never dropped, leaking the peer object. The race window: CPU0 (strparser/RX error): CPU1 (tcp_tx_work/TX error): ovpn_peer_hold() <- refcnt+1 ovpn_peer_hold() <- refcnt+2 schedule_work() <- queued schedule_work() <- NO-OP (work already pending) ovpn_tcp_peer_del_work runs: ovpn_peer_del() ovpn_peer_put() <- refcnt+1 <- peer never freed Fix by checking the return value of schedule_work() in both paths and calling ovpn_peer_put() to drop the extra reference if the work was already pending. ovpn_peer_hold() is kept unconditional in the TX path as it cannot fail at that point. Fixes: a6a5e87b3ee4 ("ovpn: avoid sleep in atomic context in TCP RX error path") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha Reviewed-by: Sabrina Dubroca Signed-off-by: Antonio Quartulli Signed-off-by: Greg Kroah-Hartman --- drivers/net/ovpn/tcp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 433bd07a4f1be..0af14055c39aa 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -151,7 +151,8 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb) /* take reference for deferred peer deletion. should never fail */ if (WARN_ON(!ovpn_peer_hold(peer))) goto err_nopeer; - schedule_work(&peer->tcp.defer_del_work); + if (!schedule_work(&peer->tcp.defer_del_work)) + ovpn_peer_put(peer); ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); err_nopeer: kfree_skb(skb); @@ -283,7 +284,8 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) * stream therefore we abort the connection */ ovpn_peer_hold(peer); - schedule_work(&peer->tcp.defer_del_work); + if (!schedule_work(&peer->tcp.defer_del_work)) + ovpn_peer_put(peer); /* we bail out immediately and keep tx_in_progress set * to true. This way we prevent more TX attempts From 460b9f0609d263dd4b3b36482b6f9ef2d8f55067 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Sat, 23 May 2026 20:38:27 +0545 Subject: [PATCH 2787/3297] ovpn: hold peer before scheduling keepalive work commit a4710ae2e7e322fdaefb4be8604228279cfaf48c upstream. ovpn_peer_keepalive_send() passes its peer reference to ovpn_xmit_special(), which ultimately drops it. The keepalive scheduler currently queues the work first and takes the reference only after schedule_work() reports that the work was queued. Once schedule_work() queues the item, another CPU may run the worker before the caller gets to ovpn_peer_hold(). In that case the worker can consume a reference that was not acquired for it, corrupting the peer lifetime accounting. Take the peer reference before queueing the work and drop it again when the work was already pending. Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism") Cc: stable@vger.kernel.org Signed-off-by: Shuvam Pandey Reviewed-by: Sabrina Dubroca Signed-off-by: Antonio Quartulli Signed-off-by: Greg Kroah-Hartman --- drivers/net/ovpn/peer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index b7c103fda2ae6..85fa9bd475f45 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1280,8 +1280,10 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer, netdev_dbg(peer->ovpn->dev, "sending keepalive to peer %u\n", peer->id); - if (schedule_work(&peer->keepalive_work)) - ovpn_peer_hold(peer); + if (WARN_ON(!ovpn_peer_hold(peer))) + return 0; + if (!schedule_work(&peer->keepalive_work)) + ovpn_peer_put(peer); } if (next_run1 < next_run2) From 6866abf59976d273164a6624234d96a967280223 Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Wed, 22 Jul 2026 09:38:43 +0000 Subject: [PATCH 2788/3297] pppoe: reload header pointer after dev_hard_header() commit e9c238f6fe42fb1b4dba3a578277de32cb487937 upstream. pppoe_sendmsg() saves a pointer to the PPPoE header before calling dev_hard_header(). Device header callbacks are allowed to reallocate the skb head, invalidating pointers into it. This can happen when a send is blocked in copy_from_user() while the first non-Ethernet port is added to an empty team device. The team's delegated GRE header callback then expands the skb head. PPPoE subsequently writes six bytes through the stale pointer into the freed head. Reload the PPPoE header through the skb's network-header offset after device header creation. pskb_expand_head() updates that offset when it relocates the head. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Asim Viladi Oglu Manizada Reviewed-by: Vadim Fedorenko Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260722093814.3017176-1-manizada@pm.me Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ppp/pppoe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index dc6d139432387..7ee63051a1619 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -903,6 +903,7 @@ static int pppoe_sendmsg(struct socket *sock, struct msghdr *m, dev_hard_header(skb, dev, ETH_P_PPP_SES, po->pppoe_pa.remote, NULL, total_len); + ph = pppoe_hdr(skb); memcpy(ph, &hdr, sizeof(struct pppoe_hdr)); ph->length = htons(total_len); From 4a4f3aa6af205bee539b5670afa2cd4e4953750e Mon Sep 17 00:00:00 2001 From: Justin Lai Date: Thu, 9 Jul 2026 18:34:56 +0800 Subject: [PATCH 2789/3297] rtase: Workaround for TX hang caused by hardware packet parsing commit 1c50efa1faf3a1a96e100b07ec7a2f3164d90bee upstream. The hardware performs packet parsing before packet transmission. Parsing incomplete IPv4, IPv6, TCP, or UDP headers may trigger a TX hang because the hardware parser expects additional protocol header data that is not present in the packet. The hardware performs additional PTP parsing on UDP packets identified by destination ports 319/320 at the expected UDP destination port offset. If such a packet has transport data smaller than RTASE_MIN_PAD_LEN, the hardware parser expects additional packet data and may trigger a TX hang. To avoid these hardware issues, the driver applies the following workarounds. Drop malformed packets that may trigger this hardware issue before transmission. For IPv4 non-initial fragments, the hardware does not check the fragment offset before parsing the expected transport header location. As a result, these packets are still subject to transport header parsing even though they do not contain a transport header. If the transport data is shorter than the minimum transport header required by the hardware parser, pad the transport data to the minimum transport header length required by the hardware parser. Packets that also match the hardware PTP parsing conditions continue to follow the corresponding workaround. For IPv6 fragmented packets, neither of the above hardware issues occurs because the hardware only continues packet parsing when the IPv6 Base Header Next Header field directly indicates UDP. Packets carrying a Fragment Header do not continue through the subsequent packet parsing stages. For packets identified for hardware PTP parsing, pad the transport data so it reaches RTASE_MIN_PAD_LEN before transmission. Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function") Cc: stable@vger.kernel.org Signed-off-by: Justin Lai Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709103456.83789-1-justinlai0215@realtek.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/realtek/rtase/rtase.h | 8 + .../net/ethernet/realtek/rtase/rtase_main.c | 197 ++++++++++++++++++ 2 files changed, 205 insertions(+) diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h index b9209eb6ea735..43087b17a0ff2 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase.h +++ b/drivers/net/ethernet/realtek/rtase/rtase.h @@ -188,6 +188,12 @@ enum rtase_sw_flag_content { RTASE_SWF_MSIX_ENABLED = BIT(2), }; +enum rtase_parse_result { + RTASE_PARSE_OK, + RTASE_PARSE_SKIP, + RTASE_PARSE_DROP, +}; + #define RSVD_MASK 0x3FFFC000 struct rtase_tx_desc { @@ -359,4 +365,6 @@ struct rtase_private { #define RTASE_MSS_MASK GENMASK(28, 18) +#define RTASE_MIN_PAD_LEN 47 + #endif /* RTASE_H */ diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index 55105d34bc797..a57a525327a3b 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -1249,6 +1250,199 @@ static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev) return csum_cmd; } +static enum rtase_parse_result rtase_get_l3_proto(struct sk_buff *skb, + __be16 *proto, + u32 *network_offset) +{ + struct vlan_hdr *vh, _vh; + struct ethhdr *eh, _eh; + u32 offset = ETH_HLEN; + + eh = skb_header_pointer(skb, 0, sizeof(_eh), &_eh); + if (!eh) + return RTASE_PARSE_DROP; + + *proto = eh->h_proto; + + while (eth_type_vlan(*proto)) { + vh = skb_header_pointer(skb, offset, sizeof(_vh), &_vh); + if (!vh) + return RTASE_PARSE_DROP; + + *proto = vh->h_vlan_encapsulated_proto; + offset += VLAN_HLEN; + } + + *network_offset = offset; + + return RTASE_PARSE_OK; +} + +static bool rtase_pad_to_transport_len(struct sk_buff *skb, + u32 transport_offset, + u32 pad_to_len) +{ + u32 trans_data_len; + u32 pad_len; + + trans_data_len = skb->len - transport_offset; + if (trans_data_len >= pad_to_len) + return true; + + if (skb_is_nonlinear(skb)) { + if (skb_linearize(skb)) + return false; + } + + pad_len = pad_to_len - trans_data_len; + if (__skb_put_padto(skb, skb->len + pad_len, false)) + return false; + + return true; +} + +static enum rtase_parse_result rtase_get_transport_offset(struct sk_buff *skb, + u32 *transport_offset, + u8 *transport_proto, + u32 *pad_to_len) +{ + enum rtase_parse_result ret; + struct ipv6hdr *i6h, _i6h; + struct iphdr *ih, _ih; + bool non_first_frag; + __be16 proto; + u32 offset; + u32 no; + + ret = rtase_get_l3_proto(skb, &proto, &no); + if (ret != RTASE_PARSE_OK) + return ret; + + switch (proto) { + case htons(ETH_P_IP): + ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih); + if (!ih) + return RTASE_PARSE_DROP; + + if (ih->ihl < 5) + return RTASE_PARSE_DROP; + + offset = no + ih->ihl * 4; + if (offset > skb->len) + return RTASE_PARSE_DROP; + + non_first_frag = ntohs(ih->frag_off) & IP_OFFSET; + + if (ih->protocol == IPPROTO_TCP) { + if (skb->len - offset < sizeof(struct tcphdr)) { + if (non_first_frag) { + *transport_offset = offset; + *transport_proto = IPPROTO_TCP; + *pad_to_len = sizeof(struct tcphdr); + + return RTASE_PARSE_OK; + } + + return RTASE_PARSE_DROP; + } + + return RTASE_PARSE_SKIP; + } + + if (ih->protocol != IPPROTO_UDP) + return RTASE_PARSE_SKIP; + + *transport_offset = offset; + *transport_proto = IPPROTO_UDP; + + if (skb->len - offset < sizeof(struct udphdr)) { + if (non_first_frag) { + *pad_to_len = sizeof(struct udphdr); + + return RTASE_PARSE_OK; + } + + return RTASE_PARSE_DROP; + } + + return RTASE_PARSE_OK; + + case htons(ETH_P_IPV6): + i6h = skb_header_pointer(skb, no, sizeof(_i6h), &_i6h); + if (!i6h) + return RTASE_PARSE_DROP; + + offset = no + sizeof(*i6h); + + if (i6h->nexthdr == IPPROTO_TCP) { + if (skb->len - offset < sizeof(struct tcphdr)) + return RTASE_PARSE_DROP; + + return RTASE_PARSE_SKIP; + } + + if (i6h->nexthdr != IPPROTO_UDP) + return RTASE_PARSE_SKIP; + + if (skb->len - offset < sizeof(struct udphdr)) + return RTASE_PARSE_DROP; + + *transport_offset = offset; + *transport_proto = IPPROTO_UDP; + + return RTASE_PARSE_OK; + + default: + return RTASE_PARSE_SKIP; + } +} + +static bool rtase_skb_pad(struct sk_buff *skb) +{ + enum rtase_parse_result ret; + u32 transport_offset; + __be16 *dest, _dest; + u32 trans_data_len; + u32 pad_to_len = 0; + u8 transport_proto; + u16 dest_port; + + ret = rtase_get_transport_offset(skb, &transport_offset, + &transport_proto, &pad_to_len); + if (ret == RTASE_PARSE_SKIP) { + return true; + } else if (ret == RTASE_PARSE_DROP) { + netdev_dbg(skb->dev, "drop malformed packet\n"); + return false; + } + + if (pad_to_len && + !rtase_pad_to_transport_len(skb, transport_offset, pad_to_len)) + return false; + + if (transport_proto != IPPROTO_UDP) + return true; + + trans_data_len = skb->len - transport_offset; + if (trans_data_len < offsetof(struct udphdr, len) || + trans_data_len >= RTASE_MIN_PAD_LEN) + return true; + + dest = skb_header_pointer(skb, + transport_offset + + offsetof(struct udphdr, dest), + sizeof(_dest), &_dest); + if (!dest) + return true; + + dest_port = ntohs(*dest); + if (dest_port != PTP_EV_PORT && dest_port != PTP_GEN_PORT) + return true; + + return rtase_pad_to_transport_len(skb, transport_offset, + RTASE_MIN_PAD_LEN); +} + static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb, u32 opts1, u32 opts2) { @@ -1362,6 +1556,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, opts2 |= rtase_tx_csum(skb, dev); } + if (!rtase_skb_pad(skb)) + goto err_dma_0; + frags = rtase_xmit_frags(ring, skb, opts1, opts2); if (unlikely(frags < 0)) goto err_dma_0; From fadaff3f66e124c3a62237f9c881819a8ac90309 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 13 Jul 2026 18:56:30 +0800 Subject: [PATCH 2790/3297] tcp: initialize standalone TCP-AO response padding commit e1a9d3cc11829c5414a75eb39c704f461936eb24 upstream. tcp_v4_send_ack() and tcp_v6_send_response() construct standalone TCP responses with TCP-AO options. The option length carries the actual MAC length, but the TCP header length includes the option rounded up to a four-byte boundary. tcp_ao_hash_hdr() writes the MAC only. Thus, when the MAC length is not four-byte aligned, the one to three bytes after the MAC are left uninitialized and may be transmitted. For the normal TCP-AO hashing mode, those bytes also have to be initialized before computing the MAC. Initialize only the alignment padding in the TCP-AO branches, before hashing the header. Use TCPOPT_NOP, as in the normal TCP-AO output path. This avoids adding work to non-AO TCP responses while preserving a valid authenticated header. Fixes: decde2586b34 ("net/tcp: Add TCP-AO sign to twsk") Fixes: da7dfaa6d6f7 ("net/tcp: Consistently align TCP-AO option in the header") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Suggested-by: Eric Dumazet Signed-off-by: Yizhou Zhao Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260713105631.8616-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_ipv4.c | 3 +++ net/ipv6/tcp_ipv6.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 7efb49f9e5b3b..3463e5b964a98 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -998,6 +998,9 @@ static void tcp_v4_send_ack(const struct sock *sk, key->rcv_next); arg.iov[0].iov_len += tcp_ao_len_aligned(key->ao_key); rep.th.doff = arg.iov[0].iov_len / 4; + memset((u8 *)&rep.opt[offset] + tcp_ao_maclen(key->ao_key), + TCPOPT_NOP, tcp_ao_len_aligned(key->ao_key) - + tcp_ao_len(key->ao_key)); tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset], key->ao_key, key->traffic_key, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ad9f69d49f2e3..952bf49efbc6a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -941,6 +941,8 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 (tcp_ao_len(key->ao_key) << 16) | (key->ao_key->sndid << 8) | (key->rcv_next)); + memset((u8 *)topt + tcp_ao_maclen(key->ao_key), TCPOPT_NOP, + tcp_ao_len_aligned(key->ao_key) - tcp_ao_len(key->ao_key)); tcp_ao_hash_hdr(AF_INET6, (char *)topt, key->ao_key, key->traffic_key, From 234f9ffbd9b2c1b24ec67200ea3cff07401bec48 Mon Sep 17 00:00:00 2001 From: Yuxiang Yang Date: Fri, 17 Jul 2026 08:14:42 +0000 Subject: [PATCH 2791/3297] tcp: challenge ACK for non-exact RST in SYN-RECEIVED commit a28c4fcbf774e23b4779cae468e3497a5ad1f4a1 upstream. The SYN-RECEIVED request-socket path in tcp_check_req() accepts an in-window RST without requiring SEG.SEQ to exactly match RCV.NXT. A non-exact RST therefore removes the request instead of eliciting a challenge ACK. RFC 9293 section 3.10.7.4 applies the RFC 5961 reset check in SYN-RECEIVED: an exact RST resets the connection, while a non-exact in-window RST must trigger a challenge ACK and be dropped. Apply that check before the ACK-field validation, following the RFC sequence-number, RST, then ACK processing order. Factor the per-netns challenge ACK quota out of tcp_send_challenge_ack() so request sockets can share it. Use the request socket's send_ack() callback and its own out-of-window ACK timestamp to send and rate-limit the response. Reported-by: Yuxiang Yang Reported-by: Yizhou Zhao Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Fixes: 282f23c6ee34 ("tcp: implement RFC 5961 3.2") Cc: stable@vger.kernel.org Signed-off-by: Yuxiang Yang Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260717081443.809393-2-yangyx22@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/net/tcp.h | 2 ++ net/ipv4/tcp_input.c | 56 ++++++++++++++++++++++++++++++---------- net/ipv4/tcp_minisocks.c | 12 ++++++++- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 31ac90230b12d..c9439e7f3e7b8 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1878,6 +1878,8 @@ static inline void tcp_fast_path_check(struct sock *sk) bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb, int mib_idx, u32 *last_oow_ack_time); +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req); static inline void tcp_mib_init(struct net *net) { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c650398e91998..8c74fbeb771d5 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3820,24 +3820,17 @@ static void tcp_send_ack_reflect_ect(struct sock *sk, bool accecn_reflector) __tcp_send_ack(sk, tp->rcv_nxt, flags); } -/* RFC 5961 7 [ACK Throttling] */ -static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) +/* Consume one slot from the per-netns RFC 5961 challenge ACK quota. + * Returns true if a challenge ACK may be sent. + */ +static bool tcp_challenge_ack_allowed(struct net *net) { - struct tcp_sock *tp = tcp_sk(sk); - struct net *net = sock_net(sk); u32 count, now, ack_limit; - /* First check our per-socket dupack rate limit. */ - if (__tcp_oow_rate_limited(net, - LINUX_MIB_TCPACKSKIPPEDCHALLENGE, - &tp->last_oow_ack_time)) - return; - ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit); if (ack_limit == INT_MAX) - goto send_ack; + return true; - /* Then check host-wide RFC 5961 rate limit. */ now = jiffies / HZ; if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) { u32 half = (ack_limit + 1) >> 1; @@ -3849,12 +3842,49 @@ static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) count = READ_ONCE(net->ipv4.tcp_challenge_count); if (count > 0) { WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1); -send_ack: + return true; + } + return false; +} + +/* RFC 5961 7 [ACK Throttling] */ +static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); + + /* First check our per-socket dupack rate limit. */ + if (__tcp_oow_rate_limited(net, + LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tp->last_oow_ack_time)) + return; + + /* Then check the per-netns RFC 5961 rate limit. */ + if (tcp_challenge_ack_allowed(net)) { NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); tcp_send_ack_reflect_ect(sk, accecn_reflector); } } +/* Send a challenge ACK from a SYN-RECEIVED request socket. Uses + * __tcp_oow_rate_limited() directly so that an RST carrying payload + * cannot bypass the per-request rate limit. + */ +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +{ + struct net *net = sock_net(sk); + + if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tcp_rsk(req)->last_oow_ack_time)) + return; + + if (tcp_challenge_ack_allowed(net)) { + NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); + req->rsk_ops->send_ack(sk, skb, req); + } +} + static void tcp_store_ts_recent(struct tcp_sock *tp) { tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index c70c29a3a0905..4ac30573f233f 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -822,7 +822,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * elsewhere and is checked directly against the child socket rather * than req because user data may have been sent out. */ - if ((flg & TCP_FLAG_ACK) && !fastopen && + if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen && (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1)) return sk; @@ -861,6 +861,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, flg &= ~TCP_FLAG_SYN; } + /* RFC 5961 section 3.2, as clarified by RFC 9293 section + * 3.10.7.4, requires a challenge ACK for a non-exact + * in-window RST in SYN-RECEIVED. + */ + if ((flg & TCP_FLAG_RST) && + TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) { + tcp_reqsk_send_challenge_ack(sk, skb, req); + return NULL; + } + /* RFC793: "second check the RST bit" and * "fourth, check the SYN bit" */ From 5f5a41a48dbf9eda57b67ce23e548602cf7195a6 Mon Sep 17 00:00:00 2001 From: Daehyeon Ko <4ncienth@gmail.com> Date: Tue, 14 Jul 2026 22:19:39 +0900 Subject: [PATCH 2792/3297] tipc: clear sock->sk on the failed-insert path in tipc_sk_create() commit ba0533fc163f905fe817cfabdf8ed4058da44800 upstream. When tipc_sk_create() fails to insert the new socket (tipc_sk_insert() returns non-zero), its error path frees the sk with sk_free() but leaves sock->sk pointing at the freed object: if (tipc_sk_insert(tsk)) { sk_free(sk); pr_warn("Socket create failed; port number exhausted\n"); return -EINVAL; } This is harmless for plain socket(): the syscall layer clears sock->ops before releasing, so tipc_release() is never called. It is not harmless on the accept() path. tipc_accept() creates the pre-allocated child socket with tipc_sk_create(net, new_sock, 0, kern); on failure it leaves new_sock->sk dangling and new_sock->ops non-NULL, and do_accept() then fput()s the new file, so __sock_release() -> tipc_release() runs lock_sock(new_sock->sk) on the freed sk -- a use-after-free write of the sk_lock spinlock. tipc_release() already guards this exact "failed accept() releases a pre-allocated child" case with "if (sk == NULL) return 0;", but the guard is bypassed because tipc_sk_create() left sock->sk non-NULL (dangling) rather than NULL. Clear sock->sk on the failed-insert path so the existing tipc_release() NULL check fires and the use-after-free is avoided. The tipc_sk_insert() failure is reached when the per-netns socket rhashtable hits its max_size (tsk_rht_params.max_size = 1048576, ~2M elements) -- i.e. once a netns holds ~2M TIPC sockets every insert returns -E2BIG. BUG: KASAN: slab-use-after-free in lock_sock_nested (net/core/sock.c:3839) Write of size 8 at addr ffff8880047cdc38 by task init/1 lock_sock_nested (net/core/sock.c:3839) tipc_release (net/tipc/socket.c:638) __sock_release (net/socket.c:710) sock_close (net/socket.c:1501) __fput (fs/file_table.c:512) Allocated by task 1: sk_alloc (net/core/sock.c:2308) tipc_sk_create (net/tipc/socket.c:487) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034) Freed by task 1: __sk_destruct (net/core/sock.c:2391) tipc_sk_create (net/tipc/socket.c:504) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034) Fixes: 00aff3590fc0 ("net: tipc: fix possible refcount leak in tipc_sk_create()") Cc: stable@vger.kernel.org Reviewed-by: Tung Nguyen Reviewed-by: Breno Leitao Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260714131939.1255974-1-4ncienth@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/tipc/socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 677e17ec2939b..24013455fbf0e 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -501,6 +501,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, tipc_set_sk_state(sk, TIPC_OPEN); if (tipc_sk_insert(tsk)) { sk_free(sk); + sock->sk = NULL; pr_warn("Socket create failed; port number exhausted\n"); return -EINVAL; } From f60bac115d8dcac50a05df993eaf1dd758f98ed1 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 8 Jul 2026 12:29:03 +0200 Subject: [PATCH 2793/3297] vsock/virtio: collapse receive queue under memory pressure commit 2a12c05aef213ff304ecc9e2f351de20731946b8 upstream. When many small packets accumulate in the receive queue, the skb overhead can exceed buf_alloc even while the payload is within bounds. This causes virtio_transport_inc_rx_pkt() to reject packets, leading to connection resets during large transfers under backpressure. The issue was reported by Brien, who has a reproducer, but it is also easily reproducible with iperf-vsock [1] using a small packet size: iperf3 --vsock -c $CID -l 129 which fails immediately without this patch but with commit 059b7dbd20a6 ("vsock/virtio: fix potential unbounded skb queue"). Inspired by TCP's tcp_collapse() which solves a similar problem, add virtio_transport_collapse_rx_queue() that walks the receive queue and re-copies data into compact linear skbs to reduce the overhead. The collapse is triggered proactively from when the number of skb queued is close to exceeding the overhead budget. A pre-scan counts the eligible bytes to size each allocation precisely, avoiding waste for isolated small packets. Partially consumed skbs are kept as-is to preserve buf_used/fwd_cnt accounting, EOM-marked skbs to maintain SEQPACKET message boundaries, and skbs already larger than the collapse target because they already have a good data-to-overhead ratio. Walking a large queue may take a significant amount of time and cache misses, causing traffic burstiness. To limit this, the collapse stops once enough room is freed for this packet and the next one, but may opportunistically free more to fill each collapsed skb to capacity. [1] https://github.com/stefano-garzarella/iperf-vsock Fixes: 059b7dbd20a6 ("vsock/virtio: fix potential unbounded skb queue") Cc: stable@vger.kernel.org Reported-by: Brien Oberstein Closes: https://lore.kernel.org/netdev/618701dd023e$063de350$12b9a9f0$@gmail.com/ Tested-by: Brien Oberstein Signed-off-by: Stefano Garzarella Acked-by: Michael S. Tsirkin Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/20260708102904.50732-2-sgarzare@redhat.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/vmw_vsock/virtio_transport_common.c | 165 +++++++++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index d93735dbe3a25..518ef9fca33d0 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -26,6 +26,13 @@ /* Threshold for detecting small packets to copy */ #define GOOD_COPY_LEN 128 +/* Max payload that can be collapsed into a single linear skb, using the same + * allocation threshold as virtio_vsock_alloc_skb() to avoid adding pressure + * on the page allocator. + */ +#define MAX_COLLAPSE_LEN \ + SKB_MAX_ORDER(VIRTIO_VSOCK_SKB_HEADROOM, PAGE_ALLOC_COSTLY_ORDER) + static void virtio_transport_cancel_close_work(struct vsock_sock *vsk, bool cancel_timeout); static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs); @@ -422,6 +429,145 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, return ret; } +static bool virtio_transport_can_collapse(struct sk_buff *skb) +{ + /* skbs that are partially consumed, mark a SEQPACKET message boundary, + * or are already large enough should not be collapsed: they either + * need special accounting, carry protocol state, or already have a + * good data-to-overhead ratio. + */ + if (VIRTIO_VSOCK_SKB_CB(skb)->offset) + return false; + if (le32_to_cpu(virtio_vsock_hdr(skb)->flags) & VIRTIO_VSOCK_SEQ_EOM) + return false; + if (skb->len >= MAX_COLLAPSE_LEN) + return false; + return true; +} + +/* Iterate through the packets in the queue starting from the current skb to + * count the number of bytes we can collapse. + */ +static unsigned int +virtio_transport_collapse_size(struct sk_buff *skb, struct sk_buff_head *queue) +{ + unsigned int target = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset; + + while ((skb = skb_peek_next(skb, queue)) && + virtio_transport_can_collapse(skb)) { + unsigned int len = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset; + + if (len > MAX_COLLAPSE_LEN - target) + return target; + + target += len; + } + + return target; +} + +/* Called under lock_sock to compact the receive queue by merging small skbs. + * @min_to_free: minimum number of skbs to eliminate from the queue. May free + * more to fill each collapsed skb to capacity. + */ +static void +virtio_transport_collapse_rx_queue(struct virtio_vsock_sock *vvs, + u32 min_to_free) +{ + struct sk_buff *skb, *next_skb, *new_skb = NULL; + struct sk_buff_head new_queue; + u32 saved = 0; + + __skb_queue_head_init(&new_queue); + + skb_queue_walk_safe(&vvs->rx_queue, skb, next_skb) { + struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb); + u32 src_off = VIRTIO_VSOCK_SKB_CB(skb)->offset; + u32 src_len = skb->len - src_off; + bool keep; + + keep = !virtio_transport_can_collapse(skb); + if (keep) { + /* Finalize pending collapsed skb to preserve packet + * ordering. + */ + if (new_skb) { + __skb_queue_tail(&new_queue, new_skb); + new_skb = NULL; + saved--; + } + goto next; + } + + /* Finalize if this packet won't fit in the remaining tailroom, + * so we can allocate a right-sized new_skb. + */ + if (new_skb && src_len > skb_tailroom(new_skb)) { + __skb_queue_tail(&new_queue, new_skb); + new_skb = NULL; + saved--; + } + + if (!new_skb) { + unsigned int alloc_size; + + /* Check after finalizing to opportunistically fill + * each collapsed skb to capacity, merging more skbs + * than strictly required. + */ + if (saved >= min_to_free) + break; + + alloc_size = virtio_transport_collapse_size(skb, &vvs->rx_queue); + + /* Only this skb's data is eligible, nothing to merge + * with. Keep as-is. + */ + if (alloc_size <= src_len) { + keep = true; + goto next; + } + + new_skb = virtio_vsock_alloc_linear_skb(alloc_size + + VIRTIO_VSOCK_SKB_HEADROOM, GFP_KERNEL); + if (!new_skb) + break; + + memcpy(virtio_vsock_hdr(new_skb), hdr, + sizeof(struct virtio_vsock_hdr)); + virtio_vsock_hdr(new_skb)->len = 0; + } + + /* Cannot fail since src_off/src_len are within bounds, but if + * it does, discard new_skb to avoid queuing corrupted data. + */ + if (WARN_ON_ONCE(skb_copy_bits(skb, src_off, + skb_put(new_skb, src_len), + src_len))) { + kfree_skb(new_skb); + new_skb = NULL; + break; + } + + le32_add_cpu(&virtio_vsock_hdr(new_skb)->len, src_len); + virtio_vsock_hdr(new_skb)->flags |= hdr->flags; + +next: + __skb_unlink(skb, &vvs->rx_queue); + if (keep) { + __skb_queue_tail(&new_queue, skb); + } else { + consume_skb(skb); + saved++; + } + } + + if (new_skb) + __skb_queue_tail(&new_queue, new_skb); + + skb_queue_splice(&new_queue, &vvs->rx_queue); +} + static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs, u32 len) { @@ -1341,12 +1487,29 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk, { struct virtio_vsock_sock *vvs = vsk->trans; bool can_enqueue, free_pkt = false; + u32 len, queue_max, queue_len; struct virtio_vsock_hdr *hdr; - u32 len; hdr = virtio_vsock_hdr(skb); len = le32_to_cpu(hdr->len); + /* virtio_transport_inc_rx_pkt() rejects packets when the per-skb + * overhead (skb_queue_len * SKB_TRUESIZE(0)) exceeds buf_alloc. + * Proactively collapse the queue before that happens. + * No rx_lock needed: lock_sock is held by caller, preventing + * concurrent enqueue or dequeue. + */ + queue_max = vvs->buf_alloc / SKB_TRUESIZE(0); + queue_len = skb_queue_len(&vvs->rx_queue); + if (queue_len >= queue_max) { + /* Walking a large queue may take a significant amount of time + * and cache misses, causing traffic burstiness. Limit the + * collapse to freeing room for this packet and the next one. + * It may free more to fill each collapsed skb to capacity. + */ + virtio_transport_collapse_rx_queue(vvs, queue_len + 2 - queue_max); + } + spin_lock_bh(&vvs->rx_lock); can_enqueue = virtio_transport_inc_rx_pkt(vvs, len); From 79370b573e92e8f190eb5f9a511fa5398340d8b2 Mon Sep 17 00:00:00 2001 From: James Raphael Tiovalen Date: Tue, 21 Jul 2026 00:04:24 +0800 Subject: [PATCH 2794/3297] vxlan: mdb: Fix source list corruption on a failed replace commit dcd9b465965422b9654f6026e8a2fa8984f74c3c upstream. When replacing the source list of an MDB remote entry, all existing sources are first marked for deletion and vxlan_mdb_remote_srcs_add() is then called to add the new source list. Sources present in the new list have their deletion mark cleared, and any sources left marked afterwards are removed. If vxlan_mdb_remote_srcs_add() fails partway through, its error path deletes all entries on the remote's source list. That rollback is only correct for its other caller, vxlan_mdb_remote_add(), where the remote was just allocated and the list contains solely entries added during the call. On the replace path the list also holds pre-existing sources, so a failed replace tears them down together with their (S, G) forwarding entries instead of leaving the entry unchanged. This is reachable from an existing (*, G) remote. An EXCLUDE filter that loses sources starts forwarding traffic that should be blocked, while an INCLUDE filter that loses sources drops traffic that should be forwarded. Mark entries created during the current pass with a new VXLAN_SGRP_F_NEW flag. On failure, delete only those entries and clear the deletion mark on the pre-existing ones, so a failed replace leaves the source list untouched. Retain the flag until the whole operation succeeds and then clear it. Also stop vxlan_mdb_remote_src_add() from deleting a pre-existing entry it only looked up when adding that entry's forwarding entry fails. Fixes: a3a48de5eade ("vxlan: mdb: Add MDB control path support") Cc: stable@vger.kernel.org Signed-off-by: James Raphael Tiovalen Reviewed-by: Ido Schimmel Reviewed-by: Antoine Tenart Reviewed-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260720160428.249356-1-jamestiotio@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/vxlan/vxlan_mdb.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 816ab1aa05262..4e02a669d045a 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -42,6 +42,7 @@ struct vxlan_mdb_remote { }; #define VXLAN_SGRP_F_DELETE BIT(0) +#define VXLAN_SGRP_F_NEW BIT(1) struct vxlan_mdb_src_entry { struct hlist_node node; @@ -844,6 +845,7 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg, ent = vxlan_mdb_remote_src_entry_add(remote, &src->addr); if (!ent) return -ENOMEM; + ent->flags |= VXLAN_SGRP_F_NEW; } else if (!(cfg->nlflags & NLM_F_REPLACE)) { NL_SET_ERR_MSG_MOD(extack, "Source entry already exists"); return -EEXIST; @@ -853,15 +855,16 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg, if (err) goto err_src_del; - /* Clear flags in case source entry was marked for deletion as part of - * replace flow. + /* Clear the deletion mark so the entry survives the replace sweep. + * The new mark is retained until the whole operation succeeds. */ - ent->flags = 0; + ent->flags &= ~VXLAN_SGRP_F_DELETE; return 0; err_src_del: - vxlan_mdb_remote_src_entry_del(ent); + if (ent->flags & VXLAN_SGRP_F_NEW) + vxlan_mdb_remote_src_entry_del(ent); return err; } @@ -889,11 +892,19 @@ static int vxlan_mdb_remote_srcs_add(const struct vxlan_mdb_config *cfg, goto err_src_del; } + hlist_for_each_entry(ent, &remote->src_list, node) + ent->flags &= ~VXLAN_SGRP_F_NEW; + return 0; err_src_del: - hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) - vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, ent); + hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) { + if (ent->flags & VXLAN_SGRP_F_NEW) + vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, + ent); + else + ent->flags &= ~VXLAN_SGRP_F_DELETE; + } return err; } @@ -1069,7 +1080,7 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg, err = vxlan_mdb_remote_srcs_add(cfg, remote, extack); if (err) - goto err_clear_delete; + return err; hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) { if (ent->flags & VXLAN_SGRP_F_DELETE) @@ -1078,11 +1089,6 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg, } return 0; - -err_clear_delete: - hlist_for_each_entry(ent, &remote->src_list, node) - ent->flags &= ~VXLAN_SGRP_F_DELETE; - return err; } static int vxlan_mdb_remote_replace(const struct vxlan_mdb_config *cfg, From 7b263cf1dd4c0a034bc6bee2e276e1631635c595 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 4 May 2026 19:44:09 -0700 Subject: [PATCH 2795/3297] watchdog: s32g_wdt: remove incorrect options in watchdog_info struct commit 2b37415618bfc6a83d4aceb00fd8d6491096f2ed upstream. The s32g_wdt driver uses two incorrect constants in the options field of its watchdog_info struct. This bit mask should contain WDIOF_* constants, but the driver uses two WDIOC_* ioctl constants (in addition to correct WDIOF_* constants). This causes many incorrect bits to be set in the bit mask. The functionality indicated by these ioctl constants is supported by all drivers using the watchdog framework, so this patch simply removes them. Fixes: bd3f54ec559b ("watchdog: Add the Watchdog Timer for the NXP S32 platform") Cc: stable@vger.kernel.org # 6.18+ Signed-off-by: Ethan Nelson-Moore Acked-by: Daniel Lezcano Link: https://lore.kernel.org/r/20260505024409.60301-1-enelsonmoore@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/s32g_wdt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/watchdog/s32g_wdt.c b/drivers/watchdog/s32g_wdt.c index ad55063060af6..6422a694fc65f 100644 --- a/drivers/watchdog/s32g_wdt.c +++ b/drivers/watchdog/s32g_wdt.c @@ -56,8 +56,7 @@ MODULE_PARM_DESC(early_enable, static const struct watchdog_info s32g_wdt_info = { .identity = "s32g watchdog", - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | - WDIOC_GETTIMEOUT | WDIOC_GETTIMELEFT, + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; static struct s32g_wdt_device *wdd_to_s32g_wdt(struct watchdog_device *wdd) From ada47af5c215efbf5b6f13b3830f14f559320684 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 18 Jun 2026 12:54:14 +0800 Subject: [PATCH 2796/3297] drm/amd/pm: fix amdgpu_pm_info power display units commit 238baca26a6279e688d1a156bd031390b82eb578 upstream. amdgpu_pm_info displayed power sensor readings with the wrong fractional unit. It treated the low byte of the raw sensor value as the decimal part of watts, while that field represents milliwatts in the decoded value. As a result, debugfs could report misleading SoC power when the remainder was not already a two-digit centiwatt value. Example with query = 0x00000354: raw field value --------------------- query >> 8 3 W query & 0xff 84 mW decoded power 3084 mW output value --------------------- before 3.84 W after 3.08 W Fixes: f0b8f65b4825 ("drm/amd/amdgpu: fix the GPU power print error in pm info") Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 01992b121fb652c753d37e0c1427a2d1a557d2b1) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 0d72118a12b4c..ebf7761248172 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -40,6 +40,8 @@ #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) +#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff)) + struct od_attribute { struct kobj_attribute attribute; struct list_head entry; @@ -3261,7 +3263,6 @@ static int amdgpu_hwmon_get_power(struct device *dev, enum amd_pp_sensors sensor) { struct amdgpu_device *adev = dev_get_drvdata(dev); - unsigned int uw; u32 query = 0; int r; @@ -3270,9 +3271,7 @@ static int amdgpu_hwmon_get_power(struct device *dev, return r; /* convert to microwatts */ - uw = (query >> 8) * 1000000 + (query & 0xff) * 1000; - - return uw; + return power_2_mwatt(query) * 1000; } static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev, @@ -4804,7 +4803,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a { uint32_t mp1_ver = amdgpu_ip_version(adev, MP1_HWIP, 0); uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0); - uint32_t value; + uint32_t value, mwatt, centiwatt; uint64_t value64 = 0; uint32_t query = 0; int size; @@ -4829,17 +4828,21 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a seq_printf(m, "\t%u mV (VDDNB)\n", value); size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { + mwatt = power_2_mwatt(query); + centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100); else - seq_printf(m, "\t%u.%02u W (average SoC)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (average SoC)\n", centiwatt / 100, centiwatt % 100); } size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { + mwatt = power_2_mwatt(query); + centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100); else - seq_printf(m, "\t%u.%02u W (current SoC)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (current SoC)\n", centiwatt / 100, centiwatt % 100); } size = sizeof(value); seq_printf(m, "\n"); From e75f71143b68bda01b641ad4e45d090fe4745618 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 12 Jun 2026 10:55:09 +0800 Subject: [PATCH 2797/3297] drm/amd/pm: make pp_features read-only when scpm is enabled commit 53c78ab388bfc1a4d72e756815d0db0a842c812e upstream. SCPM owns power feature control when enabled. Make pp_features read-only during sysfs setup by clearing its write bits and store callback. Signed-off-by: Yang Wang Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 6a5786e191fdce36c5db170e5209cf609e8f0087) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index ebf7761248172..7c9e2003e2a04 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -2619,6 +2619,11 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ gc_ver != IP_VERSION(9, 4, 3)) || gc_ver < IP_VERSION(9, 0, 0)) *states = ATTR_STATE_UNSUPPORTED; + + if (adev->scpm_enabled) { + dev_attr->attr.mode &= ~S_IWUGO; + dev_attr->store = NULL; + } } else if (DEVICE_ATTR_IS(gpu_metrics)) { if (gc_ver < IP_VERSION(9, 1, 0)) *states = ATTR_STATE_UNSUPPORTED; From 7e22de67e545d0f72595514d3a66675e9d074adc Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:19:52 -0400 Subject: [PATCH 2798/3297] drm/amdgpu/gfx10: replace BUG_ON() with WARN_ON() commit d06c4173a7c38c7a39e98859f839ce714c7af2c9 upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit ac6f00beb658239bced4aaed9efbb04a35348d48) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index fdd55609c677e..b8cc213b347cc 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -4022,7 +4022,7 @@ static void gfx_v10_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -8674,7 +8674,7 @@ static void gfx_v10_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -8709,7 +8709,7 @@ static void gfx_v10_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -8742,9 +8742,9 @@ static void gfx_v10_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -8792,9 +8792,6 @@ static void gfx_v10_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From dfd9bf09fd8fe81f113a5c7e88bfd99f2499542f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:20:55 -0400 Subject: [PATCH 2799/3297] drm/amdgpu/gfx11: replace BUG_ON() with WARN_ON() commit 0eebcab1ea2a77f086a04108f386f82ee3496022 upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit daa62107452d2451787c4248ca38fa2d1a0cbefd) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index cf23b5da6dbbb..f428b652db338 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -537,7 +537,7 @@ static void gfx_v11_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -5901,7 +5901,7 @@ static void gfx_v11_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5936,7 +5936,7 @@ static void gfx_v11_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5969,9 +5969,9 @@ static void gfx_v11_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -6025,9 +6025,6 @@ static void gfx_v11_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From 987bedd3ea89d747d1c5ab708ce3293e2f033b6c Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:21:58 -0400 Subject: [PATCH 2800/3297] drm/amdgpu/gfx12: replace BUG_ON() with WARN_ON() commit cd3b3efa1ced05528d9128755338baa62a6b562d upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit f952076f76d62f783e8ba4995a7c400d39354ccf) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 50e39b9d9df6f..b91e812768778 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -439,7 +439,7 @@ static void gfx_v12_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -4424,7 +4424,7 @@ static void gfx_v12_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, control |= ib->length_dw | (vmid << 24); amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -4443,7 +4443,7 @@ static void gfx_v12_0_ring_emit_ib_compute(struct amdgpu_ring *ring, u32 control = INDIRECT_BUFFER_VALID | ib->length_dw | (vmid << 24); amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -4474,9 +4474,9 @@ static void gfx_v12_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -4524,9 +4524,6 @@ static void gfx_v12_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From f70bd5235d9efc2ee2f70293eea51888c5f2a54d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:17:59 -0400 Subject: [PATCH 2801/3297] drm/amdgpu/gfx8: drop unecessary BUG_ON() commit 84a1a8a952ab4b8c23c5dd1f2eea4049cb4914f5 upstream. There's no need to crash the kernel for this case. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 4d7c25208ca612b754f3bf39e9f16e725b828891) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 8a81713d97aac..690128ad3094a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -6226,9 +6226,6 @@ static void gfx_v8_0_ring_emit_fence_compute(struct amdgpu_ring *ring, static void gfx_v8_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, u64 seq, unsigned int flags) { - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From 05aea3344c422fe95299bb1b21a04de30c7ea198 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:42:35 -0400 Subject: [PATCH 2802/3297] drm/amdgpu/gfx9.4.3: replace BUG_ON() with WARN_ON() commit 00f4050f7c367d7bdce347ca279ce467c434cf15 upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit 5676593d08998d7a6d9e2d51d6b54b3820e3755c) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index a4ebb6c5af557..314916bd2368f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -405,7 +405,7 @@ static void gfx_v9_4_3_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -2871,7 +2871,7 @@ static void gfx_v9_4_3_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -2905,9 +2905,9 @@ static void gfx_v9_4_3_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -2967,9 +2967,6 @@ static void gfx_v9_4_3_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | From 042c047e8bc9c9ada7574028a8e4592102e2e1fd Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:14:59 -0400 Subject: [PATCH 2803/3297] drm/amdgpu/gfx9: replace BUG_ON() with WARN_ON() commit 6302be10b521f5106ce01eb5a724b9e7945a5061 upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit b71604f8685b0eba07866f4e8dc30f93e1931054) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 2c1108848fe7b..c5549a5abcd43 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -1183,7 +1183,7 @@ static void gfx_v9_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -5473,7 +5473,7 @@ static void gfx_v9_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5569,7 +5569,7 @@ static void gfx_v9_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5610,9 +5610,9 @@ static void gfx_v9_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); From 058373af59551b5b55d075b1baec1546f342b26e Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Fri, 26 Jun 2026 10:39:26 -0400 Subject: [PATCH 2804/3297] drm/amdgpu/jpeg: fix jpeg_v4_0_3_is_idle detection commit c44af3810fc8b3adf6910a332038aa566560c8fa upstream. jpeg_v4_0_3_is_idle() initializes ret to false and then accumulates ring idle status using &=. Since false & condition always remains false, the function can never report the JPEG block as idle. Initialize ret to true so the function returns true only when all JPEG rings report RB_JOB_DONE. Signed-off-by: Boyuan Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit e9df8e9d04e0593d17ddb069f3b7958991cd18c9) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c index 99e6dabc36ee8..4521465506682 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c @@ -1010,7 +1010,7 @@ void jpeg_v4_0_3_dec_ring_nop(struct amdgpu_ring *ring, uint32_t count) static bool jpeg_v4_0_3_is_idle(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - bool ret = false; + bool ret = true; int i, j; for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { From 76c977d396f12a460a9cd71e3b55c0952a070eb4 Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Wed, 24 Jun 2026 09:50:01 -0400 Subject: [PATCH 2805/3297] drm/amdgpu/jpeg: fix jpeg_v5_0_1_is_idle detection commit efcedeececcf995fcf717b21e39aa7c446fa3bf7 upstream. jpeg_v5_0_1_is_idle() initializes ret to false and then accumulates ring idle status using &=. Since false & condition always remains false, the function can never report the JPEG block as idle. Initialize ret to true so the function returns true only when all JPEG rings report RB_JOB_DONE. Signed-off-by: Boyuan Zhang Reviewed-by: David (Ming Qiang) Wu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 680adf5faeeabb4585f7aeb53681719e2d6c2f41) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c index 1fb2386352ccb..de9ca229479e8 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c @@ -657,7 +657,7 @@ static void jpeg_v5_0_1_dec_ring_set_wptr(struct amdgpu_ring *ring) static bool jpeg_v5_0_1_is_idle(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - bool ret = false; + bool ret = true; int i, j; for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { From dc3f5da1ba8e280d31676ce15b937e4302235b03 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 15 Jun 2026 18:44:11 -0400 Subject: [PATCH 2806/3297] drm/amdgpu/sdma4.4.2: replace BUG_ON() with WARN_ON() commit 40cdbe9fa424cc6264a7aed93a04bd7d69109d9e upstream. There's no need to crash the kernel for these cases. Reviewed-by: Vitaly Prosyak Signed-off-by: Alex Deucher (cherry picked from commit fa4f86a148271e325e95287630a3a15a9cd35fdc) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index a1443990d5c60..c8b4fda590168 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -458,7 +458,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 /* write the fence */ amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -468,7 +468,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 addr += 4; amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); From f7e9eeaccca54d1ee848bff4fb4374de8d0b0611 Mon Sep 17 00:00:00 2001 From: Jakob Linke Date: Wed, 17 Jun 2026 08:24:15 +0200 Subject: [PATCH 2807/3297] drm/amdgpu/soc24: reset dGPU if suspend got aborted commit aff079bdce65f6d085e4b0091fdf87fffa95b0d9 upstream. For SOC24 ASICs (RDNA4 / Navi 4x dGPUs) re-enabling PM features fails if an S3 suspend got aborted, the same issue already handled for SOC21 and SOC15: commit df3c7dc5c58b ("drm/amdgpu: Reset dGPU if suspend got aborted") commit 38e8ca3e4b6d ("amdgpu/soc15: enable asic reset for dGPU in case of suspend abort") The aborted resume fails with: amdgpu: SMU: No response msg_reg: 6 resp_reg: 0 amdgpu: Failed to enable requested dpm features! amdgpu: resume of IP block failed -62 Apply the same workaround for soc24: detect the aborted-suspend state at resume via the sign-of-life register and reset the device before re-init. This is a workaround till a proper solution is finalized. Fixes: 98b912c50e44 ("drm/amdgpu: Add soc24 common ip block (v2)") Signed-off-by: Jakob Linke Signed-off-by: Alex Deucher (cherry picked from commit fed5bdbfe1d4a19a26c70f7fc58017dc88be1c18) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/soc24.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/soc24.c b/drivers/gpu/drm/amd/amdgpu/soc24.c index 972b449ab89fa..999d9da6756db 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc24.c +++ b/drivers/gpu/drm/amd/amdgpu/soc24.c @@ -526,8 +526,36 @@ static int soc24_common_suspend(struct amdgpu_ip_block *ip_block) return soc24_common_hw_fini(ip_block); } +static bool soc24_need_reset_on_resume(struct amdgpu_device *adev) +{ + u32 sol_reg1, sol_reg2; + + /* Will reset for the following suspend abort cases. + * 1) Only reset dGPU side. + * 2) S3 suspend got aborted and TOS is active. + * As for dGPU suspend abort cases the SOL value + * will be kept as zero at this resume point. + */ + if (!(adev->flags & AMD_IS_APU) && adev->in_s3) { + sol_reg1 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81); + msleep(100); + sol_reg2 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81); + + return (sol_reg1 != sol_reg2); + } + + return false; +} + static int soc24_common_resume(struct amdgpu_ip_block *ip_block) { + struct amdgpu_device *adev = ip_block->adev; + + if (soc24_need_reset_on_resume(adev)) { + dev_info(adev->dev, "S3 suspend aborted, resetting..."); + soc24_asic_reset(adev); + } + return soc24_common_hw_init(ip_block); } From 7eebef042c12dfe0568593ee6a8926d16505925e Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Mon, 25 May 2026 11:34:27 -0400 Subject: [PATCH 2808/3297] drm/amdgpu/vce: fix integer overflow in image size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 186bfdc4e26d019b2e7570cb121964a1d89b2e5b upstream. Fix a security vulnerability where malicious VCE command streams with oversized dimensions (e.g. 65536×65536) cause 32-bit integer overflow, wrapping the calculated buffer size to 0. This bypasses validation and allows GPU firmware to perform out-of-bound memory access. The fix uses 64-bit arithmetic to detect overflow and rejects invalid dimensions before they reach the hardware. V2: remove redundant check V3: modify max height value V4: remove size64 Signed-off-by: Boyuan Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit cbe408dba581755ad1279a487ec786d8927d778d) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index 3b9a9010ed354..3dc2b7f91e288 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -853,9 +853,20 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, goto out; } - *size = amdgpu_ib_get_value(ib, idx + 8) * - amdgpu_ib_get_value(ib, idx + 10) * - 8 * 3 / 2; + uint32_t width, height; + width = amdgpu_ib_get_value(ib, idx + 8); + height = amdgpu_ib_get_value(ib, idx + 10); + + if (width == 0 || height == 0 || + width > 4096 || height > 2304) { + DRM_ERROR("invalid VCE image size: %ux%u\n", + width, height); + r = -EINVAL; + goto out; + } + + *size = width * height * 8 * 3 / 2; + break; case 0x04000001: /* config extension */ From bd868c077f67589ed2a714307ceaade5f246e302 Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Thu, 21 May 2026 09:59:37 -0400 Subject: [PATCH 2809/3297] drm/amdgpu/vcn4: avoid rereading IB param length commit 3b4082fabc67c9780b06eb959e59dd92fa79c0f0 upstream. Reuse the parameter length returned by vcn_v4_0_enc_find_ib_param() instead of rereading it from the IB. This avoids a potential TOCTOU issue if the IB contents change between reads. Signed-off-by: Boyuan Zhang Reviewed-by: David Rosca Signed-off-by: Alex Deucher (cherry picked from commit dbb02b4755f8c1f3773263f2d779872c1c0c073a) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index 21e40eb2078d1..ad74edc1bbfc3 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -1926,14 +1926,17 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, #define RENCODE_IB_PARAM_SESSION_INIT 0x00000003 /* return the offset in ib if id is found, -1 otherwise */ -static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start) +static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start, uint32_t *length) { int i; uint32_t len; for (i = start; (len = amdgpu_ib_get_value(ib, i)) >= 8; i += len / 4) { - if (amdgpu_ib_get_value(ib, i + 1) == id) + if (amdgpu_ib_get_value(ib, i + 1) == id) { + if (length) + *length = len; return i; + } } return -1; } @@ -1943,14 +1946,14 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, struct amdgpu_ib *ib) { struct amdgpu_ring *ring = amdgpu_job_ring(job); - uint32_t val; + uint32_t val, len; int idx = 0, sidx; /* The first instance can decode anything */ if (!ring->me) return 0; - while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx)) >= 0) { + while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx, &len)) >= 0) { val = amdgpu_ib_get_value(ib, idx + 2); /* RADEON_VCN_ENGINE_TYPE */ if (val == RADEON_VCN_ENGINE_TYPE_DECODE) { uint32_t valid_buf_flag = amdgpu_ib_get_value(ib, idx + 6); @@ -1963,12 +1966,12 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, amdgpu_ib_get_value(ib, idx + 8); return vcn_v4_0_dec_msg(p, job, msg_buffer_addr); } else if (val == RADEON_VCN_ENGINE_TYPE_ENCODE) { - sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx); + sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx, NULL); if (sidx >= 0 && amdgpu_ib_get_value(ib, sidx + 2) == RENCODE_ENCODE_STANDARD_AV1) return vcn_v4_0_limit_sched(p, job); } - idx += amdgpu_ib_get_value(ib, idx) / 4; + idx += len / 4; } return 0; } From 8c6d84a54823cd839e6ce22af559925f1320c310 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 22 Jun 2026 17:03:58 +0300 Subject: [PATCH 2810/3297] drm/dp_mst: Handle torn-down topology gracefully in drm_dp_mst_topology_queue_probe() commit 613059875958e7b217b250ed14c3b189f9488421 upstream. A hotplug or link-loss event can tear down the MST topology (setting mgr->mst_state = false and mgr->mst_primary = NULL) concurrently with a caller invoking drm_dp_mst_topology_queue_probe(). Since the check is already performed under mgr->lock, the condition is not a programming error but a valid race -- the topology was valid when the caller decided to call this function, but was torn down before the lock was acquired. Replace the drm_WARN_ON() with a graceful early return. This eliminates spurious kernel warnings and the resulting compositor crashes observed when connecting/disconnecting DP MST monitors, while keeping the correct behavior of doing nothing when MST is not active. A drm_dbg_mst() trace is added so the skipped probe remains observable under MST debug logging. The existing WARN_ON(mgr->mst_primary) in drm_dp_mst_topology_mgr_set_mst() already catches the case where the topology is initialized twice, so no diagnostic coverage is lost. Fixes: dbaeef363ea5 ("drm/dp_mst: Add a helper to queue a topology probe") Cc: Imre Deak Cc: Lyude Paul Cc: stable@vger.kernel.org Cc: intel-gfx@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Signed-off-by: Jonas Emilsson Signed-off-by: Luca Coelho Link: https://lore.kernel.org/all/20260503034533.1023686-1-jonas.emilsson@gmail.com Acked-by: Imre Deak Link: https://patch.msgid.link/20260622140532.526722-1-luciano.coelho@intel.com Signed-off-by: Maarten Lankhorst Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/display/drm_dp_mst_topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 56075dde04038..383cfd99f955e 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -3740,8 +3740,10 @@ void drm_dp_mst_topology_queue_probe(struct drm_dp_mst_topology_mgr *mgr) { mutex_lock(&mgr->lock); - if (drm_WARN_ON(mgr->dev, !mgr->mst_state || !mgr->mst_primary)) + if (!mgr->mst_state || !mgr->mst_primary) { + drm_dbg_kms(mgr->dev, "queue_probe skipped: topology torn down\n"); goto out_unlock; + } drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary); drm_dp_mst_queue_probe_work(mgr); From ffb33d466a68cea3e8a3dbed04d79037a3cbabd1 Mon Sep 17 00:00:00 2001 From: Boyuan Zhang Date: Tue, 12 May 2026 10:29:36 -0400 Subject: [PATCH 2811/3297] drm/amdgpu: fix division by zero with invalid uvd dimensions commit 0c01c811be47e6b146552dd59bfedbea8f09b8f4 upstream. When width or height is less than 16, width_in_mb or height_in_mb becomes 0, leading to fs_in_mb being 0. This causes a division by zero when calculating num_dpb_buffer in H264 and H264 Perf decode paths. Add validation to reject frames with width < 16 or height < 16 before performing any calculations that depend on these values. V2: Format change - move up all vaiable definitions. V3: Use warn_once to avoid spam. Signed-off-by: Boyuan Zhang Reviewed-by: Leo Liu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 3e41d26c70b0a459d041cc19482a226c4b7423cb) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index c6804063b39d9..5f1199b592b06 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -654,6 +654,14 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; + /* Reject invalid dimensions to prevent division by zero */ + if (width < 16 || height < 16) { + dev_WARN_ONCE(adev->dev, 1, + "Invalid UVD decoding dimensions (%dx%d)!\n", + width, height); + return -EINVAL; + } + image_size = width * height; image_size += image_size / 2; image_size = ALIGN(image_size, 1024); From 68eab5a64ddbc0bb34f6241f7983fc03eb4aeaf2 Mon Sep 17 00:00:00 2001 From: Ce Sun Date: Mon, 22 Jun 2026 23:05:09 +0800 Subject: [PATCH 2812/3297] drm/amdgpu: fix resource leak on ACP reset timeout commit 020da7c5aac5b86bad8a1571f6eda6b8cff9331d upstream. When ACP soft reset poll times out, original code returns early without cleanup, leaking MFD child devices, genpd links and all ACP heap allocations. Replace direct early return with goto out to force run all cleanup logic regardless of reset success, preserve timeout error code for caller. Signed-off-by: Ce Sun Reviewed-by: Tao Zhou Signed-off-by: Alex Deucher (cherry picked from commit 98073e4328d7a8d75d03696ab27f6de70ef1aeda) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 4926996f94da0..95aedc4e7d8b4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -505,6 +505,7 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) u32 val = 0; u32 count = 0; struct amdgpu_device *adev = ip_block->adev; + int ret = 0; /* return early if no ACP */ if (!adev->acp.acp_genpd) { @@ -526,7 +527,8 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } udelay(100); } @@ -543,11 +545,12 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } udelay(100); } - +out: device_for_each_child(adev->acp.parent, NULL, acp_genpd_remove_device); @@ -556,7 +559,7 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) kfree(adev->acp.acp_genpd); kfree(adev->acp.acp_cell); - return 0; + return ret; } static int acp_suspend(struct amdgpu_ip_block *ip_block) From 08fee493e0261f9e4120a5c8e7e42e8a723574e8 Mon Sep 17 00:00:00 2001 From: Ce Sun Date: Mon, 22 Jun 2026 22:58:16 +0800 Subject: [PATCH 2813/3297] drm/amdgpu: invoke pm_genpd_remove() before freeing genpd commit 28c9b3c5dc35cc790d11e26ca3fc6e068be63998 upstream. Call pm_genpd_remove() to unregister from global list prior to releasing acp_genpd memory, and clear the pointer after free. Signed-off-by: Ce Sun Reviewed-by: Tao Zhou Signed-off-by: Alex Deucher (cherry picked from commit cd8650d7a91ee8b768e202354672553faa5cc1f2) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 95aedc4e7d8b4..fe39b150ee533 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -556,7 +556,9 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) mfd_remove_devices(adev->acp.parent); kfree(adev->acp.acp_res); + pm_genpd_remove(&adev->acp.acp_genpd->gpd); kfree(adev->acp.acp_genpd); + adev->acp.acp_genpd = NULL; kfree(adev->acp.acp_cell); return ret; From 6405c4e75b3bcf0e72bd7a0ff5f1ed0c475e23aa Mon Sep 17 00:00:00 2001 From: Asad Kamal Date: Sun, 14 Jun 2026 12:50:28 +0800 Subject: [PATCH 2814/3297] drm/amdgpu: fix aperture mapping leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ea772a440d56b285f4d491affac50ecd41f6b402 upstream. amdgpu_pci_remove() calls drm_dev_unplug() before invoking the driver fini routines. This causes drm_dev_enter() in amdgpu_ttm_fini() to always return false, so iounmap(aper_base_kaddr) never runs on normal driver unload, leaving an orphaned entry in the x86 PAT interval tree. On connected_to_cpu hardware, the aperture is mapped write-back (WB) via ioremap_cache(). On reload, IP discovery calls memremap(..., MEMREMAP_WC) over the same range. The WC vs WB conflict causes: ioremap error for 0x..., requested 0x1, got 0x0 amdgpu: discovery failed: -2 Fix by switching to devres-managed mappings so cleanup is guaranteed regardless of drm_dev_enter() state: - connected_to_cpu path: devm_memremap(MEMREMAP_WB). For IORESOURCE_SYSTEM_RAM ranges this takes the try_ram_remap() shortcut, returning __va(offset) from the existing kernel direct map. No new ioremap VA or PAT entry is created, so there is nothing to orphan. - dGPU path: devm_ioremap_wc() registers iounmap() as a devres action, guaranteeing cleanup at device_del() time. Also remove iounmap(aper_base_kaddr) from amdgpu_device_unmap_mmio() since the mapping is now devres-owned. v2: Remove redundant x86_64 guard (Lijo) Fixes: 9d0af8b4def0 ("drm/amdgpu: pre-map device buffer as cached for A+A config") Signed-off-by: Asad Kamal Reviewed-by: Christian König Reviewed-by: Lijo Lazar Signed-off-by: Alex Deucher (cherry picked from commit d871e99879cb5fd1fa798b006b4888887e63a17a) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 -- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 36 ++++++++++------------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index c3e9b916f08f5..2c55925895f92 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4946,8 +4946,6 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev) iounmap(adev->rmmio); adev->rmmio = NULL; - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr = NULL; /* Memory manager related */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 944e0feb674ab..a4b5fe35c3cda 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1980,18 +1980,23 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) /* Change the size here instead of the init above so only lpfn is affected */ amdgpu_ttm_set_buffer_funcs_status(adev, false); #ifdef CONFIG_64BIT -#ifdef CONFIG_X86 - if (adev->gmc.xgmi.connected_to_cpu) - adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base, - adev->gmc.visible_vram_size); - - else if (adev->gmc.is_app_apu) + if (adev->gmc.xgmi.connected_to_cpu) { + void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base, + adev->gmc.visible_vram_size, + MEMREMAP_WB); + if (IS_ERR(kaddr)) + return PTR_ERR(kaddr); + adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr; + } else if (adev->gmc.is_app_apu) { DRM_DEBUG_DRIVER( "No need to ioremap when real vram size is 0\n"); - else -#endif - adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base, - adev->gmc.visible_vram_size); + } else { + adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev, + adev->gmc.aper_base, + adev->gmc.visible_vram_size); + if (!adev->mman.aper_base_kaddr) + return -ENOMEM; + } #endif /* @@ -2152,8 +2157,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) */ void amdgpu_ttm_fini(struct amdgpu_device *adev) { - int idx; - if (!adev->mman.initialized) return; @@ -2180,14 +2183,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) amdgpu_ttm_fw_reserve_vram_fini(adev); amdgpu_ttm_drv_reserve_vram_fini(adev); - if (drm_dev_enter(adev_to_drm(adev), &idx)) { - - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); - adev->mman.aper_base_kaddr = NULL; - - drm_dev_exit(idx); - } + adev->mman.aper_base_kaddr = NULL; if (!adev->gmc.is_app_apu) amdgpu_vram_mgr_fini(adev); From ab7b40c638e0d65c7ed5062d9440a326f3d5a3ba Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 1 Jul 2026 09:11:15 +0800 Subject: [PATCH 2815/3297] drm/amd/pm: fix smu13 power limit range calculation commit 220f22e1d66c1cfb63387eb1c4210f92a357c2d9 upstream. SMU13 reports SocketPowerLimitAc/Dc as the default power limit, but MsgLimits.Power may carry a different firmware bound for the same PPT throttler. Using only the socket limit for both min and max can therefore expose an incorrect power range. Keep the socket limit as the default, but derive the range from both values: use the lower value for the min base and the higher value for the max base before applying OD percentages. Keep the current limit query independent from the cap calculation. Fixes: 1eaf26db9590 ("drm/amd/pm: fix smu13 power limit default/cap calculation") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5419 Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit f45bbf0f62f266ed8422d84f347d75d5fca846a7) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 11 +++++++---- .../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 15 ++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 96a2b5ab87d85..f5c20f2320621 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2481,11 +2481,14 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + uint32_t od_percent_upper = 0, od_percent_lower = 0; int ret; if (current_power_limit) { - ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); if (ret) *current_power_limit = pp_limit; } @@ -2508,12 +2511,12 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = pp_limit * (100 + od_percent_upper); + *max_power_limit = max_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = pp_limit * (100 - od_percent_lower); + *min_power_limit = min_limit * (100 - od_percent_lower); *min_power_limit /= 100; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 0843fa0f5e2ae..427f3569f1d39 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2442,15 +2442,16 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + uint32_t od_percent_upper = 0, od_percent_lower = 0; int ret; if (current_power_limit) { - ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); if (ret) - power_limit = pp_limit; - - *current_power_limit = power_limit; + *current_power_limit = pp_limit; } if (default_power_limit) @@ -2471,12 +2472,12 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = pp_limit * (100 + od_percent_upper); + *max_power_limit = max_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = pp_limit * (100 - od_percent_lower); + *min_power_limit = min_limit * (100 - od_percent_lower); *min_power_limit /= 100; } From 15a7cb71a5748a718453f3b01e1da3b52349c043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 24 Jun 2026 16:00:41 +0200 Subject: [PATCH 2816/3297] drm/amdgpu: fix check in amdgpu_hmm_invalidate_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 52f650963d8825e97a0ccdd2b616f8a01d9d3d38 upstream. For a short moment during alloc/free the userptr BO is not part of his VM, so bo->vm_bo can be NULL. Keep a reference to the VM root PD as parent of the userptr BO so that we can always use that to wait for all submissions of the VM instead of only the one involving the userptr BO. Signed-off-by: Christian König Fixes: 91250893cbaa ("drm/amdgpu: fix waiting for all submissions for userptrs") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5399 Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 631849ff5d603841e74f19f4a5e30fe1f7d7cf30) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 25bb984fa89ef..5fc9a6b1ec722 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -523,6 +523,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, bo = gem_to_amdgpu_bo(gobj); bo->preferred_domains = AMDGPU_GEM_DOMAIN_GTT; bo->allowed_domains = AMDGPU_GEM_DOMAIN_GTT; + bo->parent = amdgpu_bo_ref(fpriv->vm.root.bo); r = amdgpu_ttm_tt_set_userptr(&bo->tbo, args->addr, args->flags); if (r) goto release_object; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index ccda3b58a4558..f60f26470af03 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -69,7 +69,6 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, { struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); - struct amdgpu_bo *vm_root = bo->vm_bo->vm->root.bo; long r; if (!mmu_notifier_range_blockable(range)) @@ -80,7 +79,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(vm_root->tbo.base.resv, + r = dma_resv_wait_timeout(bo->parent->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, false, MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); From 22400725de070b787cd6d806c5795370ab46d269 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Tue, 28 Jul 2026 20:26:58 +0000 Subject: [PATCH 2817/3297] bpf: Fix same-register dst/src OOB read and pointer leak in sock_ops [ Upstream commit 10f86a2a5c91fc4c4d001960f1c21abe52545ef6 ] When a BPF sock_ops program accesses ctx fields with dst_reg == src_reg, the SOCK_OPS_GET_SK() and SOCK_OPS_GET_FIELD() macros fail to zero the destination register in the !fullsock / !locked_tcp_sock path. Both macros borrow a temporary register to check is_fullsock / is_locked_tcp_sock when dst_reg == src_reg, because dst_reg holds the ctx pointer. When the check is false (e.g., TCP_NEW_SYN_RECV state with a request_sock), dst_reg should be zeroed but is not, leaving the stale ctx pointer: - SOCK_OPS_GET_SK: dst_reg retains the ctx pointer, passes NULL checks as PTR_TO_SOCKET_OR_NULL, and can be used as a bogus socket pointer, leading to stack-out-of-bounds access in helpers like bpf_skc_to_tcp6_sock(). - SOCK_OPS_GET_FIELD: dst_reg retains the ctx pointer which the verifier believes is a SCALAR_VALUE, leaking a kernel pointer. Fix both macros by: - Changing JMP_A(1) to JMP_A(2) in the fullsock path to skip the added instruction. - Adding BPF_MOV64_IMM(si->dst_reg, 0) after the temp register restore in the !fullsock path, placed after the restore because dst_reg == src_reg means we need src_reg intact to read ctx->temp. Fixes: fd09af010788 ("bpf: sock_ops ctx access may stomp registers in corner case") Fixes: 84f44df664e9 ("bpf: sock_ops sk access may stomp registers when dst_reg = src_reg") Reported-by: Quan Sun <2022090917019@std.uestc.edu.cn> Reported-by: Yinhao Hu Reported-by: Kaiyan Mei Reported-by: Dongliang Mu Reviewed-by: Emil Tsalapatis Closes: https://lore.kernel.org/bpf/6fe1243e-149b-4d3b-99c7-fcc9e2f75787@std.uestc.edu.cn/T/#u Signed-off-by: Jiayuan Chen Acked-by: Martin KaFai Lau Link: https://patch.msgid.link/20260407022720.162151-2-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Jay Wang Signed-off-by: Sasha Levin --- net/core/filter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index 42385e7904313..332f4986376f1 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -10539,10 +10539,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, si->dst_reg, si->dst_reg, \ offsetof(OBJ, OBJ_FIELD)); \ if (si->dst_reg == si->src_reg) { \ - *insn++ = BPF_JMP_A(1); \ + *insn++ = BPF_JMP_A(2); \ *insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg, \ offsetof(struct bpf_sock_ops_kern, \ temp)); \ + *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); \ } \ } while (0) @@ -10576,10 +10577,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, si->dst_reg, si->src_reg, \ offsetof(struct bpf_sock_ops_kern, sk));\ if (si->dst_reg == si->src_reg) { \ - *insn++ = BPF_JMP_A(1); \ + *insn++ = BPF_JMP_A(2); \ *insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg, \ offsetof(struct bpf_sock_ops_kern, \ temp)); \ + *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); \ } \ } while (0) From a556189c067569c3e849e05ca59d2a2a721271a2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 28 Jul 2026 22:19:06 -0700 Subject: [PATCH 2818/3297] dm-verity-fec: fix the size of dm_verity_fec_io::erasures commit a7fca324d7d90f7b139d4d32747c83a629fdb446 upstream. At most 25 entries in dm_verity_fec_io::erasures are used: the maximum number of FEC roots plus one. Therefore, set the array size accordingly. This reduces the size of dm_verity_fec_io by 912 bytes. Note: a later commit introduces a constant DM_VERITY_FEC_MAX_ROOTS, which allows the size to be more clearly expressed as DM_VERITY_FEC_MAX_ROOTS + 1. This commit just fixes the size first. Fixes: a739ff3f543a ("dm verity: add support for forward error correction") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Mikulas Patocka Signed-off-by: Sasha Levin --- drivers/md/dm-verity-fec.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h index ec37e607cb3f0..90a0af3f35d31 100644 --- a/drivers/md/dm-verity-fec.h +++ b/drivers/md/dm-verity-fec.h @@ -50,7 +50,8 @@ struct dm_verity_fec { /* per-bio data */ struct dm_verity_fec_io { struct rs_control *rs; /* Reed-Solomon state */ - int erasures[DM_VERITY_FEC_MAX_RSN]; /* erasures for decode_rs8 */ + /* erasures for decode_rs8 */ + int erasures[DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN + 1]; u8 *bufs[DM_VERITY_FEC_BUF_MAX]; /* bufs for deinterleaving */ unsigned int nbufs; /* number of buffers allocated */ u8 *output; /* buffer for corrected output */ From d47281b9a4472cfd73122393e79fbe76b651e46a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 28 Jul 2026 22:19:07 -0700 Subject: [PATCH 2819/3297] dm-verity-fec: fix reading parity bytes split across blocks (take 3) commit 430a05cb926f6bdf53e81460a2c3a553257f3f61 upstream. fec_decode_bufs() assumes that the parity bytes of the first RS codeword it decodes are never split across parity blocks. This assumption is false. Consider v->fec->block_size == 4096 && v->fec->roots == 17 && fio->nbufs == 1, for example. In that case, each call to fec_decode_bufs() consumes v->fec->roots * (fio->nbufs << DM_VERITY_FEC_BUF_RS_BITS) = 272 parity bytes. Considering that the parity data for each message block starts on a block boundary, the byte alignment in the parity data will iterate through 272*i mod 4096 until the 3 parity blocks have been consumed. On the 16th call (i=15), the alignment will be 4080 bytes into the first block. Only 16 bytes remain in that block, but 17 parity bytes will be needed. The code reads out-of-bounds from the parity block buffer. Fortunately this doesn't normally happen, since it can occur only for certain non-default values of fec_roots *and* when the maximum number of buffers couldn't be allocated due to low memory. For example with block_size=4096 only the following cases are affected: fec_roots=17: nbufs in [1, 3, 5, 15] fec_roots=19: nbufs in [1, 229] fec_roots=21: nbufs in [1, 3, 5, 13, 15, 39, 65, 195] fec_roots=23: nbufs in [1, 89] Regardless, fix it by refactoring how the parity blocks are read. Fixes: 6df90c02bae4 ("dm-verity FEC: Fix RS FEC repair for roots unaligned to block size (take 2)") Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Mikulas Patocka Signed-off-by: Sasha Levin --- drivers/md/dm-verity-fec.c | 100 ++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 56 deletions(-) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index 03e59b85132aa..a8fbf97b97517 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -39,36 +39,6 @@ static inline u64 fec_interleave(struct dm_verity *v, u64 offset) return offset + mod * (v->fec->rounds << v->data_dev_block_bits); } -/* - * Read error-correcting codes for the requested RS block. Returns a pointer - * to the data block. Caller is responsible for releasing buf. - */ -static u8 *fec_read_parity(struct dm_verity *v, u64 rsb, int index, - unsigned int *offset, unsigned int par_buf_offset, - struct dm_buffer **buf, unsigned short ioprio) -{ - u64 position, block, rem; - u8 *res; - - /* We have already part of parity bytes read, skip to the next block */ - if (par_buf_offset) - index++; - - position = (index + rsb) * v->fec->roots; - block = div64_u64_rem(position, v->fec->io_size, &rem); - *offset = par_buf_offset ? 0 : (unsigned int)rem; - - res = dm_bufio_read_with_ioprio(v->fec->bufio, block, buf, ioprio); - if (IS_ERR(res)) { - DMERR("%s: FEC %llu: parity read failed (block %llu): %ld", - v->data_dev->name, (unsigned long long)rsb, - (unsigned long long)block, PTR_ERR(res)); - *buf = NULL; - } - - return res; -} - /* Loop over each preallocated buffer slot. */ #define fec_for_each_prealloc_buffer(__i) \ for (__i = 0; __i < DM_VERITY_FEC_BUF_PREALLOC; __i++) @@ -116,15 +86,29 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_io *io, { int r, corrected = 0, res; struct dm_buffer *buf; - unsigned int n, i, j, offset, par_buf_offset = 0; + unsigned int n, i, j, parity_pos, to_copy; uint16_t par_buf[DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN]; u8 *par, *block; + u64 parity_block; struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size); - par = fec_read_parity(v, rsb, block_offset, &offset, - par_buf_offset, &buf, bio->bi_ioprio); - if (IS_ERR(par)) + /* + * Compute the index of the first parity block that will be needed and + * the starting position in that block. Then read that block. + * + * io_size is always a power of 2, but roots might not be. Note that + * when it's not, a codeword's parity bytes can span a block boundary. + */ + parity_block = (rsb + block_offset) * v->fec->roots; + parity_pos = parity_block & (v->fec->io_size - 1); + parity_block >>= v->data_dev_block_bits; + par = dm_bufio_read_with_ioprio(v->fec->bufio, parity_block, &buf, + bio->bi_ioprio); + if (IS_ERR(par)) { + DMERR("%s: FEC %llu: parity read failed (block %llu): %ld", + v->data_dev->name, rsb, parity_block, PTR_ERR(par)); return PTR_ERR(par); + } /* * Decode the RS blocks we have in bufs. Each RS block results in @@ -132,8 +116,32 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_io *io, */ fec_for_each_buffer_rs_block(fio, n, i) { block = fec_buffer_rs_block(v, fio, n, i); - for (j = 0; j < v->fec->roots - par_buf_offset; j++) - par_buf[par_buf_offset + j] = par[offset + j]; + + /* + * Copy the next 'roots' parity bytes to 'par_buf', reading + * another parity block if needed. + */ + to_copy = min(v->fec->io_size - parity_pos, v->fec->roots); + for (j = 0; j < to_copy; j++) + par_buf[j] = par[parity_pos++]; + if (to_copy < v->fec->roots) { + parity_block++; + parity_pos = 0; + + dm_bufio_release(buf); + par = dm_bufio_read_with_ioprio(v->fec->bufio, + parity_block, &buf, + bio->bi_ioprio); + if (IS_ERR(par)) { + DMERR("%s: FEC %llu: parity read failed (block %llu): %ld", + v->data_dev->name, rsb, parity_block, + PTR_ERR(par)); + return PTR_ERR(par); + } + for (; j < v->fec->roots; j++) + par_buf[j] = par[parity_pos++]; + } + /* Decode an RS block using Reed-Solomon */ res = decode_rs8(fio->rs, block, par_buf, v->fec->rsn, NULL, neras, fio->erasures, 0, NULL); @@ -148,26 +156,6 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_io *io, block_offset++; if (block_offset >= 1 << v->data_dev_block_bits) goto done; - - /* Read the next block when we run out of parity bytes */ - offset += (v->fec->roots - par_buf_offset); - /* Check if parity bytes are split between blocks */ - if (offset < v->fec->io_size && (offset + v->fec->roots) > v->fec->io_size) { - par_buf_offset = v->fec->io_size - offset; - for (j = 0; j < par_buf_offset; j++) - par_buf[j] = par[offset + j]; - offset += par_buf_offset; - } else - par_buf_offset = 0; - - if (offset >= v->fec->io_size) { - dm_bufio_release(buf); - - par = fec_read_parity(v, rsb, block_offset, &offset, - par_buf_offset, &buf, bio->bi_ioprio); - if (IS_ERR(par)) - return PTR_ERR(par); - } } done: r = corrected; From 3f31bde63f9aeecccf843fbc87e581cbbecfe6dc Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 28 Jul 2026 22:19:08 -0700 Subject: [PATCH 2820/3297] dm-verity-fec: replace {MAX,MIN}_RSN with {MIN,MAX}_ROOTS commit 82fbd6a3e29a329d439690cd7ccc4162c9cd8db6 upstream. Every time DM_VERITY_FEC_{MAX,MIN}_RSN are used, they are subtracted from DM_VERITY_FEC_RSM to get the bounds on the number of roots. Therefore, replace these with {MIN,MAX}_ROOTS constants which are more directly useful. (Note the inversion, where MAX_RSN maps to MIN_ROOTS and MIN_RSN maps to MAX_ROOTS.) No functional change. Signed-off-by: Eric Biggers Signed-off-by: Mikulas Patocka Signed-off-by: Sasha Levin --- drivers/md/dm-verity-fec.c | 6 +++--- drivers/md/dm-verity-fec.h | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index a8fbf97b97517..cebcc8fd25d70 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -87,7 +87,7 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_io *io, int r, corrected = 0, res; struct dm_buffer *buf; unsigned int n, i, j, parity_pos, to_copy; - uint16_t par_buf[DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN]; + uint16_t par_buf[DM_VERITY_FEC_MAX_ROOTS]; u8 *par, *block; u64 parity_block; struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size); @@ -605,8 +605,8 @@ int verity_fec_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, } else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_ROOTS)) { if (sscanf(arg_value, "%hhu%c", &num_c, &dummy) != 1 || !num_c || - num_c < (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MAX_RSN) || - num_c > (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN)) { + num_c < DM_VERITY_FEC_MIN_ROOTS || + num_c > DM_VERITY_FEC_MAX_ROOTS) { ti->error = "Invalid " DM_VERITY_OPT_FEC_ROOTS; return -EINVAL; } diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h index 90a0af3f35d31..b3460103e0e10 100644 --- a/drivers/md/dm-verity-fec.h +++ b/drivers/md/dm-verity-fec.h @@ -13,8 +13,8 @@ /* Reed-Solomon(M, N) parameters */ #define DM_VERITY_FEC_RSM 255 -#define DM_VERITY_FEC_MAX_RSN 253 -#define DM_VERITY_FEC_MIN_RSN 231 /* ~10% space overhead */ +#define DM_VERITY_FEC_MIN_ROOTS 2 /* RS(255, 253): ~0.8% space overhead */ +#define DM_VERITY_FEC_MAX_ROOTS 24 /* RS(255, 231): ~10% space overhead */ /* buffers for deinterleaving and decoding */ #define DM_VERITY_FEC_BUF_PREALLOC 1 /* buffers to preallocate */ @@ -50,8 +50,7 @@ struct dm_verity_fec { /* per-bio data */ struct dm_verity_fec_io { struct rs_control *rs; /* Reed-Solomon state */ - /* erasures for decode_rs8 */ - int erasures[DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN + 1]; + int erasures[DM_VERITY_FEC_MAX_ROOTS + 1]; /* erasures for decode_rs8 */ u8 *bufs[DM_VERITY_FEC_BUF_MAX]; /* bufs for deinterleaving */ unsigned int nbufs; /* number of buffers allocated */ u8 *output; /* buffer for corrected output */ From 5488d3a69d205e28f74f18857b853aa12e778e66 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 28 Jul 2026 22:19:09 -0700 Subject: [PATCH 2821/3297] dm-verity: fix buffer overflow in FEC calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 31d6e6c0ba8d5a7bd59660035a089307100c5e8e upstream. There's a buffer overflow in dm-verity-fec: if (neras && *neras <= v->fec->roots) fio->erasures[(*neras)++] = i; This allows *neras to reach roots + 1 (the post-increment pushes it past roots). This value is then passed as no_eras to decode_rs8(). Inside the RS decoder (lib/reed_solomon/decode_rs.c:113-121), the erasure locator polynomial loop writes lambda[j] where j can reach nroots + 1 — one element past the end of lambda[] (which is sized nroots + 1, valid indices 0..nroots). The out-of-bounds write lands on syn[0], corrupting the syndrome buffer. Signed-off-by: Mikulas Patocka Assisted-by: Claude:claude-opus-4-6 Cc: stable@vger.kernel.org Fixes: a739ff3f543a ("dm verity: add support for forward error correction") Reviewed-by: Sami Tolvanen Signed-off-by: Mikulas Patocka Signed-off-by: Eric Biggers Signed-off-by: Sasha Levin --- drivers/md/dm-verity-fec.c | 4 ++-- drivers/md/dm-verity-fec.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index cebcc8fd25d70..d1e4fbc5a21d9 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -250,7 +250,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io, (unsigned long long)block, PTR_ERR(bbuf)); /* assume the block is corrupted */ - if (neras && *neras <= v->fec->roots) + if (neras && *neras < v->fec->roots) fio->erasures[(*neras)++] = i; continue; @@ -268,7 +268,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io, * skip if we have already found the theoretical * maximum number (i.e. fec->roots) of erasures */ - if (neras && *neras <= v->fec->roots && + if (neras && *neras < v->fec->roots && fec_is_erasure(v, io, want_digest, bbuf)) fio->erasures[(*neras)++] = i; } diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h index b3460103e0e10..8552b5d3c9152 100644 --- a/drivers/md/dm-verity-fec.h +++ b/drivers/md/dm-verity-fec.h @@ -50,7 +50,7 @@ struct dm_verity_fec { /* per-bio data */ struct dm_verity_fec_io { struct rs_control *rs; /* Reed-Solomon state */ - int erasures[DM_VERITY_FEC_MAX_ROOTS + 1]; /* erasures for decode_rs8 */ + int erasures[DM_VERITY_FEC_MAX_ROOTS]; /* erasures for decode_rs8 */ u8 *bufs[DM_VERITY_FEC_BUF_MAX]; /* bufs for deinterleaving */ unsigned int nbufs; /* number of buffers allocated */ u8 *output; /* buffer for corrected output */ From 5a15eaa50f92b2e33f20e1aa7f1155f7be70e11a Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Wed, 29 Jul 2026 14:37:59 +0000 Subject: [PATCH 2822/3297] drm/xe/uapi: Reject coh_none PAT index for CPU_ADDR_MIRROR commit 4d58d7535e826a3175527b6174502f0db319d7f6 upstream. Add validation in xe_vm_bind_ioctl() to reject PAT indices with XE_COH_NONE coherency mode when used with DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR. CPU address mirror mappings use system memory that is CPU cached, which makes them incompatible with COH_NONE PAT indices. Allowing COH_NONE with CPU cached buffers is a security risk, as the GPU may bypass CPU caches and read stale sensitive data from DRAM. Although CPU_ADDR_MIRROR does not create an immediate mapping, the backing system memory is still CPU cached. Apply the same PAT coherency restrictions as DRM_XE_VM_BIND_OP_MAP_USERPTR. v2: - Correct fix tag v6: - No change v7: - Correct fix tag v8: - Rebase v9: - Limit the restrictions to iGPU v10: - Just add the iGPU logic but keep dGPU logic Fixes: b43e864af0d4 ("drm/xe/uapi: Add DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR") Cc: # v6.15+ Cc: Shuicheng Lin Cc: Mathew Alwin Cc: Michal Mrozek Cc: Matthew Brost Cc: Matthew Auld Signed-off-by: Jia Yao Reviewed-by: Matthew Auld Acked-by: Michal Mrozek Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260417055917.2027459-3-jia.yao@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_vm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index b00da90e39913..ed0c8acd0fe27 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -3393,6 +3393,8 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm, op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE && op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || + XE_IOCTL_DBG(xe, !IS_DGFX(xe) && coh_mode == XE_COH_NONE && + is_cpu_addr_mirror) || XE_IOCTL_DBG(xe, op == DRM_XE_VM_BIND_OP_MAP_USERPTR && !IS_ENABLED(CONFIG_DRM_GPUSVM)) || XE_IOCTL_DBG(xe, obj && From 7dd26adf7e7d482af524e3a0cca4a81ef7c159d0 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 29 Jul 2026 09:48:06 -0500 Subject: [PATCH 2823/3297] ublk: wait on ublk_dev_ready() instead of ub->completion commit 432a9b2780c0a01caf547bd1fc2fcf28aeb8d173 upstream. ub->completion is only re-armed by a successful START_USER_RECOVERY. If the ublk server sends END_USER_RECOVERY without one - e.g. its START failed with -EBUSY and the error was ignored - the wait is satisfied by the stale completion of the previous recovery cycle, and the device is marked LIVE and the requeue list kicked while the FETCH stream is still running and ubq->canceling is still set. The kick redispatches a previously requeued request, __ublk_queue_rq_common() sees ->canceling and parks it again via __ublk_abort_rq(), and after the last FETCH clears ->canceling nothing ever kicks the requeue list again: the request is stranded there while holding its tag. If it is the flush machinery's flush_rq, every subsequent fsync piles up in uninterruptible sleep and teardown hangs on tag draining. This matches a report of a lost PREFLUSH with ext4 on top of ublk after daemon crash recovery. ub->completion is an edge-triggered latch used as a proxy for the level condition "every queue has fetched all I/O commands", which can regress (F_BATCH's UNPREP, daemon death) and whose re-arm can be skipped. Drop it and wait on the real condition instead: the new helper ublk_wait_dev_ready_and_lock() waits on ublk_dev_ready() via wait_var_event_interruptible(), woken from ublk_mark_io_ready(), then re-checks it under ub->mutex, waiting again on regression, and returns with the mutex held and readiness guaranteed. Readiness becomes true in the same ub->mutex critical section that clears the last queue's ->canceling, so END_USER_RECOVERY marks the device LIVE and kicks the requeue list strictly after ->canceling clears. The wait stays interruptible, so a server whose daemon died can still be signalled out. For ublk_ctrl_start_dev() this replaces the fail-fast -EINVAL on an F_BATCH ready->UNPREP regression with waiting until the device is ready again. Reported-by: George Salisbury Fixes: 728cbac5fe21 ("ublk: move device reset into ublk_ch_release()") Cc: stable@vger.kernel.org Signed-off-by: Ming Lei Link: https://patch.msgid.link/20260719134540.120269-1-tom.leiming@gmail.com Signed-off-by: Jens Axboe [ 6.18.y tracks readiness per fetched I/O command in ->nr_io_ready rather than per queue in ->nr_queue_ready, so wait and wake on that counter; ->canceling is cleared by ublk_reset_io_flags(), which stays in ublk_mark_io_ready(). UBLK_F_BATCH_IO does not exist here, so ublk_ctrl_start_dev() has no F_BATCH readiness re-check to replace, and readiness can only regress via ublk_reset_ch_dev() on daemon death. ] Signed-off-by: Sasha Levin --- drivers/block/ublk_drv.c | 52 ++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index c339222513b03..cb31e96f01cdd 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -230,7 +230,6 @@ struct ublk_device { struct ublk_params params; - struct completion completion; u32 nr_io_ready; bool unprivileged_daemons; struct mutex cancel_mutex; @@ -2150,9 +2149,13 @@ static void ublk_mark_io_ready(struct ublk_device *ub) ub->nr_io_ready++; if (ublk_dev_ready(ub)) { - /* now we are ready for handling ublk io request */ + /* + * now we are ready for handling ublk io request, clear + * device-level canceling flag and wake ublk_dev_ready() + * waiters + */ ublk_reset_io_flags(ub); - complete_all(&ub->completion); + wake_up_var(&ub->nr_io_ready); } } @@ -2829,7 +2832,6 @@ static int ublk_init_queues(struct ublk_device *ub) goto fail; } - init_completion(&ub->completion); return 0; fail: @@ -2966,6 +2968,26 @@ static bool ublk_validate_user_pid(struct ublk_device *ub, pid_t ublksrv_pid) return ub->ublksrv_tgid == ublksrv_pid; } +/* + * Wait until all queues have fetched their I/O commands, and return with + * ub->mutex held and readiness guaranteed: then every queue's ->canceling + * is cleared. Ready may regress between wakeup and mutex_lock() (daemon + * death), so re-check it under the mutex and wait again. + */ +static int ublk_wait_dev_ready_and_lock(struct ublk_device *ub) +{ + while (true) { + if (wait_var_event_interruptible(&ub->nr_io_ready, + ublk_dev_ready(ub))) + return -EINTR; + + mutex_lock(&ub->mutex); + if (ublk_dev_ready(ub)) + return 0; + mutex_unlock(&ub->mutex); + } +} + static int ublk_ctrl_start_dev(struct ublk_device *ub, const struct ublksrv_ctrl_cmd *header) { @@ -3031,13 +3053,13 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub, lim.max_segments = ub->params.seg.max_segments; } - if (wait_for_completion_interruptible(&ub->completion) != 0) + if (ublk_wait_dev_ready_and_lock(ub)) return -EINTR; - if (!ublk_validate_user_pid(ub, ublksrv_pid)) - return -EINVAL; - - mutex_lock(&ub->mutex); + if (!ublk_validate_user_pid(ub, ublksrv_pid)) { + ret = -EINVAL; + goto out_unlock; + } if (ub->dev_info.state == UBLK_S_DEV_LIVE || test_bit(UB_STATE_USED, &ub->state)) { ret = -EEXIST; @@ -3549,7 +3571,6 @@ static int ublk_ctrl_start_recovery(struct ublk_device *ub, goto out_unlock; } pr_devel("%s: start recovery for dev id %d.\n", __func__, header->dev_id); - init_completion(&ub->completion); ret = 0; out_unlock: mutex_unlock(&ub->mutex); @@ -3565,16 +3586,17 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub, pr_devel("%s: Waiting for all FETCH_REQs, dev id %d...\n", __func__, header->dev_id); - if (wait_for_completion_interruptible(&ub->completion)) + if (ublk_wait_dev_ready_and_lock(ub)) return -EINTR; pr_devel("%s: All FETCH_REQs received, dev id %d\n", __func__, header->dev_id); - if (!ublk_validate_user_pid(ub, ublksrv_pid)) - return -EINVAL; + if (!ublk_validate_user_pid(ub, ublksrv_pid)) { + ret = -EINVAL; + goto out_unlock; + } - mutex_lock(&ub->mutex); if (ublk_nosrv_should_stop_dev(ub)) goto out_unlock; From 17e6b8c4319fafda04fc5bfcdcea4c0dda5b59df Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Wed, 29 Jul 2026 17:02:11 +0200 Subject: [PATCH 2824/3297] net: qrtr: ns: Raise node count limit to 512 commit ff194cffd586cbd4cc49eccb002c65f2a902a277 upstream. The current node limit of 64 breaks the functionality for a number of AI200 deployments that have up to 384 nodes. Raise the limit to 512. Fixes: 27d5e84e810b ("net: qrtr: ns: Limit the total number of nodes") Cc: stable@vger.kernel.org Signed-off-by: Youssef Samir Link: https://patch.msgid.link/20260713145901.212396-1-youssef.abdulrahman@oss.qualcomm.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/qrtr/ns.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index a10e8de4f7e10..e8e3ba1b3b823 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -85,9 +85,9 @@ struct qrtr_node { /* Max nodes limit is chosen based on the current platform requirements. * If the requirement changes in the future, this value can be increased. */ -#define QRTR_NS_MAX_NODES 64 +#define QRTR_NS_MAX_NODES 512 -static u8 node_count; +static u16 node_count; static struct qrtr_node *node_get(unsigned int node_id) { From b6d3cc6a524416dfdb2b47e4bba2e7e20011d056 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Thu, 30 Jul 2026 03:00:22 +0800 Subject: [PATCH 2825/3297] ksmbd: validate num_subauth when copying ACE in set_ntacl_dacl commit 47f0b34f6bc98ed85bfdc293e8f3e432ec24958d upstream. set_ntacl_dacl() copies each ACE from the attacker-controlled stored security descriptor verbatim into the response DACL without checking sid.num_subauth. The ACE bytes (including an unchecked num_subauth) originate from an authenticated SMB2_SET_INFO(SecInfo=DACL) that is stored raw via ksmbd_vfs_set_sd_xattr(); parse_dacl() rejects a bad ACE with `break` rather than an error, so parse_sec_desc() still returns success and the malformed SD reaches the xattr intact. On a subsequent SMB2_QUERY_INFO(SecInfo=DACL) for an inode carrying a POSIX access ACL, build_sec_desc() -> set_ntacl_dacl() -> set_posix_acl_entries_dacl() walks the copied ACEs and reads ntace->sid.sub_auth[ntace->sid.num_subauth - 1] with num_subauth taken straight from the stored SD. Since sub_auth[] is fixed at SID_MAX_SUB_AUTHORITIES (15), a crafted num_subauth (e.g. 255) drives an out-of-bounds heap read of ~1 KB with an offset fully controlled by an authenticated client. The sibling functions already gate this field: parse_dacl() -- num_subauth == 0 || > SID_MAX_SUB_AUTHORITIES parse_sid() -- num_subauth > SID_MAX_SUB_AUTHORITIES smb_copy_sid() -- min_t(u8, num_subauth, SID_MAX_SUB_AUTHORITIES) set_ntacl_dacl() is the lone inconsistent path that omits the check. Add the same num_subauth validation in set_ntacl_dacl() before copying the ACE, matching the gate already enforced by parse_dacl(). Signed-off-by: Haofeng Li Reviewed-by: ChenXiaoSong Suggested-by: Namjae Jeon Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- fs/smb/server/smbacl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index b09bc8d9389a2..173469d112f10 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -745,12 +745,18 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, if (nt_ace_size > aces_size) break; + if (ntace->sid.num_subauth == 0 || + ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) + goto next_ace; + memcpy((char *)pndace + size, ntace, nt_ace_size); if (check_add_overflow(size, nt_ace_size, &size)) break; + num_aces++; + +next_ace: aces_size -= nt_ace_size; ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); - num_aces++; } } From 847ecd4eb3c117c3d2f13f1e7ab506543aad8183 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Thu, 30 Jul 2026 03:00:24 +0800 Subject: [PATCH 2826/3297] ksmbd: restore DACL size on check_add_overflow() to avoid malformed ACL commit bbf0a8e931204ecdab494a88d43b0a24a04285c5 upstream. check_add_overflow() unconditionally writes the truncated sum into *d even on overflow, per its contract in include/linux/overflow.h. The four check_add_overflow() guards in set_posix_acl_entries_dacl() and set_ntacl_dacl() break out of the ACE-building loops on overflow, but the truncated *size is then consumed downstream at the end of set_ntacl_dacl(): pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); This produces an on-wire NT ACL whose pndacl->size under-reports the bytes actually written by the preceding fill_ace_for_sid()/memcpy() calls, yielding a malformed ACL that can trigger out-of-bounds reads when re-parsed by clients or ksmbd itself. Restore *size to its pre-addition value on each overflow branch (via `*size -= ace_sz` / `size -= nt_ace_size`) so that after the break, *size once again holds the cumulative size of the successfully-written ACEs. The committed ACL is then truncated-but-self-consistent rather than malformed. The ksmbd DACL builders are the only check_add_overflow() sites found where an overflow path breaks out of a loop and the destination value is consumed afterward. The other nearby break-style cases either return -EINVAL on overflow (transport_ipc.c) or break without consuming the overflowed destination value afterward (buildid.c). Fixes: 299f962c0b02 ("ksmbd: use check_add_overflow() to prevent u16 DACL size overflow") Assisted-by: atomcode:glm-5.2 Assisted-by: Codex:gpt-5.5 Cc: stable@vger.kernel.org Signed-off-by: Wentao Guan Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smbacl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 173469d112f10..ca0e4aab6dd87 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -649,6 +649,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, pace->e_perm, 0777); if (check_add_overflow(*size, ace_sz, size)) { + *size -= ace_sz; kfree(sid); break; } @@ -663,6 +664,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x03, pace->e_perm, 0777); if (check_add_overflow(*size, ace_sz, size)) { + *size -= ace_sz; kfree(sid); break; } @@ -708,6 +710,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, pace->e_perm, 0777); if (check_add_overflow(*size, ace_sz, size)) { + *size -= ace_sz; kfree(sid); break; } @@ -750,8 +753,10 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, goto next_ace; memcpy((char *)pndace + size, ntace, nt_ace_size); - if (check_add_overflow(size, nt_ace_size, &size)) + if (check_add_overflow(size, nt_ace_size, &size)) { + size -= nt_ace_size; break; + } num_aces++; next_ace: From f1eba60db813ec28732bf18b5f0a67ebac9c3100 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 30 Jul 2026 03:00:26 +0800 Subject: [PATCH 2827/3297] ksmbd: bound DACL dedup walk to copied ACEs commit 58d97fcd0bf1aee694e244cc28635b9df95b543b upstream. set_ntacl_dacl() can stop copying ACEs before consuming the full input DACL when size accounting overflows. When that happens, num_aces reflects only the ACEs that were actually copied into the output DACL, but set_posix_acl_entries_dacl() still receives nt_num_aces and uses it to walk the existing ACE array during dedup. That makes the dedup walk scan past the copied ACE array and inspect buffer tail that does not contain valid ACEs. Split the two meanings currently carried by the NT ACE count. Pass the number of copied NT ACEs to bound the dedup walk, and preserve the original "input DACL had NT ACEs" state separately for the Everyone/default ACL fallback. This keeps the dedup walk aligned with the ACEs that are actually present in the rebuilt DACL. Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- fs/smb/server/smbacl.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index ca0e4aab6dd87..b0677e095c329 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -595,7 +595,8 @@ static void parse_dacl(struct mnt_idmap *idmap, static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, struct smb_ace *pndace, struct smb_fattr *fattr, u16 *num_aces, - u16 *size, u32 nt_aces_num) + u16 *size, u16 existing_nt_aces, + bool had_nt_aces) { struct posix_acl_entry *pace; struct smb_sid *sid; @@ -627,14 +628,14 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, gid = posix_acl_gid_translate(idmap, pace); id_to_sid(gid, SIDUNIX_GROUP, sid); - } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { + } else if (pace->e_tag == ACL_OTHER && !had_nt_aces) { smb_copy_sid(sid, &sid_everyone); } else { kfree(sid); continue; } ntace = pndace; - for (j = 0; j < nt_aces_num; j++) { + for (j = 0; j < existing_nt_aces; j++) { if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == sid->sub_auth[sid->num_subauth - 1]) goto pass_same_sid; @@ -678,7 +679,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, kfree(sid); } - if (nt_aces_num) + if (had_nt_aces) return; posix_default_acl: @@ -732,6 +733,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, { struct smb_ace *ntace, *pndace; u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0; + u16 copied_nt_aces; unsigned short size = 0; int i; @@ -765,8 +767,10 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, } } + copied_nt_aces = num_aces; set_posix_acl_entries_dacl(idmap, pndace, fattr, - &num_aces, &size, nt_num_aces); + &num_aces, &size, copied_nt_aces, + nt_num_aces != 0); pndacl->num_aces = cpu_to_le16(num_aces); pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); } @@ -784,7 +788,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap, if (fattr->cf_acls) { set_posix_acl_entries_dacl(idmap, pndace, fattr, - &num_aces, &size, num_aces); + &num_aces, &size, num_aces, false); goto out; } From 337022d9dfac441c3b35e4455a51aa981996e02e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 30 Jul 2026 03:00:29 +0800 Subject: [PATCH 2828/3297] ksmbd: validate ACE size against SID sub-authorities commit 5152c6d49e3fd4e9f2e857c57527aead752f1f87 upstream. set_ntacl_dacl() validates sid.num_subauth before copying an ACE, but does not verify that the declared ACE size contains all sub-authorities described by that field. An undersized ACE can therefore be copied and later make the POSIX ACL deduplication walk inspect data beyond the copied ACE boundary. The existing initial bound check is also too small. It only ensures that the ACE size field is accessible before set_ntacl_dacl() reads sid.num_subauth farther into the input buffer. Require enough input for the fixed SID header before accessing num_subauth, reject ACEs smaller than that header, and skip ACEs whose declared size cannot contain the complete SID. This makes the validation consistent with the other ACE walk paths. Reported-by: LocalHost Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- fs/smb/server/smbacl.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index b0677e095c329..d3d0a22620f96 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -743,15 +743,22 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, for (i = 0; i < nt_num_aces; i++) { unsigned short nt_ace_size; - if (offsetof(struct smb_ace, access_req) > aces_size) + if (aces_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; nt_ace_size = le16_to_cpu(ntace->size); - if (nt_ace_size > aces_size) + if (nt_ace_size > aces_size || + nt_ace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; if (ntace->sid.num_subauth == 0 || - ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) + ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + nt_ace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE + + sizeof(__le32) * + ntace->sid.num_subauth) goto next_ace; memcpy((char *)pndace + size, ntace, nt_ace_size); From 81ea8e8221853950c47dac7164f27c63a96f8f86 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Jul 2026 08:19:05 -0700 Subject: [PATCH 2829/3297] fscrypt: Avoid dynamic allocation in fscrypt_get_devices() commit 6fe4e4b8259e1330945b5f3c9476e08473b8e0e8 upstream. When a blk_crypto_key starts being used or is evicted, fs/crypto/ calls fscrypt_get_devices() to get the filesystem's list of block devices, then iterates over them and calls blk_crypto_config_supported(), blk_crypto_start_using_key(), or blk_crypto_evict_key() on each one. Currently, the block device pointers are placed in a dynamically allocated array. This dynamic allocation is problematic because: - It can fail, especially at the fscrypt_destroy_inline_crypt_key() call site when it's invoked for inode eviction under direct reclaim. - fscrypt_destroy_inline_crypt_key() doesn't handle the failure. It just zeroizes and frees the blk_crypto_key without calling blk_crypto_evict_key(). That causes a use-after-free. For now, let's fix this in the straightforward and easily-backportable way by switching to an on-stack array. Currently the fscrypt multi-device functionality is used only by f2fs, which has a hardcoded limit of 8 block devices. An on-stack array works fine for that. (Of course, this solution won't scale up to large number of block devices. For that we'd need a different solution, like moving the block device iteration into the filesystem. Or in the case of btrfs, which will only support blk-crypto-fallback, we should make it just call blk-crypto-fallback directly, so the block devices won't be needed.) Fixes: 22e9947a4b2b ("fscrypt: stop holding extra request_queue references") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260713023708.9245-1-ebiggers%40kernel.org Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260719055602.78828-1-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Sasha Levin --- fs/crypto/inline_crypt.c | 57 ++++++++++++++-------------------------- fs/f2fs/super.c | 25 ++++++++++-------- include/linux/fscrypt.h | 18 +++++++------ 3 files changed, 44 insertions(+), 56 deletions(-) diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 645cc49360729..500397ca8a26f 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -22,22 +22,14 @@ #include "fscrypt_private.h" -static struct block_device **fscrypt_get_devices(struct super_block *sb, - unsigned int *num_devs) +static unsigned int +fscrypt_get_devices(struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]) { - struct block_device **devs; - - if (sb->s_cop->get_devices) { - devs = sb->s_cop->get_devices(sb, num_devs); - if (devs) - return devs; - } - devs = kmalloc(sizeof(*devs), GFP_KERNEL); - if (!devs) - return ERR_PTR(-ENOMEM); + if (sb->s_cop->get_devices) + return sb->s_cop->get_devices(sb, devs); devs[0] = sb->s_bdev; - *num_devs = 1; - return devs; + return 1; } static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_inode_info *ci) @@ -96,7 +88,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; struct blk_crypto_config crypto_cfg; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; @@ -135,20 +127,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, crypto_cfg.key_type = is_hw_wrapped_key ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; - devs = fscrypt_get_devices(sb, &num_devs); - if (IS_ERR(devs)) - return PTR_ERR(devs); - + num_devs = fscrypt_get_devices(sb, devs); for (i = 0; i < num_devs; i++) { if (!blk_crypto_config_supported(devs[i], &crypto_cfg)) - goto out_free_devs; + return 0; } fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg); ci->ci_inlinecrypt = true; -out_free_devs: - kfree(devs); return 0; } @@ -164,7 +151,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, enum blk_crypto_key_type key_type = is_hw_wrapped ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; struct blk_crypto_key *blk_key; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; int err; @@ -182,17 +169,12 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, } /* Start using blk-crypto on all the filesystem's block devices. */ - devs = fscrypt_get_devices(sb, &num_devs); - if (IS_ERR(devs)) { - err = PTR_ERR(devs); - goto fail; - } + num_devs = fscrypt_get_devices(sb, devs); for (i = 0; i < num_devs; i++) { err = blk_crypto_start_using_key(devs[i], blk_key); if (err) break; } - kfree(devs); if (err) { fscrypt_err(inode, "error %d starting to use blk-crypto", err); goto fail; @@ -210,20 +192,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key) { struct blk_crypto_key *blk_key = prep_key->blk_key; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; if (!blk_key) return; - /* Evict the key from all the filesystem's block devices. */ - devs = fscrypt_get_devices(sb, &num_devs); - if (!IS_ERR(devs)) { - for (i = 0; i < num_devs; i++) - blk_crypto_evict_key(devs[i], blk_key); - kfree(devs); - } + /* + * Evict the key from all the filesystem's block devices. + * This *must* be done before the key is freed. + */ + num_devs = fscrypt_get_devices(sb, devs); + for (i = 0; i < num_devs; i++) + blk_crypto_evict_key(devs[i], blk_key); + kfree_sensitive(blk_key); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f6b75ce11d1c1..6f787cbeefd09 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3659,24 +3659,27 @@ static bool f2fs_has_stable_inodes(struct super_block *sb) return true; } -static struct block_device **f2fs_get_devices(struct super_block *sb, - unsigned int *num_devs) +static unsigned int +f2fs_get_devices(struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]) { struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct block_device **devs; + int ndevs; int i; - if (!f2fs_is_multi_device(sbi)) - return NULL; + static_assert(MAX_DEVICES <= FSCRYPT_MAX_DEVICES); - devs = kmalloc_array(sbi->s_ndevs, sizeof(*devs), GFP_KERNEL); - if (!devs) - return ERR_PTR(-ENOMEM); + if (!f2fs_is_multi_device(sbi)) { + devs[0] = sb->s_bdev; + return 1; + } + ndevs = sbi->s_ndevs; + if (WARN_ON_ONCE(ndevs > FSCRYPT_MAX_DEVICES)) + ndevs = FSCRYPT_MAX_DEVICES; - for (i = 0; i < sbi->s_ndevs; i++) + for (i = 0; i < ndevs; i++) devs[i] = FDEV(i).bdev; - *num_devs = sbi->s_ndevs; - return devs; + return ndevs; } static const struct fscrypt_operations f2fs_cryptops = { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 516aba5b858b5..c009d4afe9185 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -57,6 +57,9 @@ struct fscrypt_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +/* Maximum supported number of block devices per filesystem */ +#define FSCRYPT_MAX_DEVICES 8 + #ifdef CONFIG_FS_ENCRYPTION /* Crypto operations for filesystems */ @@ -181,21 +184,20 @@ struct fscrypt_operations { bool (*has_stable_inodes)(struct super_block *sb); /* - * Return an array of pointers to the block devices to which the - * filesystem may write encrypted file contents, NULL if the filesystem - * only has a single such block device, or an ERR_PTR() on error. + * Retrieve the list of block devices to which the filesystem may write + * encrypted file contents. * - * On successful non-NULL return, *num_devs is set to the number of - * devices in the returned array. The caller must free the returned - * array using kfree(). + * This writes the block_device pointers to @devs and returns the count + * (between 1 and FSCRYPT_MAX_DEVICES inclusively). * * If the filesystem can use multiple block devices (other than block * devices that aren't used for encrypted file contents, such as * external journal devices), and wants to support inline encryption, * then it must implement this function. Otherwise it's not needed. */ - struct block_device **(*get_devices)(struct super_block *sb, - unsigned int *num_devs); + unsigned int (*get_devices)( + struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]); }; int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name, From 682a0066dde052d1841bcc614c3de917ac836be9 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 28 Jul 2026 23:33:40 -0700 Subject: [PATCH 2830/3297] drm/amd/display: Fix DTB DTO updates breaking live pixel rate sources commit 76a2db58e95e328007043f54ac3c7336ccbee440 upstream. dcn32_update_clocks_update_dtb_dto() and its dcn35 counterpart reprogram the DTB DTO of every timing generator in the context whenever the DTBCLK reference changes, passing a zeroed pixel rate and never setting is_hdmi. Both dccg set_dtbclk_dto() implementations treat a zero pixel rate as a disable request. On dcn32 that branch drives PIPE_DTO_SRC_SEL to the DP DTO source, so a timing generator actively scanning out an HDMI stream has its pixel rate source re-muxed out from under the live raster and the OTG stops on the spot. On dcn35 it clears DTBCLK_DTO_ENABLE and restores DTBCLK_Pn clock gating, which does the same to a live 128b/132b stream. Two displays where only one runs a 128b/132b link hit this reliably. is_dtbclk_required() holds the DTBCLK reference high while both are active, and the moment the 128b/132b stream is torn down (compositor switch, display disable, hot-unplug) the next safe_to_lower pass drops the reference to the lowest DPM level and the DTO walk freezes the surviving screen. On Navi31 the DAL mailbox then goes deaf on the DISPCLK hard-min that follows the walk in dcn32_update_clocks(), stranding both SMU mailboxes until reboot. Set is_hdmi for HDMI and DVI signals so the disable path leaves the pixel rate source selection on the HDMI path, and pass the real pixel rate for 128b/132b streams so a reference change rescales their DTO instead of disabling it. Fixes: 128c1ca0303f ("drm/amd/display: Update DTBCLK for DCN32") Fixes: 8774029f76b9 ("drm/amd/display: Add DCN35 CLK_MGR") Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher [ mschwartz: dcn32 and dcn35 clk_mgr hunks only. The rest is HDMI FRL enablement, absent before 7.2, so the FRL conditions and the req_audio_dtbclk_khz assignment they guard are dropped and the FRL-centric changelog is rewritten. Added the pipe_ctx->stream check the new dereferences need. ] Signed-off-by: Matthew Schwartz Signed-off-by: Sasha Levin --- .../gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c | 9 ++++++++- .../gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index 7da7b41bd0925..b98d946a8d2ee 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -276,13 +276,20 @@ static void dcn32_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, struct dtbclk_dto_params dto_params = {0}; /* use mask to program DTO once per tg */ - if (pipe_ctx->stream_res.tg && + if (pipe_ctx->stream && pipe_ctx->stream_res.tg && !(tg_mask & (1 << pipe_ctx->stream_res.tg->inst))) { tg_mask |= (1 << pipe_ctx->stream_res.tg->inst); dto_params.otg_inst = pipe_ctx->stream_res.tg->inst; dto_params.ref_dtbclk_khz = ref_dtbclk_khz; + if (dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) + dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10; + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal) || + dc_is_dvi_signal(pipe_ctx->stream->signal)) + dto_params.is_hdmi = true; + dccg->funcs->set_dtbclk_dto(clk_mgr->dccg, &dto_params); //dccg->funcs->set_audio_dtbclk_dto(clk_mgr->dccg, &dto_params); } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c index 817a0253d10e5..d1785a5c7f85f 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c @@ -264,13 +264,20 @@ static void dcn35_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, struct dtbclk_dto_params dto_params = {0}; /* use mask to program DTO once per tg */ - if (pipe_ctx->stream_res.tg && + if (pipe_ctx->stream && pipe_ctx->stream_res.tg && !(tg_mask & (1 << pipe_ctx->stream_res.tg->inst))) { tg_mask |= (1 << pipe_ctx->stream_res.tg->inst); dto_params.otg_inst = pipe_ctx->stream_res.tg->inst; dto_params.ref_dtbclk_khz = ref_dtbclk_khz; + if (dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) + dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10; + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal) || + dc_is_dvi_signal(pipe_ctx->stream->signal)) + dto_params.is_hdmi = true; + dccg->funcs->set_dtbclk_dto(clk_mgr->dccg, &dto_params); //dccg->funcs->set_audio_dtbclk_dto(clk_mgr->dccg, &dto_params); } From a95b62759f3b9ea0f076e958fe28e2edabc07cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Tue, 21 Jul 2026 15:36:00 -0400 Subject: [PATCH 2831/3297] landlock: Fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 405ca72dc589dd746e5ee5378bb9d9ee7f844010 ] Auto-format with clang-format -i security/landlock/*.[ch] Cc: Günther Noack Cc: Kees Cook Fixes: 69050f8d6d07 ("treewide: Replace kmalloc with kmalloc_obj for non-scalar types") Reviewed-by: Günther Noack Link: https://lore.kernel.org/r/20260303173632.88040-1-mic@digikod.net Signed-off-by: Mickaël Salaün Stable-dep-of: b232bd12789f ("landlock: Account all audit data allocations to user space") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- security/landlock/domain.c | 3 +-- security/landlock/ruleset.c | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/security/landlock/domain.c b/security/landlock/domain.c index a647b68e8d060..a1377c7d7e9b8 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -94,8 +94,7 @@ static struct landlock_details *get_current_details(void) * allocate with GFP_KERNEL_ACCOUNT because it is independent from the * caller. */ - details = - kzalloc(struct_size(details, exe_path, path_size), GFP_KERNEL); + details = kzalloc_flex(*details, exe_path, path_size); if (!details) return ERR_PTR(-ENOMEM); diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index ce7940efea516..2a1e57ac0c537 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -33,9 +33,8 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers) { struct landlock_ruleset *new_ruleset; - new_ruleset = - kzalloc(struct_size(new_ruleset, access_masks, num_layers), - GFP_KERNEL_ACCOUNT); + new_ruleset = kzalloc_flex(*new_ruleset, access_masks, num_layers, + GFP_KERNEL_ACCOUNT); if (!new_ruleset) return ERR_PTR(-ENOMEM); refcount_set(&new_ruleset->usage, 1); @@ -553,7 +552,7 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent, return new_dom; new_dom->hierarchy = - kzalloc(sizeof(*new_dom->hierarchy), GFP_KERNEL_ACCOUNT); + kzalloc_obj(*new_dom->hierarchy, GFP_KERNEL_ACCOUNT); if (!new_dom->hierarchy) return ERR_PTR(-ENOMEM); From c5772ced573ea4be02b6a95a0b7d8998ae8a6830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Tue, 21 Jul 2026 15:36:01 -0400 Subject: [PATCH 2832/3297] landlock: Account all audit data allocations to user space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b232bd12789fa57405b5092f28788be97aae9999 ] Mark the kzalloc_flex() of struct landlock_details with GFP_KERNEL_ACCOUNT so the allocation is charged to the calling task, like the other Landlock per-domain allocations which have used GFP_KERNEL_ACCOUNT forever. Every property of landlock_details is caller-attributable: allocated by landlock_restrict_self(2), owned by the caller's landlock_hierarchy, contents are the caller's pid, uid, comm, and exe_path, lifetime bounded by the caller's domain. While the caller may not know nor control the size of this allocation (i.e. exe_path), this data should still be accounted for it. The deciding factor is whether userspace can trigger the allocation, not whether the size of the data is known nor controlled by the caller. This aligns with the kmemcg accounting policy established by commit 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg"). No new failure modes: the hierarchy and ruleset are allocated before details and are already accounted, so landlock_restrict_self(2) already returns -ENOMEM under memcg pressure. This change widens that existing failure window slightly; it does not introduce a new error code. Cc: Günther Noack Cc: Paul Moore Cc: stable@vger.kernel.org Fixes: 1d636984e088 ("landlock: Add AUDIT_LANDLOCK_DOMAIN and log domain status") Link: https://patch.msgid.link/20260513180309.165840-1-mic@digikod.net Signed-off-by: Mickaël Salaün Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- security/landlock/domain.c | 9 +++++---- security/landlock/domain.h | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/security/landlock/domain.c b/security/landlock/domain.c index a1377c7d7e9b8..74fbe8de1fc5f 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -90,11 +90,12 @@ static struct landlock_details *get_current_details(void) return ERR_CAST(buffer); /* - * Create the new details according to the path's length. Do not - * allocate with GFP_KERNEL_ACCOUNT because it is independent from the - * caller. + * Create the new details according to the path's length. Account to + * the calling task's memcg, like the other Landlock per-domain + * allocations, even if it may not control the related size. */ - details = kzalloc_flex(*details, exe_path, path_size); + details = + kzalloc_flex(*details, exe_path, path_size, GFP_KERNEL_ACCOUNT); if (!details) return ERR_PTR(-ENOMEM); diff --git a/security/landlock/domain.h b/security/landlock/domain.h index 7fb70b25f85a1..e3525da27b935 100644 --- a/security/landlock/domain.h +++ b/security/landlock/domain.h @@ -33,10 +33,7 @@ enum landlock_log_status { * Rarely accessed, mainly when logging the first domain's denial. * * The contained pointers are initialized at the domain creation time and never - * changed again. Contrary to most other Landlock object types, this one is - * not allocated with GFP_KERNEL_ACCOUNT because its size may not be under the - * caller's control (e.g. unknown exe_path) and the data is not explicitly - * requested nor used by tasks. + * changed again. */ struct landlock_details { /** From eef6914f2b456f76358e07396505f179a8d279d4 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 21 Jul 2026 15:36:11 -0400 Subject: [PATCH 2833/3297] audit: widen ino fields to u64 [ Upstream commit 125dfa218134df7cc112667e92984de9d8cd0bf6 ] inode->i_ino is being widened from unsigned long to u64. The audit subsystem uses unsigned long ino in struct fields, function parameters, and local variables that store inode numbers from arbitrary filesystems. On 32-bit platforms this truncates inode numbers that exceed 32 bits, which will cause incorrect audit log entries and broken watch/mark comparisons. Widen all audit ino fields, parameters, and locals to u64, and update the inode format string from %lu to %llu to match. Signed-off-by: Jeff Layton Link: https://patch.msgid.link/20260304-iino-u64-v3-2-2257ad83d372@kernel.org Acked-by: Paul Moore Signed-off-by: Christian Brauner Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/audit.h | 2 +- kernel/audit.h | 13 ++++++------- kernel/audit_fsnotify.c | 4 ++-- kernel/audit_watch.c | 12 ++++++------ kernel/auditsc.c | 4 ++-- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index b8d8029c6c480..4ab9310ba2881 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -16,7 +16,7 @@ #include #include -#define AUDIT_INO_UNSET ((unsigned long)-1) +#define AUDIT_INO_UNSET ((u64)-1) #define AUDIT_DEV_UNSET ((dev_t)-1) struct audit_sig_info { diff --git a/kernel/audit.h b/kernel/audit.h index 0f05933a173be..bf25675fc718f 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -76,7 +76,7 @@ struct audit_names { int name_len; /* number of chars to log */ bool hidden; /* don't log this record */ - unsigned long ino; + u64 ino; dev_t dev; umode_t mode; kuid_t uid; @@ -225,9 +225,9 @@ extern int auditd_test_task(struct task_struct *task); #define AUDIT_INODE_BUCKETS 32 extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; -static inline int audit_hash_ino(u32 ino) +static inline int audit_hash_ino(u64 ino) { - return (ino & (AUDIT_INODE_BUCKETS-1)); + return ((u32)ino & (AUDIT_INODE_BUCKETS-1)); } /* Indicates that audit should log the full pathname. */ @@ -277,16 +277,15 @@ extern int audit_to_watch(struct audit_krule *krule, char *path, int len, extern int audit_add_watch(struct audit_krule *krule, struct list_head **list); extern void audit_remove_watch_rule(struct audit_krule *krule); extern char *audit_watch_path(struct audit_watch *watch); -extern int audit_watch_compare(struct audit_watch *watch, unsigned long ino, - dev_t dev); +extern int audit_watch_compare(struct audit_watch *watch, u64 ino, dev_t dev); extern struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, int len); extern char *audit_mark_path(struct audit_fsnotify_mark *mark); extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark); extern void audit_remove_mark_rule(struct audit_krule *krule); -extern int audit_mark_compare(struct audit_fsnotify_mark *mark, - unsigned long ino, dev_t dev); +extern int audit_mark_compare(struct audit_fsnotify_mark *mark, u64 ino, + dev_t dev); extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old); extern int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark); diff --git a/kernel/audit_fsnotify.c b/kernel/audit_fsnotify.c index 0046f93ae9144..35753fb9029a2 100644 --- a/kernel/audit_fsnotify.c +++ b/kernel/audit_fsnotify.c @@ -25,7 +25,7 @@ */ struct audit_fsnotify_mark { dev_t dev; /* associated superblock device */ - unsigned long ino; /* associated inode number */ + u64 ino; /* associated inode number */ char *path; /* insertion path */ struct fsnotify_mark mark; /* fsnotify mark on the inode */ struct audit_krule *rule; @@ -57,7 +57,7 @@ char *audit_mark_path(struct audit_fsnotify_mark *mark) return mark->path; } -int audit_mark_compare(struct audit_fsnotify_mark *mark, unsigned long ino, dev_t dev) +int audit_mark_compare(struct audit_fsnotify_mark *mark, u64 ino, dev_t dev) { if (mark->ino == AUDIT_INO_UNSET) return 0; diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index a700e3c8925ff..e64172dc69bd5 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -37,7 +37,7 @@ struct audit_watch { refcount_t count; /* reference count */ dev_t dev; /* associated superblock device */ char *path; /* insertion path */ - unsigned long ino; /* associated inode number */ + u64 ino; /* associated inode number */ struct audit_parent *parent; /* associated parent */ struct list_head wlist; /* entry in parent->watches list */ struct list_head rules; /* anchor for krule->rlist */ @@ -125,7 +125,7 @@ char *audit_watch_path(struct audit_watch *watch) return watch->path; } -int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev) +int audit_watch_compare(struct audit_watch *watch, u64 ino, dev_t dev) { return (watch->ino != AUDIT_INO_UNSET) && (watch->ino == ino) && @@ -244,7 +244,7 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc /* Update inode info in audit rules based on filesystem event. */ static void audit_update_watch(struct audit_parent *parent, const struct qstr *dname, dev_t dev, - unsigned long ino, unsigned invalidating) + u64 ino, unsigned invalidating) { struct audit_watch *owatch, *nwatch, *nextw; struct audit_krule *r, *nextr; @@ -285,7 +285,7 @@ static void audit_update_watch(struct audit_parent *parent, list_del(&oentry->rule.list); audit_panic("error updating watch, removing"); } else { - int h = audit_hash_ino((u32)ino); + int h = audit_hash_ino(ino); /* * nentry->rule.watch == oentry->rule.watch so @@ -439,7 +439,7 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list) audit_add_to_parent(krule, parent); - h = audit_hash_ino((u32)watch->ino); + h = audit_hash_ino(watch->ino); *list = &audit_inode_hash[h]; error: path_put(&parent_path); @@ -527,7 +527,7 @@ int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old) int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark) { struct file *exe_file; - unsigned long ino; + u64 ino; dev_t dev; /* only do exe filtering if we are recording @current events/records */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 3e42d32ebb285..40917dc4137b5 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -886,7 +886,7 @@ static int audit_filter_inode_name(struct task_struct *tsk, struct audit_names *n, struct audit_context *ctx) { - int h = audit_hash_ino((u32)n->ino); + int h = audit_hash_ino(n->ino); struct list_head *list = &audit_inode_hash[h]; return __audit_filter_op(tsk, ctx, list, n, ctx->major); @@ -1534,7 +1534,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, audit_log_format(ab, " name=(null)"); if (n->ino != AUDIT_INO_UNSET) - audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#ho ouid=%u ogid=%u rdev=%02x:%02x", + audit_log_format(ab, " inode=%llu dev=%02x:%02x mode=%#ho ouid=%u ogid=%u rdev=%02x:%02x", n->ino, MAJOR(n->dev), MINOR(n->dev), From 91b64f0be416358febc3f82fe819b8922a1db05e Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Tue, 21 Jul 2026 15:36:12 -0400 Subject: [PATCH 2834/3297] audit: use 'unsigned int' instead of 'unsigned' [ Upstream commit 8b226771014beab1292081151a99530886ce54b4 ] Address checkpatch.pl warning below, across the audit subsystem: WARNING: Prefer 'unsigned int' to bare use of 'unsigned' Minor cleanup, no functional changes. Signed-off-by: Ricardo Robaina Signed-off-by: Paul Moore Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/audit.h | 4 ++-- include/linux/audit_arch.h | 12 ++++++------ kernel/audit.c | 2 +- kernel/audit.h | 2 +- kernel/audit_tree.c | 2 +- kernel/audit_watch.c | 2 +- kernel/auditfilter.c | 8 ++++---- kernel/auditsc.c | 2 +- lib/compat_audit.c | 12 ++++++------ 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 4ab9310ba2881..09d06684174ba 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -125,8 +125,8 @@ enum audit_nfcfgop { AUDIT_NFT_OP_INVALID, }; -extern int __init audit_register_class(int class, unsigned *list); -extern int audit_classify_syscall(int abi, unsigned syscall); +extern int __init audit_register_class(int class, unsigned int *list); +extern int audit_classify_syscall(int abi, unsigned int syscall); extern int audit_classify_arch(int arch); /* audit_names->type values */ diff --git a/include/linux/audit_arch.h b/include/linux/audit_arch.h index 2b8153791e6a5..a35069a6c15de 100644 --- a/include/linux/audit_arch.h +++ b/include/linux/audit_arch.h @@ -21,13 +21,13 @@ enum auditsc_class_t { AUDITSC_NVALS /* count */ }; -extern int audit_classify_compat_syscall(int abi, unsigned syscall); +extern int audit_classify_compat_syscall(int abi, unsigned int syscall); /* only for compat system calls */ -extern unsigned compat_write_class[]; -extern unsigned compat_read_class[]; -extern unsigned compat_dir_class[]; -extern unsigned compat_chattr_class[]; -extern unsigned compat_signal_class[]; +extern unsigned int compat_write_class[]; +extern unsigned int compat_read_class[]; +extern unsigned int compat_dir_class[]; +extern unsigned int compat_chattr_class[]; +extern unsigned int compat_signal_class[]; #endif diff --git a/kernel/audit.c b/kernel/audit.c index db7868945a877..e175fce41ef93 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -2030,7 +2030,7 @@ void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args) * here and AUDIT_BUFSIZ is at least 1024, then we can * log everything that printk could have logged. */ avail = audit_expand(ab, - max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail)); + max_t(unsigned int, AUDIT_BUFSIZ, 1+len-avail)); if (!avail) goto out_va_end; len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2); diff --git a/kernel/audit.h b/kernel/audit.h index bf25675fc718f..7c56035627721 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -233,7 +233,7 @@ static inline int audit_hash_ino(u64 ino) /* Indicates that audit should log the full pathname. */ #define AUDIT_NAME_FULL -1 -extern int audit_match_class(int class, unsigned syscall); +extern int audit_match_class(int class, unsigned int syscall); extern int audit_comparator(const u32 left, const u32 op, const u32 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index fda6beb041e05..f2c13a62bfb63 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -33,7 +33,7 @@ struct audit_chunk { struct audit_node { struct list_head list; struct audit_tree *owner; - unsigned index; /* index; upper bit indicates 'will prune' */ + unsigned int index; /* index; upper bit indicates 'will prune' */ } owners[] __counted_by(count); }; diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index e64172dc69bd5..f7b101e71d002 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -244,7 +244,7 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc /* Update inode info in audit rules based on filesystem event. */ static void audit_update_watch(struct audit_parent *parent, const struct qstr *dname, dev_t dev, - u64 ino, unsigned invalidating) + u64 ino, unsigned int invalidating) { struct audit_watch *owatch, *nwatch, *nextw; struct audit_krule *r, *nextr; diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index c401082d9b250..2eae8483fb19c 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -165,13 +165,13 @@ static inline int audit_to_inode(struct audit_krule *krule, static __u32 *classes[AUDIT_SYSCALL_CLASSES]; -int __init audit_register_class(int class, unsigned *list) +int __init audit_register_class(int class, unsigned int *list) { __u32 *p = kcalloc(AUDIT_BITMASK_SIZE, sizeof(__u32), GFP_KERNEL); if (!p) return -ENOMEM; while (*list != ~0U) { - unsigned n = *list++; + unsigned int n = *list++; if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) { kfree(p); return -EINVAL; @@ -186,7 +186,7 @@ int __init audit_register_class(int class, unsigned *list) return 0; } -int audit_match_class(int class, unsigned syscall) +int audit_match_class(int class, unsigned int syscall) { if (unlikely(syscall >= AUDIT_BITMASK_SIZE * 32)) return 0; @@ -237,7 +237,7 @@ static int audit_match_signal(struct audit_entry *entry) /* Common user-space to kernel rule translation. */ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data *rule) { - unsigned listnr; + unsigned int listnr; struct audit_entry *entry; int i, err; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 40917dc4137b5..c547add130284 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -150,7 +150,7 @@ static const struct audit_nfcfgop_tab audit_nfcfgs[] = { static int audit_match_perm(struct audit_context *ctx, int mask) { - unsigned n; + unsigned int n; if (unlikely(!ctx)) return 0; diff --git a/lib/compat_audit.c b/lib/compat_audit.c index 3d6b8996f027d..fee1dfccd116b 100644 --- a/lib/compat_audit.c +++ b/lib/compat_audit.c @@ -4,32 +4,32 @@ #include #include -unsigned compat_dir_class[] = { +unsigned int compat_dir_class[] = { #include ~0U }; -unsigned compat_read_class[] = { +unsigned int compat_read_class[] = { #include ~0U }; -unsigned compat_write_class[] = { +unsigned int compat_write_class[] = { #include ~0U }; -unsigned compat_chattr_class[] = { +unsigned int compat_chattr_class[] = { #include ~0U }; -unsigned compat_signal_class[] = { +unsigned int compat_signal_class[] = { #include ~0U }; -int audit_classify_compat_syscall(int abi, unsigned syscall) +int audit_classify_compat_syscall(int abi, unsigned int syscall) { switch (syscall) { #ifdef __NR_open From 40879c39d6740f3dddfb52b5d6ba7fb8cceb84d8 Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Tue, 21 Jul 2026 15:36:13 -0400 Subject: [PATCH 2835/3297] audit: fix recursive locking deadlock in audit_dupe_exe() [ Upstream commit 81905b5acbe77284734438df3fbec1158e6429a3 ] A deadlock occurs in the audit subsystem when duplicating executable-related rules. When a file is moved (e.g., via do_renameat2()), the VFS layer locks the parent directory (I_MUTEX_PARENT), which synchronously triggers an fsnotify_move event. If an existing executable audit rule matches the file being moved, the audit subsystem catches this event and calls audit_dupe_exe() to duplicate the watch and update the rule. Then, audit_alloc_mark() would call kern_path_parent() to resolve the path, leading to a blind attempt to acquire the exact same I_MUTEX_PARENT lock already held by the task, resulting in the following recursive locking deadlock: ============================================ WARNING: possible recursive locking detected 6.12.0-55.27.1.el10_0.x86_64+debug #1 Not tainted -------------------------------------------- mv/5099 is trying to acquire lock: ffff888132845358 (&inode->i_sb->s_type->i_mutex_dir_key/1){+.+.}-{3:3}, at: __kern_path_locked+0x10a/0x2f0 but task is already holding lock: ffff888132846b58 (&inode->i_sb->s_type->i_mutex_dir_key/1){+.+.}-{3:3}, at: lock_two_directories+0x13f/0x2b0 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&inode->i_sb->s_type->i_mutex_dir_key/1); lock(&inode->i_sb->s_type->i_mutex_dir_key/1); *** DEADLOCK *** May be due to missing lock nesting notation 6 locks held by mv/5099: #0: ffff888112a9c440 (sb_writers#13) at: do_renameat2+0x34c/0xbc0 #1: ffff888112a9c790 (&type->s_vfs_rename_key#3) at: do_renameat2+0x415/0xbc0 #2: ffff888132846b58 (&inode->i_sb->s_type->i_mutex_dir_key/1) at: lock_two_directories+0x13f/0x2b0 #3: ffff888132845358 (&inode->i_sb->s_type->i_mutex_dir_key/5) at: lock_two_directories+0x175/0x2b0 #4: ffffffffb3a1fb10 (&fsnotify_mark_srcu) at: fsnotify+0x454/0x28a0 #5: ffffffffaf886230 (audit_filter_mutex) at: audit_update_watch+0x36/0x11e0 stack backtrace: Call Trace: dump_stack_lvl+0x6f/0xb0 print_deadlock_bug.cold+0xbd/0xca validate_chain+0x83a/0xf00 __lock_acquire+0xcac/0x1d20 lock_acquire.part.0+0x11b/0x360 down_write_nested+0x9f/0x230 __kern_path_locked+0x10a/0x2f0 kern_path_locked+0x26/0x40 audit_alloc_mark+0xfb/0x4f0 audit_dupe_exe+0x6c/0xe0 audit_dupe_rule+0x6c2/0xc00 audit_update_watch+0x4cc/0x11e0 audit_watch_handle_event+0x12c/0x1b0 send_to_group+0x5d0/0x8b0 fsnotify+0x615/0x28a0 fsnotify_move+0x1d8/0x630 vfs_rename+0xdcd/0x1df0 do_renameat2+0x9d4/0xbc0 __x64_sys_renameat+0x192/0x260 do_syscall_64+0x92/0x180 entry_SYSCALL_64_after_hwframe+0x76/0x7e RIP: 0033:0x7f0491fe8c4e Code: 0f 1f 40 00 48 8b 15 c1 e1 16 00 f7 d8 64 89 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 08 01 00 00 0f 05 <48> 3d 00 f0 ff ff 77 0a c3 66 0f 1f 84 00 00 00 00 00 48 8b 15 89 RSP: 002b:00007ffc7210bf38 EFLAGS: 00000246 ORIG_RAX: 0000000000000108 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f0491fe8c4e RDX: 0000000000000003 RSI: 00007ffc7210e6c8 RDI: 00000000ffffff9c RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000001 R10: 00005575eb2dae2a R11: 0000000000000246 R12: 00005575eb2dae2a R13: 00007ffc7210e6c8 R14: 0000000000000003 R15: 00000000ffffff9c The aforementioned deadlock can be consistently reproduced by running the script below: audit-dupe-exe-deadlock.sh -------------------------- #!/bin/bash auditctl -D mkdir -p /tmp/foo touch /tmp/file auditctl -a always,exit -F exe=/tmp/file -F path=/tmp/file -S all -k dr mv /tmp/file /tmp/foo/file rm -Rf /tmp/foo This patch fixes the issue by introducing struct audit_watch_ctx to pass the fsnotify event context down to audit_alloc_mark(). By utilizing the already-resolved directory inode provided by the event, we bypass the kern_path_parent() path resolution entirely, safely avoiding the recursive lock. Furthermore, it explicitly allows duplicate fsnotify marks (allow_dups = 1) during the rename update, allowing the new rule's mark to safely coexist with the old rule's mark until the old rule is freed. P.S.: This issue was identified and reproduced during a comprehensive code coverage analysis of the audit subsystem. The full report is available at the link below: https://people.redhat.com/rrobaina/audit-code-coverage-analysis.pdf P.P.S: With the permission of both Ricardo and Nathan, I've squashed a fixup patch from Nathan that addresses a compile time error when CONFIG_AUDITSYSCALL=n. Cc: stable@kernel.org Fixes: 34d99af52ad4 ("audit: implement audit by executable") Acked-by: Waiman Long Acked-by: Richard Guy Briggs Signed-off-by: Nathan Chancellor Signed-off-by: Ricardo Robaina [PM: move link metadata into the msg, apply fix from NC] Signed-off-by: Paul Moore Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/audit.h | 17 ++++++++++++----- kernel/audit_fsnotify.c | 32 +++++++++++++++++++++++--------- kernel/audit_watch.c | 25 +++++++++++++++++-------- kernel/auditfilter.c | 9 +++++---- 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/kernel/audit.h b/kernel/audit.h index 7c56035627721..08bd61f431b20 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -256,8 +256,13 @@ extern int audit_del_rule(struct audit_entry *entry); extern void audit_free_rule_rcu(struct rcu_head *head); extern struct list_head audit_filter_list[]; -extern struct audit_entry *audit_dupe_rule(struct audit_krule *old); +struct audit_watch_ctx { + struct inode *dir; + struct inode *child; +}; +extern struct audit_entry *audit_dupe_rule(struct audit_krule *old, + struct audit_watch_ctx *ctx); extern void audit_log_d_path_exe(struct audit_buffer *ab, struct mm_struct *mm); @@ -280,13 +285,15 @@ extern char *audit_watch_path(struct audit_watch *watch); extern int audit_watch_compare(struct audit_watch *watch, u64 ino, dev_t dev); extern struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, - char *pathname, int len); + char *pathname, int len, + struct audit_watch_ctx *ctx); extern char *audit_mark_path(struct audit_fsnotify_mark *mark); extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark); extern void audit_remove_mark_rule(struct audit_krule *krule); extern int audit_mark_compare(struct audit_fsnotify_mark *mark, u64 ino, dev_t dev); -extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old); +extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old, + struct audit_watch_ctx *ctx); extern int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark); @@ -317,13 +324,13 @@ extern struct list_head *audit_killed_trees(void); #define audit_watch_path(w) "" #define audit_watch_compare(w, i, d) 0 -#define audit_alloc_mark(k, p, l) (ERR_PTR(-EINVAL)) +#define audit_alloc_mark(k, p, l, c) (ERR_PTR(-EINVAL)) #define audit_mark_path(m) "" #define audit_remove_mark(m) do { } while (0) #define audit_remove_mark_rule(k) do { } while (0) #define audit_mark_compare(m, i, d) 0 #define audit_exe_compare(t, m) (-EINVAL) -#define audit_dupe_exe(n, o) (-EINVAL) +#define audit_dupe_exe(n, o, c) (-EINVAL) #define audit_remove_tree_rule(rule) BUG() #define audit_add_tree_rule(rule) -EINVAL diff --git a/kernel/audit_fsnotify.c b/kernel/audit_fsnotify.c index 35753fb9029a2..e62066bf0b0af 100644 --- a/kernel/audit_fsnotify.c +++ b/kernel/audit_fsnotify.c @@ -71,19 +71,30 @@ static void audit_update_mark(struct audit_fsnotify_mark *audit_mark, audit_mark->ino = inode ? inode->i_ino : AUDIT_INO_UNSET; } -struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, int len) +struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, + int len, struct audit_watch_ctx *ctx) { struct audit_fsnotify_mark *audit_mark; struct path path; struct dentry *dentry; - int ret; + struct inode *dir, *child; + int ret, allow_dups; if (pathname[0] != '/' || pathname[len-1] == '/') return ERR_PTR(-EINVAL); - dentry = kern_path_parent(pathname, &path); - if (IS_ERR(dentry)) - return ERR_CAST(dentry); /* returning an error */ + if (!ctx) { + dentry = kern_path_parent(pathname, &path); + if (IS_ERR(dentry)) + return ERR_CAST(dentry); /* returning an error */ + dir = d_inode(path.dentry); + child = d_inode(dentry); + allow_dups = 0; + } else { + dir = ctx->dir; + child = ctx->child; + allow_dups = 1; + } audit_mark = kzalloc(sizeof(*audit_mark), GFP_KERNEL); if (unlikely(!audit_mark)) { @@ -94,18 +105,21 @@ struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pa fsnotify_init_mark(&audit_mark->mark, audit_fsnotify_group); audit_mark->mark.mask = AUDIT_FS_EVENTS; audit_mark->path = pathname; - audit_update_mark(audit_mark, dentry->d_inode); audit_mark->rule = krule; - ret = fsnotify_add_inode_mark(&audit_mark->mark, path.dentry->d_inode, 0); + audit_update_mark(audit_mark, child); + ret = fsnotify_add_inode_mark(&audit_mark->mark, dir, allow_dups); + if (ret < 0) { audit_mark->path = NULL; fsnotify_put_mark(&audit_mark->mark); audit_mark = ERR_PTR(ret); } out: - dput(dentry); - path_put(&path); + if (!ctx) { + dput(dentry); + path_put(&path); + } return audit_mark; } diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index f7b101e71d002..ec88f38ee685b 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -244,7 +244,8 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc /* Update inode info in audit rules based on filesystem event. */ static void audit_update_watch(struct audit_parent *parent, const struct qstr *dname, dev_t dev, - u64 ino, unsigned int invalidating) + u64 ino, unsigned int invalidating, + struct audit_watch_ctx *ctx) { struct audit_watch *owatch, *nwatch, *nextw; struct audit_krule *r, *nextr; @@ -280,7 +281,7 @@ static void audit_update_watch(struct audit_parent *parent, list_del(&oentry->rule.rlist); list_del_rcu(&oentry->list); - nentry = audit_dupe_rule(&oentry->rule); + nentry = audit_dupe_rule(&oentry->rule, ctx); if (IS_ERR(nentry)) { list_del(&oentry->rule.list); audit_panic("error updating watch, removing"); @@ -479,10 +480,17 @@ static int audit_watch_handle_event(struct fsnotify_mark *inode_mark, u32 mask, if (WARN_ON_ONCE(inode_mark->group != audit_watch_group)) return 0; - if (mask & (FS_CREATE|FS_MOVED_TO) && inode) - audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0); - else if (mask & (FS_DELETE|FS_MOVED_FROM)) - audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1); + if (mask & (FS_CREATE|FS_MOVED_TO) && inode) { + struct audit_watch_ctx ctx = { .dir = dir, .child = inode }; + + audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0, + &ctx); + } else if (mask & (FS_DELETE|FS_MOVED_FROM)) { + struct audit_watch_ctx ctx = { .dir = dir, .child = NULL }; + + audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1, + &ctx); + } else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF)) audit_remove_parent_watches(parent); @@ -505,7 +513,8 @@ static int __init audit_watch_init(void) } device_initcall(audit_watch_init); -int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old) +int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old, + struct audit_watch_ctx *ctx) { struct audit_fsnotify_mark *audit_mark; char *pathname; @@ -514,7 +523,7 @@ int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old) if (!pathname) return -ENOMEM; - audit_mark = audit_alloc_mark(new, pathname, strlen(pathname)); + audit_mark = audit_alloc_mark(new, pathname, strlen(pathname), ctx); if (IS_ERR(audit_mark)) { kfree(pathname); return PTR_ERR(audit_mark); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 2eae8483fb19c..d0bd6e549634f 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -590,7 +590,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, err = PTR_ERR(str); goto exit_free; } - audit_mark = audit_alloc_mark(&entry->rule, str, f_val); + audit_mark = audit_alloc_mark(&entry->rule, str, f_val, NULL); if (IS_ERR(audit_mark)) { kfree(str); err = PTR_ERR(audit_mark); @@ -818,7 +818,8 @@ static inline int audit_dupe_lsm_field(struct audit_field *df, * rule with the new rule in the filterlist, then free the old rule. * The rlist element is undefined; list manipulations are handled apart from * the initial copy. */ -struct audit_entry *audit_dupe_rule(struct audit_krule *old) +struct audit_entry *audit_dupe_rule(struct audit_krule *old, + struct audit_watch_ctx *ctx) { u32 fcount = old->field_count; struct audit_entry *entry; @@ -877,7 +878,7 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old) new->filterkey = fk; break; case AUDIT_EXE: - err = audit_dupe_exe(new, old); + err = audit_dupe_exe(new, old, ctx); break; } if (err) { @@ -1416,7 +1417,7 @@ static int update_lsm_rule(struct audit_krule *r) if (!security_audit_rule_known(r)) return 0; - nentry = audit_dupe_rule(r); + nentry = audit_dupe_rule(r, NULL); if (entry->rule.exe) audit_remove_mark(entry->rule.exe); if (IS_ERR(nentry)) { From 3bca70235a706de76fe9a81defd37d987062c686 Mon Sep 17 00:00:00 2001 From: Joanne Koong Date: Wed, 22 Jul 2026 07:33:50 -0400 Subject: [PATCH 2836/3297] fuse-uring: fix race between registration and connection abortion [ Upstream commit 952b5d36f6a298f57c52a59e72076c69386a8aaf ] This fixes this race: - thread a: io_uring_enter -> register sqe -> fuse_uring_create_ring_ent -> allocate ent but doesn't grab queue_ref yet - thread b: fuse_conn_destroy() -> fuse_chan_abort() -> fuse_uring_abort() is a no-op due to queue ref being 0 - thread a: grabs the queue_ref, queue_ref is now 1, rest of fuse_uring_do_register() logic executes - thread b: fuse_chan_abort() returns, fuse_chan_wait_aborted() now runs and calls "wait_event(ring->stop_waitq, atomic_read(&ring->queue_refs) == 0);" The abort/unmount thread will hang indefinitely in unkillable state as nothing will decrement queue_refs or wake stop_waitq, and the ring, queue, and ent are leaked. Fix this by checking fch->connected under fch->lock after the created ent has grabbed a ref count on the queue. This ensures that in the scenario above, it is guaranteed that we either release the queue ref and wake up stop_waitq (in case fuse_chan_wait_aborted() is already waiting) in fuse_uring_do_register() when we detect !fch->connected, or if the connection is aborted after the check, it is guaranteed that the async teardown worker will be running in the background cleaning up ents and decrementing the ent's ref on the queue, which will unblock the eventual queue and ring teardown. Fixes: 24fe962c86f5 ("fuse: {io-uring} Handle SQEs - register commands") Cc: stable@vger.kernel.org Reviewed-by: Bernd Schubert Signed-off-by: Joanne Koong Signed-off-by: Miklos Szeredi [ changed fch->lock/fch->connected references to fc->lock/fc->connected since struct fuse_chan does not exist in this tree ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dev_uring.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 42c82b5476593..6f74c345080f4 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -998,15 +998,26 @@ static bool is_ring_ready(struct fuse_ring *ring, int current_qid) /* * fuse_uring_req_fetch command handling */ -static void fuse_uring_do_register(struct fuse_ring_ent *ent, - struct io_uring_cmd *cmd, - unsigned int issue_flags) +static int fuse_uring_do_register(struct fuse_ring_ent *ent, + struct io_uring_cmd *cmd, + unsigned int issue_flags) { struct fuse_ring_queue *queue = ent->queue; struct fuse_ring *ring = queue->ring; struct fuse_conn *fc = ring->fc; struct fuse_iqueue *fiq = &fc->iq; + spin_lock(&fc->lock); + /* abort teardown path is running or has run */ + if (!fc->connected) { + spin_unlock(&fc->lock); + if (atomic_dec_and_test(&ring->queue_refs)) + wake_up_all(&ring->stop_waitq); + kfree(ent); + return -ECONNABORTED; + } + spin_unlock(&fc->lock); + fuse_uring_prepare_cancel(cmd, issue_flags, ent); spin_lock(&queue->lock); @@ -1023,6 +1034,7 @@ static void fuse_uring_do_register(struct fuse_ring_ent *ent, wake_up_all(&fc->blocked_waitq); } } + return 0; } /* @@ -1138,9 +1150,7 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, if (IS_ERR(ent)) return PTR_ERR(ent); - fuse_uring_do_register(ent, cmd, issue_flags); - - return 0; + return fuse_uring_do_register(ent, cmd, issue_flags); } /* From b1f38c3ec620a37007dff25fc6db7db592558a72 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 22 Jul 2026 07:33:53 -0400 Subject: [PATCH 2837/3297] xfs: don't replace the wrong part of the cow fork [ Upstream commit a1caeeadbf57ff86dfc3454398c46de86056a74e ] LOLLM points out that xfs_iext_lookup_extent can return a @got where got->br_startoff < startoff. In this case, xrep_cow_replace_range replaces the entire mapping instead of just the part that had been marked bad in the bitmap, but advances the bitmap cursor in xrep_cow_replace by the amount replaced. As a result, we fail to replace the end of the bad range, and replace part of the good range. Fix this by rewriting the replace method to handle replacing the middle of a cow fork mapping. This we do by returning both the current mapping as @got, and the subset of the mapping that we want to replace as @rep, using @rep to store the results of the new allocation, and comparing @rep to @got to figure out the exact transformations needed. Cc: stable@vger.kernel.org # v6.8 Fixes: dbbdbd0086320a ("xfs: repair problems in CoW forks") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/scrub/cow_repair.c | 203 ++++++++++++++++++++++++-------------- fs/xfs/scrub/trace.h | 28 +++--- 2 files changed, 148 insertions(+), 83 deletions(-) diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c index fc70fd5b975a3..87627ab8943c1 100644 --- a/fs/xfs/scrub/cow_repair.c +++ b/fs/xfs/scrub/cow_repair.c @@ -80,12 +80,6 @@ struct xrep_cow { unsigned int next_bno; }; -/* CoW staging extent. */ -struct xrep_cow_extent { - xfs_fsblock_t fsbno; - xfs_extlen_t len; -}; - /* * Mark the part of the file range that corresponds to the given physical * space. Caller must ensure that the physical range is within xc->irec. @@ -401,22 +395,21 @@ xrep_cow_find_bad_rt( STATIC int xrep_cow_alloc( struct xfs_scrub *sc, - xfs_extlen_t maxlen, - struct xrep_cow_extent *repl) + struct xfs_bmbt_irec *del) { struct xfs_alloc_arg args = { .tp = sc->tp, .mp = sc->mp, .oinfo = XFS_RMAP_OINFO_SKIP_UPDATE, .minlen = 1, - .maxlen = maxlen, + .maxlen = del->br_blockcount, .prod = 1, .resv = XFS_AG_RESV_NONE, .datatype = XFS_ALLOC_USERDATA, }; int error; - error = xfs_trans_reserve_more(sc->tp, maxlen, 0); + error = xfs_trans_reserve_more(sc->tp, del->br_blockcount, 0); if (error) return error; @@ -429,8 +422,8 @@ xrep_cow_alloc( xfs_refcount_alloc_cow_extent(sc->tp, false, args.fsbno, args.len); - repl->fsbno = args.fsbno; - repl->len = args.len; + del->br_startblock = args.fsbno; + del->br_blockcount = args.len; return 0; } @@ -441,10 +434,12 @@ xrep_cow_alloc( STATIC int xrep_cow_alloc_rt( struct xfs_scrub *sc, - xfs_extlen_t maxlen, - struct xrep_cow_extent *repl) + struct xfs_bmbt_irec *del) { - xfs_rtxlen_t maxrtx = xfs_rtb_to_rtx(sc->mp, maxlen); + xfs_fsblock_t fsbno; + xfs_rtxlen_t maxrtx = + min(U32_MAX, xfs_blen_to_rtbxlen(sc->mp, del->br_blockcount)); + xfs_extlen_t len; int error; error = xfs_trans_reserve_more(sc->tp, 0, maxrtx); @@ -452,11 +447,14 @@ xrep_cow_alloc_rt( return error; error = xfs_rtallocate_rtgs(sc->tp, NULLRTBLOCK, 1, maxrtx, 1, false, - false, &repl->fsbno, &repl->len); + false, &fsbno, &len); if (error) return error; - xfs_refcount_alloc_cow_extent(sc->tp, true, repl->fsbno, repl->len); + xfs_refcount_alloc_cow_extent(sc->tp, true, fsbno, len); + + del->br_startblock = fsbno; + del->br_blockcount = len; return 0; } @@ -470,19 +468,19 @@ static inline int xrep_cow_find_mapping( struct xrep_cow *xc, struct xfs_iext_cursor *icur, - xfs_fileoff_t startoff, - struct xfs_bmbt_irec *got) + xfs_fileoff_t badoff, + xfs_extlen_t badlen, + struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *rep) { struct xfs_inode *ip = xc->sc->ip; struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); - if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got)) + if (!xfs_iext_lookup_extent(ip, ifp, badoff, icur, got)) goto bad; + memcpy(rep, got, sizeof(*rep)); - if (got->br_startoff > startoff) - goto bad; - - if (got->br_blockcount == 0) + if (got->br_startoff > badoff) goto bad; if (isnullstartblock(got->br_startblock)) @@ -491,6 +489,24 @@ xrep_cow_find_mapping( if (xfs_bmap_is_written_extent(got)) goto bad; + if (got->br_startoff < badoff) { + const int64_t delta = badoff - got->br_startoff; + + rep->br_blockcount -= delta; + rep->br_startoff += delta; + rep->br_startblock += delta; + } + + if (got->br_startoff + got->br_blockcount > badoff + badlen) { + const int64_t delta = (got->br_startoff + got->br_blockcount) - + (badoff + badlen); + + rep->br_blockcount -= delta; + } + + if (got->br_blockcount == 0) + goto bad; + return 0; bad: ASSERT(0); @@ -501,46 +517,92 @@ xrep_cow_find_mapping( #define REPLACE_RIGHT_SIDE (1U << 1) /* - * Given a CoW fork mapping @got and a replacement mapping @repl, remap the - * beginning of @got with the space described by @rep. + * Given a CoW fork mapping @got and a replacement mapping @rep, map the space + * described by @rep into the cow fork, pushing aside @got as necessary. @icur + * must point to iext tree leaf containing @got. */ static inline void xrep_cow_replace_mapping( - struct xfs_inode *ip, - struct xfs_iext_cursor *icur, - const struct xfs_bmbt_irec *got, - const struct xrep_cow_extent *repl) + struct xfs_inode *ip, + struct xfs_iext_cursor *icur, + struct xfs_bmbt_irec *got, + struct xfs_bmbt_irec *rep) { - struct xfs_bmbt_irec new = *got; /* struct copy */ + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); + xfs_fileoff_t rep_endoff = + rep->br_startoff + rep->br_blockcount; + xfs_fileoff_t got_endoff = + got->br_startoff + got->br_blockcount; + uint32_t state = BMAP_COWFORK; - ASSERT(repl->len > 0); + ASSERT(rep->br_blockcount > 0); ASSERT(!isnullstartblock(got->br_startblock)); + ASSERT(got->br_startoff <= rep->br_startoff); + ASSERT(got_endoff >= rep_endoff); + + trace_xrep_cow_replace_mapping(ip, got, rep); - trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len); + if (got->br_startoff == rep->br_startoff) + state |= BMAP_LEFT_FILLING; + if (got_endoff == rep_endoff) + state |= BMAP_RIGHT_FILLING; - if (got->br_blockcount == repl->len) { + switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) { + case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: /* - * The new extent is a complete replacement for the existing - * extent. Update the COW fork record. + * Replacement matches the whole mapping, update the record. */ - new.br_startblock = repl->fsbno; - xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new); - return; - } + xfs_iext_update_extent(ip, state, icur, rep); + break; + case BMAP_LEFT_FILLING: + /* + * Replace the first part of the mapping: Update the cursor + * position with the new mapping, then add a record with the + * tail of the old mapping. + */ + got->br_startoff = rep_endoff; + got->br_blockcount -= rep->br_blockcount; + got->br_startblock += rep->br_blockcount; + + xfs_iext_update_extent(ip, state, icur, rep); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, got, state); + break; + case BMAP_RIGHT_FILLING: + /* + * Replacing the last part of the mapping. Shorten the current + * mapping then add a record with the new mapping. + */ + got->br_blockcount -= rep->br_blockcount; - /* - * The new extent can replace the beginning of the COW fork record. - * Move the left side of @got upwards, then insert the new record. - */ - new.br_startoff += repl->len; - new.br_startblock += repl->len; - new.br_blockcount -= repl->len; - xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new); - - new.br_startoff = got->br_startoff; - new.br_startblock = repl->fsbno; - new.br_blockcount = repl->len; - xfs_iext_insert(ip, icur, &new, BMAP_COWFORK); + xfs_iext_update_extent(ip, state, icur, got); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, rep, state); + break; + case 0: + /* + * Replacing the middle of the extent. Shorten the current + * mapping, add a new record with the new mapping, and add a + * second new record with the tail of the old mapping. + */ + got->br_blockcount = rep->br_startoff - got->br_startoff; + + struct xfs_bmbt_irec new = { + .br_startoff = rep_endoff, + .br_blockcount = got_endoff - rep_endoff, + .br_state = got->br_state, + .br_startblock = got->br_startblock + + rep->br_blockcount + + got->br_blockcount, + }; + + xfs_iext_update_extent(ip, state, icur, got); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, rep, state); + xfs_iext_next(ifp, icur); + xfs_iext_insert(ip, icur, &new, state); + break; + } } /* @@ -554,33 +616,30 @@ xrep_cow_replace_range( xfs_extlen_t *blockcount) { struct xfs_iext_cursor icur; - struct xrep_cow_extent repl; - struct xfs_bmbt_irec got; + struct xfs_bmbt_irec got, rep; struct xfs_scrub *sc = xc->sc; - xfs_fileoff_t nextoff; - xfs_extlen_t alloc_len; + xfs_fsblock_t old_fsbno; int error; /* - * Put the existing CoW fork mapping in @got. If @got ends before - * @rep, truncate @rep so we only replace one extent mapping at a time. + * Put the existing CoW fork mapping in @got, and put in @rep the + * contents of @got trimmed to @startoff/@blockcount. We only want + * to replace the bad region, and only one mapping at a time. */ - error = xrep_cow_find_mapping(xc, &icur, startoff, &got); + error = xrep_cow_find_mapping(xc, &icur, startoff, *blockcount, &got, + &rep); if (error) return error; - nextoff = min(startoff + *blockcount, - got.br_startoff + got.br_blockcount); + old_fsbno = rep.br_startblock; /* * Allocate a replacement extent. If we don't fill all the blocks, * shorten the quantity that will be deleted in this step. */ - alloc_len = min_t(xfs_fileoff_t, XFS_MAX_BMBT_EXTLEN, - nextoff - startoff); if (XFS_IS_REALTIME_INODE(sc->ip)) - error = xrep_cow_alloc_rt(sc, alloc_len, &repl); + error = xrep_cow_alloc_rt(sc, &rep); else - error = xrep_cow_alloc(sc, alloc_len, &repl); + error = xrep_cow_alloc(sc, &rep); if (error) return error; @@ -588,7 +647,7 @@ xrep_cow_replace_range( * Replace the old mapping with the new one, and commit the metadata * changes made so far. */ - xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl); + xrep_cow_replace_mapping(sc->ip, &icur, &got, &rep); xfs_inode_set_cowblocks_tag(sc->ip); error = xfs_defer_finish(&sc->tp); @@ -597,15 +656,15 @@ xrep_cow_replace_range( /* Note the old CoW staging extents; we'll reap them all later. */ if (XFS_IS_REALTIME_INODE(sc->ip)) - error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, - got.br_startblock, repl.len); + error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, old_fsbno, + rep.br_blockcount); else - error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, - got.br_startblock, repl.len); + error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, old_fsbno, + rep.br_blockcount); if (error) return error; - *blockcount = repl.len; + *blockcount = rep.br_blockcount; return 0; } diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 286c5f5e05444..fab3ce323e4c7 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -2672,9 +2672,9 @@ TRACE_EVENT(xrep_cow_mark_file_range, ); TRACE_EVENT(xrep_cow_replace_mapping, - TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec, - xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount), - TP_ARGS(ip, irec, new_startblock, new_blockcount), + TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got, + const struct xfs_bmbt_irec *rep), + TP_ARGS(ip, got, rep), TP_STRUCT__entry( __field(dev_t, dev) __field(xfs_ino_t, ino) @@ -2682,28 +2682,34 @@ TRACE_EVENT(xrep_cow_replace_mapping, __field(xfs_fileoff_t, startoff) __field(xfs_filblks_t, blockcount) __field(xfs_exntst_t, state) + __field(xfs_fileoff_t, new_startoff) __field(xfs_fsblock_t, new_startblock) __field(xfs_extlen_t, new_blockcount) + __field(xfs_exntst_t, new_state) ), TP_fast_assign( __entry->dev = ip->i_mount->m_super->s_dev; __entry->ino = ip->i_ino; - __entry->startoff = irec->br_startoff; - __entry->startblock = irec->br_startblock; - __entry->blockcount = irec->br_blockcount; - __entry->state = irec->br_state; - __entry->new_startblock = new_startblock; - __entry->new_blockcount = new_blockcount; + __entry->startoff = got->br_startoff; + __entry->startblock = got->br_startblock; + __entry->blockcount = got->br_blockcount; + __entry->state = got->br_state; + __entry->new_startoff = rep->br_startoff; + __entry->new_startblock = rep->br_startblock; + __entry->new_blockcount = rep->br_blockcount; + __entry->new_state = rep->br_state; ), - TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x", + TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->startoff, __entry->startblock, __entry->blockcount, __entry->state, + __entry->new_startoff, __entry->new_startblock, - __entry->new_blockcount) + __entry->new_blockcount, + __entry->new_state) ); TRACE_EVENT(xrep_cow_free_staging, From 82e48ad2a13265a4a7d92278ac28edfdc03b95cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Wed, 22 Jul 2026 12:07:28 -0400 Subject: [PATCH 2838/3297] vduse: return internal vq group struct as map token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 02e3f7ffe2906033da73b7c7ea8180b131d0cdbc ] Return the internal struct that represents the vq group as virtqueue map token, instead of the device. This allows the map functions to access the information per group. At this moment all the virtqueues share the same vq group, that only can point to ASID 0. This change prepares the infrastructure for actual per-group address space handling Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Michael S. Tsirkin Message-Id: <20260119143306.1818855-5-eperezma@redhat.com> Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/vdpa/vdpa_user/iova_domain.c | 21 ++---- drivers/vdpa/vdpa_user/iova_domain.h | 5 +- drivers/vdpa/vdpa_user/vduse_dev.c | 109 +++++++++++++++++++++++---- include/linux/virtio.h | 6 +- 4 files changed, 108 insertions(+), 33 deletions(-) diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 4352b5cf74f07..58a94f81175fd 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -493,17 +493,15 @@ void vduse_domain_unmap_page(struct vduse_iova_domain *domain, vduse_domain_free_iova(iovad, dma_addr, size); } -void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, - size_t size, dma_addr_t *dma_addr, - gfp_t flag) +dma_addr_t vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, + size_t size, void *orig) { struct iova_domain *iovad = &domain->consistent_iovad; unsigned long limit = domain->iova_limit; dma_addr_t iova = vduse_domain_alloc_iova(iovad, size, limit); - void *orig = alloc_pages_exact(size, flag); - if (!iova || !orig) - goto err; + if (!iova) + return DMA_MAPPING_ERROR; spin_lock(&domain->iotlb_lock); if (vduse_iotlb_add_range(domain, (u64)iova, (u64)iova + size - 1, @@ -514,17 +512,12 @@ void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, } spin_unlock(&domain->iotlb_lock); - *dma_addr = iova; + return iova; - return orig; err: - *dma_addr = DMA_MAPPING_ERROR; - if (orig) - free_pages_exact(orig, size); - if (iova) - vduse_domain_free_iova(iovad, iova, size); + vduse_domain_free_iova(iovad, iova, size); - return NULL; + return DMA_MAPPING_ERROR; } void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, diff --git a/drivers/vdpa/vdpa_user/iova_domain.h b/drivers/vdpa/vdpa_user/iova_domain.h index 775cad5238f3a..153b8c24c4776 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.h +++ b/drivers/vdpa/vdpa_user/iova_domain.h @@ -67,9 +67,8 @@ void vduse_domain_unmap_page(struct vduse_iova_domain *domain, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir, unsigned long attrs); -void *vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, - size_t size, dma_addr_t *dma_addr, - gfp_t flag); +dma_addr_t vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, + size_t size, void *orig); void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, void *vaddr, dma_addr_t dma_addr, diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index db7af4b3fd4ad..f7eec4319f269 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,10 @@ struct vduse_umem { struct mm_struct *mm; }; +struct vduse_vq_group { + struct vduse_dev *dev; +}; + struct vduse_dev { struct vduse_vdpa *vdev; struct device *dev; @@ -115,6 +120,7 @@ struct vduse_dev { u32 vq_num; u32 vq_align; struct vduse_umem *umem; + struct vduse_vq_group *groups; struct mutex mem_lock; unsigned int bounce_size; struct mutex domain_lock; @@ -615,6 +621,16 @@ static int vduse_vdpa_set_vq_state(struct vdpa_device *vdpa, u16 idx, return 0; } +static union virtio_map vduse_get_vq_map(struct vdpa_device *vdpa, u16 idx) +{ + struct vduse_dev *dev = vdpa_to_vduse(vdpa); + union virtio_map ret = { + .group = &dev->groups[0], + }; + + return ret; +} + static int vduse_vdpa_get_vq_state(struct vdpa_device *vdpa, u16 idx, struct vdpa_vq_state *state) { @@ -834,6 +850,7 @@ static const struct vdpa_config_ops vduse_vdpa_config_ops = { .get_vq_affinity = vduse_vdpa_get_vq_affinity, .reset = vduse_vdpa_reset, .set_map = vduse_vdpa_set_map, + .get_vq_map = vduse_get_vq_map, .free = vduse_vdpa_free, }; @@ -841,7 +858,14 @@ static void vduse_dev_sync_single_for_device(union virtio_map token, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir) { - struct vduse_iova_domain *domain = token.iova_domain; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; + + if (!token.group) + return; + + vdev = token.group->dev; + domain = vdev->domain; vduse_domain_sync_single_for_device(domain, dma_addr, size, dir); } @@ -850,7 +874,14 @@ static void vduse_dev_sync_single_for_cpu(union virtio_map token, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir) { - struct vduse_iova_domain *domain = token.iova_domain; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; + + if (!token.group) + return; + + vdev = token.group->dev; + domain = vdev->domain; vduse_domain_sync_single_for_cpu(domain, dma_addr, size, dir); } @@ -860,7 +891,14 @@ static dma_addr_t vduse_dev_map_page(union virtio_map token, struct page *page, enum dma_data_direction dir, unsigned long attrs) { - struct vduse_iova_domain *domain = token.iova_domain; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; + + if (!token.group) + return DMA_MAPPING_ERROR; + + vdev = token.group->dev; + domain = vdev->domain; return vduse_domain_map_page(domain, page, offset, size, dir, attrs); } @@ -869,7 +907,14 @@ static void vduse_dev_unmap_page(union virtio_map token, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir, unsigned long attrs) { - struct vduse_iova_domain *domain = token.iova_domain; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; + + if (!token.group) + return; + + vdev = token.group->dev; + domain = vdev->domain; return vduse_domain_unmap_page(domain, dma_addr, size, dir, attrs); } @@ -877,33 +922,57 @@ static void vduse_dev_unmap_page(union virtio_map token, dma_addr_t dma_addr, static void *vduse_dev_alloc_coherent(union virtio_map token, size_t size, dma_addr_t *dma_addr, gfp_t flag) { - struct vduse_iova_domain *domain = token.iova_domain; - unsigned long iova; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; void *addr; *dma_addr = DMA_MAPPING_ERROR; - addr = vduse_domain_alloc_coherent(domain, size, - (dma_addr_t *)&iova, flag); + if (!token.group) + return NULL; + + addr = alloc_pages_exact(size, flag); if (!addr) return NULL; - *dma_addr = (dma_addr_t)iova; + vdev = token.group->dev; + domain = vdev->domain; + *dma_addr = vduse_domain_alloc_coherent(domain, size, addr); + if (*dma_addr == DMA_MAPPING_ERROR) + goto err; return addr; + +err: + free_pages_exact(addr, size); + return NULL; } static void vduse_dev_free_coherent(union virtio_map token, size_t size, void *vaddr, dma_addr_t dma_addr, unsigned long attrs) { - struct vduse_iova_domain *domain = token.iova_domain; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; + + if (!token.group) + return; + + vdev = token.group->dev; + domain = vdev->domain; vduse_domain_free_coherent(domain, size, vaddr, dma_addr, attrs); } static bool vduse_dev_need_sync(union virtio_map token, dma_addr_t dma_addr) { - struct vduse_iova_domain *domain = token.iova_domain; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; + + if (!token.group) + return false; + + vdev = token.group->dev; + domain = vdev->domain; return dma_addr < domain->bounce_size; } @@ -917,7 +986,14 @@ static int vduse_dev_mapping_error(union virtio_map token, dma_addr_t dma_addr) static size_t vduse_dev_max_mapping_size(union virtio_map token) { - struct vduse_iova_domain *domain = token.iova_domain; + struct vduse_dev *vdev; + struct vduse_iova_domain *domain; + + if (!token.group) + return 0; + + vdev = token.group->dev; + domain = vdev->domain; return domain->bounce_size; } @@ -1716,6 +1792,7 @@ static int vduse_destroy_dev(char *name) if (dev->domain) vduse_domain_destroy(dev->domain); kfree(dev->name); + kfree(dev->groups); vduse_dev_destroy(dev); module_put(THIS_MODULE); @@ -1874,6 +1951,11 @@ static int vduse_create_dev(struct vduse_dev_config *config, dev->device_features = config->features; dev->device_id = config->device_id; dev->vendor_id = config->vendor_id; + dev->groups = kcalloc(1, sizeof(dev->groups[0]), GFP_KERNEL); + if (!dev->groups) + goto err_vq_groups; + dev->groups[0].dev = dev; + dev->name = kstrdup(config->name, GFP_KERNEL); if (!dev->name) goto err_str; @@ -1910,6 +1992,8 @@ static int vduse_create_dev(struct vduse_dev_config *config, err_idr: kfree(dev->name); err_str: + kfree(dev->groups); +err_vq_groups: vduse_dev_destroy(dev); err: return ret; @@ -2071,7 +2155,6 @@ static int vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, return -ENOMEM; } - dev->vdev->vdpa.vmap.iova_domain = dev->domain; ret = _vdpa_register_device(&dev->vdev->vdpa, dev->vq_num); if (ret) { put_device(&dev->vdev->vdpa.dev); diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 132a474e59140..d3f723b875a78 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -41,13 +41,13 @@ struct virtqueue { void *priv; }; -struct vduse_iova_domain; +struct vduse_vq_group; union virtio_map { /* Device that performs DMA */ struct device *dma_dev; - /* VDUSE specific mapping data */ - struct vduse_iova_domain *iova_domain; + /* VDUSE specific virtqueue group for doing map */ + struct vduse_vq_group *group; }; int virtqueue_add_outbuf(struct virtqueue *vq, From db5c554b36d50ea2eb1cacd18116572e78e8c2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Wed, 22 Jul 2026 12:07:29 -0400 Subject: [PATCH 2839/3297] vduse: remove unused vaddr parameter of vduse_domain_free_coherent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 766e1749c0ef6a09651be9b8a8283d508c322b58 ] We will modify the function in next patches so let's clean it first. Signed-off-by: Eugenio Pérez Signed-off-by: Michael S. Tsirkin Message-Id: <20260119143306.1818855-9-eperezma@redhat.com> Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/vdpa/vdpa_user/iova_domain.c | 3 +-- drivers/vdpa/vdpa_user/iova_domain.h | 3 +-- drivers/vdpa/vdpa_user/vduse_dev.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 58a94f81175fd..aab730e8a3167 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -521,8 +521,7 @@ dma_addr_t vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, } void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, - void *vaddr, dma_addr_t dma_addr, - unsigned long attrs) + dma_addr_t dma_addr, unsigned long attrs) { struct iova_domain *iovad = &domain->consistent_iovad; struct vhost_iotlb_map *map; diff --git a/drivers/vdpa/vdpa_user/iova_domain.h b/drivers/vdpa/vdpa_user/iova_domain.h index 153b8c24c4776..ead5ab477079b 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.h +++ b/drivers/vdpa/vdpa_user/iova_domain.h @@ -71,8 +71,7 @@ dma_addr_t vduse_domain_alloc_coherent(struct vduse_iova_domain *domain, size_t size, void *orig); void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, - void *vaddr, dma_addr_t dma_addr, - unsigned long attrs); + dma_addr_t dma_addr, unsigned long attrs); void vduse_domain_reset_bounce_map(struct vduse_iova_domain *domain); diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index f7eec4319f269..647158bb6ccf0 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -960,7 +960,7 @@ static void vduse_dev_free_coherent(union virtio_map token, size_t size, vdev = token.group->dev; domain = vdev->domain; - vduse_domain_free_coherent(domain, size, vaddr, dma_addr, attrs); + vduse_domain_free_coherent(domain, size, dma_addr, attrs); } static bool vduse_dev_need_sync(union virtio_map token, dma_addr_t dma_addr) From 7764e9c727d58fcdcd2944da43d4cb300d112535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Wed, 22 Jul 2026 12:07:30 -0400 Subject: [PATCH 2840/3297] vduse: take out allocations from vduse_dev_alloc_coherent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 489d76520612abf9a4ede4344349105406c91a73 ] The function vduse_dev_alloc_coherent will be called under rwlock in next patches. Make it out of the lock to avoid increasing its fail rate. Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Michael S. Tsirkin Message-Id: <20260119143306.1818855-10-eperezma@redhat.com> Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/vdpa/vdpa_user/iova_domain.c | 3 --- drivers/vdpa/vdpa_user/vduse_dev.c | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index aab730e8a3167..0a9f668467a8d 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -526,7 +526,6 @@ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, struct iova_domain *iovad = &domain->consistent_iovad; struct vhost_iotlb_map *map; struct vdpa_map_file *map_file; - phys_addr_t pa; spin_lock(&domain->iotlb_lock); map = vhost_iotlb_itree_first(domain->iotlb, (u64)dma_addr, @@ -538,12 +537,10 @@ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, map_file = (struct vdpa_map_file *)map->opaque; fput(map_file->file); kfree(map_file); - pa = map->addr; vhost_iotlb_map_free(domain->iotlb, map); spin_unlock(&domain->iotlb_lock); vduse_domain_free_iova(iovad, dma_addr, size); - free_pages_exact(phys_to_virt(pa), size); } static vm_fault_t vduse_domain_mmap_fault(struct vm_fault *vmf) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 647158bb6ccf0..18c67b3db1f36 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -961,6 +961,7 @@ static void vduse_dev_free_coherent(union virtio_map token, size_t size, domain = vdev->domain; vduse_domain_free_coherent(domain, size, dma_addr, attrs); + free_pages_exact(vaddr, size); } static bool vduse_dev_need_sync(union virtio_map token, dma_addr_t dma_addr) From 690fb82c4122f8c2656fa4f842275132771b68b9 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 22 Jul 2026 12:07:31 -0400 Subject: [PATCH 2841/3297] VDUSE: avoid leaking information to userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9c1523803445ee0348f62b77793266dd981596e0 ] The bounceing is not necessarily page aligned, so current VDUSE can leak kernel information through mapping bounce pages to userspace. Allocate bounce pages with __GFP_ZERO to avoid leaking information to userspace. Fixes: 8c773d53fb7b ("vduse: Implement an MMU-based software IOTLB") Cc: stable@vger.kernel.org Signed-off-by: Jason Wang Reviewed-by: Xie Yongji Reviewed-by: Eugenio Pérez Signed-off-by: Michael S. Tsirkin Message-ID: <20260130050750.4050-1-jasowang@redhat.com> Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/vdpa/vdpa_user/iova_domain.c | 2 +- drivers/vdpa/vdpa_user/vduse_dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 0a9f668467a8d..ec743bed361c1 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -124,7 +124,7 @@ static int vduse_domain_map_bounce_page(struct vduse_iova_domain *domain, if (!map->bounce_page) { head_map = &domain->bounce_maps[(iova & PAGE_MASK) >> BOUNCE_MAP_SHIFT]; if (!head_map->bounce_page) { - tmp_page = alloc_page(GFP_ATOMIC); + tmp_page = alloc_page(GFP_ATOMIC | __GFP_ZERO); if (!tmp_page) return -ENOMEM; if (cmpxchg(&head_map->bounce_page, NULL, tmp_page)) diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 18c67b3db1f36..1cc826742ac9c 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -930,7 +930,7 @@ static void *vduse_dev_alloc_coherent(union virtio_map token, size_t size, if (!token.group) return NULL; - addr = alloc_pages_exact(size, flag); + addr = alloc_pages_exact(size, flag | __GFP_ZERO); if (!addr) return NULL; From 0d810ff7a6f655fcda3330b8199e940f4ad13211 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 22 Jul 2026 14:00:47 -0400 Subject: [PATCH 2842/3297] arm64: dts: qcom: correct RBR opp entry [ Upstream commit a5c21b9bd5f531e50141b0484faabb707b92f1e2 ] DisplayPort Reduced Bit Rate uses link rate of 1.62 Gbps, the main link clock should be 162 MHz. Having the incorrect frequency (160 MHz) in the OPP table will result in selecting wrong link frequency. Correct the entry in the OPP table. Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260304-msm-fix-rbr-v1-1-b9eba986eaef@oss.qualcomm.com Signed-off-by: Bjorn Andersson Stable-dep-of: c17e22094667 ("arm64: dts: qcom: hamoa: Fix OPP tables for all DisplayPort controllers") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/lemans.dtsi | 8 +++---- arch/arm64/boot/dts/qcom/sc7180.dtsi | 4 ++-- arch/arm64/boot/dts/qcom/sc7280.dtsi | 8 +++---- arch/arm64/boot/dts/qcom/sc8180x.dtsi | 12 +++++----- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 32 +++++++++++++------------- arch/arm64/boot/dts/qcom/sm6350.dtsi | 4 ++-- arch/arm64/boot/dts/qcom/sm8150.dtsi | 4 ++-- arch/arm64/boot/dts/qcom/sm8250.dtsi | 4 ++-- arch/arm64/boot/dts/qcom/sm8350.dtsi | 4 ++-- arch/arm64/boot/dts/qcom/sm8450.dtsi | 4 ++-- arch/arm64/boot/dts/qcom/x1e80100.dtsi | 16 ++++++------- 11 files changed, 50 insertions(+), 50 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index 5c00d2e89fb8c..496e319d7b111 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -5146,8 +5146,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5234,8 +5234,8 @@ dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi index 892b3d2f1bf99..dc96d3d5e943b 100644 --- a/arch/arm64/boot/dts/qcom/sc7180.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi @@ -3452,8 +3452,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index ed87bcb0d4b69..0f566dd75c964 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -5196,8 +5196,8 @@ edp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5295,8 +5295,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sc8180x.dtsi b/arch/arm64/boot/dts/qcom/sc8180x.dtsi index e20a096aac7f9..c6d96022c666d 100644 --- a/arch/arm64/boot/dts/qcom/sc8180x.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8180x.dtsi @@ -3303,8 +3303,8 @@ dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -3385,8 +3385,8 @@ dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -3461,8 +3461,8 @@ edp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index d89938e17e093..40649fe7a5095 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -4754,8 +4754,8 @@ mdss0_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -4836,8 +4836,8 @@ mdss0_dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -4916,8 +4916,8 @@ mdss0_dp2_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -4991,8 +4991,8 @@ mdss0_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6110,8 +6110,8 @@ mdss1_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6190,8 +6190,8 @@ mdss1_dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6270,8 +6270,8 @@ mdss1_dp2_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6345,8 +6345,8 @@ mdss1_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sm6350.dtsi b/arch/arm64/boot/dts/qcom/sm6350.dtsi index 19a7b9f9ea8bc..0c6787d46ddcd 100644 --- a/arch/arm64/boot/dts/qcom/sm6350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6350.dtsi @@ -2306,8 +2306,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi index acdba79612aa8..e1f480e3ed0b7 100644 --- a/arch/arm64/boot/dts/qcom/sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi @@ -3939,8 +3939,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi index adf6b49b52c48..79ba2c0c96b7e 100644 --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi @@ -4824,8 +4824,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi index fc4ce9d4977e8..af892b76322fd 100644 --- a/arch/arm64/boot/dts/qcom/sm8350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi @@ -2925,8 +2925,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index 2d49df7b69e4b..991f3cbde1c8d 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -3483,8 +3483,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/x1e80100.dtsi b/arch/arm64/boot/dts/qcom/x1e80100.dtsi index ee9c12600f95a..062911cc20832 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100.dtsi @@ -5522,8 +5522,8 @@ mdss_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5610,8 +5610,8 @@ mdss_dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5697,8 +5697,8 @@ mdss_dp2_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5779,8 +5779,8 @@ mdss_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; From 1de827e24d0adeff943acdd50e86122040bf3238 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Wed, 22 Jul 2026 14:00:48 -0400 Subject: [PATCH 2843/3297] arm64: dts: qcom: hamoa: Fix OPP tables for all DisplayPort controllers [ Upstream commit c17e220946675232d383620ed9cff6685735ec48 ] According to internal documentation, the corners specific for each rate from the DP link clock are: - LOWSVS_D1 -> 19.2 MHz - LOWSVS -> 270 MHz - SVS -> 540 MHz (594 MHz in case of DP3) - SVS_L1 -> 594 MHz - NOM -> 810 MHz - NOM_L1 -> 810 MHz - TURBO -> 810 MHz So fix all tables for each of the four controllers according to the documentation, but since DP0 through DP2 have the same entries in their tables, lets drop the DP1 and DP2 and have all of them share the DP0 table instead. However keep a separate table for the DP3 as it is different for the SVS, compared to the rest of the controllers. The 19.2 MHz @ LOWSVS_D1 isn't needed as it's not an actual working frequency and the controller will never select it. So remove it. Cc: stable@vger.kernel.org # v6.9+ Fixes: 1940c25eaa63 ("arm64: dts: qcom: x1e80100: Add display nodes") Suggested-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Abel Vesa Link: https://lore.kernel.org/r/20260323-hamoa-fix-dp3-opp-table-v3-1-a823776bd1b0@oss.qualcomm.com Signed-off-by: Bjorn Andersson Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/qcom/x1e80100.dtsi | 77 ++++---------------------- 1 file changed, 12 insertions(+), 65 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/x1e80100.dtsi b/arch/arm64/boot/dts/qcom/x1e80100.dtsi index 062911cc20832..adb18c10dbd9f 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100.dtsi @@ -5522,18 +5522,18 @@ mdss_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - opp-270000000 { opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; + required-opps = <&rpmhpd_opp_low_svs>; }; opp-540000000 { opp-hz = /bits/ 64 <540000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-594000000 { + opp-hz = /bits/ 64 <594000000>; required-opps = <&rpmhpd_opp_svs_l1>; }; @@ -5574,7 +5574,7 @@ <&usb_1_ss1_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, <&usb_1_ss1_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; - operating-points-v2 = <&mdss_dp1_opp_table>; + operating-points-v2 = <&mdss_dp0_opp_table>; power-domains = <&rpmhpd RPMHPD_MMCX>; @@ -5606,30 +5606,6 @@ }; }; }; - - mdss_dp1_opp_table: opp-table { - compatible = "operating-points-v2"; - - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - - opp-270000000 { - opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; - }; - - opp-540000000 { - opp-hz = /bits/ 64 <540000000>; - required-opps = <&rpmhpd_opp_svs_l1>; - }; - - opp-810000000 { - opp-hz = /bits/ 64 <810000000>; - required-opps = <&rpmhpd_opp_nom>; - }; - }; }; mdss_dp2: displayport-controller@ae9a000 { @@ -5662,7 +5638,7 @@ <&usb_1_ss2_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, <&usb_1_ss2_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; - operating-points-v2 = <&mdss_dp2_opp_table>; + operating-points-v2 = <&mdss_dp0_opp_table>; power-domains = <&rpmhpd RPMHPD_MMCX>; @@ -5693,30 +5669,6 @@ }; }; }; - - mdss_dp2_opp_table: opp-table { - compatible = "operating-points-v2"; - - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - - opp-270000000 { - opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; - }; - - opp-540000000 { - opp-hz = /bits/ 64 <540000000>; - required-opps = <&rpmhpd_opp_svs_l1>; - }; - - opp-810000000 { - opp-hz = /bits/ 64 <810000000>; - required-opps = <&rpmhpd_opp_nom>; - }; - }; }; mdss_dp3: displayport-controller@aea0000 { @@ -5779,19 +5731,14 @@ mdss_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-162000000 { - opp-hz = /bits/ 64 <162000000>; - required-opps = <&rpmhpd_opp_low_svs>; - }; - opp-270000000 { opp-hz = /bits/ 64 <270000000>; - required-opps = <&rpmhpd_opp_svs>; + required-opps = <&rpmhpd_opp_low_svs>; }; - opp-540000000 { - opp-hz = /bits/ 64 <540000000>; - required-opps = <&rpmhpd_opp_svs_l1>; + opp-594000000 { + opp-hz = /bits/ 64 <594000000>; + required-opps = <&rpmhpd_opp_svs>; }; opp-810000000 { From beeda5bf78577862e2b0c1d70fe1d6f9be542fc8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 23 Jul 2026 10:18:34 -0400 Subject: [PATCH 2844/3297] netfilter: nf_tables: remove register tracking infrastructure [ Upstream commit 6b94d081f81dd524626f7aab2b98a9de335edb72 ] This facility was disabled in commit 9e539c5b6d9c ("netfilter: nf_tables: disable expression reduction infra"), because not all nft_exprs guarantee they will update the destination register: some may set NFT_BREAK instead to cancel evaluation of the rule. This has been dead code ever since. There are no plans to salvage this at this time, so remove this. Signed-off-by: Florian Westphal Link: https://patch.msgid.link/20260224205048.4718-10-fw@strlen.de Signed-off-by: Jakub Kicinski Stable-dep-of: d07955dd34ec ("netfilter: nft_fib: reject fib expression on the netdev egress hook") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/net/netfilter/nf_tables.h | 32 ------- include/net/netfilter/nft_fib.h | 2 - include/net/netfilter/nft_meta.h | 3 - net/bridge/netfilter/nft_meta_bridge.c | 20 ----- net/bridge/netfilter/nft_reject_bridge.c | 1 - net/ipv4/netfilter/nft_dup_ipv4.c | 1 - net/ipv4/netfilter/nft_fib_ipv4.c | 2 - net/ipv4/netfilter/nft_reject_ipv4.c | 1 - net/ipv6/netfilter/nft_dup_ipv6.c | 1 - net/ipv6/netfilter/nft_fib_ipv6.c | 2 - net/ipv6/netfilter/nft_reject_ipv6.c | 1 - net/netfilter/nf_tables_api.c | 67 --------------- net/netfilter/nft_bitwise.c | 104 ----------------------- net/netfilter/nft_byteorder.c | 11 --- net/netfilter/nft_cmp.c | 3 - net/netfilter/nft_compat.c | 10 --- net/netfilter/nft_connlimit.c | 1 - net/netfilter/nft_counter.c | 1 - net/netfilter/nft_ct.c | 46 ---------- net/netfilter/nft_dup_netdev.c | 1 - net/netfilter/nft_dynset.c | 1 - net/netfilter/nft_exthdr.c | 34 -------- net/netfilter/nft_fib.c | 42 --------- net/netfilter/nft_fib_inet.c | 1 - net/netfilter/nft_fib_netdev.c | 1 - net/netfilter/nft_flow_offload.c | 1 - net/netfilter/nft_fwd_netdev.c | 2 - net/netfilter/nft_hash.c | 36 -------- net/netfilter/nft_immediate.c | 12 --- net/netfilter/nft_last.c | 1 - net/netfilter/nft_limit.c | 2 - net/netfilter/nft_log.c | 1 - net/netfilter/nft_lookup.c | 12 --- net/netfilter/nft_masq.c | 3 - net/netfilter/nft_meta.c | 45 ---------- net/netfilter/nft_nat.c | 2 - net/netfilter/nft_numgen.c | 22 ----- net/netfilter/nft_objref.c | 2 - net/netfilter/nft_osf.c | 25 ------ net/netfilter/nft_payload.c | 47 ---------- net/netfilter/nft_queue.c | 2 - net/netfilter/nft_quota.c | 1 - net/netfilter/nft_range.c | 1 - net/netfilter/nft_redir.c | 3 - net/netfilter/nft_reject_inet.c | 1 - net/netfilter/nft_reject_netdev.c | 1 - net/netfilter/nft_rt.c | 1 - net/netfilter/nft_socket.c | 26 ------ net/netfilter/nft_synproxy.c | 1 - net/netfilter/nft_tproxy.c | 1 - net/netfilter/nft_tunnel.c | 26 ------ net/netfilter/nft_xfrm.c | 27 ------ 52 files changed, 693 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index b35e8c02fadcd..fda5bc82dd504 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -123,17 +123,6 @@ struct nft_regs { }; }; -struct nft_regs_track { - struct { - const struct nft_expr *selector; - const struct nft_expr *bitwise; - u8 num_reg; - } regs[NFT_REG32_NUM]; - - const struct nft_expr *cur; - const struct nft_expr *last; -}; - /* Store/load an u8, u16 or u64 integer to/from the u32 data register. * * Note, when using concatenations, register allocation happens at 32-bit @@ -433,8 +422,6 @@ int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src, gfp_t gfp); void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); int nft_expr_dump(struct sk_buff *skb, unsigned int attr, const struct nft_expr *expr, bool reset); -bool nft_expr_reduce_bitwise(struct nft_regs_track *track, - const struct nft_expr *expr); struct nft_set_ext; @@ -949,7 +936,6 @@ struct nft_offload_ctx; * @destroy_clone: destruction clone function * @dump: function to dump parameters * @validate: validate expression, called during loop detection - * @reduce: reduce expression * @gc: garbage collection expression * @offload: hardware offload expression * @offload_action: function to report true/false to allocate one slot or not in the flow @@ -983,8 +969,6 @@ struct nft_expr_ops { bool reset); int (*validate)(const struct nft_ctx *ctx, const struct nft_expr *expr); - bool (*reduce)(struct nft_regs_track *track, - const struct nft_expr *expr); bool (*gc)(struct net *net, const struct nft_expr *expr); int (*offload)(struct nft_offload_ctx *ctx, @@ -1967,20 +1951,4 @@ static inline u64 nft_net_tstamp(const struct net *net) return nft_pernet(net)->tstamp; } -#define __NFT_REDUCE_READONLY 1UL -#define NFT_REDUCE_READONLY (void *)__NFT_REDUCE_READONLY - -void nft_reg_track_update(struct nft_regs_track *track, - const struct nft_expr *expr, u8 dreg, u8 len); -void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len); -void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg); - -static inline bool nft_reg_track_cmp(struct nft_regs_track *track, - const struct nft_expr *expr, u8 dreg) -{ - return track->regs[dreg].selector && - track->regs[dreg].selector->ops == expr->ops && - track->regs[dreg].num_reg == 0; -} - #endif /* _NET_NF_TABLES_H */ diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h index 7370fba844efc..e0422456f27bc 100644 --- a/include/net/netfilter/nft_fib.h +++ b/include/net/netfilter/nft_fib.h @@ -66,6 +66,4 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, void nft_fib_store_result(void *reg, const struct nft_fib *priv, const struct net_device *dev); -bool nft_fib_reduce(struct nft_regs_track *track, - const struct nft_expr *expr); #endif diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h index fab3e6658e91c..6cf1d910bbf8d 100644 --- a/include/net/netfilter/nft_meta.h +++ b/include/net/netfilter/nft_meta.h @@ -45,9 +45,6 @@ int nft_meta_get_validate(const struct nft_ctx *ctx, int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr); -bool nft_meta_get_reduce(struct nft_regs_track *track, - const struct nft_expr *expr); - struct nft_inner_tun_ctx; void nft_meta_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt, diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index a3d0c8a82d865..e4c9aa1f64e25 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -134,7 +134,6 @@ static const struct nft_expr_ops nft_meta_bridge_get_ops = { .init = nft_meta_bridge_get_init, .validate = nft_meta_bridge_get_validate, .dump = nft_meta_get_dump, - .reduce = nft_meta_get_reduce, }; static void nft_meta_bridge_set_eval(const struct nft_expr *expr, @@ -181,24 +180,6 @@ static int nft_meta_bridge_set_init(const struct nft_ctx *ctx, return 0; } -static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - int i; - - for (i = 0; i < NFT_REG32_NUM; i++) { - if (!track->regs[i].selector) - continue; - - if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops) - continue; - - __nft_reg_track_cancel(track, i); - } - - return false; -} - static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) { @@ -223,7 +204,6 @@ static const struct nft_expr_ops nft_meta_bridge_set_ops = { .init = nft_meta_bridge_set_init, .destroy = nft_meta_set_destroy, .dump = nft_meta_set_dump, - .reduce = nft_meta_bridge_set_reduce, .validate = nft_meta_bridge_set_validate, }; diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index 1cb5c16e97b7f..cd2b04236a99c 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -184,7 +184,6 @@ static const struct nft_expr_ops nft_reject_bridge_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_bridge_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_bridge_type __read_mostly = { diff --git a/net/ipv4/netfilter/nft_dup_ipv4.c b/net/ipv4/netfilter/nft_dup_ipv4.c index ef5dd88107ddb..d53a65ddbd7b7 100644 --- a/net/ipv4/netfilter/nft_dup_ipv4.c +++ b/net/ipv4/netfilter/nft_dup_ipv4.c @@ -76,7 +76,6 @@ static const struct nft_expr_ops nft_dup_ipv4_ops = { .eval = nft_dup_ipv4_eval, .init = nft_dup_ipv4_init, .dump = nft_dup_ipv4_dump, - .reduce = NFT_REDUCE_READONLY, }; static const struct nla_policy nft_dup_ipv4_policy[NFTA_DUP_MAX + 1] = { diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index e695283aeb2d0..177d738825b4c 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -163,7 +163,6 @@ static const struct nft_expr_ops nft_fib4_type_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, - .reduce = nft_fib_reduce, }; static const struct nft_expr_ops nft_fib4_ops = { @@ -173,7 +172,6 @@ static const struct nft_expr_ops nft_fib4_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, - .reduce = nft_fib_reduce, }; static const struct nft_expr_ops * diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c index 6cb213bb7256a..55fc23a8f7a70 100644 --- a/net/ipv4/netfilter/nft_reject_ipv4.c +++ b/net/ipv4/netfilter/nft_reject_ipv4.c @@ -45,7 +45,6 @@ static const struct nft_expr_ops nft_reject_ipv4_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_ipv4_type __read_mostly = { diff --git a/net/ipv6/netfilter/nft_dup_ipv6.c b/net/ipv6/netfilter/nft_dup_ipv6.c index 492a811828a71..95ec27b3971c1 100644 --- a/net/ipv6/netfilter/nft_dup_ipv6.c +++ b/net/ipv6/netfilter/nft_dup_ipv6.c @@ -74,7 +74,6 @@ static const struct nft_expr_ops nft_dup_ipv6_ops = { .eval = nft_dup_ipv6_eval, .init = nft_dup_ipv6_init, .dump = nft_dup_ipv6_dump, - .reduce = NFT_REDUCE_READONLY, }; static const struct nla_policy nft_dup_ipv6_policy[NFTA_DUP_MAX + 1] = { diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index 3005dfbca6155..6c04d0a80b26a 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -225,7 +225,6 @@ static const struct nft_expr_ops nft_fib6_type_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, - .reduce = nft_fib_reduce, }; static const struct nft_expr_ops nft_fib6_ops = { @@ -235,7 +234,6 @@ static const struct nft_expr_ops nft_fib6_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, - .reduce = nft_fib_reduce, }; static const struct nft_expr_ops * diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c index 5c61294f410ee..ed69c768797ec 100644 --- a/net/ipv6/netfilter/nft_reject_ipv6.c +++ b/net/ipv6/netfilter/nft_reject_ipv6.c @@ -46,7 +46,6 @@ static const struct nft_expr_ops nft_reject_ipv6_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_ipv6_type __read_mostly = { diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index c82c16bd4257e..51d3d4d44918c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -939,58 +939,6 @@ static int nft_delflowtable(struct nft_ctx *ctx, return 0; } -static void __nft_reg_track_clobber(struct nft_regs_track *track, u8 dreg) -{ - int i; - - for (i = track->regs[dreg].num_reg; i > 0; i--) - __nft_reg_track_cancel(track, dreg - i); -} - -static void __nft_reg_track_update(struct nft_regs_track *track, - const struct nft_expr *expr, - u8 dreg, u8 num_reg) -{ - track->regs[dreg].selector = expr; - track->regs[dreg].bitwise = NULL; - track->regs[dreg].num_reg = num_reg; -} - -void nft_reg_track_update(struct nft_regs_track *track, - const struct nft_expr *expr, u8 dreg, u8 len) -{ - unsigned int regcount; - int i; - - __nft_reg_track_clobber(track, dreg); - - regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE); - for (i = 0; i < regcount; i++, dreg++) - __nft_reg_track_update(track, expr, dreg, i); -} -EXPORT_SYMBOL_GPL(nft_reg_track_update); - -void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len) -{ - unsigned int regcount; - int i; - - __nft_reg_track_clobber(track, dreg); - - regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE); - for (i = 0; i < regcount; i++, dreg++) - __nft_reg_track_cancel(track, dreg); -} -EXPORT_SYMBOL_GPL(nft_reg_track_cancel); - -void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg) -{ - track->regs[dreg].selector = NULL; - track->regs[dreg].bitwise = NULL; - track->regs[dreg].num_reg = 0; -} -EXPORT_SYMBOL_GPL(__nft_reg_track_cancel); - /* * Tables */ @@ -10173,16 +10121,9 @@ void nf_tables_trans_destroy_flush_work(struct net *net) } EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work); -static bool nft_expr_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - return false; -} - static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain) { const struct nft_expr *expr, *last; - struct nft_regs_track track = {}; unsigned int size, data_size; void *data, *data_boundary; struct nft_rule_dp *prule; @@ -10219,15 +10160,7 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha return -ENOMEM; size = 0; - track.last = nft_expr_last(rule); nft_rule_for_each_expr(expr, last, rule) { - track.cur = expr; - - if (nft_expr_reduce(&track, expr)) { - expr = track.cur; - continue; - } - if (WARN_ON_ONCE(data + size + expr->ops->size > data_boundary)) return -ENOMEM; diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 1afb36fb5994d..e0a6703dc0e4a 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -402,61 +402,12 @@ static int nft_bitwise_offload(struct nft_offload_ctx *ctx, return 0; } -static bool nft_bitwise_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_bitwise *priv = nft_expr_priv(expr); - const struct nft_bitwise *bitwise; - unsigned int regcount; - u8 dreg; - int i; - - if (!track->regs[priv->sreg].selector) - return false; - - bitwise = nft_expr_priv(track->regs[priv->dreg].selector); - if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector && - track->regs[priv->sreg].num_reg == 0 && - track->regs[priv->dreg].bitwise && - track->regs[priv->dreg].bitwise->ops == expr->ops && - priv->sreg == bitwise->sreg && - priv->sreg2 == bitwise->sreg2 && - priv->dreg == bitwise->dreg && - priv->op == bitwise->op && - priv->len == bitwise->len && - !memcmp(&priv->mask, &bitwise->mask, sizeof(priv->mask)) && - !memcmp(&priv->xor, &bitwise->xor, sizeof(priv->xor)) && - !memcmp(&priv->data, &bitwise->data, sizeof(priv->data))) { - track->cur = expr; - return true; - } - - if (track->regs[priv->sreg].bitwise || - track->regs[priv->sreg].num_reg != 0) { - nft_reg_track_cancel(track, priv->dreg, priv->len); - return false; - } - - if (priv->sreg != priv->dreg) { - nft_reg_track_update(track, track->regs[priv->sreg].selector, - priv->dreg, priv->len); - } - - dreg = priv->dreg; - regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE); - for (i = 0; i < regcount; i++, dreg++) - track->regs[dreg].bitwise = expr; - - return false; -} - static const struct nft_expr_ops nft_bitwise_ops = { .type = &nft_bitwise_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise)), .eval = nft_bitwise_eval, .init = nft_bitwise_init, .dump = nft_bitwise_dump, - .reduce = nft_bitwise_reduce, .offload = nft_bitwise_offload, }; @@ -559,48 +510,12 @@ static int nft_bitwise_fast_offload(struct nft_offload_ctx *ctx, return 0; } -static bool nft_bitwise_fast_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr); - const struct nft_bitwise_fast_expr *bitwise; - - if (!track->regs[priv->sreg].selector) - return false; - - bitwise = nft_expr_priv(track->regs[priv->dreg].selector); - if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector && - track->regs[priv->dreg].bitwise && - track->regs[priv->dreg].bitwise->ops == expr->ops && - priv->sreg == bitwise->sreg && - priv->dreg == bitwise->dreg && - priv->mask == bitwise->mask && - priv->xor == bitwise->xor) { - track->cur = expr; - return true; - } - - if (track->regs[priv->sreg].bitwise) { - nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE); - return false; - } - - if (priv->sreg != priv->dreg) { - track->regs[priv->dreg].selector = - track->regs[priv->sreg].selector; - } - track->regs[priv->dreg].bitwise = expr; - - return false; -} - const struct nft_expr_ops nft_bitwise_fast_ops = { .type = &nft_bitwise_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise_fast_expr)), .eval = NULL, /* inlined */ .init = nft_bitwise_fast_init, .dump = nft_bitwise_fast_dump, - .reduce = nft_bitwise_fast_reduce, .offload = nft_bitwise_fast_offload, }; @@ -637,22 +552,3 @@ struct nft_expr_type nft_bitwise_type __read_mostly = { .maxattr = NFTA_BITWISE_MAX, .owner = THIS_MODULE, }; - -bool nft_expr_reduce_bitwise(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_expr *last = track->last; - const struct nft_expr *next; - - if (expr == last) - return false; - - next = nft_expr_next(expr); - if (next->ops == &nft_bitwise_ops) - return nft_bitwise_reduce(track, next); - else if (next->ops == &nft_bitwise_fast_ops) - return nft_bitwise_fast_reduce(track, next); - - return false; -} -EXPORT_SYMBOL_GPL(nft_expr_reduce_bitwise); diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index 5e7a7841b789b..74548b4c114b9 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -177,23 +177,12 @@ static int nft_byteorder_dump(struct sk_buff *skb, return -1; } -static bool nft_byteorder_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - struct nft_byteorder *priv = nft_expr_priv(expr); - - nft_reg_track_cancel(track, priv->dreg, priv->len); - - return false; -} - static const struct nft_expr_ops nft_byteorder_ops = { .type = &nft_byteorder_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)), .eval = nft_byteorder_eval, .init = nft_byteorder_init, .dump = nft_byteorder_dump, - .reduce = nft_byteorder_reduce, }; struct nft_expr_type nft_byteorder_type __read_mostly = { diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 2605f43737bc9..b61dc9c3383ee 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -190,7 +190,6 @@ static const struct nft_expr_ops nft_cmp_ops = { .eval = nft_cmp_eval, .init = nft_cmp_init, .dump = nft_cmp_dump, - .reduce = NFT_REDUCE_READONLY, .offload = nft_cmp_offload, }; @@ -282,7 +281,6 @@ const struct nft_expr_ops nft_cmp_fast_ops = { .eval = NULL, /* inlined */ .init = nft_cmp_fast_init, .dump = nft_cmp_fast_dump, - .reduce = NFT_REDUCE_READONLY, .offload = nft_cmp_fast_offload, }; @@ -376,7 +374,6 @@ const struct nft_expr_ops nft_cmp16_fast_ops = { .eval = NULL, /* inlined */ .init = nft_cmp16_fast_init, .dump = nft_cmp16_fast_dump, - .reduce = NFT_REDUCE_READONLY, .offload = nft_cmp16_fast_offload, }; diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index a77ffe2fcea75..ba4d304a30141 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -794,14 +794,6 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = { static struct nft_expr_type nft_match_type; -static bool nft_match_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct xt_match *match = expr->ops->data; - - return strcmp(match->name, "comment") == 0; -} - static const struct nft_expr_ops * nft_match_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) @@ -844,7 +836,6 @@ nft_match_select_ops(const struct nft_ctx *ctx, ops->dump = nft_match_dump; ops->validate = nft_match_validate; ops->data = match; - ops->reduce = nft_match_reduce; matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize)); if (matchsize > NFT_MATCH_LARGE_THRESH) { @@ -933,7 +924,6 @@ nft_target_select_ops(const struct nft_ctx *ctx, ops->destroy = nft_target_destroy; ops->dump = nft_target_dump; ops->data = target; - ops->reduce = NFT_REDUCE_READONLY; if (family == NFPROTO_BRIDGE) { ops->eval = nft_target_eval_bridge; diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c index 714a594859357..99079afcc5baf 100644 --- a/net/netfilter/nft_connlimit.c +++ b/net/netfilter/nft_connlimit.c @@ -247,7 +247,6 @@ static const struct nft_expr_ops nft_connlimit_ops = { .destroy_clone = nft_connlimit_destroy_clone, .dump = nft_connlimit_dump, .gc = nft_connlimit_gc, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_connlimit_type __read_mostly = { diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index 169ae93688bcc..3fa6369790f4d 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -313,7 +313,6 @@ static const struct nft_expr_ops nft_counter_ops = { .destroy_clone = nft_counter_destroy, .dump = nft_counter_dump, .clone = nft_counter_clone, - .reduce = NFT_REDUCE_READONLY, .offload = nft_counter_offload, .offload_stats = nft_counter_offload_stats, }; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index c776eb38f1dbd..b29ff555979b2 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -698,29 +698,6 @@ static int nft_ct_get_dump(struct sk_buff *skb, return -1; } -static bool nft_ct_get_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_ct *priv = nft_expr_priv(expr); - const struct nft_ct *ct; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - ct = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->key != ct->key) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return nft_expr_reduce_bitwise(track, expr); -} - static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset) { @@ -755,27 +732,8 @@ static const struct nft_expr_ops nft_ct_get_ops = { .init = nft_ct_get_init, .destroy = nft_ct_get_destroy, .dump = nft_ct_get_dump, - .reduce = nft_ct_get_reduce, }; -static bool nft_ct_set_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - int i; - - for (i = 0; i < NFT_REG32_NUM; i++) { - if (!track->regs[i].selector) - continue; - - if (track->regs[i].selector->ops != &nft_ct_get_ops) - continue; - - __nft_reg_track_cancel(track, i); - } - - return false; -} - #ifdef CONFIG_MITIGATION_RETPOLINE static const struct nft_expr_ops nft_ct_get_fast_ops = { .type = &nft_ct_type, @@ -784,7 +742,6 @@ static const struct nft_expr_ops nft_ct_get_fast_ops = { .init = nft_ct_get_init, .destroy = nft_ct_get_destroy, .dump = nft_ct_get_dump, - .reduce = nft_ct_set_reduce, }; #endif @@ -795,7 +752,6 @@ static const struct nft_expr_ops nft_ct_set_ops = { .init = nft_ct_set_init, .destroy = nft_ct_set_destroy, .dump = nft_ct_set_dump, - .reduce = nft_ct_set_reduce, }; #ifdef CONFIG_NF_CONNTRACK_ZONES @@ -806,7 +762,6 @@ static const struct nft_expr_ops nft_ct_set_zone_ops = { .init = nft_ct_set_init, .destroy = nft_ct_set_destroy, .dump = nft_ct_set_dump, - .reduce = nft_ct_set_reduce, }; #endif @@ -876,7 +831,6 @@ static const struct nft_expr_ops nft_notrack_ops = { .type = &nft_notrack_type, .size = NFT_EXPR_SIZE(0), .eval = nft_notrack_eval, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_notrack_type __read_mostly = { diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c index 0573f96ce0791..06866799e9463 100644 --- a/net/netfilter/nft_dup_netdev.c +++ b/net/netfilter/nft_dup_netdev.c @@ -80,7 +80,6 @@ static const struct nft_expr_ops nft_dup_netdev_ops = { .eval = nft_dup_netdev_eval, .init = nft_dup_netdev_init, .dump = nft_dup_netdev_dump, - .reduce = NFT_REDUCE_READONLY, .offload = nft_dup_netdev_offload, .offload_action = nft_dup_netdev_offload_action, }; diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 9123277be03ce..57bf94ae8724a 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -429,7 +429,6 @@ static const struct nft_expr_ops nft_dynset_ops = { .activate = nft_dynset_activate, .deactivate = nft_dynset_deactivate, .dump = nft_dynset_dump, - .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_dynset_type __read_mostly = { diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 9471328802d3b..cee93149dca7e 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -705,40 +705,12 @@ static int nft_exthdr_dump_strip(struct sk_buff *skb, return nft_exthdr_dump_common(skb, priv); } -static bool nft_exthdr_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_exthdr *priv = nft_expr_priv(expr); - const struct nft_exthdr *exthdr; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - exthdr = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->type != exthdr->type || - priv->op != exthdr->op || - priv->flags != exthdr->flags || - priv->offset != exthdr->offset || - priv->len != exthdr->len) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return nft_expr_reduce_bitwise(track, expr); -} - static const struct nft_expr_ops nft_exthdr_ipv6_ops = { .type = &nft_exthdr_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), .eval = nft_exthdr_ipv6_eval, .init = nft_exthdr_init, .dump = nft_exthdr_dump, - .reduce = nft_exthdr_reduce, }; static const struct nft_expr_ops nft_exthdr_ipv4_ops = { @@ -747,7 +719,6 @@ static const struct nft_expr_ops nft_exthdr_ipv4_ops = { .eval = nft_exthdr_ipv4_eval, .init = nft_exthdr_ipv4_init, .dump = nft_exthdr_dump, - .reduce = nft_exthdr_reduce, }; static const struct nft_expr_ops nft_exthdr_tcp_ops = { @@ -756,7 +727,6 @@ static const struct nft_expr_ops nft_exthdr_tcp_ops = { .eval = nft_exthdr_tcp_eval, .init = nft_exthdr_init, .dump = nft_exthdr_dump, - .reduce = nft_exthdr_reduce, }; static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { @@ -765,7 +735,6 @@ static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { .eval = nft_exthdr_tcp_set_eval, .init = nft_exthdr_tcp_set_init, .dump = nft_exthdr_dump_set, - .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = { @@ -774,7 +743,6 @@ static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = { .eval = nft_exthdr_tcp_strip_eval, .init = nft_exthdr_tcp_strip_init, .dump = nft_exthdr_dump_strip, - .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops nft_exthdr_sctp_ops = { @@ -783,7 +751,6 @@ static const struct nft_expr_ops nft_exthdr_sctp_ops = { .eval = nft_exthdr_sctp_eval, .init = nft_exthdr_init, .dump = nft_exthdr_dump, - .reduce = nft_exthdr_reduce, }; #ifdef CONFIG_NFT_EXTHDR_DCCP @@ -793,7 +760,6 @@ static const struct nft_expr_ops nft_exthdr_dccp_ops = { .eval = nft_exthdr_dccp_eval, .init = nft_exthdr_dccp_init, .dump = nft_exthdr_dump, - .reduce = nft_exthdr_reduce, }; #endif diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index 22846136c754a..cf76733d9c0e3 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -168,48 +168,6 @@ void nft_fib_store_result(void *reg, const struct nft_fib *priv, } EXPORT_SYMBOL_GPL(nft_fib_store_result); -bool nft_fib_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_fib *priv = nft_expr_priv(expr); - unsigned int len = NFT_REG32_SIZE; - const struct nft_fib *fib; - - switch (priv->result) { - case NFT_FIB_RESULT_OIF: - break; - case NFT_FIB_RESULT_OIFNAME: - if (priv->flags & NFTA_FIB_F_PRESENT) - len = NFT_REG32_SIZE; - else - len = IFNAMSIZ; - break; - case NFT_FIB_RESULT_ADDRTYPE: - break; - default: - WARN_ON_ONCE(1); - break; - } - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, len); - return false; - } - - fib = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->result != fib->result || - priv->flags != fib->flags) { - nft_reg_track_update(track, expr, priv->dreg, len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return false; -} -EXPORT_SYMBOL_GPL(nft_fib_reduce); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Query routing table from nftables"); MODULE_AUTHOR("Florian Westphal "); diff --git a/net/netfilter/nft_fib_inet.c b/net/netfilter/nft_fib_inet.c index 666a3741d20b6..a88d44e163d19 100644 --- a/net/netfilter/nft_fib_inet.c +++ b/net/netfilter/nft_fib_inet.c @@ -49,7 +49,6 @@ static const struct nft_expr_ops nft_fib_inet_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, - .reduce = nft_fib_reduce, }; static struct nft_expr_type nft_fib_inet_type __read_mostly = { diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c index 9121ec64e918f..3f3478abd8454 100644 --- a/net/netfilter/nft_fib_netdev.c +++ b/net/netfilter/nft_fib_netdev.c @@ -58,7 +58,6 @@ static const struct nft_expr_ops nft_fib_netdev_ops = { .init = nft_fib_init, .dump = nft_fib_dump, .validate = nft_fib_validate, - .reduce = nft_fib_reduce, }; static struct nft_expr_type nft_fib_netdev_type __read_mostly = { diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c index b8f76c9057fda..3e1d6c6295b95 100644 --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -224,7 +224,6 @@ static const struct nft_expr_ops nft_flow_offload_ops = { .destroy = nft_flow_offload_destroy, .validate = nft_flow_offload_validate, .dump = nft_flow_offload_dump, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_flow_offload_type __read_mostly = { diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index 256e832f1bb99..4bce36c3a6a07 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -228,7 +228,6 @@ static const struct nft_expr_ops nft_fwd_neigh_netdev_ops = { .init = nft_fwd_neigh_init, .dump = nft_fwd_neigh_dump, .validate = nft_fwd_validate, - .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops nft_fwd_netdev_ops = { @@ -238,7 +237,6 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = { .init = nft_fwd_netdev_init, .dump = nft_fwd_netdev_dump, .validate = nft_fwd_validate, - .reduce = NFT_REDUCE_READONLY, .offload = nft_fwd_netdev_offload, .offload_action = nft_fwd_netdev_offload_action, }; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 5d034bbb6913c..1cf41e0a0e0c8 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -166,16 +166,6 @@ static int nft_jhash_dump(struct sk_buff *skb, return -1; } -static bool nft_jhash_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_jhash *priv = nft_expr_priv(expr); - - nft_reg_track_cancel(track, priv->dreg, sizeof(u32)); - - return false; -} - static int nft_symhash_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset) { @@ -196,30 +186,6 @@ static int nft_symhash_dump(struct sk_buff *skb, return -1; } -static bool nft_symhash_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - struct nft_symhash *priv = nft_expr_priv(expr); - struct nft_symhash *symhash; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, sizeof(u32)); - return false; - } - - symhash = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->offset != symhash->offset || - priv->modulus != symhash->modulus) { - nft_reg_track_update(track, expr, priv->dreg, sizeof(u32)); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return false; -} - static struct nft_expr_type nft_hash_type; static const struct nft_expr_ops nft_jhash_ops = { .type = &nft_hash_type, @@ -227,7 +193,6 @@ static const struct nft_expr_ops nft_jhash_ops = { .eval = nft_jhash_eval, .init = nft_jhash_init, .dump = nft_jhash_dump, - .reduce = nft_jhash_reduce, }; static const struct nft_expr_ops nft_symhash_ops = { @@ -236,7 +201,6 @@ static const struct nft_expr_ops nft_symhash_ops = { .eval = nft_symhash_eval, .init = nft_symhash_init, .dump = nft_symhash_dump, - .reduce = nft_symhash_reduce, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 02ee5fb69871f..37c29947b380b 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -320,17 +320,6 @@ static bool nft_immediate_offload_action(const struct nft_expr *expr) return false; } -static bool nft_immediate_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_immediate_expr *priv = nft_expr_priv(expr); - - if (priv->dreg != NFT_REG_VERDICT) - nft_reg_track_cancel(track, priv->dreg, priv->dlen); - - return false; -} - static const struct nft_expr_ops nft_imm_ops = { .type = &nft_imm_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), @@ -341,7 +330,6 @@ static const struct nft_expr_ops nft_imm_ops = { .destroy = nft_immediate_destroy, .dump = nft_immediate_dump, .validate = nft_immediate_validate, - .reduce = nft_immediate_reduce, .offload = nft_immediate_offload, .offload_action = nft_immediate_offload_action, }; diff --git a/net/netfilter/nft_last.c b/net/netfilter/nft_last.c index de1b6066bfa85..e845779268d3a 100644 --- a/net/netfilter/nft_last.c +++ b/net/netfilter/nft_last.c @@ -125,7 +125,6 @@ static const struct nft_expr_ops nft_last_ops = { .destroy = nft_last_destroy, .clone = nft_last_clone, .dump = nft_last_dump, - .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_last_type __read_mostly = { diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index 21d26b79b4607..0daeb0b23c205 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -243,7 +243,6 @@ static const struct nft_expr_ops nft_limit_pkts_ops = { .destroy = nft_limit_pkts_destroy, .clone = nft_limit_pkts_clone, .dump = nft_limit_pkts_dump, - .reduce = NFT_REDUCE_READONLY, }; static void nft_limit_bytes_eval(const struct nft_expr *expr, @@ -299,7 +298,6 @@ static const struct nft_expr_ops nft_limit_bytes_ops = { .dump = nft_limit_bytes_dump, .clone = nft_limit_bytes_clone, .destroy = nft_limit_bytes_destroy, - .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index e355881379957..575021f6ee33c 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -291,7 +291,6 @@ static const struct nft_expr_ops nft_log_ops = { .init = nft_log_init, .destroy = nft_log_destroy, .dump = nft_log_dump, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_log_type __read_mostly = { diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index beca9c7705e01..699254cb3ecd6 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -269,17 +269,6 @@ static int nft_lookup_validate(const struct nft_ctx *ctx, return 0; } -static bool nft_lookup_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_lookup *priv = nft_expr_priv(expr); - - if (priv->set->flags & NFT_SET_MAP) - nft_reg_track_cancel(track, priv->dreg, priv->set->dlen); - - return false; -} - static const struct nft_expr_ops nft_lookup_ops = { .type = &nft_lookup_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), @@ -290,7 +279,6 @@ static const struct nft_expr_ops nft_lookup_ops = { .destroy = nft_lookup_destroy, .dump = nft_lookup_dump, .validate = nft_lookup_validate, - .reduce = nft_lookup_reduce, }; struct nft_expr_type nft_lookup_type __read_mostly = { diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 868bd4d735555..2b01128737a3a 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -143,7 +143,6 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = { .destroy = nft_masq_ipv4_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { @@ -171,7 +170,6 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = { .destroy = nft_masq_ipv6_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_masq_ipv6_type __read_mostly = { @@ -213,7 +211,6 @@ static const struct nft_expr_ops nft_masq_inet_ops = { .destroy = nft_masq_inet_destroy, .dump = nft_masq_dump, .validate = nft_masq_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_masq_inet_type __read_mostly = { diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index d3b0efec036aa..19e6d1c2436af 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -743,60 +743,16 @@ static int nft_meta_get_offload(struct nft_offload_ctx *ctx, return 0; } -bool nft_meta_get_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_meta *priv = nft_expr_priv(expr); - const struct nft_meta *meta; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - meta = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->key != meta->key || - priv->dreg != meta->dreg) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return nft_expr_reduce_bitwise(track, expr); -} -EXPORT_SYMBOL_GPL(nft_meta_get_reduce); - static const struct nft_expr_ops nft_meta_get_ops = { .type = &nft_meta_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .eval = nft_meta_get_eval, .init = nft_meta_get_init, .dump = nft_meta_get_dump, - .reduce = nft_meta_get_reduce, .validate = nft_meta_get_validate, .offload = nft_meta_get_offload, }; -static bool nft_meta_set_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - int i; - - for (i = 0; i < NFT_REG32_NUM; i++) { - if (!track->regs[i].selector) - continue; - - if (track->regs[i].selector->ops != &nft_meta_get_ops) - continue; - - __nft_reg_track_cancel(track, i); - } - - return false; -} - static const struct nft_expr_ops nft_meta_set_ops = { .type = &nft_meta_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), @@ -804,7 +760,6 @@ static const struct nft_expr_ops nft_meta_set_ops = { .init = nft_meta_set_init, .destroy = nft_meta_set_destroy, .dump = nft_meta_set_dump, - .reduce = nft_meta_set_reduce, .validate = nft_meta_set_validate, }; diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index 6e21f72c5b574..e32cd9fbc7c2e 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -320,7 +320,6 @@ static const struct nft_expr_ops nft_nat_ops = { .destroy = nft_nat_destroy, .dump = nft_nat_dump, .validate = nft_nat_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_nat_type __read_mostly = { @@ -351,7 +350,6 @@ static const struct nft_expr_ops nft_nat_inet_ops = { .destroy = nft_nat_destroy, .dump = nft_nat_dump, .validate = nft_nat_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_inet_nat_type __read_mostly = { diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c index bd058babfc820..06e87dfd76e74 100644 --- a/net/netfilter/nft_numgen.c +++ b/net/netfilter/nft_numgen.c @@ -84,16 +84,6 @@ static int nft_ng_inc_init(const struct nft_ctx *ctx, return err; } -static bool nft_ng_inc_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_ng_inc *priv = nft_expr_priv(expr); - - nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE); - - return false; -} - static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg, u32 modulus, enum nft_ng_types type, u32 offset) { @@ -178,16 +168,6 @@ static int nft_ng_random_dump(struct sk_buff *skb, priv->offset); } -static bool nft_ng_random_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_ng_random *priv = nft_expr_priv(expr); - - nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE); - - return false; -} - static struct nft_expr_type nft_ng_type; static const struct nft_expr_ops nft_ng_inc_ops = { .type = &nft_ng_type, @@ -196,7 +176,6 @@ static const struct nft_expr_ops nft_ng_inc_ops = { .init = nft_ng_inc_init, .destroy = nft_ng_inc_destroy, .dump = nft_ng_inc_dump, - .reduce = nft_ng_inc_reduce, }; static const struct nft_expr_ops nft_ng_random_ops = { @@ -205,7 +184,6 @@ static const struct nft_expr_ops nft_ng_random_ops = { .eval = nft_ng_random_eval, .init = nft_ng_random_init, .dump = nft_ng_random_dump, - .reduce = nft_ng_random_reduce, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 1a62e384766a7..633cce69568f1 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -123,7 +123,6 @@ static const struct nft_expr_ops nft_objref_ops = { .deactivate = nft_objref_deactivate, .dump = nft_objref_dump, .validate = nft_objref_validate, - .reduce = NFT_REDUCE_READONLY, }; struct nft_objref_map { @@ -245,7 +244,6 @@ static const struct nft_expr_ops nft_objref_map_ops = { .destroy = nft_objref_map_destroy, .dump = nft_objref_map_dump, .validate = nft_objref_map_validate, - .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index bdc2f6c90e2f7..8d84ef3fb487a 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -131,30 +131,6 @@ static int nft_osf_validate(const struct nft_ctx *ctx, return nft_chain_validate_hooks(ctx->chain, hooks); } -static bool nft_osf_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - struct nft_osf *priv = nft_expr_priv(expr); - struct nft_osf *osf; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN); - return false; - } - - osf = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->flags != osf->flags || - priv->ttl != osf->ttl) { - nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return false; -} - static struct nft_expr_type nft_osf_type; static const struct nft_expr_ops nft_osf_op = { .eval = nft_osf_eval, @@ -163,7 +139,6 @@ static const struct nft_expr_ops nft_osf_op = { .dump = nft_osf_dump, .type = &nft_osf_type, .validate = nft_osf_validate, - .reduce = nft_osf_reduce, }; static struct nft_expr_type nft_osf_type __read_mostly = { diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index da1c6bf0dc572..86a21c2b954f8 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -256,31 +256,6 @@ static int nft_payload_dump(struct sk_buff *skb, return -1; } -static bool nft_payload_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_payload *priv = nft_expr_priv(expr); - const struct nft_payload *payload; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - payload = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->base != payload->base || - priv->offset != payload->offset || - priv->len != payload->len) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return nft_expr_reduce_bitwise(track, expr); -} - static bool nft_payload_offload_mask(struct nft_offload_reg *reg, u32 priv_len, u32 field_len) { @@ -584,7 +559,6 @@ static const struct nft_expr_ops nft_payload_ops = { .eval = nft_payload_eval, .init = nft_payload_init, .dump = nft_payload_dump, - .reduce = nft_payload_reduce, .offload = nft_payload_offload, }; @@ -594,7 +568,6 @@ const struct nft_expr_ops nft_payload_fast_ops = { .eval = nft_payload_eval, .init = nft_payload_init, .dump = nft_payload_dump, - .reduce = nft_payload_reduce, .offload = nft_payload_offload, }; @@ -1022,32 +995,12 @@ static int nft_payload_set_dump(struct sk_buff *skb, return -1; } -static bool nft_payload_set_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - int i; - - for (i = 0; i < NFT_REG32_NUM; i++) { - if (!track->regs[i].selector) - continue; - - if (track->regs[i].selector->ops != &nft_payload_ops && - track->regs[i].selector->ops != &nft_payload_fast_ops) - continue; - - __nft_reg_track_cancel(track, i); - } - - return false; -} - static const struct nft_expr_ops nft_payload_set_ops = { .type = &nft_payload_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_payload_set)), .eval = nft_payload_set_eval, .init = nft_payload_set_init, .dump = nft_payload_set_dump, - .reduce = nft_payload_set_reduce, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index 344fe311878fe..8eb13a02942ed 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -191,7 +191,6 @@ static const struct nft_expr_ops nft_queue_ops = { .init = nft_queue_init, .dump = nft_queue_dump, .validate = nft_queue_validate, - .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops nft_queue_sreg_ops = { @@ -201,7 +200,6 @@ static const struct nft_expr_ops nft_queue_sreg_ops = { .init = nft_queue_sreg_init, .dump = nft_queue_sreg_dump, .validate = nft_queue_validate, - .reduce = NFT_REDUCE_READONLY, }; static const struct nft_expr_ops * diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index cb6c0e04ff675..bb3cf3d16e79b 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -266,7 +266,6 @@ static const struct nft_expr_ops nft_quota_ops = { .destroy = nft_quota_destroy, .clone = nft_quota_clone, .dump = nft_quota_dump, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_quota_type __read_mostly = { diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index ea382f7bbd78d..cbb02644b8362 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -138,7 +138,6 @@ static const struct nft_expr_ops nft_range_ops = { .eval = nft_range_eval, .init = nft_range_init, .dump = nft_range_dump, - .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_range_type __read_mostly = { diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index 95eedad85c835..58ae802db8f52 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -146,7 +146,6 @@ static const struct nft_expr_ops nft_redir_ipv4_ops = { .destroy = nft_redir_ipv4_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_redir_ipv4_type __read_mostly = { @@ -174,7 +173,6 @@ static const struct nft_expr_ops nft_redir_ipv6_ops = { .destroy = nft_redir_ipv6_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_redir_ipv6_type __read_mostly = { @@ -203,7 +201,6 @@ static const struct nft_expr_ops nft_redir_inet_ops = { .destroy = nft_redir_inet_destroy, .dump = nft_redir_dump, .validate = nft_redir_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_redir_inet_type __read_mostly = { diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c index 49020e67304ad..dcae83ddc32e5 100644 --- a/net/netfilter/nft_reject_inet.c +++ b/net/netfilter/nft_reject_inet.c @@ -79,7 +79,6 @@ static const struct nft_expr_ops nft_reject_inet_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_inet_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_inet_type __read_mostly = { diff --git a/net/netfilter/nft_reject_netdev.c b/net/netfilter/nft_reject_netdev.c index 2558ce1505d98..b53e81e4ca75d 100644 --- a/net/netfilter/nft_reject_netdev.c +++ b/net/netfilter/nft_reject_netdev.c @@ -158,7 +158,6 @@ static const struct nft_expr_ops nft_reject_netdev_ops = { .init = nft_reject_init, .dump = nft_reject_dump, .validate = nft_reject_netdev_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_reject_netdev_type __read_mostly = { diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index dc50b9a5bd688..ad527f3596c03 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -195,7 +195,6 @@ static const struct nft_expr_ops nft_rt_get_ops = { .init = nft_rt_get_init, .dump = nft_rt_get_dump, .validate = nft_rt_validate, - .reduce = NFT_REDUCE_READONLY, }; struct nft_expr_type nft_rt_type __read_mostly = { diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c index 36affbb697c2f..c55a1310226a4 100644 --- a/net/netfilter/nft_socket.c +++ b/net/netfilter/nft_socket.c @@ -249,31 +249,6 @@ static int nft_socket_dump(struct sk_buff *skb, return 0; } -static bool nft_socket_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_socket *priv = nft_expr_priv(expr); - const struct nft_socket *socket; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - socket = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->key != socket->key || - priv->dreg != socket->dreg || - priv->level != socket->level) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return nft_expr_reduce_bitwise(track, expr); -} - static int nft_socket_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) { @@ -296,7 +271,6 @@ static const struct nft_expr_ops nft_socket_ops = { .init = nft_socket_init, .dump = nft_socket_dump, .validate = nft_socket_validate, - .reduce = nft_socket_reduce, }; static struct nft_expr_type nft_socket_type __read_mostly = { diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c index ad3e7d27f02e9..a80bdce38546f 100644 --- a/net/netfilter/nft_synproxy.c +++ b/net/netfilter/nft_synproxy.c @@ -290,7 +290,6 @@ static const struct nft_expr_ops nft_synproxy_ops = { .dump = nft_synproxy_dump, .type = &nft_synproxy_type, .validate = nft_synproxy_validate, - .reduce = NFT_REDUCE_READONLY, }; static struct nft_expr_type nft_synproxy_type __read_mostly = { diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c index 50481280abd26..f2101af8c867f 100644 --- a/net/netfilter/nft_tproxy.c +++ b/net/netfilter/nft_tproxy.c @@ -331,7 +331,6 @@ static const struct nft_expr_ops nft_tproxy_ops = { .init = nft_tproxy_init, .destroy = nft_tproxy_destroy, .dump = nft_tproxy_dump, - .reduce = NFT_REDUCE_READONLY, .validate = nft_tproxy_validate, }; diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index db183e43941ac..78d47f5503551 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -124,31 +124,6 @@ static int nft_tunnel_get_dump(struct sk_buff *skb, return -1; } -static bool nft_tunnel_get_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_tunnel *priv = nft_expr_priv(expr); - const struct nft_tunnel *tunnel; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - tunnel = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->key != tunnel->key || - priv->dreg != tunnel->dreg || - priv->mode != tunnel->mode) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return false; -} - static struct nft_expr_type nft_tunnel_type; static const struct nft_expr_ops nft_tunnel_get_ops = { .type = &nft_tunnel_type, @@ -156,7 +131,6 @@ static const struct nft_expr_ops nft_tunnel_get_ops = { .eval = nft_tunnel_get_eval, .init = nft_tunnel_get_init, .dump = nft_tunnel_get_dump, - .reduce = nft_tunnel_get_reduce, }; static struct nft_expr_type nft_tunnel_type __read_mostly = { diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index 3210cfc966ab2..7ffe6a2690d13 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -259,32 +259,6 @@ static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *e return nft_chain_validate_hooks(ctx->chain, hooks); } -static bool nft_xfrm_reduce(struct nft_regs_track *track, - const struct nft_expr *expr) -{ - const struct nft_xfrm *priv = nft_expr_priv(expr); - const struct nft_xfrm *xfrm; - - if (!nft_reg_track_cmp(track, expr, priv->dreg)) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - xfrm = nft_expr_priv(track->regs[priv->dreg].selector); - if (priv->key != xfrm->key || - priv->dreg != xfrm->dreg || - priv->dir != xfrm->dir || - priv->spnum != xfrm->spnum) { - nft_reg_track_update(track, expr, priv->dreg, priv->len); - return false; - } - - if (!track->regs[priv->dreg].bitwise) - return true; - - return nft_expr_reduce_bitwise(track, expr); -} - static struct nft_expr_type nft_xfrm_type; static const struct nft_expr_ops nft_xfrm_get_ops = { .type = &nft_xfrm_type, @@ -293,7 +267,6 @@ static const struct nft_expr_ops nft_xfrm_get_ops = { .init = nft_xfrm_get_init, .dump = nft_xfrm_get_dump, .validate = nft_xfrm_validate, - .reduce = nft_xfrm_reduce, }; static struct nft_expr_type nft_xfrm_type __read_mostly = { From d01c913febead04a01a5f3a6374d1f45504dc523 Mon Sep 17 00:00:00 2001 From: Theodor Arsenij Larionov-Trichkine Date: Thu, 23 Jul 2026 10:18:35 -0400 Subject: [PATCH 2845/3297] netfilter: nft_fib: reject fib expression on the netdev egress hook [ Upstream commit d07955dd34ecae17d35d8c7d0a273a3fba653a8c ] A fib expression in a netdev egress base chain dereferences nft_in(pkt), NULL on the transmit path, causing a NULL pointer dereference at eval. nft_fib_validate() masks the hook with NF_INET_* values, but netdev hook numbers are a separate enum that aliases them (NF_NETDEV_EGRESS == NF_INET_LOCAL_IN), so an egress chain passes validation and then faults. Add nft_fib_netdev_validate() that limits each result/flag to the netdev hook where the device it reads exists: the input-device cases (OIF, OIFNAME, ADDRTYPE with F_IIF) to ingress, the output-device case (ADDRTYPE with F_OIF) to egress, ADDRTYPE with no device flag to both. Also restrict nft_fib_validate() to NFPROTO_IPV4/IPV6/INET so its NF_INET_* masks are not applied to another family's hooks. Fixes: 42df6e1d221d ("netfilter: Introduce egress hook") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/netfilter-devel/ajxsjcDOnwllMfoR@strlen.de/ Signed-off-by: Theodor Arsenij Larionov-Trichkine Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_fib.c | 9 +++++++++ net/netfilter/nft_fib_netdev.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index cf76733d9c0e3..7b2a0a031c4b4 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -31,6 +31,15 @@ int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) const struct nft_fib *priv = nft_expr_priv(expr); unsigned int hooks; + switch (ctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return -EOPNOTSUPP; + } + switch (priv->result) { case NFT_FIB_RESULT_OIF: case NFT_FIB_RESULT_OIFNAME: diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c index 3f3478abd8454..5774a75440278 100644 --- a/net/netfilter/nft_fib_netdev.c +++ b/net/netfilter/nft_fib_netdev.c @@ -50,6 +50,33 @@ static void nft_fib_netdev_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } +static int nft_fib_netdev_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + hooks = (1 << NF_NETDEV_INGRESS); + break; + case NFT_FIB_RESULT_ADDRTYPE: + if (priv->flags & NFTA_FIB_F_IIF) + hooks = (1 << NF_NETDEV_INGRESS); + else if (priv->flags & NFTA_FIB_F_OIF) + hooks = (1 << NF_NETDEV_EGRESS); + else + hooks = (1 << NF_NETDEV_INGRESS) | + (1 << NF_NETDEV_EGRESS); + break; + default: + return -EINVAL; + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_fib_netdev_type; static const struct nft_expr_ops nft_fib_netdev_ops = { .type = &nft_fib_netdev_type, @@ -57,7 +84,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = { .eval = nft_fib_netdev_eval, .init = nft_fib_init, .dump = nft_fib_dump, - .validate = nft_fib_validate, + .validate = nft_fib_netdev_validate, }; static struct nft_expr_type nft_fib_netdev_type __read_mostly = { From a1a94a00b88448e8832976f5008c50afc83c59b3 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 23 Jul 2026 10:18:39 -0400 Subject: [PATCH 2846/3297] netfilter: nf_conntrack_sip: remove net variable shadowing [ Upstream commit 7970d6aaf710db166de98c5356a260089896fae5 ] net is already set, derived from nf_conn. I don't see how the device could be living in a different netns than the conntrack entry. Remove the extra variable and re-use existing one. Signed-off-by: Florian Westphal Stable-dep-of: e5e24a365a5e ("netfilter: nf_conntrack_sip: validate skb_dst() before accessing it") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_sip.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 81534213f00f3..58fce6242f89c 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -948,9 +948,8 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, saddr = &ct->tuplehash[!dir].tuple.src.u3; } else if (sip_external_media) { struct net_device *dev = skb_dst(skb)->dev; - struct net *net = dev_net(dev); - struct flowi fl; struct dst_entry *dst = NULL; + struct flowi fl; memset(&fl, 0, sizeof(fl)); From 09755dc62b026076b1d47f83489eb0547c8135e0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 23 Jul 2026 10:18:40 -0400 Subject: [PATCH 2847/3297] netfilter: nf_conntrack_sip: validate skb_dst() before accessing it [ Upstream commit e5e24a365a5e024efef63cc49abb345fbd4852c5 ] tc ingress and openvswitch do not guarantee routing information to be available. These subsystems use the conntrack helper infrastructure, and the SIP helper relies on the skb_dst() to be present if sip_external_media is set to 1 (which is disabled by default as a module parameter). This effectively disables the sip_external_media toggle for these subsystems without resulting in a crash. Fixes: cae3a2627520 ("openvswitch: Allow attaching helpers to ct action") Fixes: b57dc7c13ea9 ("net/sched: Introduce action ct") Cc: stable@vger.kernel.org Reported-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_sip.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 58fce6242f89c..147043f19521b 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -947,7 +947,6 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, return NF_ACCEPT; saddr = &ct->tuplehash[!dir].tuple.src.u3; } else if (sip_external_media) { - struct net_device *dev = skb_dst(skb)->dev; struct dst_entry *dst = NULL; struct flowi fl; @@ -969,7 +968,11 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, * through the same interface as the signalling peer. */ if (dst) { - bool external_media = (dst->dev == dev); + const struct dst_entry *this_dst = skb_dst(skb); + bool external_media = false; + + if (this_dst && dst->dev == this_dst->dev) + external_media = true; dst_release(dst); if (external_media) From 65677f7a20c48cf9ed02c56db389cc327d2b61b2 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Thu, 23 Jul 2026 10:19:29 -0400 Subject: [PATCH 2848/3297] gpu: Move DRM buddy allocator one level up (part two) [ Upstream commit ba110db8e1bc206c13fd7d985e79b033f53bfdea ] Move the DRM buddy allocator one level up so that it can be used by GPU drivers (example, nova-core) that have usecases other than DRM (such as VFIO vGPU support). Modify the API, structures and Kconfigs to use "gpu_buddy" terminology. Adapt the drivers and tests to use the new API. The commit cannot be split due to bisectability, however no functional change is intended. Verified by running K-UNIT tests and build tested various configurations. Signed-off-by: Joel Fernandes Reviewed-by: Dave Airlie [airlied: I've split this into two so git can find copies easier. I've also just nuked drm_random library, that stuff needs to be done elsewhere and only the buddy tests seem to be using it]. Signed-off-by: Dave Airlie Stable-dep-of: 56bc6384314f ("gpu/buddy: bail out of try_harder when alignment cannot be honoured") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- Documentation/gpu/drm-mm.rst | 10 +- MAINTAINERS | 13 + drivers/gpu/Kconfig | 13 + drivers/gpu/Makefile | 3 +- drivers/gpu/buddy.c | 1322 +++++++++++++++++ drivers/gpu/drm/Kconfig | 5 +- drivers/gpu/drm/Kconfig.debug | 1 - drivers/gpu/drm/Makefile | 1 - drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 2 +- .../gpu/drm/amd/amdgpu/amdgpu_res_cursor.h | 12 +- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 79 +- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h | 20 +- drivers/gpu/drm/drm_buddy.c | 1278 +--------------- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 3 +- drivers/gpu/drm/i915/i915_scatterlist.c | 10 +- drivers/gpu/drm/i915/i915_ttm_buddy_manager.c | 59 +- drivers/gpu/drm/i915/i915_ttm_buddy_manager.h | 4 +- .../drm/i915/selftests/intel_memory_region.c | 20 +- drivers/gpu/drm/tests/Makefile | 1 - drivers/gpu/drm/tests/drm_exec_test.c | 2 - drivers/gpu/drm/tests/drm_mm_test.c | 2 - .../gpu/drm/ttm/tests/ttm_bo_validate_test.c | 4 +- drivers/gpu/drm/ttm/tests/ttm_mock_manager.c | 18 +- drivers/gpu/drm/ttm/tests/ttm_mock_manager.h | 4 +- drivers/gpu/drm/xe/xe_res_cursor.h | 34 +- drivers/gpu/drm/xe/xe_svm.c | 12 +- drivers/gpu/drm/xe/xe_ttm_vram_mgr.c | 71 +- drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h | 4 +- drivers/gpu/tests/Makefile | 4 + .../gpu_buddy_test.c} | 358 ++--- .../lib/drm_random.c => tests/gpu_random.c} | 18 +- .../lib/drm_random.h => tests/gpu_random.h} | 18 +- drivers/video/Kconfig | 1 + include/drm/drm_buddy.h | 165 +- include/linux/gpu_buddy.h | 177 +++ 35 files changed, 1934 insertions(+), 1814 deletions(-) create mode 100644 drivers/gpu/Kconfig create mode 100644 drivers/gpu/buddy.c create mode 100644 drivers/gpu/tests/Makefile rename drivers/gpu/{drm/tests/drm_buddy_test.c => tests/gpu_buddy_test.c} (66%) rename drivers/gpu/{drm/lib/drm_random.c => tests/gpu_random.c} (59%) rename drivers/gpu/{drm/lib/drm_random.h => tests/gpu_random.h} (53%) create mode 100644 include/linux/gpu_buddy.h diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index d55751cad67cf..8e0d31230b29b 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -509,8 +509,14 @@ DRM GPUVM Function References DRM Buddy Allocator =================== -DRM Buddy Function References ------------------------------ +Buddy Allocator Function References (GPU buddy) +----------------------------------------------- + +.. kernel-doc:: drivers/gpu/buddy.c + :export: + +DRM Buddy Specific Logging Function References +---------------------------------------------- .. kernel-doc:: drivers/gpu/drm/drm_buddy.c :export: diff --git a/MAINTAINERS b/MAINTAINERS index cbfe2a7dd993f..d27beeb0d9b8d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8656,6 +8656,19 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/ttm/ F: include/drm/ttm/ +GPU BUDDY ALLOCATOR +M: Matthew Auld +M: Arun Pravin +R: Christian Koenig +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm_buddy.c +F: drivers/gpu/buddy.c +F: drivers/gpu/tests/gpu_buddy_test.c +F: include/linux/gpu_buddy.h +F: include/drm/drm_buddy.h + DRM AUTOMATED TESTING M: Helen Koike M: Vignesh Raman diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig new file mode 100644 index 0000000000000..ebb2ad4b7ea0b --- /dev/null +++ b/drivers/gpu/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +config GPU_BUDDY + bool + help + A page based buddy allocator for GPU memory. + +config GPU_BUDDY_KUNIT_TEST + tristate "KUnit tests for GPU buddy allocator" if !KUNIT_ALL_TESTS + depends on GPU_BUDDY && KUNIT + default KUNIT_ALL_TESTS + help + KUnit tests for the GPU buddy allocator. diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index 36a54d4566303..5cd54d06e2627 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -2,7 +2,8 @@ # drm/tegra depends on host1x, so if both drivers are built-in care must be # taken to initialize them in the correct order. Link order is the only way # to ensure this currently. -obj-y += host1x/ drm/ vga/ +obj-y += host1x/ drm/ vga/ tests/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ obj-$(CONFIG_TRACE_GPU_MEM) += trace/ obj-$(CONFIG_NOVA_CORE) += nova-core/ +obj-$(CONFIG_GPU_BUDDY) += buddy.o diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c new file mode 100644 index 0000000000000..603c59a2013a0 --- /dev/null +++ b/drivers/gpu/buddy.c @@ -0,0 +1,1322 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2021 Intel Corporation + */ + +#include + +#include +#include +#include +#include + +#include + +static struct kmem_cache *slab_blocks; + +static struct gpu_buddy_block *gpu_block_alloc(struct gpu_buddy *mm, + struct gpu_buddy_block *parent, + unsigned int order, + u64 offset) +{ + struct gpu_buddy_block *block; + + BUG_ON(order > GPU_BUDDY_MAX_ORDER); + + block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL); + if (!block) + return NULL; + + block->header = offset; + block->header |= order; + block->parent = parent; + + RB_CLEAR_NODE(&block->rb); + + BUG_ON(block->header & GPU_BUDDY_HEADER_UNUSED); + return block; +} + +static void gpu_block_free(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + kmem_cache_free(slab_blocks, block); +} + +static enum gpu_buddy_free_tree +get_block_tree(struct gpu_buddy_block *block) +{ + return gpu_buddy_block_is_clear(block) ? + GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; +} + +static struct gpu_buddy_block * +rbtree_get_free_block(const struct rb_node *node) +{ + return node ? rb_entry(node, struct gpu_buddy_block, rb) : NULL; +} + +static struct gpu_buddy_block * +rbtree_last_free_block(struct rb_root *root) +{ + return rbtree_get_free_block(rb_last(root)); +} + +static bool rbtree_is_empty(struct rb_root *root) +{ + return RB_EMPTY_ROOT(root); +} + +static bool gpu_buddy_block_offset_less(const struct gpu_buddy_block *block, + const struct gpu_buddy_block *node) +{ + return gpu_buddy_block_offset(block) < gpu_buddy_block_offset(node); +} + +static bool rbtree_block_offset_less(struct rb_node *block, + const struct rb_node *node) +{ + return gpu_buddy_block_offset_less(rbtree_get_free_block(block), + rbtree_get_free_block(node)); +} + +static void rbtree_insert(struct gpu_buddy *mm, + struct gpu_buddy_block *block, + enum gpu_buddy_free_tree tree) +{ + rb_add(&block->rb, + &mm->free_trees[tree][gpu_buddy_block_order(block)], + rbtree_block_offset_less); +} + +static void rbtree_remove(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + unsigned int order = gpu_buddy_block_order(block); + enum gpu_buddy_free_tree tree; + struct rb_root *root; + + tree = get_block_tree(block); + root = &mm->free_trees[tree][order]; + + rb_erase(&block->rb, root); + RB_CLEAR_NODE(&block->rb); +} + +static void clear_reset(struct gpu_buddy_block *block) +{ + block->header &= ~GPU_BUDDY_HEADER_CLEAR; +} + +static void mark_cleared(struct gpu_buddy_block *block) +{ + block->header |= GPU_BUDDY_HEADER_CLEAR; +} + +static void mark_allocated(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + block->header &= ~GPU_BUDDY_HEADER_STATE; + block->header |= GPU_BUDDY_ALLOCATED; + + rbtree_remove(mm, block); +} + +static void mark_free(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + enum gpu_buddy_free_tree tree; + + block->header &= ~GPU_BUDDY_HEADER_STATE; + block->header |= GPU_BUDDY_FREE; + + tree = get_block_tree(block); + rbtree_insert(mm, block, tree); +} + +static void mark_split(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + block->header &= ~GPU_BUDDY_HEADER_STATE; + block->header |= GPU_BUDDY_SPLIT; + + rbtree_remove(mm, block); +} + +static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) +{ + return s1 <= e2 && e1 >= s2; +} + +static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) +{ + return s1 <= s2 && e1 >= e2; +} + +static struct gpu_buddy_block * +__get_buddy(struct gpu_buddy_block *block) +{ + struct gpu_buddy_block *parent; + + parent = block->parent; + if (!parent) + return NULL; + + if (parent->left == block) + return parent->right; + + return parent->left; +} + +static unsigned int __gpu_buddy_free(struct gpu_buddy *mm, + struct gpu_buddy_block *block, + bool force_merge) +{ + struct gpu_buddy_block *parent; + unsigned int order; + + while ((parent = block->parent)) { + struct gpu_buddy_block *buddy; + + buddy = __get_buddy(block); + + if (!gpu_buddy_block_is_free(buddy)) + break; + + if (!force_merge) { + /* + * Check the block and its buddy clear state and exit + * the loop if they both have the dissimilar state. + */ + if (gpu_buddy_block_is_clear(block) != + gpu_buddy_block_is_clear(buddy)) + break; + + if (gpu_buddy_block_is_clear(block)) + mark_cleared(parent); + } + + rbtree_remove(mm, buddy); + if (force_merge && gpu_buddy_block_is_clear(buddy)) + mm->clear_avail -= gpu_buddy_block_size(mm, buddy); + + gpu_block_free(mm, block); + gpu_block_free(mm, buddy); + + block = parent; + } + + order = gpu_buddy_block_order(block); + mark_free(mm, block); + + return order; +} + +static int __force_merge(struct gpu_buddy *mm, + u64 start, + u64 end, + unsigned int min_order) +{ + unsigned int tree, order; + int i; + + if (!min_order) + return -ENOMEM; + + if (min_order > mm->max_order) + return -EINVAL; + + for_each_free_tree(tree) { + for (i = min_order - 1; i >= 0; i--) { + struct rb_node *iter = rb_last(&mm->free_trees[tree][i]); + + while (iter) { + struct gpu_buddy_block *block, *buddy; + u64 block_start, block_end; + + block = rbtree_get_free_block(iter); + iter = rb_prev(iter); + + if (!block || !block->parent) + continue; + + block_start = gpu_buddy_block_offset(block); + block_end = block_start + gpu_buddy_block_size(mm, block) - 1; + + if (!contains(start, end, block_start, block_end)) + continue; + + buddy = __get_buddy(block); + if (!gpu_buddy_block_is_free(buddy)) + continue; + + WARN_ON(gpu_buddy_block_is_clear(block) == + gpu_buddy_block_is_clear(buddy)); + + /* + * Advance to the next node when the current node is the buddy, + * as freeing the block will also remove its buddy from the tree. + */ + if (iter == &buddy->rb) + iter = rb_prev(iter); + + rbtree_remove(mm, block); + if (gpu_buddy_block_is_clear(block)) + mm->clear_avail -= gpu_buddy_block_size(mm, block); + + order = __gpu_buddy_free(mm, block, true); + if (order >= min_order) + return 0; + } + } + } + + return -ENOMEM; +} + +/** + * gpu_buddy_init - init memory manager + * + * @mm: GPU buddy manager to initialize + * @size: size in bytes to manage + * @chunk_size: minimum page size in bytes for our allocations + * + * Initializes the memory manager and its resources. + * + * Returns: + * 0 on success, error code on failure. + */ +int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) +{ + unsigned int i, j, root_count = 0; + u64 offset = 0; + + if (size < chunk_size) + return -EINVAL; + + if (chunk_size < SZ_4K) + return -EINVAL; + + if (!is_power_of_2(chunk_size)) + return -EINVAL; + + size = round_down(size, chunk_size); + + mm->size = size; + mm->avail = size; + mm->clear_avail = 0; + mm->chunk_size = chunk_size; + mm->max_order = ilog2(size) - ilog2(chunk_size); + + BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER); + + mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES, + sizeof(*mm->free_trees), + GFP_KERNEL); + if (!mm->free_trees) + return -ENOMEM; + + for_each_free_tree(i) { + mm->free_trees[i] = kmalloc_array(mm->max_order + 1, + sizeof(struct rb_root), + GFP_KERNEL); + if (!mm->free_trees[i]) + goto out_free_tree; + + for (j = 0; j <= mm->max_order; ++j) + mm->free_trees[i][j] = RB_ROOT; + } + + mm->n_roots = hweight64(size); + + mm->roots = kmalloc_array(mm->n_roots, + sizeof(struct gpu_buddy_block *), + GFP_KERNEL); + if (!mm->roots) + goto out_free_tree; + + /* + * Split into power-of-two blocks, in case we are given a size that is + * not itself a power-of-two. + */ + do { + struct gpu_buddy_block *root; + unsigned int order; + u64 root_size; + + order = ilog2(size) - ilog2(chunk_size); + root_size = chunk_size << order; + + root = gpu_block_alloc(mm, NULL, order, offset); + if (!root) + goto out_free_roots; + + mark_free(mm, root); + + BUG_ON(root_count > mm->max_order); + BUG_ON(gpu_buddy_block_size(mm, root) < chunk_size); + + mm->roots[root_count] = root; + + offset += root_size; + size -= root_size; + root_count++; + } while (size); + + return 0; + +out_free_roots: + while (root_count--) + gpu_block_free(mm, mm->roots[root_count]); + kfree(mm->roots); +out_free_tree: + while (i--) + kfree(mm->free_trees[i]); + kfree(mm->free_trees); + return -ENOMEM; +} +EXPORT_SYMBOL(gpu_buddy_init); + +/** + * gpu_buddy_fini - tear down the memory manager + * + * @mm: GPU buddy manager to free + * + * Cleanup memory manager resources and the freetree + */ +void gpu_buddy_fini(struct gpu_buddy *mm) +{ + u64 root_size, size, start; + unsigned int order; + int i; + + size = mm->size; + + for (i = 0; i < mm->n_roots; ++i) { + order = ilog2(size) - ilog2(mm->chunk_size); + start = gpu_buddy_block_offset(mm->roots[i]); + __force_merge(mm, start, start + size, order); + + if (WARN_ON(!gpu_buddy_block_is_free(mm->roots[i]))) + kunit_fail_current_test("buddy_fini() root"); + + gpu_block_free(mm, mm->roots[i]); + + root_size = mm->chunk_size << order; + size -= root_size; + } + + WARN_ON(mm->avail != mm->size); + + for_each_free_tree(i) + kfree(mm->free_trees[i]); + kfree(mm->free_trees); + kfree(mm->roots); +} +EXPORT_SYMBOL(gpu_buddy_fini); + +static int split_block(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + unsigned int block_order = gpu_buddy_block_order(block) - 1; + u64 offset = gpu_buddy_block_offset(block); + + BUG_ON(!gpu_buddy_block_is_free(block)); + BUG_ON(!gpu_buddy_block_order(block)); + + block->left = gpu_block_alloc(mm, block, block_order, offset); + if (!block->left) + return -ENOMEM; + + block->right = gpu_block_alloc(mm, block, block_order, + offset + (mm->chunk_size << block_order)); + if (!block->right) { + gpu_block_free(mm, block->left); + return -ENOMEM; + } + + mark_split(mm, block); + + if (gpu_buddy_block_is_clear(block)) { + mark_cleared(block->left); + mark_cleared(block->right); + clear_reset(block); + } + + mark_free(mm, block->left); + mark_free(mm, block->right); + + return 0; +} + +/** + * gpu_get_buddy - get buddy address + * + * @block: GPU buddy block + * + * Returns the corresponding buddy block for @block, or NULL + * if this is a root block and can't be merged further. + * Requires some kind of locking to protect against + * any concurrent allocate and free operations. + */ +struct gpu_buddy_block * +gpu_get_buddy(struct gpu_buddy_block *block) +{ + return __get_buddy(block); +} +EXPORT_SYMBOL(gpu_get_buddy); + +/** + * gpu_buddy_reset_clear - reset blocks clear state + * + * @mm: GPU buddy manager + * @is_clear: blocks clear state + * + * Reset the clear state based on @is_clear value for each block + * in the freetree. + */ +void gpu_buddy_reset_clear(struct gpu_buddy *mm, bool is_clear) +{ + enum gpu_buddy_free_tree src_tree, dst_tree; + u64 root_size, size, start; + unsigned int order; + int i; + + size = mm->size; + for (i = 0; i < mm->n_roots; ++i) { + order = ilog2(size) - ilog2(mm->chunk_size); + start = gpu_buddy_block_offset(mm->roots[i]); + __force_merge(mm, start, start + size, order); + + root_size = mm->chunk_size << order; + size -= root_size; + } + + src_tree = is_clear ? GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE; + dst_tree = is_clear ? GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; + + for (i = 0; i <= mm->max_order; ++i) { + struct rb_root *root = &mm->free_trees[src_tree][i]; + struct gpu_buddy_block *block, *tmp; + + rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { + rbtree_remove(mm, block); + if (is_clear) { + mark_cleared(block); + mm->clear_avail += gpu_buddy_block_size(mm, block); + } else { + clear_reset(block); + mm->clear_avail -= gpu_buddy_block_size(mm, block); + } + + rbtree_insert(mm, block, dst_tree); + } + } +} +EXPORT_SYMBOL(gpu_buddy_reset_clear); + +/** + * gpu_buddy_free_block - free a block + * + * @mm: GPU buddy manager + * @block: block to be freed + */ +void gpu_buddy_free_block(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + BUG_ON(!gpu_buddy_block_is_allocated(block)); + mm->avail += gpu_buddy_block_size(mm, block); + if (gpu_buddy_block_is_clear(block)) + mm->clear_avail += gpu_buddy_block_size(mm, block); + + __gpu_buddy_free(mm, block, false); +} +EXPORT_SYMBOL(gpu_buddy_free_block); + +static void __gpu_buddy_free_list(struct gpu_buddy *mm, + struct list_head *objects, + bool mark_clear, + bool mark_dirty) +{ + struct gpu_buddy_block *block, *on; + + WARN_ON(mark_dirty && mark_clear); + + list_for_each_entry_safe(block, on, objects, link) { + if (mark_clear) + mark_cleared(block); + else if (mark_dirty) + clear_reset(block); + gpu_buddy_free_block(mm, block); + cond_resched(); + } + INIT_LIST_HEAD(objects); +} + +static void gpu_buddy_free_list_internal(struct gpu_buddy *mm, + struct list_head *objects) +{ + /* + * Don't touch the clear/dirty bit, since allocation is still internal + * at this point. For example we might have just failed part of the + * allocation. + */ + __gpu_buddy_free_list(mm, objects, false, false); +} + +/** + * gpu_buddy_free_list - free blocks + * + * @mm: GPU buddy manager + * @objects: input list head to free blocks + * @flags: optional flags like GPU_BUDDY_CLEARED + */ +void gpu_buddy_free_list(struct gpu_buddy *mm, + struct list_head *objects, + unsigned int flags) +{ + bool mark_clear = flags & GPU_BUDDY_CLEARED; + + __gpu_buddy_free_list(mm, objects, mark_clear, !mark_clear); +} +EXPORT_SYMBOL(gpu_buddy_free_list); + +static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags) +{ + bool needs_clear = flags & GPU_BUDDY_CLEAR_ALLOCATION; + + return needs_clear != gpu_buddy_block_is_clear(block); +} + +static struct gpu_buddy_block * +__alloc_range_bias(struct gpu_buddy *mm, + u64 start, u64 end, + unsigned int order, + unsigned long flags, + bool fallback) +{ + u64 req_size = mm->chunk_size << order; + struct gpu_buddy_block *block; + struct gpu_buddy_block *buddy; + LIST_HEAD(dfs); + int err; + int i; + + end = end - 1; + + for (i = 0; i < mm->n_roots; ++i) + list_add_tail(&mm->roots[i]->tmp_link, &dfs); + + do { + u64 block_start; + u64 block_end; + + block = list_first_entry_or_null(&dfs, + struct gpu_buddy_block, + tmp_link); + if (!block) + break; + + list_del(&block->tmp_link); + + if (gpu_buddy_block_order(block) < order) + continue; + + block_start = gpu_buddy_block_offset(block); + block_end = block_start + gpu_buddy_block_size(mm, block) - 1; + + if (!overlaps(start, end, block_start, block_end)) + continue; + + if (gpu_buddy_block_is_allocated(block)) + continue; + + if (block_start < start || block_end > end) { + u64 adjusted_start = max(block_start, start); + u64 adjusted_end = min(block_end, end); + + if (round_down(adjusted_end + 1, req_size) <= + round_up(adjusted_start, req_size)) + continue; + } + + if (!fallback && block_incompatible(block, flags)) + continue; + + if (contains(start, end, block_start, block_end) && + order == gpu_buddy_block_order(block)) { + /* + * Find the free block within the range. + */ + if (gpu_buddy_block_is_free(block)) + return block; + + continue; + } + + if (!gpu_buddy_block_is_split(block)) { + err = split_block(mm, block); + if (unlikely(err)) + goto err_undo; + } + + list_add(&block->right->tmp_link, &dfs); + list_add(&block->left->tmp_link, &dfs); + } while (1); + + return ERR_PTR(-ENOSPC); + +err_undo: + /* + * We really don't want to leave around a bunch of split blocks, since + * bigger is better, so make sure we merge everything back before we + * free the allocated blocks. + */ + buddy = __get_buddy(block); + if (buddy && + (gpu_buddy_block_is_free(block) && + gpu_buddy_block_is_free(buddy))) + __gpu_buddy_free(mm, block, false); + return ERR_PTR(err); +} + +static struct gpu_buddy_block * +__gpu_buddy_alloc_range_bias(struct gpu_buddy *mm, + u64 start, u64 end, + unsigned int order, + unsigned long flags) +{ + struct gpu_buddy_block *block; + bool fallback = false; + + block = __alloc_range_bias(mm, start, end, order, + flags, fallback); + if (IS_ERR(block)) + return __alloc_range_bias(mm, start, end, order, + flags, !fallback); + + return block; +} + +static struct gpu_buddy_block * +get_maxblock(struct gpu_buddy *mm, + unsigned int order, + enum gpu_buddy_free_tree tree) +{ + struct gpu_buddy_block *max_block = NULL, *block = NULL; + struct rb_root *root; + unsigned int i; + + for (i = order; i <= mm->max_order; ++i) { + root = &mm->free_trees[tree][i]; + block = rbtree_last_free_block(root); + if (!block) + continue; + + if (!max_block) { + max_block = block; + continue; + } + + if (gpu_buddy_block_offset(block) > + gpu_buddy_block_offset(max_block)) { + max_block = block; + } + } + + return max_block; +} + +static struct gpu_buddy_block * +alloc_from_freetree(struct gpu_buddy *mm, + unsigned int order, + unsigned long flags) +{ + struct gpu_buddy_block *block = NULL; + struct rb_root *root; + enum gpu_buddy_free_tree tree; + unsigned int tmp; + int err; + + tree = (flags & GPU_BUDDY_CLEAR_ALLOCATION) ? + GPU_BUDDY_CLEAR_TREE : GPU_BUDDY_DIRTY_TREE; + + if (flags & GPU_BUDDY_TOPDOWN_ALLOCATION) { + block = get_maxblock(mm, order, tree); + if (block) + /* Store the obtained block order */ + tmp = gpu_buddy_block_order(block); + } else { + for (tmp = order; tmp <= mm->max_order; ++tmp) { + /* Get RB tree root for this order and tree */ + root = &mm->free_trees[tree][tmp]; + block = rbtree_last_free_block(root); + if (block) + break; + } + } + + if (!block) { + /* Try allocating from the other tree */ + tree = (tree == GPU_BUDDY_CLEAR_TREE) ? + GPU_BUDDY_DIRTY_TREE : GPU_BUDDY_CLEAR_TREE; + + for (tmp = order; tmp <= mm->max_order; ++tmp) { + root = &mm->free_trees[tree][tmp]; + block = rbtree_last_free_block(root); + if (block) + break; + } + + if (!block) + return ERR_PTR(-ENOSPC); + } + + BUG_ON(!gpu_buddy_block_is_free(block)); + + while (tmp != order) { + err = split_block(mm, block); + if (unlikely(err)) + goto err_undo; + + block = block->right; + tmp--; + } + return block; + +err_undo: + if (tmp != order) + __gpu_buddy_free(mm, block, false); + return ERR_PTR(err); +} + +static int __alloc_range(struct gpu_buddy *mm, + struct list_head *dfs, + u64 start, u64 size, + struct list_head *blocks, + u64 *total_allocated_on_err) +{ + struct gpu_buddy_block *block; + struct gpu_buddy_block *buddy; + u64 total_allocated = 0; + LIST_HEAD(allocated); + u64 end; + int err; + + end = start + size - 1; + + do { + u64 block_start; + u64 block_end; + + block = list_first_entry_or_null(dfs, + struct gpu_buddy_block, + tmp_link); + if (!block) + break; + + list_del(&block->tmp_link); + + block_start = gpu_buddy_block_offset(block); + block_end = block_start + gpu_buddy_block_size(mm, block) - 1; + + if (!overlaps(start, end, block_start, block_end)) + continue; + + if (gpu_buddy_block_is_allocated(block)) { + err = -ENOSPC; + goto err_free; + } + + if (contains(start, end, block_start, block_end)) { + if (gpu_buddy_block_is_free(block)) { + mark_allocated(mm, block); + total_allocated += gpu_buddy_block_size(mm, block); + mm->avail -= gpu_buddy_block_size(mm, block); + if (gpu_buddy_block_is_clear(block)) + mm->clear_avail -= gpu_buddy_block_size(mm, block); + list_add_tail(&block->link, &allocated); + continue; + } else if (!mm->clear_avail) { + err = -ENOSPC; + goto err_free; + } + } + + if (!gpu_buddy_block_is_split(block)) { + err = split_block(mm, block); + if (unlikely(err)) + goto err_undo; + } + + list_add(&block->right->tmp_link, dfs); + list_add(&block->left->tmp_link, dfs); + } while (1); + + if (total_allocated < size) { + err = -ENOSPC; + goto err_free; + } + + list_splice_tail(&allocated, blocks); + + return 0; + +err_undo: + /* + * We really don't want to leave around a bunch of split blocks, since + * bigger is better, so make sure we merge everything back before we + * free the allocated blocks. + */ + buddy = __get_buddy(block); + if (buddy && + (gpu_buddy_block_is_free(block) && + gpu_buddy_block_is_free(buddy))) + __gpu_buddy_free(mm, block, false); + +err_free: + if (err == -ENOSPC && total_allocated_on_err) { + list_splice_tail(&allocated, blocks); + *total_allocated_on_err = total_allocated; + } else { + gpu_buddy_free_list_internal(mm, &allocated); + } + + return err; +} + +static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, + u64 start, + u64 size, + u64 *total_allocated_on_err, + struct list_head *blocks) +{ + LIST_HEAD(dfs); + int i; + + for (i = 0; i < mm->n_roots; ++i) + list_add_tail(&mm->roots[i]->tmp_link, &dfs); + + return __alloc_range(mm, &dfs, start, size, + blocks, total_allocated_on_err); +} + +static int __alloc_contig_try_harder(struct gpu_buddy *mm, + u64 size, + u64 min_block_size, + struct list_head *blocks) +{ + u64 rhs_offset, lhs_offset, lhs_size, filled; + struct gpu_buddy_block *block; + unsigned int tree, order; + LIST_HEAD(blocks_lhs); + unsigned long pages; + u64 modify_size; + int err; + + modify_size = rounddown_pow_of_two(size); + pages = modify_size >> ilog2(mm->chunk_size); + order = fls(pages) - 1; + if (order == 0) + return -ENOSPC; + + for_each_free_tree(tree) { + struct rb_root *root; + struct rb_node *iter; + + root = &mm->free_trees[tree][order]; + if (rbtree_is_empty(root)) + continue; + + iter = rb_last(root); + while (iter) { + block = rbtree_get_free_block(iter); + + /* Allocate blocks traversing RHS */ + rhs_offset = gpu_buddy_block_offset(block); + err = __gpu_buddy_alloc_range(mm, rhs_offset, size, + &filled, blocks); + if (!err || err != -ENOSPC) + return err; + + lhs_size = max((size - filled), min_block_size); + if (!IS_ALIGNED(lhs_size, min_block_size)) + lhs_size = round_up(lhs_size, min_block_size); + + /* Allocate blocks traversing LHS */ + lhs_offset = gpu_buddy_block_offset(block) - lhs_size; + err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size, + NULL, &blocks_lhs); + if (!err) { + list_splice(&blocks_lhs, blocks); + return 0; + } else if (err != -ENOSPC) { + gpu_buddy_free_list_internal(mm, blocks); + return err; + } + /* Free blocks for the next iteration */ + gpu_buddy_free_list_internal(mm, blocks); + + iter = rb_prev(iter); + } + } + + return -ENOSPC; +} + +/** + * gpu_buddy_block_trim - free unused pages + * + * @mm: GPU buddy manager + * @start: start address to begin the trimming. + * @new_size: original size requested + * @blocks: Input and output list of allocated blocks. + * MUST contain single block as input to be trimmed. + * On success will contain the newly allocated blocks + * making up the @new_size. Blocks always appear in + * ascending order + * + * For contiguous allocation, we round up the size to the nearest + * power of two value, drivers consume *actual* size, so remaining + * portions are unused and can be optionally freed with this function + * + * Returns: + * 0 on success, error code on failure. + */ +int gpu_buddy_block_trim(struct gpu_buddy *mm, + u64 *start, + u64 new_size, + struct list_head *blocks) +{ + struct gpu_buddy_block *parent; + struct gpu_buddy_block *block; + u64 block_start, block_end; + LIST_HEAD(dfs); + u64 new_start; + int err; + + if (!list_is_singular(blocks)) + return -EINVAL; + + block = list_first_entry(blocks, + struct gpu_buddy_block, + link); + + block_start = gpu_buddy_block_offset(block); + block_end = block_start + gpu_buddy_block_size(mm, block); + + if (WARN_ON(!gpu_buddy_block_is_allocated(block))) + return -EINVAL; + + if (new_size > gpu_buddy_block_size(mm, block)) + return -EINVAL; + + if (!new_size || !IS_ALIGNED(new_size, mm->chunk_size)) + return -EINVAL; + + if (new_size == gpu_buddy_block_size(mm, block)) + return 0; + + new_start = block_start; + if (start) { + new_start = *start; + + if (new_start < block_start) + return -EINVAL; + + if (!IS_ALIGNED(new_start, mm->chunk_size)) + return -EINVAL; + + if (range_overflows(new_start, new_size, block_end)) + return -EINVAL; + } + + list_del(&block->link); + mark_free(mm, block); + mm->avail += gpu_buddy_block_size(mm, block); + if (gpu_buddy_block_is_clear(block)) + mm->clear_avail += gpu_buddy_block_size(mm, block); + + /* Prevent recursively freeing this node */ + parent = block->parent; + block->parent = NULL; + + list_add(&block->tmp_link, &dfs); + err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL); + if (err) { + mark_allocated(mm, block); + mm->avail -= gpu_buddy_block_size(mm, block); + if (gpu_buddy_block_is_clear(block)) + mm->clear_avail -= gpu_buddy_block_size(mm, block); + list_add(&block->link, blocks); + } + + block->parent = parent; + return err; +} +EXPORT_SYMBOL(gpu_buddy_block_trim); + +static struct gpu_buddy_block * +__gpu_buddy_alloc_blocks(struct gpu_buddy *mm, + u64 start, u64 end, + unsigned int order, + unsigned long flags) +{ + if (flags & GPU_BUDDY_RANGE_ALLOCATION) + /* Allocate traversing within the range */ + return __gpu_buddy_alloc_range_bias(mm, start, end, + order, flags); + else + /* Allocate from freetree */ + return alloc_from_freetree(mm, order, flags); +} + +/** + * gpu_buddy_alloc_blocks - allocate power-of-two blocks + * + * @mm: GPU buddy manager to allocate from + * @start: start of the allowed range for this block + * @end: end of the allowed range for this block + * @size: size of the allocation in bytes + * @min_block_size: alignment of the allocation + * @blocks: output list head to add allocated blocks + * @flags: GPU_BUDDY_*_ALLOCATION flags + * + * alloc_range_bias() called on range limitations, which traverses + * the tree and returns the desired block. + * + * alloc_from_freetree() called when *no* range restrictions + * are enforced, which picks the block from the freetree. + * + * Returns: + * 0 on success, error code on failure. + */ +int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, + u64 start, u64 end, u64 size, + u64 min_block_size, + struct list_head *blocks, + unsigned long flags) +{ + struct gpu_buddy_block *block = NULL; + u64 original_size, original_min_size; + unsigned int min_order, order; + LIST_HEAD(allocated); + unsigned long pages; + int err; + + if (size < mm->chunk_size) + return -EINVAL; + + if (min_block_size < mm->chunk_size) + return -EINVAL; + + if (!is_power_of_2(min_block_size)) + return -EINVAL; + + if (!IS_ALIGNED(start | end | size, mm->chunk_size)) + return -EINVAL; + + if (end > mm->size) + return -EINVAL; + + if (range_overflows(start, size, mm->size)) + return -EINVAL; + + /* Actual range allocation */ + if (start + size == end) { + if (!IS_ALIGNED(start | end, min_block_size)) + return -EINVAL; + + return __gpu_buddy_alloc_range(mm, start, size, NULL, blocks); + } + + original_size = size; + original_min_size = min_block_size; + + /* Roundup the size to power of 2 */ + if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) { + size = roundup_pow_of_two(size); + min_block_size = size; + /* Align size value to min_block_size */ + } else if (!IS_ALIGNED(size, min_block_size)) { + size = round_up(size, min_block_size); + } + + pages = size >> ilog2(mm->chunk_size); + order = fls(pages) - 1; + min_order = ilog2(min_block_size) - ilog2(mm->chunk_size); + + if (order > mm->max_order || size > mm->size) { + if ((flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) && + !(flags & GPU_BUDDY_RANGE_ALLOCATION)) + return __alloc_contig_try_harder(mm, original_size, + original_min_size, blocks); + + return -EINVAL; + } + + do { + order = min(order, (unsigned int)fls(pages) - 1); + BUG_ON(order > mm->max_order); + BUG_ON(order < min_order); + + do { + block = __gpu_buddy_alloc_blocks(mm, start, + end, + order, + flags); + if (!IS_ERR(block)) + break; + + if (order-- == min_order) { + /* Try allocation through force merge method */ + if (mm->clear_avail && + !__force_merge(mm, start, end, min_order)) { + block = __gpu_buddy_alloc_blocks(mm, start, + end, + min_order, + flags); + if (!IS_ERR(block)) { + order = min_order; + break; + } + } + + /* + * Try contiguous block allocation through + * try harder method. + */ + if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION && + !(flags & GPU_BUDDY_RANGE_ALLOCATION)) + return __alloc_contig_try_harder(mm, + original_size, + original_min_size, + blocks); + err = -ENOSPC; + goto err_free; + } + } while (1); + + mark_allocated(mm, block); + mm->avail -= gpu_buddy_block_size(mm, block); + if (gpu_buddy_block_is_clear(block)) + mm->clear_avail -= gpu_buddy_block_size(mm, block); + kmemleak_update_trace(block); + list_add_tail(&block->link, &allocated); + + pages -= BIT(order); + + if (!pages) + break; + } while (1); + + /* Trim the allocated block to the required size */ + if (!(flags & GPU_BUDDY_TRIM_DISABLE) && + original_size != size) { + struct list_head *trim_list; + LIST_HEAD(temp); + u64 trim_size; + + trim_list = &allocated; + trim_size = original_size; + + if (!list_is_singular(&allocated)) { + block = list_last_entry(&allocated, typeof(*block), link); + list_move(&block->link, &temp); + trim_list = &temp; + trim_size = gpu_buddy_block_size(mm, block) - + (size - original_size); + } + + gpu_buddy_block_trim(mm, + NULL, + trim_size, + trim_list); + + if (!list_empty(&temp)) + list_splice_tail(trim_list, &allocated); + } + + list_splice_tail(&allocated, blocks); + return 0; + +err_free: + gpu_buddy_free_list_internal(mm, &allocated); + return err; +} +EXPORT_SYMBOL(gpu_buddy_alloc_blocks); + +/** + * gpu_buddy_block_print - print block information + * + * @mm: GPU buddy manager + * @block: GPU buddy block + */ +void gpu_buddy_block_print(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + u64 start = gpu_buddy_block_offset(block); + u64 size = gpu_buddy_block_size(mm, block); + + pr_info("%#018llx-%#018llx: %llu\n", start, start + size, size); +} +EXPORT_SYMBOL(gpu_buddy_block_print); + +/** + * gpu_buddy_print - print allocator state + * + * @mm: GPU buddy manager + * @p: GPU printer to use + */ +void gpu_buddy_print(struct gpu_buddy *mm) +{ + int order; + + pr_info("chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB, clear_free: %lluMiB\n", + mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); + + for (order = mm->max_order; order >= 0; order--) { + struct gpu_buddy_block *block, *tmp; + struct rb_root *root; + u64 count = 0, free; + unsigned int tree; + + for_each_free_tree(tree) { + root = &mm->free_trees[tree][order]; + + rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { + BUG_ON(!gpu_buddy_block_is_free(block)); + count++; + } + } + + free = count * (mm->chunk_size << order); + if (free < SZ_1M) + pr_info("order-%2d free: %8llu KiB, blocks: %llu\n", + order, free >> 10, count); + else + pr_info("order-%2d free: %8llu MiB, blocks: %llu\n", + order, free >> 20, count); + } +} +EXPORT_SYMBOL(gpu_buddy_print); + +static void gpu_buddy_module_exit(void) +{ + kmem_cache_destroy(slab_blocks); +} + +static int __init gpu_buddy_module_init(void) +{ + slab_blocks = KMEM_CACHE(gpu_buddy_block, 0); + if (!slab_blocks) + return -ENOMEM; + + return 0; +} + +module_init(gpu_buddy_module_init); +module_exit(gpu_buddy_module_exit); + +MODULE_DESCRIPTION("GPU Buddy Allocator"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ed85d0ceee3ba..4b0228c88f496 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -220,6 +220,7 @@ config DRM_GPUSVM config DRM_BUDDY tristate depends on DRM + select GPU_BUDDY help A page based buddy allocator @@ -416,10 +417,6 @@ config DRM_HYPERV config DRM_PANEL_BACKLIGHT_QUIRKS tristate -config DRM_LIB_RANDOM - bool - default n - config DRM_PRIVACY_SCREEN bool default n diff --git a/drivers/gpu/drm/Kconfig.debug b/drivers/gpu/drm/Kconfig.debug index 05dc43c0b8c5a..3b7886865335a 100644 --- a/drivers/gpu/drm/Kconfig.debug +++ b/drivers/gpu/drm/Kconfig.debug @@ -69,7 +69,6 @@ config DRM_KUNIT_TEST select DRM_EXPORT_FOR_TESTS if m select DRM_GEM_SHMEM_HELPER select DRM_KUNIT_TEST_HELPERS - select DRM_LIB_RANDOM select DRM_SYSFB_HELPER select PRIME_NUMBERS default KUNIT_ALL_TESTS diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index b248e64587ed4..82cd30cf6cb2d 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -77,7 +77,6 @@ drm-$(CONFIG_DRM_CLIENT) += \ drm_client.o \ drm_client_event.o \ drm_client_modeset.o -drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index e9ed1a3b135cc..9df691364a18c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -5416,7 +5416,7 @@ int amdgpu_ras_add_critical_region(struct amdgpu_device *adev, struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct amdgpu_vram_mgr_resource *vres; struct ras_critical_region *region; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; int ret = 0; if (!bo || !bo->tbo.resource) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h index be2e56ce1355e..8908d9e08a304 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h @@ -55,7 +55,7 @@ static inline void amdgpu_res_first(struct ttm_resource *res, uint64_t start, uint64_t size, struct amdgpu_res_cursor *cur) { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; struct list_head *head, *next; struct drm_mm_node *node; @@ -71,7 +71,7 @@ static inline void amdgpu_res_first(struct ttm_resource *res, head = &to_amdgpu_vram_mgr_resource(res)->blocks; block = list_first_entry_or_null(head, - struct drm_buddy_block, + struct gpu_buddy_block, link); if (!block) goto fallback; @@ -81,7 +81,7 @@ static inline void amdgpu_res_first(struct ttm_resource *res, next = block->link.next; if (next != head) - block = list_entry(next, struct drm_buddy_block, link); + block = list_entry(next, struct gpu_buddy_block, link); } cur->start = amdgpu_vram_mgr_block_start(block) + start; @@ -125,7 +125,7 @@ static inline void amdgpu_res_first(struct ttm_resource *res, */ static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size) { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; struct drm_mm_node *node; struct list_head *next; @@ -146,7 +146,7 @@ static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size) block = cur->node; next = block->link.next; - block = list_entry(next, struct drm_buddy_block, link); + block = list_entry(next, struct gpu_buddy_block, link); cur->node = block; cur->start = amdgpu_vram_mgr_block_start(block); @@ -175,7 +175,7 @@ static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size) */ static inline bool amdgpu_res_cleared(struct amdgpu_res_cursor *cur) { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; switch (cur->mem_type) { case TTM_PL_VRAM: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 9d934c07fa6b8..cd94f6efb7cb3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "amdgpu.h" #include "amdgpu_vm.h" @@ -52,15 +53,15 @@ to_amdgpu_device(struct amdgpu_vram_mgr *mgr) return container_of(mgr, struct amdgpu_device, mman.vram_mgr); } -static inline struct drm_buddy_block * +static inline struct gpu_buddy_block * amdgpu_vram_mgr_first_block(struct list_head *list) { - return list_first_entry_or_null(list, struct drm_buddy_block, link); + return list_first_entry_or_null(list, struct gpu_buddy_block, link); } static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; u64 start, size; block = amdgpu_vram_mgr_first_block(head); @@ -71,7 +72,7 @@ static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) start = amdgpu_vram_mgr_block_start(block); size = amdgpu_vram_mgr_block_size(block); - block = list_entry(block->link.next, struct drm_buddy_block, link); + block = list_entry(block->link.next, struct gpu_buddy_block, link); if (start + size != amdgpu_vram_mgr_block_start(block)) return false; } @@ -81,7 +82,7 @@ static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) static inline u64 amdgpu_vram_mgr_blocks_size(struct list_head *head) { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; u64 size = 0; list_for_each_entry(block, head, link) @@ -254,7 +255,7 @@ const struct attribute_group amdgpu_vram_mgr_attr_group = { * Calculate how many bytes of the DRM BUDDY block are inside visible VRAM */ static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev, - struct drm_buddy_block *block) + struct gpu_buddy_block *block) { u64 start = amdgpu_vram_mgr_block_start(block); u64 end = start + amdgpu_vram_mgr_block_size(block); @@ -279,7 +280,7 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo) struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); struct ttm_resource *res = bo->tbo.resource; struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res); - struct drm_buddy_block *block; + struct gpu_buddy_block *block; u64 usage = 0; if (amdgpu_gmc_vram_full_visible(&adev->gmc)) @@ -299,15 +300,15 @@ static void amdgpu_vram_mgr_do_reserve(struct ttm_resource_manager *man) { struct amdgpu_vram_mgr *mgr = to_vram_mgr(man); struct amdgpu_device *adev = to_amdgpu_device(mgr); - struct drm_buddy *mm = &mgr->mm; + struct gpu_buddy *mm = &mgr->mm; struct amdgpu_vram_reservation *rsv, *temp; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; uint64_t vis_usage; list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, blocks) { - if (drm_buddy_alloc_blocks(mm, rsv->start, rsv->start + rsv->size, + if (gpu_buddy_alloc_blocks(mm, rsv->start, rsv->start + rsv->size, rsv->size, mm->chunk_size, &rsv->allocated, - DRM_BUDDY_RANGE_ALLOCATION)) + GPU_BUDDY_RANGE_ALLOCATION)) continue; block = amdgpu_vram_mgr_first_block(&rsv->allocated); @@ -403,7 +404,7 @@ int amdgpu_vram_mgr_query_address_block_info(struct amdgpu_vram_mgr *mgr, uint64_t address, struct amdgpu_vram_block_info *info) { struct amdgpu_vram_mgr_resource *vres; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; u64 start, size; int ret = -ENOENT; @@ -450,8 +451,8 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, struct amdgpu_vram_mgr_resource *vres; u64 size, remaining_size, lpfn, fpfn; unsigned int adjust_dcc_size = 0; - struct drm_buddy *mm = &mgr->mm; - struct drm_buddy_block *block; + struct gpu_buddy *mm = &mgr->mm; + struct gpu_buddy_block *block; unsigned long pages_per_block; int r; @@ -493,17 +494,17 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, INIT_LIST_HEAD(&vres->blocks); if (place->flags & TTM_PL_FLAG_TOPDOWN) - vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + vres->flags |= GPU_BUDDY_TOPDOWN_ALLOCATION; if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS) - vres->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION; + vres->flags |= GPU_BUDDY_CONTIGUOUS_ALLOCATION; if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CLEARED) - vres->flags |= DRM_BUDDY_CLEAR_ALLOCATION; + vres->flags |= GPU_BUDDY_CLEAR_ALLOCATION; if (fpfn || lpfn != mgr->mm.size) /* Allocate blocks in desired range */ - vres->flags |= DRM_BUDDY_RANGE_ALLOCATION; + vres->flags |= GPU_BUDDY_RANGE_ALLOCATION; if (bo->flags & AMDGPU_GEM_CREATE_GFX12_DCC && adev->gmc.gmc_funcs->get_dcc_alignment) @@ -516,7 +517,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, dcc_size = roundup_pow_of_two(vres->base.size + adjust_dcc_size); remaining_size = (u64)dcc_size; - vres->flags |= DRM_BUDDY_TRIM_DISABLE; + vres->flags |= GPU_BUDDY_TRIM_DISABLE; } mutex_lock(&mgr->lock); @@ -536,7 +537,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, BUG_ON(min_block_size < mm->chunk_size); - r = drm_buddy_alloc_blocks(mm, fpfn, + r = gpu_buddy_alloc_blocks(mm, fpfn, lpfn, size, min_block_size, @@ -545,7 +546,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, if (unlikely(r == -ENOSPC) && pages_per_block == ~0ul && !(place->flags & TTM_PL_FLAG_CONTIGUOUS)) { - vres->flags &= ~DRM_BUDDY_CONTIGUOUS_ALLOCATION; + vres->flags &= ~GPU_BUDDY_CONTIGUOUS_ALLOCATION; pages_per_block = max_t(u32, 2UL << (20UL - PAGE_SHIFT), tbo->page_alignment); @@ -566,7 +567,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, list_add_tail(&vres->vres_node, &mgr->allocated_vres_list); if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS && adjust_dcc_size) { - struct drm_buddy_block *dcc_block; + struct gpu_buddy_block *dcc_block; unsigned long dcc_start; u64 trim_start; @@ -576,7 +577,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, roundup((unsigned long)amdgpu_vram_mgr_block_start(dcc_block), adjust_dcc_size); trim_start = (u64)dcc_start; - drm_buddy_block_trim(mm, &trim_start, + gpu_buddy_block_trim(mm, &trim_start, (u64)vres->base.size, &vres->blocks); } @@ -614,7 +615,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, return 0; error_free_blocks: - drm_buddy_free_list(mm, &vres->blocks, 0); + gpu_buddy_free_list(mm, &vres->blocks, 0); mutex_unlock(&mgr->lock); error_fini: ttm_resource_fini(man, &vres->base); @@ -637,8 +638,8 @@ static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man, struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res); struct amdgpu_vram_mgr *mgr = to_vram_mgr(man); struct amdgpu_device *adev = to_amdgpu_device(mgr); - struct drm_buddy *mm = &mgr->mm; - struct drm_buddy_block *block; + struct gpu_buddy *mm = &mgr->mm; + struct gpu_buddy_block *block; uint64_t vis_usage = 0; mutex_lock(&mgr->lock); @@ -649,7 +650,7 @@ static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man, list_for_each_entry(block, &vres->blocks, link) vis_usage += amdgpu_vram_mgr_vis_size(adev, block); - drm_buddy_free_list(mm, &vres->blocks, vres->flags); + gpu_buddy_free_list(mm, &vres->blocks, vres->flags); amdgpu_vram_mgr_do_reserve(man); mutex_unlock(&mgr->lock); @@ -688,7 +689,7 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev, if (!*sgt) return -ENOMEM; - /* Determine the number of DRM_BUDDY blocks to export */ + /* Determine the number of GPU_BUDDY blocks to export */ amdgpu_res_first(res, offset, length, &cursor); while (cursor.remaining) { num_entries++; @@ -704,10 +705,10 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev, sg->length = 0; /* - * Walk down DRM_BUDDY blocks to populate scatterlist nodes - * @note: Use iterator api to get first the DRM_BUDDY block + * Walk down GPU_BUDDY blocks to populate scatterlist nodes + * @note: Use iterator api to get first the GPU_BUDDY block * and the number of bytes from it. Access the following - * DRM_BUDDY block(s) if more buffer needs to exported + * GPU_BUDDY block(s) if more buffer needs to exported */ amdgpu_res_first(res, offset, length, &cursor); for_each_sgtable_sg((*sgt), sg, i) { @@ -792,10 +793,10 @@ uint64_t amdgpu_vram_mgr_vis_usage(struct amdgpu_vram_mgr *mgr) void amdgpu_vram_mgr_clear_reset_blocks(struct amdgpu_device *adev) { struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr; - struct drm_buddy *mm = &mgr->mm; + struct gpu_buddy *mm = &mgr->mm; mutex_lock(&mgr->lock); - drm_buddy_reset_clear(mm, false); + gpu_buddy_reset_clear(mm, false); mutex_unlock(&mgr->lock); } @@ -815,7 +816,7 @@ static bool amdgpu_vram_mgr_intersects(struct ttm_resource_manager *man, size_t size) { struct amdgpu_vram_mgr_resource *mgr = to_amdgpu_vram_mgr_resource(res); - struct drm_buddy_block *block; + struct gpu_buddy_block *block; /* Check each drm buddy block individually */ list_for_each_entry(block, &mgr->blocks, link) { @@ -848,7 +849,7 @@ static bool amdgpu_vram_mgr_compatible(struct ttm_resource_manager *man, size_t size) { struct amdgpu_vram_mgr_resource *mgr = to_amdgpu_vram_mgr_resource(res); - struct drm_buddy_block *block; + struct gpu_buddy_block *block; /* Check each drm buddy block individually */ list_for_each_entry(block, &mgr->blocks, link) { @@ -877,7 +878,7 @@ static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man, struct drm_printer *printer) { struct amdgpu_vram_mgr *mgr = to_vram_mgr(man); - struct drm_buddy *mm = &mgr->mm; + struct gpu_buddy *mm = &mgr->mm; struct amdgpu_vram_reservation *rsv; drm_printf(printer, " vis usage:%llu\n", @@ -930,7 +931,7 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev) mgr->default_page_size = PAGE_SIZE; man->func = &amdgpu_vram_mgr_func; - err = drm_buddy_init(&mgr->mm, man->size, PAGE_SIZE); + err = gpu_buddy_init(&mgr->mm, man->size, PAGE_SIZE); if (err) return err; @@ -965,11 +966,11 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev) kfree(rsv); list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, blocks) { - drm_buddy_free_list(&mgr->mm, &rsv->allocated, 0); + gpu_buddy_free_list(&mgr->mm, &rsv->allocated, 0); kfree(rsv); } if (!adev->gmc.is_app_apu) - drm_buddy_fini(&mgr->mm); + gpu_buddy_fini(&mgr->mm); mutex_unlock(&mgr->lock); ttm_resource_manager_cleanup(man); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h index 5f5fd9a911c26..429a21a2e9b23 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h @@ -24,11 +24,11 @@ #ifndef __AMDGPU_VRAM_MGR_H__ #define __AMDGPU_VRAM_MGR_H__ -#include +#include struct amdgpu_vram_mgr { struct ttm_resource_manager manager; - struct drm_buddy mm; + struct gpu_buddy mm; /* protects access to buffer objects */ struct mutex lock; struct list_head reservations_pending; @@ -57,19 +57,19 @@ struct amdgpu_vram_mgr_resource { struct amdgpu_vres_task task; }; -static inline u64 amdgpu_vram_mgr_block_start(struct drm_buddy_block *block) +static inline u64 amdgpu_vram_mgr_block_start(struct gpu_buddy_block *block) { - return drm_buddy_block_offset(block); + return gpu_buddy_block_offset(block); } -static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block) +static inline u64 amdgpu_vram_mgr_block_size(struct gpu_buddy_block *block) { - return (u64)PAGE_SIZE << drm_buddy_block_order(block); + return (u64)PAGE_SIZE << gpu_buddy_block_order(block); } -static inline bool amdgpu_vram_mgr_is_cleared(struct drm_buddy_block *block) +static inline bool amdgpu_vram_mgr_is_cleared(struct gpu_buddy_block *block) { - return drm_buddy_block_is_clear(block); + return gpu_buddy_block_is_clear(block); } static inline struct amdgpu_vram_mgr_resource * @@ -82,8 +82,8 @@ static inline void amdgpu_vram_mgr_set_cleared(struct ttm_resource *res) { struct amdgpu_vram_mgr_resource *ares = to_amdgpu_vram_mgr_resource(res); - WARN_ON(ares->flags & DRM_BUDDY_CLEARED); - ares->flags |= DRM_BUDDY_CLEARED; + WARN_ON(ares->flags & GPU_BUDDY_CLEARED); + ares->flags |= GPU_BUDDY_CLEARED; } int amdgpu_vram_mgr_query_address_block_info(struct amdgpu_vram_mgr *mgr, diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index 640d93070bb7c..841f3de5f307a 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -10,1250 +10,9 @@ #include #include +#include #include - -enum drm_buddy_free_tree { - DRM_BUDDY_CLEAR_TREE = 0, - DRM_BUDDY_DIRTY_TREE, - DRM_BUDDY_MAX_FREE_TREES, -}; - -static struct kmem_cache *slab_blocks; - -#define for_each_free_tree(tree) \ - for ((tree) = 0; (tree) < DRM_BUDDY_MAX_FREE_TREES; (tree)++) - -static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, - struct drm_buddy_block *parent, - unsigned int order, - u64 offset) -{ - struct drm_buddy_block *block; - - BUG_ON(order > DRM_BUDDY_MAX_ORDER); - - block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL); - if (!block) - return NULL; - - block->header = offset; - block->header |= order; - block->parent = parent; - - RB_CLEAR_NODE(&block->rb); - - BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED); - return block; -} - -static void drm_block_free(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - kmem_cache_free(slab_blocks, block); -} - -static enum drm_buddy_free_tree -get_block_tree(struct drm_buddy_block *block) -{ - return drm_buddy_block_is_clear(block) ? - DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; -} - -static struct drm_buddy_block * -rbtree_get_free_block(const struct rb_node *node) -{ - return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; -} - -static struct drm_buddy_block * -rbtree_last_free_block(struct rb_root *root) -{ - return rbtree_get_free_block(rb_last(root)); -} - -static bool rbtree_is_empty(struct rb_root *root) -{ - return RB_EMPTY_ROOT(root); -} - -static bool drm_buddy_block_offset_less(const struct drm_buddy_block *block, - const struct drm_buddy_block *node) -{ - return drm_buddy_block_offset(block) < drm_buddy_block_offset(node); -} - -static bool rbtree_block_offset_less(struct rb_node *block, - const struct rb_node *node) -{ - return drm_buddy_block_offset_less(rbtree_get_free_block(block), - rbtree_get_free_block(node)); -} - -static void rbtree_insert(struct drm_buddy *mm, - struct drm_buddy_block *block, - enum drm_buddy_free_tree tree) -{ - rb_add(&block->rb, - &mm->free_trees[tree][drm_buddy_block_order(block)], - rbtree_block_offset_less); -} - -static void rbtree_remove(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - unsigned int order = drm_buddy_block_order(block); - enum drm_buddy_free_tree tree; - struct rb_root *root; - - tree = get_block_tree(block); - root = &mm->free_trees[tree][order]; - - rb_erase(&block->rb, root); - RB_CLEAR_NODE(&block->rb); -} - -static void clear_reset(struct drm_buddy_block *block) -{ - block->header &= ~DRM_BUDDY_HEADER_CLEAR; -} - -static void mark_cleared(struct drm_buddy_block *block) -{ - block->header |= DRM_BUDDY_HEADER_CLEAR; -} - -static void mark_allocated(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - block->header &= ~DRM_BUDDY_HEADER_STATE; - block->header |= DRM_BUDDY_ALLOCATED; - - rbtree_remove(mm, block); -} - -static void mark_free(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - enum drm_buddy_free_tree tree; - - block->header &= ~DRM_BUDDY_HEADER_STATE; - block->header |= DRM_BUDDY_FREE; - - tree = get_block_tree(block); - rbtree_insert(mm, block, tree); -} - -static void mark_split(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - block->header &= ~DRM_BUDDY_HEADER_STATE; - block->header |= DRM_BUDDY_SPLIT; - - rbtree_remove(mm, block); -} - -static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) -{ - return s1 <= e2 && e1 >= s2; -} - -static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) -{ - return s1 <= s2 && e1 >= e2; -} - -static struct drm_buddy_block * -__get_buddy(struct drm_buddy_block *block) -{ - struct drm_buddy_block *parent; - - parent = block->parent; - if (!parent) - return NULL; - - if (parent->left == block) - return parent->right; - - return parent->left; -} - -static unsigned int __drm_buddy_free(struct drm_buddy *mm, - struct drm_buddy_block *block, - bool force_merge) -{ - struct drm_buddy_block *parent; - unsigned int order; - - while ((parent = block->parent)) { - struct drm_buddy_block *buddy; - - buddy = __get_buddy(block); - - if (!drm_buddy_block_is_free(buddy)) - break; - - if (!force_merge) { - /* - * Check the block and its buddy clear state and exit - * the loop if they both have the dissimilar state. - */ - if (drm_buddy_block_is_clear(block) != - drm_buddy_block_is_clear(buddy)) - break; - - if (drm_buddy_block_is_clear(block)) - mark_cleared(parent); - } - - rbtree_remove(mm, buddy); - if (force_merge && drm_buddy_block_is_clear(buddy)) - mm->clear_avail -= drm_buddy_block_size(mm, buddy); - - drm_block_free(mm, block); - drm_block_free(mm, buddy); - - block = parent; - } - - order = drm_buddy_block_order(block); - mark_free(mm, block); - - return order; -} - -static int __force_merge(struct drm_buddy *mm, - u64 start, - u64 end, - unsigned int min_order) -{ - unsigned int tree, order; - int i; - - if (!min_order) - return -ENOMEM; - - if (min_order > mm->max_order) - return -EINVAL; - - for_each_free_tree(tree) { - for (i = min_order - 1; i >= 0; i--) { - struct rb_node *iter = rb_last(&mm->free_trees[tree][i]); - - while (iter) { - struct drm_buddy_block *block, *buddy; - u64 block_start, block_end; - - block = rbtree_get_free_block(iter); - iter = rb_prev(iter); - - if (!block || !block->parent) - continue; - - block_start = drm_buddy_block_offset(block); - block_end = block_start + drm_buddy_block_size(mm, block) - 1; - - if (!contains(start, end, block_start, block_end)) - continue; - - buddy = __get_buddy(block); - if (!drm_buddy_block_is_free(buddy)) - continue; - - WARN_ON(drm_buddy_block_is_clear(block) == - drm_buddy_block_is_clear(buddy)); - - /* - * Advance to the next node when the current node is the buddy, - * as freeing the block will also remove its buddy from the tree. - */ - if (iter == &buddy->rb) - iter = rb_prev(iter); - - rbtree_remove(mm, block); - if (drm_buddy_block_is_clear(block)) - mm->clear_avail -= drm_buddy_block_size(mm, block); - - order = __drm_buddy_free(mm, block, true); - if (order >= min_order) - return 0; - } - } - } - - return -ENOMEM; -} - -/** - * drm_buddy_init - init memory manager - * - * @mm: DRM buddy manager to initialize - * @size: size in bytes to manage - * @chunk_size: minimum page size in bytes for our allocations - * - * Initializes the memory manager and its resources. - * - * Returns: - * 0 on success, error code on failure. - */ -int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) -{ - unsigned int i, j, root_count = 0; - u64 offset = 0; - - if (size < chunk_size) - return -EINVAL; - - if (chunk_size < SZ_4K) - return -EINVAL; - - if (!is_power_of_2(chunk_size)) - return -EINVAL; - - size = round_down(size, chunk_size); - - mm->size = size; - mm->avail = size; - mm->clear_avail = 0; - mm->chunk_size = chunk_size; - mm->max_order = ilog2(size) - ilog2(chunk_size); - - BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER); - - mm->free_trees = kmalloc_array(DRM_BUDDY_MAX_FREE_TREES, - sizeof(*mm->free_trees), - GFP_KERNEL); - if (!mm->free_trees) - return -ENOMEM; - - for_each_free_tree(i) { - mm->free_trees[i] = kmalloc_array(mm->max_order + 1, - sizeof(struct rb_root), - GFP_KERNEL); - if (!mm->free_trees[i]) - goto out_free_tree; - - for (j = 0; j <= mm->max_order; ++j) - mm->free_trees[i][j] = RB_ROOT; - } - - mm->n_roots = hweight64(size); - - mm->roots = kmalloc_array(mm->n_roots, - sizeof(struct drm_buddy_block *), - GFP_KERNEL); - if (!mm->roots) - goto out_free_tree; - - /* - * Split into power-of-two blocks, in case we are given a size that is - * not itself a power-of-two. - */ - do { - struct drm_buddy_block *root; - unsigned int order; - u64 root_size; - - order = ilog2(size) - ilog2(chunk_size); - root_size = chunk_size << order; - - root = drm_block_alloc(mm, NULL, order, offset); - if (!root) - goto out_free_roots; - - mark_free(mm, root); - - BUG_ON(root_count > mm->max_order); - BUG_ON(drm_buddy_block_size(mm, root) < chunk_size); - - mm->roots[root_count] = root; - - offset += root_size; - size -= root_size; - root_count++; - } while (size); - - return 0; - -out_free_roots: - while (root_count--) - drm_block_free(mm, mm->roots[root_count]); - kfree(mm->roots); -out_free_tree: - while (i--) - kfree(mm->free_trees[i]); - kfree(mm->free_trees); - return -ENOMEM; -} -EXPORT_SYMBOL(drm_buddy_init); - -/** - * drm_buddy_fini - tear down the memory manager - * - * @mm: DRM buddy manager to free - * - * Cleanup memory manager resources and the freetree - */ -void drm_buddy_fini(struct drm_buddy *mm) -{ - u64 root_size, size, start; - unsigned int order; - int i; - - size = mm->size; - - for (i = 0; i < mm->n_roots; ++i) { - order = ilog2(size) - ilog2(mm->chunk_size); - start = drm_buddy_block_offset(mm->roots[i]); - __force_merge(mm, start, start + size, order); - - if (WARN_ON(!drm_buddy_block_is_free(mm->roots[i]))) - kunit_fail_current_test("buddy_fini() root"); - - drm_block_free(mm, mm->roots[i]); - - root_size = mm->chunk_size << order; - size -= root_size; - } - - WARN_ON(mm->avail != mm->size); - - for_each_free_tree(i) - kfree(mm->free_trees[i]); - kfree(mm->free_trees); - kfree(mm->roots); -} -EXPORT_SYMBOL(drm_buddy_fini); - -static int split_block(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - unsigned int block_order = drm_buddy_block_order(block) - 1; - u64 offset = drm_buddy_block_offset(block); - - BUG_ON(!drm_buddy_block_is_free(block)); - BUG_ON(!drm_buddy_block_order(block)); - - block->left = drm_block_alloc(mm, block, block_order, offset); - if (!block->left) - return -ENOMEM; - - block->right = drm_block_alloc(mm, block, block_order, - offset + (mm->chunk_size << block_order)); - if (!block->right) { - drm_block_free(mm, block->left); - return -ENOMEM; - } - - mark_split(mm, block); - - if (drm_buddy_block_is_clear(block)) { - mark_cleared(block->left); - mark_cleared(block->right); - clear_reset(block); - } - - mark_free(mm, block->left); - mark_free(mm, block->right); - - return 0; -} - -/** - * drm_get_buddy - get buddy address - * - * @block: DRM buddy block - * - * Returns the corresponding buddy block for @block, or NULL - * if this is a root block and can't be merged further. - * Requires some kind of locking to protect against - * any concurrent allocate and free operations. - */ -struct drm_buddy_block * -drm_get_buddy(struct drm_buddy_block *block) -{ - return __get_buddy(block); -} -EXPORT_SYMBOL(drm_get_buddy); - -/** - * drm_buddy_reset_clear - reset blocks clear state - * - * @mm: DRM buddy manager - * @is_clear: blocks clear state - * - * Reset the clear state based on @is_clear value for each block - * in the freetree. - */ -void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear) -{ - enum drm_buddy_free_tree src_tree, dst_tree; - u64 root_size, size, start; - unsigned int order; - int i; - - size = mm->size; - for (i = 0; i < mm->n_roots; ++i) { - order = ilog2(size) - ilog2(mm->chunk_size); - start = drm_buddy_block_offset(mm->roots[i]); - __force_merge(mm, start, start + size, order); - - root_size = mm->chunk_size << order; - size -= root_size; - } - - src_tree = is_clear ? DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE; - dst_tree = is_clear ? DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; - - for (i = 0; i <= mm->max_order; ++i) { - struct rb_root *root = &mm->free_trees[src_tree][i]; - struct drm_buddy_block *block, *tmp; - - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - rbtree_remove(mm, block); - if (is_clear) { - mark_cleared(block); - mm->clear_avail += drm_buddy_block_size(mm, block); - } else { - clear_reset(block); - mm->clear_avail -= drm_buddy_block_size(mm, block); - } - - rbtree_insert(mm, block, dst_tree); - } - } -} -EXPORT_SYMBOL(drm_buddy_reset_clear); - -/** - * drm_buddy_free_block - free a block - * - * @mm: DRM buddy manager - * @block: block to be freed - */ -void drm_buddy_free_block(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - BUG_ON(!drm_buddy_block_is_allocated(block)); - mm->avail += drm_buddy_block_size(mm, block); - if (drm_buddy_block_is_clear(block)) - mm->clear_avail += drm_buddy_block_size(mm, block); - - __drm_buddy_free(mm, block, false); -} -EXPORT_SYMBOL(drm_buddy_free_block); - -static void __drm_buddy_free_list(struct drm_buddy *mm, - struct list_head *objects, - bool mark_clear, - bool mark_dirty) -{ - struct drm_buddy_block *block, *on; - - WARN_ON(mark_dirty && mark_clear); - - list_for_each_entry_safe(block, on, objects, link) { - if (mark_clear) - mark_cleared(block); - else if (mark_dirty) - clear_reset(block); - drm_buddy_free_block(mm, block); - cond_resched(); - } - INIT_LIST_HEAD(objects); -} - -static void drm_buddy_free_list_internal(struct drm_buddy *mm, - struct list_head *objects) -{ - /* - * Don't touch the clear/dirty bit, since allocation is still internal - * at this point. For example we might have just failed part of the - * allocation. - */ - __drm_buddy_free_list(mm, objects, false, false); -} - -/** - * drm_buddy_free_list - free blocks - * - * @mm: DRM buddy manager - * @objects: input list head to free blocks - * @flags: optional flags like DRM_BUDDY_CLEARED - */ -void drm_buddy_free_list(struct drm_buddy *mm, - struct list_head *objects, - unsigned int flags) -{ - bool mark_clear = flags & DRM_BUDDY_CLEARED; - - __drm_buddy_free_list(mm, objects, mark_clear, !mark_clear); -} -EXPORT_SYMBOL(drm_buddy_free_list); - -static bool block_incompatible(struct drm_buddy_block *block, unsigned int flags) -{ - bool needs_clear = flags & DRM_BUDDY_CLEAR_ALLOCATION; - - return needs_clear != drm_buddy_block_is_clear(block); -} - -static struct drm_buddy_block * -__alloc_range_bias(struct drm_buddy *mm, - u64 start, u64 end, - unsigned int order, - unsigned long flags, - bool fallback) -{ - u64 req_size = mm->chunk_size << order; - struct drm_buddy_block *block; - struct drm_buddy_block *buddy; - LIST_HEAD(dfs); - int err; - int i; - - end = end - 1; - - for (i = 0; i < mm->n_roots; ++i) - list_add_tail(&mm->roots[i]->tmp_link, &dfs); - - do { - u64 block_start; - u64 block_end; - - block = list_first_entry_or_null(&dfs, - struct drm_buddy_block, - tmp_link); - if (!block) - break; - - list_del(&block->tmp_link); - - if (drm_buddy_block_order(block) < order) - continue; - - block_start = drm_buddy_block_offset(block); - block_end = block_start + drm_buddy_block_size(mm, block) - 1; - - if (!overlaps(start, end, block_start, block_end)) - continue; - - if (drm_buddy_block_is_allocated(block)) - continue; - - if (block_start < start || block_end > end) { - u64 adjusted_start = max(block_start, start); - u64 adjusted_end = min(block_end, end); - - if (round_down(adjusted_end + 1, req_size) <= - round_up(adjusted_start, req_size)) - continue; - } - - if (!fallback && block_incompatible(block, flags)) - continue; - - if (contains(start, end, block_start, block_end) && - order == drm_buddy_block_order(block)) { - /* - * Find the free block within the range. - */ - if (drm_buddy_block_is_free(block)) - return block; - - continue; - } - - if (!drm_buddy_block_is_split(block)) { - err = split_block(mm, block); - if (unlikely(err)) - goto err_undo; - } - - list_add(&block->right->tmp_link, &dfs); - list_add(&block->left->tmp_link, &dfs); - } while (1); - - return ERR_PTR(-ENOSPC); - -err_undo: - /* - * We really don't want to leave around a bunch of split blocks, since - * bigger is better, so make sure we merge everything back before we - * free the allocated blocks. - */ - buddy = __get_buddy(block); - if (buddy && - (drm_buddy_block_is_free(block) && - drm_buddy_block_is_free(buddy))) - __drm_buddy_free(mm, block, false); - return ERR_PTR(err); -} - -static struct drm_buddy_block * -__drm_buddy_alloc_range_bias(struct drm_buddy *mm, - u64 start, u64 end, - unsigned int order, - unsigned long flags) -{ - struct drm_buddy_block *block; - bool fallback = false; - - block = __alloc_range_bias(mm, start, end, order, - flags, fallback); - if (IS_ERR(block)) - return __alloc_range_bias(mm, start, end, order, - flags, !fallback); - - return block; -} - -static struct drm_buddy_block * -get_maxblock(struct drm_buddy *mm, - unsigned int order, - enum drm_buddy_free_tree tree) -{ - struct drm_buddy_block *max_block = NULL, *block = NULL; - struct rb_root *root; - unsigned int i; - - for (i = order; i <= mm->max_order; ++i) { - root = &mm->free_trees[tree][i]; - block = rbtree_last_free_block(root); - if (!block) - continue; - - if (!max_block) { - max_block = block; - continue; - } - - if (drm_buddy_block_offset(block) > - drm_buddy_block_offset(max_block)) { - max_block = block; - } - } - - return max_block; -} - -static struct drm_buddy_block * -alloc_from_freetree(struct drm_buddy *mm, - unsigned int order, - unsigned long flags) -{ - struct drm_buddy_block *block = NULL; - struct rb_root *root; - enum drm_buddy_free_tree tree; - unsigned int tmp; - int err; - - tree = (flags & DRM_BUDDY_CLEAR_ALLOCATION) ? - DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; - - if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) { - block = get_maxblock(mm, order, tree); - if (block) - /* Store the obtained block order */ - tmp = drm_buddy_block_order(block); - } else { - for (tmp = order; tmp <= mm->max_order; ++tmp) { - /* Get RB tree root for this order and tree */ - root = &mm->free_trees[tree][tmp]; - block = rbtree_last_free_block(root); - if (block) - break; - } - } - - if (!block) { - /* Try allocating from the other tree */ - tree = (tree == DRM_BUDDY_CLEAR_TREE) ? - DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE; - - for (tmp = order; tmp <= mm->max_order; ++tmp) { - root = &mm->free_trees[tree][tmp]; - block = rbtree_last_free_block(root); - if (block) - break; - } - - if (!block) - return ERR_PTR(-ENOSPC); - } - - BUG_ON(!drm_buddy_block_is_free(block)); - - while (tmp != order) { - err = split_block(mm, block); - if (unlikely(err)) - goto err_undo; - - block = block->right; - tmp--; - } - return block; - -err_undo: - if (tmp != order) - __drm_buddy_free(mm, block, false); - return ERR_PTR(err); -} - -static int __alloc_range(struct drm_buddy *mm, - struct list_head *dfs, - u64 start, u64 size, - struct list_head *blocks, - u64 *total_allocated_on_err) -{ - struct drm_buddy_block *block; - struct drm_buddy_block *buddy; - u64 total_allocated = 0; - LIST_HEAD(allocated); - u64 end; - int err; - - end = start + size - 1; - - do { - u64 block_start; - u64 block_end; - - block = list_first_entry_or_null(dfs, - struct drm_buddy_block, - tmp_link); - if (!block) - break; - - list_del(&block->tmp_link); - - block_start = drm_buddy_block_offset(block); - block_end = block_start + drm_buddy_block_size(mm, block) - 1; - - if (!overlaps(start, end, block_start, block_end)) - continue; - - if (drm_buddy_block_is_allocated(block)) { - err = -ENOSPC; - goto err_free; - } - - if (contains(start, end, block_start, block_end)) { - if (drm_buddy_block_is_free(block)) { - mark_allocated(mm, block); - total_allocated += drm_buddy_block_size(mm, block); - mm->avail -= drm_buddy_block_size(mm, block); - if (drm_buddy_block_is_clear(block)) - mm->clear_avail -= drm_buddy_block_size(mm, block); - list_add_tail(&block->link, &allocated); - continue; - } else if (!mm->clear_avail) { - err = -ENOSPC; - goto err_free; - } - } - - if (!drm_buddy_block_is_split(block)) { - err = split_block(mm, block); - if (unlikely(err)) - goto err_undo; - } - - list_add(&block->right->tmp_link, dfs); - list_add(&block->left->tmp_link, dfs); - } while (1); - - if (total_allocated < size) { - err = -ENOSPC; - goto err_free; - } - - list_splice_tail(&allocated, blocks); - - return 0; - -err_undo: - /* - * We really don't want to leave around a bunch of split blocks, since - * bigger is better, so make sure we merge everything back before we - * free the allocated blocks. - */ - buddy = __get_buddy(block); - if (buddy && - (drm_buddy_block_is_free(block) && - drm_buddy_block_is_free(buddy))) - __drm_buddy_free(mm, block, false); - -err_free: - if (err == -ENOSPC && total_allocated_on_err) { - list_splice_tail(&allocated, blocks); - *total_allocated_on_err = total_allocated; - } else { - drm_buddy_free_list_internal(mm, &allocated); - } - - return err; -} - -static int __drm_buddy_alloc_range(struct drm_buddy *mm, - u64 start, - u64 size, - u64 *total_allocated_on_err, - struct list_head *blocks) -{ - LIST_HEAD(dfs); - int i; - - for (i = 0; i < mm->n_roots; ++i) - list_add_tail(&mm->roots[i]->tmp_link, &dfs); - - return __alloc_range(mm, &dfs, start, size, - blocks, total_allocated_on_err); -} - -static int __alloc_contig_try_harder(struct drm_buddy *mm, - u64 size, - u64 min_block_size, - struct list_head *blocks) -{ - u64 rhs_offset, lhs_offset, lhs_size, filled; - struct drm_buddy_block *block; - unsigned int tree, order; - LIST_HEAD(blocks_lhs); - unsigned long pages; - u64 modify_size; - int err; - - modify_size = rounddown_pow_of_two(size); - pages = modify_size >> ilog2(mm->chunk_size); - order = fls(pages) - 1; - if (order == 0) - return -ENOSPC; - - for_each_free_tree(tree) { - struct rb_root *root; - struct rb_node *iter; - - root = &mm->free_trees[tree][order]; - if (rbtree_is_empty(root)) - continue; - - iter = rb_last(root); - while (iter) { - block = rbtree_get_free_block(iter); - - /* Allocate blocks traversing RHS */ - rhs_offset = drm_buddy_block_offset(block); - err = __drm_buddy_alloc_range(mm, rhs_offset, size, - &filled, blocks); - if (!err || err != -ENOSPC) - return err; - - lhs_size = max((size - filled), min_block_size); - if (!IS_ALIGNED(lhs_size, min_block_size)) - lhs_size = round_up(lhs_size, min_block_size); - - /* Allocate blocks traversing LHS */ - lhs_offset = drm_buddy_block_offset(block) - lhs_size; - err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size, - NULL, &blocks_lhs); - if (!err) { - list_splice(&blocks_lhs, blocks); - return 0; - } else if (err != -ENOSPC) { - drm_buddy_free_list_internal(mm, blocks); - return err; - } - /* Free blocks for the next iteration */ - drm_buddy_free_list_internal(mm, blocks); - - iter = rb_prev(iter); - } - } - - return -ENOSPC; -} - -/** - * drm_buddy_block_trim - free unused pages - * - * @mm: DRM buddy manager - * @start: start address to begin the trimming. - * @new_size: original size requested - * @blocks: Input and output list of allocated blocks. - * MUST contain single block as input to be trimmed. - * On success will contain the newly allocated blocks - * making up the @new_size. Blocks always appear in - * ascending order - * - * For contiguous allocation, we round up the size to the nearest - * power of two value, drivers consume *actual* size, so remaining - * portions are unused and can be optionally freed with this function - * - * Returns: - * 0 on success, error code on failure. - */ -int drm_buddy_block_trim(struct drm_buddy *mm, - u64 *start, - u64 new_size, - struct list_head *blocks) -{ - struct drm_buddy_block *parent; - struct drm_buddy_block *block; - u64 block_start, block_end; - LIST_HEAD(dfs); - u64 new_start; - int err; - - if (!list_is_singular(blocks)) - return -EINVAL; - - block = list_first_entry(blocks, - struct drm_buddy_block, - link); - - block_start = drm_buddy_block_offset(block); - block_end = block_start + drm_buddy_block_size(mm, block); - - if (WARN_ON(!drm_buddy_block_is_allocated(block))) - return -EINVAL; - - if (new_size > drm_buddy_block_size(mm, block)) - return -EINVAL; - - if (!new_size || !IS_ALIGNED(new_size, mm->chunk_size)) - return -EINVAL; - - if (new_size == drm_buddy_block_size(mm, block)) - return 0; - - new_start = block_start; - if (start) { - new_start = *start; - - if (new_start < block_start) - return -EINVAL; - - if (!IS_ALIGNED(new_start, mm->chunk_size)) - return -EINVAL; - - if (range_overflows(new_start, new_size, block_end)) - return -EINVAL; - } - - list_del(&block->link); - mark_free(mm, block); - mm->avail += drm_buddy_block_size(mm, block); - if (drm_buddy_block_is_clear(block)) - mm->clear_avail += drm_buddy_block_size(mm, block); - - /* Prevent recursively freeing this node */ - parent = block->parent; - block->parent = NULL; - - list_add(&block->tmp_link, &dfs); - err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL); - if (err) { - mark_allocated(mm, block); - mm->avail -= drm_buddy_block_size(mm, block); - if (drm_buddy_block_is_clear(block)) - mm->clear_avail -= drm_buddy_block_size(mm, block); - list_add(&block->link, blocks); - } - - block->parent = parent; - return err; -} -EXPORT_SYMBOL(drm_buddy_block_trim); - -static struct drm_buddy_block * -__drm_buddy_alloc_blocks(struct drm_buddy *mm, - u64 start, u64 end, - unsigned int order, - unsigned long flags) -{ - if (flags & DRM_BUDDY_RANGE_ALLOCATION) - /* Allocate traversing within the range */ - return __drm_buddy_alloc_range_bias(mm, start, end, - order, flags); - else - /* Allocate from freetree */ - return alloc_from_freetree(mm, order, flags); -} - -/** - * drm_buddy_alloc_blocks - allocate power-of-two blocks - * - * @mm: DRM buddy manager to allocate from - * @start: start of the allowed range for this block - * @end: end of the allowed range for this block - * @size: size of the allocation in bytes - * @min_block_size: alignment of the allocation - * @blocks: output list head to add allocated blocks - * @flags: DRM_BUDDY_*_ALLOCATION flags - * - * alloc_range_bias() called on range limitations, which traverses - * the tree and returns the desired block. - * - * alloc_from_freetree() called when *no* range restrictions - * are enforced, which picks the block from the freetree. - * - * Returns: - * 0 on success, error code on failure. - */ -int drm_buddy_alloc_blocks(struct drm_buddy *mm, - u64 start, u64 end, u64 size, - u64 min_block_size, - struct list_head *blocks, - unsigned long flags) -{ - struct drm_buddy_block *block = NULL; - u64 original_size, original_min_size; - unsigned int min_order, order; - LIST_HEAD(allocated); - unsigned long pages; - int err; - - if (size < mm->chunk_size) - return -EINVAL; - - if (min_block_size < mm->chunk_size) - return -EINVAL; - - if (!is_power_of_2(min_block_size)) - return -EINVAL; - - if (!IS_ALIGNED(start | end | size, mm->chunk_size)) - return -EINVAL; - - if (end > mm->size) - return -EINVAL; - - if (range_overflows(start, size, mm->size)) - return -EINVAL; - - /* Actual range allocation */ - if (start + size == end) { - if (!IS_ALIGNED(start | end, min_block_size)) - return -EINVAL; - - return __drm_buddy_alloc_range(mm, start, size, NULL, blocks); - } - - original_size = size; - original_min_size = min_block_size; - - /* Roundup the size to power of 2 */ - if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION) { - size = roundup_pow_of_two(size); - min_block_size = size; - /* Align size value to min_block_size */ - } else if (!IS_ALIGNED(size, min_block_size)) { - size = round_up(size, min_block_size); - } - - pages = size >> ilog2(mm->chunk_size); - order = fls(pages) - 1; - min_order = ilog2(min_block_size) - ilog2(mm->chunk_size); - - if (order > mm->max_order || size > mm->size) { - if ((flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION) && - !(flags & DRM_BUDDY_RANGE_ALLOCATION)) - return __alloc_contig_try_harder(mm, original_size, - original_min_size, blocks); - - return -EINVAL; - } - - do { - order = min(order, (unsigned int)fls(pages) - 1); - BUG_ON(order > mm->max_order); - BUG_ON(order < min_order); - - do { - block = __drm_buddy_alloc_blocks(mm, start, - end, - order, - flags); - if (!IS_ERR(block)) - break; - - if (order-- == min_order) { - /* Try allocation through force merge method */ - if (mm->clear_avail && - !__force_merge(mm, start, end, min_order)) { - block = __drm_buddy_alloc_blocks(mm, start, - end, - min_order, - flags); - if (!IS_ERR(block)) { - order = min_order; - break; - } - } - - /* - * Try contiguous block allocation through - * try harder method. - */ - if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION && - !(flags & DRM_BUDDY_RANGE_ALLOCATION)) - return __alloc_contig_try_harder(mm, - original_size, - original_min_size, - blocks); - err = -ENOSPC; - goto err_free; - } - } while (1); - - mark_allocated(mm, block); - mm->avail -= drm_buddy_block_size(mm, block); - if (drm_buddy_block_is_clear(block)) - mm->clear_avail -= drm_buddy_block_size(mm, block); - kmemleak_update_trace(block); - list_add_tail(&block->link, &allocated); - - pages -= BIT(order); - - if (!pages) - break; - } while (1); - - /* Trim the allocated block to the required size */ - if (!(flags & DRM_BUDDY_TRIM_DISABLE) && - original_size != size) { - struct list_head *trim_list; - LIST_HEAD(temp); - u64 trim_size; - - trim_list = &allocated; - trim_size = original_size; - - if (!list_is_singular(&allocated)) { - block = list_last_entry(&allocated, typeof(*block), link); - list_move(&block->link, &temp); - trim_list = &temp; - trim_size = drm_buddy_block_size(mm, block) - - (size - original_size); - } - - drm_buddy_block_trim(mm, - NULL, - trim_size, - trim_list); - - if (!list_empty(&temp)) - list_splice_tail(trim_list, &allocated); - } - - list_splice_tail(&allocated, blocks); - return 0; - -err_free: - drm_buddy_free_list_internal(mm, &allocated); - return err; -} -EXPORT_SYMBOL(drm_buddy_alloc_blocks); +#include /** * drm_buddy_block_print - print block information @@ -1262,12 +21,12 @@ EXPORT_SYMBOL(drm_buddy_alloc_blocks); * @block: DRM buddy block * @p: DRM printer to use */ -void drm_buddy_block_print(struct drm_buddy *mm, - struct drm_buddy_block *block, +void drm_buddy_block_print(struct gpu_buddy *mm, + struct gpu_buddy_block *block, struct drm_printer *p) { - u64 start = drm_buddy_block_offset(block); - u64 size = drm_buddy_block_size(mm, block); + u64 start = gpu_buddy_block_offset(block); + u64 size = gpu_buddy_block_size(mm, block); drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size); } @@ -1279,7 +38,7 @@ EXPORT_SYMBOL(drm_buddy_block_print); * @mm: DRM buddy manager * @p: DRM printer to use */ -void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) +void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p) { int order; @@ -1287,7 +46,7 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct drm_buddy_block *block, *tmp; + struct gpu_buddy_block *block, *tmp; struct rb_root *root; u64 count = 0, free; unsigned int tree; @@ -1296,7 +55,7 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) root = &mm->free_trees[tree][order]; rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - BUG_ON(!drm_buddy_block_is_free(block)); + BUG_ON(!gpu_buddy_block_is_free(block)); count++; } } @@ -1314,22 +73,5 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) } EXPORT_SYMBOL(drm_buddy_print); -static void drm_buddy_module_exit(void) -{ - kmem_cache_destroy(slab_blocks); -} - -static int __init drm_buddy_module_init(void) -{ - slab_blocks = KMEM_CACHE(drm_buddy_block, 0); - if (!slab_blocks) - return -ENOMEM; - - return 0; -} - -module_init(drm_buddy_module_init); -module_exit(drm_buddy_module_exit); - -MODULE_DESCRIPTION("DRM Buddy Allocator"); +MODULE_DESCRIPTION("DRM-specific GPU Buddy Allocator Print Helpers"); MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index 3d53c54eb48fe..d17695ae0a31c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -5,9 +5,10 @@ #include +#include +#include #include #include -#include #include "i915_drv.h" #include "i915_ttm_buddy_manager.h" diff --git a/drivers/gpu/drm/i915/i915_scatterlist.c b/drivers/gpu/drm/i915/i915_scatterlist.c index 4d830740946da..6a34dae13769e 100644 --- a/drivers/gpu/drm/i915/i915_scatterlist.c +++ b/drivers/gpu/drm/i915/i915_scatterlist.c @@ -7,7 +7,7 @@ #include "i915_scatterlist.h" #include "i915_ttm_buddy_manager.h" -#include +#include #include #include @@ -167,9 +167,9 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res, struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res); const u64 size = res->size; const u32 max_segment = round_down(UINT_MAX, page_alignment); - struct drm_buddy *mm = bman_res->mm; + struct gpu_buddy *mm = bman_res->mm; struct list_head *blocks = &bman_res->blocks; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; struct i915_refct_sgt *rsgt; struct scatterlist *sg; struct sg_table *st; @@ -202,8 +202,8 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res, list_for_each_entry(block, blocks, link) { u64 block_size, offset; - block_size = min_t(u64, size, drm_buddy_block_size(mm, block)); - offset = drm_buddy_block_offset(block); + block_size = min_t(u64, size, gpu_buddy_block_size(mm, block)); + offset = gpu_buddy_block_offset(block); while (block_size) { u64 len; diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c index 942345548bc32..c5ca90088705a 100644 --- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c +++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c @@ -5,18 +5,19 @@ #include +#include +#include +#include #include #include -#include - #include "i915_ttm_buddy_manager.h" #include "i915_gem.h" struct i915_ttm_buddy_manager { struct ttm_resource_manager manager; - struct drm_buddy mm; + struct gpu_buddy mm; struct list_head reserved; struct mutex lock; unsigned long visible_size; @@ -38,7 +39,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, { struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); struct i915_ttm_buddy_resource *bman_res; - struct drm_buddy *mm = &bman->mm; + struct gpu_buddy *mm = &bman->mm; unsigned long n_pages, lpfn; u64 min_page_size; u64 size; @@ -57,13 +58,13 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, bman_res->mm = mm; if (place->flags & TTM_PL_FLAG_TOPDOWN) - bman_res->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + bman_res->flags |= GPU_BUDDY_TOPDOWN_ALLOCATION; if (place->flags & TTM_PL_FLAG_CONTIGUOUS) - bman_res->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION; + bman_res->flags |= GPU_BUDDY_CONTIGUOUS_ALLOCATION; if (place->fpfn || lpfn != man->size) - bman_res->flags |= DRM_BUDDY_RANGE_ALLOCATION; + bman_res->flags |= GPU_BUDDY_RANGE_ALLOCATION; GEM_BUG_ON(!bman_res->base.size); size = bman_res->base.size; @@ -89,7 +90,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, goto err_free_res; } - err = drm_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT, + err = gpu_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT, (u64)lpfn << PAGE_SHIFT, (u64)n_pages << PAGE_SHIFT, min_page_size, @@ -101,15 +102,15 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, if (lpfn <= bman->visible_size) { bman_res->used_visible_size = PFN_UP(bman_res->base.size); } else { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; list_for_each_entry(block, &bman_res->blocks, link) { unsigned long start = - drm_buddy_block_offset(block) >> PAGE_SHIFT; + gpu_buddy_block_offset(block) >> PAGE_SHIFT; if (start < bman->visible_size) { unsigned long end = start + - (drm_buddy_block_size(mm, block) >> PAGE_SHIFT); + (gpu_buddy_block_size(mm, block) >> PAGE_SHIFT); bman_res->used_visible_size += min(end, bman->visible_size) - start; @@ -126,7 +127,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, return 0; err_free_blocks: - drm_buddy_free_list(mm, &bman_res->blocks, 0); + gpu_buddy_free_list(mm, &bman_res->blocks, 0); mutex_unlock(&bman->lock); err_free_res: ttm_resource_fini(man, &bman_res->base); @@ -141,7 +142,7 @@ static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man, struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); mutex_lock(&bman->lock); - drm_buddy_free_list(&bman->mm, &bman_res->blocks, 0); + gpu_buddy_free_list(&bman->mm, &bman_res->blocks, 0); bman->visible_avail += bman_res->used_visible_size; mutex_unlock(&bman->lock); @@ -156,8 +157,8 @@ static bool i915_ttm_buddy_man_intersects(struct ttm_resource_manager *man, { struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res); struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); - struct drm_buddy *mm = &bman->mm; - struct drm_buddy_block *block; + struct gpu_buddy *mm = &bman->mm; + struct gpu_buddy_block *block; if (!place->fpfn && !place->lpfn) return true; @@ -176,9 +177,9 @@ static bool i915_ttm_buddy_man_intersects(struct ttm_resource_manager *man, /* Check each drm buddy block individually */ list_for_each_entry(block, &bman_res->blocks, link) { unsigned long fpfn = - drm_buddy_block_offset(block) >> PAGE_SHIFT; + gpu_buddy_block_offset(block) >> PAGE_SHIFT; unsigned long lpfn = fpfn + - (drm_buddy_block_size(mm, block) >> PAGE_SHIFT); + (gpu_buddy_block_size(mm, block) >> PAGE_SHIFT); if (place->fpfn < lpfn && place->lpfn > fpfn) return true; @@ -194,8 +195,8 @@ static bool i915_ttm_buddy_man_compatible(struct ttm_resource_manager *man, { struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res); struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); - struct drm_buddy *mm = &bman->mm; - struct drm_buddy_block *block; + struct gpu_buddy *mm = &bman->mm; + struct gpu_buddy_block *block; if (!place->fpfn && !place->lpfn) return true; @@ -209,9 +210,9 @@ static bool i915_ttm_buddy_man_compatible(struct ttm_resource_manager *man, /* Check each drm buddy block individually */ list_for_each_entry(block, &bman_res->blocks, link) { unsigned long fpfn = - drm_buddy_block_offset(block) >> PAGE_SHIFT; + gpu_buddy_block_offset(block) >> PAGE_SHIFT; unsigned long lpfn = fpfn + - (drm_buddy_block_size(mm, block) >> PAGE_SHIFT); + (gpu_buddy_block_size(mm, block) >> PAGE_SHIFT); if (fpfn < place->fpfn || lpfn > place->lpfn) return false; @@ -224,7 +225,7 @@ static void i915_ttm_buddy_man_debug(struct ttm_resource_manager *man, struct drm_printer *printer) { struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); - struct drm_buddy_block *block; + struct gpu_buddy_block *block; mutex_lock(&bman->lock); drm_printf(printer, "default_page_size: %lluKiB\n", @@ -293,7 +294,7 @@ int i915_ttm_buddy_man_init(struct ttm_device *bdev, if (!bman) return -ENOMEM; - err = drm_buddy_init(&bman->mm, size, chunk_size); + err = gpu_buddy_init(&bman->mm, size, chunk_size); if (err) goto err_free_bman; @@ -333,7 +334,7 @@ int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type) { struct ttm_resource_manager *man = ttm_manager_type(bdev, type); struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); - struct drm_buddy *mm = &bman->mm; + struct gpu_buddy *mm = &bman->mm; int ret; ttm_resource_manager_set_used(man, false); @@ -345,8 +346,8 @@ int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type) ttm_set_driver_manager(bdev, type, NULL); mutex_lock(&bman->lock); - drm_buddy_free_list(mm, &bman->reserved, 0); - drm_buddy_fini(mm); + gpu_buddy_free_list(mm, &bman->reserved, 0); + gpu_buddy_fini(mm); bman->visible_avail += bman->visible_reserved; WARN_ON_ONCE(bman->visible_avail != bman->visible_size); mutex_unlock(&bman->lock); @@ -371,15 +372,15 @@ int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man, u64 start, u64 size) { struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); - struct drm_buddy *mm = &bman->mm; + struct gpu_buddy *mm = &bman->mm; unsigned long fpfn = start >> PAGE_SHIFT; unsigned long flags = 0; int ret; - flags |= DRM_BUDDY_RANGE_ALLOCATION; + flags |= GPU_BUDDY_RANGE_ALLOCATION; mutex_lock(&bman->lock); - ret = drm_buddy_alloc_blocks(mm, start, + ret = gpu_buddy_alloc_blocks(mm, start, start + size, size, mm->chunk_size, &bman->reserved, diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h index d646207128303..1cff018c1689a 100644 --- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h +++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.h @@ -13,7 +13,7 @@ struct ttm_device; struct ttm_resource_manager; -struct drm_buddy; +struct gpu_buddy; /** * struct i915_ttm_buddy_resource @@ -33,7 +33,7 @@ struct i915_ttm_buddy_resource { struct list_head blocks; unsigned long flags; unsigned long used_visible_size; - struct drm_buddy *mm; + struct gpu_buddy *mm; }; /** diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c index 7b856b5090f9a..8307390943a2f 100644 --- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c +++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c @@ -6,7 +6,7 @@ #include #include -#include +#include #include "../i915_selftest.h" @@ -371,7 +371,7 @@ static int igt_mock_splintered_region(void *arg) struct drm_i915_private *i915 = mem->i915; struct i915_ttm_buddy_resource *res; struct drm_i915_gem_object *obj; - struct drm_buddy *mm; + struct gpu_buddy *mm; unsigned int expected_order; LIST_HEAD(objects); u64 size; @@ -447,8 +447,8 @@ static int igt_mock_max_segment(void *arg) struct drm_i915_private *i915 = mem->i915; struct i915_ttm_buddy_resource *res; struct drm_i915_gem_object *obj; - struct drm_buddy_block *block; - struct drm_buddy *mm; + struct gpu_buddy_block *block; + struct gpu_buddy *mm; struct list_head *blocks; struct scatterlist *sg; I915_RND_STATE(prng); @@ -487,8 +487,8 @@ static int igt_mock_max_segment(void *arg) mm = res->mm; size = 0; list_for_each_entry(block, blocks, link) { - if (drm_buddy_block_size(mm, block) > size) - size = drm_buddy_block_size(mm, block); + if (gpu_buddy_block_size(mm, block) > size) + size = gpu_buddy_block_size(mm, block); } if (size < max_segment) { pr_err("%s: Failed to create a huge contiguous block [> %u], largest block %lld\n", @@ -527,14 +527,14 @@ static u64 igt_object_mappable_total(struct drm_i915_gem_object *obj) struct intel_memory_region *mr = obj->mm.region; struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(obj->mm.res); - struct drm_buddy *mm = bman_res->mm; - struct drm_buddy_block *block; + struct gpu_buddy *mm = bman_res->mm; + struct gpu_buddy_block *block; u64 total; total = 0; list_for_each_entry(block, &bman_res->blocks, link) { - u64 start = drm_buddy_block_offset(block); - u64 end = start + drm_buddy_block_size(mm, block); + u64 start = gpu_buddy_block_offset(block); + u64 end = start + gpu_buddy_block_size(mm, block); if (start < resource_size(&mr->io)) total += min_t(u64, end, resource_size(&mr->io)) - start; diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index c0e952293ad02..524310241e16d 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_atomic_test.o \ drm_atomic_state_test.o \ drm_bridge_test.o \ - drm_buddy_test.o \ drm_cmdline_parser_test.o \ drm_connector_test.o \ drm_damage_helper_test.o \ diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c index 3a20c788c51f8..2fc47f3b463b7 100644 --- a/drivers/gpu/drm/tests/drm_exec_test.c +++ b/drivers/gpu/drm/tests/drm_exec_test.c @@ -16,8 +16,6 @@ #include #include -#include "../lib/drm_random.h" - struct drm_exec_priv { struct device *dev; struct drm_device *drm; diff --git a/drivers/gpu/drm/tests/drm_mm_test.c b/drivers/gpu/drm/tests/drm_mm_test.c index 6174d09290208..99744322ed1a2 100644 --- a/drivers/gpu/drm/tests/drm_mm_test.c +++ b/drivers/gpu/drm/tests/drm_mm_test.c @@ -15,8 +15,6 @@ #include -#include "../lib/drm_random.h" - enum { BEST, BOTTOMUP, diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c index 1bcc67977f487..3cbdc8101b7c6 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c @@ -251,7 +251,7 @@ static void ttm_bo_validate_basic(struct kunit *test) NULL, &dummy_ttm_bo_destroy); KUNIT_EXPECT_EQ(test, err, 0); - snd_place = ttm_place_kunit_init(test, snd_mem, DRM_BUDDY_TOPDOWN_ALLOCATION); + snd_place = ttm_place_kunit_init(test, snd_mem, GPU_BUDDY_TOPDOWN_ALLOCATION); snd_placement = ttm_placement_kunit_init(test, snd_place, 1); err = ttm_bo_validate(bo, snd_placement, &ctx_val); @@ -263,7 +263,7 @@ static void ttm_bo_validate_basic(struct kunit *test) KUNIT_EXPECT_TRUE(test, ttm_tt_is_populated(bo->ttm)); KUNIT_EXPECT_EQ(test, bo->resource->mem_type, snd_mem); KUNIT_EXPECT_EQ(test, bo->resource->placement, - DRM_BUDDY_TOPDOWN_ALLOCATION); + GPU_BUDDY_TOPDOWN_ALLOCATION); ttm_bo_put(bo); ttm_mock_manager_fini(priv->ttm_dev, snd_mem); diff --git a/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c b/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c index d7eb6471f2eda..a9c233f982f67 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c +++ b/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c @@ -30,7 +30,7 @@ static int ttm_mock_manager_alloc(struct ttm_resource_manager *man, { struct ttm_mock_manager *manager = to_mock_mgr(man); struct ttm_mock_resource *mock_res; - struct drm_buddy *mm = &manager->mm; + struct gpu_buddy *mm = &manager->mm; u64 lpfn, fpfn, alloc_size; int err; @@ -46,14 +46,14 @@ static int ttm_mock_manager_alloc(struct ttm_resource_manager *man, INIT_LIST_HEAD(&mock_res->blocks); if (place->flags & TTM_PL_FLAG_TOPDOWN) - mock_res->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + mock_res->flags |= GPU_BUDDY_TOPDOWN_ALLOCATION; if (place->flags & TTM_PL_FLAG_CONTIGUOUS) - mock_res->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION; + mock_res->flags |= GPU_BUDDY_CONTIGUOUS_ALLOCATION; alloc_size = (uint64_t)mock_res->base.size; mutex_lock(&manager->lock); - err = drm_buddy_alloc_blocks(mm, fpfn, lpfn, alloc_size, + err = gpu_buddy_alloc_blocks(mm, fpfn, lpfn, alloc_size, manager->default_page_size, &mock_res->blocks, mock_res->flags); @@ -66,7 +66,7 @@ static int ttm_mock_manager_alloc(struct ttm_resource_manager *man, return 0; error_free_blocks: - drm_buddy_free_list(mm, &mock_res->blocks, 0); + gpu_buddy_free_list(mm, &mock_res->blocks, 0); ttm_resource_fini(man, &mock_res->base); mutex_unlock(&manager->lock); @@ -78,10 +78,10 @@ static void ttm_mock_manager_free(struct ttm_resource_manager *man, { struct ttm_mock_manager *manager = to_mock_mgr(man); struct ttm_mock_resource *mock_res = to_mock_mgr_resource(res); - struct drm_buddy *mm = &manager->mm; + struct gpu_buddy *mm = &manager->mm; mutex_lock(&manager->lock); - drm_buddy_free_list(mm, &mock_res->blocks, 0); + gpu_buddy_free_list(mm, &mock_res->blocks, 0); mutex_unlock(&manager->lock); ttm_resource_fini(man, res); @@ -105,7 +105,7 @@ int ttm_mock_manager_init(struct ttm_device *bdev, u32 mem_type, u32 size) mutex_init(&manager->lock); - err = drm_buddy_init(&manager->mm, size, PAGE_SIZE); + err = gpu_buddy_init(&manager->mm, size, PAGE_SIZE); if (err) { kfree(manager); @@ -141,7 +141,7 @@ void ttm_mock_manager_fini(struct ttm_device *bdev, u32 mem_type) ttm_resource_manager_set_used(man, false); mutex_lock(&mock_man->lock); - drm_buddy_fini(&mock_man->mm); + gpu_buddy_fini(&mock_man->mm); mutex_unlock(&mock_man->lock); ttm_set_driver_manager(bdev, mem_type, NULL); diff --git a/drivers/gpu/drm/ttm/tests/ttm_mock_manager.h b/drivers/gpu/drm/ttm/tests/ttm_mock_manager.h index e4c95f86a4670..08710756fd8e0 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_mock_manager.h +++ b/drivers/gpu/drm/ttm/tests/ttm_mock_manager.h @@ -5,11 +5,11 @@ #ifndef TTM_MOCK_MANAGER_H #define TTM_MOCK_MANAGER_H -#include +#include struct ttm_mock_manager { struct ttm_resource_manager man; - struct drm_buddy mm; + struct gpu_buddy mm; u64 default_page_size; /* protects allocations of mock buffer objects */ struct mutex lock; diff --git a/drivers/gpu/drm/xe/xe_res_cursor.h b/drivers/gpu/drm/xe/xe_res_cursor.h index 4e00008b70817..5f4ab08c06869 100644 --- a/drivers/gpu/drm/xe/xe_res_cursor.h +++ b/drivers/gpu/drm/xe/xe_res_cursor.h @@ -58,7 +58,7 @@ struct xe_res_cursor { /** @dma_addr: Current element in a struct drm_pagemap_addr array */ const struct drm_pagemap_addr *dma_addr; /** @mm: Buddy allocator for VRAM cursor */ - struct drm_buddy *mm; + struct gpu_buddy *mm; /** * @dma_start: DMA start address for the current segment. * This may be different to @dma_addr.addr since elements in @@ -69,7 +69,7 @@ struct xe_res_cursor { u64 dma_seg_size; }; -static struct drm_buddy *xe_res_get_buddy(struct ttm_resource *res) +static struct gpu_buddy *xe_res_get_buddy(struct ttm_resource *res) { struct ttm_resource_manager *mgr; @@ -104,30 +104,30 @@ static inline void xe_res_first(struct ttm_resource *res, case XE_PL_STOLEN: case XE_PL_VRAM0: case XE_PL_VRAM1: { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; struct list_head *head, *next; - struct drm_buddy *mm = xe_res_get_buddy(res); + struct gpu_buddy *mm = xe_res_get_buddy(res); head = &to_xe_ttm_vram_mgr_resource(res)->blocks; block = list_first_entry_or_null(head, - struct drm_buddy_block, + struct gpu_buddy_block, link); if (!block) goto fallback; - while (start >= drm_buddy_block_size(mm, block)) { - start -= drm_buddy_block_size(mm, block); + while (start >= gpu_buddy_block_size(mm, block)) { + start -= gpu_buddy_block_size(mm, block); next = block->link.next; if (next != head) - block = list_entry(next, struct drm_buddy_block, + block = list_entry(next, struct gpu_buddy_block, link); } cur->mm = mm; - cur->start = drm_buddy_block_offset(block) + start; - cur->size = min(drm_buddy_block_size(mm, block) - start, + cur->start = gpu_buddy_block_offset(block) + start; + cur->size = min(gpu_buddy_block_size(mm, block) - start, size); cur->remaining = size; cur->node = block; @@ -259,7 +259,7 @@ static inline void xe_res_first_dma(const struct drm_pagemap_addr *dma_addr, */ static inline void xe_res_next(struct xe_res_cursor *cur, u64 size) { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; struct list_head *next; u64 start; @@ -295,18 +295,18 @@ static inline void xe_res_next(struct xe_res_cursor *cur, u64 size) block = cur->node; next = block->link.next; - block = list_entry(next, struct drm_buddy_block, link); + block = list_entry(next, struct gpu_buddy_block, link); - while (start >= drm_buddy_block_size(cur->mm, block)) { - start -= drm_buddy_block_size(cur->mm, block); + while (start >= gpu_buddy_block_size(cur->mm, block)) { + start -= gpu_buddy_block_size(cur->mm, block); next = block->link.next; - block = list_entry(next, struct drm_buddy_block, link); + block = list_entry(next, struct gpu_buddy_block, link); } - cur->start = drm_buddy_block_offset(block) + start; - cur->size = min(drm_buddy_block_size(cur->mm, block) - start, + cur->start = gpu_buddy_block_offset(block) + start; + cur->size = min(gpu_buddy_block_size(cur->mm, block) - start, cur->remaining); cur->node = block; break; diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index 40015e091ed52..e1b8030b6bcf5 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -689,7 +689,7 @@ static u64 block_offset_to_pfn(struct xe_vram_region *vr, u64 offset) return PHYS_PFN(offset + vr->hpa_base); } -static struct drm_buddy *vram_to_buddy(struct xe_vram_region *vram) +static struct gpu_buddy *vram_to_buddy(struct xe_vram_region *vram) { return &vram->ttm.mm; } @@ -700,16 +700,16 @@ static int xe_svm_populate_devmem_pfn(struct drm_pagemap_devmem *devmem_allocati struct xe_bo *bo = to_xe_bo(devmem_allocation); struct ttm_resource *res = bo->ttm.resource; struct list_head *blocks = &to_xe_ttm_vram_mgr_resource(res)->blocks; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; int j = 0; list_for_each_entry(block, blocks, link) { struct xe_vram_region *vr = block->private; - struct drm_buddy *buddy = vram_to_buddy(vr); - u64 block_pfn = block_offset_to_pfn(vr, drm_buddy_block_offset(block)); + struct gpu_buddy *buddy = vram_to_buddy(vr); + u64 block_pfn = block_offset_to_pfn(vr, gpu_buddy_block_offset(block)); int i; - for (i = 0; i < drm_buddy_block_size(buddy, block) >> PAGE_SHIFT; ++i) + for (i = 0; i < gpu_buddy_block_size(buddy, block) >> PAGE_SHIFT; ++i) pfn[j++] = block_pfn + i; } @@ -877,7 +877,7 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, struct dma_fence *pre_migrate_fence = NULL; struct xe_device *xe = vr->xe; struct device *dev = xe->drm.dev; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; struct xe_validation_ctx vctx; struct list_head *blocks; struct drm_exec exec; diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c index 9175b4a2214b8..f3d72773835cc 100644 --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -17,16 +18,16 @@ #include "xe_ttm_vram_mgr.h" #include "xe_vram_types.h" -static inline struct drm_buddy_block * +static inline struct gpu_buddy_block * xe_ttm_vram_mgr_first_block(struct list_head *list) { - return list_first_entry_or_null(list, struct drm_buddy_block, link); + return list_first_entry_or_null(list, struct gpu_buddy_block, link); } -static inline bool xe_is_vram_mgr_blocks_contiguous(struct drm_buddy *mm, +static inline bool xe_is_vram_mgr_blocks_contiguous(struct gpu_buddy *mm, struct list_head *head) { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; u64 start, size; block = xe_ttm_vram_mgr_first_block(head); @@ -34,12 +35,12 @@ static inline bool xe_is_vram_mgr_blocks_contiguous(struct drm_buddy *mm, return false; while (head != block->link.next) { - start = drm_buddy_block_offset(block); - size = drm_buddy_block_size(mm, block); + start = gpu_buddy_block_offset(block); + size = gpu_buddy_block_size(mm, block); - block = list_entry(block->link.next, struct drm_buddy_block, + block = list_entry(block->link.next, struct gpu_buddy_block, link); - if (start + size != drm_buddy_block_offset(block)) + if (start + size != gpu_buddy_block_offset(block)) return false; } @@ -53,7 +54,7 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man, { struct xe_ttm_vram_mgr *mgr = to_xe_ttm_vram_mgr(man); struct xe_ttm_vram_mgr_resource *vres; - struct drm_buddy *mm = &mgr->mm; + struct gpu_buddy *mm = &mgr->mm; u64 size, min_page_size; unsigned long lpfn; int err; @@ -80,10 +81,10 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man, INIT_LIST_HEAD(&vres->blocks); if (place->flags & TTM_PL_FLAG_TOPDOWN) - vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + vres->flags |= GPU_BUDDY_TOPDOWN_ALLOCATION; if (place->fpfn || lpfn != man->size >> PAGE_SHIFT) - vres->flags |= DRM_BUDDY_RANGE_ALLOCATION; + vres->flags |= GPU_BUDDY_RANGE_ALLOCATION; if (WARN_ON(!vres->base.size)) { err = -EINVAL; @@ -119,27 +120,27 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man, lpfn = max_t(unsigned long, place->fpfn + (size >> PAGE_SHIFT), lpfn); } - err = drm_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT, + err = gpu_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT, (u64)lpfn << PAGE_SHIFT, size, min_page_size, &vres->blocks, vres->flags); if (err) goto error_unlock; if (place->flags & TTM_PL_FLAG_CONTIGUOUS) { - if (!drm_buddy_block_trim(mm, NULL, vres->base.size, &vres->blocks)) + if (!gpu_buddy_block_trim(mm, NULL, vres->base.size, &vres->blocks)) size = vres->base.size; } if (lpfn <= mgr->visible_size >> PAGE_SHIFT) { vres->used_visible_size = size; } else { - struct drm_buddy_block *block; + struct gpu_buddy_block *block; list_for_each_entry(block, &vres->blocks, link) { - u64 start = drm_buddy_block_offset(block); + u64 start = gpu_buddy_block_offset(block); if (start < mgr->visible_size) { - u64 end = start + drm_buddy_block_size(mm, block); + u64 end = start + gpu_buddy_block_size(mm, block); vres->used_visible_size += min(end, mgr->visible_size) - start; @@ -159,11 +160,11 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man, * the object. */ if (vres->base.placement & TTM_PL_FLAG_CONTIGUOUS) { - struct drm_buddy_block *block = list_first_entry(&vres->blocks, + struct gpu_buddy_block *block = list_first_entry(&vres->blocks, typeof(*block), link); - vres->base.start = drm_buddy_block_offset(block) >> PAGE_SHIFT; + vres->base.start = gpu_buddy_block_offset(block) >> PAGE_SHIFT; } else { vres->base.start = XE_BO_INVALID_OFFSET; } @@ -185,10 +186,10 @@ static void xe_ttm_vram_mgr_del(struct ttm_resource_manager *man, struct xe_ttm_vram_mgr_resource *vres = to_xe_ttm_vram_mgr_resource(res); struct xe_ttm_vram_mgr *mgr = to_xe_ttm_vram_mgr(man); - struct drm_buddy *mm = &mgr->mm; + struct gpu_buddy *mm = &mgr->mm; mutex_lock(&mgr->lock); - drm_buddy_free_list(mm, &vres->blocks, 0); + gpu_buddy_free_list(mm, &vres->blocks, 0); mgr->visible_avail += vres->used_visible_size; mutex_unlock(&mgr->lock); @@ -201,7 +202,7 @@ static void xe_ttm_vram_mgr_debug(struct ttm_resource_manager *man, struct drm_printer *printer) { struct xe_ttm_vram_mgr *mgr = to_xe_ttm_vram_mgr(man); - struct drm_buddy *mm = &mgr->mm; + struct gpu_buddy *mm = &mgr->mm; mutex_lock(&mgr->lock); drm_printf(printer, "default_page_size: %lluKiB\n", @@ -224,8 +225,8 @@ static bool xe_ttm_vram_mgr_intersects(struct ttm_resource_manager *man, struct xe_ttm_vram_mgr *mgr = to_xe_ttm_vram_mgr(man); struct xe_ttm_vram_mgr_resource *vres = to_xe_ttm_vram_mgr_resource(res); - struct drm_buddy *mm = &mgr->mm; - struct drm_buddy_block *block; + struct gpu_buddy *mm = &mgr->mm; + struct gpu_buddy_block *block; if (!place->fpfn && !place->lpfn) return true; @@ -235,9 +236,9 @@ static bool xe_ttm_vram_mgr_intersects(struct ttm_resource_manager *man, list_for_each_entry(block, &vres->blocks, link) { unsigned long fpfn = - drm_buddy_block_offset(block) >> PAGE_SHIFT; + gpu_buddy_block_offset(block) >> PAGE_SHIFT; unsigned long lpfn = fpfn + - (drm_buddy_block_size(mm, block) >> PAGE_SHIFT); + (gpu_buddy_block_size(mm, block) >> PAGE_SHIFT); if (place->fpfn < lpfn && place->lpfn > fpfn) return true; @@ -254,8 +255,8 @@ static bool xe_ttm_vram_mgr_compatible(struct ttm_resource_manager *man, struct xe_ttm_vram_mgr *mgr = to_xe_ttm_vram_mgr(man); struct xe_ttm_vram_mgr_resource *vres = to_xe_ttm_vram_mgr_resource(res); - struct drm_buddy *mm = &mgr->mm; - struct drm_buddy_block *block; + struct gpu_buddy *mm = &mgr->mm; + struct gpu_buddy_block *block; if (!place->fpfn && !place->lpfn) return true; @@ -265,9 +266,9 @@ static bool xe_ttm_vram_mgr_compatible(struct ttm_resource_manager *man, list_for_each_entry(block, &vres->blocks, link) { unsigned long fpfn = - drm_buddy_block_offset(block) >> PAGE_SHIFT; + gpu_buddy_block_offset(block) >> PAGE_SHIFT; unsigned long lpfn = fpfn + - (drm_buddy_block_size(mm, block) >> PAGE_SHIFT); + (gpu_buddy_block_size(mm, block) >> PAGE_SHIFT); if (fpfn < place->fpfn || lpfn > place->lpfn) return false; @@ -297,7 +298,7 @@ static void ttm_vram_mgr_fini(struct drm_device *dev, void *arg) WARN_ON_ONCE(mgr->visible_avail != mgr->visible_size); - drm_buddy_fini(&mgr->mm); + gpu_buddy_fini(&mgr->mm); ttm_resource_manager_cleanup(&mgr->manager); @@ -328,7 +329,7 @@ int __xe_ttm_vram_mgr_init(struct xe_device *xe, struct xe_ttm_vram_mgr *mgr, mgr->visible_avail = io_size; ttm_resource_manager_init(man, &xe->ttm, size); - err = drm_buddy_init(&mgr->mm, man->size, default_page_size); + err = gpu_buddy_init(&mgr->mm, man->size, default_page_size); if (err) return err; @@ -376,7 +377,7 @@ int xe_ttm_vram_mgr_alloc_sgt(struct xe_device *xe, if (!*sgt) return -ENOMEM; - /* Determine the number of DRM_BUDDY blocks to export */ + /* Determine the number of GPU_BUDDY blocks to export */ xe_res_first(res, offset, length, &cursor); while (cursor.remaining) { num_entries++; @@ -393,10 +394,10 @@ int xe_ttm_vram_mgr_alloc_sgt(struct xe_device *xe, sg->length = 0; /* - * Walk down DRM_BUDDY blocks to populate scatterlist nodes - * @note: Use iterator api to get first the DRM_BUDDY block + * Walk down GPU_BUDDY blocks to populate scatterlist nodes + * @note: Use iterator api to get first the GPU_BUDDY block * and the number of bytes from it. Access the following - * DRM_BUDDY block(s) if more buffer needs to exported + * GPU_BUDDY block(s) if more buffer needs to exported */ xe_res_first(res, offset, length, &cursor); for_each_sgtable_sg((*sgt), sg, i) { diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h b/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h index 1144f9232ebbb..674a846f99862 100644 --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h @@ -6,7 +6,7 @@ #ifndef _XE_TTM_VRAM_MGR_TYPES_H_ #define _XE_TTM_VRAM_MGR_TYPES_H_ -#include +#include #include /** @@ -18,7 +18,7 @@ struct xe_ttm_vram_mgr { /** @manager: Base TTM resource manager */ struct ttm_resource_manager manager; /** @mm: DRM buddy allocator which manages the VRAM */ - struct drm_buddy mm; + struct gpu_buddy mm; /** @visible_size: Proped size of the CPU visible portion */ u64 visible_size; /** @visible_avail: CPU visible portion still unallocated */ diff --git a/drivers/gpu/tests/Makefile b/drivers/gpu/tests/Makefile new file mode 100644 index 0000000000000..4183e6e2de451 --- /dev/null +++ b/drivers/gpu/tests/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +gpu_buddy_tests-y = gpu_buddy_test.o gpu_random.o +obj-$(CONFIG_GPU_BUDDY_KUNIT_TEST) += gpu_buddy_tests.o diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c similarity index 66% rename from drivers/gpu/drm/tests/drm_buddy_test.c rename to drivers/gpu/tests/gpu_buddy_test.c index 7a0e523651f05..3f689e1f3581a 100644 --- a/drivers/gpu/drm/tests/drm_buddy_test.c +++ b/drivers/gpu/tests/gpu_buddy_test.c @@ -10,9 +10,9 @@ #include #include -#include +#include -#include "../lib/drm_random.h" +#include "gpu_random.h" static unsigned int random_seed; @@ -21,14 +21,14 @@ static inline u64 get_size(int order, u64 chunk_size) return (1 << order) * chunk_size; } -static void drm_test_buddy_alloc_range_bias(struct kunit *test) +static void gpu_test_buddy_alloc_range_bias(struct kunit *test) { u32 mm_size, size, ps, bias_size, bias_start, bias_end, bias_rem; - DRM_RND_STATE(prng, random_seed); + GPU_RND_STATE(prng, random_seed); unsigned int i, count, *order; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; unsigned long flags; - struct drm_buddy mm; + struct gpu_buddy mm; LIST_HEAD(allocated); bias_size = SZ_1M; @@ -38,11 +38,11 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) kunit_info(test, "mm_size=%u, ps=%u\n", mm_size, ps); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, ps), + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps), "buddy_init failed\n"); count = mm_size / bias_size; - order = drm_random_order(count, &prng); + order = gpu_random_order(count, &prng); KUNIT_EXPECT_TRUE(test, order); /* @@ -62,79 +62,79 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) /* internal round_up too big */ KUNIT_ASSERT_TRUE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, bias_size + ps, bias_size, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, bias_size, bias_size); /* size too big */ KUNIT_ASSERT_TRUE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, bias_size + ps, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc didn't fail with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, bias_size + ps, ps); /* bias range too small for size */ KUNIT_ASSERT_TRUE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start + ps, + gpu_buddy_alloc_blocks(&mm, bias_start + ps, bias_end, bias_size, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc didn't fail with bias(%x-%x), size=%u, ps=%u\n", bias_start + ps, bias_end, bias_size, ps); /* bias misaligned */ KUNIT_ASSERT_TRUE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start + ps, + gpu_buddy_alloc_blocks(&mm, bias_start + ps, bias_end - ps, bias_size >> 1, bias_size >> 1, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc h didn't fail with bias(%x-%x), size=%u, ps=%u\n", bias_start + ps, bias_end - ps, bias_size >> 1, bias_size >> 1); /* single big page */ KUNIT_ASSERT_FALSE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, bias_size, bias_size, &tmp, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc i failed with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, bias_size, bias_size); - drm_buddy_free_list(&mm, &tmp, 0); + gpu_buddy_free_list(&mm, &tmp, 0); /* single page with internal round_up */ KUNIT_ASSERT_FALSE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, ps, bias_size, &tmp, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, ps, bias_size); - drm_buddy_free_list(&mm, &tmp, 0); + gpu_buddy_free_list(&mm, &tmp, 0); /* random size within */ size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps); if (size) KUNIT_ASSERT_FALSE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, size, ps, &tmp, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, size, ps); bias_rem -= size; /* too big for current avail */ KUNIT_ASSERT_TRUE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, bias_rem + ps, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc didn't fail with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, bias_rem + ps, ps); @@ -144,10 +144,10 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) size = max(size, ps); KUNIT_ASSERT_FALSE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, size, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, size, ps); /* @@ -155,15 +155,15 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) * unallocated, and ideally not always on the bias * boundaries. */ - drm_buddy_free_list(&mm, &tmp, 0); + gpu_buddy_free_list(&mm, &tmp, 0); } else { list_splice_tail(&tmp, &allocated); } } kfree(order); - drm_buddy_free_list(&mm, &allocated, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &allocated, 0); + gpu_buddy_fini(&mm); /* * Something more free-form. Idea is to pick a random starting bias @@ -174,7 +174,7 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) * allocated nodes in the middle of the address space. */ - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, ps), + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps), "buddy_init failed\n"); bias_start = round_up(prandom_u32_state(&prng) % (mm_size - ps), ps); @@ -186,10 +186,10 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) u32 size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps); KUNIT_ASSERT_FALSE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, size, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc failed with bias(%x-%x), size=%u, ps=%u\n", bias_start, bias_end, size, ps); bias_rem -= size; @@ -215,24 +215,24 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) KUNIT_ASSERT_EQ(test, bias_start, 0); KUNIT_ASSERT_EQ(test, bias_end, mm_size); KUNIT_ASSERT_TRUE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, bias_end, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, ps, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc passed with bias(%x-%x), size=%u\n", bias_start, bias_end, ps); - drm_buddy_free_list(&mm, &allocated, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &allocated, 0); + gpu_buddy_fini(&mm); /* - * Allocate cleared blocks in the bias range when the DRM buddy's clear avail is + * Allocate cleared blocks in the bias range when the GPU buddy's clear avail is * zero. This will validate the bias range allocation in scenarios like system boot * when no cleared blocks are available and exercise the fallback path too. The resulting * blocks should always be dirty. */ - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, ps), + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps), "buddy_init failed\n"); bias_start = round_up(prandom_u32_state(&prng) % (mm_size - ps), ps); @@ -240,11 +240,11 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) bias_end = max(bias_end, bias_start + ps); bias_rem = bias_end - bias_start; - flags = DRM_BUDDY_CLEAR_ALLOCATION | DRM_BUDDY_RANGE_ALLOCATION; + flags = GPU_BUDDY_CLEAR_ALLOCATION | GPU_BUDDY_RANGE_ALLOCATION; size = max(round_up(prandom_u32_state(&prng) % bias_rem, ps), ps); KUNIT_ASSERT_FALSE_MSG(test, - drm_buddy_alloc_blocks(&mm, bias_start, + gpu_buddy_alloc_blocks(&mm, bias_start, bias_end, size, ps, &allocated, flags), @@ -252,27 +252,27 @@ static void drm_test_buddy_alloc_range_bias(struct kunit *test) bias_start, bias_end, size, ps); list_for_each_entry(block, &allocated, link) - KUNIT_EXPECT_EQ(test, drm_buddy_block_is_clear(block), false); + KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false); - drm_buddy_free_list(&mm, &allocated, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &allocated, 0); + gpu_buddy_fini(&mm); } -static void drm_test_buddy_alloc_clear(struct kunit *test) +static void gpu_test_buddy_alloc_clear(struct kunit *test) { unsigned long n_pages, total, i = 0; const unsigned long ps = SZ_4K; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; const int max_order = 12; LIST_HEAD(allocated); - struct drm_buddy mm; + struct gpu_buddy mm; unsigned int order; u32 mm_size, size; LIST_HEAD(dirty); LIST_HEAD(clean); mm_size = SZ_4K << max_order; - KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, mm_size, ps)); + KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); KUNIT_EXPECT_EQ(test, mm.max_order, max_order); @@ -285,11 +285,11 @@ static void drm_test_buddy_alloc_clear(struct kunit *test) * is indeed all dirty pages and vice versa. Free it all again, * keeping the dirty/clear status. */ - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 5 * ps, ps, &allocated, - DRM_BUDDY_TOPDOWN_ALLOCATION), + GPU_BUDDY_TOPDOWN_ALLOCATION), "buddy_alloc hit an error size=%lu\n", 5 * ps); - drm_buddy_free_list(&mm, &allocated, DRM_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); n_pages = 10; do { @@ -302,37 +302,37 @@ static void drm_test_buddy_alloc_clear(struct kunit *test) flags = 0; } else { list = &clean; - flags = DRM_BUDDY_CLEAR_ALLOCATION; + flags = GPU_BUDDY_CLEAR_ALLOCATION; } - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, ps, ps, list, flags), "buddy_alloc hit an error size=%lu\n", ps); } while (++i < n_pages); list_for_each_entry(block, &clean, link) - KUNIT_EXPECT_EQ(test, drm_buddy_block_is_clear(block), true); + KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), true); list_for_each_entry(block, &dirty, link) - KUNIT_EXPECT_EQ(test, drm_buddy_block_is_clear(block), false); + KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false); - drm_buddy_free_list(&mm, &clean, DRM_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); /* * Trying to go over the clear limit for some allocation. * The allocation should never fail with reasonable page-size. */ - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 10 * ps, ps, &clean, - DRM_BUDDY_CLEAR_ALLOCATION), + GPU_BUDDY_CLEAR_ALLOCATION), "buddy_alloc hit an error size=%lu\n", 10 * ps); - drm_buddy_free_list(&mm, &clean, DRM_BUDDY_CLEARED); - drm_buddy_free_list(&mm, &dirty, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &dirty, 0); + gpu_buddy_fini(&mm); - KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, mm_size, ps)); + KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); /* * Create a new mm. Intentionally fragment the address space by creating @@ -354,34 +354,34 @@ static void drm_test_buddy_alloc_clear(struct kunit *test) else list = &clean; - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, ps, ps, list, 0), "buddy_alloc hit an error size=%lu\n", ps); } while (++i < n_pages); - drm_buddy_free_list(&mm, &clean, DRM_BUDDY_CLEARED); - drm_buddy_free_list(&mm, &dirty, 0); + gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &dirty, 0); order = 1; do { size = SZ_4K << order; - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, size, &allocated, - DRM_BUDDY_CLEAR_ALLOCATION), + GPU_BUDDY_CLEAR_ALLOCATION), "buddy_alloc hit an error size=%u\n", size); total = 0; list_for_each_entry(block, &allocated, link) { if (size != mm_size) - KUNIT_EXPECT_EQ(test, drm_buddy_block_is_clear(block), false); - total += drm_buddy_block_size(&mm, block); + KUNIT_EXPECT_EQ(test, gpu_buddy_block_is_clear(block), false); + total += gpu_buddy_block_size(&mm, block); } KUNIT_EXPECT_EQ(test, total, size); - drm_buddy_free_list(&mm, &allocated, 0); + gpu_buddy_free_list(&mm, &allocated, 0); } while (++order <= max_order); - drm_buddy_fini(&mm); + gpu_buddy_fini(&mm); /* * Create a new mm with a non power-of-two size. Allocate a random size from each @@ -390,44 +390,44 @@ static void drm_test_buddy_alloc_clear(struct kunit *test) */ mm_size = (SZ_4K << max_order) + (SZ_4K << (max_order - 2)); - KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, mm_size, ps)); + KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); KUNIT_EXPECT_EQ(test, mm.max_order, max_order); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order, 4 * ps, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc hit an error size=%lu\n", 4 * ps); - drm_buddy_free_list(&mm, &allocated, DRM_BUDDY_CLEARED); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order, + gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, SZ_4K << max_order, 2 * ps, ps, &allocated, - DRM_BUDDY_CLEAR_ALLOCATION), + GPU_BUDDY_CLEAR_ALLOCATION), "buddy_alloc hit an error size=%lu\n", 2 * ps); - drm_buddy_free_list(&mm, &allocated, DRM_BUDDY_CLEARED); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, SZ_4K << max_order, mm_size, + gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, SZ_4K << max_order, mm_size, ps, ps, &allocated, - DRM_BUDDY_RANGE_ALLOCATION), + GPU_BUDDY_RANGE_ALLOCATION), "buddy_alloc hit an error size=%lu\n", ps); - drm_buddy_free_list(&mm, &allocated, DRM_BUDDY_CLEARED); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); + gpu_buddy_fini(&mm); } -static void drm_test_buddy_alloc_contiguous(struct kunit *test) +static void gpu_test_buddy_alloc_contiguous(struct kunit *test) { const unsigned long ps = SZ_4K, mm_size = 16 * 3 * SZ_4K; unsigned long i, n_pages, total; - struct drm_buddy_block *block; - struct drm_buddy mm; + struct gpu_buddy_block *block; + struct gpu_buddy mm; LIST_HEAD(left); LIST_HEAD(middle); LIST_HEAD(right); LIST_HEAD(allocated); - KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, mm_size, ps)); + KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); /* * Idea is to fragment the address space by alternating block * allocations between three different lists; one for left, middle and * right. We can then free a list to simulate fragmentation. In - * particular we want to exercise the DRM_BUDDY_CONTIGUOUS_ALLOCATION, + * particular we want to exercise the GPU_BUDDY_CONTIGUOUS_ALLOCATION, * including the try_harder path. */ @@ -444,66 +444,66 @@ static void drm_test_buddy_alloc_contiguous(struct kunit *test) else list = &right; KUNIT_ASSERT_FALSE_MSG(test, - drm_buddy_alloc_blocks(&mm, 0, mm_size, + gpu_buddy_alloc_blocks(&mm, 0, mm_size, ps, ps, list, 0), "buddy_alloc hit an error size=%lu\n", ps); } while (++i < n_pages); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 3 * ps, ps, &allocated, - DRM_BUDDY_CONTIGUOUS_ALLOCATION), + GPU_BUDDY_CONTIGUOUS_ALLOCATION), "buddy_alloc didn't error size=%lu\n", 3 * ps); - drm_buddy_free_list(&mm, &middle, 0); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + gpu_buddy_free_list(&mm, &middle, 0); + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 3 * ps, ps, &allocated, - DRM_BUDDY_CONTIGUOUS_ALLOCATION), + GPU_BUDDY_CONTIGUOUS_ALLOCATION), "buddy_alloc didn't error size=%lu\n", 3 * ps); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 2 * ps, ps, &allocated, - DRM_BUDDY_CONTIGUOUS_ALLOCATION), + GPU_BUDDY_CONTIGUOUS_ALLOCATION), "buddy_alloc didn't error size=%lu\n", 2 * ps); - drm_buddy_free_list(&mm, &right, 0); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + gpu_buddy_free_list(&mm, &right, 0); + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 3 * ps, ps, &allocated, - DRM_BUDDY_CONTIGUOUS_ALLOCATION), + GPU_BUDDY_CONTIGUOUS_ALLOCATION), "buddy_alloc didn't error size=%lu\n", 3 * ps); /* * At this point we should have enough contiguous space for 2 blocks, * however they are never buddies (since we freed middle and right) so * will require the try_harder logic to find them. */ - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 2 * ps, ps, &allocated, - DRM_BUDDY_CONTIGUOUS_ALLOCATION), + GPU_BUDDY_CONTIGUOUS_ALLOCATION), "buddy_alloc hit an error size=%lu\n", 2 * ps); - drm_buddy_free_list(&mm, &left, 0); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, + gpu_buddy_free_list(&mm, &left, 0); + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, 3 * ps, ps, &allocated, - DRM_BUDDY_CONTIGUOUS_ALLOCATION), + GPU_BUDDY_CONTIGUOUS_ALLOCATION), "buddy_alloc hit an error size=%lu\n", 3 * ps); total = 0; list_for_each_entry(block, &allocated, link) - total += drm_buddy_block_size(&mm, block); + total += gpu_buddy_block_size(&mm, block); KUNIT_ASSERT_EQ(test, total, ps * 2 + ps * 3); - drm_buddy_free_list(&mm, &allocated, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &allocated, 0); + gpu_buddy_fini(&mm); } -static void drm_test_buddy_alloc_pathological(struct kunit *test) +static void gpu_test_buddy_alloc_pathological(struct kunit *test) { u64 mm_size, size, start = 0; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; const int max_order = 3; unsigned long flags = 0; int order, top; - struct drm_buddy mm; + struct gpu_buddy mm; LIST_HEAD(blocks); LIST_HEAD(holes); LIST_HEAD(tmp); @@ -516,7 +516,7 @@ static void drm_test_buddy_alloc_pathological(struct kunit *test) */ mm_size = SZ_4K << max_order; - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, SZ_4K), + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), "buddy_init failed\n"); KUNIT_EXPECT_EQ(test, mm.max_order, max_order); @@ -526,18 +526,18 @@ static void drm_test_buddy_alloc_pathological(struct kunit *test) block = list_first_entry_or_null(&blocks, typeof(*block), link); if (block) { list_del(&block->link); - drm_buddy_free_block(&mm, block); + gpu_buddy_free_block(&mm, block); } for (order = top; order--;) { size = get_size(order, mm.chunk_size); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc hit -ENOMEM with order=%d, top=%d\n", order, top); - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); list_move_tail(&block->link, &blocks); @@ -545,45 +545,45 @@ static void drm_test_buddy_alloc_pathological(struct kunit *test) /* There should be one final page for this sub-allocation */ size = get_size(0, mm.chunk_size); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc hit -ENOMEM for hole\n"); - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); list_move_tail(&block->link, &holes); size = get_size(top, mm.chunk_size); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!", top, max_order); } - drm_buddy_free_list(&mm, &holes, 0); + gpu_buddy_free_list(&mm, &holes, 0); /* Nothing larger than blocks of chunk_size now available */ for (order = 1; order <= max_order; order++) { size = get_size(order, mm.chunk_size); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc unexpectedly succeeded at order %d, it should be full!", order); } list_splice_tail(&holes, &blocks); - drm_buddy_free_list(&mm, &blocks, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &blocks, 0); + gpu_buddy_fini(&mm); } -static void drm_test_buddy_alloc_pessimistic(struct kunit *test) +static void gpu_test_buddy_alloc_pessimistic(struct kunit *test) { u64 mm_size, size, start = 0; - struct drm_buddy_block *block, *bn; + struct gpu_buddy_block *block, *bn; const unsigned int max_order = 16; unsigned long flags = 0; - struct drm_buddy mm; + struct gpu_buddy mm; unsigned int order; LIST_HEAD(blocks); LIST_HEAD(tmp); @@ -595,19 +595,19 @@ static void drm_test_buddy_alloc_pessimistic(struct kunit *test) */ mm_size = SZ_4K << max_order; - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, SZ_4K), + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), "buddy_init failed\n"); KUNIT_EXPECT_EQ(test, mm.max_order, max_order); for (order = 0; order < max_order; order++) { size = get_size(order, mm.chunk_size); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc hit -ENOMEM with order=%d\n", order); - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); list_move_tail(&block->link, &blocks); @@ -615,11 +615,11 @@ static void drm_test_buddy_alloc_pessimistic(struct kunit *test) /* And now the last remaining block available */ size = get_size(0, mm.chunk_size); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc hit -ENOMEM on final alloc\n"); - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); list_move_tail(&block->link, &blocks); @@ -627,58 +627,58 @@ static void drm_test_buddy_alloc_pessimistic(struct kunit *test) /* Should be completely full! */ for (order = max_order; order--;) { size = get_size(order, mm.chunk_size); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc unexpectedly succeeded, it should be full!"); } block = list_last_entry(&blocks, typeof(*block), link); list_del(&block->link); - drm_buddy_free_block(&mm, block); + gpu_buddy_free_block(&mm, block); /* As we free in increasing size, we make available larger blocks */ order = 1; list_for_each_entry_safe(block, bn, &blocks, link) { list_del(&block->link); - drm_buddy_free_block(&mm, block); + gpu_buddy_free_block(&mm, block); size = get_size(order, mm.chunk_size); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc hit -ENOMEM with order=%d\n", order); - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); list_del(&block->link); - drm_buddy_free_block(&mm, block); + gpu_buddy_free_block(&mm, block); order++; } /* To confirm, now the whole mm should be available */ size = get_size(max_order, mm.chunk_size); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc (realloc) hit -ENOMEM with order=%d\n", max_order); - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); list_del(&block->link); - drm_buddy_free_block(&mm, block); - drm_buddy_free_list(&mm, &blocks, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_block(&mm, block); + gpu_buddy_free_list(&mm, &blocks, 0); + gpu_buddy_fini(&mm); } -static void drm_test_buddy_alloc_optimistic(struct kunit *test) +static void gpu_test_buddy_alloc_optimistic(struct kunit *test) { u64 mm_size, size, start = 0; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; unsigned long flags = 0; const int max_order = 16; - struct drm_buddy mm; + struct gpu_buddy mm; LIST_HEAD(blocks); LIST_HEAD(tmp); int order; @@ -690,19 +690,19 @@ static void drm_test_buddy_alloc_optimistic(struct kunit *test) mm_size = SZ_4K * ((1 << (max_order + 1)) - 1); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, SZ_4K), + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, SZ_4K), "buddy_init failed\n"); KUNIT_EXPECT_EQ(test, mm.max_order, max_order); for (order = 0; order <= max_order; order++) { size = get_size(order, mm.chunk_size); - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc hit -ENOMEM with order=%d\n", order); - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + block = list_first_entry_or_null(&tmp, struct gpu_buddy_block, link); KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); list_move_tail(&block->link, &blocks); @@ -710,79 +710,79 @@ static void drm_test_buddy_alloc_optimistic(struct kunit *test) /* Should be completely full! */ size = get_size(0, mm.chunk_size); - KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + KUNIT_ASSERT_TRUE_MSG(test, gpu_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags), "buddy_alloc unexpectedly succeeded, it should be full!"); - drm_buddy_free_list(&mm, &blocks, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &blocks, 0); + gpu_buddy_fini(&mm); } -static void drm_test_buddy_alloc_limit(struct kunit *test) +static void gpu_test_buddy_alloc_limit(struct kunit *test) { u64 size = U64_MAX, start = 0; - struct drm_buddy_block *block; + struct gpu_buddy_block *block; unsigned long flags = 0; LIST_HEAD(allocated); - struct drm_buddy mm; + struct gpu_buddy mm; - KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, size, SZ_4K)); + KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, size, SZ_4K)); - KUNIT_EXPECT_EQ_MSG(test, mm.max_order, DRM_BUDDY_MAX_ORDER, + KUNIT_EXPECT_EQ_MSG(test, mm.max_order, GPU_BUDDY_MAX_ORDER, "mm.max_order(%d) != %d\n", mm.max_order, - DRM_BUDDY_MAX_ORDER); + GPU_BUDDY_MAX_ORDER); size = mm.chunk_size << mm.max_order; - KUNIT_EXPECT_FALSE(test, drm_buddy_alloc_blocks(&mm, start, size, size, + KUNIT_EXPECT_FALSE(test, gpu_buddy_alloc_blocks(&mm, start, size, size, mm.chunk_size, &allocated, flags)); - block = list_first_entry_or_null(&allocated, struct drm_buddy_block, link); + block = list_first_entry_or_null(&allocated, struct gpu_buddy_block, link); KUNIT_EXPECT_TRUE(test, block); - KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), mm.max_order, + KUNIT_EXPECT_EQ_MSG(test, gpu_buddy_block_order(block), mm.max_order, "block order(%d) != %d\n", - drm_buddy_block_order(block), mm.max_order); + gpu_buddy_block_order(block), mm.max_order); - KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_size(&mm, block), + KUNIT_EXPECT_EQ_MSG(test, gpu_buddy_block_size(&mm, block), BIT_ULL(mm.max_order) * mm.chunk_size, "block size(%llu) != %llu\n", - drm_buddy_block_size(&mm, block), + gpu_buddy_block_size(&mm, block), BIT_ULL(mm.max_order) * mm.chunk_size); - drm_buddy_free_list(&mm, &allocated, 0); - drm_buddy_fini(&mm); + gpu_buddy_free_list(&mm, &allocated, 0); + gpu_buddy_fini(&mm); } -static int drm_buddy_suite_init(struct kunit_suite *suite) +static int gpu_buddy_suite_init(struct kunit_suite *suite) { while (!random_seed) random_seed = get_random_u32(); - kunit_info(suite, "Testing DRM buddy manager, with random_seed=0x%x\n", + kunit_info(suite, "Testing GPU buddy manager, with random_seed=0x%x\n", random_seed); return 0; } -static struct kunit_case drm_buddy_tests[] = { - KUNIT_CASE(drm_test_buddy_alloc_limit), - KUNIT_CASE(drm_test_buddy_alloc_optimistic), - KUNIT_CASE(drm_test_buddy_alloc_pessimistic), - KUNIT_CASE(drm_test_buddy_alloc_pathological), - KUNIT_CASE(drm_test_buddy_alloc_contiguous), - KUNIT_CASE(drm_test_buddy_alloc_clear), - KUNIT_CASE(drm_test_buddy_alloc_range_bias), +static struct kunit_case gpu_buddy_tests[] = { + KUNIT_CASE(gpu_test_buddy_alloc_limit), + KUNIT_CASE(gpu_test_buddy_alloc_optimistic), + KUNIT_CASE(gpu_test_buddy_alloc_pessimistic), + KUNIT_CASE(gpu_test_buddy_alloc_pathological), + KUNIT_CASE(gpu_test_buddy_alloc_contiguous), + KUNIT_CASE(gpu_test_buddy_alloc_clear), + KUNIT_CASE(gpu_test_buddy_alloc_range_bias), {} }; -static struct kunit_suite drm_buddy_test_suite = { - .name = "drm_buddy", - .suite_init = drm_buddy_suite_init, - .test_cases = drm_buddy_tests, +static struct kunit_suite gpu_buddy_test_suite = { + .name = "gpu_buddy", + .suite_init = gpu_buddy_suite_init, + .test_cases = gpu_buddy_tests, }; -kunit_test_suite(drm_buddy_test_suite); +kunit_test_suite(gpu_buddy_test_suite); MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Kunit test for drm_buddy functions"); +MODULE_DESCRIPTION("Kunit test for gpu_buddy functions"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/lib/drm_random.c b/drivers/gpu/tests/gpu_random.c similarity index 59% rename from drivers/gpu/drm/lib/drm_random.c rename to drivers/gpu/tests/gpu_random.c index 0e9dba1ef4af5..6356372f7e52e 100644 --- a/drivers/gpu/drm/lib/drm_random.c +++ b/drivers/gpu/tests/gpu_random.c @@ -6,28 +6,28 @@ #include #include -#include "drm_random.h" +#include "gpu_random.h" -u32 drm_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state) +u32 gpu_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state) { return upper_32_bits((u64)prandom_u32_state(state) * ep_ro); } -EXPORT_SYMBOL(drm_prandom_u32_max_state); +EXPORT_SYMBOL(gpu_prandom_u32_max_state); -void drm_random_reorder(unsigned int *order, unsigned int count, +void gpu_random_reorder(unsigned int *order, unsigned int count, struct rnd_state *state) { unsigned int i, j; for (i = 0; i < count; ++i) { BUILD_BUG_ON(sizeof(unsigned int) > sizeof(u32)); - j = drm_prandom_u32_max_state(count, state); + j = gpu_prandom_u32_max_state(count, state); swap(order[i], order[j]); } } -EXPORT_SYMBOL(drm_random_reorder); +EXPORT_SYMBOL(gpu_random_reorder); -unsigned int *drm_random_order(unsigned int count, struct rnd_state *state) +unsigned int *gpu_random_order(unsigned int count, struct rnd_state *state) { unsigned int *order, i; @@ -38,7 +38,7 @@ unsigned int *drm_random_order(unsigned int count, struct rnd_state *state) for (i = 0; i < count; i++) order[i] = i; - drm_random_reorder(order, count, state); + gpu_random_reorder(order, count, state); return order; } -EXPORT_SYMBOL(drm_random_order); +EXPORT_SYMBOL(gpu_random_order); diff --git a/drivers/gpu/drm/lib/drm_random.h b/drivers/gpu/tests/gpu_random.h similarity index 53% rename from drivers/gpu/drm/lib/drm_random.h rename to drivers/gpu/tests/gpu_random.h index 9f827260a89db..b68cf34482644 100644 --- a/drivers/gpu/drm/lib/drm_random.h +++ b/drivers/gpu/tests/gpu_random.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __DRM_RANDOM_H__ -#define __DRM_RANDOM_H__ +#ifndef __GPU_RANDOM_H__ +#define __GPU_RANDOM_H__ /* This is a temporary home for a couple of utility functions that should * be transposed to lib/ at the earliest convenience. @@ -8,21 +8,21 @@ #include -#define DRM_RND_STATE_INITIALIZER(seed__) ({ \ +#define GPU_RND_STATE_INITIALIZER(seed__) ({ \ struct rnd_state state__; \ prandom_seed_state(&state__, (seed__)); \ state__; \ }) -#define DRM_RND_STATE(name__, seed__) \ - struct rnd_state name__ = DRM_RND_STATE_INITIALIZER(seed__) +#define GPU_RND_STATE(name__, seed__) \ + struct rnd_state name__ = GPU_RND_STATE_INITIALIZER(seed__) -unsigned int *drm_random_order(unsigned int count, +unsigned int *gpu_random_order(unsigned int count, struct rnd_state *state); -void drm_random_reorder(unsigned int *order, +void gpu_random_reorder(unsigned int *order, unsigned int count, struct rnd_state *state); -u32 drm_prandom_u32_max_state(u32 ep_ro, +u32 gpu_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state); -#endif /* !__DRM_RANDOM_H__ */ +#endif /* !__GPU_RANDOM_H__ */ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index d51777df12d16..0adb1e2fa5338 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -37,6 +37,7 @@ source "drivers/char/agp/Kconfig" source "drivers/gpu/vga/Kconfig" +source "drivers/gpu/Kconfig" source "drivers/gpu/host1x/Kconfig" source "drivers/gpu/ipu-v3/Kconfig" source "drivers/gpu/nova-core/Kconfig" diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h index c2e05a2812522..3054369bebffb 100644 --- a/include/drm/drm_buddy.h +++ b/include/drm/drm_buddy.h @@ -6,166 +6,13 @@ #ifndef __DRM_BUDDY_H__ #define __DRM_BUDDY_H__ -#include -#include -#include -#include -#include +#include -#include +struct drm_printer; -#define DRM_BUDDY_RANGE_ALLOCATION BIT(0) -#define DRM_BUDDY_TOPDOWN_ALLOCATION BIT(1) -#define DRM_BUDDY_CONTIGUOUS_ALLOCATION BIT(2) -#define DRM_BUDDY_CLEAR_ALLOCATION BIT(3) -#define DRM_BUDDY_CLEARED BIT(4) -#define DRM_BUDDY_TRIM_DISABLE BIT(5) - -struct drm_buddy_block { -#define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12) -#define DRM_BUDDY_HEADER_STATE GENMASK_ULL(11, 10) -#define DRM_BUDDY_ALLOCATED (1 << 10) -#define DRM_BUDDY_FREE (2 << 10) -#define DRM_BUDDY_SPLIT (3 << 10) -#define DRM_BUDDY_HEADER_CLEAR GENMASK_ULL(9, 9) -/* Free to be used, if needed in the future */ -#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6) -#define DRM_BUDDY_HEADER_ORDER GENMASK_ULL(5, 0) - u64 header; - - struct drm_buddy_block *left; - struct drm_buddy_block *right; - struct drm_buddy_block *parent; - - void *private; /* owned by creator */ - - /* - * While the block is allocated by the user through drm_buddy_alloc*, - * the user has ownership of the link, for example to maintain within - * a list, if so desired. As soon as the block is freed with - * drm_buddy_free* ownership is given back to the mm. - */ - union { - struct rb_node rb; - struct list_head link; - }; - - struct list_head tmp_link; -}; - -/* Order-zero must be at least SZ_4K */ -#define DRM_BUDDY_MAX_ORDER (63 - 12) - -/* - * Binary Buddy System. - * - * Locking should be handled by the user, a simple mutex around - * drm_buddy_alloc* and drm_buddy_free* should suffice. - */ -struct drm_buddy { - /* Maintain a free list for each order. */ - struct rb_root **free_trees; - - /* - * Maintain explicit binary tree(s) to track the allocation of the - * address space. This gives us a simple way of finding a buddy block - * and performing the potentially recursive merge step when freeing a - * block. Nodes are either allocated or free, in which case they will - * also exist on the respective free list. - */ - struct drm_buddy_block **roots; - - /* - * Anything from here is public, and remains static for the lifetime of - * the mm. Everything above is considered do-not-touch. - */ - unsigned int n_roots; - unsigned int max_order; - - /* Must be at least SZ_4K */ - u64 chunk_size; - u64 size; - u64 avail; - u64 clear_avail; -}; - -static inline u64 -drm_buddy_block_offset(const struct drm_buddy_block *block) -{ - return block->header & DRM_BUDDY_HEADER_OFFSET; -} - -static inline unsigned int -drm_buddy_block_order(struct drm_buddy_block *block) -{ - return block->header & DRM_BUDDY_HEADER_ORDER; -} - -static inline unsigned int -drm_buddy_block_state(struct drm_buddy_block *block) -{ - return block->header & DRM_BUDDY_HEADER_STATE; -} - -static inline bool -drm_buddy_block_is_allocated(struct drm_buddy_block *block) -{ - return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED; -} - -static inline bool -drm_buddy_block_is_clear(struct drm_buddy_block *block) -{ - return block->header & DRM_BUDDY_HEADER_CLEAR; -} - -static inline bool -drm_buddy_block_is_free(struct drm_buddy_block *block) -{ - return drm_buddy_block_state(block) == DRM_BUDDY_FREE; -} - -static inline bool -drm_buddy_block_is_split(struct drm_buddy_block *block) -{ - return drm_buddy_block_state(block) == DRM_BUDDY_SPLIT; -} - -static inline u64 -drm_buddy_block_size(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - return mm->chunk_size << drm_buddy_block_order(block); -} - -int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size); - -void drm_buddy_fini(struct drm_buddy *mm); - -struct drm_buddy_block * -drm_get_buddy(struct drm_buddy_block *block); - -int drm_buddy_alloc_blocks(struct drm_buddy *mm, - u64 start, u64 end, u64 size, - u64 min_page_size, - struct list_head *blocks, - unsigned long flags); - -int drm_buddy_block_trim(struct drm_buddy *mm, - u64 *start, - u64 new_size, - struct list_head *blocks); - -void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear); - -void drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block); - -void drm_buddy_free_list(struct drm_buddy *mm, - struct list_head *objects, - unsigned int flags); - -void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p); -void drm_buddy_block_print(struct drm_buddy *mm, - struct drm_buddy_block *block, +/* DRM-specific GPU Buddy Allocator print helpers */ +void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p); +void drm_buddy_block_print(struct gpu_buddy *mm, + struct gpu_buddy_block *block, struct drm_printer *p); #endif diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h new file mode 100644 index 0000000000000..07ac65db6d2e8 --- /dev/null +++ b/include/linux/gpu_buddy.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2021 Intel Corporation + */ + +#ifndef __GPU_BUDDY_H__ +#define __GPU_BUDDY_H__ + +#include +#include +#include +#include +#include + +#define GPU_BUDDY_RANGE_ALLOCATION BIT(0) +#define GPU_BUDDY_TOPDOWN_ALLOCATION BIT(1) +#define GPU_BUDDY_CONTIGUOUS_ALLOCATION BIT(2) +#define GPU_BUDDY_CLEAR_ALLOCATION BIT(3) +#define GPU_BUDDY_CLEARED BIT(4) +#define GPU_BUDDY_TRIM_DISABLE BIT(5) + +enum gpu_buddy_free_tree { + GPU_BUDDY_CLEAR_TREE = 0, + GPU_BUDDY_DIRTY_TREE, + GPU_BUDDY_MAX_FREE_TREES, +}; + +#define for_each_free_tree(tree) \ + for ((tree) = 0; (tree) < GPU_BUDDY_MAX_FREE_TREES; (tree)++) + +struct gpu_buddy_block { +#define GPU_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12) +#define GPU_BUDDY_HEADER_STATE GENMASK_ULL(11, 10) +#define GPU_BUDDY_ALLOCATED (1 << 10) +#define GPU_BUDDY_FREE (2 << 10) +#define GPU_BUDDY_SPLIT (3 << 10) +#define GPU_BUDDY_HEADER_CLEAR GENMASK_ULL(9, 9) +/* Free to be used, if needed in the future */ +#define GPU_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6) +#define GPU_BUDDY_HEADER_ORDER GENMASK_ULL(5, 0) + u64 header; + + struct gpu_buddy_block *left; + struct gpu_buddy_block *right; + struct gpu_buddy_block *parent; + + void *private; /* owned by creator */ + + /* + * While the block is allocated by the user through gpu_buddy_alloc*, + * the user has ownership of the link, for example to maintain within + * a list, if so desired. As soon as the block is freed with + * gpu_buddy_free* ownership is given back to the mm. + */ + union { + struct rb_node rb; + struct list_head link; + }; + + struct list_head tmp_link; +}; + +/* Order-zero must be at least SZ_4K */ +#define GPU_BUDDY_MAX_ORDER (63 - 12) + +/* + * Binary Buddy System. + * + * Locking should be handled by the user, a simple mutex around + * gpu_buddy_alloc* and gpu_buddy_free* should suffice. + */ +struct gpu_buddy { + /* Maintain a free list for each order. */ + struct rb_root **free_trees; + + /* + * Maintain explicit binary tree(s) to track the allocation of the + * address space. This gives us a simple way of finding a buddy block + * and performing the potentially recursive merge step when freeing a + * block. Nodes are either allocated or free, in which case they will + * also exist on the respective free list. + */ + struct gpu_buddy_block **roots; + + /* + * Anything from here is public, and remains static for the lifetime of + * the mm. Everything above is considered do-not-touch. + */ + unsigned int n_roots; + unsigned int max_order; + + /* Must be at least SZ_4K */ + u64 chunk_size; + u64 size; + u64 avail; + u64 clear_avail; +}; + +static inline u64 +gpu_buddy_block_offset(const struct gpu_buddy_block *block) +{ + return block->header & GPU_BUDDY_HEADER_OFFSET; +} + +static inline unsigned int +gpu_buddy_block_order(struct gpu_buddy_block *block) +{ + return block->header & GPU_BUDDY_HEADER_ORDER; +} + +static inline unsigned int +gpu_buddy_block_state(struct gpu_buddy_block *block) +{ + return block->header & GPU_BUDDY_HEADER_STATE; +} + +static inline bool +gpu_buddy_block_is_allocated(struct gpu_buddy_block *block) +{ + return gpu_buddy_block_state(block) == GPU_BUDDY_ALLOCATED; +} + +static inline bool +gpu_buddy_block_is_clear(struct gpu_buddy_block *block) +{ + return block->header & GPU_BUDDY_HEADER_CLEAR; +} + +static inline bool +gpu_buddy_block_is_free(struct gpu_buddy_block *block) +{ + return gpu_buddy_block_state(block) == GPU_BUDDY_FREE; +} + +static inline bool +gpu_buddy_block_is_split(struct gpu_buddy_block *block) +{ + return gpu_buddy_block_state(block) == GPU_BUDDY_SPLIT; +} + +static inline u64 +gpu_buddy_block_size(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + return mm->chunk_size << gpu_buddy_block_order(block); +} + +int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size); + +void gpu_buddy_fini(struct gpu_buddy *mm); + +struct gpu_buddy_block * +gpu_get_buddy(struct gpu_buddy_block *block); + +int gpu_buddy_alloc_blocks(struct gpu_buddy *mm, + u64 start, u64 end, u64 size, + u64 min_page_size, + struct list_head *blocks, + unsigned long flags); + +int gpu_buddy_block_trim(struct gpu_buddy *mm, + u64 *start, + u64 new_size, + struct list_head *blocks); + +void gpu_buddy_reset_clear(struct gpu_buddy *mm, bool is_clear); + +void gpu_buddy_free_block(struct gpu_buddy *mm, struct gpu_buddy_block *block); + +void gpu_buddy_free_list(struct gpu_buddy *mm, + struct list_head *objects, + unsigned int flags); + +void gpu_buddy_print(struct gpu_buddy *mm); +void gpu_buddy_block_print(struct gpu_buddy *mm, + struct gpu_buddy_block *block); +#endif From 9634fd144cdc115786ff92da45ae2108b674d5f7 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 6 Feb 2026 11:44:09 +1000 Subject: [PATCH 2849/3297] drm: drop lib from header search path. commit ee8bfb15d02dddb554ad4bdd6c44297f19556563 upstream. This was leftover from when I dropped it in 4a9671a03f2b ("gpu: Move DRM buddy allocator one level up (part one)") Signed-off-by: Dave Airlie Cc: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 82cd30cf6cb2d..1b3a7ab6df585 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -238,7 +238,7 @@ obj-$(CONFIG_DRM_POWERVR) += imagination/ # Ensure drm headers are self-contained and pass kernel-doc hdrtest-files := \ $(shell cd $(src) && find . -maxdepth 1 -name 'drm_*.h') \ - $(shell cd $(src) && find display lib -name '*.h') + $(shell cd $(src) && find display -name '*.h') always-$(CONFIG_DRM_HEADER_TEST) += \ $(patsubst %.h,%.hdrtest, $(hdrtest-files)) From 7185c5262435b93e5eeffa647008992256b9c51a Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Thu, 23 Jul 2026 10:19:30 -0400 Subject: [PATCH 2850/3297] gpu/buddy: bail out of try_harder when alignment cannot be honoured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 56bc6384314fb9ae98975fb2af8b143097ede3dc ] The try_harder contiguous fallback could return a range whose start offset did not match the caller's min_block_size. When a candidate's start is misaligned, realign it: free the misaligned run and reallocate exactly @size at the next lower min_block_size boundary. This keeps the returned size unchanged with no surplus to trim, and rejects the request only when no aligned candidate fits. v2: align misaligned candidates down to min_block_size instead of bailing out, for both the RHS and LHS paths (Matthew). Fixes: 0a1844bf0b53 ("drm/buddy: Improve contiguous memory allocation") Suggested-by: Christian König Cc: Matthew Auld Cc: Christian König Cc: Timur Kristóf Cc: stable@vger.kernel.org Reviewed-by: Matthew Auld Tested-by: John Olender Signed-off-by: Arunpravin Paneer Selvam Link: https://patch.msgid.link/20260709131050.1022759-1-Arunpravin.PaneerSelvam@amd.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/buddy.c | 63 +++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index 603c59a2013a0..bf5920bd22580 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -901,22 +901,30 @@ static int __gpu_buddy_alloc_range(struct gpu_buddy *mm, blocks, total_allocated_on_err); } +static int __alloc_contig_aligned_retry(struct gpu_buddy *mm, + u64 unaligned_offset, + u64 size, + u64 min_block_size, + struct list_head *blocks) +{ + u64 aligned_offset = round_down(unaligned_offset, min_block_size); + + return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks); +} + static int __alloc_contig_try_harder(struct gpu_buddy *mm, u64 size, u64 min_block_size, struct list_head *blocks) { - u64 rhs_offset, lhs_offset, lhs_size, filled; + u64 rhs_offset, lhs_offset, filled; struct gpu_buddy_block *block; unsigned int tree, order; - LIST_HEAD(blocks_lhs); - unsigned long pages; u64 modify_size; int err; modify_size = rounddown_pow_of_two(size); - pages = modify_size >> ilog2(mm->chunk_size); - order = fls(pages) - 1; + order = ilog2(modify_size) - ilog2(mm->chunk_size); if (order == 0) return -ENOSPC; @@ -932,31 +940,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm, while (iter) { block = rbtree_get_free_block(iter); - /* Allocate blocks traversing RHS */ rhs_offset = gpu_buddy_block_offset(block); + + /* Allocate blocks traversing RHS */ err = __gpu_buddy_alloc_range(mm, rhs_offset, size, &filled, blocks); - if (!err || err != -ENOSPC) + if (err && err != -ENOSPC) return err; + if (!err && IS_ALIGNED(rhs_offset, min_block_size)) + return 0; + if (!err) { + /* Allocate the unaligned RHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, rhs_offset, + size, + min_block_size, + blocks); + if (!err) + return 0; + if (err != -ENOSPC) { + gpu_buddy_free_list_internal(mm, blocks); + return err; + } + goto next; + } - lhs_size = max((size - filled), min_block_size); - if (!IS_ALIGNED(lhs_size, min_block_size)) - lhs_size = round_up(lhs_size, min_block_size); + if (size - filled > rhs_offset) + goto next; - /* Allocate blocks traversing LHS */ - lhs_offset = gpu_buddy_block_offset(block) - lhs_size; - err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size, - NULL, &blocks_lhs); - if (!err) { - list_splice(&blocks_lhs, blocks); + lhs_offset = rhs_offset - (size - filled); + + /* Allocate the unaligned LHS offset using round_down */ + gpu_buddy_free_list_internal(mm, blocks); + err = __alloc_contig_aligned_retry(mm, lhs_offset, size, + min_block_size, blocks); + if (!err) return 0; - } else if (err != -ENOSPC) { + if (err != -ENOSPC) { gpu_buddy_free_list_internal(mm, blocks); return err; } - /* Free blocks for the next iteration */ +next: gpu_buddy_free_list_internal(mm, blocks); - iter = rb_prev(iter); } } From 6876f767b04900c3c8552e64509c4c597f63f557 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 23 Jul 2026 13:03:25 -0400 Subject: [PATCH 2851/3297] pinctrl: renesas: rzg2l: Use -ENOTSUPP instead of -EOPNOTSUPP [ Upstream commit c1492da3939c89372929e062d731f328f7693f1e ] The pinctrl and GPIO core code make exceptions for the -ENOTSUPP error code. One such example is gpio_set_config_with_argument_optional(), which returns success when gpio_set_config_with_argument() returns -ENOTSUPP, but reports failure for all other error codes. Returning -EOPNOTSUPP from the pinctrl driver on the unsupported pinctrl operation may lead to boot failures when pinctrl drivers implements struct gpio_chip::set_config, the system uses GPIO hogs, and the struct gpio_chip::set_config implementation returns -EOPNOTSUPP for the unsupported operations. Return -ENOTSUPP for the unsupported pinctrl operation. Fixes: 560c633d378a ("pinctrl: renesas: rzg2l: Drop oen_read and oen_write callbacks") Fixes: c4c4637eb57f ("pinctrl: renesas: Add RZ/G2L pin and gpio controller driver") Cc: stable@vger.kernel.org Signed-off-by: Claudiu Beznea Reviewed-by: Bartosz Golaszewski Reviewed-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260515124008.2947838-2-claudiu.beznea@kernel.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index aaa783c79e6dd..e8cc83029db33 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -1097,7 +1097,7 @@ static int rzg2l_read_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin) int bit; if (!pctrl->data->pin_to_oen_bit) - return -EOPNOTSUPP; + return -ENOTSUPP; bit = pctrl->data->pin_to_oen_bit(pctrl, _pin); if (bit < 0) @@ -1115,7 +1115,7 @@ static int rzg2l_write_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin, u8 oe int bit; if (!pctrl->data->pin_to_oen_bit) - return -EOPNOTSUPP; + return -ENOTSUPP; bit = pctrl->data->pin_to_oen_bit(pctrl, _pin); if (bit < 0) @@ -1552,7 +1552,7 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, break; default: - return -EOPNOTSUPP; + return -ENOTSUPP; } } @@ -1634,7 +1634,7 @@ static int rzg2l_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, /* Check config matching between to pin */ if (i && prev_config != *config) - return -EOPNOTSUPP; + return -ENOTSUPP; prev_config = *config; } From 3120f21df3fb0460f61c9f2f43545403ec768cc5 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 23 Jul 2026 14:19:06 -0400 Subject: [PATCH 2852/3297] NFSD: pass nfsd_file to nfsd_iter_read() [ Upstream commit 803bc849f0039291f546ba0e2237faebeb5c073e ] Prepare for nfsd_iter_read() to use the DIO alignment stored in nfsd_file by passing the nfsd_file to nfsd_iter_read() rather than just the file which is associaed with the nfsd_file. This means nfsd4_encode_readv() now also needs the nfsd_file rather than the file. Instead of changing the file arg to be the nfsd_file, we discard the file arg as the nfsd_file (and indeed the file) is already available via the "read" argument. Signed-off-by: Mike Snitzer Reviewed-by: Jeff Layton Reviewed-by: NeilBrown Reviewed-by: Christoph Hellwig Signed-off-by: Chuck Lever Stable-dep-of: 18c1cc698861 ("SUNRPC: Return an error from xdr_buf_to_bvec() on overflow") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4xdr.c | 8 ++++---- fs/nfsd/vfs.c | 7 ++++--- fs/nfsd/vfs.h | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index a96029c967e6b..1e158881c1af9 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -4478,7 +4478,7 @@ static __be32 nfsd4_encode_splice_read( static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, struct nfsd4_read *read, - struct file *file, unsigned long maxcount) + unsigned long maxcount) { struct xdr_stream *xdr = resp->xdr; unsigned int base = xdr->buf->page_len & ~PAGE_MASK; @@ -4489,7 +4489,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, if (xdr_reserve_space_vec(xdr, maxcount) < 0) return nfserr_resource; - nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file, + nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, read->rd_nf, read->rd_offset, &maxcount, base, &read->rd_eof); read->rd_length = maxcount; @@ -4536,7 +4536,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, if (file->f_op->splice_read && splice_ok) nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount); else - nfserr = nfsd4_encode_readv(resp, read, file, maxcount); + nfserr = nfsd4_encode_readv(resp, read, maxcount); if (nfserr) { xdr_truncate_encode(xdr, eof_offset); return nfserr; @@ -5432,7 +5432,7 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, if (file->f_op->splice_read && splice_ok) nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount); else - nfserr = nfsd4_encode_readv(resp, read, file, maxcount); + nfserr = nfsd4_encode_readv(resp, read, maxcount); if (nfserr) return nfserr; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 2033037238780..4874446c2468d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1078,7 +1078,7 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, * nfsd_iter_read - Perform a VFS read using an iterator * @rqstp: RPC transaction context * @fhp: file handle of file to be read - * @file: opened struct file of file to be read + * @nf: opened struct nfsd_file of file to be read * @offset: starting byte offset * @count: IN: requested number of bytes; OUT: number of bytes read * @base: offset in first page of read buffer @@ -1091,9 +1091,10 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, * returned. */ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct file *file, loff_t offset, unsigned long *count, + struct nfsd_file *nf, loff_t offset, unsigned long *count, unsigned int base, u32 *eof) { + struct file *file = nf->nf_file; unsigned long v, total; struct iov_iter iter; struct kiocb kiocb; @@ -1346,7 +1347,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, if (file->f_op->splice_read && nfsd_read_splice_ok(rqstp)) err = nfsd_splice_read(rqstp, fhp, file, offset, count, eof); else - err = nfsd_iter_read(rqstp, fhp, file, offset, count, 0, eof); + err = nfsd_iter_read(rqstp, fhp, nf, offset, count, 0, eof); nfsd_file_put(nf); trace_nfsd_read_done(rqstp, fhp, offset, *count); diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 9652c1de53ec1..1dd3ae3ceb3a8 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -122,7 +122,7 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long *count, u32 *eof); __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct file *file, loff_t offset, + struct nfsd_file *nf, loff_t offset, unsigned long *count, unsigned int base, u32 *eof); bool nfsd_read_splice_ok(struct svc_rqst *rqstp); From a112b91dd63491ceb2ca0e7af758eb3055cebc96 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 23 Jul 2026 14:19:07 -0400 Subject: [PATCH 2853/3297] sunrpc: allocate a separate bvec array for socket sends [ Upstream commit 6b3b697d65d46a0f640216a3f6c72856c159c567 ] svc_tcp_sendmsg() calls xdr_buf_to_bvec() with the second slot of rq_bvec as the start, but doesn't reduce the array length by one, which could lead to an array overrun. Also, rq_bvec is always rq_maxpages in length, which can be too short in some cases, since the TCP record marker consumes a slot. Fix both problems by adding a separate bvec array to the svc_sock that is specifically for sending. For TCP, make this array one slot longer than rq_maxpages, to account for the record marker. For UDP, only allocate as large an array as we need since it's limited to 64k of payload. Signed-off-by: Jeff Layton Reviewed-by: NeilBrown Signed-off-by: Chuck Lever Stable-dep-of: 18c1cc698861 ("SUNRPC: Return an error from xdr_buf_to_bvec() on overflow") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/sunrpc/svcsock.h | 3 ++ net/sunrpc/svcsock.c | 55 +++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 963bbe251e521..de37069aba908 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -26,6 +26,9 @@ struct svc_sock { void (*sk_odata)(struct sock *); void (*sk_owspace)(struct sock *); + /* For sends (protected by xpt_mutex) */ + struct bio_vec *sk_bvec; + /* private TCP part */ /* On-the-wire fragment header: */ __be32 sk_marker; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 9c14558bc552b..f4e8e37e1048b 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -68,6 +68,17 @@ #define RPCDBG_FACILITY RPCDBG_SVCXPRT +/* + * For UDP: + * 1 for header page + * enough pages for RPCSVC_MAXPAYLOAD_UDP + * 1 in case payload is not aligned + * 1 for tail page + */ +enum { + SUNRPC_MAX_UDP_SENDPAGES = 1 + RPCSVC_MAXPAYLOAD_UDP / PAGE_SIZE + 1 + 1 +}; + /* To-do: to avoid tying up an nfsd thread while waiting for a * handshake request, the request could instead be deferred. */ @@ -750,14 +761,14 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) if (svc_xprt_is_dead(xprt)) goto out_notconn; - count = xdr_buf_to_bvec(rqstp->rq_bvec, rqstp->rq_maxpages, xdr); + count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr); - iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, rqstp->rq_bvec, + iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec, count, rqstp->rq_res.len); err = sock_sendmsg(svsk->sk_sock, &msg); if (err == -ECONNREFUSED) { /* ICMP error on earlier request. */ - iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, rqstp->rq_bvec, + iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec, count, rqstp->rq_res.len); err = sock_sendmsg(svsk->sk_sock, &msg); } @@ -1245,19 +1256,19 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp, int ret; /* The stream record marker is copied into a temporary page - * fragment buffer so that it can be included in rq_bvec. + * fragment buffer so that it can be included in sk_bvec. */ buf = page_frag_alloc(&svsk->sk_frag_cache, sizeof(marker), GFP_KERNEL); if (!buf) return -ENOMEM; memcpy(buf, &marker, sizeof(marker)); - bvec_set_virt(rqstp->rq_bvec, buf, sizeof(marker)); + bvec_set_virt(svsk->sk_bvec, buf, sizeof(marker)); - count = xdr_buf_to_bvec(rqstp->rq_bvec + 1, rqstp->rq_maxpages, + count = xdr_buf_to_bvec(svsk->sk_bvec + 1, rqstp->rq_maxpages, &rqstp->rq_res); - iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, rqstp->rq_bvec, + iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec, 1 + count, sizeof(marker) + rqstp->rq_res.len); ret = sock_sendmsg(svsk->sk_sock, &msg); page_frag_free(buf); @@ -1402,6 +1413,20 @@ void svc_sock_update_bufs(struct svc_serv *serv) spin_unlock_bh(&serv->sv_lock); } +static int svc_sock_sendpages(struct svc_serv *serv, struct socket *sock, int flags) +{ + switch (sock->type) { + case SOCK_STREAM: + /* +1 for TCP record marker */ + if (flags & SVC_SOCK_TEMPORARY) + return svc_serv_maxpages(serv) + 1; + return 0; + case SOCK_DGRAM: + return SUNRPC_MAX_UDP_SENDPAGES; + } + return -EINVAL; +} + /* * Initialize socket for RPC use and create svc_sock struct */ @@ -1412,12 +1437,26 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, struct svc_sock *svsk; struct sock *inet; int pmap_register = !(flags & SVC_SOCK_ANONYMOUS); + int sendpages; unsigned long pages; + sendpages = svc_sock_sendpages(serv, sock, flags); + if (sendpages < 0) + return ERR_PTR(sendpages); + pages = svc_serv_maxpages(serv); svsk = kzalloc(struct_size(svsk, sk_pages, pages), GFP_KERNEL); if (!svsk) return ERR_PTR(-ENOMEM); + + if (sendpages) { + svsk->sk_bvec = kcalloc(sendpages, sizeof(*svsk->sk_bvec), GFP_KERNEL); + if (!svsk->sk_bvec) { + kfree(svsk); + return ERR_PTR(-ENOMEM); + } + } + svsk->sk_maxpages = pages; inet = sock->sk; @@ -1429,6 +1468,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, inet->sk_protocol, ntohs(inet_sk(inet)->inet_sport)); if (err < 0) { + kfree(svsk->sk_bvec); kfree(svsk); return ERR_PTR(err); } @@ -1646,5 +1686,6 @@ static void svc_sock_free(struct svc_xprt *xprt) sock_release(sock); page_frag_cache_drain(&svsk->sk_frag_cache); + kfree(svsk->sk_bvec); kfree(svsk); } From b50b2cb87e7ac723ba01793d7ce1e64594fc5403 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 23 Jul 2026 14:19:08 -0400 Subject: [PATCH 2854/3297] SUNRPC: Add helpers to convert xdr_buf byte ranges to scatterlists [ Upstream commit e9be933959b581effd426f93b86654f5fbf0c574 ] The crypto/krb5 library accepts data in scatterlist form, but the GSS-API layer presents RPC payloads as struct xdr_buf. Bridge that gap with a pair of helper functions: xdr_buf_to_sg() - populate a caller-supplied scatterlist array from a byte range xdr_buf_to_sg_alloc() - populate a caller-supplied inline scatterlist, chaining to a heap- allocated overflow for large payloads The inline array (typically stack-allocated at eight entries) covers the common case of small RPCs with no heap allocation on the encrypt/decrypt path. Only buffers spanning many pages incur a kmalloc for the chained extension. The segment-walking logic follows the same head, page array, tail traversal as xdr_process_buf(), but populates a scatterlist directly rather than invoking a per-segment callback. sg_next() traversal makes the walker safe for chained scatterlists. Once subsequent patches reroute all per-message crypto operations through crypto/krb5, xdr_process_buf() loses its last callers and is removed. Assisted-by: Claude:claude-opus-4-6 Reviewed-by: Jeff Layton Acked-by: Anna Schumaker Signed-off-by: Chuck Lever Stable-dep-of: 18c1cc698861 ("SUNRPC: Return an error from xdr_buf_to_bvec() on overflow") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/sunrpc/xdr.h | 15 +++ net/sunrpc/xdr.c | 199 +++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 152597750f55b..4b4e767e2ab5b 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -140,6 +140,21 @@ int xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp); void xdr_free_bvec(struct xdr_buf *buf); unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, const struct xdr_buf *xdr); +int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset, + unsigned int len, struct scatterlist *sg, unsigned int nsg); +int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset, + unsigned int len, struct scatterlist *sg_head, + unsigned int sg_head_nents, + struct scatterlist **sg_overflow, gfp_t gfp); + +/* + * Inline scatterlist entries for xdr_buf_to_sg_alloc(). Sized to cover the + * head kvec, tail kvec, and a few page fragments without any heap allocation. + */ +enum { + XDR_BUF_TO_SG_NENTS = 8, +}; + static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int len) { diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index fe17a6e8372b4..82d3e8f65e486 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -191,6 +191,205 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, } EXPORT_SYMBOL_GPL(xdr_buf_to_bvec); +/** + * xdr_buf_to_sg - Populate a scatterlist from an xdr_buf range + * @buf: xdr_buf to map + * @offset: starting byte offset within @buf + * @len: number of bytes to cover + * @sg: scatterlist array initialized with sg_init_table() + * @nsg: number of entries available in @sg + * + * @sg is traversed with sg_next(), so callers may pass a list + * assembled with sg_chain(). + * + * Return: on success, the number of scatterlist entries used; the + * last used entry is marked with sg_mark_end(). On failure, a + * negative errno. + */ +int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset, + unsigned int len, struct scatterlist *sg, unsigned int nsg) +{ + unsigned int page_len, thislen, page_offset; + struct scatterlist *cur = sg, *prev = NULL; + int nents = 0; + int i; + + if (len == 0) + return 0; + + if (offset >= buf->head[0].iov_len) { + offset -= buf->head[0].iov_len; + } else { + thislen = min_t(unsigned int, + buf->head[0].iov_len - offset, len); + if (nents >= nsg) + return -ENOSPC; + sg_set_buf(cur, buf->head[0].iov_base + offset, + thislen); + prev = cur; + cur = sg_next(cur); + nents++; + len -= thislen; + offset = 0; + } + if (len == 0) + goto done; + + if (offset >= buf->page_len) { + offset -= buf->page_len; + } else { + page_len = min(buf->page_len - offset, len); + len -= page_len; + page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); + i = (offset + buf->page_base) >> PAGE_SHIFT; + thislen = PAGE_SIZE - page_offset; + do { + if (thislen > page_len) + thislen = page_len; + if (nents >= nsg) + return -ENOSPC; + sg_set_page(cur, buf->pages[i], + thislen, page_offset); + prev = cur; + cur = sg_next(cur); + nents++; + page_len -= thislen; + i++; + page_offset = 0; + thislen = PAGE_SIZE; + } while (page_len != 0); + offset = 0; + } + if (len == 0) + goto done; + + if (offset < buf->tail[0].iov_len) { + thislen = min_t(unsigned int, + buf->tail[0].iov_len - offset, len); + if (nents >= nsg) + return -ENOSPC; + sg_set_buf(cur, buf->tail[0].iov_base + offset, + thislen); + prev = cur; + nents++; + len -= thislen; + } + if (len != 0) + return -EINVAL; + +done: + if (prev) + sg_mark_end(prev); + return nents; +} +EXPORT_SYMBOL_GPL(xdr_buf_to_sg); + +/* + * Count the scatterlist entries needed to cover [offset, offset + len) + * within @buf. Mirrors the walk in xdr_buf_to_sg() so the caller can + * size an allocation that matches the requested sub-range rather than + * the full xdr_buf. + */ +static unsigned int xdr_buf_sg_nents(const struct xdr_buf *buf, + unsigned int offset, unsigned int len) +{ + unsigned int nsg = 0, thislen, page_offset; + + if (len == 0) + return 0; + + if (offset < buf->head[0].iov_len) { + thislen = min_t(unsigned int, + buf->head[0].iov_len - offset, len); + nsg++; + len -= thislen; + offset = 0; + } else { + offset -= buf->head[0].iov_len; + } + if (len == 0) + return nsg; + + if (offset < buf->page_len) { + thislen = min(buf->page_len - offset, len); + page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1); + nsg += DIV_ROUND_UP(page_offset + thislen, PAGE_SIZE); + len -= thislen; + offset = 0; + } else { + offset -= buf->page_len; + } + if (len == 0) + return nsg; + + if (offset < buf->tail[0].iov_len) + nsg++; + return nsg; +} + +/** + * xdr_buf_to_sg_alloc - Populate a scatterlist for an xdr_buf range + * @buf: xdr_buf to map + * @offset: starting byte offset within @buf + * @len: number of bytes to cover + * @sg_head: caller-provided scatterlist array (typically stack-allocated) + * @sg_head_nents: number of entries in @sg_head + * @sg_overflow: OUT: chained extension, or NULL when @sg_head sufficed + * @gfp: memory allocation flags for overflow + * + * Populates @sg_head directly when the xdr_buf fits. When more + * entries are needed, an overflow scatterlist is allocated and + * chained from @sg_head so that the result is traversable with + * sg_next(). + * + * Return: on success, the number of populated scatterlist entries + * (counting only data entries, not chain entries). @sg_head is + * the head of the resulting list. Caller must kfree @sg_overflow + * when done. On failure, a negative errno. + */ +int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset, + unsigned int len, struct scatterlist *sg_head, + unsigned int sg_head_nents, + struct scatterlist **sg_overflow, gfp_t gfp) +{ + unsigned int nsg; + int ret; + + *sg_overflow = NULL; + if (len == 0) + return 0; + + nsg = xdr_buf_sg_nents(buf, offset, len); + if (nsg == 0) + return -EINVAL; + + if (nsg <= sg_head_nents) { + sg_init_table(sg_head, nsg); + } else { + /* +1 replaces the slot sg_chain() consumes as the link. */ + unsigned int overflow_nents = nsg - sg_head_nents + 1; + struct scatterlist *overflow; + + overflow = kmalloc_array(overflow_nents, sizeof(*overflow), + gfp); + if (!overflow) + return -ENOMEM; + + sg_init_table(sg_head, sg_head_nents); + sg_init_table(overflow, overflow_nents); + sg_chain(sg_head, sg_head_nents, overflow); + *sg_overflow = overflow; + } + + ret = xdr_buf_to_sg(buf, offset, len, sg_head, nsg); + if (ret < 0) { + kfree(*sg_overflow); + *sg_overflow = NULL; + } + return ret; +} +EXPORT_SYMBOL_GPL(xdr_buf_to_sg_alloc); + /** * xdr_inline_pages - Prepare receive buffer for a large reply * @xdr: xdr_buf into which reply will be placed From 5d964cb2b7bc852c986d7ad669e86c20ad635d62 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 23 Jul 2026 14:19:10 -0400 Subject: [PATCH 2855/3297] SUNRPC: Return an error from xdr_buf_to_bvec() on overflow [ Upstream commit 18c1cc69886192e33536498289d26dba6894e3d5 ] xdr_buf_to_bvec() returns a slot count even when the caller's bvec budget is exhausted partway through the xdr_buf. Callers feed that count into iov_iter_bvec() and continue as if the conversion had succeeded, silently sending or writing fewer bytes than the data length declares. For an NFS WRITE the server reports the truncated transfer to the client as full success. The overflow represents an internal invariant violation: a higher layer reserved a bvec budget too small for the xdr_buf it then asked the encoder to convert. That is a server-side fault, not a media I/O failure and not a malformed client argument. Change xdr_buf_to_bvec() to return a signed int and have the overflow label return -ESERVERFAULT. Update the three callers to detect the negative return and fail the request: nfsd_vfs_write() folds the error into host_err, which nfserrno() translates to nfserr_serverfault for the WRITE reply; svc_udp_sendto() and svc_tcp_sendmsg() propagate the error out of the send path. Reported-by: Chris Mason Fixes: 2eb2b9358181 ("SUNRPC: Convert svc_tcp_sendmsg to use bio_vecs directly") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/vfs.c | 8 +++++++- include/linux/sunrpc/xdr.h | 4 ++-- net/sunrpc/svcsock.c | 14 ++++++++++++-- net/sunrpc/xdr.c | 11 ++++++----- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 4874446c2468d..ba882a4ea9cb9 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1203,7 +1203,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long exp_op_flags = 0; unsigned int pflags = current->flags; bool restore_flags = false; - unsigned int nvecs; + int nvecs; trace_nfsd_write_opened(rqstp, fhp, offset, *cnt); @@ -1243,7 +1243,13 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, } nvecs = xdr_buf_to_bvec(rqstp->rq_bvec, rqstp->rq_maxpages, payload); + if (nvecs < 0) { + host_err = nvecs; + goto out_nfserr; + } + iov_iter_bvec(&iter, ITER_SOURCE, rqstp->rq_bvec, nvecs, *cnt); + since = READ_ONCE(file->f_wb_err); if (verf) nfsd_copy_write_verifier(verf, nn); diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 4b4e767e2ab5b..656e0ad323af1 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -138,8 +138,8 @@ void xdr_terminate_string(const struct xdr_buf *, const u32); size_t xdr_buf_pagecount(const struct xdr_buf *buf); int xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp); void xdr_free_bvec(struct xdr_buf *buf); -unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, - const struct xdr_buf *xdr); +int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, + const struct xdr_buf *xdr); int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset, unsigned int len, struct scatterlist *sg, unsigned int nsg); int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset, diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index f4e8e37e1048b..0ed8d56374714 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -748,7 +748,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) .msg_flags = MSG_SPLICE_PAGES, .msg_controllen = sizeof(buffer), }; - unsigned int count; + int count; int err; svc_udp_release_ctxt(xprt, rqstp->rq_xprt_ctxt); @@ -762,6 +762,10 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) goto out_notconn; count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr); + if (count < 0) { + err = count; + goto out_trace; + } iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec, count, rqstp->rq_res.len); @@ -773,6 +777,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp) err = sock_sendmsg(svsk->sk_sock, &msg); } +out_trace: trace_svcsock_udp_send(xprt, err); mutex_unlock(&xprt->xpt_mutex); @@ -1251,7 +1256,7 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp, struct msghdr msg = { .msg_flags = MSG_SPLICE_PAGES, }; - unsigned int count; + int count; void *buf; int ret; @@ -1267,10 +1272,15 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp, count = xdr_buf_to_bvec(svsk->sk_bvec + 1, rqstp->rq_maxpages, &rqstp->rq_res); + if (count < 0) { + ret = count; + goto out; + } iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec, 1 + count, sizeof(marker) + rqstp->rq_res.len); ret = sock_sendmsg(svsk->sk_sock, &msg); +out: page_frag_free(buf); return ret; } diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 82d3e8f65e486..fb4dab2070007 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -139,13 +139,14 @@ xdr_free_bvec(struct xdr_buf *buf) /** * xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array * @bvec: bio_vec array to populate - * @bvec_size: element count of @bio_vec + * @bvec_size: element count of @bvec * @xdr: xdr_buf to be copied * - * Returns the number of entries consumed in @bvec. + * Returns the number of entries consumed in @bvec on success, or + * -ESERVERFAULT when @xdr does not fit within @bvec_size entries. */ -unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, - const struct xdr_buf *xdr) +int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, + const struct xdr_buf *xdr) { const struct kvec *head = xdr->head; const struct kvec *tail = xdr->tail; @@ -187,7 +188,7 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size, bvec_overflow: pr_warn_once("%s: bio_vec array overflow\n", __func__); - return count; + return -ESERVERFAULT; } EXPORT_SYMBOL_GPL(xdr_buf_to_bvec); From a64661dccb2eb349cc4cc70930e560254b2dfa5d Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Thu, 23 Jul 2026 14:28:03 -0400 Subject: [PATCH 2856/3297] cxl/pci: Remove unnecessary CXL Endpoint handling helper functions [ Upstream commit ca3d1a53e62093d17436abd447463da9c0f4e56b ] The CXL driver's cxl_handle_endpoint_cor_ras()/cxl_handle_endpoint_ras() are unnecessary helper functions used only for Endpoints. Remove these functions as they are not common for all CXL devices and do not provide value for EP handling. Rename __cxl_handle_ras to cxl_handle_ras() and __cxl_handle_cor_ras() to cxl_handle_cor_ras(). Signed-off-by: Terry Bowman Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Jonathan Cameron Reviewed-by: Dave Jiang Tested-by: Joshua Hahn Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260114182055.46029-5-terry.bowman@amd.com Signed-off-by: Dan Williams Signed-off-by: Dave Jiang Stable-dep-of: c268f949e219 ("cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cxl/core/pci.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 18825e1505d6a..078e9e5651e1b 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -711,8 +711,8 @@ void read_cdat_data(struct cxl_port *port) } EXPORT_SYMBOL_NS_GPL(read_cdat_data, "CXL"); -static void __cxl_handle_cor_ras(struct cxl_dev_state *cxlds, - void __iomem *ras_base) +static void cxl_handle_cor_ras(struct cxl_dev_state *cxlds, + void __iomem *ras_base) { void __iomem *addr; u32 status; @@ -728,11 +728,6 @@ static void __cxl_handle_cor_ras(struct cxl_dev_state *cxlds, } } -static void cxl_handle_endpoint_cor_ras(struct cxl_dev_state *cxlds) -{ - return __cxl_handle_cor_ras(cxlds, cxlds->regs.ras); -} - /* CXL spec rev3.0 8.2.4.16.1 */ static void header_log_copy(void __iomem *ras_base, u32 *log) { @@ -754,8 +749,8 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) * Log the state of the RAS status registers and prepare them to log the * next error status. Return 1 if reset needed. */ -static bool __cxl_handle_ras(struct cxl_dev_state *cxlds, - void __iomem *ras_base) +static bool cxl_handle_ras(struct cxl_dev_state *cxlds, + void __iomem *ras_base) { u32 hl[CXL_HEADERLOG_SIZE_U32]; void __iomem *addr; @@ -788,11 +783,6 @@ static bool __cxl_handle_ras(struct cxl_dev_state *cxlds, return true; } -static bool cxl_handle_endpoint_ras(struct cxl_dev_state *cxlds) -{ - return __cxl_handle_ras(cxlds, cxlds->regs.ras); -} - #ifdef CONFIG_PCIEAER_CXL static void cxl_dport_map_rch_aer(struct cxl_dport *dport) @@ -871,13 +861,13 @@ EXPORT_SYMBOL_NS_GPL(cxl_dport_init_ras_reporting, "CXL"); static void cxl_handle_rdport_cor_ras(struct cxl_dev_state *cxlds, struct cxl_dport *dport) { - return __cxl_handle_cor_ras(cxlds, dport->regs.ras); + return cxl_handle_cor_ras(cxlds, dport->regs.ras); } static bool cxl_handle_rdport_ras(struct cxl_dev_state *cxlds, struct cxl_dport *dport) { - return __cxl_handle_ras(cxlds, dport->regs.ras); + return cxl_handle_ras(cxlds, dport->regs.ras); } /* @@ -974,7 +964,7 @@ void cxl_cor_error_detected(struct pci_dev *pdev) if (cxlds->rcd) cxl_handle_rdport_errors(cxlds); - cxl_handle_endpoint_cor_ras(cxlds); + cxl_handle_cor_ras(cxlds, cxlds->regs.ras); } } EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, "CXL"); @@ -1003,7 +993,7 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, * chance the situation is recoverable dump the status of the RAS * capability registers and bounce the active state of the memdev. */ - ue = cxl_handle_endpoint_ras(cxlds); + ue = cxl_handle_ras(cxlds, cxlds->regs.ras); } From eeab775069920a8e0d9d8b28b65ef7b772ccdef0 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Thu, 23 Jul 2026 14:28:04 -0400 Subject: [PATCH 2857/3297] cxl/pci: Remove unnecessary CXL RCH handling helper functions [ Upstream commit eb78ef4d6f0e51243c1ee117f801dbc503e886ab ] cxl_handle_rdport_cor_ras() and cxl_handle_rdport_ras() are specific to Restricted CXL Host (RCH) handling. Improve readability and maintainability by replacing these and instead using the common cxl_handle_cor_ras() and cxl_handle_ras() functions. Signed-off-by: Terry Bowman Reviewed-by: Alejandro Lucero Reviewed-by: Dave Jiang Reviewed-by: Jonathan Cameron Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260114182055.46029-6-terry.bowman@amd.com Signed-off-by: Dan Williams Signed-off-by: Dave Jiang Stable-dep-of: c268f949e219 ("cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cxl/core/pci.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 078e9e5651e1b..3fb9462954da2 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -858,18 +858,6 @@ void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host) } EXPORT_SYMBOL_NS_GPL(cxl_dport_init_ras_reporting, "CXL"); -static void cxl_handle_rdport_cor_ras(struct cxl_dev_state *cxlds, - struct cxl_dport *dport) -{ - return cxl_handle_cor_ras(cxlds, dport->regs.ras); -} - -static bool cxl_handle_rdport_ras(struct cxl_dev_state *cxlds, - struct cxl_dport *dport) -{ - return cxl_handle_ras(cxlds, dport->regs.ras); -} - /* * Copy the AER capability registers using 32 bit read accesses. * This is necessary because RCRB AER capability is MMIO mapped. Clear the @@ -939,9 +927,9 @@ static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) pci_print_aer(pdev, severity, &aer_regs); if (severity == AER_CORRECTABLE) - cxl_handle_rdport_cor_ras(cxlds, dport); + cxl_handle_cor_ras(cxlds, dport->regs.ras); else - cxl_handle_rdport_ras(cxlds, dport); + cxl_handle_ras(cxlds, dport->regs.ras); } #else From 89b2ae039d1885781610a814df18c0264c339bbb Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 23 Jul 2026 14:28:05 -0400 Subject: [PATCH 2858/3297] cxl/pci: Remove CXL VH handling in CONFIG_PCIEAER_CXL conditional blocks from core/pci.c [ Upstream commit 7ff8b1d60881c5f97b5ae426e14d2822917d3b69 ] Create new config CONFIG_CXL_RAS and put all CXL RAS items behind the config. The config will depend on CPER and PCIE AER to build. Move the related VH RAS code from core/pci.c to core/ras.c. Restricted CXL host (RCH) RAS functions will be moved in a future patch. Cc: Robert Richter Reviewed-by: Joshua Hahn Reviewed-by: Jonathan Cameron Signed-off-by: Dave Jiang Reviewed-by: Alison Schofield Co-developed-by: Terry Bowman Signed-off-by: Terry Bowman Reviewed-by: Dan Williams Link: https://patch.msgid.link/20260114182055.46029-8-terry.bowman@amd.com Signed-off-by: Dan Williams Signed-off-by: Dave Jiang Stable-dep-of: c268f949e219 ("cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cxl/Kconfig | 4 + drivers/cxl/core/Makefile | 2 +- drivers/cxl/core/core.h | 31 +++++++ drivers/cxl/core/pci.c | 189 +------------------------------------- drivers/cxl/core/ras.c | 176 +++++++++++++++++++++++++++++++++++ drivers/cxl/cxl.h | 8 -- drivers/cxl/cxlpci.h | 16 ++++ tools/testing/cxl/Kbuild | 2 +- 8 files changed, 233 insertions(+), 195 deletions(-) diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index 599e126a18eb7..4bdec46b2a6bb 100644 --- a/drivers/cxl/Kconfig +++ b/drivers/cxl/Kconfig @@ -234,4 +234,8 @@ config CXL_MCE def_bool y depends on X86_MCE && MEMORY_FAILURE +config CXL_RAS + def_bool y + depends on ACPI_APEI_GHES && PCIEAER && CXL_PCI + endif diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index 5ad8fef210b5c..b2930cc54f8ba 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -14,9 +14,9 @@ cxl_core-y += pci.o cxl_core-y += hdm.o cxl_core-y += pmu.o cxl_core-y += cdat.o -cxl_core-y += ras.o cxl_core-$(CONFIG_TRACING) += trace.o cxl_core-$(CONFIG_CXL_REGION) += region.o cxl_core-$(CONFIG_CXL_MCE) += mce.o cxl_core-$(CONFIG_CXL_FEATURES) += features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o +cxl_core-$(CONFIG_CXL_RAS) += ras.o diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 1fb66132b7777..bc818de87cccc 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -144,8 +144,39 @@ int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c); int cxl_port_get_switch_dport_bandwidth(struct cxl_port *port, struct access_coordinate *c); +#ifdef CONFIG_CXL_RAS int cxl_ras_init(void); void cxl_ras_exit(void); +bool cxl_handle_ras(struct cxl_dev_state *cxlds, void __iomem *ras_base); +void cxl_handle_cor_ras(struct cxl_dev_state *cxlds, void __iomem *ras_base); +#else +static inline int cxl_ras_init(void) +{ + return 0; +} + +static inline void cxl_ras_exit(void) +{ +} + +static inline bool cxl_handle_ras(struct cxl_dev_state *cxlds, void __iomem *ras_base) +{ + return false; +} +static inline void cxl_handle_cor_ras(struct cxl_dev_state *cxlds, void __iomem *ras_base) { } +#endif /* CONFIG_CXL_RAS */ + +/* Restricted CXL Host specific RAS functions */ +#ifdef CONFIG_CXL_RAS +void cxl_dport_map_rch_aer(struct cxl_dport *dport); +void cxl_disable_rch_root_ints(struct cxl_dport *dport); +void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds); +#else +static inline void cxl_dport_map_rch_aer(struct cxl_dport *dport) { } +static inline void cxl_disable_rch_root_ints(struct cxl_dport *dport) { } +static inline void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { } +#endif /* CONFIG_CXL_RAS */ + int cxl_gpf_port_setup(struct cxl_dport *dport); struct cxl_hdm; diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 3fb9462954da2..bd7d7e382d041 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -711,81 +711,8 @@ void read_cdat_data(struct cxl_port *port) } EXPORT_SYMBOL_NS_GPL(read_cdat_data, "CXL"); -static void cxl_handle_cor_ras(struct cxl_dev_state *cxlds, - void __iomem *ras_base) -{ - void __iomem *addr; - u32 status; - - if (!ras_base) - return; - - addr = ras_base + CXL_RAS_CORRECTABLE_STATUS_OFFSET; - status = readl(addr); - if (status & CXL_RAS_CORRECTABLE_STATUS_MASK) { - writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr); - trace_cxl_aer_correctable_error(cxlds->cxlmd, status); - } -} - -/* CXL spec rev3.0 8.2.4.16.1 */ -static void header_log_copy(void __iomem *ras_base, u32 *log) -{ - void __iomem *addr; - u32 *log_addr; - int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32); - - addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET; - log_addr = log; - - for (i = 0; i < log_u32_size; i++) { - *log_addr = readl(addr); - log_addr++; - addr += sizeof(u32); - } -} - -/* - * Log the state of the RAS status registers and prepare them to log the - * next error status. Return 1 if reset needed. - */ -static bool cxl_handle_ras(struct cxl_dev_state *cxlds, - void __iomem *ras_base) -{ - u32 hl[CXL_HEADERLOG_SIZE_U32]; - void __iomem *addr; - u32 status; - u32 fe; - - if (!ras_base) - return false; - - addr = ras_base + CXL_RAS_UNCORRECTABLE_STATUS_OFFSET; - status = readl(addr); - if (!(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK)) - return false; - - /* If multiple errors, log header points to first error from ctrl reg */ - if (hweight32(status) > 1) { - void __iomem *rcc_addr = - ras_base + CXL_RAS_CAP_CONTROL_OFFSET; - - fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK, - readl(rcc_addr))); - } else { - fe = status; - } - - header_log_copy(ras_base, hl); - trace_cxl_aer_uncorrectable_error(cxlds->cxlmd, status, fe, hl); - writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr); - - return true; -} - -#ifdef CONFIG_PCIEAER_CXL - -static void cxl_dport_map_rch_aer(struct cxl_dport *dport) +#ifdef CONFIG_CXL_RAS +void cxl_dport_map_rch_aer(struct cxl_dport *dport) { resource_size_t aer_phys; struct device *host; @@ -800,19 +727,7 @@ static void cxl_dport_map_rch_aer(struct cxl_dport *dport) } } -static void cxl_dport_map_ras(struct cxl_dport *dport) -{ - struct cxl_register_map *map = &dport->reg_map; - struct device *dev = dport->dport_dev; - - if (!map->component_map.ras.valid) - dev_dbg(dev, "RAS registers not found\n"); - else if (cxl_map_component_regs(map, &dport->regs.component, - BIT(CXL_CM_CAP_CAP_ID_RAS))) - dev_dbg(dev, "Failed to map RAS capability.\n"); -} - -static void cxl_disable_rch_root_ints(struct cxl_dport *dport) +void cxl_disable_rch_root_ints(struct cxl_dport *dport) { void __iomem *aer_base = dport->regs.dport_aer; u32 aer_cmd_mask, aer_cmd; @@ -836,28 +751,6 @@ static void cxl_disable_rch_root_ints(struct cxl_dport *dport) writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND); } -/** - * cxl_dport_init_ras_reporting - Setup CXL RAS report on this dport - * @dport: the cxl_dport that needs to be initialized - * @host: host device for devm operations - */ -void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host) -{ - dport->reg_map.host = host; - cxl_dport_map_ras(dport); - - if (dport->rch) { - struct pci_host_bridge *host_bridge = to_pci_host_bridge(dport->dport_dev); - - if (!host_bridge->native_aer) - return; - - cxl_dport_map_rch_aer(dport); - cxl_disable_rch_root_ints(dport); - } -} -EXPORT_SYMBOL_NS_GPL(cxl_dport_init_ras_reporting, "CXL"); - /* * Copy the AER capability registers using 32 bit read accesses. * This is necessary because RCRB AER capability is MMIO mapped. Clear the @@ -906,7 +799,7 @@ static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs, return false; } -static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) +void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { struct pci_dev *pdev = to_pci_dev(cxlds->dev); struct aer_capability_regs aer_regs; @@ -931,82 +824,8 @@ static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) else cxl_handle_ras(cxlds, dport->regs.ras); } - -#else -static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { } #endif -void cxl_cor_error_detected(struct pci_dev *pdev) -{ - struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); - struct device *dev = &cxlds->cxlmd->dev; - - scoped_guard(device, dev) { - if (!dev->driver) { - dev_warn(&pdev->dev, - "%s: memdev disabled, abort error handling\n", - dev_name(dev)); - return; - } - - if (cxlds->rcd) - cxl_handle_rdport_errors(cxlds); - - cxl_handle_cor_ras(cxlds, cxlds->regs.ras); - } -} -EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, "CXL"); - -pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, - pci_channel_state_t state) -{ - struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); - struct cxl_memdev *cxlmd = cxlds->cxlmd; - struct device *dev = &cxlmd->dev; - bool ue; - - scoped_guard(device, dev) { - if (!dev->driver) { - dev_warn(&pdev->dev, - "%s: memdev disabled, abort error handling\n", - dev_name(dev)); - return PCI_ERS_RESULT_DISCONNECT; - } - - if (cxlds->rcd) - cxl_handle_rdport_errors(cxlds); - /* - * A frozen channel indicates an impending reset which is fatal to - * CXL.mem operation, and will likely crash the system. On the off - * chance the situation is recoverable dump the status of the RAS - * capability registers and bounce the active state of the memdev. - */ - ue = cxl_handle_ras(cxlds, cxlds->regs.ras); - } - - - switch (state) { - case pci_channel_io_normal: - if (ue) { - device_release_driver(dev); - return PCI_ERS_RESULT_NEED_RESET; - } - return PCI_ERS_RESULT_CAN_RECOVER; - case pci_channel_io_frozen: - dev_warn(&pdev->dev, - "%s: frozen state error detected, disable CXL.mem\n", - dev_name(dev)); - device_release_driver(dev); - return PCI_ERS_RESULT_NEED_RESET; - case pci_channel_io_perm_failure: - dev_warn(&pdev->dev, - "failure state error detected, request disconnect\n"); - return PCI_ERS_RESULT_DISCONNECT; - } - return PCI_ERS_RESULT_NEED_RESET; -} -EXPORT_SYMBOL_NS_GPL(cxl_error_detected, "CXL"); - static int cxl_flit_size(struct pci_dev *pdev) { if (cxl_pci_flit_256(pdev)) diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 2731ba3a07993..b933030b8e1e7 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "trace.h" static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, @@ -124,3 +125,178 @@ void cxl_ras_exit(void) cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work); cancel_work_sync(&cxl_cper_prot_err_work); } + +static void cxl_dport_map_ras(struct cxl_dport *dport) +{ + struct cxl_register_map *map = &dport->reg_map; + struct device *dev = dport->dport_dev; + + if (!map->component_map.ras.valid) + dev_dbg(dev, "RAS registers not found\n"); + else if (cxl_map_component_regs(map, &dport->regs.component, + BIT(CXL_CM_CAP_CAP_ID_RAS))) + dev_dbg(dev, "Failed to map RAS capability.\n"); +} + +/** + * cxl_dport_init_ras_reporting - Setup CXL RAS report on this dport + * @dport: the cxl_dport that needs to be initialized + * @host: host device for devm operations + */ +void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host) +{ + dport->reg_map.host = host; + cxl_dport_map_ras(dport); + + if (dport->rch) { + struct pci_host_bridge *host_bridge = to_pci_host_bridge(dport->dport_dev); + + if (!host_bridge->native_aer) + return; + + cxl_dport_map_rch_aer(dport); + cxl_disable_rch_root_ints(dport); + } +} +EXPORT_SYMBOL_NS_GPL(cxl_dport_init_ras_reporting, "CXL"); + +void cxl_handle_cor_ras(struct cxl_dev_state *cxlds, void __iomem *ras_base) +{ + void __iomem *addr; + u32 status; + + if (!ras_base) + return; + + addr = ras_base + CXL_RAS_CORRECTABLE_STATUS_OFFSET; + status = readl(addr); + if (status & CXL_RAS_CORRECTABLE_STATUS_MASK) { + writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr); + trace_cxl_aer_correctable_error(cxlds->cxlmd, status); + } +} + +/* CXL spec rev3.0 8.2.4.16.1 */ +static void header_log_copy(void __iomem *ras_base, u32 *log) +{ + void __iomem *addr; + u32 *log_addr; + int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32); + + addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET; + log_addr = log; + + for (i = 0; i < log_u32_size; i++) { + *log_addr = readl(addr); + log_addr++; + addr += sizeof(u32); + } +} + +/* + * Log the state of the RAS status registers and prepare them to log the + * next error status. Return 1 if reset needed. + */ +bool cxl_handle_ras(struct cxl_dev_state *cxlds, void __iomem *ras_base) +{ + u32 hl[CXL_HEADERLOG_SIZE_U32]; + void __iomem *addr; + u32 status; + u32 fe; + + if (!ras_base) + return false; + + addr = ras_base + CXL_RAS_UNCORRECTABLE_STATUS_OFFSET; + status = readl(addr); + if (!(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK)) + return false; + + /* If multiple errors, log header points to first error from ctrl reg */ + if (hweight32(status) > 1) { + void __iomem *rcc_addr = + ras_base + CXL_RAS_CAP_CONTROL_OFFSET; + + fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK, + readl(rcc_addr))); + } else { + fe = status; + } + + header_log_copy(ras_base, hl); + trace_cxl_aer_uncorrectable_error(cxlds->cxlmd, status, fe, hl); + writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr); + + return true; +} + +void cxl_cor_error_detected(struct pci_dev *pdev) +{ + struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); + struct device *dev = &cxlds->cxlmd->dev; + + scoped_guard(device, dev) { + if (!dev->driver) { + dev_warn(&pdev->dev, + "%s: memdev disabled, abort error handling\n", + dev_name(dev)); + return; + } + + if (cxlds->rcd) + cxl_handle_rdport_errors(cxlds); + + cxl_handle_cor_ras(cxlds, cxlds->regs.ras); + } +} +EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, "CXL"); + +pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); + struct cxl_memdev *cxlmd = cxlds->cxlmd; + struct device *dev = &cxlmd->dev; + bool ue; + + scoped_guard(device, dev) { + if (!dev->driver) { + dev_warn(&pdev->dev, + "%s: memdev disabled, abort error handling\n", + dev_name(dev)); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (cxlds->rcd) + cxl_handle_rdport_errors(cxlds); + /* + * A frozen channel indicates an impending reset which is fatal to + * CXL.mem operation, and will likely crash the system. On the off + * chance the situation is recoverable dump the status of the RAS + * capability registers and bounce the active state of the memdev. + */ + ue = cxl_handle_ras(cxlds, cxlds->regs.ras); + } + + + switch (state) { + case pci_channel_io_normal: + if (ue) { + device_release_driver(dev); + return PCI_ERS_RESULT_NEED_RESET; + } + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + dev_warn(&pdev->dev, + "%s: frozen state error detected, disable CXL.mem\n", + dev_name(dev)); + device_release_driver(dev); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + dev_warn(&pdev->dev, + "failure state error detected, request disconnect\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + return PCI_ERS_RESULT_NEED_RESET; +} +EXPORT_SYMBOL_NS_GPL(cxl_error_detected, "CXL"); diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 3a794278cc7fe..05c49133e9e03 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -781,14 +781,6 @@ struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port, struct device *dport_dev, int port_id, resource_size_t rcrb); -#ifdef CONFIG_PCIEAER_CXL -void cxl_setup_parent_dport(struct device *host, struct cxl_dport *dport); -void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host); -#else -static inline void cxl_dport_init_ras_reporting(struct cxl_dport *dport, - struct device *host) { } -#endif - struct cxl_decoder *to_cxl_decoder(struct device *dev); struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev); struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev); diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index 7ae621e618e79..970e84cf49e9a 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -130,7 +130,23 @@ static inline bool cxl_pci_flit_256(struct pci_dev *pdev) int devm_cxl_port_enumerate_dports(struct cxl_port *port); struct cxl_dev_state; void read_cdat_data(struct cxl_port *port); + +#ifdef CONFIG_CXL_RAS void cxl_cor_error_detected(struct pci_dev *pdev); pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, pci_channel_state_t state); +void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host); +#else +static inline void cxl_cor_error_detected(struct pci_dev *pdev) { } + +static inline pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + return PCI_ERS_RESULT_NONE; +} + +static inline void cxl_dport_init_ras_reporting(struct cxl_dport *dport, + struct device *host) { } +#endif + #endif /* __CXL_PCI_H__ */ diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index 0d5ce4b74b9f7..927fbb6c061f3 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -58,12 +58,12 @@ cxl_core-y += $(CXL_CORE_SRC)/pci.o cxl_core-y += $(CXL_CORE_SRC)/hdm.o cxl_core-y += $(CXL_CORE_SRC)/pmu.o cxl_core-y += $(CXL_CORE_SRC)/cdat.o -cxl_core-y += $(CXL_CORE_SRC)/ras.o cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o +cxl_core-$(CONFIG_CXL_RAS) += $(CXL_CORE_SRC)/ras.o cxl_core-y += config_check.o cxl_core-y += cxl_core_test.o cxl_core-y += cxl_core_exports.o From 6fc1919a6f2ed541484dd1f6cd93374044f8fd3d Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Thu, 23 Jul 2026 14:28:06 -0400 Subject: [PATCH 2859/3297] cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size [ Upstream commit c268f949e219f9e179558e836f457f6c5fbec416 ] The CXL r4.0 8.2.4.17.7 RAS Capability Structure has total length 0x58 bytes (CXL_RAS_CAPABILITY_LENGTH); the Header Log occupies the trailing 64 bytes at offset 0x18. CXL_HEADERLOG_SIZE was defined as SZ_512, eight times the actual on-device size. header_log_copy() reads CXL_HEADERLOG_SIZE_U32 (128) dwords from the RAS capability iomap, overrunning the 88-byte mapping by 448 bytes. The cxl_aer_uncorrectable_error trace event memcpy()s CXL_HEADERLOG_SIZE (512) bytes from its source. For the CPER caller the source is struct cxl_ras_capability_regs::header_log[16] (64 bytes) embedded in a stack-local cxl_cper_prot_err_work_data, so the memcpy reads 448 bytes of kernel stack into the trace event ring buffer where userspace can read it via tracefs. Set CXL_HEADERLOG_SIZE to 64 and derive CXL_HEADERLOG_SIZE_U32 from it, bringing all iomap readers into agreement on 16 dwords. Userspace tools such as rasdaemon have grown a dependency on the buggy 512-byte (128 u32) header_log layout in the cxl_aer_uncorrectable_error trace event. Add CXL_HEADERLOG_TRACE_SIZE_U32 = 128 and use it for the trace event __array and its memcpy to preserve that ABI. Both callers now pass a zero-filled u32[CXL_HEADERLOG_TRACE_SIZE_U32] staging buffer with only the first CXL_HEADERLOG_SIZE_U32 (16) entries populated from hardware; the remaining 112 u32s are zero-padded, keeping the 512-byte trace ring buffer layout intact. [ dj: Replaced 64 with SZ_64 per RichardC ] Fixes: 36f257e3b0ba ("acpi/ghes, cxl/pci: Process CXL CPER Protocol Errors") Fixes: 2905cb5236cb ("cxl/pci: Add (hopeful) error handling support") Cc: stable@vger.kernel.org Reported-by: Sashiko Signed-off-by: Terry Bowman Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Reviewed-by: Ben Cheatham Reviewed-by: Richard Cheng Link: https://patch.msgid.link/20260605180610.2249458-1-terry.bowman@amd.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cxl/core/ras.c | 27 ++++++++++++++++++++------- drivers/cxl/core/trace.h | 24 ++++++++++++++++-------- drivers/cxl/cxl.h | 14 ++++++++++++-- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index b933030b8e1e7..7056a79bff4d1 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -8,6 +8,10 @@ #include #include "trace.h" +/* Check that UCE header definition is maintained to keep ABI intact */ +static_assert(CXL_HEADERLOG_TRACE_SIZE_U32 == 128, + "rasdaemon ABI requires exactly 128 u32s"); + static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { @@ -19,6 +23,7 @@ static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -28,8 +33,8 @@ static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, else fe = status; - trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, - ras_cap.header_log); + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, hl); } static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd, @@ -44,6 +49,7 @@ static void cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -53,8 +59,15 @@ cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, else fe = status; - trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, - ras_cap.header_log); + /* + * ras_cap.header_log[] holds CXL_HEADERLOG_SIZE_U32 (16) hardware + * dwords. Copy them into the front of a zero-filled + * CXL_HEADERLOG_TRACE_SIZE_U32 (128) u32 staging buffer so the trace + * event memcpy sees a full 512-byte source and the userspace ABI + * (rasdaemon) is preserved. + */ + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, hl); } static int match_memdev_by_parent(struct device *dev, const void *uport) @@ -181,12 +194,12 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) { void __iomem *addr; u32 *log_addr; - int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32); + int i; addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET; log_addr = log; - for (i = 0; i < log_u32_size; i++) { + for (i = 0; i < CXL_HEADERLOG_SIZE_U32; i++) { *log_addr = readl(addr); log_addr++; addr += sizeof(u32); @@ -199,7 +212,7 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) */ bool cxl_handle_ras(struct cxl_dev_state *cxlds, void __iomem *ras_base) { - u32 hl[CXL_HEADERLOG_SIZE_U32]; + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; void __iomem *addr; u32 status; u32 fe; diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index a972e4ef19368..d37876096dd7c 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -56,7 +56,7 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __string(host, dev_name(dev->parent)) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(device); @@ -64,10 +64,14 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("device=%s host=%s status: '%s' first_error: '%s'", __get_str(device), __get_str(host), @@ -85,7 +89,7 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __field(u64, serial) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(memdev); @@ -94,10 +98,14 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("memdev=%s host=%s serial=%lld: status: '%s' first_error: '%s'", __get_str(memdev), __get_str(host), __entry->serial, diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 05c49133e9e03..a5b00c3b04447 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -148,8 +148,18 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw) #define CXL_RAS_CAP_CONTROL_FE_MASK GENMASK(5, 0) #define CXL_RAS_HEADER_LOG_OFFSET 0x18 #define CXL_RAS_CAPABILITY_LENGTH 0x58 -#define CXL_HEADERLOG_SIZE SZ_512 -#define CXL_HEADERLOG_SIZE_U32 SZ_512 / sizeof(u32) +#define CXL_HEADERLOG_SIZE SZ_64 +#define CXL_HEADERLOG_SIZE_U32 (CXL_HEADERLOG_SIZE / sizeof(u32)) + +/* + * The RAS UCE trace event header array was originally sized at SZ_512/sizeof(u32) + * = 128 u32s due to a bug. Userspace tools (rasdaemon) have grown a dependency + * on that 512-byte layout. Keep the trace array at 128 u32s to preserve the + * ABI; only CXL_HEADERLOG_SIZE_U32 (16) dwords are valid hardware data, the + * remainder are zero-filled. + */ +#define CXL_HEADERLOG_TRACE_SIZE SZ_512 +#define CXL_HEADERLOG_TRACE_SIZE_U32 (CXL_HEADERLOG_TRACE_SIZE / sizeof(u32)) /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */ #define CXLDEV_CAP_ARRAY_OFFSET 0x0 From 8af652cd994606328bb807a085c518636e39302c Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Thu, 23 Jul 2026 15:58:58 -0400 Subject: [PATCH 2860/3297] remoteproc: xlnx: Check remote core state [ Upstream commit a48df51d23138388900995add2854cda4aa68e55 ] The remote state is set to RPROC_DETACHED if the resource table is found in the memory. However, this can be wrong if the remote is not started, but firmware is still loaded in the memory. Use PM_GET_NODE_STATUS call to the firmware to request the state of the RPU node. If the RPU is actually out of reset and running, only then move the remote state to RPROC_DETACHED, otherwise keep the remote state to RPROC_OFFLINE. Signed-off-by: Tanmay Shah Fixes: bca4b02ef92e ("remoteproc: xlnx: Add attach detach support") Reviewed-by: Beleswar Padhi Acked-by: Michal Simek Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20260428221855.313752-1-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier [ replaced the unavailable zynqmp_pm_get_node_status() helper with a direct zynqmp_pm_invoke_fn() call and exported it for modular builds. ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/xilinx/zynqmp.c | 1 + drivers/remoteproc/xlnx_r5_remoteproc.c | 50 ++++++++++++++++++++----- include/linux/firmware/xlnx-zynqmp.h | 13 +++++++ 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 02da3e48bc8f3..dc9c5d32108d5 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -461,6 +461,7 @@ int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...) return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3], smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]); } +EXPORT_SYMBOL_GPL(zynqmp_pm_invoke_fn); static u32 pm_api_version; static u32 pm_tz_version; diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 5ef586fd37bca..a238b45bf2f2c 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -959,16 +959,6 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) goto free_rproc; } - /* - * If firmware is already available in the memory then move rproc state - * to DETACHED. Firmware can be preloaded via debugger or by any other - * agent (processors) in the system. - * If firmware isn't available in the memory and resource table isn't - * found, then rproc state remains OFFLINE. - */ - if (!zynqmp_r5_get_rsc_table_va(r5_core)) - r5_rproc->state = RPROC_DETACHED; - r5_core->rproc = r5_rproc; return r5_core; @@ -1221,6 +1211,7 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, { struct device *dev = cluster->dev; struct zynqmp_r5_core *r5_core; + u32 payload[PAYLOAD_ARG_CNT]; int ret = -EINVAL, i; r5_core = cluster->r5_cores[0]; @@ -1266,6 +1257,45 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, ret = zynqmp_r5_get_sram_banks(r5_core); if (ret) return ret; + + /* + * It is possible that firmware is loaded into the memory, but + * RPU (remote) is not running. In such case, RPU state will be + * moved to RPROC_DETACHED wrongfully. To avoid it first make + * sure RPU is power-on and out of reset before parsing for the + * resource table. + */ + ret = zynqmp_pm_feature(PM_GET_NODE_STATUS); + if (ret < PM_API_VERSION_2) + ret = -EOPNOTSUPP; + else + ret = zynqmp_pm_invoke_fn(PM_GET_NODE_STATUS, payload, 1, + r5_core->pm_domain_id); + if (ret) { + dev_warn(r5_core->dev, + "failed to get rpu node status, err %d\n", ret); + continue; + } + + /* + * If RPU state is power on and out of reset i.e. running, then + * assign RPROC_DETACHED state. If the RPU is not out of reset + * then do not attempt to attach to the remote processor. + */ + if (payload[1] == PM_NODE_RUNNING) { + /* + * Not all the firmware that is running on the remote + * core is expected to have the resource table. The + * firmware might not use RPMsg at all, and in that case + * resource table becomes irrelevant. However, we still + * need to make sure that running core is not reported + * as offline. so do not decide remote core state based + * on the resource table availability + */ + if (zynqmp_r5_get_rsc_table_va(r5_core)) + dev_dbg(r5_core->dev, "rsc tbl not found\n"); + r5_core->rproc->state = RPROC_DETACHED; + } } return 0; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index ae48d619c4e0a..2f1c71f0cea76 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -164,6 +164,7 @@ enum pm_api_cb_id { enum pm_api_id { PM_API_FEATURES = 0, PM_GET_API_VERSION = 1, + PM_GET_NODE_STATUS = 3, PM_REGISTER_NOTIFIER = 5, PM_FORCE_POWERDOWN = 8, PM_REQUEST_WAKEUP = 10, @@ -544,6 +545,18 @@ enum pm_gem_config_type { GEM_CONFIG_FIXED = 2, }; +/** + * enum pm_node_status - Device node status provided by xilpm fw + * @PM_NODE_UNUSED: Device is not used + * @PM_NODE_RUNNING: Device is power-on and out of reset + * @PM_NODE_HALT: Device is power-on but in the reset state + */ +enum pm_node_status { + PM_NODE_UNUSED = 0, + PM_NODE_RUNNING = 1, + PM_NODE_HALT = 12, +}; + /** * struct zynqmp_pm_query_data - PM query data * @qid: query ID From 3d561f46fa78491bc2398db025c7292100668a48 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Fri, 24 Jul 2026 06:49:37 -0400 Subject: [PATCH 2861/3297] mm/sparse-vmemmap: fix vmemmap accounting underflow [ Upstream commit c373f7f98e6ad591c85d40548cf8b6443be69311 ] Patch series "mm: Fix vmemmap optimization accounting and initialization", v8. The series fixes several bugs in vmemmap optimization, mainly around incorrect page accounting and memmap initialization in DAX and memory hotplug paths. It also fixes pageblock migratetype initialization and struct page initialization for ZONE_DEVICE compound pages. Patches 1-4 fix vmemmap accounting issues. Patch 1 fixes an accounting underflow in the section activation failure path by moving vmemmap page accounting into the lower-level allocation and freeing helpers. Patch 2 fixes incorrect altmap passing in the memory hotplug error path. Patch 3 passes pgmap through memory deactivation paths so the teardown side can determine whether vmemmap optimization was in effect. Patch 4 uses that information to account the optimized DAX vmemmap size correctly. Patches 5-6 fix initialization issues in mm/mm_init. One makes sure all pageblocks in ZONE_DEVICE compound pages get their migratetype initialized. The other fixes a case where DAX memory hotplug reuses an unoptimized early-section memmap while compound_nr_pages() still assumes vmemmap optimization, leaving tail struct pages uninitialized. This patch (of 6): In section_activate(), if populate_section_memmap() fails, the error handling path calls section_deactivate() to roll back the state. This causes a vmemmap accounting imbalance. Since commit c3576889d87b ("mm: fix accounting of memmap pages"), memmap pages are accounted for only after populate_section_memmap() succeeds. However, the failure path unconditionally calls section_deactivate(), which decreases the vmemmap count. Consequently, a failure in populate_section_memmap() leads to an accounting underflow, incorrectly reducing the system's tracked vmemmap usage. Fix this more thoroughly by moving all accounting calls into the lower level functions that actually perform the vmemmap allocation and freeing: - populate_section_memmap() accounts for newly allocated vmemmap pages - depopulate_section_memmap() unaccounts when vmemmap is freed This ensures proper accounting in all code paths, including error handling and early section cases. Link: https://lore.kernel.org/20260428081855.1249045-1-songmuchun@bytedance.com Link: https://lore.kernel.org/20260428081855.1249045-2-songmuchun@bytedance.com Fixes: c3576889d87b ("mm: fix accounting of memmap pages") Signed-off-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Acked-by: Liam R. Howlett Cc: "Aneesh Kumar K.V" Cc: Joao Martins Cc: Lorenzo Stoakes Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Michal Hocko Cc: Nicholas Piggin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- mm/sparse.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/mm/sparse.c b/mm/sparse.c index 17c50a6415c2d..bda92a084681a 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -670,7 +670,12 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { - return __populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap); + struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap, + pgmap); + + memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); + + return page; } static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, @@ -679,13 +684,17 @@ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); + memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); vmemmap_free(start, end, altmap); } + static void free_map_bootmem(struct page *memmap) { unsigned long start = (unsigned long)memmap; unsigned long end = (unsigned long)(memmap + PAGES_PER_SECTION); + memmap_boot_pages_add(-1L * (DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page), + PAGE_SIZE))); vmemmap_free(start, end, NULL); } @@ -742,13 +751,18 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap, struct dev_pagemap *pgmap) { - return kvmalloc_node(array_size(sizeof(struct page), - PAGES_PER_SECTION), GFP_KERNEL, nid); + struct page *page = kvmalloc_node(array_size(sizeof(struct page), + PAGES_PER_SECTION), GFP_KERNEL, nid); + + memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); + + return page; } static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, struct vmem_altmap *altmap) { + memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); kvfree(pfn_to_page(pfn)); } @@ -761,6 +775,9 @@ static void free_map_bootmem(struct page *memmap) nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page)) >> PAGE_SHIFT; + memmap_boot_pages_add(-1L * (DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page), + PAGE_SIZE))); + for (i = 0; i < nr_pages; i++, page++) { type = bootmem_type(page); @@ -854,14 +871,10 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, * The memmap of early sections is always fully populated. See * section_activate() and pfn_valid() . */ - if (!section_is_early) { - memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE))); + if (!section_is_early) depopulate_section_memmap(pfn, nr_pages, altmap); - } else if (memmap) { - memmap_boot_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), - PAGE_SIZE))); + else if (memmap) free_map_bootmem(memmap); - } if (empty) ms->section_mem_map = (unsigned long)NULL; @@ -906,7 +919,6 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, section_deactivate(pfn, nr_pages, altmap); return ERR_PTR(-ENOMEM); } - memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)); return memmap; } From b8271be34bce151597da5b5179e0648c281322a4 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Sat, 25 Jul 2026 06:54:33 -0400 Subject: [PATCH 2862/3297] kho: make sure scratch size is always aligned by CMA_MIN_ALIGNMENT_BYTES [ Upstream commit 0e39380a7316122e1b00012b3f3cd3e318b3e7d3 ] When using scratch_scale, the scratch sizes are rounded up to CMA_MIN_ALIGNMENT_BYTES since they will be released as MIGRATE_CMA. This is not done when using fixed scratch sizes via command line. This can result in user specifying a size which is not aligned, and thus kernel releasing a pageblock that is only partially scratch. Do the rounding up for both cases in scratch_size_update(). Fixes: 3dc92c311498 ("kexec: add Kexec HandOver (KHO) generation helpers") Cc: stable@kernel.org Signed-off-by: Pratyush Yadav (Google) Link: https://patch.msgid.link/20260519160554.2713361-1-pratyush@kernel.org Signed-off-by: Pasha Tatashin Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/kexec_handover.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c index c13b99f7c9891..e1a06b4fc17fc 100644 --- a/kernel/kexec_handover.c +++ b/kernel/kexec_handover.c @@ -562,20 +562,30 @@ early_param("kho_scratch", kho_parse_scratch_size); static void __init scratch_size_update(void) { - phys_addr_t size; + /* + * If fixed sizes are not provided via command line, calculate them + * now. + */ + if (scratch_scale) { + phys_addr_t size; - if (!scratch_scale) - return; + size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT, + NUMA_NO_NODE); + size = size * scratch_scale / 100; + scratch_size_lowmem = size; - size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT, - NUMA_NO_NODE); - size = size * scratch_scale / 100; - scratch_size_lowmem = round_up(size, CMA_MIN_ALIGNMENT_BYTES); + size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE, + NUMA_NO_NODE); + size = size * scratch_scale / 100 - scratch_size_lowmem; + scratch_size_global = size; + } - size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE, - NUMA_NO_NODE); - size = size * scratch_scale / 100 - scratch_size_lowmem; - scratch_size_global = round_up(size, CMA_MIN_ALIGNMENT_BYTES); + /* + * Scratch areas are released as MIGRATE_CMA. Round them up to the right + * size. + */ + scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES); + scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES); } static phys_addr_t __init scratch_size_node(int nid) From 19360c25135fccb6bbafbee49a5f66baf9a311af Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Sat, 25 Jul 2026 06:54:40 -0400 Subject: [PATCH 2863/3297] mtd: maps: vmu-flash: fix fault in unaligned fixup [ Upstream commit 79d1661502c6e4b6f626185cef72cf2fa78116e1 ] Use kzalloc_obj() / kzalloc_objs() to allocate the memcard structs, instead of kmalloc_obj() / kmalloc_objs() to prevent access to uninitialized data. Fixes runtime error: Fault in unaligned fixup: 0000 [#1] at mtd_get_fact_prot_info. Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.") Cc: stable@vger.kernel.org Signed-off-by: Florian Fuchs Signed-off-by: Miquel Raynal Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/maps/vmu-flash.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 3667faefe6c58..e992f4d1417e6 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -610,7 +610,7 @@ static int vmu_connect(struct maple_device *mdev) basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]); - card = kmalloc(sizeof(struct memcard), GFP_KERNEL); + card = kzalloc_obj(struct memcard); if (!card) { error = -ENOMEM; goto fail_nomem; @@ -628,15 +628,13 @@ static int vmu_connect(struct maple_device *mdev) * Not sure there are actually any multi-partition devices in the * real world, but the hardware supports them, so, so will we */ - card->parts = kmalloc_array(card->partitions, sizeof(struct vmupart), - GFP_KERNEL); + card->parts = kzalloc_objs(struct vmupart, card->partitions); if (!card->parts) { error = -ENOMEM; goto fail_partitions; } - card->mtd = kmalloc_array(card->partitions, sizeof(struct mtd_info), - GFP_KERNEL); + card->mtd = kzalloc_objs(struct mtd_info, card->partitions); if (!card->mtd) { error = -ENOMEM; goto fail_mtd_info; From 277a035cda47dd11cf6d0079e2276796923a81bd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 26 Jul 2026 08:47:40 -0400 Subject: [PATCH 2864/3297] dmaengine: dw-edma: Fix confusing cleanup.h syntax [ Upstream commit f9ef8dedee34e2d7828d5a6a0643cd969aaa8437 ] Initializing automatic __free variables to NULL without need (e.g. branches with different allocations), followed by actual allocation is in contrary to explicit coding rules guiding cleanup.h: "Given that the "__free(...) = NULL" pattern for variables defined at the top of the function poses this potential interdependency problem the recommendation is to always define and assign variables in one statement and not group variable definitions at the top of the function when __free() is used." Code does not have a bug, but is less readable and uses discouraged coding practice, so fix that by moving declaration to the place of assignment. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20251208020729.4654-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Vinod Koul Stable-dep-of: 11d7cfe0c119 ("dmaengine: dw-edma-pcie: Reject devices without driver data") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/dma/dw-edma/dw-edma-pcie.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 3371e0a76d3ca..e9caf8adca1fc 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -161,13 +161,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *pid) { struct dw_edma_pcie_data *pdata = (void *)pid->driver_data; - struct dw_edma_pcie_data *vsec_data __free(kfree) = NULL; struct device *dev = &pdev->dev; struct dw_edma_chip *chip; int err, nr_irqs; int i, mask; - vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL); + struct dw_edma_pcie_data *vsec_data __free(kfree) = + kmalloc(sizeof(*vsec_data), GFP_KERNEL); if (!vsec_data) return -ENOMEM; From 043acb00e4edd4b9ffb9dd0e357482dcd9086486 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Sun, 26 Jul 2026 08:47:41 -0400 Subject: [PATCH 2865/3297] dmaengine: dw-edma-pcie: Reject devices without driver data [ Upstream commit 11d7cfe0c119691b2dafbb699bbca90258c678aa ] dw_edma_pcie_probe() treats the PCI device ID driver_data as the template for the controller layout and copies it unconditionally. A device bound dynamically via sysfs can match the driver without that data, which leads to a NULL pointer dereference. Reject such matches before enabling the device. Fixes: 41aaff2a2ac0 ("dmaengine: Add Synopsys eDMA IP PCIe glue-logic") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den Reviewed-by: Frank Li Link: https://patch.msgid.link/20260521142153.2957432-3-den@valinux.co.jp Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/dma/dw-edma/dw-edma-pcie.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index e9caf8adca1fc..dc6ffb6941ade 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -166,6 +166,9 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, int err, nr_irqs; int i, mask; + if (!pdata) + return -ENODEV; + struct dw_edma_pcie_data *vsec_data __free(kfree) = kmalloc(sizeof(*vsec_data), GFP_KERNEL); if (!vsec_data) From 62dae36be7a62348fa0ac191f248317c2dd885a0 Mon Sep 17 00:00:00 2001 From: Souvik Banerjee Date: Mon, 27 Jul 2026 09:31:39 -0400 Subject: [PATCH 2866/3297] ovl: use linked upper dentry in copy-up tmpfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e348eecd4d8fa8d18a5157ff59f7be1dc59c5928 ] ovl_copy_up_tmpfile() stores the disconnected O_TMPFILE dentry as the overlay's upper dentry reference via ovl_inode_update(). vfs_tmpfile() allocated this dentry via d_alloc(parentpath->dentry, &slash_name), so d_name is "/" and d_parent is c->workdir. Local upper filesystems (ext4, btrfs, xfs, ...) immediately rename it to "#" via d_mark_tmpfile() inside their ->tmpfile() op; FUSE and virtiofs do not, so both fields stay that way. Neither identifies the destination directory and filename where ovl_do_link() actually linked the file. When the upper filesystem implements ->d_revalidate() (e.g. FUSE or virtiofs), ovl_revalidate_real() calls it with the dentry's parent inode and a snapshot of d_name. The server tries to look up "/" inside c->workdir, fails, and overlayfs reports -ESTALE. This causes persistent ESTALE errors for any file that was copied up via the tmpfile path, breaking dpkg, apt, and other tools that do rename-over-existing on overlayfs with a FUSE/virtiofs upper. Before commit 6b52243f633e ("ovl: fold copy-up helpers into callers"), the tmpfile copy-up path used a dedicated helper ovl_link_tmpfile() that captured the linked destination dentry returned by ovl_do_link(): err = ovl_do_link(temp, udir, upper); ... if (!err) *newdentry = dget(upper); and published it via ovl_inode_update(d_inode(c->dentry), newdentry). The fold inlined ovl_do_link() into ovl_copy_up_tmpfile() but dropped the dget(upper) capture, and rewrote the publish line as ovl_inode_update(d_inode(c->dentry), dget(temp)) — where temp is the disconnected O_TMPFILE dentry. Fix by keeping a reference to the linked destination dentry after ovl_do_link() succeeds, and publishing that dentry at the existing ovl_inode_update() call site. The non-tmpfile/workdir path continues to publish the renamed temporary dentry. Reproducer: - Mount overlayfs with virtiofs (or a FUSE fs whose server advertises FUSE_TMPFILE) as upper - Run: dpkg -i - Observe: "error installing new file '...': Stale file handle" Fixes: 6b52243f633e ("ovl: fold copy-up helpers into callers") Cc: stable@vger.kernel.org # v4.20+ Signed-off-by: Souvik Banerjee Link: https://patch.msgid.link/20260501232735.2610824-1-souvik@amlalabs.com Reviewed-by: Amir Goldstein Reviewed-by: Miklos Szeredi Signed-off-by: Christian Brauner (Amutable) [ adapted scoped credential and creation helpers to explicit credential, locking, lookup, and cleanup handling ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/overlayfs/copy_up.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 97f373fd98797..89317e1e7361b 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -864,7 +864,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) { struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb); struct inode *udir = d_inode(c->destdir); - struct dentry *temp, *upper; + struct dentry *temp, *upper, *newdentry = NULL; struct file *tmpfile; struct ovl_cu_creds cc; int err; @@ -901,6 +901,14 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) err = PTR_ERR(upper); if (!IS_ERR(upper)) { err = ovl_do_link(ofs, temp, udir, upper); + if (!err) { + /* + * Record the linked dentry -- not the disconnected + * O_TMPFILE dentry -- so that ->d_revalidate() on + * the upper fs sees the real parent/name. + */ + newdentry = dget(upper); + } dput(upper); } inode_unlock(udir); @@ -916,7 +924,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) if (!c->metacopy) ovl_set_upperdata(d_inode(c->dentry)); - ovl_inode_update(d_inode(c->dentry), dget(temp)); + ovl_inode_update(d_inode(c->dentry), newdentry); out: ovl_end_write(c->dentry); From af7a4c2caa7a191d6e7ed33903b69f9901874cd0 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Mon, 27 Jul 2026 15:50:59 -0400 Subject: [PATCH 2867/3297] accel/amdxdna: reject command submission on devices without a submit op [ Upstream commit 38953513d7313992676d4136cd425cdb70c6278e ] amdxdna_cmd_submit() calls xdna->dev_info->ops->cmd_submit() unconditionally, but only aie2_dev_ops defines that callback. aie4_vf_ops (the AIE4 SR-IOV virtual function) does not, so a user AMDXDNA_EXEC_CMD ioctl on an AIE4 device reaches a NULL function-pointer call and oopses the kernel. AIE4 submits work through a mapped user queue and doorbell, not this ioctl path. Reject the submission early with -EOPNOTSUPP when the device provides no cmd_submit op, so the shared EXEC ioctl is a clean no-op on such devices. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Cc: stable@vger.kernel.org Found by 0sec automated security-research tooling (https://0sec.ai). Assisted-by: 0sec:claude-opus-4-8 Signed-off-by: Doruk Tan Ozturk Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260713173030.87541-3-doruk@0sec.ai Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/accel/amdxdna/amdxdna_ctx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 347e046d2a0fb..a49c059e6ed49 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -406,6 +406,10 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, int ret, idx; XDNA_DBG(xdna, "Command BO hdl %d, Arg BO count %d", cmd_bo_hdl, arg_bo_cnt); + + if (!xdna->dev_info->ops->cmd_submit) + return -EOPNOTSUPP; + job = kzalloc(struct_size(job, bos, arg_bo_cnt), GFP_KERNEL); if (!job) return -ENOMEM; From dd73cc92a55d7b04051cd6202665cd1ae477b899 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 27 Jul 2026 16:59:56 -0400 Subject: [PATCH 2868/3297] cred: add kernel_cred() helper [ Upstream commit 4c7ceeb62d3330b6fb2b549ae833a92c0f481f3e ] Access kernel creds based off of init_task. This will let us avoid any direct access to init_cred. Link: https://patch.msgid.link/20251103-work-creds-init_cred-v1-2-cb3ec8711a6a@kernel.org Reviewed-by: Jens Axboe Signed-off-by: Christian Brauner Stable-dep-of: 981ccd97f715 ("dm: avoid leaking the caller's thread keyring via the table device file") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/cred.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/cred.h b/include/linux/cred.h index 6225c0cef3d77..be2cd07b174c5 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -20,6 +20,8 @@ struct cred; struct inode; +extern struct task_struct init_task; + /* * COW Supplementary groups list */ @@ -156,6 +158,11 @@ extern struct cred *prepare_exec_creds(void); extern int commit_creds(struct cred *); extern void abort_creds(struct cred *); extern struct cred *prepare_kernel_cred(struct task_struct *); +static inline const struct cred *kernel_cred(void) +{ + /* shut up sparse */ + return rcu_dereference_raw(init_task.cred); +} extern int set_security_override(struct cred *, u32); extern int set_security_override_from_ctx(struct cred *, const char *); extern int set_create_files_as(struct cred *, struct inode *); From 8ced1d242c34e342defcccdb00663354f212aae6 Mon Sep 17 00:00:00 2001 From: Ingo Blechschmidt Date: Mon, 27 Jul 2026 16:59:58 -0400 Subject: [PATCH 2869/3297] dm: avoid leaking the caller's thread keyring via the table device file [ Upstream commit 981ccd97f7153d310dfa92a534525bbaf46752c2 ] The refactoring in commit a28d893eb327 ("md: port block device access to file") accidentally causes the caller's thread keyring to be kept alive long beyond the caller's lifetime. As a result, "cryptsetup luksSuspend" silently fails to wipe the LUKS volume key from memory. In detail: "cryptsetup luksOpen" uses its supposedly ephemeral thread keyring to pass the volume key to the kernel. dm-crypt's crypt_set_keyring_key() copies the key material into its own crypt_config structure and then drops its own reference to the key in the keyring with key_put(). With this fix, restoring pre-v6.9 behavior, the copy in the thread keyring is then promptly garbage collected, such that exactly one copy of the volume key remains. This single copy is correctly wiped from memory on "cryptsetup luksSuspend". Without this fix, the thread keyring and the volume key in it remains. This second copy is only freed on "luksClose". "luksSuspend" neither knows about this copy nor has any way to remove it, so the key remains recoverable from RAM after a suspend that is documented to have wiped it. This fix should not introduce new security problems, as the code is anyway gated by CAP_SYS_ADMIN. The device-mapper core, not the calling task, is the legitimate owner of this long-lived file. Fixes: a28d893eb327 ("md: port block device access to file") Closes: https://gitlab.com/cryptsetup/cryptsetup/-/work_items/993 Link: https://www.speicherleck.de/iblech/cryptsetup-luksSuspend-issue-reproduction/ Signed-off-by: Ingo Blechschmidt Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Tested-by: Ondrej Kozina Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index fb6bcd598c5b8..28d825cc69c53 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -735,7 +735,16 @@ static struct table_device *open_table_device(struct mapped_device *md, return ERR_PTR(-ENOMEM); refcount_set(&td->count, 1); - bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL); + /* + * Open the backing device with kernel rather than caller + * credentials. Otherwise the caller's credentials would be + * pinned in bdev_file->f_cred until the table device is closed. + * That would keep the caller's thread keyring alive long beyond the + * lifetime of the caller, breaking userspace expectation (e.g. + * cryptsetup(8) leaking the LUKS volume key). + */ + scoped_with_kernel_creds() + bdev_file = bdev_file_open_by_dev(dev, mode, _dm_claim_ptr, NULL); if (IS_ERR(bdev_file)) { r = PTR_ERR(bdev_file); goto out_free_td; From 73d397ab54f2a013eebbda9d44a5fb40894bedc3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 27 Jul 2026 18:20:44 -0400 Subject: [PATCH 2870/3297] mmc: vub300: rename probe error labels [ Upstream commit 5b8b35d6f4fa758dd5e8ae18526ea1c73f6787e0 ] Error labels should be named after what they do. Rename the probe error labels. Signed-off-by: Johan Hovold Signed-off-by: Ulf Hansson Stable-dep-of: a3b5f242997a ("mmc: vub300: fix use-after-free on probe failure") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/vub300.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 93c4e145009e0..9a86436959816 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2115,19 +2115,19 @@ static int vub300_probe(struct usb_interface *interface, command_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_out_urb) { retval = -ENOMEM; - goto error0; + goto err_put_udev; } command_res_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_res_urb) { retval = -ENOMEM; - goto error1; + goto err_free_out_urb; } /* this also allocates memory for our VUB300 mmc host device */ mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev); if (!mmc) { retval = -ENOMEM; dev_err(&udev->dev, "not enough memory for the mmc_host\n"); - goto error4; + goto err_free_res_urb; } /* MMC core transfer sizes tunable parameters */ mmc->caps = 0; @@ -2344,10 +2344,11 @@ static int vub300_probe(struct usb_interface *interface, interface_to_InterfaceNumber(interface)); retval = mmc_add_host(mmc); if (retval) - goto error6; + goto err_delete_timer; return 0; -error6: + +err_delete_timer: timer_delete_sync(&vub300->inactivity_timer); err_free_host: mmc_free_host(mmc); @@ -2355,12 +2356,13 @@ static int vub300_probe(struct usb_interface *interface, * and hence also frees vub300 * which is contained at the end of struct mmc */ -error4: +err_free_res_urb: usb_free_urb(command_res_urb); -error1: +err_free_out_urb: usb_free_urb(command_out_urb); -error0: +err_put_udev: usb_put_dev(udev); + return retval; } From 5b82af744e06cd741938c3da54fdbe564a7e52d9 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 27 Jul 2026 18:20:45 -0400 Subject: [PATCH 2871/3297] mmc: vub300: fix use-after-free on probe failure [ Upstream commit a3b5f242997a3be7404112fd48784881560aea57 ] The vub300 driver lifetime-manages its controller state using vub300->kref, with vub300_delete() freeing the mmc host when the last reference is dropped. The probe error path after the inactivity timer has been armed still bypasses that lifetime rule, however, and falls through to mmc_free_host() directly if mmc_add_host() fails. The race window is between arming the inactivity timer and reaching the probe error unwind after mmc_add_host() fails: probe thread timer/workqueue ------------ --------------- kref_init(&vub300->kref) ref = 1 kref_get(&vub300->kref) ref = 2, timer ref add_timer(inactivity_timer) fires after one second | | race window |<----------------------------------------------------> | mmc_add_host(mmc) inactivity timer fires vub300_queue_dead_work() kref_get() ref = 3 queue_work(deadwork) mmc_add_host() fails timer_delete_sync() mmc_free_host(mmc) frees vub300 deadwork runs use-after-free The inactivity timeout is one second, so this would require mmc_add_host() to both fail and take more than one second to do so. This is unlikely to happen in practice, but the error path is still wrong. timer_delete_sync() only waits for the timer callback itself. It does not flush deadwork that the callback may already have queued. As a result, queued deadwork can still hold a kref while the probe error path directly frees the backing mmc host, including the vub300 storage. Fix this by using the same lifetime mechanism as disconnect. Clear vub300->interface so that the timer callback and any queued deadwork return early and drop their references, then drop the initial probe reference and return without falling through to err_free_host. Fixes: 0613ad2401f8 ("mmc: vub300: fix return value check of mmc_add_host()") Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/vub300.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 9a86436959816..1614e9995ee68 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2344,12 +2344,16 @@ static int vub300_probe(struct usb_interface *interface, interface_to_InterfaceNumber(interface)); retval = mmc_add_host(mmc); if (retval) - goto err_delete_timer; + goto err_stop_io; return 0; -err_delete_timer: - timer_delete_sync(&vub300->inactivity_timer); +err_stop_io: + vub300->interface = NULL; + kref_put(&vub300->kref, vub300_delete); + + return retval; + err_free_host: mmc_free_host(mmc); /* From ad4ea2a169a4850857b774ab2fe4a0e07bf05ad6 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 27 Jul 2026 19:34:15 -0400 Subject: [PATCH 2872/3297] fs/resctrl: Split L3 dependent parts out of __mon_event_count() [ Upstream commit ad5c2ff75e0c53d2588dfc10eb87458e759b6bbe ] Carve out the L3 resource specific event reading code into a separate helper to support reading event data from a new monitoring resource. Suggested-by: Reinette Chatre Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Reinette Chatre Link: https://lore.kernel.org/20251217172121.12030-1-tony.luck@intel.com Stable-dep-of: 52fce648607e ("fs/resctrl: Fix use-after-free during unmount") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/monitor.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 7012a75814ac3..cdd5070d9ec71 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -418,7 +418,7 @@ static void mbm_cntr_free(struct rdt_mon_domain *d, int cntr_id) memset(&d->cntr_cfg[cntr_id], 0, sizeof(*d->cntr_cfg)); } -static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) +static int __l3_mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) { int cpu = smp_processor_id(); u32 closid = rdtgrp->closid; @@ -499,6 +499,17 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) return ret; } +static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) +{ + switch (rr->r->rid) { + case RDT_RESOURCE_L3: + return __l3_mon_event_count(rdtgrp, rr); + default: + rr->err = -EINVAL; + return -EINVAL; + } +} + /* * mbm_bw_count() - Update bw count from values previously read by * __mon_event_count(). From 696c34a1964f42ee211a39936fc5cfa428e3fd2f Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 27 Jul 2026 19:34:16 -0400 Subject: [PATCH 2873/3297] x86,fs/resctrl: Rename struct rdt_mon_domain and rdt_hw_mon_domain [ Upstream commit 4bc3ef46ff41d5e7ba557e56e9cd2031527cd7f8 ] The upcoming telemetry event monitoring is not tied to the L3 resource and will have a new domain structure. Rename the L3 resource specific domain data structures to include "l3_" in their names to avoid confusion between the different resource specific domain structures: rdt_mon_domain -> rdt_l3_mon_domain rdt_hw_mon_domain -> rdt_hw_l3_mon_domain No functional change. Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Reinette Chatre Link: https://lore.kernel.org/20251217172121.12030-1-tony.luck@intel.com Stable-dep-of: 52fce648607e ("fs/resctrl: Fix use-after-free during unmount") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/resctrl/core.c | 16 +++---- arch/x86/kernel/cpu/resctrl/internal.h | 16 +++---- arch/x86/kernel/cpu/resctrl/monitor.c | 34 +++++++------- fs/resctrl/ctrlmondata.c | 6 +-- fs/resctrl/internal.h | 12 ++--- fs/resctrl/monitor.c | 62 +++++++++++++------------- fs/resctrl/rdtgroup.c | 32 ++++++------- include/linux/resctrl.h | 28 ++++++------ 8 files changed, 103 insertions(+), 103 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c index 79a1f2d5f5019..0942344e2f7f7 100644 --- a/arch/x86/kernel/cpu/resctrl/core.c +++ b/arch/x86/kernel/cpu/resctrl/core.c @@ -363,7 +363,7 @@ static void ctrl_domain_free(struct rdt_hw_ctrl_domain *hw_dom) kfree(hw_dom); } -static void mon_domain_free(struct rdt_hw_mon_domain *hw_dom) +static void mon_domain_free(struct rdt_hw_l3_mon_domain *hw_dom) { int idx; @@ -400,7 +400,7 @@ static int domain_setup_ctrlval(struct rdt_resource *r, struct rdt_ctrl_domain * * @num_rmid: The size of the MBM counter array * @hw_dom: The domain that owns the allocated arrays */ -static int arch_domain_mbm_alloc(u32 num_rmid, struct rdt_hw_mon_domain *hw_dom) +static int arch_domain_mbm_alloc(u32 num_rmid, struct rdt_hw_l3_mon_domain *hw_dom) { size_t tsize = sizeof(*hw_dom->arch_mbm_states[0]); enum resctrl_event_id eventid; @@ -499,9 +499,9 @@ static void domain_add_cpu_mon(int cpu, struct rdt_resource *r) { int id = get_domain_id_from_scope(cpu, r->mon_scope); struct list_head *add_pos = NULL; - struct rdt_hw_mon_domain *hw_dom; + struct rdt_hw_l3_mon_domain *hw_dom; struct rdt_domain_hdr *hdr; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; struct cacheinfo *ci; int err; @@ -517,7 +517,7 @@ static void domain_add_cpu_mon(int cpu, struct rdt_resource *r) if (hdr) { if (WARN_ON_ONCE(hdr->type != RESCTRL_MON_DOMAIN)) return; - d = container_of(hdr, struct rdt_mon_domain, hdr); + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); cpumask_set_cpu(cpu, &d->hdr.cpu_mask); /* Update the mbm_assign_mode state for the CPU if supported */ @@ -620,9 +620,9 @@ static void domain_remove_cpu_ctrl(int cpu, struct rdt_resource *r) static void domain_remove_cpu_mon(int cpu, struct rdt_resource *r) { int id = get_domain_id_from_scope(cpu, r->mon_scope); - struct rdt_hw_mon_domain *hw_dom; + struct rdt_hw_l3_mon_domain *hw_dom; struct rdt_domain_hdr *hdr; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; lockdep_assert_held(&domain_list_lock); @@ -642,7 +642,7 @@ static void domain_remove_cpu_mon(int cpu, struct rdt_resource *r) if (WARN_ON_ONCE(hdr->type != RESCTRL_MON_DOMAIN)) return; - d = container_of(hdr, struct rdt_mon_domain, hdr); + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); hw_dom = resctrl_to_arch_mon_dom(d); cpumask_clear_cpu(cpu, &d->hdr.cpu_mask); diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h index 6da9bd1a188b9..5b3ccebdd39c9 100644 --- a/arch/x86/kernel/cpu/resctrl/internal.h +++ b/arch/x86/kernel/cpu/resctrl/internal.h @@ -63,17 +63,17 @@ struct rdt_hw_ctrl_domain { }; /** - * struct rdt_hw_mon_domain - Arch private attributes of a set of CPUs that share - * a resource for a monitor function - * @d_resctrl: Properties exposed to the resctrl file system + * struct rdt_hw_l3_mon_domain - Arch private attributes of a set of CPUs sharing + * RDT_RESOURCE_L3 monitoring + * @d_resctrl: Properties exposed to the resctrl file system * @arch_mbm_states: Per-event pointer to the MBM event's saved state. * An MBM event's state is an array of struct arch_mbm_state * indexed by RMID on x86. * * Members of this structure are accessed via helpers that provide abstraction. */ -struct rdt_hw_mon_domain { - struct rdt_mon_domain d_resctrl; +struct rdt_hw_l3_mon_domain { + struct rdt_l3_mon_domain d_resctrl; struct arch_mbm_state *arch_mbm_states[QOS_NUM_L3_MBM_EVENTS]; }; @@ -82,9 +82,9 @@ static inline struct rdt_hw_ctrl_domain *resctrl_to_arch_ctrl_dom(struct rdt_ctr return container_of(r, struct rdt_hw_ctrl_domain, d_resctrl); } -static inline struct rdt_hw_mon_domain *resctrl_to_arch_mon_dom(struct rdt_mon_domain *r) +static inline struct rdt_hw_l3_mon_domain *resctrl_to_arch_mon_dom(struct rdt_l3_mon_domain *r) { - return container_of(r, struct rdt_hw_mon_domain, d_resctrl); + return container_of(r, struct rdt_hw_l3_mon_domain, d_resctrl); } /** @@ -138,7 +138,7 @@ static inline struct rdt_hw_resource *resctrl_to_arch_res(struct rdt_resource *r extern struct rdt_hw_resource rdt_resources_all[]; -void arch_mon_domain_online(struct rdt_resource *r, struct rdt_mon_domain *d); +void arch_mon_domain_online(struct rdt_resource *r, struct rdt_l3_mon_domain *d); /* CPUID.(EAX=10H, ECX=ResID=1).EAX */ union cpuid_0x10_1_eax { diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index 4b2b539ddff95..d917a61b4e4d2 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -109,7 +109,7 @@ static inline u64 get_corrected_mbm_count(u32 rmid, unsigned long val) * * In RMID sharing mode there are fewer "logical RMID" values available * to accumulate data ("physical RMIDs" are divided evenly between SNC - * nodes that share an L3 cache). Linux creates an rdt_mon_domain for + * nodes that share an L3 cache). Linux creates an rdt_l3_mon_domain for * each SNC node. * * The value loaded into IA32_PQR_ASSOC is the "logical RMID". @@ -157,7 +157,7 @@ static int __rmid_read_phys(u32 prmid, enum resctrl_event_id eventid, u64 *val) return 0; } -static struct arch_mbm_state *get_arch_mbm_state(struct rdt_hw_mon_domain *hw_dom, +static struct arch_mbm_state *get_arch_mbm_state(struct rdt_hw_l3_mon_domain *hw_dom, u32 rmid, enum resctrl_event_id eventid) { @@ -171,11 +171,11 @@ static struct arch_mbm_state *get_arch_mbm_state(struct rdt_hw_mon_domain *hw_do return state ? &state[rmid] : NULL; } -void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_mon_domain *d, +void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 unused, u32 rmid, enum resctrl_event_id eventid) { - struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); + struct rdt_hw_l3_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); int cpu = cpumask_any(&d->hdr.cpu_mask); struct arch_mbm_state *am; u32 prmid; @@ -194,9 +194,9 @@ void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_mon_domain *d, * Assumes that hardware counters are also reset and thus that there is * no need to record initial non-zero counts. */ -void resctrl_arch_reset_rmid_all(struct rdt_resource *r, struct rdt_mon_domain *d) +void resctrl_arch_reset_rmid_all(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { - struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); + struct rdt_hw_l3_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); enum resctrl_event_id eventid; int idx; @@ -217,10 +217,10 @@ static u64 mbm_overflow_count(u64 prev_msr, u64 cur_msr, unsigned int width) return chunks >> shift; } -static u64 get_corrected_val(struct rdt_resource *r, struct rdt_mon_domain *d, +static u64 get_corrected_val(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 rmid, enum resctrl_event_id eventid, u64 msr_val) { - struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); + struct rdt_hw_l3_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); struct arch_mbm_state *am; u64 chunks; @@ -238,11 +238,11 @@ static u64 get_corrected_val(struct rdt_resource *r, struct rdt_mon_domain *d, return chunks * hw_res->mon_scale; } -int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, +int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 unused, u32 rmid, enum resctrl_event_id eventid, u64 *val, void *ignored) { - struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); + struct rdt_hw_l3_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); struct arch_mbm_state *am; u64 msr_val; u32 prmid; @@ -308,11 +308,11 @@ static int __cntr_id_read(u32 cntr_id, u64 *val) return 0; } -void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 unused, u32 rmid, int cntr_id, enum resctrl_event_id eventid) { - struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); + struct rdt_hw_l3_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); struct arch_mbm_state *am; am = get_arch_mbm_state(hw_dom, rmid, eventid); @@ -324,7 +324,7 @@ void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, } } -int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_mon_domain *d, +int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 unused, u32 rmid, int cntr_id, enum resctrl_event_id eventid, u64 *val) { @@ -354,7 +354,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_mon_domain *d, * must adjust RMID counter numbers based on SNC node. See * logical_rmid_to_physical_rmid() for code that does this. */ -void arch_mon_domain_online(struct rdt_resource *r, struct rdt_mon_domain *d) +void arch_mon_domain_online(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { if (snc_nodes_per_l3_cache > 1) msr_clear_bit(MSR_RMID_SNC_CONFIG, 0); @@ -515,7 +515,7 @@ static void resctrl_abmc_set_one_amd(void *arg) */ static void _resctrl_abmc_enable(struct rdt_resource *r, bool enable) { - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; lockdep_assert_cpus_held(); @@ -554,11 +554,11 @@ static void resctrl_abmc_config_one_amd(void *info) /* * Send an IPI to the domain to assign the counter to RMID, event pair. */ -void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, enum resctrl_event_id evtid, u32 rmid, u32 closid, u32 cntr_id, bool assign) { - struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); + struct rdt_hw_l3_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); union l3_qos_abmc_cfg abmc_cfg = { 0 }; struct arch_mbm_state *am; diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c index 0d0ef54fc4de1..eeb12ec594c7b 100644 --- a/fs/resctrl/ctrlmondata.c +++ b/fs/resctrl/ctrlmondata.c @@ -547,7 +547,7 @@ struct rdt_domain_hdr *resctrl_find_domain(struct list_head *h, int id, } void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, - struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, + struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, cpumask_t *cpumask, int evtid, int first) { int cpu; @@ -596,9 +596,9 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg) struct kernfs_open_file *of = m->private; enum resctrl_res_level resid; enum resctrl_event_id evtid; + struct rdt_l3_mon_domain *d; struct rdt_domain_hdr *hdr; struct rmid_read rr = {0}; - struct rdt_mon_domain *d; struct rdtgroup *rdtgrp; int domid, cpu, ret = 0; struct rdt_resource *r; @@ -653,7 +653,7 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg) ret = -ENOENT; goto out; } - d = container_of(hdr, struct rdt_mon_domain, hdr); + d = container_of(hdr, struct rdt_l3_mon_domain, hdr); mon_event_read(&rr, r, d, rdtgrp, &d->hdr.cpu_mask, evtid, false); } diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h index cf1fd82dc5a99..73cc4e9d5e810 100644 --- a/fs/resctrl/internal.h +++ b/fs/resctrl/internal.h @@ -123,7 +123,7 @@ struct mon_data { struct rmid_read { struct rdtgroup *rgrp; struct rdt_resource *r; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; enum resctrl_event_id evtid; bool first; struct cacheinfo *ci; @@ -362,12 +362,12 @@ void mon_event_count(void *info); int rdtgroup_mondata_show(struct seq_file *m, void *arg); void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, - struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, + struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, cpumask_t *cpumask, int evtid, int first); int resctrl_mon_resource_init(void); -void mbm_setup_overflow_handler(struct rdt_mon_domain *dom, +void mbm_setup_overflow_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu); @@ -375,14 +375,14 @@ void mbm_handle_overflow(struct work_struct *work); bool is_mba_sc(struct rdt_resource *r); -void cqm_setup_limbo_handler(struct rdt_mon_domain *dom, unsigned long delay_ms, +void cqm_setup_limbo_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu); void cqm_handle_limbo(struct work_struct *work); -bool has_busy_rmid(struct rdt_mon_domain *d); +bool has_busy_rmid(struct rdt_l3_mon_domain *d); -void __check_limbo(struct rdt_mon_domain *d, bool force_free); +void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free); void resctrl_file_fflags_init(const char *config, unsigned long fflags); diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index cdd5070d9ec71..9f0a863abc0d2 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -130,7 +130,7 @@ static void limbo_release_entry(struct rmid_entry *entry) * decrement the count. If the busy count gets to zero on an RMID, we * free the RMID */ -void __check_limbo(struct rdt_mon_domain *d, bool force_free) +void __check_limbo(struct rdt_l3_mon_domain *d, bool force_free) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); u32 idx_limit = resctrl_arch_system_num_rmid_idx(); @@ -193,7 +193,7 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free) resctrl_arch_mon_ctx_free(r, QOS_L3_OCCUP_EVENT_ID, arch_mon_ctx); } -bool has_busy_rmid(struct rdt_mon_domain *d) +bool has_busy_rmid(struct rdt_l3_mon_domain *d) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); @@ -294,7 +294,7 @@ int alloc_rmid(u32 closid) static void add_rmid_to_limbo(struct rmid_entry *entry) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; u32 idx; lockdep_assert_held(&rdtgroup_mutex); @@ -347,7 +347,7 @@ void free_rmid(u32 closid, u32 rmid) list_add_tail(&entry->list, &rmid_free_lru); } -static struct mbm_state *get_mbm_state(struct rdt_mon_domain *d, u32 closid, +static struct mbm_state *get_mbm_state(struct rdt_l3_mon_domain *d, u32 closid, u32 rmid, enum resctrl_event_id evtid) { u32 idx = resctrl_arch_rmid_idx_encode(closid, rmid); @@ -367,7 +367,7 @@ static struct mbm_state *get_mbm_state(struct rdt_mon_domain *d, u32 closid, * Return: * Valid counter ID on success, or -ENOENT on failure. */ -static int mbm_cntr_get(struct rdt_resource *r, struct rdt_mon_domain *d, +static int mbm_cntr_get(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { int cntr_id; @@ -394,7 +394,7 @@ static int mbm_cntr_get(struct rdt_resource *r, struct rdt_mon_domain *d, * Return: * Valid counter ID on success, or -ENOSPC on failure. */ -static int mbm_cntr_alloc(struct rdt_resource *r, struct rdt_mon_domain *d, +static int mbm_cntr_alloc(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { int cntr_id; @@ -413,7 +413,7 @@ static int mbm_cntr_alloc(struct rdt_resource *r, struct rdt_mon_domain *d, /* * mbm_cntr_free() - Clear the counter ID configuration details in the domain @d. */ -static void mbm_cntr_free(struct rdt_mon_domain *d, int cntr_id) +static void mbm_cntr_free(struct rdt_l3_mon_domain *d, int cntr_id) { memset(&d->cntr_cfg[cntr_id], 0, sizeof(*d->cntr_cfg)); } @@ -423,7 +423,7 @@ static int __l3_mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) int cpu = smp_processor_id(); u32 closid = rdtgrp->closid; u32 rmid = rdtgrp->mon.rmid; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; int cntr_id = -ENOENT; struct mbm_state *m; int err, ret; @@ -628,7 +628,7 @@ static struct rdt_ctrl_domain *get_ctrl_domain_from_cpu(int cpu, * throttle MSRs already have low percentage values. To avoid * unnecessarily restricting such rdtgroups, we also increase the bandwidth. */ -static void update_mba_bw(struct rdtgroup *rgrp, struct rdt_mon_domain *dom_mbm) +static void update_mba_bw(struct rdtgroup *rgrp, struct rdt_l3_mon_domain *dom_mbm) { u32 closid, rmid, cur_msr_val, new_msr_val; struct mbm_state *pmbm_data, *cmbm_data; @@ -696,7 +696,7 @@ static void update_mba_bw(struct rdtgroup *rgrp, struct rdt_mon_domain *dom_mbm) resctrl_arch_update_one(r_mba, dom_mba, closid, CDP_NONE, new_msr_val); } -static void mbm_update_one_event(struct rdt_resource *r, struct rdt_mon_domain *d, +static void mbm_update_one_event(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { struct rmid_read rr = {0}; @@ -728,7 +728,7 @@ static void mbm_update_one_event(struct rdt_resource *r, struct rdt_mon_domain * resctrl_arch_mon_ctx_free(rr.r, rr.evtid, rr.arch_mon_ctx); } -static void mbm_update(struct rdt_resource *r, struct rdt_mon_domain *d, +static void mbm_update(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp) { /* @@ -749,12 +749,12 @@ static void mbm_update(struct rdt_resource *r, struct rdt_mon_domain *d, void cqm_handle_limbo(struct work_struct *work) { unsigned long delay = msecs_to_jiffies(CQM_LIMBOCHECK_INTERVAL); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; cpus_read_lock(); mutex_lock(&rdtgroup_mutex); - d = container_of(work, struct rdt_mon_domain, cqm_limbo.work); + d = container_of(work, struct rdt_l3_mon_domain, cqm_limbo.work); __check_limbo(d, false); @@ -777,7 +777,7 @@ void cqm_handle_limbo(struct work_struct *work) * @exclude_cpu: Which CPU the handler should not run on, * RESCTRL_PICK_ANY_CPU to pick any CPU. */ -void cqm_setup_limbo_handler(struct rdt_mon_domain *dom, unsigned long delay_ms, +void cqm_setup_limbo_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu) { unsigned long delay = msecs_to_jiffies(delay_ms); @@ -794,7 +794,7 @@ void mbm_handle_overflow(struct work_struct *work) { unsigned long delay = msecs_to_jiffies(MBM_OVERFLOW_INTERVAL); struct rdtgroup *prgrp, *crgrp; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; struct list_head *head; struct rdt_resource *r; @@ -809,7 +809,7 @@ void mbm_handle_overflow(struct work_struct *work) goto out_unlock; r = resctrl_arch_get_resource(RDT_RESOURCE_L3); - d = container_of(work, struct rdt_mon_domain, mbm_over.work); + d = container_of(work, struct rdt_l3_mon_domain, mbm_over.work); list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) { mbm_update(r, d, prgrp); @@ -843,7 +843,7 @@ void mbm_handle_overflow(struct work_struct *work) * @exclude_cpu: Which CPU the handler should not run on, * RESCTRL_PICK_ANY_CPU to pick any CPU. */ -void mbm_setup_overflow_handler(struct rdt_mon_domain *dom, unsigned long delay_ms, +void mbm_setup_overflow_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu) { unsigned long delay = msecs_to_jiffies(delay_ms); @@ -1098,7 +1098,7 @@ ssize_t resctrl_mbm_assign_on_mkdir_write(struct kernfs_open_file *of, char *buf * mbm_cntr_free_all() - Clear all the counter ID configuration details in the * domain @d. Called when mbm_assign_mode is changed. */ -static void mbm_cntr_free_all(struct rdt_resource *r, struct rdt_mon_domain *d) +static void mbm_cntr_free_all(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { memset(d->cntr_cfg, 0, sizeof(*d->cntr_cfg) * r->mon.num_mbm_cntrs); } @@ -1107,7 +1107,7 @@ static void mbm_cntr_free_all(struct rdt_resource *r, struct rdt_mon_domain *d) * resctrl_reset_rmid_all() - Reset all non-architecture states for all the * supported RMIDs. */ -static void resctrl_reset_rmid_all(struct rdt_resource *r, struct rdt_mon_domain *d) +static void resctrl_reset_rmid_all(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); enum resctrl_event_id evt; @@ -1128,7 +1128,7 @@ static void resctrl_reset_rmid_all(struct rdt_resource *r, struct rdt_mon_domain * Assign the counter if @assign is true else unassign the counter. Reset the * associated non-architectural state. */ -static void rdtgroup_assign_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +static void rdtgroup_assign_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, enum resctrl_event_id evtid, u32 rmid, u32 closid, u32 cntr_id, bool assign) { @@ -1148,7 +1148,7 @@ static void rdtgroup_assign_cntr(struct rdt_resource *r, struct rdt_mon_domain * * Return: * 0 on success, < 0 on failure. */ -static int rdtgroup_alloc_assign_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +static int rdtgroup_alloc_assign_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { int cntr_id; @@ -1183,7 +1183,7 @@ static int rdtgroup_alloc_assign_cntr(struct rdt_resource *r, struct rdt_mon_dom * Return: * 0 on success, < 0 on failure. */ -static int rdtgroup_assign_cntr_event(struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, +static int rdtgroup_assign_cntr_event(struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { struct rdt_resource *r = resctrl_arch_get_resource(mevt->rid); @@ -1233,7 +1233,7 @@ void rdtgroup_assign_cntrs(struct rdtgroup *rdtgrp) * rdtgroup_free_unassign_cntr() - Unassign and reset the counter ID configuration * for the event pointed to by @mevt within the domain @d and resctrl group @rdtgrp. */ -static void rdtgroup_free_unassign_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +static void rdtgroup_free_unassign_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { int cntr_id; @@ -1254,7 +1254,7 @@ static void rdtgroup_free_unassign_cntr(struct rdt_resource *r, struct rdt_mon_d * the event structure @mevt from the domain @d and the group @rdtgrp. Unassign * the counters from all the domains if @d is NULL else unassign from @d. */ -static void rdtgroup_unassign_cntr_event(struct rdt_mon_domain *d, struct rdtgroup *rdtgrp, +static void rdtgroup_unassign_cntr_event(struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { struct rdt_resource *r = resctrl_arch_get_resource(mevt->rid); @@ -1329,7 +1329,7 @@ static int resctrl_parse_mem_transactions(char *tok, u32 *val) static void rdtgroup_update_cntr_event(struct rdt_resource *r, struct rdtgroup *rdtgrp, enum resctrl_event_id evtid) { - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; int cntr_id; list_for_each_entry(d, &r->mon_domains, hdr.list) { @@ -1435,7 +1435,7 @@ ssize_t resctrl_mbm_assign_mode_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct rdt_resource *r = rdt_kn_parent_priv(of->kn); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; int ret = 0; bool enable; @@ -1508,7 +1508,7 @@ int resctrl_num_mbm_cntrs_show(struct kernfs_open_file *of, struct seq_file *s, void *v) { struct rdt_resource *r = rdt_kn_parent_priv(of->kn); - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; bool sep = false; cpus_read_lock(); @@ -1532,7 +1532,7 @@ int resctrl_available_mbm_cntrs_show(struct kernfs_open_file *of, struct seq_file *s, void *v) { struct rdt_resource *r = rdt_kn_parent_priv(of->kn); - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; bool sep = false; u32 cntrs, i; int ret = 0; @@ -1573,7 +1573,7 @@ int resctrl_available_mbm_cntrs_show(struct kernfs_open_file *of, int mbm_L3_assignments_show(struct kernfs_open_file *of, struct seq_file *s, void *v) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; struct rdtgroup *rdtgrp; struct mon_evt *mevt; int ret = 0; @@ -1636,7 +1636,7 @@ static struct mon_evt *mbm_get_mon_event_by_name(struct rdt_resource *r, char *n return NULL; } -static int rdtgroup_modify_assign_state(char *assign, struct rdt_mon_domain *d, +static int rdtgroup_modify_assign_state(char *assign, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, struct mon_evt *mevt) { int ret = 0; @@ -1662,7 +1662,7 @@ static int rdtgroup_modify_assign_state(char *assign, struct rdt_mon_domain *d, static int resctrl_parse_mbm_assignment(struct rdt_resource *r, struct rdtgroup *rdtgrp, char *event, char *tok) { - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; unsigned long dom_id = 0; char *dom_str, *id_str; struct mon_evt *mevt; diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 7cfb0086451c5..207139a9072ed 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -1620,7 +1620,7 @@ static void mondata_config_read(struct resctrl_mon_config_info *mon_info) static int mbm_config_show(struct seq_file *s, struct rdt_resource *r, u32 evtid) { struct resctrl_mon_config_info mon_info; - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; bool sep = false; cpus_read_lock(); @@ -1668,7 +1668,7 @@ static int mbm_local_bytes_config_show(struct kernfs_open_file *of, } static void mbm_config_write_domain(struct rdt_resource *r, - struct rdt_mon_domain *d, u32 evtid, u32 val) + struct rdt_l3_mon_domain *d, u32 evtid, u32 val) { struct resctrl_mon_config_info mon_info = {0}; @@ -1710,7 +1710,7 @@ static int mon_config_write(struct rdt_resource *r, char *tok, u32 evtid) { char *dom_str = NULL, *id_str; unsigned long dom_id, val; - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; /* Walking r->domains, ensure it can't race with cpuhp */ lockdep_assert_cpus_held(); @@ -2718,7 +2718,7 @@ static int rdt_get_tree(struct fs_context *fc) { struct rdt_fs_context *ctx = rdt_fc2context(fc); unsigned long flags = RFTYPE_CTRL_BASE; - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; struct rdt_resource *r; int ret; @@ -3169,7 +3169,7 @@ static void mon_rmdir_one_subdir(struct kernfs_node *pkn, char *name, char *subn * when last domain being summed is removed. */ static void rmdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, - struct rdt_mon_domain *d) + struct rdt_l3_mon_domain *d) { struct rdtgroup *prgrp, *crgrp; char subname[32]; @@ -3189,7 +3189,7 @@ static void rmdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, } } -static int mon_add_all_files(struct kernfs_node *kn, struct rdt_mon_domain *d, +static int mon_add_all_files(struct kernfs_node *kn, struct rdt_l3_mon_domain *d, struct rdt_resource *r, struct rdtgroup *prgrp, bool do_sum) { @@ -3218,7 +3218,7 @@ static int mon_add_all_files(struct kernfs_node *kn, struct rdt_mon_domain *d, } static int mkdir_mondata_subdir(struct kernfs_node *parent_kn, - struct rdt_mon_domain *d, + struct rdt_l3_mon_domain *d, struct rdt_resource *r, struct rdtgroup *prgrp) { struct kernfs_node *kn, *ckn; @@ -3280,7 +3280,7 @@ static int mkdir_mondata_subdir(struct kernfs_node *parent_kn, * and "monitor" groups with given domain id. */ static void mkdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, - struct rdt_mon_domain *d) + struct rdt_l3_mon_domain *d) { struct kernfs_node *parent_kn; struct rdtgroup *prgrp, *crgrp; @@ -3302,7 +3302,7 @@ static int mkdir_mondata_subdir_alldom(struct kernfs_node *parent_kn, struct rdt_resource *r, struct rdtgroup *prgrp) { - struct rdt_mon_domain *dom; + struct rdt_l3_mon_domain *dom; int ret; /* Walking r->domains, ensure it can't race with cpuhp */ @@ -4170,7 +4170,7 @@ static void rdtgroup_setup_default(void) mutex_unlock(&rdtgroup_mutex); } -static void domain_destroy_mon_state(struct rdt_mon_domain *d) +static void domain_destroy_mon_state(struct rdt_l3_mon_domain *d) { int idx; @@ -4192,7 +4192,7 @@ void resctrl_offline_ctrl_domain(struct rdt_resource *r, struct rdt_ctrl_domain mutex_unlock(&rdtgroup_mutex); } -void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d) +void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { mutex_lock(&rdtgroup_mutex); @@ -4236,7 +4236,7 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d * * Returns 0 for success, or -ENOMEM. */ -static int domain_setup_mon_state(struct rdt_resource *r, struct rdt_mon_domain *d) +static int domain_setup_mon_state(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); size_t tsize = sizeof(*d->mbm_states[0]); @@ -4292,7 +4292,7 @@ int resctrl_online_ctrl_domain(struct rdt_resource *r, struct rdt_ctrl_domain *d return err; } -int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d) +int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { int err; @@ -4344,10 +4344,10 @@ static void clear_childcpus(struct rdtgroup *r, unsigned int cpu) } } -static struct rdt_mon_domain *get_mon_domain_from_cpu(int cpu, +static struct rdt_l3_mon_domain *get_mon_domain_from_cpu(int cpu, struct rdt_resource *r) { - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; lockdep_assert_cpus_held(); @@ -4363,7 +4363,7 @@ static struct rdt_mon_domain *get_mon_domain_from_cpu(int cpu, void resctrl_offline_cpu(unsigned int cpu) { struct rdt_resource *l3 = resctrl_arch_get_resource(RDT_RESOURCE_L3); - struct rdt_mon_domain *d; + struct rdt_l3_mon_domain *d; struct rdtgroup *rdtgrp; mutex_lock(&rdtgroup_mutex); diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h index a7d92718b653f..f9bed457fc5f1 100644 --- a/include/linux/resctrl.h +++ b/include/linux/resctrl.h @@ -169,7 +169,7 @@ struct mbm_cntr_cfg { }; /** - * struct rdt_mon_domain - group of CPUs sharing a resctrl monitor resource + * struct rdt_l3_mon_domain - group of CPUs sharing RDT_RESOURCE_L3 monitoring * @hdr: common header for different domain types * @ci_id: cache info id for this domain * @rmid_busy_llc: bitmap of which limbo RMIDs are above threshold @@ -183,7 +183,7 @@ struct mbm_cntr_cfg { * @cntr_cfg: array of assignable counters' configuration (indexed * by counter ID) */ -struct rdt_mon_domain { +struct rdt_l3_mon_domain { struct rdt_domain_hdr hdr; unsigned int ci_id; unsigned long *rmid_busy_llc; @@ -355,10 +355,10 @@ struct resctrl_cpu_defaults { }; struct resctrl_mon_config_info { - struct rdt_resource *r; - struct rdt_mon_domain *d; - u32 evtid; - u32 mon_config; + struct rdt_resource *r; + struct rdt_l3_mon_domain *d; + u32 evtid; + u32 mon_config; }; /** @@ -495,9 +495,9 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d, u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d, u32 closid, enum resctrl_conf_type type); int resctrl_online_ctrl_domain(struct rdt_resource *r, struct rdt_ctrl_domain *d); -int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d); +int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_l3_mon_domain *d); void resctrl_offline_ctrl_domain(struct rdt_resource *r, struct rdt_ctrl_domain *d); -void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_mon_domain *d); +void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_l3_mon_domain *d); void resctrl_online_cpu(unsigned int cpu); void resctrl_offline_cpu(unsigned int cpu); @@ -526,7 +526,7 @@ void resctrl_offline_cpu(unsigned int cpu); * Return: * 0 on success, or -EIO, -EINVAL etc on error. */ -int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, +int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 closid, u32 rmid, enum resctrl_event_id eventid, u64 *val, void *arch_mon_ctx); @@ -573,7 +573,7 @@ struct rdt_domain_hdr *resctrl_find_domain(struct list_head *h, int id, * * This can be called from any CPU. */ -void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_mon_domain *d, +void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 closid, u32 rmid, enum resctrl_event_id eventid); @@ -586,7 +586,7 @@ void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_mon_domain *d, * * This can be called from any CPU. */ -void resctrl_arch_reset_rmid_all(struct rdt_resource *r, struct rdt_mon_domain *d); +void resctrl_arch_reset_rmid_all(struct rdt_resource *r, struct rdt_l3_mon_domain *d); /** * resctrl_arch_reset_all_ctrls() - Reset the control for each CLOSID to its @@ -612,7 +612,7 @@ void resctrl_arch_reset_all_ctrls(struct rdt_resource *r); * * This can be called from any CPU. */ -void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, enum resctrl_event_id evtid, u32 rmid, u32 closid, u32 cntr_id, bool assign); @@ -635,7 +635,7 @@ void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, * Return: * 0 on success, or -EIO, -EINVAL etc on error. */ -int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_mon_domain *d, +int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 closid, u32 rmid, int cntr_id, enum resctrl_event_id eventid, u64 *val); @@ -650,7 +650,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_mon_domain *d, * * This can be called from any CPU. */ -void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_mon_domain *d, +void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d, u32 closid, u32 rmid, int cntr_id, enum resctrl_event_id eventid); From 8c5925f0fa128aae727da67416997911be39d834 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 27 Jul 2026 19:34:17 -0400 Subject: [PATCH 2874/3297] x86,fs/resctrl: Rename some L3 specific functions [ Upstream commit 9c214d10c50990c7a61b95887493df9ae713eec5 ] With the arrival of monitor events tied to new domains associated with a different resource it would be clearer if the L3 resource specific functions are more accurately named. Rename three groups of functions: Functions that allocate/free architecture per-RMID MBM state information: arch_domain_mbm_alloc() -> l3_mon_domain_mbm_alloc() mon_domain_free() -> l3_mon_domain_free() Functions that allocate/free filesystem per-RMID MBM state information: domain_setup_mon_state() -> domain_setup_l3_mon_state() domain_destroy_mon_state() -> domain_destroy_l3_mon_state() Initialization/exit: rdt_get_mon_l3_config() -> rdt_get_l3_mon_config() resctrl_mon_resource_init() -> resctrl_l3_mon_resource_init() resctrl_mon_resource_exit() -> resctrl_l3_mon_resource_exit() Ensure kernel-doc descriptions of these functions' return values are present and correctly formatted. Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Reinette Chatre Link: https://lore.kernel.org/20251217172121.12030-1-tony.luck@intel.com Stable-dep-of: 52fce648607e ("fs/resctrl: Fix use-after-free during unmount") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/resctrl/core.c | 20 +++++++++++--------- arch/x86/kernel/cpu/resctrl/internal.h | 2 +- arch/x86/kernel/cpu/resctrl/monitor.c | 2 +- fs/resctrl/internal.h | 6 +++--- fs/resctrl/monitor.c | 8 ++++---- fs/resctrl/rdtgroup.c | 24 ++++++++++++------------ 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c index 0942344e2f7f7..f9025779292a8 100644 --- a/arch/x86/kernel/cpu/resctrl/core.c +++ b/arch/x86/kernel/cpu/resctrl/core.c @@ -363,7 +363,7 @@ static void ctrl_domain_free(struct rdt_hw_ctrl_domain *hw_dom) kfree(hw_dom); } -static void mon_domain_free(struct rdt_hw_l3_mon_domain *hw_dom) +static void l3_mon_domain_free(struct rdt_hw_l3_mon_domain *hw_dom) { int idx; @@ -396,11 +396,13 @@ static int domain_setup_ctrlval(struct rdt_resource *r, struct rdt_ctrl_domain * } /** - * arch_domain_mbm_alloc() - Allocate arch private storage for the MBM counters + * l3_mon_domain_mbm_alloc() - Allocate arch private storage for the MBM counters * @num_rmid: The size of the MBM counter array * @hw_dom: The domain that owns the allocated arrays + * + * Return: 0 for success, or -ENOMEM. */ -static int arch_domain_mbm_alloc(u32 num_rmid, struct rdt_hw_l3_mon_domain *hw_dom) +static int l3_mon_domain_mbm_alloc(u32 num_rmid, struct rdt_hw_l3_mon_domain *hw_dom) { size_t tsize = sizeof(*hw_dom->arch_mbm_states[0]); enum resctrl_event_id eventid; @@ -536,7 +538,7 @@ static void domain_add_cpu_mon(int cpu, struct rdt_resource *r) ci = get_cpu_cacheinfo_level(cpu, RESCTRL_L3_CACHE); if (!ci) { pr_warn_once("Can't find L3 cache for CPU:%d resource %s\n", cpu, r->name); - mon_domain_free(hw_dom); + l3_mon_domain_free(hw_dom); return; } d->ci_id = ci->id; @@ -548,8 +550,8 @@ static void domain_add_cpu_mon(int cpu, struct rdt_resource *r) arch_mon_domain_online(r, d); - if (arch_domain_mbm_alloc(r->mon.num_rmid, hw_dom)) { - mon_domain_free(hw_dom); + if (l3_mon_domain_mbm_alloc(r->mon.num_rmid, hw_dom)) { + l3_mon_domain_free(hw_dom); return; } @@ -559,7 +561,7 @@ static void domain_add_cpu_mon(int cpu, struct rdt_resource *r) if (err) { list_del_rcu(&d->hdr.list); synchronize_rcu(); - mon_domain_free(hw_dom); + l3_mon_domain_free(hw_dom); } } @@ -650,7 +652,7 @@ static void domain_remove_cpu_mon(int cpu, struct rdt_resource *r) resctrl_offline_mon_domain(r, d); list_del_rcu(&d->hdr.list); synchronize_rcu(); - mon_domain_free(hw_dom); + l3_mon_domain_free(hw_dom); return; } @@ -897,7 +899,7 @@ static __init bool get_rdt_mon_resources(void) if (!ret) return false; - return !rdt_get_mon_l3_config(r); + return !rdt_get_l3_mon_config(r); } static __init void __check_quirks_intel(void) diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h index 5b3ccebdd39c9..6c9e75440c10e 100644 --- a/arch/x86/kernel/cpu/resctrl/internal.h +++ b/arch/x86/kernel/cpu/resctrl/internal.h @@ -211,7 +211,7 @@ union l3_qos_abmc_cfg { void rdt_ctrl_update(void *arg); -int rdt_get_mon_l3_config(struct rdt_resource *r); +int rdt_get_l3_mon_config(struct rdt_resource *r); bool rdt_cpu_has(int flag); diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index d917a61b4e4d2..9fc3adb4b232c 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -423,7 +423,7 @@ static __init int snc_get_config(void) return ret; } -int __init rdt_get_mon_l3_config(struct rdt_resource *r) +int __init rdt_get_l3_mon_config(struct rdt_resource *r) { unsigned int mbm_offset = boot_cpu_data.x86_cache_mbm_width_offset; struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h index 73cc4e9d5e810..2b7be96d3db84 100644 --- a/fs/resctrl/internal.h +++ b/fs/resctrl/internal.h @@ -355,7 +355,9 @@ int alloc_rmid(u32 closid); void free_rmid(u32 closid, u32 rmid); -void resctrl_mon_resource_exit(void); +int resctrl_l3_mon_resource_init(void); + +void resctrl_l3_mon_resource_exit(void); void mon_event_count(void *info); @@ -365,8 +367,6 @@ void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, struct rdt_l3_mon_domain *d, struct rdtgroup *rdtgrp, cpumask_t *cpumask, int evtid, int first); -int resctrl_mon_resource_init(void); - void mbm_setup_overflow_handler(struct rdt_l3_mon_domain *dom, unsigned long delay_ms, int exclude_cpu); diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 9f0a863abc0d2..fcc155eb5cac3 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -1758,7 +1758,7 @@ ssize_t mbm_L3_assignments_write(struct kernfs_open_file *of, char *buf, } /** - * resctrl_mon_resource_init() - Initialise global monitoring structures. + * resctrl_l3_mon_resource_init() - Initialise global monitoring structures. * * Allocate and initialise global monitor resources that do not belong to a * specific domain. i.e. the rmid_ptrs[] used for the limbo and free lists. @@ -1767,9 +1767,9 @@ ssize_t mbm_L3_assignments_write(struct kernfs_open_file *of, char *buf, * Resctrl's cpuhp callbacks may be called before this point to bring a domain * online. * - * Returns 0 for success, or -ENOMEM. + * Return: 0 for success, or -ENOMEM. */ -int resctrl_mon_resource_init(void) +int resctrl_l3_mon_resource_init(void) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); int ret; @@ -1819,7 +1819,7 @@ int resctrl_mon_resource_init(void) return 0; } -void resctrl_mon_resource_exit(void) +void resctrl_l3_mon_resource_exit(void) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 207139a9072ed..e9dcfda6f67fc 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -4170,7 +4170,7 @@ static void rdtgroup_setup_default(void) mutex_unlock(&rdtgroup_mutex); } -static void domain_destroy_mon_state(struct rdt_l3_mon_domain *d) +static void domain_destroy_l3_mon_state(struct rdt_l3_mon_domain *d) { int idx; @@ -4218,13 +4218,13 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_l3_mon_domain cancel_delayed_work(&d->cqm_limbo); } - domain_destroy_mon_state(d); + domain_destroy_l3_mon_state(d); mutex_unlock(&rdtgroup_mutex); } /** - * domain_setup_mon_state() - Initialise domain monitoring structures. + * domain_setup_l3_mon_state() - Initialise domain monitoring structures. * @r: The resource for the newly online domain. * @d: The newly online domain. * @@ -4232,11 +4232,11 @@ void resctrl_offline_mon_domain(struct rdt_resource *r, struct rdt_l3_mon_domain * Called when the first CPU of a domain comes online, regardless of whether * the filesystem is mounted. * During boot this may be called before global allocations have been made by - * resctrl_mon_resource_init(). + * resctrl_l3_mon_resource_init(). * - * Returns 0 for success, or -ENOMEM. + * Return: 0 for success, or -ENOMEM. */ -static int domain_setup_mon_state(struct rdt_resource *r, struct rdt_l3_mon_domain *d) +static int domain_setup_l3_mon_state(struct rdt_resource *r, struct rdt_l3_mon_domain *d) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); size_t tsize = sizeof(*d->mbm_states[0]); @@ -4298,7 +4298,7 @@ int resctrl_online_mon_domain(struct rdt_resource *r, struct rdt_l3_mon_domain * mutex_lock(&rdtgroup_mutex); - err = domain_setup_mon_state(r, d); + err = domain_setup_l3_mon_state(r, d); if (err) goto out_unlock; @@ -4413,13 +4413,13 @@ int resctrl_init(void) thread_throttle_mode_init(); - ret = resctrl_mon_resource_init(); + ret = resctrl_l3_mon_resource_init(); if (ret) return ret; ret = sysfs_create_mount_point(fs_kobj, "resctrl"); if (ret) { - resctrl_mon_resource_exit(); + resctrl_l3_mon_resource_exit(); return ret; } @@ -4454,7 +4454,7 @@ int resctrl_init(void) cleanup_mountpoint: sysfs_remove_mount_point(fs_kobj, "resctrl"); - resctrl_mon_resource_exit(); + resctrl_l3_mon_resource_exit(); return ret; } @@ -4490,7 +4490,7 @@ static bool resctrl_online_domains_exist(void) * When called by the architecture code, all CPUs and resctrl domains must be * offline. This ensures the limbo and overflow handlers are not scheduled to * run, meaning the data structures they access can be freed by - * resctrl_mon_resource_exit(). + * resctrl_l3_mon_resource_exit(). * * After resctrl_exit() returns, the architecture code should return an * error from all resctrl_arch_ functions that can do this. @@ -4517,5 +4517,5 @@ void resctrl_exit(void) * it can be used to umount resctrl. */ - resctrl_mon_resource_exit(); + resctrl_l3_mon_resource_exit(); } From 682d3c2cd20e023bf90d2eb3cf216047e5f0d53e Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 27 Jul 2026 19:34:18 -0400 Subject: [PATCH 2875/3297] fs/resctrl: Move allocation/free of closid_num_dirty_rmid[] [ Upstream commit ee7f6af79f0916b6c49e15edd4cba020b3e4c4ac ] closid_num_dirty_rmid[] and rmid_ptrs[] are allocated together during resctrl initialization and freed together during resctrl exit. Telemetry events are enumerated on resctrl mount so only at resctrl mount will the number of RMID supported by all monitoring resources and needed as size for rmid_ptrs[] be known. Separate closid_num_dirty_rmid[] and rmid_ptrs[] allocation and free in preparation for rmid_ptrs[] to be allocated on resctrl mount. Keep the rdtgroup_mutex protection around the allocation and free of closid_num_dirty_rmid[] as ARM needs this to guarantee memory ordering. Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Reinette Chatre Link: https://lore.kernel.org/20251217172121.12030-1-tony.luck@intel.com Stable-dep-of: 52fce648607e ("fs/resctrl: Fix use-after-free during unmount") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/monitor.c | 79 ++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index fcc155eb5cac3..cefa15ce2823a 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -865,36 +865,14 @@ void mbm_setup_overflow_handler(struct rdt_l3_mon_domain *dom, unsigned long del static int dom_data_init(struct rdt_resource *r) { u32 idx_limit = resctrl_arch_system_num_rmid_idx(); - u32 num_closid = resctrl_arch_get_num_closid(r); struct rmid_entry *entry = NULL; int err = 0, i; u32 idx; mutex_lock(&rdtgroup_mutex); - if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { - u32 *tmp; - - /* - * If the architecture hasn't provided a sanitised value here, - * this may result in larger arrays than necessary. Resctrl will - * use a smaller system wide value based on the resources in - * use. - */ - tmp = kcalloc(num_closid, sizeof(*tmp), GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto out_unlock; - } - - closid_num_dirty_rmid = tmp; - } rmid_ptrs = kcalloc(idx_limit, sizeof(struct rmid_entry), GFP_KERNEL); if (!rmid_ptrs) { - if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { - kfree(closid_num_dirty_rmid); - closid_num_dirty_rmid = NULL; - } err = -ENOMEM; goto out_unlock; } @@ -930,11 +908,6 @@ static void dom_data_exit(struct rdt_resource *r) if (!r->mon_capable) goto out_unlock; - if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { - kfree(closid_num_dirty_rmid); - closid_num_dirty_rmid = NULL; - } - kfree(rmid_ptrs); rmid_ptrs = NULL; @@ -1757,6 +1730,45 @@ ssize_t mbm_L3_assignments_write(struct kernfs_open_file *of, char *buf, return ret ?: nbytes; } +static int closid_num_dirty_rmid_alloc(struct rdt_resource *r) +{ + if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { + u32 num_closid = resctrl_arch_get_num_closid(r); + u32 *tmp; + + /* For ARM memory ordering access to closid_num_dirty_rmid */ + mutex_lock(&rdtgroup_mutex); + + /* + * If the architecture hasn't provided a sanitised value here, + * this may result in larger arrays than necessary. Resctrl will + * use a smaller system wide value based on the resources in + * use. + */ + tmp = kcalloc(num_closid, sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + mutex_unlock(&rdtgroup_mutex); + return -ENOMEM; + } + + closid_num_dirty_rmid = tmp; + + mutex_unlock(&rdtgroup_mutex); + } + + return 0; +} + +static void closid_num_dirty_rmid_free(void) +{ + if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) { + mutex_lock(&rdtgroup_mutex); + kfree(closid_num_dirty_rmid); + closid_num_dirty_rmid = NULL; + mutex_unlock(&rdtgroup_mutex); + } +} + /** * resctrl_l3_mon_resource_init() - Initialise global monitoring structures. * @@ -1777,10 +1789,16 @@ int resctrl_l3_mon_resource_init(void) if (!r->mon_capable) return 0; - ret = dom_data_init(r); + ret = closid_num_dirty_rmid_alloc(r); if (ret) return ret; + ret = dom_data_init(r); + if (ret) { + closid_num_dirty_rmid_free(); + return ret; + } + if (resctrl_arch_is_evt_configurable(QOS_L3_MBM_TOTAL_EVENT_ID)) { mon_event_all[QOS_L3_MBM_TOTAL_EVENT_ID].configurable = true; resctrl_file_fflags_init("mbm_total_bytes_config", @@ -1823,5 +1841,10 @@ void resctrl_l3_mon_resource_exit(void) { struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3); + if (!r->mon_capable) + return; + + closid_num_dirty_rmid_free(); + dom_data_exit(r); } From d48cf914c739e902ab2db228f6cd86e08fc5b869 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 27 Jul 2026 19:34:19 -0400 Subject: [PATCH 2876/3297] fs/resctrl: Move RMID initialization to first mount [ Upstream commit d0891647fbc6e931f27517364cbc4ee1811d76db ] L3 monitor features are enumerated during resctrl initialization and rmid_ptrs[] that tracks all RMIDs and depends on the number of supported RMIDs is allocated during this time. Telemetry monitor features are enumerated during first resctrl mount and may support a different number of RMIDs compared to L3 monitor features. Delay allocation and initialization of rmid_ptrs[] until first mount. Since the number of RMIDs cannot change on later mounts, keep the same set of rmid_ptrs[] until resctrl_exit(). This is required because the limbo handler keeps running after resctrl is unmounted and needs to access rmid_ptrs[] as it keeps tracking busy RMIDs after unmount. Rename routines to match what they now do: dom_data_init() -> setup_rmid_lru_list() dom_data_exit() -> free_rmid_lru_list() Signed-off-by: Tony Luck Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Reinette Chatre Link: https://lore.kernel.org/20251217172121.12030-1-tony.luck@intel.com Stable-dep-of: 52fce648607e ("fs/resctrl: Fix use-after-free during unmount") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/internal.h | 4 ++++ fs/resctrl/monitor.c | 54 ++++++++++++++++++++----------------------- fs/resctrl/rdtgroup.c | 5 ++++ 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h index 2b7be96d3db84..77f6e531e261b 100644 --- a/fs/resctrl/internal.h +++ b/fs/resctrl/internal.h @@ -351,6 +351,10 @@ int closids_supported(void); void closid_free(int closid); +int setup_rmid_lru_list(void); + +void free_rmid_lru_list(void); + int alloc_rmid(u32 closid); void free_rmid(u32 closid, u32 rmid); diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index cefa15ce2823a..55db47e969ce6 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -862,20 +862,29 @@ void mbm_setup_overflow_handler(struct rdt_l3_mon_domain *dom, unsigned long del schedule_delayed_work_on(cpu, &dom->mbm_over, delay); } -static int dom_data_init(struct rdt_resource *r) +int setup_rmid_lru_list(void) { - u32 idx_limit = resctrl_arch_system_num_rmid_idx(); struct rmid_entry *entry = NULL; - int err = 0, i; + u32 idx_limit; u32 idx; + int i; - mutex_lock(&rdtgroup_mutex); + if (!resctrl_arch_mon_capable()) + return 0; + /* + * Called on every mount, but the number of RMIDs cannot change + * after the first mount, so keep using the same set of rmid_ptrs[] + * until resctrl_exit(). Note that the limbo handler continues to + * access rmid_ptrs[] after resctrl is unmounted. + */ + if (rmid_ptrs) + return 0; + + idx_limit = resctrl_arch_system_num_rmid_idx(); rmid_ptrs = kcalloc(idx_limit, sizeof(struct rmid_entry), GFP_KERNEL); - if (!rmid_ptrs) { - err = -ENOMEM; - goto out_unlock; - } + if (!rmid_ptrs) + return -ENOMEM; for (i = 0; i < idx_limit; i++) { entry = &rmid_ptrs[i]; @@ -888,30 +897,24 @@ static int dom_data_init(struct rdt_resource *r) /* * RESCTRL_RESERVED_CLOSID and RESCTRL_RESERVED_RMID are special and * are always allocated. These are used for the rdtgroup_default - * control group, which will be setup later in resctrl_init(). + * control group, which was setup earlier in rdtgroup_setup_default(). */ idx = resctrl_arch_rmid_idx_encode(RESCTRL_RESERVED_CLOSID, RESCTRL_RESERVED_RMID); entry = __rmid_entry(idx); list_del(&entry->list); -out_unlock: - mutex_unlock(&rdtgroup_mutex); - - return err; + return 0; } -static void dom_data_exit(struct rdt_resource *r) +void free_rmid_lru_list(void) { - mutex_lock(&rdtgroup_mutex); - - if (!r->mon_capable) - goto out_unlock; + if (!resctrl_arch_mon_capable()) + return; + mutex_lock(&rdtgroup_mutex); kfree(rmid_ptrs); rmid_ptrs = NULL; - -out_unlock: mutex_unlock(&rdtgroup_mutex); } @@ -1773,7 +1776,8 @@ static void closid_num_dirty_rmid_free(void) * resctrl_l3_mon_resource_init() - Initialise global monitoring structures. * * Allocate and initialise global monitor resources that do not belong to a - * specific domain. i.e. the rmid_ptrs[] used for the limbo and free lists. + * specific domain. i.e. the closid_num_dirty_rmid[] used to find the CLOSID + * with the cleanest set of RMIDs. * Called once during boot after the struct rdt_resource's have been configured * but before the filesystem is mounted. * Resctrl's cpuhp callbacks may be called before this point to bring a domain @@ -1793,12 +1797,6 @@ int resctrl_l3_mon_resource_init(void) if (ret) return ret; - ret = dom_data_init(r); - if (ret) { - closid_num_dirty_rmid_free(); - return ret; - } - if (resctrl_arch_is_evt_configurable(QOS_L3_MBM_TOTAL_EVENT_ID)) { mon_event_all[QOS_L3_MBM_TOTAL_EVENT_ID].configurable = true; resctrl_file_fflags_init("mbm_total_bytes_config", @@ -1845,6 +1843,4 @@ void resctrl_l3_mon_resource_exit(void) return; closid_num_dirty_rmid_free(); - - dom_data_exit(r); } diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index e9dcfda6f67fc..260339c8df508 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -2732,6 +2732,10 @@ static int rdt_get_tree(struct fs_context *fc) goto out; } + ret = setup_rmid_lru_list(); + if (ret) + goto out; + ret = rdtgroup_setup_root(ctx); if (ret) goto out; @@ -4518,4 +4522,5 @@ void resctrl_exit(void) */ resctrl_l3_mon_resource_exit(); + free_rmid_lru_list(); } From 7b7bb07efe41bb646a93c4624aa2bb35df190342 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 27 Jul 2026 19:34:20 -0400 Subject: [PATCH 2877/3297] fs/resctrl: Fix use-after-free during unmount [ Upstream commit 52fce648607e0d6a76eeb443d78708c49df1c554 ] During unmount or failure teardown all mon_data structures that contain monitoring event file private data are freed after which kernfs nodes are removed. However, the RDT_DELETED flag is never set for the statically allocated default resource group. A concurrent reader of an event file associated with the default resource group may, after dropping kernfs active protection, block on rdtgroup_mutex while unmount proceeds to free the file private data and destroy the kernfs node without waiting for the reader. When the mutex is released, the reader wakes up, observes that RDT_DELETED is not set for the default group, and dereferences the already-freed file private data. The scenario can be depicted as follows: CPU0 CPU1 /* * Default resource group's * monitoring data accessible via * kernfs file with kernfs_node::priv * pointing to a struct mon_data. * User opens the file for reading. */ rdtgroup_mondata_show() /* arch encounters fatal error */ rdtgroup_kn_lock_live() resctrl_exit() atomic_inc(&rdtgroup_default.waitcount) cpus_read_lock() kernfs_break_active_protection(kn) mutex_lock(&rdtgroup_mutex) cpus_read_lock() resctrl_fs_teardown() mutex_lock(&rdtgroup_mutex) rmdir_all_sub() mon_put_kn_priv() /* Delete all mon_data structures */ rdtgroup_destroy_root() kernfs_destroy_root() rdtgroup_default.kn = NULL mutex_unlock(&rdtgroup_mutex) /* * rdtgroup_default.flags is empty so * rdtgroup_kn_lock_live() returns * &rdtgroup_default */ md = of->kn->priv; /* md points to freed mon_data */ Set RDT_DELETED for the default group unconditionally since the flag does not lead to the freeing of this statically allocated group. Do not allow a new resctrl mount if there are any waiters on default group of previous mount. A new mount will re-initialize the default group that would appear to waiters from previous mount as though the default group is accessible causing them to access the mon_data structures from the previous mount that have been removed. Fixes: 2a6566038544 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits") Closes: https://sashiko.dev/#/patchset/20260508182143.14592-1-tony.luck%40intel.com?part=2 [1] Reported-by: Sashiko Signed-off-by: Tony Luck Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Chen Yu Cc: Link: https://patch.msgid.link/49a2ca3ca688f27e1a646cf90e1dc69287021127.1783377598.git.reinette.chatre@intel.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/resctrl/rdtgroup.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 260339c8df508..08e26c0c9fb9c 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -581,14 +581,20 @@ static ssize_t rdtgroup_cpus_write(struct kernfs_open_file *of, * * On resource group creation via a mkdir, an extra kernfs_node reference is * taken to ensure that the rdtgroup structure remains accessible for the - * rdtgroup_kn_unlock() calls where it is removed. + * rdtgroup_kn_unlock() calls where it is removed. The default group is + * statically allocated: it does not have an extra reference but will have + * RDT_DELETED set on unmount to support safe access to its associated files + * via rdtgroup_kn_lock_live/rdtgroup_kn_unlock(). * - * Drop the extra reference here, then free the rdtgroup structure. + * For all but the default group: drop the extra reference, then free the + * rdtgroup structure. * * Return: void */ static void rdtgroup_remove(struct rdtgroup *rdtgrp) { + if (rdtgrp == &rdtgroup_default) + return; kernfs_put(rdtgrp->kn); kfree(rdtgrp); } @@ -2732,6 +2738,12 @@ static int rdt_get_tree(struct fs_context *fc) goto out; } + /* Avoid races from pending operations from a previous mount */ + if (atomic_read(&rdtgroup_default.waitcount) != 0) { + ret = -EBUSY; + goto out; + } + ret = setup_rmid_lru_list(); if (ret) goto out; @@ -3094,6 +3106,7 @@ static void resctrl_fs_teardown(void) mon_put_kn_priv(); rdt_pseudo_lock_release(); rdtgroup_default.mode = RDT_MODE_SHAREABLE; + rdtgroup_default.flags = RDT_DELETED; closid_exit(); schemata_list_destroy(); rdtgroup_destroy_root(); @@ -4148,6 +4161,7 @@ static int rdtgroup_setup_root(struct rdt_fs_context *ctx) ctx->kfc.root = rdt_root; rdtgroup_default.kn = kernfs_root_to_node(rdt_root); + rdtgroup_default.flags = 0; return 0; } From 6d13eaa13341a8f80aaf86f78591e1b1d393711d Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Mon, 27 Jul 2026 22:46:45 -0400 Subject: [PATCH 2878/3297] net: mana: Validate the packet length reported by the NIC [ Upstream commit 2e2a83b4998af4384e677d3b2ac08565274279bf ] Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted. Cc: stable@vger.kernel.org Reviewed-by: Haiyang Zhang Signed-off-by: Dexuan Cui Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Link: https://patch.msgid.link/20260702041237.617719-2-decui@microsoft.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/microsoft/mana/mana_en.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 62418f164dbac..adf25acb0d46f 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2160,6 +2160,19 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, rxbuf_oob = &rxq->rx_oobs[curr]; WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1); + if (unlikely(pktlen > rxq->datasize)) { + /* Increase it even if mana_rx_skb() isn't called. */ + rxq->rx_cq.work_done++; + + ++ndev->stats.rx_dropped; + netdev_warn_once(ndev, + "Dropped oversized RX packet: len=%u, datasize=%u\n", + pktlen, rxq->datasize); + + /* Reuse the RX buffer since rxbuf_oob is unchanged. */ + goto drop; + } + mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); /* Unsuccessful refill will have old_buf == NULL. From ae5ae3d5bfaa698cf689b86d593eb68b5d3e4342 Mon Sep 17 00:00:00 2001 From: Shradha Gupta Date: Mon, 27 Jul 2026 23:57:14 -0400 Subject: [PATCH 2879/3297] net: mana: Optimize irq affinity for low vcpu configs [ Upstream commit 5316394b1752f6cf3f9901e7fefdec1cd1d97fd3 ] Before the commit 755391121038 ("net: mana: Allocate MSI-X vectors dynamically"), all the MANA IRQs were assigned statically and together during early driver load. After this commit, the IRQ allocation for MANA was done in two phases. HWC IRQ allocated earlier and then, queue IRQs dynamically added at a later point. By this time, the IRQ weights on vCPUs can become imbalanced and if IRQ count is greater than the vCPU count the topology aware IRQ distribution logic in MANA can cause multiple MANA IRQs to land on the same vCPUs, while other sibling vCPUs have none (case 1). On SMP enabled, low-vCPU systems, this becomes a bigger problem as the softIRQ handling overhead of two IRQs on the same vCPUs becomes much more than their overheads if they were spread across sibling vCPUs. In such cases when many parallel TCP connections are tested, the throughput drops significantly. Fix the affinity assignment logic, in cases where the IRQ count is greater than the vCPU count and when IRQs are added dynamically, by utilizing all the vCPUs irrespective of their NUMA/core bindings (case 2). The results of setting the affinity and hint to NULL were also studied, and we observed that, with this logic if there are pre-existing IRQs allocated on the VM (apart from MANA), during MANA IRQs allocation, it leads to clustering of the MANA queue IRQs again (case 3). ======================================================= Case 1: without this patch ======================================================= 4 vcpu(2 cores), 5 MANA IRQs (1 HWC + 4 Queue) TYPE effective vCPU aff ======================================================= IRQ0: HWC 0 IRQ1: mana_q1 0 IRQ2: mana_q2 2 IRQ3: mana_q3 0 IRQ4: mana_q4 3 %soft on each vCPU(mpstat -P ALL 1) on receiver vCPU 0 1 2 3 ======================================================= pass 1: 38.85 0.03 24.89 24.65 pass 2: 39.15 0.03 24.57 25.28 pass 3: 40.36 0.03 23.20 23.17 ======================================================= Case 2: with this patch ======================================================= 4 vcpu(2 cores), 5 MANA IRQs (1 HWC + 4 Queue) TYPE effective vCPU aff ======================================================= IRQ0: HWC 0 IRQ1: mana_q1 0 IRQ2: mana_q2 1 IRQ3: mana_q3 2 IRQ4: mana_q4 3 %soft on each vCPU(mpstat -P ALL 1) on receiver vCPU 0 1 2 3 ======================================================= pass 1: 15.42 15.85 14.99 14.51 pass 2: 15.53 15.94 15.81 15.93 pass 3: 16.41 16.35 16.40 16.36 ======================================================= Case 3: with affinity set to NULL ======================================================= 4 vCPU(2 cores), 5 MANA IRQs (1 HWC + 4 Queue) TYPE effective vCPU aff ======================================================= IRQ0: HWC 0 IRQ1: mana_q1 2 IRQ2: mana_q2 3 IRQ3: mana_q3 2 IRQ4: mana_q4 3 ======================================================= Throughput Impact(in Gbps, same env) ======================================================= TCP conn with patch w/o patch aff NULL 20480 15.65 7.73 5.25 10240 15.63 8.93 5.77 8192 15.64 9.69 7.16 6144 15.64 13.16 9.33 4096 15.69 15.75 13.50 2048 15.69 15.83 13.61 1024 15.71 15.28 13.60 Fixes: 755391121038 ("net: mana: Allocate MSI-X vectors dynamically") Cc: stable@vger.kernel.org Co-developed-by: Erni Sri Satya Vennela Signed-off-by: Erni Sri Satya Vennela Signed-off-by: Shradha Gupta Reviewed-by: Haiyang Zhang Reviewed-by: Simon Horman Reviewed-by: Yury Norov Link: https://patch.msgid.link/20260624072138.1632849-1-shradhagupta@linux.microsoft.com Signed-off-by: Jakub Kicinski [ Kept 7.1's `int *irqs, irq, err, i;` declaration (minus the deleted `skip_first_cpu`) instead of upstream's `msi` variant, as this tree lacks the `mana_gd_get_gic()` refactor. ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../net/ethernet/microsoft/mana/gdma_main.c | 78 +++++++++++++++---- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 243f8d11be684..81cbc47de0c62 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -125,6 +125,8 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev) } else { /* If dynamic allocation is enabled we have already allocated * hwc msi + * Also, we make sure in this case the following is always true + * (num_msix_usable - 1 HWC) <= num_online_cpus() */ gc->num_msix_usable = min(resp.max_msix, num_online_cpus() + 1); } @@ -1587,8 +1589,8 @@ void mana_gd_free_res_map(struct gdma_resource *r) * do the same thing. */ -static int irq_setup(unsigned int *irqs, unsigned int len, int node, - bool skip_first_cpu) +static int mana_irq_setup_numa_aware(unsigned int *irqs, unsigned int len, + int node, bool skip_first_cpu) { const struct cpumask *next, *prev = cpu_none_mask; cpumask_var_t cpus __free(free_cpumask_var); @@ -1624,11 +1626,24 @@ static int irq_setup(unsigned int *irqs, unsigned int len, int node, return 0; } +/* must be called with cpus_read_lock() held */ +static void mana_irq_setup_linear(unsigned int *irqs, unsigned int len) +{ + int cpu; + + for_each_online_cpu(cpu) { + if (len == 0) + break; + + irq_set_affinity_and_hint(*irqs++, cpumask_of(cpu)); + len--; + } +} + static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec) { struct gdma_context *gc = pci_get_drvdata(pdev); struct gdma_irq_context *gic; - bool skip_first_cpu = false; int *irqs, irq, err, i; irqs = kmalloc_array(nvec, sizeof(int), GFP_KERNEL); @@ -1636,10 +1651,12 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec) return -ENOMEM; /* + * In this function, num_msix_usable = HWC IRQ + Queue IRQ. + * nvec is only Queue IRQ (HWC already setup). * While processing the next pci irq vector, we start with index 1, * as IRQ vector at index 0 is already processed for HWC. * However, the population of irqs array starts with index 0, to be - * further used in irq_setup() + * further used in mana_irq_setup_numa_aware() */ for (i = 1; i <= nvec; i++) { gic = kzalloc(sizeof(*gic), GFP_KERNEL); @@ -1669,18 +1686,51 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec) } /* - * When calling irq_setup() for dynamically added IRQs, if number of - * CPUs is more than or equal to allocated MSI-X, we need to skip the - * first CPU sibling group since they are already affinitized to HWC IRQ + * When calling mana_irq_setup_numa_aware() for dynamically added IRQs, + * if number of CPUs is more than or equal to allocated MSI-X, we need to + * skip the first CPU sibling group since they are already affinitized to + * HWC IRQ */ cpus_read_lock(); - if (gc->num_msix_usable <= num_online_cpus()) - skip_first_cpu = true; + if (gc->num_msix_usable <= num_online_cpus()) { + err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node, + true); + if (err) { + cpus_read_unlock(); + goto free_irq; + } + } else { + /* + * When num_msix_usable are more than num_online_cpus, our + * queue IRQs should be equal to num of online vCPUs. + * We try to make sure queue IRQs spread across all vCPUs. + * In such a case NUMA or CPU core affinity does not matter. + * Note: in this case the total mana IRQ should always be + * num_online_cpus + 1. The first HWC IRQ is already handled + * in HWC setup calls + * However, if CPUs went offline since num_msix_usable was + * computed, queue IRQs will be more than num_online_cpus(). + * In such cases remaining extra IRQs will retain their default + * affinity. + */ + int first_unassigned = num_online_cpus(); - err = irq_setup(irqs, nvec, gc->numa_node, skip_first_cpu); - if (err) { - cpus_read_unlock(); - goto free_irq; + if (nvec > first_unassigned) { + char buf[32]; + + if (first_unassigned == nvec - 1) + snprintf(buf, sizeof(buf), "%d", + first_unassigned); + else + snprintf(buf, sizeof(buf), "%d-%d", + first_unassigned, nvec - 1); + + dev_dbg(&pdev->dev, + "MANA IRQ indices #%s will retain the default CPU affinity\n", + buf); + } + + mana_irq_setup_linear(irqs, nvec); } cpus_read_unlock(); @@ -1766,7 +1816,7 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec) nvec -= 1; } - err = irq_setup(irqs, nvec, gc->numa_node, false); + err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node, false); if (err) { cpus_read_unlock(); goto free_irq; From 4467fa514482bbce82f73788943c815f3d126ab3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 28 Jul 2026 09:30:36 -0400 Subject: [PATCH 2880/3297] octeontx2-af: validate body pcifunc in rvu_mbox_handler_rep_event_notify [ Upstream commit 2156a29aecfffa2eb7c558255690084efbe9f3b0 ] rvu_mbox_handler_rep_event_notify() in drivers/net/ethernet/marvell/ octeontx2/af/rvu_rep.c queues a sender-controlled REP_EVENT_NOTIFY request body verbatim, and rvu_rep_up_notify() then forwards event->pcifunc (the nested body field, distinct from the AF-normalised header pcifunc) into rvu_get_pfvf(), rvu_get_pf() and the AF->PF mailbox device index without any bounds check. A VF attached to a PF that has been put into switchdev representor mode reaches this path: the VF mailbox handler otx2_pfvf_mbox_handler() forwards every message id including MBOX_MSG_REP_EVENT_NOTIFY to AF without an allowlist, and the AF dispatcher rewrites only msg->pcifunc, leaving struct rep_event::pcifunc attacker-controlled. The sibling rvu_mbox_handler_esw_cfg() refuses requests whose header pcifunc is not rvu->rep_pcifunc; this handler has no equivalent gate. An out-of-range body pcifunc selects an &rvu->pf[]/&rvu->hwvf[] element past the allocated array and, for RVU_EVENT_MAC_ADDR_CHANGE, turns into a six-byte attacker-chosen OOB ether_addr_copy() target inside the queued worker; KASAN reports a slab-out-of-bounds write in rvu_rep_wq_handler. Reject malformed requests at the handler entry by gating on is_pf_func_valid(), which is already the canonical PF/VF range check in this driver; expose it via rvu.h so callers in rvu_rep.c can use it instead of open-coding the same range arithmetic. Fixes: b8fea84a0468 ("octeontx2-pf: Add support to sync link state between representor and VFs") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260520154157.1439319-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Stable-dep-of: 8cdcf3d2caac ("octeontx2-af: cn10k: restrict VF LMTLINE sharing to its own PF") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 2 +- drivers/net/ethernet/marvell/octeontx2/af/rvu.h | 1 + drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index ad733f4da3c18..019bd49015c60 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -435,7 +435,7 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc) return &rvu->pf[rvu_get_pf(rvu->pdev, pcifunc)]; } -static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc) +bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc) { int pf, vf, nvfs; u64 cfg; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 914ba2b691ec8..c9ed5276690d3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -900,6 +900,7 @@ u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blkaddr); struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc); void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf); bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr); +bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc); bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype); int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot); int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c index 4415d0ce9aef4..d43c034b0264a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c @@ -97,6 +97,14 @@ int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req, { struct rep_evtq_ent *qentry; + /* The mailbox dispatcher normalises only the header pcifunc; the + * nested struct rep_event::pcifunc body field is sender-controlled + * and is later used by rvu_rep_up_notify() to index rvu->pf[] / + * rvu->hwvf[]. Reject out-of-range body selectors before queueing. + */ + if (!is_pf_func_valid(rvu, req->pcifunc)) + return -EINVAL; + qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC); if (!qentry) return -ENOMEM; From c73b8795b45f4ad5a95120d2e9b435ea4616e08e Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 28 Jul 2026 09:30:37 -0400 Subject: [PATCH 2881/3297] octeontx2-af: cn10k: restrict VF LMTLINE sharing to its own PF [ Upstream commit 8cdcf3d2caacdee7ddd363705fb4d93b0c1a0915 ] rvu_mbox_handler_lmtst_tbl_setup() uses req->base_pcifunc as a direct index into the LMT map table to read another function's LMTLINE physical base address and copy it into the caller's own LMT map table entry. The mailbox dispatcher authenticates req->hdr.pcifunc from the IRQ source, but req->base_pcifunc is a separate payload field and is not sanitized. Reject the request with -EPERM when a VF caller's base_pcifunc is not a valid function under its own PF. is_pf_func_valid() bounds the FUNC field to the PF's configured VF count, keeping the computed index inside the caller's own slot block. Fixes: 893ae97214c3 ("octeontx2-af: cn10k: Support configurable LMTST regions") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB78811656934E713B77DA6CEDAFE62@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c index d2163da28d18f..fa4ea1258d29d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cn10k.c @@ -178,6 +178,15 @@ int rvu_mbox_handler_lmtst_tbl_setup(struct rvu *rvu, * pcifunc (will be the one who is calling this mailbox). */ if (req->base_pcifunc) { + /* A VF is untrusted and must not redirect its LMTLINE to + * another PF's region, so confine VF callers to their own PF. + */ + if (is_vf(req->hdr.pcifunc) && + (!is_pf_func_valid(rvu, req->base_pcifunc) || + rvu_get_pf(rvu->pdev, req->hdr.pcifunc) != + rvu_get_pf(rvu->pdev, req->base_pcifunc))) + return -EPERM; + /* Calculating the LMT table index equivalent to primary * pcifunc. */ From a88c2a70ea0ad9739f06448fa1d974b09ead939e Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 28 Jul 2026 12:29:45 -0400 Subject: [PATCH 2882/3297] bootconfig: move xbc_snprint_cmdline() to lib/bootconfig.c [ Upstream commit 5a643e4623238e14b03d75ca0d4eda0645720cee ] Move xbc_snprint_cmdline() from init/main.c to lib/bootconfig.c so the function (and its xbc_namebuf scratch buffer) becomes part of the shared parser library. tools/bootconfig already compiles lib/bootconfig.c directly, which lets a follow-up patch reuse the same renderer in the userspace tool to convert a bootconfig file into a flat cmdline string at build time. No functional change. Link: https://lore.kernel.org/all/20260508-bootconfig_using_tools-v1-1-1132219aa773@debian.org/ Signed-off-by: Breno Leitao Signed-off-by: Masami Hiramatsu (Google) Stable-dep-of: dec4d8118c17 ("bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/bootconfig.h | 3 ++ init/main.c | 45 ------------------------------ lib/bootconfig.c | 56 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 45 deletions(-) diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h index 25df9260d206d..1f6e9c54a2a5e 100644 --- a/include/linux/bootconfig.h +++ b/include/linux/bootconfig.h @@ -265,6 +265,9 @@ static inline struct xbc_node * __init xbc_node_get_subkey(struct xbc_node *node int __init xbc_node_compose_key_after(struct xbc_node *root, struct xbc_node *node, char *buf, size_t size); +/* Render key/value pairs under @root as a flat cmdline string */ +int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root); + /** * xbc_node_compose_key() - Compose full key string of the XBC node * @node: An XBC node. diff --git a/init/main.c b/init/main.c index 048a625382429..e3ef1eb34aa3e 100644 --- a/init/main.c +++ b/init/main.c @@ -320,51 +320,6 @@ static void * __init get_boot_config_from_initrd(size_t *_size) #ifdef CONFIG_BOOT_CONFIG -static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata; - -#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0) - -static int __init xbc_snprint_cmdline(char *buf, size_t size, - struct xbc_node *root) -{ - struct xbc_node *knode, *vnode; - char *end = buf + size; - const char *val, *q; - int ret; - - xbc_node_for_each_key_value(root, knode, val) { - ret = xbc_node_compose_key_after(root, knode, - xbc_namebuf, XBC_KEYLEN_MAX); - if (ret < 0) - return ret; - - vnode = xbc_node_get_child(knode); - if (!vnode) { - ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf); - if (ret < 0) - return ret; - buf += ret; - continue; - } - xbc_array_for_each_value(vnode, val) { - /* - * For prettier and more readable /proc/cmdline, only - * quote the value when necessary, i.e. when it contains - * whitespace. - */ - q = strpbrk(val, " \t\r\n") ? "\"" : ""; - ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ", - xbc_namebuf, q, val, q); - if (ret < 0) - return ret; - buf += ret; - } - } - - return buf - (end - size); -} -#undef rest - /* Make an extra command line under given key word */ static char * __init xbc_make_cmdline(const char *key) { diff --git a/lib/bootconfig.c b/lib/bootconfig.c index 5d3802eba52a3..c149ee8eb55df 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -405,6 +405,62 @@ const char * __init xbc_node_find_next_key_value(struct xbc_node *root, return ""; /* No value key */ } +static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata; + +#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0) + +/** + * xbc_snprint_cmdline() - Render bootconfig keys under @root as a cmdline string + * @buf: Destination buffer (may be NULL when @size is 0 to query the length) + * @size: Size of @buf in bytes + * @root: Subtree root whose key=value pairs should be rendered + * + * Walk all key/value pairs under @root and emit them as a space-separated + * cmdline string into @buf. Values containing whitespace are quoted with + * double quotes. Returns the number of bytes that would be written if @buf + * were large enough (matching snprintf semantics), or a negative errno on + * failure. + */ +int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root) +{ + struct xbc_node *knode, *vnode; + char *end = buf + size; + const char *val, *q; + int ret; + + xbc_node_for_each_key_value(root, knode, val) { + ret = xbc_node_compose_key_after(root, knode, + xbc_namebuf, XBC_KEYLEN_MAX); + if (ret < 0) + return ret; + + vnode = xbc_node_get_child(knode); + if (!vnode) { + ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf); + if (ret < 0) + return ret; + buf += ret; + continue; + } + xbc_array_for_each_value(vnode, val) { + /* + * For prettier and more readable /proc/cmdline, only + * quote the value when necessary, i.e. when it contains + * whitespace. + */ + q = strpbrk(val, " \t\r\n") ? "\"" : ""; + ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ", + xbc_namebuf, q, val, q); + if (ret < 0) + return ret; + buf += ret; + } + } + + return buf - (end - size); +} +#undef rest + /* XBC parse and tree build */ static int __init xbc_init_node(struct xbc_node *node, char *data, uint32_t flag) From 9466dc5e377f009f1dc2d42d01a049a29bb029f5 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 28 Jul 2026 12:29:46 -0400 Subject: [PATCH 2883/3297] bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline() [ Upstream commit dec4d8118c179b3d12bca7e609054c6011c4f2ce ] xbc_snprint_cmdline() is meant to be called twice: first with buf=NULL, size=0 to probe the rendered length, then with a real buffer to fill it (the standard snprintf() two-pass pattern). The probe call makes the function compute "buf + size" (NULL + 0) and, on every iteration, advance "buf += ret" from that NULL base and pass the result back into snprintf(). Pointer arithmetic on a NULL pointer is undefined behavior. It is harmless in the in-kernel callers today, but the follow-up patches run this same code in the userspace tools/bootconfig parser at kernel build time, where host UBSan / FORTIFY_SOURCE abort the build. Track a running written length (size_t) instead of mutating @buf, and only form "buf + len" when @buf is non-NULL. snprintf(NULL, 0, ...) is itself well defined and returns the would-be length, so the two-pass "probe then fill" usage returns identical byte counts. Link: https://lore.kernel.org/all/20260626-bootconfig_using_tools-v7-1-24ab72139c29@debian.org/ Fixes: 51887d03aca1 ("bootconfig: init: Allow admin to use bootconfig for kernel command line") Cc: stable@vger.kernel.org Signed-off-by: Breno Leitao Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- lib/bootconfig.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/bootconfig.c b/lib/bootconfig.c index c149ee8eb55df..b4e8e17e29155 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -424,10 +424,18 @@ static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata; int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root) { struct xbc_node *knode, *vnode; - char *end = buf + size; const char *val, *q; + size_t len = 0; int ret; + /* + * Track the running written length rather than advancing @buf, so we + * never form "buf + size" or "buf += ret" while @buf is NULL (the + * size-probe call passes buf=NULL, size=0). NULL pointer arithmetic + * is undefined behavior and trips host UBSan / FORTIFY_SOURCE when + * this renderer runs at kernel build time. snprintf(NULL, 0, ...) + * itself is well defined and returns the would-be length. + */ xbc_node_for_each_key_value(root, knode, val) { ret = xbc_node_compose_key_after(root, knode, xbc_namebuf, XBC_KEYLEN_MAX); @@ -436,10 +444,11 @@ int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root) vnode = xbc_node_get_child(knode); if (!vnode) { - ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf); + ret = snprintf(buf ? buf + len : NULL, rest(len, size), + "%s ", xbc_namebuf); if (ret < 0) return ret; - buf += ret; + len += ret; continue; } xbc_array_for_each_value(vnode, val) { @@ -449,15 +458,15 @@ int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root) * whitespace. */ q = strpbrk(val, " \t\r\n") ? "\"" : ""; - ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ", - xbc_namebuf, q, val, q); + ret = snprintf(buf ? buf + len : NULL, rest(len, size), + "%s=%s%s%s ", xbc_namebuf, q, val, q); if (ret < 0) return ret; - buf += ret; + len += ret; } } - return buf - (end - size); + return len; } #undef rest From 4c1e8ccd8655ee8cf1bcb1b7dfee72c9fa941fd4 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Tue, 28 Jul 2026 20:30:49 -0400 Subject: [PATCH 2884/3297] ata: libata-core: Reject an invalid concurrent positioning ranges count [ Upstream commit 533a0b940f901c15e5cbbd4b5d66e871c209e8ce ] ata_dev_config_cpr() takes the number of range descriptors from buf[0] of the concurrent positioning ranges log (up to 255), which the device reports independently of the log size in the GPL directory. The count is then walked at a fixed 32-byte stride in two places with no bound: the log read here, and the INQUIRY VPD page B9h emitter, which writes one descriptor per range into the fixed 2048-byte ata_scsi_rbuf. A device reporting a count larger than its own log overflows the read buffer (up to 7704 bytes past a 512-byte slab), and a count above 62 overflows the response buffer on the emit side. Bound the count once, on probe, against both the log the device returned and the number of descriptors the VPD B9h response buffer can hold (ATA_DEV_MAX_CPR, derived from the rbuf size). Reject an out-of-range count with a warning; this keeps the emitter in bounds with no separate change there. Suggested-by: Damien Le Moal Fixes: fe22e1c2f705 ("libata: support concurrent positioning ranges log") Fixes: c745dfc541e7 ("libata: fix reading concurrent positioning ranges log") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: Niklas Cassel Signed-off-by: Damien Le Moal [ adapted `kzalloc_flex()` allocation to `kzalloc(struct_size(...), GFP_KERNEL)` and adjusted context offsets. ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 18 ++++++++++++++++++ drivers/ata/libata-scsi.c | 2 -- drivers/ata/libata.h | 9 +++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 22ce6accbc5bb..f0753f5e57a8e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2833,6 +2833,24 @@ static void ata_dev_config_cpr(struct ata_device *dev) if (!nr_cpr) goto out; + /* + * The device reports the number of CPR descriptors independently of the + * log size, and that count is also used to emit VPD page B9h into the + * fixed-size rbuf. Reject a count larger than what that buffer can hold + * (ATA_DEV_MAX_CPR) or larger than the log the device actually returned. + */ + if (nr_cpr > ATA_DEV_MAX_CPR) { + ata_dev_warn(dev, + "Too many concurrent positioning ranges\n"); + goto out; + } + + if (buf_len < 64 + (size_t)nr_cpr * 32) { + ata_dev_warn(dev, + "Invalid number of concurrent positioning ranges\n"); + goto out; + } + cpr_log = kzalloc(struct_size(cpr_log, cpr, nr_cpr), GFP_KERNEL); if (!cpr_log) goto out; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 46ef1ef6f8128..a7916b50cbfca 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -37,8 +37,6 @@ #include "libata.h" #include "libata-transport.h" -#define ATA_SCSI_RBUF_SIZE 2048 - static DEFINE_SPINLOCK(ata_scsi_rbuf_lock); static u8 ata_scsi_rbuf[ATA_SCSI_RBUF_SIZE]; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 0002994ddfc9b..56c4136b74179 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -144,6 +144,15 @@ static inline void ata_acpi_bind_dev(struct ata_device *dev) {} #endif /* libata-scsi.c */ +#define ATA_SCSI_RBUF_SIZE 2048 + +/* + * Maximum number of concurrent positioning ranges (CPR) supported. The ACS + * specifications allow up to 255, but we limit this to the number of CPR + * descriptors that fit in the rbuf buffer used to emit VPD page B9h. + */ +#define ATA_DEV_MAX_CPR min(255, ((ATA_SCSI_RBUF_SIZE - 64) / 32)) + extern struct ata_device *ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev); extern int ata_scsi_add_hosts(struct ata_host *host, From dc8347f263b21a5d2f32ca50f2c5fd3fce75a512 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 28 Jul 2026 22:30:25 -0400 Subject: [PATCH 2885/3297] net: ipa: fix SMEM state handle leaks in SMP2P init [ Upstream commit 96ca1e658ae459276292bd6d971ab5d8c7e0379a ] ipa_smp2p_init() acquires two Qualcomm SMEM state handles with qcom_smem_state_get(). However, neither the init error paths nor ipa_smp2p_exit() release them. Release both handles with qcom_smem_state_put() in the init error paths and in ipa_smp2p_exit(). Fixes: 530f9216a953 ("soc: qcom: ipa: AP/modem communications") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Larysa Zaremba Reviewed-by: Alex Elder Link: https://patch.msgid.link/20260624065955.2822765-1-haoxiang_li2024@163.com Signed-off-by: Jakub Kicinski [ kzalloc_obj() context line kept as kzalloc(sizeof(*smp2p), GFP_KERNEL) since ipa_smp2p.c was not yet converted in this tree ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ipa/ipa_smp2p.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/net/ipa/ipa_smp2p.c b/drivers/net/ipa/ipa_smp2p.c index fcaadd111a8a3..0c184f6f91c6f 100644 --- a/drivers/net/ipa/ipa_smp2p.c +++ b/drivers/net/ipa/ipa_smp2p.c @@ -234,19 +234,27 @@ ipa_smp2p_init(struct ipa *ipa, struct platform_device *pdev, bool modem_init) &valid_bit); if (IS_ERR(valid_state)) return PTR_ERR(valid_state); - if (valid_bit >= 32) /* BITS_PER_U32 */ - return -EINVAL; + if (valid_bit >= 32) { /* BITS_PER_U32 */ + ret = -EINVAL; + goto err_valid_state_put; + } enabled_state = qcom_smem_state_get(dev, "ipa-clock-enabled", &enabled_bit); - if (IS_ERR(enabled_state)) - return PTR_ERR(enabled_state); - if (enabled_bit >= 32) /* BITS_PER_U32 */ - return -EINVAL; + if (IS_ERR(enabled_state)) { + ret = PTR_ERR(enabled_state); + goto err_valid_state_put; + } + if (enabled_bit >= 32) { /* BITS_PER_U32 */ + ret = -EINVAL; + goto err_enabled_state_put; + } smp2p = kzalloc(sizeof(*smp2p), GFP_KERNEL); - if (!smp2p) - return -ENOMEM; + if (!smp2p) { + ret = -ENOMEM; + goto err_enabled_state_put; + } smp2p->ipa = ipa; @@ -291,6 +299,10 @@ ipa_smp2p_init(struct ipa *ipa, struct platform_device *pdev, bool modem_init) ipa->smp2p = NULL; mutex_destroy(&smp2p->mutex); kfree(smp2p); +err_enabled_state_put: + qcom_smem_state_put(enabled_state); +err_valid_state_put: + qcom_smem_state_put(valid_state); return ret; } @@ -307,6 +319,8 @@ void ipa_smp2p_exit(struct ipa *ipa) ipa_smp2p_power_release(ipa); ipa->smp2p = NULL; mutex_destroy(&smp2p->mutex); + qcom_smem_state_put(smp2p->enabled_state); + qcom_smem_state_put(smp2p->valid_state); kfree(smp2p); } From c389893bb40371143aaef6e12eb850448faa4f61 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 29 Jul 2026 14:02:07 -0400 Subject: [PATCH 2886/3297] pmdomain: imx93-blk-ctrl: convert to devm_* only [ Upstream commit 52becc142280eeef828d19f9cd01fd653b588786 ] Convert the driver to devm_ APIs only by making use of devm_add_action_or_reset() and devm_pm_runtime_enable() to simplify the probe error path and to drop the .remove() callback. This also ensures that the device release order equals the device probe error path order. Furthermore drop the dev_set_drvdata() usage since the only user was the .remove() callback which is removed by this commit. Reviewed-by: Frank Li Signed-off-by: Marco Felsch Signed-off-by: Ulf Hansson Stable-dep-of: 99611233f8cd ("pmdomain: imx93-blk-ctrl: Extract PHY as shared domain for DSI/CSI") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/imx/imx93-blk-ctrl.c | 66 ++++++++++++--------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c index e094fe5a42bf6..325711323ba4f 100644 --- a/drivers/pmdomain/imx/imx93-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c @@ -188,6 +188,20 @@ static int imx93_blk_ctrl_power_off(struct generic_pm_domain *genpd) return 0; } +static void imx93_release_genpd_provider(void *data) +{ + struct device_node *of_node = data; + + of_genpd_del_provider(of_node); +} + +static void imx93_release_pm_genpd(void *data) +{ + struct generic_pm_domain *genpd = data; + + pm_genpd_remove(genpd); +} + static struct lock_class_key blk_ctrl_genpd_lock_class; static int imx93_blk_ctrl_probe(struct platform_device *pdev) @@ -258,10 +272,8 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev) domain->clks[j].id = data->clk_names[j]; ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); - if (ret) { - dev_err_probe(dev, ret, "failed to get clock\n"); - goto cleanup_pds; - } + if (ret) + return dev_err_probe(dev, ret, "failed to get clock\n"); domain->genpd.name = data->name; domain->genpd.power_on = imx93_blk_ctrl_power_on; @@ -269,11 +281,12 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev) domain->bc = bc; ret = pm_genpd_init(&domain->genpd, NULL, true); - if (ret) { - dev_err_probe(dev, ret, "failed to init power domain\n"); - goto cleanup_pds; - } + if (ret) + return dev_err_probe(dev, ret, "failed to init power domain\n"); + ret = devm_add_action_or_reset(dev, imx93_release_pm_genpd, &domain->genpd); + if (ret) + return dev_err_probe(dev, ret, "failed to add pm_genpd release callback\n"); /* * We use runtime PM to trigger power on/off of the upstream GPC * domain, as a strict hierarchical parent/child power domain @@ -290,39 +303,19 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev) bc->onecell_data.domains[i] = &domain->genpd; } - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to enable pm-runtime\n"); ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); - if (ret) { - dev_err_probe(dev, ret, "failed to add power domain provider\n"); - goto cleanup_pds; - } + if (ret) + return dev_err_probe(dev, ret, "failed to add power domain provider\n"); - dev_set_drvdata(dev, bc); + ret = devm_add_action_or_reset(dev, imx93_release_genpd_provider, dev->of_node); + if (ret) + return dev_err_probe(dev, ret, "failed to add genpd_provider release callback\n"); return 0; - -cleanup_pds: - for (i--; i >= 0; i--) - pm_genpd_remove(&bc->domains[i].genpd); - - return ret; -} - -static void imx93_blk_ctrl_remove(struct platform_device *pdev) -{ - struct imx93_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); - int i; - - of_genpd_del_provider(pdev->dev.of_node); - - pm_runtime_disable(&pdev->dev); - - for (i = 0; i < bc->onecell_data.num_domains; i++) { - struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; - - pm_genpd_remove(&domain->genpd); - } } static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] = { @@ -457,7 +450,6 @@ MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match); static struct platform_driver imx93_blk_ctrl_driver = { .probe = imx93_blk_ctrl_probe, - .remove = imx93_blk_ctrl_remove, .driver = { .name = "imx93-blk-ctrl", .of_match_table = imx93_blk_ctrl_of_match, From 4fd5b33faf092ef2d09f730a7d9e49f9e976ac67 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Wed, 29 Jul 2026 14:02:08 -0400 Subject: [PATCH 2887/3297] pmdomain: imx93-blk-ctrl: Extract PHY as shared domain for DSI/CSI [ Upstream commit 99611233f8cda833169fa6487d5dacdf189e5cb0 ] The MIPI DSI and CSI domains share control bits for clock and reset, which can lead to incorrect behavior if one domain disables the shared resource while the other is still active. To fix the issue, introduce a shared MIPI PHY power domain to own the common resources and make DSI and CSI its subdomains. This ensures the shared bits are properly managed and not disabled while still in use. Fixes: e9aa77d413c9 ("soc: imx: add i.MX93 media blk ctrl driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Frank Li Reviewed-by: Peng Fan Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pmdomain/imx/imx93-blk-ctrl.c | 60 ++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c index 325711323ba4f..28688ca46517a 100644 --- a/drivers/pmdomain/imx/imx93-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c @@ -47,6 +47,8 @@ #define PRIO(X) (X) +#define BLK_CTRL_NO_PARENT UINT_MAX + struct imx93_blk_ctrl_domain; struct imx93_blk_ctrl { @@ -67,12 +69,18 @@ struct imx93_blk_ctrl_qos { u32 cfg_prio; }; +struct imx93_blk_ctrl_subdomain_link { + struct generic_pm_domain *parent; + struct generic_pm_domain *subdomain; +}; + struct imx93_blk_ctrl_domain_data { const char *name; const char * const *clk_names; int num_clks; u32 rst_mask; u32 clk_mask; + u32 parent; int num_qos; struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS]; }; @@ -202,6 +210,13 @@ static void imx93_release_pm_genpd(void *data) pm_genpd_remove(genpd); } +static void imx93_release_subdomain(void *data) +{ + struct imx93_blk_ctrl_subdomain_link *link = data; + + pm_genpd_remove_subdomain(link->parent, link->subdomain); +} + static struct lock_class_key blk_ctrl_genpd_lock_class; static int imx93_blk_ctrl_probe(struct platform_device *pdev) @@ -303,6 +318,34 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev) bc->onecell_data.domains[i] = &domain->genpd; } + for (i = 0; i < bc_data->num_domains; i++) { + struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; + const struct imx93_blk_ctrl_domain_data *data = domain->data; + struct imx93_blk_ctrl_subdomain_link *link; + + if (bc_data->skip_mask & BIT(i) || + data->parent == BLK_CTRL_NO_PARENT) + continue; + + link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + link->parent = &bc->domains[data->parent].genpd; + link->subdomain = &domain->genpd; + + ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd, + &domain->genpd); + if (ret) + return dev_err_probe(dev, ret, "failed to add subdomain %s\n", + domain->genpd.name); + + ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link); + if (ret) + return dev_err_probe(dev, ret, + "failed to add subdomain release callback\n"); + } + ret = devm_pm_runtime_enable(dev); if (ret) return dev_err_probe(dev, ret, "failed to enable pm-runtime\n"); @@ -323,8 +366,9 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .name = "mediablk-mipi-dsi", .clk_names = (const char *[]){ "dsi" }, .num_clks = 1, - .rst_mask = BIT(11) | BIT(12), - .clk_mask = BIT(11) | BIT(12), + .rst_mask = BIT(11), + .clk_mask = BIT(11), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_MIPI_CSI] = { .name = "mediablk-mipi-csi", @@ -332,6 +376,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(9) | BIT(10), .clk_mask = BIT(9) | BIT(10), + .parent = IMX93_MEDIABLK_PD_MIPI_PHY, }, [IMX93_MEDIABLK_PD_PXP] = { .name = "mediablk-pxp", @@ -339,6 +384,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(7) | BIT(8), .clk_mask = BIT(7) | BIT(8), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 2, .qos = { { @@ -360,6 +406,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 2, .rst_mask = BIT(4) | BIT(5) | BIT(6), .clk_mask = BIT(4) | BIT(5) | BIT(6), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 1, .qos = { { @@ -376,6 +423,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] .num_clks = 1, .rst_mask = BIT(2) | BIT(3), .clk_mask = BIT(2) | BIT(3), + .parent = BLK_CTRL_NO_PARENT, .num_qos = 4, .qos = { { @@ -401,6 +449,14 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] } } }, + [IMX93_MEDIABLK_PD_MIPI_PHY] = { + .name = "mediablk-mipi-phy", + .clk_names = NULL, + .num_clks = 0, + .rst_mask = BIT(12), + .clk_mask = BIT(12), + .parent = BLK_CTRL_NO_PARENT, + }, }; static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = { From 811b581fae651f6824a68e9108c9107eed809a8a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jul 2026 17:23:50 -0400 Subject: [PATCH 2888/3297] i3c: mipi-i3c-hci: Fix Hot-Join NACK [ Upstream commit fa9586bd77ada1e3861c7bef65f6bb9dcf8d9481 ] The MIPI I3C HCI host controller driver does not implement Hot-Join handling, yet Hot-Join response control defaults to allowing devices to Hot-Join the bus. Configure HC_CONTROL_HOT_JOIN_CTRL to NACK all Hot-Join attempts. Fixes: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260306072451.11131-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Stable-dep-of: c6396b835a5e ("i3c: mipi-i3c-hci: Fix handling of shared IRQs during early initialization") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/i3c/master/mipi-i3c-hci/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 75e6610cfd812..f2529315445f1 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -147,7 +147,8 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD) amd_set_resp_buf_thld(hci); - reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE); + /* Enable bus with Hot-Join disabled */ + reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL); dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL)); return 0; From deead12d2e650b4c1f97c50d4a5a2dc1117e8805 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jul 2026 17:23:51 -0400 Subject: [PATCH 2889/3297] i3c: mipi-i3c-hci: Fix handling of shared IRQs during early initialization [ Upstream commit c6396b835a5e599c4df656112140f065bb544a24 ] Shared interrupts may fire unexpectedly, including during periods when the controller is not yet fully initialized. Commit b9a15012a1452 ("i3c: mipi-i3c-hci: Add optional Runtime PM support") addressed this issue for the runtime-suspended state, but the same problem can also occur before the bus is enabled for the first time. Ensure the IRQ handler ignores interrupts until initialization is complete by making consistent use of the existing irq_inactive flag. The flag is now set to false immediately before enabling the bus. To guarantee correct ordering with respect to the IRQ handler, protect all transitions of irq_inactive with the same spinlock used inside the handler. Fixes: b8460480f62e1 ("i3c: mipi-i3c-hci: Allow for Multi-Bus Instances") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260306072451.11131-14-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/i3c/master/mipi-i3c-hci/core.c | 13 +++++++++++++ drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + 2 files changed, 14 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index f2529315445f1..1eeab337e5e53 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -147,6 +147,8 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD) amd_set_resp_buf_thld(hci); + WRITE_ONCE(hci->irq_inactive, false); + /* Enable bus with Hot-Join disabled */ reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL); dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL)); @@ -537,6 +539,15 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) irqreturn_t result = IRQ_NONE; u32 val; + /* + * The IRQ can be shared, so the handler may be called when the IRQ is + * due to a different device. That could happen before the controller + * has been initialized, so exit immediately if IRQs are not expected + * for this device. + */ + if (READ_ONCE(hci->irq_inactive)) + return IRQ_NONE; + val = reg_read(INTR_STATUS); reg_write(INTR_STATUS, val); dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val); @@ -776,6 +787,8 @@ static int i3c_hci_probe(struct platform_device *pdev) if (ret) return ret; + WRITE_ONCE(hci->irq_inactive, true); + irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler, IRQF_SHARED, NULL, hci); diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 32c8aecde9f76..5502ab0a3535d 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -46,6 +46,7 @@ struct i3c_hci { void *io_data; const struct hci_cmd_ops *cmd; spinlock_t lock; + bool irq_inactive; struct mutex control_mutex; atomic_t next_cmd_tid; u32 caps; From 4b6f1d6d5d07855bd1bb9e64922b049062138bfa Mon Sep 17 00:00:00 2001 From: SJ Park Date: Mon, 29 Jun 2026 20:52:19 -0700 Subject: [PATCH 2890/3297] mm/damon/core: validate ranges in damon_set_regions() commit 1292c0ecb1caefb8ca064a3639d5673991e8810c upstream. DAMON core logic assumes zero length regions don't exist. However, a few DAMON API callers including DAMON_SYSFS, DAMON_RECLAIM and DAMON_LRU_SORT allow users to set empty monitoring target regions. This could result in WARN_ONCE() on CONFIG_DAMON_DEBUG_SANITY enabled kernel, and divide-by-zero from damon_merge_two_regions(). For example, the WANR_ONCE() can be triggered like below. # grep DAMON_DEBUG_SANITY /boot/config-$(uname -r) # CONFIG_DAMON_DEBUG_SANITY=y # damo start # cd /sys/kernel/mm/damon/admin/kdamonds/0 # echo 0 > contexts/0/targets/0/regions/0/start # echo 0 > contexts/0/targets/0/regions/0/end # echo commit > state # dmesg [....] [ 73.705780] ------------[ cut here ]------------ [ 73.707552] start 0 >= end 0 [ 73.708452] WARNING: mm/damon/core.c:359 at damon_new_region+0x6e/0x80, CPU#1: kdamond.0/758 [...] All DAMON API callers eventually use damon_set_regions() to setup the regions. Add the validation logic in the function. Link: https://lore.kernel.org/20260630035221.146458-1-sj@kernel.org Fixes: 43b0536cb471 ("mm/damon: introduce DAMON-based Reclamation (DAMON_RECLAIM)") Signed-off-by: SJ Park Cc: Yang yingliang Cc: # 5.16.x Signed-off-by: Andrew Morton Signed-off-by: SJ Park Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index 3e221c888e302..4ab1ed472fbcc 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -215,6 +215,12 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, unsigned int i; int err; + for (i = 0; i < nr_ranges; i++) { + if (ALIGN_DOWN(ranges[i].start, min_sz_region) >= + ALIGN(ranges[i].end, min_sz_region)) + return -EINVAL; + } + /* Remove regions which are not in the new ranges */ damon_for_each_region_safe(r, next, t) { for (i = 0; i < nr_ranges; i++) { From 6ce0db97fb37ab8cf8596edca0e3de8618ab009a Mon Sep 17 00:00:00 2001 From: SJ Park Date: Fri, 3 Jul 2026 09:56:08 -0700 Subject: [PATCH 2891/3297] mm/damon/core: disallow overlapping input ranges for damon_set_regions() commit 954157679ec34661c2e87e7eb796104a797c32db upstream. damon_set_regions() assumes the input ranges are sorted by the address and don't overlap each other. Hence the assumption was initially to be explicitly validated. But commit 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting") has mistakenly removed the validation. This can make DAMON behave in unexpected ways. At the best, the monitoring results snapshot will just look weird since there will be overlapping regions. DAMOS will also work weirdly, applying the same action multiple times for overlapping regions, and make DAMOS quota weird. More seriously, depending on the setup and regions updates sequence, negative size regions can be made. It will trigger WARN_ONCE() if the kernel is built with CONFIG_DAMON_DEBUG_SANITY=y. Depending on the monitoring results, the negative size region can further trigger division by zero in damon_merge_two_regions(). Note that some of the consequences including the WARN_ONCE() and the divide by zero depend on commits that were introduced after the root cause commit 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting"). Fix the problems by checking the assumption and returning an error if the input ranges don't meet the assumption. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260703165610.92894-1-sj@kernel.org Link: https://lore.kernel.org/20260630041806.151124-1-sj@kernel.org [1] Fixes: 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting") Signed-off-by: SJ Park Cc: # 5.19.x Signed-off-by: Andrew Morton Signed-off-by: SJ Park Signed-off-by: Greg Kroah-Hartman --- mm/damon/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 4ab1ed472fbcc..70ac1f08753d1 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -213,12 +213,19 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, { struct damon_region *r, *next; unsigned int i; + unsigned long last_end; int err; for (i = 0; i < nr_ranges; i++) { - if (ALIGN_DOWN(ranges[i].start, min_sz_region) >= - ALIGN(ranges[i].end, min_sz_region)) + unsigned long start, end; + + start = ALIGN_DOWN(ranges[i].start, min_sz_region); + end = ALIGN(ranges[i].end, min_sz_region); + if (start >= end) + return -EINVAL; + if (i > 0 && last_end > start) return -EINVAL; + last_end = end; } /* Remove regions which are not in the new ranges */ From e94e820df37d487a92ca08e77bc8d2319267152b Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 15 Jun 2026 16:32:25 +0200 Subject: [PATCH 2892/3297] rust: allow `suspicious_runtime_symbol_definitions` lint for Rust >= 1.98 commit 608045a91d9176d66b2114d0006bc8b57dff2ca9 upstream. Starting with Rust 1.98.0 (expected 2026-08-20), Rust is introducing a couple new lints, `invalid_runtime_symbol_definitions` (deny-by-default) and `suspicious_runtime_symbol_definitions` (warn-by-default), which check the signature of items whose symbol name is a runtime symbol expected by `core`. Our build hits the second one, i.e. the warning: error: suspicious definition of the runtime `strlen` symbol used by the standard library --> rust/bindings/bindings_generated.rs:20018:5 | 20018 | pub fn strlen(s: *const ffi::c_char) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected `unsafe extern "C" fn(*const i8) -> usize` found `unsafe extern "C" fn(*const u8) -> usize` = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "strlen")]`, or `#[link_name = "strlen"]` = help: allow this lint if the signature is compatible = note: `-D suspicious-runtime-symbol-definitions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(suspicious_runtime_symbol_definitions)]` error: suspicious definition of the runtime `strlen` symbol used by the standard library --> rust/uapi/uapi_generated.rs:14236:5 | 14236 | pub fn strlen(s: *const ffi::c_char) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected `unsafe extern "C" fn(*const i8) -> usize` found `unsafe extern "C" fn(*const u8) -> usize` = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "strlen")]`, or `#[link_name = "strlen"]` = help: allow this lint if the signature is compatible = note: `-D suspicious-runtime-symbol-definitions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(suspicious_runtime_symbol_definitions)]` Thus `allow` the lint in `bindings` and `uapi`. A more targeted alternative to avoid `allow`ing it would be to pass `--blocklist-function strlen` to `bindgen`, but we would perhaps need to adjust if other C headers end up adding more (or Rust checking more). Since it is just the less critical one that we hit, and since eventually this should be properly fixed by getting upstream Rust to provide a flag like GCC/Clang's `-funsigned-char` [2][3], just `allow` it for now. Cc: Urgau Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Link: https://github.com/rust-lang/rust/pull/155521 [1] Link: https://github.com/rust-lang/rust/issues/138446 [2] Link: https://github.com/Rust-for-Linux/linux/issues/355 [3] Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl Reviewed-by: Tamir Duberstein Link: https://patch.msgid.link/20260615143225.471756-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- init/Kconfig | 3 +++ rust/bindings/lib.rs | 4 ++++ rust/uapi/lib.rs | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/init/Kconfig b/init/Kconfig index 0c25e78e10f47..6ce315af48c15 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -162,6 +162,9 @@ config RUSTC_HAS_FILE_WITH_NUL config RUSTC_HAS_FILE_AS_C_STR def_bool RUSTC_VERSION >= 109100 +config RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS + def_bool RUSTC_VERSION >= 109800 + config PAHOLE_VERSION int default $(shell,$(srctree)/scripts/pahole-version.sh $(PAHOLE)) diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 0c57cf9b4004f..5e208064d46c2 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -30,6 +30,10 @@ #[allow(clippy::ref_as_ptr)] #[allow(clippy::undocumented_unsafe_blocks)] #[cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))] +#[cfg_attr( + CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + allow(suspicious_runtime_symbol_definitions) +)] mod bindings_raw { use pin_init::{MaybeZeroable, Zeroable}; diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs index 1d5fd9efb93e9..bec80b1dba23e 100644 --- a/rust/uapi/lib.rs +++ b/rust/uapi/lib.rs @@ -28,6 +28,10 @@ unsafe_op_in_unsafe_fn )] #![cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))] +#![cfg_attr( + CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + allow(suspicious_runtime_symbol_definitions) +)] // Manual definition of blocklisted types. type __kernel_size_t = usize; From aeebcfa8237c314ddcae2c85204bfef842af3a85 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 16 Jul 2026 10:22:43 +0000 Subject: [PATCH 2893/3297] rust: device: avoid trailing ; in printing macros commit a19bda861b3a79e25417462539df8b0d77c6b322 upstream. These macros are used like expressions, so they should not emit a semicolon. This is being turned into a hard error in a future release of Rust. error: trailing semicolon in macro used in expression position --> drivers/gpu/nova-core/firmware/fsp.rs:79:34 | 79 | .inspect_err(|_| dev_err!(dev, "FMC firmware missing '{}' section\n", name)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #79813 = note: this error originates in the macro `dev_err` (in Nightly builds, run with -Z macro-backtrace for more info) [ I was doubly surprised since upstream made it a deny-by-default lint a year ago for Rust 1.91.0, and yet we didn't see it; plus I hadn't seen this in my CI even yesterday. It turns out this just landed into today's nightly (nightly-2026-07-16, using upstream commit d0babd8b6): Link: https://github.com/rust-lang/rust/pull/159222 which says: "The `semicolon_in_expressions_from_macros` lint previously suppressed warnings about non-local macros. This masks a lint that will subsequently become a hard error." So that explains it. And this is the PR that will make it a hard error at some point in the future: Link: https://github.com/rust-lang/rust/pull/159218 Thus starting with Rust 1.99.0 (expected 2026-10-01), we will be seeing the deny-by-default lint above, so clean it up already. - Miguel ] Cc: stable@vger.kernel.org # Needed in 6.18.y and later. Link: https://github.com/rust-lang/rust/issues/79813 Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://github.com/rust-lang/rust/pull/159218 Link: https://github.com/rust-lang/rust/pull/159222 Link: https://patch.msgid.link/20260716-device-trail-semicolon-v1-1-f48e9dcfae15@google.com [ Fixed typo. ] Signed-off-by: Miguel Ojeda [ Resolved conflict on `dev_printk` by not performing a similar cleanup since it is not strictly needed. - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Greg Kroah-Hartman --- rust/kernel/device.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 176531f54ed30..a82b1bde1fa8a 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -625,7 +625,7 @@ macro_rules! dev_printk { /// ``` #[macro_export] macro_rules! dev_emerg { - ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*) } } /// Prints an alert-level message (level 1) prefixed with device information. @@ -651,7 +651,7 @@ macro_rules! dev_emerg { /// ``` #[macro_export] macro_rules! dev_alert { - ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*) } } /// Prints a critical-level message (level 2) prefixed with device information. @@ -677,7 +677,7 @@ macro_rules! dev_alert { /// ``` #[macro_export] macro_rules! dev_crit { - ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*) } } /// Prints an error-level message (level 3) prefixed with device information. @@ -703,7 +703,7 @@ macro_rules! dev_crit { /// ``` #[macro_export] macro_rules! dev_err { - ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*) } } /// Prints a warning-level message (level 4) prefixed with device information. @@ -729,7 +729,7 @@ macro_rules! dev_err { /// ``` #[macro_export] macro_rules! dev_warn { - ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*) } } /// Prints a notice-level message (level 5) prefixed with device information. @@ -755,7 +755,7 @@ macro_rules! dev_warn { /// ``` #[macro_export] macro_rules! dev_notice { - ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*) } } /// Prints an info-level message (level 6) prefixed with device information. @@ -781,7 +781,7 @@ macro_rules! dev_notice { /// ``` #[macro_export] macro_rules! dev_info { - ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*) } } /// Prints a debug-level message (level 7) prefixed with device information. @@ -807,5 +807,5 @@ macro_rules! dev_info { /// ``` #[macro_export] macro_rules! dev_dbg { - ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*) } } From f282242906c12fd476b86757afba51f211d4f959 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 18:41:53 +0800 Subject: [PATCH 2894/3297] usb: gadget: f_tcm: synchronize delayed set_alt with teardown commit 79e2d75725c85607f8a9d87ae9cace62a19f767d upstream. The f_tcm set_alt() path defers endpoint setup to a work item and completes the delayed status response from process context. The delayed work uses f_tcm private state and may complete the setup request after disconnect or function teardown has already moved on. Cancel and drain the delayed set_alt work when the function is unbound or freed. For disable paths, which are reached under the composite device lock, use a small state machine and a non-sleeping cancellation path instead of cancel_work_sync(). If the work is already running, mark it cancelled and let the worker own the cleanup; otherwise tcm_disable() can cancel the queued work and clean up immediately. Also serialize the final delayed-status completion with the cancellation check while holding the composite device lock. This prevents a disconnect from clearing delayed_status while the worker is about to complete the control request. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in tcm_delayed_set_alt+0x6c/0xef0 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? tcm_delayed_set_alt+0x6c/0xef0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? tcm_delayed_set_alt+0x6c/0xef0 kasan_report+0xe0/0x110 ? tcm_delayed_set_alt+0x6c/0xef0 tcm_delayed_set_alt+0x6c/0xef0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? process_one_work+0x4cb/0xb90 ? rcu_is_watching+0x20/0x50 ? tcm_delayed_set_alt+0x9/0xef0 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Allocated by task 544: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x8f/0xa0 tcm_alloc+0x68/0x180 usb_get_function+0x36/0x60 config_usb_cfg_link+0x125/0x1b0 configfs_symlink+0x322/0x890 vfs_symlink+0xc2/0x270 filename_symlinkat+0x295/0x2f0 __x64_sys_symlinkat+0x62/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 661: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kfree+0x2f9/0x530 config_usb_cfg_unlink+0x173/0x1e0 configfs_unlink+0x1fa/0x340 vfs_unlink+0x15c/0x510 filename_unlinkat+0x2ba/0x450 __x64_sys_unlinkat+0x63/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: c52661d60f63 ("usb-gadget: Initial merge of target module for UASP + BOT") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260627104153.3822495-1-zzzccc427@gmail.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_tcm.c | 192 ++++++++++++++++++++++++---- drivers/usb/gadget/function/tcm.h | 13 ++ 2 files changed, 177 insertions(+), 28 deletions(-) diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 7b27f8082ace6..5753c83f55784 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -2361,31 +2361,158 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f) return -ENOTSUPP; } -struct guas_setup_wq { - struct work_struct work; - struct f_uas *fu; - unsigned int alt; -}; +static void tcm_cleanup_old_alt(struct f_uas *fu) +{ + if (fu->flags & USBG_IS_UAS) + uasp_cleanup_old_alt(fu); + else if (fu->flags & USBG_IS_BOT) + bot_cleanup_old_alt(fu); + fu->flags = 0; +} + +static void tcm_delayed_set_alt_done(struct f_uas *fu) +{ + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); +} + +static bool tcm_delayed_set_alt_cancelled(struct f_uas *fu) +{ + bool cancelled; + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + cancelled = fu->delayed_set_alt_cancel; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + return cancelled; +} + +static bool tcm_complete_delayed_status(struct f_uas *fu) +{ + struct usb_composite_dev *cdev = fu->function.config->cdev; + struct usb_request *req = cdev->req; + unsigned long cdev_flags; + bool cancelled; + int ret; + + spin_lock_irqsave(&cdev->lock, cdev_flags); + spin_lock(&fu->delayed_set_alt_lock); + cancelled = fu->delayed_set_alt_cancel; + if (!cancelled) { + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + } + spin_unlock(&fu->delayed_set_alt_lock); + + if (cancelled) { + spin_unlock_irqrestore(&cdev->lock, cdev_flags); + return false; + } + + if (cdev->delayed_status == 0) { + WARN(cdev, "%s: Unexpected call\n", __func__); + } else if (--cdev->delayed_status == 0) { + req->length = 0; + req->context = cdev; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret == 0) { + cdev->setup_pending = true; + } else { + req->status = 0; + req->complete(cdev->gadget->ep0, req); + } + } + + spin_unlock_irqrestore(&cdev->lock, cdev_flags); + + return true; +} + +static bool tcm_cancel_delayed_set_alt(struct f_uas *fu) +{ + bool cleanup = false; + bool cancel = false; + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + switch (fu->delayed_set_alt_state) { + case USBG_DELAYED_SET_ALT_IDLE: + cleanup = true; + break; + case USBG_DELAYED_SET_ALT_QUEUED: + case USBG_DELAYED_SET_ALT_RUNNING: + fu->delayed_set_alt_cancel = true; + cancel = true; + break; + } + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + if (cancel && cancel_work(&fu->delayed_set_alt)) { + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state == USBG_DELAYED_SET_ALT_QUEUED) { + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + cleanup = true; + } + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + } + + return cleanup; +} + +static void tcm_cancel_delayed_set_alt_sync(struct f_uas *fu) +{ + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) + fu->delayed_set_alt_cancel = true; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + cancel_work_sync(&fu->delayed_set_alt); + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); +} static void tcm_delayed_set_alt(struct work_struct *wq) { - struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq, - work); - struct f_uas *fu = work->fu; - int alt = work->alt; + struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt); + unsigned long flags; + unsigned int alt; - kfree(work); + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_QUEUED) { + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + return; + } + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_RUNNING; + alt = fu->delayed_alt; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); - if (fu->flags & USBG_IS_BOT) - bot_cleanup_old_alt(fu); - if (fu->flags & USBG_IS_UAS) - uasp_cleanup_old_alt(fu); + tcm_cleanup_old_alt(fu); + + if (tcm_delayed_set_alt_cancelled(fu)) + goto out_done; if (alt == USB_G_ALT_INT_BBB) bot_set_alt(fu); else if (alt == USB_G_ALT_INT_UAS) uasp_set_alt(fu); - usb_composite_setup_continue(fu->function.config->cdev); + + if (tcm_complete_delayed_status(fu)) + return; + + tcm_cleanup_old_alt(fu); +out_done: + tcm_delayed_set_alt_done(fu); } static int tcm_get_alt(struct usb_function *f, unsigned intf) @@ -2411,15 +2538,20 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return -EOPNOTSUPP; if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) { - struct guas_setup_wq *work; + unsigned long flags; - work = kmalloc(sizeof(*work), GFP_ATOMIC); - if (!work) - return -ENOMEM; - INIT_WORK(&work->work, tcm_delayed_set_alt); - work->fu = fu; - work->alt = alt; - schedule_work(&work->work); + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) { + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, + flags); + return -EBUSY; + } + fu->delayed_alt = alt; + fu->delayed_set_alt_cancel = false; + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_QUEUED; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + schedule_work(&fu->delayed_set_alt); return USB_GADGET_DELAYED_STATUS; } return -EOPNOTSUPP; @@ -2429,11 +2561,8 @@ static void tcm_disable(struct usb_function *f) { struct f_uas *fu = to_f_uas(f); - if (fu->flags & USBG_IS_UAS) - uasp_cleanup_old_alt(fu); - else if (fu->flags & USBG_IS_BOT) - bot_cleanup_old_alt(fu); - fu->flags = 0; + if (tcm_cancel_delayed_set_alt(fu)) + tcm_cleanup_old_alt(fu); } static int tcm_setup(struct usb_function *f, @@ -2581,11 +2710,16 @@ static void tcm_free(struct usb_function *f) { struct f_uas *tcm = to_f_uas(f); + tcm_cancel_delayed_set_alt_sync(tcm); kfree(tcm); } static void tcm_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_uas *fu = to_f_uas(f); + + tcm_cancel_delayed_set_alt_sync(fu); + tcm_cleanup_old_alt(fu); usb_free_all_descriptors(f); } @@ -2618,6 +2752,8 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi) fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; fu->tpg = tpg_instances[i].tpg; + INIT_WORK(&fu->delayed_set_alt, tcm_delayed_set_alt); + spin_lock_init(&fu->delayed_set_alt_lock); hash_init(fu->stream_hash); mutex_unlock(&tpg_instances_lock); diff --git a/drivers/usb/gadget/function/tcm.h b/drivers/usb/gadget/function/tcm.h index 009974d81d66b..e1d5a93916127 100644 --- a/drivers/usb/gadget/function/tcm.h +++ b/drivers/usb/gadget/function/tcm.h @@ -3,6 +3,7 @@ #define __TARGET_USB_GADGET_H__ #include +#include /* #include */ #include #include @@ -29,6 +30,12 @@ enum { #define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS +enum { + USBG_DELAYED_SET_ALT_IDLE = 0, + USBG_DELAYED_SET_ALT_QUEUED, + USBG_DELAYED_SET_ALT_RUNNING, +}; + struct tcm_usbg_nexus { struct se_session *tvn_se_sess; }; @@ -132,6 +139,12 @@ struct f_uas { #define USBG_BOT_CMD_PEND (1 << 4) #define USBG_BOT_WEDGED (1 << 5) + struct work_struct delayed_set_alt; + spinlock_t delayed_set_alt_lock; /* protects delayed_set_alt_* */ + unsigned int delayed_alt; + unsigned int delayed_set_alt_state; + bool delayed_set_alt_cancel; + struct usbg_cdb cmd[USBG_NUM_CMDS]; struct usb_ep *ep_in; struct usb_ep *ep_out; From a0d1a11b90a512afc011308da197bfa5157db5d1 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Thu, 25 Dec 2025 15:27:15 +0200 Subject: [PATCH 2895/3297] net/mlx5e: Fix NULL pointer dereference in ioctl module EEPROM query commit 7d36a4a8bf62dc508bc6bb4b59727aec25064ca5 upstream. The mlx5_query_mcia() function unconditionally dereferences the status pointer to store the MCIA register status value. However, mlx5e_get_module_id() passes NULL since it doesn't need the status value. Add a NULL check before dereferencing the status pointer to prevent a NULL pointer dereference. Fixes: 2e4c44b12f4d ("net/mlx5: Refactor EEPROM query error handling to return status separately") Signed-off-by: Gal Pressman Reviewed-by: Tariq Toukan Reviewed-by: Dragos Tatulea Signed-off-by: Mark Bloch Link: https://patch.msgid.link/20251225132717.358820-4-mbloch@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/port.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index d25713ce65b6d..23796fb353e4d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -393,9 +393,11 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev, if (err) return err; - *status = MLX5_GET(mcia_reg, out, status); - if (*status) + if (MLX5_GET(mcia_reg, out, status)) { + if (status) + *status = MLX5_GET(mcia_reg, out, status); return -EIO; + } ptr = MLX5_ADDR_OF(mcia_reg, out, dwords); memcpy(data, ptr, size); From bcb29986bbba8e1febbfb0c055478a76bd0be536 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 16 Jan 2026 00:49:24 +0000 Subject: [PATCH 2896/3297] net: stmmac: fix dwmac4 transmit performance regression commit 5ccde4c81e843ab6b3a324c8e2aa96d9b1270a1a upstream. dwmac4's transmit performance dropped by a factor of four due to an incorrect assumption about which definitions are for what. This highlights the need for sane register macros. Commit 8409495bf6c9 ("net: stmmac: cores: remove many xxx_SHIFT definitions") changed the way the txpbl value is merged into the register: value = readl(ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan)); - value = value | (txpbl << DMA_BUS_MODE_PBL_SHIFT); + value = value | FIELD_PREP(DMA_BUS_MODE_PBL, txpbl); With the following in the header file: #define DMA_BUS_MODE_PBL BIT(16) -#define DMA_BUS_MODE_PBL_SHIFT 16 The assumption here was that DMA_BUS_MODE_PBL was the mask for DMA_BUS_MODE_PBL_SHIFT, but this turns out not to be the case. The field is actually six bits wide, buts 21:16, and is called TXPBL. What's even more confusing is, there turns out to be a PBLX8 single bit in the DMA_CHAN_CONTROL register (0x1100 for channel 0), and DMA_BUS_MODE_PBL seems to be used for that. However, this bit et.al. was listed under a comment "/* DMA SYS Bus Mode bitmap */" which is for register 0x1004. Fix this up by adding an appropriately named field definition under the DMA_CHAN_TX_CONTROL() register address definition. Move the RPBL mask definition under DMA_CHAN_RX_CONTROL(), correctly renaming it as well. Also move the PBL bit definition under DMA_CHAN_CONTROL(), correctly renaming it. This removes confusion over the PBL fields. Fixes: 8409495bf6c9 ("net: stmmac: cores: remove many xxx_SHIFT definitions") Signed-off-by: Russell King (Oracle) Bisected-by: Maxime Chevallier Link: https://lore.kernel.org/51859704-57fd-4913-b09d-9ac58a57f185@bootlin.com Tested-by: Maxime Chevallier Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/E1vgY1k-00000003vOC-0Z1H@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 8 ++++---- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index d17bcc31a89c9..617e55c3711ed 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -76,7 +76,7 @@ static void dwmac4_dma_init_rx_chan(struct stmmac_priv *priv, u32 rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; value = readl(ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan)); - value = value | FIELD_PREP(DMA_BUS_MODE_RPBL_MASK, rxpbl); + value = value | FIELD_PREP(DMA_CHAN_RX_CTRL_RXPBL_MASK, rxpbl); writel(value, ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan)); if (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) && likely(dma_cfg->eame)) @@ -97,7 +97,7 @@ static void dwmac4_dma_init_tx_chan(struct stmmac_priv *priv, u32 txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; value = readl(ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan)); - value = value | FIELD_PREP(DMA_BUS_MODE_PBL, txpbl); + value = value | FIELD_PREP(DMA_CHAN_TX_CTRL_TXPBL_MASK, txpbl); /* Enable OSP to get best performance */ value |= DMA_CONTROL_OSP; @@ -122,7 +122,7 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv, /* common channel control register config */ value = readl(ioaddr + DMA_CHAN_CONTROL(dwmac4_addrs, chan)); if (dma_cfg->pblx8) - value = value | DMA_BUS_MODE_PBL; + value = value | DMA_CHAN_CTRL_PBLX8; writel(value, ioaddr + DMA_CHAN_CONTROL(dwmac4_addrs, chan)); /* Mask interrupts by writing to CSR7 */ @@ -140,7 +140,7 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv, /* common channel control register config */ value = readl(ioaddr + DMA_CHAN_CONTROL(dwmac4_addrs, chan)); if (dma_cfg->pblx8) - value = value | DMA_BUS_MODE_PBL; + value = value | DMA_CHAN_CTRL_PBLX8; writel(value, ioaddr + DMA_CHAN_CONTROL(dwmac4_addrs, chan)); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 7573ca2db352d..a019eb07afcf1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -32,8 +32,6 @@ /* DMA SYS Bus Mode bitmap */ #define DMA_BUS_MODE_SPH BIT(24) -#define DMA_BUS_MODE_PBL BIT(16) -#define DMA_BUS_MODE_RPBL_MASK GENMASK(21, 16) #define DMA_BUS_MODE_MB BIT(14) #define DMA_BUS_MODE_FB BIT(0) @@ -126,18 +124,21 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs, #define DMA_CHAN_STATUS(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x60) /* DMA Control X */ +#define DMA_CHAN_CTRL_PBLX8 BIT(16) #define DMA_CONTROL_SPH BIT(24) #define DMA_CONTROL_MSS_MASK GENMASK(13, 0) /* DMA Tx Channel X Control register defines */ #define DMA_CONTROL_EDSE BIT(28) +#define DMA_CHAN_TX_CTRL_TXPBL_MASK GENMASK(21, 16) #define DMA_CONTROL_TSE BIT(12) #define DMA_CONTROL_OSP BIT(4) #define DMA_CONTROL_ST BIT(0) /* DMA Rx Channel X Control register defines */ -#define DMA_CONTROL_SR BIT(0) +#define DMA_CHAN_RX_CTRL_RXPBL_MASK GENMASK(21, 16) #define DMA_RBSZ_MASK GENMASK(14, 1) +#define DMA_CONTROL_SR BIT(0) /* Interrupt status per channel */ #define DMA_CHAN_STATUS_REB GENMASK(21, 19) From 846ed916cfa0c57308aabc1c9fec2ce9a883b092 Mon Sep 17 00:00:00 2001 From: Koen Koning Date: Fri, 13 Feb 2026 16:20:47 +0100 Subject: [PATCH 2897/3297] gpu: Fix uninitialized buddy for built-in drivers commit cc27314c67516c138ee3829197d1c3b998e29fae upstream. Move buddy to the start of the link order, so its __init runs before any other built-in drivers that may depend on it. Otherwise, a built-in driver that tries to use the buddy allocator will run into a kernel NULL pointer dereference because slab_blocks is uninitialized. Specifically, this fixes drm/xe (as built-in) running into a kernel panic during boot, because it uses buddy during device probe. Fixes: ba110db8e1bc ("gpu: Move DRM buddy allocator one level up (part two)") Cc: Joel Fernandes Cc: Dave Airlie Cc: intel-xe@lists.freedesktop.org Reviewed-by: Dave Airlie Tested-by: Peter Senna Tschudin Signed-off-by: Koen Koning Signed-off-by: Dave Airlie Link: https://patch.msgid.link/20260213152047.179628-1-koen.koning@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index 5cd54d06e2627..b4e5e338efa23 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -2,8 +2,9 @@ # drm/tegra depends on host1x, so if both drivers are built-in care must be # taken to initialize them in the correct order. Link order is the only way # to ensure this currently. +# Similarly, buddy must come first since it is used by other drivers. +obj-$(CONFIG_GPU_BUDDY) += buddy.o obj-y += host1x/ drm/ vga/ tests/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ obj-$(CONFIG_TRACE_GPU_MEM) += trace/ obj-$(CONFIG_NOVA_CORE) += nova-core/ -obj-$(CONFIG_GPU_BUDDY) += buddy.o From 0f33b1c457c2199ed130b92cc2ff363a3f7b9415 Mon Sep 17 00:00:00 2001 From: Nikunj A Dadhania Date: Wed, 15 Jul 2026 06:35:06 +0000 Subject: [PATCH 2898/3297] KVM: SVM: Bump asid_generation on CPU online to avoid ASID collision after hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 25f744ffa0c8e799e06250ce2e618367b166b0d4 upstream. If a vCPU stays scheduled out (or blocked) while the last pCPU it ran on goes through a hotplug cycle (online->offline->online), and the vCPU then resumes execution on the same pCPU, then it is possible for it to run with an ASID that has now been assigned to a different vCPU, resulting in stale TLB translations being used. svm_enable_virtualization_cpu() resets asid_generation to 1 and sets next_asid to max_asid + 1 on every CPU online event, including hotplug cycles. Because next_asid starts beyond the pool boundary, the first call to new_asid() after an online event always wraps the pool, incrementing asid_generation to 2 and assigning ASIDs starting from min_asid. Consider two vCPUs from different VMs, vCPU-A pinned to CPU-X holding asid_generation=2 and ASID=N from before the hotplug event: 1. CPU-X goes offline and back online: asid_generation resets to 1, next_asid = max_asid + 1. 2. One or more vCPUs migrate to CPU-X and call new_asid(), wrapping the pool and consuming ASIDs starting from min_asid. Eventually vCPU-B from a different VM is assigned asid_generation=2, ASID=N — the same ASID that vCPU-A held before the hotplug. 3. vCPU-A enters pre_svm_run() on CPU-X: current_vmcb->cpu is unchanged so the migration branch is skipped. Its saved asid_generation=2 matches sd->asid_generation=2, so the generation check silently passes and vCPU-A continues running with ASID=N — the same ASID just freshly assigned to vCPU-B. Both vCPUs from different VMs now run on CPU-X with the same ASID, causing them to share NPT TLB entries and producing stale translations. The collision manifests as a KVM internal error (Suberror: 1, emulation failure). The NPT page fault reports a faulting GPA far outside the VM's physical memory range — a sign of stale TLB translations being used. KVM falls back to instruction emulation, which fails on FPU/XSave instructions (XRSTOR, STMXCSR) that the emulator does not implement. Fix this by incrementing asid_generation instead of resetting it to 1 in svm_enable_virtualization_cpu(). On module load, asid_generation starts at 0 (memset) and the increment produces 1, identical to the old behaviour. On subsequent hotplug cycles the generation advances beyond any value a vCPU previously observed on this CPU, so the generation check in pre_svm_run() reliably forces new_asid() on every vCPU after every hotplug cycle. Fixes: 774c47f1d78e ("[PATCH] KVM: cpu hotplug support") Reported-by: Chandrakanth Silveru Tested-by: Srikanth Aithal Reviewed-by: K Prateek Nayak Reviewed-by: Tom Lendacky Signed-off-by: Nikunj A Dadhania Message-ID: <20260715063506.672432-1-nikunj@amd.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/svm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index bb034896bf6df..36fad2f95bd2e 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -523,7 +523,12 @@ static int svm_enable_virtualization_cpu(void) return -EBUSY; sd = per_cpu_ptr(&svm_data, me); - sd->asid_generation = 1; + /* + * Bump the current asid_generation value to ensure any vCPU that + * previously ran on this CPU sees a stale generation and is forced + * to acquire a new ASID, preventing a latent ASID collision. + */ + sd->asid_generation++; sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1; sd->next_asid = sd->max_asid + 1; sd->min_asid = max_sev_asid + 1; From 856a9b51680cbbed1ace1207b29e424a616cff48 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 3 Aug 2026 11:22:13 +0200 Subject: [PATCH 2899/3297] Linux 6.18.42 Link: https://lore.kernel.org/r/20260730141445.110192266@linuxfoundation.org Tested-by: Miguel Ojeda Tested-by: Wentao Guan Tested-by: Florian Fainelli Tested-by: Brett A C Sheffield Tested-by: Peter Schneider Tested-by: Shuah Khan Tested-by: Pavel Machek (CIP) Tested-by: Mark Brown Tested-by: Ron Economos Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 51ba8fb199dee..05ccceb3a87c5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 41 +SUBLEVEL = 42 EXTRAVERSION = NAME = Baby Opossum Posse From bfe7f9993467ba431b2731437949ac1e2634e771 Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Tue, 2 Jun 2026 21:26:44 -0700 Subject: [PATCH 2900/3297] x86/bugs: Make Safe-RET robust against interrupt injection commit 7e7f81cf6f5ca3311e526308f55d7c54d3ba71f9 upstream. An attacker injecting interrupts while the Safe-RET mitigation executes on machines affected by SRSO can neutralize the safe return sequence, potentially leading to data leakage through speculative execution. Fixup register state as if the Safe-RET sequence executed successfully by "emulating" it, in a manner of speaking, and avoid executing a RET instruction after returning from the interrupt. Co-developed-by: David Kaplan Signed-off-by: David Kaplan Signed-off-by: Borislav Petkov (AMD) Signed-off-by: Greg Kroah-Hartman --- arch/x86/entry/entry_64.S | 8 +++- arch/x86/include/asm/nospec-branch.h | 57 ++++++++++++++++++++++++++++ arch/x86/kernel/cpu/bugs.c | 39 +++++++++++++++++++ arch/x86/lib/retpoline.S | 20 ++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index ed04a968cc7d0..1bb73f4c26c2c 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -937,6 +937,8 @@ SYM_CODE_START(paranoid_entry) IBRS_ENTER save_reg=%r15 UNTRAIN_RET_FROM_CALL + HANDLE_INTR_SAFERET 8(%rsp) + RET SYM_CODE_END(paranoid_entry) @@ -1039,6 +1041,11 @@ SYM_CODE_START(error_entry) movl %ecx, %eax /* zero extend */ cmpq %rax, RIP+8(%rsp) je .Lbstep_iret + + VALIDATE_UNRET_END + + HANDLE_INTR_SAFERET 8(%rsp) + cmpq $.Lgs_change, RIP+8(%rsp) jne .Lerror_entry_done_lfence @@ -1057,7 +1064,6 @@ SYM_CODE_START(error_entry) FENCE_SWAPGS_KERNEL_ENTRY CALL_DEPTH_ACCOUNT leaq 8(%rsp), %rax /* return pt_regs pointer */ - VALIDATE_UNRET_END RET .Lbstep_iret: diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index 71e9861a16a89..e316cc994bf07 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * Call depth tracking for Intel SKL CPUs to address the RSB underflow @@ -176,6 +177,50 @@ add $(BITS_PER_LONG/8), %_ASM_SP; \ lfence; +/* + * Helper for detecting if an interrupt occurred at an unsafe location within + * Safe-RET. If Safe-RET is interrupted after the CALL or LEA the RSB may get + * poisoned by the interrupt handler. + * + * The Safe-RET sequence is: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * The two CMPs below check whether RIP points to after the CALL or after the + * LEA. + * + * The LFENCE below is to address this particular speculation case: + * + * 1. Userspace runs and poisons the BTB around the safe-RET routine + * + * 2. Userspace triggers some kind of exception + * + * 3. Kernel executes error_entry() and mis-speculates the branch into thinking + * it actually came from kernel space + * + * 4. The kernel then further mis-speculates that the exception occurred due + * to an interrupted safe-RET + * + * 5. The handle_interrupted_saferet() routine speculatively executes and + * speculatively does a safe-RET. But this is unsafe since it was never + * untrained. + * + * The LFENCE fixes this by ensuring step 5 is never reached speculatively. + * Note that this LFENCE only occurs if safe-RET was actually interrupted (so + * it's outside of the normal path). + */ +#define __HANDLE_INTR_SAFERET(name, pt_regs) \ + cmpq $(name), RIP+pt_regs; \ + jb 1f; \ + cmpq $(name)+5, RIP+pt_regs; \ + ja 1f; \ + lfence; \ + leaq pt_regs, %rdi; \ + call handle_interrupted_saferet; \ + 1: + #ifdef __ASSEMBLER__ /* @@ -293,6 +338,14 @@ #define UNTRAIN_RET_FROM_CALL \ __UNTRAIN_RET X86_FEATURE_ENTRY_IBPB, __stringify(RESET_CALL_DEPTH_FROM_CALL) +.macro HANDLE_INTR_SAFERET pt_regs +#ifdef CONFIG_MITIGATION_SRSO + ALTERNATIVE_2 "", \ + __stringify(__HANDLE_INTR_SAFERET(srso_safe_ret, \pt_regs)), X86_FEATURE_SRSO, \ + __stringify(__HANDLE_INTR_SAFERET(srso_alias_safe_ret, \pt_regs)), X86_FEATURE_SRSO_ALIAS + +#endif +.endm .macro CALL_DEPTH_ACCOUNT #ifdef CONFIG_MITIGATION_CALL_DEPTH_TRACKING @@ -625,6 +678,10 @@ static __always_inline void x86_idle_clear_cpu_buffers(void) x86_clear_cpu_buffers(); } +void srso_safe_ret(void); +void srso_alias_safe_ret(void); +void handle_interrupted_saferet(struct pt_regs *regs); + #endif /* __ASSEMBLER__ */ #endif /* _ASM_X86_NOSPEC_BRANCH_H_ */ diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index fd0b7880cf7e5..fbc465555e67c 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -3829,3 +3829,42 @@ void __warn_thunk(void) { WARN_ONCE(1, "Unpatched return thunk in use. This should not happen!\n"); } + +#ifdef CONFIG_MITIGATION_SRSO +/* + * Called during exception/interrupt entry if interrupted during the + * safe-RET sequence. The safe-RET sequence consists of 3 instructions: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * An interrupt after the CALL or after the LEA could potentially lead + * to branch predictor poisoning and results in the sequence not being + * able to be safely resumed. + * + * Therefore, modify the regs state as if the remaining part of the + * safe-RET sequence executed so the interrupt returns back to the + * desired return target, instead of the to the safe-RET sequence. + */ +void noinstr handle_interrupted_saferet(struct pt_regs *regs) +{ + unsigned long rip = regs->ip; + + if (rip == (unsigned long) srso_safe_ret || + rip == (unsigned long) srso_alias_safe_ret) { + /* Modify stack pointer as if LEA executed: */ + regs->sp += 8; + } + + /* + * Adjust registers as if RET executed: + * + * 1. Read the return address off the stack and into rIP: + */ + regs->ip = *(unsigned long *)(regs->sp); + + /* 2. Pop rIP off the stack: */ + regs->sp += 8; +} +#endif /* CONFIG_MITIGATION_SRSO */ diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S index 8f1fed0c3b83f..f9ca1d8eabca0 100644 --- a/arch/x86/lib/retpoline.S +++ b/arch/x86/lib/retpoline.S @@ -207,10 +207,24 @@ __EXPORT_THUNK(srso_alias_untrain_ret) .pushsection .text..__x86.rethunk_safe SYM_CODE_START_NOALIGN(srso_alias_safe_ret) + + /* + * Tell objtool that those are not function pointers referenced by + * __HANDLE_INTR_SAFERET(). Below too. + */ + ANNOTATE_NOENDBR + + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP UNWIND_HINT_FUNC + + ANNOTATE_NOENDBR ANNOTATE_UNRET_SAFE ret + /* End of Safe-RET sequence */ int3 SYM_FUNC_END(srso_alias_safe_ret) @@ -245,8 +259,14 @@ SYM_CODE_START_LOCAL_NOALIGN(srso_untrain_ret) * the stack. */ SYM_INNER_LABEL(srso_safe_ret, SYM_L_GLOBAL) + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP ret + /* End of Safe-RET sequence */ + int3 int3 /* end of movabs */ From 7b923c78b50d2ec52690c4353e5aad8302e80599 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 6 Aug 2026 19:27:09 +0200 Subject: [PATCH 2901/3297] Linux 6.18.43 Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 05ccceb3a87c5..5976ab6f0128e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 42 +SUBLEVEL = 43 EXTRAVERSION = NAME = Baby Opossum Posse From 285641eb82f0e150e639e18bc44ce7c8567ce3b4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 7 May 2026 13:00:28 +0200 Subject: [PATCH 2902/3297] netfilter: nf_conntrack_expect: restore helper propagation via expectation [ Upstream commit dcb0f9aefdd604d36710fda53c25bd7cf4a3e37a ] A recent series to fix expectations broke helper propagation via expectation, this mechanism is used by the sip and h323 helper. This also propagates the conntrack helper to expected connections. I changed semantics of exp->helper which now tells us the actual helper that created the expectation. Add an explicit assign_helper field to expectations for this purpose and update helpers to use it. Restore this feature for userspace conntrack helper via ctnetlink nfqueue integration so it is again possible to attach a helper to an expectation, where it makes sense. This is not restored via ctnetlink expectation creation as there is no client for such feature. Use the expectation layer 4 protocol number for the helper lookup for consistency. Make sure the expectation using this helper propagation mechanism also go away when the helper is unregistered. Fixes: 9c42bc9db90a ("netfilter: nf_conntrack_expect: honor expectation helper field") Fixes: 917b61fa2042 ("netfilter: ctnetlink: ignore explicit helper on new expectations") Reported-by: Ilya Maximets Tested-by: Ilya Maximets Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_conntrack_expect.h | 5 ++++- net/netfilter/nf_conntrack_broadcast.c | 1 + net/netfilter/nf_conntrack_core.c | 7 +++++-- net/netfilter/nf_conntrack_expect.c | 1 + net/netfilter/nf_conntrack_h323_main.c | 12 ++++++------ net/netfilter/nf_conntrack_helper.c | 5 +++++ net/netfilter/nf_conntrack_netlink.c | 18 ++++++++++++++++-- net/netfilter/nf_conntrack_sip.c | 2 +- 8 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index e9a8350e7ccfb..80f50fd0f7ad2 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -45,9 +45,12 @@ struct nf_conntrack_expect { void (*expectfn)(struct nf_conn *new, struct nf_conntrack_expect *this); - /* Helper to assign to new connection */ + /* Helper that created this expectation */ struct nf_conntrack_helper __rcu *helper; + /* Helper to assign to new connection */ + struct nf_conntrack_helper __rcu *assign_helper; + /* The conntrack of the master connection */ struct nf_conn *master; diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index 4f39bf7c843f2..75e53fde6b297 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -72,6 +72,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, exp->flags = NF_CT_EXPECT_PERMANENT; exp->class = NF_CT_EXPECT_CLASS_DEFAULT; rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, NULL); write_pnet(&exp->net, net); #ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index a877f9ad1cf2d..44fa3224cc870 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1771,14 +1771,17 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, spin_lock_bh(&nf_conntrack_expect_lock); exp = nf_ct_find_expectation(net, zone, tuple, !tmpl || nf_ct_is_confirmed(tmpl)); if (exp) { + struct nf_conntrack_helper *assign_helper; + /* Welcome, Mr. Bond. We've been expecting you... */ __set_bit(IPS_EXPECTED_BIT, &ct->status); /* exp->master safe, refcnt bumped in nf_ct_find_expectation */ ct->master = exp->master; - if (exp->helper) { + assign_helper = rcu_dereference(exp->assign_helper); + if (assign_helper) { help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); if (help) - rcu_assign_pointer(help->helper, exp->helper); + rcu_assign_pointer(help->helper, assign_helper); } #ifdef CONFIG_NF_CONNTRACK_MARK diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 24d0576d84b7f..8e943efbdf0a5 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -344,6 +344,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, helper = rcu_dereference(help->helper); rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, NULL); write_pnet(&exp->net, net); #ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index bd7e9e13e4f68..a116c6629e2b8 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -642,7 +642,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245); + rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -766,7 +766,7 @@ static int expect_callforwarding(struct sk_buff *skb, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -1233,7 +1233,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3 : NULL, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */ nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1305,7 +1305,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_UDP, NULL, &port); - rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect RAS "); @@ -1522,7 +1522,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); @@ -1576,7 +1576,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 9150bcfd7ca83..ea0cdb7ec9151 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -419,6 +419,11 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) this = rcu_dereference_protected(exp->helper, lockdep_is_held(&nf_conntrack_expect_lock)); + if (this == me) + return true; + + this = rcu_dereference_protected(exp->assign_helper, + lockdep_is_held(&nf_conntrack_expect_lock)); return this == me; } diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index c8fe284b86db4..3df7e5fc76c8b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2635,6 +2635,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { static struct nf_conntrack_expect * ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, + const struct nf_conntrack_helper *assign_helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask); @@ -2861,6 +2862,7 @@ static int ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, u32 portid, u32 report) { + struct nf_conntrack_helper *assign_helper = NULL; struct nlattr *cda[CTA_EXPECT_MAX+1]; struct nf_conntrack_tuple tuple, mask; struct nf_conntrack_expect *exp; @@ -2876,8 +2878,18 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, if (err < 0) return err; + if (cda[CTA_EXPECT_HELP_NAME]) { + const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); + + assign_helper = __nf_conntrack_helper_find(helpname, + nf_ct_l3num(ct), + tuple.dst.protonum); + if (!assign_helper) + return -EOPNOTSUPP; + } + exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, - &tuple, &mask); + assign_helper, &tuple, &mask); if (IS_ERR(exp)) return PTR_ERR(exp); @@ -3516,6 +3528,7 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr, static struct nf_conntrack_expect * ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, + const struct nf_conntrack_helper *assign_helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask) { @@ -3569,6 +3582,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp->zone = ct->zone; #endif rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, assign_helper); exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; exp->mask.src.u.all = mask->src.u.all; @@ -3624,7 +3638,7 @@ ctnetlink_create_expect(struct net *net, ct = nf_ct_tuplehash_to_ctrack(h); rcu_read_lock(); - exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask); + exp = ctnetlink_alloc_expect(cda, ct, NULL, &tuple, &mask); if (IS_ERR(exp)) { err = PTR_ERR(exp); goto err_rcu; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 147043f19521b..4bebf66410ea3 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1386,7 +1386,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), saddr, &daddr, proto, NULL, &port); exp->timeout.expires = sip_timeout * HZ; - rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, helper); exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; hooks = rcu_dereference(nf_nat_sip_hooks); From d36086cd1826e79a92debb9280a800c642d4b8a3 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Fri, 27 Feb 2026 18:16:36 +0545 Subject: [PATCH 2903/3297] kunit: tool: skip stty when stdin is not a tty [ Upstream commit e42c349f4cdfa43cb39a68c8f764f8cafc23a9a9 ] run_kernel() cleanup and signal_handler() invoke stty unconditionally. When stdin is not a tty (for example in CI or unit tests), this writes noise to stderr. Call stty only when stdin is a tty. Add regression tests for these paths: - run_kernel() with non-tty stdin - signal_handler() with non-tty stdin - signal_handler() with tty stdin Signed-off-by: Shuvam Pandey Reviewed-by: David Gow Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- tools/testing/kunit/kunit_kernel.py | 10 ++++-- tools/testing/kunit/kunit_tool_test.py | 42 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index 2998e1bc088b2..b610fcf0715a0 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -345,6 +345,12 @@ def build_kernel(self, jobs: int, build_dir: str, make_options: Optional[List[st return False return self.validate_config(build_dir) + def _restore_terminal_if_tty(self) -> None: + # stty requires a controlling terminal; skip headless runs. + if sys.stdin is None or not sys.stdin.isatty(): + return + subprocess.call(['stty', 'sane']) + def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None) -> Iterator[str]: # Copy to avoid mutating the caller-supplied list. exec_tests() reuses # the same args across repeated run_kernel() calls (e.g. --run_isolated), @@ -386,8 +392,8 @@ def _wait_proc() -> None: process.stdout.close() waiter.join() - subprocess.call(['stty', 'sane']) + self._restore_terminal_if_tty() def signal_handler(self, unused_sig: int, unused_frame: Optional[FrameType]) -> None: logging.error('Build interruption occurred. Cleaning console.') - subprocess.call(['stty', 'sane']) + self._restore_terminal_if_tty() diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index ed45bac1548d9..0eb61de9abd4b 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -515,6 +515,48 @@ def fake_start(start_args, unused_build_dir): self.assertIn('kunit.filter_glob=suite.test1', start_calls[0]) self.assertIn('kunit.filter_glob=suite.test2', start_calls[1]) + def test_run_kernel_skips_terminal_reset_without_tty(self): + def fake_start(unused_args, unused_build_dir): + return subprocess.Popen(['printf', 'KTAP version 1\n'], + text=True, stdout=subprocess.PIPE) + + non_tty_stdin = mock.Mock() + non_tty_stdin.isatty.return_value = False + + with tempfile.TemporaryDirectory('') as build_dir: + tree = kunit_kernel.LinuxSourceTree(build_dir, kunitconfig_paths=[os.devnull]) + with mock.patch.object(tree._ops, 'start', side_effect=fake_start), \ + mock.patch.object(kunit_kernel.sys, 'stdin', non_tty_stdin), \ + mock.patch.object(kunit_kernel.subprocess, 'call') as mock_call: + for _ in tree.run_kernel(build_dir=build_dir): + pass + + mock_call.assert_not_called() + + def test_signal_handler_skips_terminal_reset_without_tty(self): + non_tty_stdin = mock.Mock() + non_tty_stdin.isatty.return_value = False + tree = kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[os.devnull]) + + with mock.patch.object(kunit_kernel.sys, 'stdin', non_tty_stdin), \ + mock.patch.object(kunit_kernel.subprocess, 'call') as mock_call, \ + mock.patch.object(kunit_kernel.logging, 'error') as mock_error: + tree.signal_handler(signal.SIGINT, None) + mock_error.assert_called_once() + mock_call.assert_not_called() + + def test_signal_handler_resets_terminal_with_tty(self): + tty_stdin = mock.Mock() + tty_stdin.isatty.return_value = True + tree = kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[os.devnull]) + + with mock.patch.object(kunit_kernel.sys, 'stdin', tty_stdin), \ + mock.patch.object(kunit_kernel.subprocess, 'call') as mock_call, \ + mock.patch.object(kunit_kernel.logging, 'error') as mock_error: + tree.signal_handler(signal.SIGINT, None) + mock_error.assert_called_once() + mock_call.assert_called_once_with(['stty', 'sane']) + def test_build_reconfig_no_config(self): with tempfile.TemporaryDirectory('') as build_dir: with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f: From 9bb3714e09986e56bf7294ee9c91e499eb7ea287 Mon Sep 17 00:00:00 2001 From: David Gow Date: Sat, 28 Feb 2026 18:07:22 +0800 Subject: [PATCH 2904/3297] kunit: tool: Terminate kernel under test on SIGINT [ Upstream commit 8f260b02eeeffbf2263c2b82b6e3e32fd73cde2b ] kunit.py will attempt to catch SIGINT / ^C in order to ensure the TTY isn't messed up, but never actually attempts to terminate the running kernel (be it UML or QEMU). This can lead to a bit of frustration if the kernel has crashed or hung. Terminate the kernel process in the signal handler, if it's running. This requires plumbing through the process handle in a few more places (and having some checks to see if the kernel is still running in places where it may have already been killed). Reported-by: Andy Shevchenko Closes: https://lore.kernel.org/all/aaFmiAmg9S18EANA@smile.fi.intel.com/ Signed-off-by: David Gow Reviewed-by: Andy Shevchenko Tested-by: Andy Shevchenko Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- tools/testing/kunit/kunit_kernel.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index b610fcf0715a0..2869fcb199ff7 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -16,7 +16,7 @@ import signal import sys import threading -from typing import Iterator, List, Optional, Tuple +from typing import Iterator, List, Optional, Tuple, Any from types import FrameType import kunit_config @@ -265,6 +265,7 @@ def __init__( if kconfig_add: kconfig = kunit_config.parse_from_string('\n'.join(kconfig_add)) self._kconfig.merge_in_entries(kconfig) + self._process : Optional[subprocess.Popen[Any]] = None def arch(self) -> str: return self._arch @@ -364,36 +365,45 @@ def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_g args.append('kunit.filter_action=' + filter_action) args.append('kunit.enable=1') - process = self._ops.start(args, build_dir) - assert process.stdout is not None # tell mypy it's set + self._process = self._ops.start(args, build_dir) + assert self._process is not None # tell mypy it's set + assert self._process.stdout is not None # tell mypy it's set # Enforce the timeout in a background thread. def _wait_proc() -> None: try: - process.wait(timeout=timeout) + if self._process: + self._process.wait(timeout=timeout) except Exception as e: print(e) - process.terminate() - process.wait() + if self._process: + self._process.terminate() + self._process.wait() waiter = threading.Thread(target=_wait_proc) waiter.start() output = open(get_outfile_path(build_dir), 'w') try: # Tee the output to the file and to our caller in real time. - for line in process.stdout: + for line in self._process.stdout: output.write(line) yield line # This runs even if our caller doesn't consume every line. finally: # Flush any leftover output to the file - output.write(process.stdout.read()) + if self._process: + if self._process.stdout: + output.write(self._process.stdout.read()) + self._process.stdout.close() + self._process = None output.close() - process.stdout.close() waiter.join() self._restore_terminal_if_tty() def signal_handler(self, unused_sig: int, unused_frame: Optional[FrameType]) -> None: logging.error('Build interruption occurred. Cleaning console.') + if self._process: + self._process.terminate() + self._process.wait() self._restore_terminal_if_tty() From 85b94a74a0b83306c69e00844ea45c0bd1dba057 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 14 May 2026 16:46:38 +0200 Subject: [PATCH 2905/3297] netfilter: br_netfilter: Reallocate headroom if necessary in neigh_hh_bridge() [ Upstream commit b2870fc21601db9133bc70c48c603b487614fa3b ] neigh_hh_bridge() assumes the skb always has sufficient headroom to copy the aligned L2 header. This assumption can trigger the crash reported below using the following netfilter setup: $modprobe br_netfilter $sysctl -w net.bridge.bridge-nf-call-iptables=1 $root@OpenWrt:~# nft list ruleset table ip nat { chain prerouting { type nat hook prerouting priority dstnat; policy accept; ip daddr 192.168.83.123 dnat to 192.168.83.120 } } - iperf3 client (192.168.83.119) --> bridge (192.168.83.118) --> iperf3 server (192.168.83.120) the iperf3 client is sending packet for 192.168.83.123 to the bridge device. [ 1579.036575] Unable to handle kernel write to read-only memory at virtual address ffffff8004d76ffe [ 1579.045482] Mem abort info: [ 1579.048273] ESR = 0x000000009600004f [ 1579.052024] EC = 0x25: DABT (current EL), IL = 32 bits [ 1579.057363] SET = 0, FnV = 0 [ 1579.060417] EA = 0, S1PTW = 0 [ 1579.063550] FSC = 0x0f: level 3 permission fault [ 1579.068345] Data abort info: [ 1579.071224] ISV = 0, ISS = 0x0000004f, ISS2 = 0x00000000 [ 1579.076720] CM = 0, WnR = 1, TnD = 0, TagAccess = 0 [ 1579.081770] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 1579.087092] swapper pgtable: 4k pages, 39-bit VAs, pgdp=0000000080dc4000 [ 1579.093794] [ffffff8004d76ffe] pgd=180000009ffff003, p4d=180000009ffff003, pud=180000009ffff003, pmd=180000009ffe3003, pte=0060000084d76787 [ 1579.106343] Internal error: Oops: 000000009600004f [#1] SMP [ 1579.193824] CPU: 0 UID: 0 PID: 235 Comm: napi/qdma_eth-3 Tainted: G O 6.12.57 #0 [ 1579.202614] Tainted: [O]=OOT_MODULE [ 1579.206102] Hardware name: Airoha AN7581 Evaluation Board (DT) [ 1579.211929] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 1579.218889] pc : br_nf_pre_routing_finish_bridge+0x1ac/0xcc8 [br_netfilter] [ 1579.225859] lr : br_nf_pre_routing_finish_bridge+0x18c/0xcc8 [br_netfilter] [ 1579.232822] sp : ffffffc0817cba20 [ 1579.236128] x29: ffffffc0817cba20 x28: 0000000000000000 x27: ffffff8002b89000 [ 1579.243273] x26: ffffff8004d7700e x25: 0000000000000008 x24: 0000000000000000 [ 1579.250416] x23: ffffffc08179d4c0 x22: 0000000000000000 x21: ffffffc08179d4c0 [ 1579.257561] x20: ffffff8004d9b800 x19: ffffff8015010000 x18: 0000000000000014 [ 1579.264704] x17: ffffffbf9e930000 x16: ffffffc0817c8000 x15: 0000000000000070 [ 1579.271848] x14: 0000000000000080 x13: 0000000000000001 x12: 0000000000000000 [ 1579.278993] x11: ffffffc0798caae0 x10: ffffff8014db6fd8 x9 : 0000000000000000 [ 1579.286136] x8 : 0000000000000003 x7 : ffffffc08171f628 x6 : 000000001a3b83d3 [ 1579.293281] x5 : 0000000000000000 x4 : 1beb76f22fee0000 x3 : ffffff8004d7700e [ 1579.300425] x2 : 0000000000000000 x1 : ffffff8004d9b8bc x0 : ffffff80026ed000 [ 1579.307570] Call trace: [ 1579.310018] br_nf_pre_routing_finish_bridge+0x1ac/0xcc8 [br_netfilter] [ 1579.316632] br_nf_hook_thresh+0xd4/0x14bc [br_netfilter] [ 1579.322032] br_nf_hook_thresh+0x250/0x14bc [br_netfilter] [ 1579.327517] br_nf_hook_thresh+0x76c/0x14bc [br_netfilter] [ 1579.333003] br_handle_frame+0x180/0x480 [ 1579.336935] __netif_receive_skb_core.constprop.0+0x540/0xf40 [ 1579.342682] __netif_receive_skb_one_core+0x28/0x50 [ 1579.347561] process_backlog+0x98/0x1e0 [ 1579.351398] __napi_poll+0x34/0x1c4 [ 1579.354887] net_rx_action+0x178/0x330 [ 1579.358638] handle_softirqs+0x108/0x2d4 [ 1579.362560] __do_softirq+0x10/0x18 [ 1579.366051] ____do_softirq+0xc/0x20 [ 1579.369627] call_on_irq_stack+0x30/0x4c [ 1579.373550] do_softirq_own_stack+0x18/0x20 [ 1579.377734] do_softirq+0x4c/0x60 [ 1579.381050] __local_bh_enable_ip+0x88/0x98 [ 1579.385234] napi_threaded_poll_loop+0x188/0x21c [ 1579.389853] napi_threaded_poll+0x70/0x80 [ 1579.393863] kthread+0xd8/0xdc [ 1579.396918] ret_from_fork+0x10/0x20 [ 1579.400499] Code: 88dffc22 3707ffc2 f9406663 f9406684 (f81f0064) [ 1579.406589] ---[ end trace 0000000000000000 ]--- [ 1579.411209] Kernel panic - not syncing: Oops: Fatal exception in interrupt [ 1579.418083] SMP: stopping secondary CPUs [ 1579.422012] Kernel Offset: disabled Fix the issue reallocating the skb headroom if necessary in neigh_hh_bridge routine. Fixes: e179e6322ac33 ("netfilter: bridge-netfilter: Fix MAC header handling with IP DNAT") Reviewed-by: Ido Schimmel Signed-off-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/neighbour.h | 8 ++++++-- net/bridge/br_netfilter_hooks.c | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 4a30bd458c5a9..c13c758aca7e0 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -480,11 +480,15 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb) { - unsigned int seq, hh_alen; + unsigned int seq, hh_alen = HH_DATA_ALIGN(ETH_HLEN); + int err; + + err = skb_cow_head(skb, hh_alen); + if (err) + return err; do { seq = read_seqbegin(&hh->hh_lock); - hh_alen = HH_DATA_ALIGN(ETH_HLEN); memcpy(skb->data - hh_alen, hh->hh_data, ETH_ALEN + hh_alen - ETH_HLEN); } while (read_seqretry(&hh->hh_lock, seq)); return 0; diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 083e2fe96441d..6544c18febefe 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -296,7 +296,11 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_ goto free_skb; } - neigh_hh_bridge(&neigh->hh, skb); + if (neigh_hh_bridge(&neigh->hh, skb)) { + neigh_release(neigh); + goto free_skb; + } + skb->dev = br_indev; ret = br_handle_frame_finish(net, sk, skb); From a5cdd2407dd890f741f59b8367e4c6c101cce154 Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Thu, 23 Jul 2026 10:08:29 +0900 Subject: [PATCH 2906/3297] net: mpls: initialize rtm_tos in mpls_getroute() [ Upstream commit 295dd295e2137e10e9a5b1891d97e0f08de76f03 ] mpls_getroute() builds the RTM_NEWROUTE reply to an RTM_GETROUTE request by filling a struct rtmsg allocated from an skb whose data area is not zeroed (alloc_skb(NLMSG_GOODSIZE, ...)). It sets every field of the header except rtm_tos: r = nlmsg_data(nlh); r->rtm_family = AF_MPLS; r->rtm_dst_len = 20; r->rtm_src_len = 0; r->rtm_table = RT_TABLE_MAIN; r->rtm_type = RTN_UNICAST; r->rtm_scope = RT_SCOPE_UNIVERSE; r->rtm_protocol = rt->rt_protocol; r->rtm_flags = 0; struct rtmsg has no padding, so the one uninitialised byte rtm_tos (offset 3) is copied straight to user space on recvmsg(), leaking a byte of uninitialised heap memory. This is in contrast to mpls_dump_route(), which fills the very same header and does set rtm_tos = 0. Initialize rtm_tos to 0, matching mpls_dump_route(). Reproduced with KMSAN by adding an MPLS route and issuing a non-RTM_F_FIB_MATCH RTM_GETROUTE for its label: BUG: KMSAN: kernel-infoleak in _copy_to_iter+0x36c/0x33f0 _copy_to_iter+0x36c/0x33f0 __skb_datagram_iter+0x196/0x12c0 skb_copy_datagram_iter+0x5b/0x210 netlink_recvmsg+0x37b/0xef0 ... Uninit was created at: __alloc_skb+0x8ca/0x10e0 mpls_getroute+0x1280/0x3a40 rtnetlink_rcv_msg+0x1138/0x15a0 ... Byte 19 of 64 is uninitialized (byte 19 = nlmsghdr(16) + rtmsg offset 3 = rtm_tos) Fixes: 397fc9e5cefe ("mpls: route get support") Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260723010830.289917-1-yhlee@isslab.korea.ac.kr Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/mpls/af_mpls.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index f7f60025d042a..f9b8c4d0f0490 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -2468,6 +2468,7 @@ static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, r->rtm_family = AF_MPLS; r->rtm_dst_len = 20; r->rtm_src_len = 0; + r->rtm_tos = 0; r->rtm_table = RT_TABLE_MAIN; r->rtm_type = RTN_UNICAST; r->rtm_scope = RT_SCOPE_UNIVERSE; From e8362523fd1b61712f7d996802f9b5dee545c7e6 Mon Sep 17 00:00:00 2001 From: Honglei Huang Date: Fri, 31 Jul 2026 19:17:28 +0800 Subject: [PATCH 2907/3297] drm/gpusvm: publish dpagemap early to avoid device mapping leak on error commit 7f708f51e3955bda0d77a0b67ab9bea6c97fea99 upstream. drm_gpusvm_get_pages() only stored the local dpagemap into svm_pages->dpagemap on the success path. If a later page failed (e.g. -EOPNOTSUPP when ctx->allow_mixed is false) and jumped to err_unmap, svm_pages->dpagemap was still NULL, so __drm_gpusvm_unmap_pages() skipped device_unmap() and leaked the device mappings already created. Assign svm_pages->dpagemap when the first device page is mapped so the err_unmap path can device_unmap() those mappings. This issue was found by Sashiko AI review. Fixes: f70da6f99d4f ("drm/gpusvm: pull out drm_gpusvm_pages substructure") Cc: stable@vger.kernel.org Reviewed-by: Matthew Brost Signed-off-by: Honglei Huang Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260701062800.409248-4-honghuan@amd.com [ Adjusted for 6.18.y: drop drm_pagemap_get()/drm_pagemap_put() around the svm_pages->dpagemap assignment; that reference counting is not present in 6.18 where the field is not refcounted (unmap path only clears it). ] Signed-off-by: Honglei Huang Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_gpusvm.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 781cd4e5b704f..e5c7befe31081 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1390,6 +1390,14 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, err = -EAGAIN; goto err_unmap; } + + /* + * Set the dpagemap as soon as the first + * device page is mapped so the err_unmap path + * can device_unmap() the device mappings that + * have already been created. + */ + svm_pages->dpagemap = dpagemap; } svm_pages->dma_addr[j] = dpagemap->ops->device_map(dpagemap, @@ -1432,10 +1440,8 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, flags.has_dma_mapping = true; } - if (pagemap) { + if (pagemap) flags.has_devmem_pages = true; - svm_pages->dpagemap = dpagemap; - } /* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */ WRITE_ONCE(svm_pages->flags.__flags, flags.__flags); From 302eb87651326f6f9f4e77e09e3cd5a600f049c1 Mon Sep 17 00:00:00 2001 From: Fabian Lippold Date: Tue, 26 May 2026 17:41:01 +0200 Subject: [PATCH 2908/3297] ALSA: hda/realtek: add quirk for HP Dragonfly Folio G3 2-in-1 [ Upstream commit 0a10faad5ca58332ad70f7663ba82611f4daf736 ] Add PCI quirk for HP Dragonfly Folio G3 (PCI ID 103c:8a06) to select the CS35L41 SPI4 & GPIO LED fixup variant. Signed-off-by: Fabian Lippold Link: https://patch.msgid.link/20260526154418.1850568-3-fabianlippold1184@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- 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 40a75d72f7125..d84d17c6e049d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6839,6 +6839,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), From df0f33293c0a3f4b06cf4b362dbeba21489c1a1b Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 24 Mar 2026 14:36:43 +0000 Subject: [PATCH 2909/3297] HID: logitech-dj: Standardise hid_report_enum variable nomenclature [ Upstream commit a940aee176437046598dfc786b719bd96db3c74c ] Since we will need to differentiate between the two report_enum types soon, let's unify the naming conventions now to save confusion and/or unnecessary/unrelated changes in upcoming commits. {input,output}_report_enum is used in other places to let's conform. Signed-off-by: Lee Jones Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-logitech-dj.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 3b5412541c925..d6fb7015f0d77 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1749,7 +1749,7 @@ static int logi_dj_raw_event(struct hid_device *hdev, static int logi_dj_probe(struct hid_device *hdev, const struct hid_device_id *id) { - struct hid_report_enum *rep_enum; + struct hid_report_enum *input_report_enum; struct hid_report *rep; struct dj_receiver_dev *djrcv_dev; struct usb_interface *intf; @@ -1793,10 +1793,10 @@ static int logi_dj_probe(struct hid_device *hdev, } } - rep_enum = &hdev->report_enum[HID_INPUT_REPORT]; + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT]; /* no input reports, bail out */ - if (list_empty(&rep_enum->report_list)) + if (list_empty(&input_report_enum->report_list)) return -ENODEV; /* @@ -1804,7 +1804,7 @@ static int logi_dj_probe(struct hid_device *hdev, * Note: we should theoretically check for HID++ and DJ * collections, but this will do. */ - list_for_each_entry(rep, &rep_enum->report_list, list) { + list_for_each_entry(rep, &input_report_enum->report_list, list) { if (rep->application == 0xff000001) has_hidpp = true; } @@ -1817,7 +1817,7 @@ static int logi_dj_probe(struct hid_device *hdev, return -ENODEV; /* get the current application attached to the node */ - rep = list_first_entry(&rep_enum->report_list, struct hid_report, list); + rep = list_first_entry(&input_report_enum->report_list, struct hid_report, list); djrcv_dev = dj_get_receiver_dev(hdev, id->driver_data, rep->application, has_hidpp); if (!djrcv_dev) { @@ -1825,7 +1825,7 @@ static int logi_dj_probe(struct hid_device *hdev, return -ENOMEM; } - if (!rep_enum->numbered) + if (!input_report_enum->numbered) djrcv_dev->unnumbered_application = rep->application; /* Starts the usb device and connects to upper interfaces hiddev and From bc3bba4656ad280267ea78cf845ab6b1d95b9fcd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 24 Mar 2026 14:36:44 +0000 Subject: [PATCH 2910/3297] HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write [ Upstream commit b6a57912854e7ea36f3b270032661140cc4209cd ] logi_dj_recv_send_report() assumes that all incoming REPORT_ID_DJ_SHORT reports are 14 Bytes (DJREPORT_SHORT_LENGTH - 1) long. It uses that assumption to load the associated field's 'value' array with 14 Bytes of data. However, if a malicious user only sends say 1 Byte of data, 'report_count' will be 1 and only 1 Byte of memory will be allocated to the 'value' Byte array. When we come to populate 'value[1-13]' we will experience an OOB write. Signed-off-by: Lee Jones Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-logitech-dj.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index d6fb7015f0d77..3a37d61616293 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1750,6 +1750,7 @@ static int logi_dj_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct hid_report_enum *input_report_enum; + struct hid_report_enum *output_report_enum; struct hid_report *rep; struct dj_receiver_dev *djrcv_dev; struct usb_interface *intf; @@ -1793,6 +1794,15 @@ static int logi_dj_probe(struct hid_device *hdev, } } + output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; + rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; + + if (rep->maxfield < 1 || rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1) { + hid_err(hdev, "Expected size of DJ short report is %d, but got %d", + DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count); + return -EINVAL; + } + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT]; /* no input reports, bail out */ From 98f57011e6cd1322cd376b1965c44583d343f12c Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 10 Apr 2026 16:03:07 +0200 Subject: [PATCH 2911/3297] HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report [ Upstream commit 8b9a097eb2fc37b486afd81388c693bf3ab44466 ] commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") assumed that all HID devices attached to the logitech-dj driver was having an output report of DJ_SHORT. However, on the receiver itself, we have 2 other HID device we attach here: the mouse emulation and the keyboard emulation. For those devices the value of rep is NULL and we are triggered a segfault here. This is doubly required because logitech-dj also handles non DJ devices that might not have the DJ collection. Fixes: b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-logitech-dj.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 3a37d61616293..f1e923ed9d1c5 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1797,7 +1797,8 @@ static int logi_dj_probe(struct hid_device *hdev, output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - if (rep->maxfield < 1 || rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1) { + if (rep && (rep->maxfield < 1 || + rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) { hid_err(hdev, "Expected size of DJ short report is %d, but got %d", DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count); return -EINVAL; From 3e957c9b160cf8789637c7bc733148b90872c78c Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 3 Aug 2026 07:15:10 +0000 Subject: [PATCH 2912/3297] lib/alloc_tag: introduce mem_alloc_profiling_permanently_disabled() commit a37b0066a10aabf3c968b4566706fb866eaf9a85 upstream. mem_alloc_profiling_enabled() tells whether memalloc profiling is currently enabled. However, even when this function returns false, it can be enabled later. However, this is not enough. Some optimizations can be applied only when memalloc profiling is permanently disabled. For example, to skip the creation of KMALLOC_NO_OBJ_EXT caches at boot time, mem_profiling must be set to "never", "0" w/ debugging on, or have been shutdown so that it can no longer be enabled. Introduce mem_alloc_profiling_permanently_disabled() for this purpose. Signed-off-by: Harry Yoo (Oracle) Acked-by: Suren Baghdasaryan Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-3-47c7bd138de7@kernel.org Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Harry Yoo Signed-off-by: Sasha Levin --- include/linux/alloc_tag.h | 3 +++ lib/alloc_tag.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h index 02de2ede560f3..7e7cdc7612bed 100644 --- a/include/linux/alloc_tag.h +++ b/include/linux/alloc_tag.h @@ -134,6 +134,8 @@ static inline bool mem_alloc_profiling_enabled(void) &mem_alloc_profiling_key); } +bool mem_alloc_profiling_permanently_disabled(void); + static inline struct alloc_tag_counters alloc_tag_read(struct alloc_tag *tag) { struct alloc_tag_counters v = { 0, 0 }; @@ -239,6 +241,7 @@ static inline bool alloc_tag_is_inaccurate(struct alloc_tag *tag) #define DEFINE_ALLOC_TAG(_alloc_tag) static inline bool mem_alloc_profiling_enabled(void) { return false; } +static inline bool mem_alloc_profiling_permanently_disabled(void) { return true; } static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, size_t bytes) {} static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) {} diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index 3082d977efaae..7c6d3a086fdc4 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -26,6 +26,15 @@ static bool mem_profiling_support = true; static bool mem_profiling_support; #endif +/* + * Memory allocation profiling is permanently disabled and cannot be enabled. + * Must be called after setup_early_mem_profiling(). + */ +bool mem_alloc_profiling_permanently_disabled(void) +{ + return !mem_profiling_support; +} + static struct codetag_type *alloc_tag_cttype; #ifdef CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU From d01e88d421a6d07f35235600a43fbd0e551cf292 Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 3 Aug 2026 07:15:11 +0000 Subject: [PATCH 2913/3297] mm/slab: prevent unbounded recursion in free path with new kmalloc type commit d9e6a7623938968e3752b67e37eaff097e559a54 upstream. Commit 280ea9c3154b ("mm/slab: avoid allocating slabobj_ext array from its own slab") avoided recursive allocation of obj_exts from kmalloc caches of the same size, by bumping the obj_exts array's allocation size whenever the array size equals the size of the object being allocated. However, as reported by Danielle Costantino and Shakeel Butt, even slabs from kmalloc caches of different sizes can form a cycle by allocating obj_exts arrays from each other [1]: What happened: a KMALLOC_NORMAL slab's obj_exts array (used by allocation profiling / memcg accounting) is itself kmalloc()'d from a KMALLOC_NORMAL cache, so the "slab holds another slab's obj_exts array" relation can form cycles. With sizeof(struct slabobj_ext) == 16 and the host's geometry: - kmalloc-512 has 64 objects/slab -> array is 64*16 == 1024 bytes, served from kmalloc-1k; - kmalloc-1k has 32 objects/slab -> array is 32*16 == 512 bytes, served from kmalloc-512. A kmalloc-512 slab and a kmalloc-1k slab therefore hold each other's obj_exts array. Discarding one frees the other's array, which empties and discards that slab, which frees the first's array, and so on: __free_slab() -> free_slab_obj_exts() -> kfree() -> discard_slab() -> __free_slab() recurses along the cycle until the stack is exhausted. With memory allocation profiling, this allows unbounded recursion in the free path and led to a stack overflow on a production host in the Meta fleet [1]: BUG: TASK stack guard page was hit Oops: stack guard page RIP: 0010:kfree+0x8/0x5d0 Call Trace: __free_slab+0x66/0xc0 kfree+0x3f0/0x5d0 ... ( ~125x __free_slab <-> kfree ) ... do_syscall_64 It is proposed [1] to resolve this issue by always serving the obj_exts array allocation from kmalloc caches (or large kmalloc) of sizes larger than the object size. However, as pointed out by Vlastimil Babka [2], this can waste an excessive amount of memory as slabs from large kmalloc sizes (e.g. kmalloc-8k) generally need obj_exts arrays much smaller than the object size. Therefore, rather than bumping the size, let us take a different approach; disallow formation of cycles between kmalloc types when allocating obj_exts arrays. Currently, all obj_exts arrays are served from normal kmalloc caches. Cycles cannot be created if obj_exts arrays of normal kmalloc caches are served from a special kmalloc type that can never have obj_exts arrays. To achieve this, create a new kmalloc type called KMALLOC_NO_OBJ_EXT. KMALLOC_NO_OBJ_EXT caches are created with SLAB_NO_OBJ_EXT flag when either 1) memory allocation profiling is not permanently disabled, or 2) kmalloc types with a priority higher than KMALLOC_CGROUP are aliased with KMALLOC_NORMAL. Sheaf bootstrapping for KMALLOC_NO_OBJ_EXT caches now must be deferred because allocation of a barn can trigger obj_exts array allocation of normal kmalloc caches when the KMALLOC_NO_OBJ_EXT cache for that size is not ready yet. For simplicity, perform bootstrapping of sheaves for all kmalloc caches later. Introduce a new slab alloc flag, SLAB_ALLOC_NO_OBJ_EXT, to prevent allocation of obj_exts arrays, and let kmalloc_slab() override the type to KMALLOC_NO_OBJ_EXT when specified. Note that kmalloc_type() remains unchanged because kmalloc_flags() bypasses the kmalloc fastpath. Do not pass SLAB_ALLOC_NO_RECURSE to kmalloc_flags() in alloc_slab_obj_exts() and instead use SLAB_ALLOC_NO_OBJ_EXT only when the objects are allocated from normal kmalloc caches. While this prevents unbounded recursive allocation of obj_exts, it allows KMALLOC_NO_OBJ_EXT caches to have sheaves. Since sheaf allocations specify SLAB_ALLOC_NO_RECURSE that prevents allocation of both sheaves and obj_exts arrays, the recursion depth is bounded. obj_exts arrays for non-kmalloc-normal caches can now have a valid tag. Do not call mark_obj_codetag_empty() when freeing an obj_exts array to avoid false warnings. KMALLOC_NO_OBJ_EXT don't need this as they never allocate those arrays. Reported-by: Danielle Costantino Reported-by: Shakeel Butt Closes: https://lore.kernel.org/linux-mm/20260625230029.703750-1-shakeel.butt@linux.dev [1] Fixes: 4b8736964640 ("mm/slab: add allocation accounting into slab allocation and free paths") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-mm/c5c4208d-a6f0-413e-bad9-49be12f12d55@kernel.org [2] Signed-off-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-4-47c7bd138de7@kernel.org Signed-off-by: Vlastimil Babka (SUSE) [harry@kernel.org: Backport notes: - Fix a minor conflict due to missing partitioned kmalloc caches in 6.18. - Use __GFP_NO_OBJ_EXT instead of SLAB_ALLOC_NO_OBJ_EXT since slab's internal alloc_flags do not exist in 6.18. - Deferring sheaf bootstrapping for kmalloc caches is not applied as they don't have sheaves in 6.18. - Adjust the comment for SLAB_NO_OBJ_EXT, like in the commit 982e31382d9a ("mm/slab: decouple SLAB_NO_SHEAVES from SLAB_NO_OBJ_EXT"). The rest of that commit is a no-op in 6.18 as kmalloc caches don't have sheaves and thus only adjust the comment. - Apply the __GFP_NO_OBJ_EXT flag to the !allow_spin path in alloc_slab_obj_exts(). ] Signed-off-by: Harry Yoo Signed-off-by: Sasha Levin --- include/linux/slab.h | 8 ++++- mm/slab.h | 28 ++++++++++++++-- mm/slab_common.c | 13 ++++++++ mm/slub.c | 76 ++++++++++++++------------------------------ 4 files changed, 70 insertions(+), 55 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 22daf3f34a76f..a12601cc93a1b 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -238,7 +238,7 @@ enum _slab_flag_bits { #endif #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT /* Objects are short-lived */ -/* Slab created using create_boot_cache */ +/* Slab caches without obj_exts array */ #ifdef CONFIG_SLAB_OBJ_EXT #define SLAB_NO_OBJ_EXT __SLAB_FLAG_BIT(_SLAB_NO_OBJ_EXT) #else @@ -641,6 +641,9 @@ enum kmalloc_cache_type { #endif #ifndef CONFIG_MEMCG KMALLOC_CGROUP = KMALLOC_NORMAL, +#endif +#ifndef CONFIG_SLAB_OBJ_EXT + KMALLOC_NO_OBJ_EXT = KMALLOC_NORMAL, #endif KMALLOC_RANDOM_START = KMALLOC_NORMAL, KMALLOC_RANDOM_END = KMALLOC_RANDOM_START + RANDOM_KMALLOC_CACHES_NR, @@ -654,6 +657,9 @@ enum kmalloc_cache_type { #endif #ifdef CONFIG_MEMCG KMALLOC_CGROUP, +#endif +#ifdef CONFIG_SLAB_OBJ_EXT + KMALLOC_NO_OBJ_EXT, #endif NR_KMALLOC_TYPES }; diff --git a/mm/slab.h b/mm/slab.h index 36893299fa67c..c452f2a32c801 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -394,9 +394,13 @@ static inline struct kmem_cache * kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, unsigned long caller) { unsigned int index; + enum kmalloc_cache_type type = kmalloc_type(flags, caller); + + if (flags & __GFP_NO_OBJ_EXT) + type = KMALLOC_NO_OBJ_EXT; if (!b) - b = &kmalloc_caches[kmalloc_type(flags, caller)]; + b = &kmalloc_caches[type]; if (size <= 192) index = kmalloc_size_index[size_index_elem(size)]; else @@ -435,7 +439,8 @@ static inline bool is_kmalloc_normal(struct kmem_cache *s) { if (!is_kmalloc_cache(s)) return false; - return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT)); + + return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT|SLAB_NO_OBJ_EXT)); } bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj); @@ -520,6 +525,25 @@ bool slab_in_kunit_test(void); static inline bool slab_in_kunit_test(void) { return false; } #endif +/* + * Return true if KMALLOC_NORMAL caches may need obj_exts arrays. + * + * Memory allocation profiling requires obj_exts for all caches. + * Memcg usually doesn't need them for normal kmalloc caches, but kmalloc types + * with a priority higher than KMALLOC_CGROUP can be aliased with KMALLOC_NORMAL. + */ +static inline bool need_kmalloc_no_objext(void) +{ + if (!mem_alloc_profiling_permanently_disabled()) + return true; + + if (!mem_cgroup_kmem_disabled() && + (KMALLOC_NORMAL == KMALLOC_RECLAIM)) + return true; + + return false; +} + #ifdef CONFIG_SLAB_OBJ_EXT /* diff --git a/mm/slab_common.c b/mm/slab_common.c index 04583044a2bf0..085af9a96fd92 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -803,6 +803,12 @@ EXPORT_SYMBOL(kmalloc_size_roundup); #define KMALLOC_RANDOM_NAME(N, sz) #endif +#ifdef CONFIG_SLAB_OBJ_EXT +#define KMALLOC_NO_OBJ_EXT_NAME(sz) .name[KMALLOC_NO_OBJ_EXT] = "kmalloc-no-objext-" #sz, +#else +#define KMALLOC_NO_OBJ_EXT_NAME(sz) +#endif + #define INIT_KMALLOC_INFO(__size, __short_size) \ { \ .name[KMALLOC_NORMAL] = "kmalloc-" #__short_size, \ @@ -810,6 +816,7 @@ EXPORT_SYMBOL(kmalloc_size_roundup); KMALLOC_CGROUP_NAME(__short_size) \ KMALLOC_DMA_NAME(__short_size) \ KMALLOC_RANDOM_NAME(RANDOM_KMALLOC_CACHES_NR, __short_size) \ + KMALLOC_NO_OBJ_EXT_NAME(__short_size) \ .size = __size, \ } @@ -917,6 +924,12 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type) return; } flags |= SLAB_ACCOUNT; + } else if (IS_ENABLED(CONFIG_SLAB_OBJ_EXT) && type == KMALLOC_NO_OBJ_EXT) { + if (!need_kmalloc_no_objext()) { + kmalloc_caches[type][idx] = kmalloc_caches[KMALLOC_NORMAL][idx]; + return; + } + flags |= SLAB_NO_OBJ_EXT | SLAB_NO_MERGE; } else if (IS_ENABLED(CONFIG_ZONE_DMA) && (type == KMALLOC_DMA)) { flags |= SLAB_CACHE_DMA; } diff --git a/mm/slub.c b/mm/slub.c index 71e09c675047d..5ddd2a5c9e79b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2096,42 +2096,6 @@ static inline void init_slab_obj_exts(struct slab *slab) slab->obj_exts = 0; } -/* - * Calculate the allocation size for slabobj_ext array. - * - * When memory allocation profiling is enabled, the obj_exts array - * could be allocated from the same slab cache it's being allocated for. - * This would prevent the slab from ever being freed because it would - * always contain at least one allocated object (its own obj_exts array). - * - * To avoid this, increase the allocation size when we detect the array - * may come from the same cache, forcing it to use a different cache. - */ -static inline size_t obj_exts_alloc_size(struct kmem_cache *s, - struct slab *slab, gfp_t gfp) -{ - size_t sz = sizeof(struct slabobj_ext) * slab->objects; - struct kmem_cache *obj_exts_cache; - - if (sz > KMALLOC_MAX_CACHE_SIZE) - return sz; - - if (!is_kmalloc_normal(s)) - return sz; - - obj_exts_cache = kmalloc_slab(sz, NULL, gfp, 0); - /* - * We can't simply compare s with obj_exts_cache, because random kmalloc - * caches have multiple caches per size, selected by caller address. - * Since caller address may differ between kmalloc_slab() and actual - * allocation, bump size when sizes are equal. - */ - if (s->object_size == obj_exts_cache->object_size) - return obj_exts_cache->object_size + 1; - - return sz; -} - int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, gfp_t gfp, bool new_slab) { @@ -2140,13 +2104,17 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, unsigned long new_exts; unsigned long old_exts; struct slabobj_ext *vec; - size_t sz; + size_t sz = sizeof(struct slabobj_ext) * slab->objects; gfp &= ~OBJCGS_CLEAR_MASK; - /* Prevent recursive extension vector allocation */ - gfp |= __GFP_NO_OBJ_EXT; - sz = obj_exts_alloc_size(s, slab, gfp); + /* + * In most cases, obj_exts arrays are allocated from normal kmalloc. + * However, normal kmalloc caches must allocate them from + * KMALLOC_NO_OBJ_EXT caches to prevent recursion. + */ + if (is_kmalloc_normal(s)) + gfp |= __GFP_NO_OBJ_EXT; /* * Note that allow_spin may be false during early boot and its @@ -2155,7 +2123,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, * very early allocations on those. */ if (unlikely(!allow_spin)) - vec = kmalloc_nolock(sz, __GFP_ZERO | __GFP_NO_OBJ_EXT, + vec = kmalloc_nolock(sz, __GFP_ZERO | (gfp & __GFP_NO_OBJ_EXT), slab_nid(slab)); else vec = kmalloc_node(sz, gfp | __GFP_ZERO, slab_nid(slab)); @@ -2173,8 +2141,21 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, return -ENOMEM; } - VM_WARN_ON_ONCE(virt_to_slab(vec) != NULL && - virt_to_slab(vec)->slab_cache == s); + if (IS_ENABLED(CONFIG_DEBUG_VM)) { + struct kmem_cache *exts_cache; + struct slab *exts_slab; + + exts_slab = virt_to_slab(vec); + if (exts_slab) { + /* + * The vector must be allocated from either normal or + * KMALLOC_NO_OBJ_EXT kmalloc caches to avoid cycles. + */ + exts_cache = exts_slab->slab_cache; + WARN_ON_ONCE(!is_kmalloc_normal(exts_cache) && + !(exts_cache->flags & SLAB_NO_OBJ_EXT)); + } + } new_exts = (unsigned long)vec; if (unlikely(!allow_spin)) @@ -2198,7 +2179,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, * assign slabobj_exts in parallel. In this case the existing * objcg vector should be reused. */ - mark_objexts_empty(vec); if (unlikely(!allow_spin)) kfree_nolock(vec); else @@ -2229,14 +2209,6 @@ static inline void free_slab_obj_exts(struct slab *slab) return; } - /* - * obj_exts was created with __GFP_NO_OBJ_EXT flag, therefore its - * corresponding extension will be NULL. alloc_tag_sub() will throw a - * warning if slab has extensions but the extension of an object is - * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that - * the extension for obj_exts is expected to be NULL. - */ - mark_objexts_empty(obj_exts); if (unlikely(READ_ONCE(slab->obj_exts) & OBJEXTS_NOSPIN_ALLOC)) kfree_nolock(obj_exts); else From 54a62153c765cd24239cde1f2633f2a2fd005368 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 07:46:04 -0400 Subject: [PATCH 2914/3297] thunderbolt: Prevent XDomain delayed work use-after-free on disconnect [ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ] tb_xdp_handle_request() runs on system_wq and queues xd->state_work via queue_delayed_work() in three request handlers: PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake), and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues xd->properties_changed_work when local properties change. Concurrently, tb_xdomain_remove() calls stop_handshake() which does cancel_delayed_work_sync() on both delayed works. Later, tb_xdomain_unregister() calls device_unregister() which eventually frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue") moved the request handler off tb->wq, the handler and the remove path are no longer serialized. If queue_delayed_work() executes after cancel_delayed_work_sync() but before the xdomain is freed, the delayed work fires on a freed object. Add xd->removing that tb_xdomain_remove() sets under xd->lock before calling stop_handshake(). Each external queue site holds the same lock and checks removing before calling queue_delayed_work(). This provides the mutual exclusion needed: either the queue site acquires the lock first and queues work that the subsequent cancel will see, or the remove path acquires the lock first and the queue site observes removing == true and skips the queue. Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg Signed-off-by: Sasha Levin --- drivers/thunderbolt/xdomain.c | 40 ++++++++++++++++++++++++++--------- include/linux/thunderbolt.h | 3 +++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 458476907eab0..6b16fc7254b8e 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -785,9 +785,13 @@ static void tb_xdp_handle_request(struct work_struct *work) * the xdomain related to this connection as well in * case there is a change in services it offers. */ - if (xd && device_is_registered(&xd->dev)) - queue_delayed_work(tb->wq, &xd->state_work, - msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + if (xd) { + mutex_lock(&xd->lock); + if (!xd->removing && device_is_registered(&xd->dev)) + queue_delayed_work(tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + mutex_unlock(&xd->lock); + } break; case UUID_REQUEST_OLD: @@ -800,8 +804,12 @@ static void tb_xdp_handle_request(struct work_struct *work) * received UUID request from the remote host. */ if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) { - dev_dbg(&xd->dev, "restarting handshake\n"); - start_handshake(xd); + mutex_lock(&xd->lock); + if (!xd->removing) { + dev_dbg(&xd->dev, "restarting handshake\n"); + start_handshake(xd); + } + mutex_unlock(&xd->lock); } break; @@ -829,9 +837,13 @@ static void tb_xdp_handle_request(struct work_struct *work) ret = tb_xdp_link_state_change_response(ctl, route, sequence, 0); - xd->target_link_width = lsc->tlw; - queue_delayed_work(tb->wq, &xd->state_work, - msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + mutex_lock(&xd->lock); + if (!xd->removing) { + xd->target_link_width = lsc->tlw; + queue_delayed_work(tb->wq, &xd->state_work, + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); + } + mutex_unlock(&xd->lock); } else { tb_xdp_error_response(ctl, route, sequence, ERROR_NOT_READY); @@ -2074,6 +2086,10 @@ void tb_xdomain_remove(struct tb_xdomain *xd) { tb_xdomain_debugfs_remove(xd); + mutex_lock(&xd->lock); + xd->removing = true; + mutex_unlock(&xd->lock); + stop_handshake(xd); device_for_each_child_reverse(&xd->dev, xd, unregister_service); @@ -2484,8 +2500,12 @@ static int update_xdomain(struct device *dev, void *data) xd = tb_to_xdomain(dev); if (xd) { - queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, - msecs_to_jiffies(50)); + mutex_lock(&xd->lock); + if (!xd->removing) + queue_delayed_work(xd->tb->wq, + &xd->properties_changed_work, + msecs_to_jiffies(50)); + mutex_unlock(&xd->lock); } return 0; diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 0ba112175bb39..7204586c10c3e 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -209,6 +209,8 @@ enum tb_link_width { * @link_width: Width of the downstream facing link * @link_usb4: Downstream link is USB4 * @is_unplugged: The XDomain is unplugged + * @removing: Set by tb_xdomain_remove() under @lock to prevent + * concurrent delayed work queueing * @needs_uuid: If the XDomain does not have @remote_uuid it will be * queried first * @service_ids: Used to generate IDs for the services @@ -257,6 +259,7 @@ struct tb_xdomain { enum tb_link_width link_width; bool link_usb4; bool is_unplugged; + bool removing; bool needs_uuid; struct ida service_ids; struct ida in_hopids; From 3e55d280954760e4b5127cf7e0bd11cbbf2f496b Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Tue, 16 Jun 2026 17:24:53 +0530 Subject: [PATCH 2915/3297] pinctrl: qcom: Unconditionally mark gpio as wakeup enable [ Upstream commit 859e02a369ab328a77dfcabf59562100e55f9c5c ] GPIO interrupts that are wakeup capable need to be forwarded to wakeup capable parent irqchip. This is done via writing to it's wakeup_enable bit. Currently the bit is set only for PDC irqchip by checking skip_wake_irqs. skip_wake_irqs is set to differentiate between parent irqchips MPM and PDC. It is set when the parent irqchip is PDC to inform pinctrl about skipping the IRQ setting up at TLMM. However, the functionality to forward GPIO interrupts during SoC low power mode is needed regardless of which parent irqchip it is. Without the functionality it is impossible for MPM irqchip to detect the GPIO interrupt during SoC low power mode since for MPM irqchip the skip_wake_irqs is always false. Remove skip_wake_irqs condition when setting wakeup enable bit to allow forwarding GPIO interrupts for SoCs using MPM irqchip too. Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits") Signed-off-by: Sneh Mankad Reviewed-by: Maulik Shah Reviewed-by: Linus Walleij Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index e99871b90ab9c..424b14bc29e96 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1226,12 +1226,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d) /* * If the wakeup_enable bit is present and marked as available for the * requested GPIO, it should be enabled when the GPIO is marked as - * wake irq in order to allow the interrupt event to be transfered to - * the PDC HW. + * wake irq in order to allow the interrupt event to be transferred to + * the PDC/MPM HW. * While the name implies only the wakeup event, it's also required for * the interrupt event. */ - if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { + if (g->intr_wakeup_present_bit) { u32 intr_cfg; raw_spin_lock_irqsave(&pctrl->lock, flags); @@ -1259,7 +1259,7 @@ static void msm_gpio_irq_relres(struct irq_data *d) unsigned long flags; /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */ - if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { + if (g->intr_wakeup_present_bit) { u32 intr_cfg; raw_spin_lock_irqsave(&pctrl->lock, flags); From 2ef9bb422dd6d94097b464ed8265ed6ec1b9346a Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 26 Jun 2026 15:08:05 +0200 Subject: [PATCH 2916/3297] pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151 [ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ] Pins 143 and 151 were not included in the PDC wakeup map. They are normally used for PCIe2A and PCIe3a PERST# respectively, so they're unlikely to be excercised in practice, but still add them for the sake of completeness. Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver") Signed-off-by: Konrad Dybcio Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c index cf8297e8b8f8c..d6e31b00e1c5e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c @@ -1884,16 +1884,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, - { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, - { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, - { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, - { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, - { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, - { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, - { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, - { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, - { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, - { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, + { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, + { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, + { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, + { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, + { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, + { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, + { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, + { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, + { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, + { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, + { 225, 196 }, { 227, 197 }, }; static struct msm_pinctrl_soc_data sc8280xp_pinctrl = { From 9086b488f2737d5dcee86852b83f2059f1cdfabf Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 1 Jul 2026 12:57:33 +0800 Subject: [PATCH 2917/3297] dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA [ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] When terminating DMA transfers, active descriptors are not properly reclaimed. Only cyclic descriptors were handled, leaving non-cyclic descriptors and their LLI chains to be permanently leaked. Fix by using vchan_terminate_vdesc() which handles both cyclic and non-cyclic descriptors by adding them to desc_terminated queue for proper cleanup. Add pchan->desc != pchan->done check to prevent double-adding completed descriptors, which would corrupt the list. Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") Signed-off-by: Hongling Zeng Acked-by: Jernej Skrabec Suggested-by: Frank Li Reviewed-by: Frank Li Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/sun6i-dma.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index f9d876deb1f05..ae362a2f7c40f 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -935,16 +935,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&vchan->vc.lock, flags); - if (vchan->cyclic) { - vchan->cyclic = false; - if (pchan && pchan->desc) { - struct virt_dma_desc *vd = &pchan->desc->vd; - struct virt_dma_chan *vc = &vchan->vc; + if (pchan && pchan->desc && pchan->desc != pchan->done) { + struct virt_dma_desc *vd = &pchan->desc->vd; - list_add_tail(&vd->node, &vc->desc_completed); - } + vchan_terminate_vdesc(vd); } + vchan->cyclic = false; vchan_get_all_descriptors(&vchan->vc, &head); if (pchan) { From 0679c0c189d2548f00e1bac95be28e2df5c6c7f7 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 25 May 2026 10:15:50 -0400 Subject: [PATCH 2918/3297] dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open() [ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ] The failed_dev_add and failed_dev_name paths drop the file-device reference while wq->wq_lock is still held. If put_device(fdev) drops the last reference, idxd_file_dev_release() runs synchronously and tries to take wq->wq_lock again, deadlocking. Those paths also fall through into the later ctx cleanup labels even though idxd_file_dev_release() owns that cleanup and frees ctx. This can make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context. Move idxd_wq_get() before file-device setup can fail, since the release callback always calls idxd_wq_put(). Then unlock wq->wq_lock before put_device(fdev) and return directly from the file-device setup failure path, leaving ctx cleanup to the release callback. Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened") Signed-off-by: Yuho Choi Reviewed-by: Dave Jiang Acked-by: Vinicius Costa Gomes Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/dma/idxd/cdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c index 4105688cf3f06..37addcdc10bc2 100644 --- a/drivers/dma/idxd/cdev.c +++ b/drivers/dma/idxd/cdev.c @@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) fdev->parent = cdev_dev(idxd_cdev); fdev->bus = &dsa_bus_type; fdev->type = &idxd_cdev_file_type; + idxd_wq_get(wq); rc = dev_set_name(fdev, "file%d", ctx->id); if (rc < 0) { @@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) goto failed_dev_add; } - idxd_wq_get(wq); mutex_unlock(&wq->wq_lock); return 0; failed_dev_add: failed_dev_name: + mutex_unlock(&wq->wq_lock); put_device(fdev); + return rc; failed_ida: failed_set_pasid: if (device_user_pasid_enabled(idxd)) From 3808bab5d95ae79e333e11f6a73d178e084c645d Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:11 -0700 Subject: [PATCH 2919/3297] iommu/arm-smmu-v3-iommufd: Require exactly one Stream ID for a vDEVICE [ Upstream commit c3b8ee84a965058b41275069d4696f37a8b14bf6 ] arm_vsmmu_vsid_to_sid() maps a guest's vSID to a single physical Stream ID taken from master->streams[0], assuming a device has exactly one stream. A device with several streams gets only its first one mapped, so a guest vSID invalidation cannot reach the others' ATC and IOTLB entries; a device with none makes master->streams a ZERO_SIZE_PTR, read out of bounds. Add an arm_vsmmu_vdevice_init() op to reject the vDEVICE with -EOPNOTSUPP when master->num_streams is not one, rather than mapping it silently. Fixes: d68beb276ba26 ("iommu/arm-smmu-v3: Support IOMMU_HWPT_INVALIDATE using a VIOMMU object") Link: https://patch.msgid.link/r/b15f2b73520f389f3f57881da2f040e7bdc18876.1783311134.git.nicolinc@nvidia.com Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe Signed-off-by: Sasha Levin --- .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c index 9ec5591565083..a52f86454f77d 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c @@ -285,6 +285,20 @@ static int arm_vsmmu_vsid_to_sid(struct arm_vsmmu *vsmmu, u32 vsid, u32 *sid) return ret; } +static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev) +{ + struct device *dev = iommufd_vdevice_to_device(vdev); + struct arm_smmu_master *master = dev_iommu_priv_get(dev); + + /* + * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so + * more streams would leave the rest stale and none reads out of bounds. + */ + if (master->num_streams != 1) + return -EOPNOTSUPP; + return 0; +} + /* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */ struct arm_vsmmu_invalidation_cmd { union { @@ -391,6 +405,7 @@ int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu, static const struct iommufd_viommu_ops arm_vsmmu_ops = { .alloc_domain_nested = arm_vsmmu_alloc_domain_nested, .cache_invalidate = arm_vsmmu_cache_invalidate, + .vdevice_init = arm_vsmmu_vdevice_init, }; size_t arm_smmu_get_viommu_size(struct device *dev, From 094145989b31f8926707d3f1ca7c79d71858b67d Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Wed, 15 Jul 2026 13:23:08 +0530 Subject: [PATCH 2920/3297] gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe() [ Upstream commit 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 ] The memory allocated for priv->blob.data is not freed in the error paths that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as well as in the remove function. Fix that by using device managed action to free the memory on remove. Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling") Signed-off-by: Abdun Nihaal Reviewed-by: Wolfram Sang Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 969dddd3d6faf..0f4a6228a7488 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -161,6 +161,13 @@ static int fops_buf_size_get(void *data, u64 *val) return 0; } +static void fops_buf_release(void *data) +{ + struct gpio_la_poll_priv *priv = data; + + vfree(priv->blob.data); +} + static int fops_buf_size_set(void *data, u64 val) { struct gpio_la_poll_priv *priv = data; @@ -239,6 +246,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev) return ret; fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); + ret = devm_add_action_or_reset(dev, fops_buf_release, priv); + if (ret) + return ret; priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); if (IS_ERR(priv->descs)) From 99dc2c143dfc03bdf7de2e163ec401fbbe343ae6 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Wed, 15 Jul 2026 16:02:10 +0800 Subject: [PATCH 2921/3297] selftests/lkdtm: rename STACKLEAK_ERASING to KSTACK_ERASE [ Upstream commit b3a7aa9c0020ae549a0d4964867ff66d2bd61709 ] Commit 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") renamed the LKDTM crash type and selftest configuration but missed the entry in tests.txt. As a result, the selftest generates STACKLEAK_ERASING.sh, which run.sh skips because the LKDTM DIRECT trigger only exposes KSTACK_ERASE. Rename the test entry so the generated runner uses the registered crash type. Fixes: 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") Signed-off-by: Haofeng Li Link: https://patch.msgid.link/tencent_CD80B5F746B6AABD68AF3F1097AD02C96F05@qq.com Signed-off-by: Kees Cook Signed-off-by: Sasha Levin --- tools/testing/selftests/lkdtm/tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index cff124c1eddd3..665a13a37363c 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -74,7 +74,7 @@ USERCOPY_STACK_FRAME_TO USERCOPY_STACK_FRAME_FROM USERCOPY_STACK_BEYOND USERCOPY_KERNEL -STACKLEAK_ERASING OK: the rest of the thread stack is properly erased +KSTACK_ERASE OK: the rest of the thread stack is properly erased CFI_FORWARD_PROTO CFI_BACKWARD call trace:|ok: control flow unchanged FORTIFY_STRSCPY detected buffer overflow From 8229a538885401c744b9ffb2b4cdbc0c0d2c5cb7 Mon Sep 17 00:00:00 2001 From: Kuan-Ying Lee Date: Wed, 15 Jul 2026 13:35:52 +0800 Subject: [PATCH 2922/3297] selftests/seccomp: Fix pointer type mismatch build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3421b9b056a6576d0ebac1030eafb48ad0544092 ] We hit the following build error while running the seccomp selftests in our testing. CC seccomp_bpf seccomp_bpf.c: In function ‘UPROBE_setup’: seccomp_bpf.c:5175:74: error: pointer type mismatch in conditional expression [-Wincompatible-pointer-types] 5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); | ^ seccomp_bpf.c:5175:57: note: first expression has type ‘int (*)(void)’ 5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); | ^~~~~~~~~~~~~~~~ seccomp_bpf.c:5175:76: note: second expression has type ‘int (__attribute__((nocf_check)) *)(void)’ 5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); | ^~~~~~~~~~~~~ get_uprobe_offset() takes a 'const void *' argument, so cast both operands to 'void *'. Fixes: 9ffc7a635c35 ("selftests/seccomp: validate uprobe syscall passes through seccomp") Signed-off-by: Kuan-Ying Lee Acked-by: Jiri Olsa Link: https://patch.msgid.link/20260715053559.28535-1-kuan-ying.lee@canonical.com Signed-off-by: Kees Cook Signed-off-by: Sasha Levin --- tools/testing/selftests/seccomp/seccomp_bpf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 874f17763536b..19c9afa0be719 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -5172,7 +5172,8 @@ FIXTURE_SETUP(UPROBE) ASSERT_GE(bit, 0); } - offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); + offset = get_uprobe_offset(variant->uretprobe ? (void *)probed_uretprobe + : (void *)probed_uprobe); ASSERT_GE(offset, 0); if (variant->uretprobe) From a0f288ebe6d8ab78527bed2e331033255b9e065f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 15:31:37 -0700 Subject: [PATCH 2923/3297] ata: sata_mv: accept 1 or 2 resources in platform probe [ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ] Board files in arch/arm/plat-orion, arch/arm/mach-dove, arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the "sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). Those devices are rejected with -EINVAL, so SATA no longer probes on legacy Marvell Orion/Kirkwood-style boards. Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 resources (legacy, IRQ supplied as a second resource) so both probing paths work. Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/sata_mv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index ffb396f61731f..1d8ca95fb7328 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev) /* * Simple resource validation .. */ - if (unlikely(pdev->num_resources != 1)) { + if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { dev_err(&pdev->dev, "invalid number of resources\n"); return -EINVAL; } From 9355f526c821fd618549c065fecfbedaf8d37e5c Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Fri, 17 Jul 2026 23:55:26 +0530 Subject: [PATCH 2924/3297] ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources() [ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ] On phy_init() failure the error path fallsthrough to disable_rsts, which deasserts the controller reset and then enters disable_phys calling phy_power_off() on PHYs that were never powered on. That corrupts the PHY power_count and triggers an extra runtime PM put. Use a separate exit_phys path that unwinds with phy_exit() only and falls through to disable_clks while the controller remains in reset. Reserve phy_power_off() for the phy_power_on() failure path only, and skip masked-out ports in both unwind loops. On phy_power_on() failure re-assert the controller reset before disabling clocks and regulators, matching the teardown order used by ahci_platform_enable_resources() and ahci_platform_disable_resources(). Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") Signed-off-by: Radhey Shyam Pandey Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c index 2d6a08c23d6ad..2961e53288f42 100644 --- a/drivers/ata/ahci_ceva.c +++ b/drivers/ata/ahci_ceva.c @@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) rc = phy_init(hpriv->phys[i]); if (rc) - goto disable_rsts; + goto exit_phys; } /* De-assert the controller reset */ @@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) return 0; -disable_rsts: - ahci_platform_deassert_rsts(hpriv); - disable_phys: while (--i >= 0) { + if (ahci_ignore_port(hpriv, i)) + continue; + phy_power_off(hpriv->phys[i]); phy_exit(hpriv->phys[i]); } + ahci_platform_assert_rsts(hpriv); + goto disable_clks; + +exit_phys: + while (--i >= 0) { + if (ahci_ignore_port(hpriv, i)) + continue; + + phy_exit(hpriv->phys[i]); + } disable_clks: ahci_platform_disable_clks(hpriv); From 3fd94b9ffe997e5e6a47e667b3d0398e9b4cae07 Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sat, 18 Jul 2026 15:01:31 +0530 Subject: [PATCH 2925/3297] phy: qcom: m31-eusb2: Fix return value of init call [ Upstream commit 361f533a2dce2c2841fe4dc0c9d85a67117edf95 ] The init call currently returns success irrespective of any failures during repeater init or clock enablement. Return appropriate error value in the init call failure path. Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver") Signed-off-by: Krishna Kurapati Link: https://patch.msgid.link/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c index 68f1ba8fec4ad..9434bd22ef32d 100644 --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c @@ -221,7 +221,7 @@ static int m31eusb2_phy_init(struct phy *uphy) disable_vreg: regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs); - return 0; + return ret; } static int m31eusb2_phy_exit(struct phy *uphy) From 3cbfb9b886dc1936f0dd27e0504f7481deaf12de Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 20 Jul 2026 16:09:50 +0530 Subject: [PATCH 2926/3297] ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup [ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard. The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check: if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98095->mclk holding an error pointer with no indication to the caller. This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field. Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers. Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/max98095.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index cfb63fe69267b..ad2587d2df819 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component) int ret = 0; max98095->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (IS_ERR(max98095->mclk)) + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; /* reset the codec, the DSP core, and disable all interrupts */ max98095_reset(component); From f5edba9bc69d0a93cb39137dbbcbb839caf8cf13 Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 20 Jul 2026 16:12:54 +0530 Subject: [PATCH 2927/3297] ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup [ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard. The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check: if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98090->mclk holding an error pointer with no indication to the caller. This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field. Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers. Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/max98090.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 5aff5a459a433..830caf62d0fa8 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2390,8 +2390,9 @@ static int max98090_probe(struct snd_soc_component *component) dev_dbg(component->dev, "max98090_probe\n"); max98090->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (IS_ERR(max98090->mclk)) + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; max98090->component = component; From cfa7e2734877330d6c10e0f33953905486c4530c Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 14 Jun 2026 22:38:06 +0900 Subject: [PATCH 2928/3297] of: reserved_mem: prevent OOB when too many dynamic regions are defined [ Upstream commit db3dbdfea1b8f38774419c5c2c14e4b81c48708d ] On boot, fdt_scan_reserved_mem() saves each dynamically-placed /reserved-memory subnode into a local array of size MAX_RESERVED_REGIONS. If the device tree defines more than MAX_RESERVED_REGIONS dynamically-placed regions, fdt_scan_reserved_mem() writes past the end of the local array. Add a bounds check that logs an error and skips the excess regions, restoring the original behavior. Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed") Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com Signed-off-by: Rob Herring (Arm) Signed-off-by: Sasha Levin --- drivers/of/of_reserved_mem.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 39d8305a16800..56c2a1e4e86c1 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -326,6 +326,7 @@ int __init fdt_scan_reserved_mem(void) err = __reserved_mem_reserve_reg(child, uname); if (!err) count++; + /* * Save the nodes for the dynamically-placed regions * into an array which will be used for allocation right @@ -333,10 +334,17 @@ int __init fdt_scan_reserved_mem(void) * or marked as no-map. This is done to avoid dynamically * allocating from one of the statically-placed regions. */ - if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { - dynamic_nodes[dynamic_nodes_cnt] = child; - dynamic_nodes_cnt++; + if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL)) + continue; + + if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) { + pr_err("too many defined dynamic regions, skip '%s'\n", + uname); + continue; } + + dynamic_nodes[dynamic_nodes_cnt] = child; + dynamic_nodes_cnt++; } for (int i = 0; i < dynamic_nodes_cnt; i++) { const char *uname; From 762561c438599a5eee789fade69802748803a364 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sat, 4 Jul 2026 17:58:56 +0930 Subject: [PATCH 2929/3297] btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag [ Upstream commit 6881f45d0eb541f2cee8c37c84b3860a23823bb3 ] [BUG] The following script can lead to unexpected qgroup rescan failure: # mkfs.btrfs -f -O quota $dev # mount $dev $mnt # mount -o remount,rescue=ibadroots $mnt ^^^^^ This above command is expected to fail # btrfs quota rescan -w $mnt ^^^^^ The above qgroup rescan is not expected to fail # btrfs qgroup show $mnt WARNING: qgroup data inconsistent, rescan recommended Qgroupid Referenced Exclusive Path -------- ---------- --------- ---- 0/5 16.00KiB 16.00KiB The above short script will be converted to a proper fstests case. [CAUSE] Inside btrfs_reconfigure(), if either btrfs_check_options() or btrfs_check_features() failed, we will always have BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful remount. That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations, including: - Qgroup rescan - Auto defrag - Space reclaim [FIX] Change the error handling of btrfs_check_options() and btrfs_check_features() to goto restore label. Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context") Reviewed-by: Johannes Thumshirn Signed-off-by: Qu Wenruo Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/super.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 736575f925179..157d551344707 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1518,12 +1518,14 @@ static int btrfs_reconfigure(struct fs_context *fc) sync_filesystem(sb); set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); - if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) - return -EINVAL; + if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) { + ret = -EINVAL; + goto restore; + } ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY)); if (ret < 0) - return ret; + goto restore; btrfs_ctx_to_info(fs_info, ctx); btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags); From deddd28fd83c264ee2ff5cd6b34449a9f1be6112 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 3 Jul 2026 07:54:40 +0200 Subject: [PATCH 2930/3297] btrfs: zoned: fix deadlock between metadata writeback and transaction commit [ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ] When writing out metadata extent buffers in a zoned filesystem, btree_writepages() holds fs_info->zoned_meta_io_lock across the whole writeback loop, including the call to btrfs_check_meta_write_pointer() -> check_bg_is_active(). For the tree-log block group, check_bg_is_active() may fail to activate the zone and fall back to btrfs_zone_finish_one_bg() to free an active zone. That path waits for the running transaction to commit while still holding zoned_meta_io_lock, but the committer needs that same lock to write out the tree extents, so the two tasks deadlock: Task A (kworker, metadata writeback) Task B (fsstress, transaction commit) ------------------------------------ ------------------------------------- wb_workfn() btrfs_commit_transaction(T) btree_writepages() btrfs_write_and_wait_transaction() btrfs_zoned_meta_io_lock() btrfs_write_marked_extents() btrfs_check_meta_write_pointer() btree_writepages() check_bg_is_active() [treelog_bg] btrfs_zoned_meta_io_lock() btrfs_zone_finish_one_bg() do_zone_finish() btrfs_inc_block_group_ro() btrfs_wait_for_commit() The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock around do_zone_finish() for this exact reason. Do the same in the tree-log branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire it afterwards. The lock only protects fs_info->active_{meta,system}_bg, which this branch does not touch, and ctx->zoned_bg keeps a reference to the block group across the unlock, so nothing is lost while the lock is dropped. This hang occasionally reproduces with fstests generic/475 on a zoned btrfs filesystem. Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time") Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/zoned.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index b8fc97e33009b..340380dbe81d1 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -2167,7 +2167,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx, if (fs_info->treelog_bg == block_group->start) { if (!btrfs_zone_activate(block_group)) { - int ret_fin = btrfs_zone_finish_one_bg(fs_info); + int ret_fin; + + btrfs_zoned_meta_io_unlock(fs_info); + ret_fin = btrfs_zone_finish_one_bg(fs_info); + btrfs_zoned_meta_io_lock(fs_info); if (ret_fin != 1 || !btrfs_zone_activate(block_group)) return false; From 4d4ef6627304acc39b6c8c09325e5dc9167a7ed7 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 3 Jul 2026 07:54:45 +0200 Subject: [PATCH 2931/3297] btrfs: zoned: reset meta_write_pointer on zone reset [ Upstream commit 5fabb1cf25d723274009d7b759545fd59f230c9d ] btrfs_reset_unused_block_groups() resets a block group's zone and sets alloc_offset back to 0 so the space can be reused, but it leaves meta_write_pointer pointing at the previous end of the zone. Once the block group is reactivated and reused for metadata, newly allocated tree blocks live before that stale write pointer. btrfs_check_meta_write_pointer() then sees them behind the write pointer, so they can never be written out in sequential order: the dirty extent buffers are stranded and pin their btree_inode folios until unmount. Reset meta_write_pointer back to the start of the block group for metadata and system block groups. Fixes: 453a73c3069a ("btrfs: zoned: reclaim unused zone by zone resetting") Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/zoned.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 340380dbe81d1..0dfbb28b7445c 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -3171,6 +3171,17 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num reclaimed = bg->alloc_offset; bg->zone_unusable = bg->length - bg->zone_capacity; bg->alloc_offset = 0; + /* + * The zone was just reset to empty, so alloc_offset went back to + * the start of the zone. For metadata/system block groups the + * write pointer must follow it back to the start of the zone; + * otherwise it stays stale at the previous (finished) zone end, + * and metadata written into the reused zone would sit behind the + * write pointer, could never be written out in sequential order, + * and would be stranded (pinning its folio) until unmount. + */ + if (bg->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM)) + bg->meta_write_pointer = bg->start; /* * This holds because we currently reset fully used then freed * block group. From dff474e723edea86624d077ea73945f0ecdee48a Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sun, 12 Jul 2026 13:12:51 +0930 Subject: [PATCH 2932/3297] btrfs: raid56: fix an incorrect csum skip during scrub [ Upstream commit 330dcc553f282e8dc0b88c9495b4c296465364e1 ] Commit 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper") uses the new helper to replace the nested loop inside verify_bio_data_sectors(), which simplifies the code. However that also changed the behavior of "continue" when a block has no data checksum. Previously the "continue" would skip the old for() loop, which would also increase @total_sector_nr. Now the "continue" will skip the new btrfs_bio_for_each_block_all() loop, which doesn't update @total_sector_nr. This means if we hit a block that has no data checksum, we will skip all the remaining blocks no matter if they have data checksum. As @total_sector_nr will never be updated, and that test_bit() will always return false. Fix it by increasing @total_sector_nr before calling "continue". Fixes: 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper") Reviewed-by: Daniel Vacek Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/raid56.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 0135dceb7baaa..4d4852f2ba7eb 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1583,8 +1583,10 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio, int ret; /* No csum for this sector, skip to the next sector. */ - if (!test_bit(total_sector_nr, rbio->csum_bitmap)) + if (!test_bit(total_sector_nr, rbio->csum_bitmap)) { + total_sector_nr++; continue; + } ret = btrfs_check_block_csum(fs_info, paddr, csum_buf, expected_csum); From 86fb88ac8c91583d89a80a9c1cfe47c1ba32d430 Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Mon, 20 Jul 2026 21:08:30 +0530 Subject: [PATCH 2933/3297] phy: zynqmp: fix clock error handling in xpsgtr_phy_init() [ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ] Propagate clk_prepare_enable() failures to the caller instead of returning success, and disable the reference clock on initialization error paths to avoid leaking clock references when phy_exit() is not called. Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") Signed-off-by: Radhey Shyam Pandey Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index fe6b4925d1662..c8230f2bda629 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy) { struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); struct xpsgtr_dev *gtr_dev = gtr_phy->dev; - int ret = 0; + int ret; mutex_lock(>r_dev->gtr_mutex); /* Configure and enable the clock when peripheral phy_init call */ - if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) + ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); + if (ret) goto out; /* Skip initialization if not required. */ @@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) if (gtr_dev->tx_term_fix) { ret = xpsgtr_phy_tx_term_fix(gtr_phy); if (ret < 0) - goto out; + goto out_disable_clk; gtr_dev->tx_term_fix = false; } @@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy) */ ret = xpsgtr_configure_pll(gtr_phy); if (ret) - goto out; + goto out_disable_clk; xpsgtr_lane_set_protocol(gtr_phy); @@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy) break; } + goto out; + +out_disable_clk: + clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); out: mutex_unlock(>r_dev->gtr_mutex); return ret; From ec81ecd2ac0930e232e5e9e456aa8d965477df95 Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Mon, 20 Jul 2026 21:08:31 +0530 Subject: [PATCH 2934/3297] phy: zynqmp: fix runtime PM leak on probe allocation failure [ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ] Allocate saved_regs before pm_runtime_resume_and_get() so a devm_kmalloc() failure does not leave an unreleased runtime PM usage counter. Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") Signed-off-by: Radhey Shyam Pandey Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index c8230f2bda629..2138f5399821a 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev) return PTR_ERR(provider); } + gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, + sizeof(save_reg_address), + GFP_KERNEL); + if (!gtr_dev->saved_regs) + return -ENOMEM; + pm_runtime_set_active(gtr_dev->dev); pm_runtime_enable(gtr_dev->dev); @@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev) return ret; } - gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, - sizeof(save_reg_address), - GFP_KERNEL); - if (!gtr_dev->saved_regs) - return -ENOMEM; - return 0; } From f74554e67ccf04d1fa71069e8c9afa2717e40716 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 12 Jul 2026 16:42:01 -0700 Subject: [PATCH 2935/3297] netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in sip_help_tcp() [ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] sip_help_tcp() stores the size change of each NAT-rewritten SIP message in s16 diff and accumulates it in s16 tdiff, but a single message can grow by more than S16_MAX while the packet stays under the 65535 enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long Contact list expands the message by tens of kilobytes. diff then wraps, and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, so the next iteration's ct_sip_get_header() reads past the linearized skb tail. Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the 65535 byte packet limit, and the seqadj core is already s32 (nf_ct_seqadj_set() takes s32), so no previously accepted input is rejected. BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) nf_confirm (net/netfilter/nf_conntrack_proto.c:183) nf_hook_slow (net/netfilter/core.c:619) ip6_output (net/ipv6/ip6_output.c:246) ip6_forward (net/ipv6/ip6_output.c:690) ipv6_rcv (net/ipv6/ip6_input.c:351) __netif_receive_skb_one_core (net/core/dev.c:6212) process_backlog (net/core/dev.c:6676) __napi_poll (net/core/dev.c:7735) net_rx_action (net/core/dev.c:7955) handle_softirqs (kernel/softirq.c:622) run_ksoftirqd (kernel/softirq.c:1076) ... Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") Reported-by: Weiming Shi Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/linux/netfilter/nf_conntrack_sip.h | 2 +- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/nf_nat_sip.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index dbc614dfe0d56..aafa0c04f917e 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { unsigned int *datalen); void (*seq_adjust)(struct sk_buff *skb, - unsigned int protoff, s16 off); + unsigned int protoff, s32 off); unsigned int (*expect)(struct sk_buff *skb, unsigned int protoff, diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 4bebf66410ea3..f5f9bd47d2889 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1629,7 +1629,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, unsigned int matchoff, matchlen; unsigned int msglen, origlen; const char *dptr, *end; - s16 diff, tdiff = 0; + s32 diff, tdiff = 0; int ret = NF_ACCEPT; unsigned long clen; bool term; diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index f7be30c69b5c8..a1c41defaf22d 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, } static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, - s16 off) + s32 off) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); From 6e70eba930a24510e540e99a7326887bbebae668 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 7 Jul 2026 23:05:28 +0800 Subject: [PATCH 2936/3297] drm/mediatek: Check CRTC state before freeing [ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ] mtk_crtc_reset() destroys the current CRTC state only when crtc->state is non-NULL, but it always converts crtc->state to struct mtk_crtc_state and passes the result to kfree(). When reset is called without an existing state, container_of(NULL, ...) does not produce NULL. Keep the mtk state free in the same crtc->state guard as the helper state destruction. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") Signed-off-by: Ruoyu Wang Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ Signed-off-by: Chun-Kuang Hu Signed-off-by: Sasha Levin --- drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c index c4c6d0249df56..aa8e6f6ddcd32 100644 --- a/drivers/gpu/drm/mediatek/mtk_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_crtc.c @@ -153,10 +153,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc) { struct mtk_crtc_state *state; - if (crtc->state) + if (crtc->state) { __drm_atomic_helper_crtc_destroy_state(crtc->state); - - kfree(to_mtk_crtc_state(crtc->state)); + kfree(to_mtk_crtc_state(crtc->state)); + } crtc->state = NULL; state = kzalloc(sizeof(*state), GFP_KERNEL); From 82cdbb6155a2c49357c859b1baca1393bf4c8701 Mon Sep 17 00:00:00 2001 From: Roman Kisel Date: Wed, 8 Oct 2025 16:34:05 -0700 Subject: [PATCH 2937/3297] arch/x86: mshyperv: Discover Confidential VMBus availability [ Upstream commit 7c8b6c326d830ca5c6b95f390c703966e14167e6 ] Confidential VMBus requires enabling paravisor SynIC, and the x86_64 guest has to inspect the Virtualization Stack (VS) CPUID leaf to see if Confidential VMBus is available. If it is, the guest shall enable the paravisor SynIC. Read the relevant data from the VS CPUID leaf. Refactor the code to avoid repeating CPUID and add flags to the struct ms_hyperv_info. For ARM64, the flag for Confidential VMBus is not set which provides the desired behaviour for now as it is not available on ARM64 just yet. Once ARM64 CCA guests are supported, this flag will be set unconditionally when running such a guest. Signed-off-by: Roman Kisel Reviewed-by: Michael Kelley Signed-off-by: Wei Liu Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation") Signed-off-by: Sasha Levin --- arch/x86/kernel/cpu/mshyperv.c | 28 +++++++++++++++------------- include/asm-generic/mshyperv.h | 2 ++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index c4febdbcfe4d8..cae0dcd9006bd 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -440,7 +440,7 @@ EXPORT_SYMBOL_GPL(hv_get_hypervisor_version); static void __init ms_hyperv_init_platform(void) { - int hv_max_functions_eax; + int hv_max_functions_eax, eax; #ifdef CONFIG_PARAVIRT pv_info.name = "Hyper-V"; @@ -475,6 +475,19 @@ static void __init ms_hyperv_init_platform(void) pr_info("Hyper-V: running on a nested hypervisor\n"); } + /* + * There is no check against the max function for HYPERV_CPUID_VIRT_STACK_* CPUID + * leaves as the hypervisor doesn't handle them. Even a nested root partition (L2 + * root) will not get them because the nested (L1) hypervisor filters them out. + * These are handled through intercept processing by the Windows Hyper-V stack + * or the paravisor. + */ + eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_PROPERTIES); + ms_hyperv.confidential_vmbus_available = + eax & HYPERV_VS_PROPERTIES_EAX_CONFIDENTIAL_VMBUS_AVAILABLE; + ms_hyperv.msi_ext_dest_id = + eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE; + if (ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS && ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) { x86_platform.calibrate_tsc = hv_get_tsc_khz; @@ -675,21 +688,10 @@ static bool __init ms_hyperv_x2apic_available(void) * pci-hyperv host bridge. * * Note: for a Hyper-V root partition, this will always return false. - * The hypervisor doesn't expose these HYPERV_CPUID_VIRT_STACK_* cpuids by - * default, they are implemented as intercepts by the Windows Hyper-V stack. - * Even a nested root partition (L2 root) will not get them because the - * nested (L1) hypervisor filters them out. */ static bool __init ms_hyperv_msi_ext_dest_id(void) { - u32 eax; - - eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_INTERFACE); - if (eax != HYPERV_VS_INTERFACE_EAX_SIGNATURE) - return false; - - eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_PROPERTIES); - return eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE; + return ms_hyperv.msi_ext_dest_id; } #ifdef CONFIG_AMD_MEM_ENCRYPT diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index b89c7e3a20474..8862f77241e6c 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -62,6 +62,8 @@ struct ms_hyperv_info { }; }; u64 shared_gpa_boundary; + bool msi_ext_dest_id; + bool confidential_vmbus_available; }; extern struct ms_hyperv_info ms_hyperv; extern bool hv_nested; From 74d20e3cf88e48988c2c8cc47cbaee8ff7ec0594 Mon Sep 17 00:00:00 2001 From: Roman Kisel Date: Wed, 8 Oct 2025 16:34:08 -0700 Subject: [PATCH 2938/3297] Drivers: hv: Rename fields for SynIC message and event pages [ Upstream commit 163224c189e8b679ce919aa64ccabb7a992ca2d1 ] Confidential VMBus requires interacting with two SynICs -- one provided by the host hypervisor, and one provided by the paravisor. Each SynIC requires its own message and event pages. Rename the existing host-accessible SynIC message and event pages with the "hyp_" prefix to clearly distinguish them from the paravisor ones. The field name is also changed in mshv_root.* for consistency. No functional changes. Signed-off-by: Roman Kisel Reviewed-by: Tianyu Lan Reviewed-by: Michael Kelley Signed-off-by: Wei Liu Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation") Signed-off-by: Sasha Levin --- drivers/hv/channel_mgmt.c | 6 ++-- drivers/hv/hv.c | 66 +++++++++++++++++++-------------------- drivers/hv/hyperv_vmbus.h | 4 +-- drivers/hv/mshv_root.h | 2 +- drivers/hv/mshv_synic.c | 6 ++-- drivers/hv/vmbus_drv.c | 6 ++-- 6 files changed, 45 insertions(+), 45 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index fd1d675ae37a4..d5348195e4fb0 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -844,14 +844,14 @@ static void vmbus_wait_for_unload(void) = per_cpu_ptr(hv_context.cpu_context, cpu); /* - * In a CoCo VM the synic_message_page is not allocated + * In a CoCo VM the hyp_synic_message_page is not allocated * in hv_synic_alloc(). Instead it is set/cleared in * hv_synic_enable_regs() and hv_synic_disable_regs() * such that it is set only when the CPU is online. If * not all present CPUs are online, the message page * might be NULL, so skip such CPUs. */ - page_addr = hv_cpu->synic_message_page; + page_addr = hv_cpu->hyp_synic_message_page; if (!page_addr) continue; @@ -892,7 +892,7 @@ static void vmbus_wait_for_unload(void) struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); - page_addr = hv_cpu->synic_message_page; + page_addr = hv_cpu->hyp_synic_message_page; if (!page_addr) continue; diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index b14c5f9e0ef29..b7419d0fad1d3 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -147,20 +147,20 @@ int hv_synic_alloc(void) * Skip these pages allocation here. */ if (!ms_hyperv.paravisor_present && !hv_root_partition()) { - hv_cpu->synic_message_page = + hv_cpu->hyp_synic_message_page = (void *)get_zeroed_page(GFP_ATOMIC); - if (!hv_cpu->synic_message_page) { + if (!hv_cpu->hyp_synic_message_page) { pr_err("Unable to allocate SYNIC message page\n"); goto err; } - hv_cpu->synic_event_page = + hv_cpu->hyp_synic_event_page = (void *)get_zeroed_page(GFP_ATOMIC); - if (!hv_cpu->synic_event_page) { + if (!hv_cpu->hyp_synic_event_page) { pr_err("Unable to allocate SYNIC event page\n"); - free_page((unsigned long)hv_cpu->synic_message_page); - hv_cpu->synic_message_page = NULL; + free_page((unsigned long)hv_cpu->hyp_synic_message_page); + hv_cpu->hyp_synic_message_page = NULL; goto err; } } @@ -168,30 +168,30 @@ int hv_synic_alloc(void) if (!ms_hyperv.paravisor_present && (hv_isolation_type_snp() || hv_isolation_type_tdx())) { ret = set_memory_decrypted((unsigned long) - hv_cpu->synic_message_page, 1); + hv_cpu->hyp_synic_message_page, 1); if (ret) { pr_err("Failed to decrypt SYNIC msg page: %d\n", ret); - hv_cpu->synic_message_page = NULL; + hv_cpu->hyp_synic_message_page = NULL; /* * Free the event page here so that hv_synic_free() * won't later try to re-encrypt it. */ - free_page((unsigned long)hv_cpu->synic_event_page); - hv_cpu->synic_event_page = NULL; + free_page((unsigned long)hv_cpu->hyp_synic_event_page); + hv_cpu->hyp_synic_event_page = NULL; goto err; } ret = set_memory_decrypted((unsigned long) - hv_cpu->synic_event_page, 1); + hv_cpu->hyp_synic_event_page, 1); if (ret) { pr_err("Failed to decrypt SYNIC event page: %d\n", ret); - hv_cpu->synic_event_page = NULL; + hv_cpu->hyp_synic_event_page = NULL; goto err; } - memset(hv_cpu->synic_message_page, 0, PAGE_SIZE); - memset(hv_cpu->synic_event_page, 0, PAGE_SIZE); + memset(hv_cpu->hyp_synic_message_page, 0, PAGE_SIZE); + memset(hv_cpu->hyp_synic_event_page, 0, PAGE_SIZE); } } @@ -227,28 +227,28 @@ void hv_synic_free(void) if (!ms_hyperv.paravisor_present && (hv_isolation_type_snp() || hv_isolation_type_tdx())) { - if (hv_cpu->synic_message_page) { + if (hv_cpu->hyp_synic_message_page) { ret = set_memory_encrypted((unsigned long) - hv_cpu->synic_message_page, 1); + hv_cpu->hyp_synic_message_page, 1); if (ret) { pr_err("Failed to encrypt SYNIC msg page: %d\n", ret); - hv_cpu->synic_message_page = NULL; + hv_cpu->hyp_synic_message_page = NULL; } } - if (hv_cpu->synic_event_page) { + if (hv_cpu->hyp_synic_event_page) { ret = set_memory_encrypted((unsigned long) - hv_cpu->synic_event_page, 1); + hv_cpu->hyp_synic_event_page, 1); if (ret) { pr_err("Failed to encrypt SYNIC event page: %d\n", ret); - hv_cpu->synic_event_page = NULL; + hv_cpu->hyp_synic_event_page = NULL; } } } free_page((unsigned long)hv_cpu->post_msg_page); - free_page((unsigned long)hv_cpu->synic_event_page); - free_page((unsigned long)hv_cpu->synic_message_page); + free_page((unsigned long)hv_cpu->hyp_synic_event_page); + free_page((unsigned long)hv_cpu->hyp_synic_message_page); } kfree(hv_context.hv_numa_map); @@ -278,12 +278,12 @@ void hv_synic_enable_regs(unsigned int cpu) /* Mask out vTOM bit. ioremap_cache() maps decrypted */ u64 base = (simp.base_simp_gpa << HV_HYP_PAGE_SHIFT) & ~ms_hyperv.shared_gpa_boundary; - hv_cpu->synic_message_page = + hv_cpu->hyp_synic_message_page = (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE); - if (!hv_cpu->synic_message_page) + if (!hv_cpu->hyp_synic_message_page) pr_err("Fail to map synic message page.\n"); } else { - simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page) + simp.base_simp_gpa = virt_to_phys(hv_cpu->hyp_synic_message_page) >> HV_HYP_PAGE_SHIFT; } @@ -297,12 +297,12 @@ void hv_synic_enable_regs(unsigned int cpu) /* Mask out vTOM bit. ioremap_cache() maps decrypted */ u64 base = (siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT) & ~ms_hyperv.shared_gpa_boundary; - hv_cpu->synic_event_page = + hv_cpu->hyp_synic_event_page = (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE); - if (!hv_cpu->synic_event_page) + if (!hv_cpu->hyp_synic_event_page) pr_err("Fail to map synic event page.\n"); } else { - siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page) + siefp.base_siefp_gpa = virt_to_phys(hv_cpu->hyp_synic_event_page) >> HV_HYP_PAGE_SHIFT; } @@ -360,8 +360,8 @@ void hv_synic_disable_regs(unsigned int cpu) */ simp.simp_enabled = 0; if (ms_hyperv.paravisor_present || hv_root_partition()) { - iounmap(hv_cpu->synic_message_page); - hv_cpu->synic_message_page = NULL; + iounmap(hv_cpu->hyp_synic_message_page); + hv_cpu->hyp_synic_message_page = NULL; } else { simp.base_simp_gpa = 0; } @@ -372,8 +372,8 @@ void hv_synic_disable_regs(unsigned int cpu) siefp.siefp_enabled = 0; if (ms_hyperv.paravisor_present || hv_root_partition()) { - iounmap(hv_cpu->synic_event_page); - hv_cpu->synic_event_page = NULL; + iounmap(hv_cpu->hyp_synic_event_page); + hv_cpu->hyp_synic_event_page = NULL; } else { siefp.base_siefp_gpa = 0; } @@ -403,7 +403,7 @@ static bool hv_synic_event_pending(void) { struct hv_per_cpu_context *hv_cpu = this_cpu_ptr(hv_context.cpu_context); union hv_synic_event_flags *event = - (union hv_synic_event_flags *)hv_cpu->synic_event_page + VMBUS_MESSAGE_SINT; + (union hv_synic_event_flags *)hv_cpu->hyp_synic_event_page + VMBUS_MESSAGE_SINT; unsigned long *recv_int_page = event->flags; /* assumes VMBus version >= VERSION_WIN8 */ bool pending; u32 relid; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 34943de7d6ac4..1b05a57286101 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -120,8 +120,8 @@ enum { * Per cpu state for channel handling */ struct hv_per_cpu_context { - void *synic_message_page; - void *synic_event_page; + void *hyp_synic_message_page; + void *hyp_synic_event_page; /* * The page is only used in hv_post_message() for a TDX VM (with the diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index e3931b0f12693..db6b42db2fdc6 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -169,7 +169,7 @@ struct mshv_girq_routing_table { }; struct hv_synic_pages { - struct hv_message_page *synic_message_page; + struct hv_message_page *hyp_synic_message_page; struct hv_synic_event_flags_page *synic_event_flags_page; struct hv_synic_event_ring_page *synic_event_ring_page; }; diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index 1a822beae2390..3c65b1c4abbe3 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -399,7 +399,7 @@ mshv_intercept_isr(struct hv_message *msg) void mshv_isr(void) { struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages); - struct hv_message_page **msg_page = &spages->synic_message_page; + struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_message *msg; bool handled; @@ -461,7 +461,7 @@ int mshv_synic_init(unsigned int cpu) #endif union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages); - struct hv_message_page **msg_page = &spages->synic_message_page; + struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = @@ -555,7 +555,7 @@ int mshv_synic_cleanup(unsigned int cpu) union hv_synic_sirbp sirbp; union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages); - struct hv_message_page **msg_page = &spages->synic_message_page; + struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 1a0e350c19404..0788281b28ded 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1048,7 +1048,7 @@ static void vmbus_onmessage_work(struct work_struct *work) void vmbus_on_msg_dpc(unsigned long data) { struct hv_per_cpu_context *hv_cpu = (void *)data; - void *page_addr = hv_cpu->synic_message_page; + void *page_addr = hv_cpu->hyp_synic_message_page; struct hv_message msg_copy, *msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; struct vmbus_channel_message_header *hdr; @@ -1232,7 +1232,7 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) * The event page can be directly checked to get the id of * the channel that has the interrupt pending. */ - void *page_addr = hv_cpu->synic_event_page; + void *page_addr = hv_cpu->hyp_synic_event_page; union hv_synic_event_flags *event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; @@ -1315,7 +1315,7 @@ static void __vmbus_isr(void) vmbus_chan_sched(hv_cpu); - page_addr = hv_cpu->synic_message_page; + page_addr = hv_cpu->hyp_synic_message_page; msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; /* Check if there are actual msgs to be processed */ From d397787dbc4bf0c961dfcd0def0c15f70b6d9b41 Mon Sep 17 00:00:00 2001 From: Roman Kisel Date: Wed, 8 Oct 2025 16:34:09 -0700 Subject: [PATCH 2939/3297] Drivers: hv: Allocate the paravisor SynIC pages when required [ Upstream commit 226494e5ee4eb0bae4fc7b525505828271d5047e ] Confidential VMBus requires interacting with two SynICs -- one provided by the host hypervisor, and one provided by the paravisor. Each SynIC requires its own message and event pages. Refactor and extend the existing code to add allocating and freeing the message and event pages for the paravisor SynIC when it is present. Signed-off-by: Roman Kisel Reviewed-by: Tianyu Lan Reviewed-by: Michael Kelley Signed-off-by: Wei Liu Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation") Signed-off-by: Sasha Levin --- drivers/hv/hv.c | 184 +++++++++++++++++++------------------- drivers/hv/hyperv_vmbus.h | 18 ++++ 2 files changed, 112 insertions(+), 90 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index b7419d0fad1d3..90db1e17582d8 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -96,10 +96,70 @@ int hv_post_message(union hv_connection_id connection_id, return hv_result(status); } +static int hv_alloc_page(void **page, bool decrypt, const char *note) +{ + int ret = 0; + + /* + * After the page changes its encryption status, its contents might + * appear scrambled on some hardware. Thus `get_zeroed_page` would + * zero the page out in vain, so do that explicitly exactly once. + * + * By default, the page is allocated encrypted in a CoCo VM. + */ + *page = (void *)__get_free_page(GFP_KERNEL); + if (!*page) + return -ENOMEM; + + if (decrypt) + ret = set_memory_decrypted((unsigned long)*page, 1); + if (ret) + goto failed; + + memset(*page, 0, PAGE_SIZE); + return 0; + +failed: + /* + * Report the failure but don't put the page back on the free list as + * its encryption status is unknown. + */ + pr_err("allocation failed for %s page, error %d, decrypted %d\n", + note, ret, decrypt); + *page = NULL; + return ret; +} + +static int hv_free_page(void **page, bool encrypt, const char *note) +{ + int ret = 0; + + if (!*page) + return 0; + + if (encrypt) + ret = set_memory_encrypted((unsigned long)*page, 1); + + /* + * In the case of the failure, the page is leaked. Something is wrong, + * prefer to lose the page with the unknown encryption status and stay afloat. + */ + if (ret) + pr_err("deallocation failed for %s page, error %d, encrypt %d\n", + note, ret, encrypt); + else + free_page((unsigned long)*page); + + *page = NULL; + + return ret; +} + int hv_synic_alloc(void) { int cpu, ret = -ENOMEM; struct hv_per_cpu_context *hv_cpu; + const bool decrypt = !vmbus_is_confidential(); /* * First, zero all per-cpu memory areas so hv_synic_free() can @@ -125,73 +185,37 @@ int hv_synic_alloc(void) vmbus_on_msg_dpc, (unsigned long)hv_cpu); if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) { - hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC); - if (!hv_cpu->post_msg_page) { - pr_err("Unable to allocate post msg page\n"); + ret = hv_alloc_page(&hv_cpu->post_msg_page, + decrypt, "post msg"); + if (ret) goto err; - } - - ret = set_memory_decrypted((unsigned long)hv_cpu->post_msg_page, 1); - if (ret) { - pr_err("Failed to decrypt post msg page: %d\n", ret); - /* Just leak the page, as it's unsafe to free the page. */ - hv_cpu->post_msg_page = NULL; - goto err; - } - - memset(hv_cpu->post_msg_page, 0, PAGE_SIZE); } /* - * Synic message and event pages are allocated by paravisor. - * Skip these pages allocation here. + * If these SynIC pages are not allocated, SIEF and SIM pages + * are configured using what the root partition or the paravisor + * provides upon reading the SIEFP and SIMP registers. */ if (!ms_hyperv.paravisor_present && !hv_root_partition()) { - hv_cpu->hyp_synic_message_page = - (void *)get_zeroed_page(GFP_ATOMIC); - if (!hv_cpu->hyp_synic_message_page) { - pr_err("Unable to allocate SYNIC message page\n"); + ret = hv_alloc_page(&hv_cpu->hyp_synic_message_page, + decrypt, "hypervisor SynIC msg"); + if (ret) goto err; - } - - hv_cpu->hyp_synic_event_page = - (void *)get_zeroed_page(GFP_ATOMIC); - if (!hv_cpu->hyp_synic_event_page) { - pr_err("Unable to allocate SYNIC event page\n"); - - free_page((unsigned long)hv_cpu->hyp_synic_message_page); - hv_cpu->hyp_synic_message_page = NULL; + ret = hv_alloc_page(&hv_cpu->hyp_synic_event_page, + decrypt, "hypervisor SynIC event"); + if (ret) goto err; - } } - if (!ms_hyperv.paravisor_present && - (hv_isolation_type_snp() || hv_isolation_type_tdx())) { - ret = set_memory_decrypted((unsigned long) - hv_cpu->hyp_synic_message_page, 1); - if (ret) { - pr_err("Failed to decrypt SYNIC msg page: %d\n", ret); - hv_cpu->hyp_synic_message_page = NULL; - - /* - * Free the event page here so that hv_synic_free() - * won't later try to re-encrypt it. - */ - free_page((unsigned long)hv_cpu->hyp_synic_event_page); - hv_cpu->hyp_synic_event_page = NULL; + if (vmbus_is_confidential()) { + ret = hv_alloc_page(&hv_cpu->para_synic_message_page, + false, "paravisor SynIC msg"); + if (ret) goto err; - } - - ret = set_memory_decrypted((unsigned long) - hv_cpu->hyp_synic_event_page, 1); - if (ret) { - pr_err("Failed to decrypt SYNIC event page: %d\n", ret); - hv_cpu->hyp_synic_event_page = NULL; + ret = hv_alloc_page(&hv_cpu->para_synic_event_page, + false, "paravisor SynIC event"); + if (ret) goto err; - } - - memset(hv_cpu->hyp_synic_message_page, 0, PAGE_SIZE); - memset(hv_cpu->hyp_synic_event_page, 0, PAGE_SIZE); } } @@ -207,48 +231,28 @@ int hv_synic_alloc(void) void hv_synic_free(void) { - int cpu, ret; + int cpu; + const bool encrypt = !vmbus_is_confidential(); for_each_present_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); - /* It's better to leak the page if the encryption fails. */ - if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) { - if (hv_cpu->post_msg_page) { - ret = set_memory_encrypted((unsigned long) - hv_cpu->post_msg_page, 1); - if (ret) { - pr_err("Failed to encrypt post msg page: %d\n", ret); - hv_cpu->post_msg_page = NULL; - } - } + if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) + hv_free_page(&hv_cpu->post_msg_page, + encrypt, "post msg"); + if (!ms_hyperv.paravisor_present && !hv_root_partition()) { + hv_free_page(&hv_cpu->hyp_synic_event_page, + encrypt, "hypervisor SynIC event"); + hv_free_page(&hv_cpu->hyp_synic_message_page, + encrypt, "hypervisor SynIC msg"); } - - if (!ms_hyperv.paravisor_present && - (hv_isolation_type_snp() || hv_isolation_type_tdx())) { - if (hv_cpu->hyp_synic_message_page) { - ret = set_memory_encrypted((unsigned long) - hv_cpu->hyp_synic_message_page, 1); - if (ret) { - pr_err("Failed to encrypt SYNIC msg page: %d\n", ret); - hv_cpu->hyp_synic_message_page = NULL; - } - } - - if (hv_cpu->hyp_synic_event_page) { - ret = set_memory_encrypted((unsigned long) - hv_cpu->hyp_synic_event_page, 1); - if (ret) { - pr_err("Failed to encrypt SYNIC event page: %d\n", ret); - hv_cpu->hyp_synic_event_page = NULL; - } - } + if (vmbus_is_confidential()) { + hv_free_page(&hv_cpu->para_synic_event_page, + false, "paravisor SynIC event"); + hv_free_page(&hv_cpu->para_synic_message_page, + false, "paravisor SynIC msg"); } - - free_page((unsigned long)hv_cpu->post_msg_page); - free_page((unsigned long)hv_cpu->hyp_synic_event_page); - free_page((unsigned long)hv_cpu->hyp_synic_message_page); } kfree(hv_context.hv_numa_map); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 1b05a57286101..39cdded062e8b 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -120,8 +120,26 @@ enum { * Per cpu state for channel handling */ struct hv_per_cpu_context { + /* + * SynIC pages for communicating with the host. + * + * These pages are accessible to the host partition and the hypervisor. + * They may be used for exchanging data with the host partition and the + * hypervisor even when they aren't trusted yet the guest partition + * must be prepared to handle the malicious behavior. + */ void *hyp_synic_message_page; void *hyp_synic_event_page; + /* + * SynIC pages for communicating with the paravisor. + * + * These pages may be accessed from within the guest partition only in + * CoCo VMs. Neither the host partition nor the hypervisor can access + * these pages in that case; they are used for exchanging data with the + * paravisor. + */ + void *para_synic_message_page; + void *para_synic_event_page; /* * The page is only used in hv_post_message() for a TDX VM (with the From b215cb70e14c7911a30e47742863fb5159e63e4b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 Jul 2026 17:32:15 +0200 Subject: [PATCH 2940/3297] Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation [ Upstream commit 8c7ab779c8850f4dab8473463cca9a7d52fdaecc ] lockdep_hardirq_threaded() is supposed to be used within IRQ core code and not within drivers. It is not obvious from within the driver, that this is the only interrupt service routing and that it is not shared handler. Replace lockdep_hardirq_threaded() with a lockdep annotation limiting threaded context on PREEMPT_RT to __vmbus_isr(). Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT") Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Michael Kelley Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/vmbus_drv.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 0788281b28ded..4f1f5f294a735 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1372,8 +1372,19 @@ static void vmbus_isr(void) if (IS_ENABLED(CONFIG_PREEMPT_RT)) { vmbus_irqd_wake(); } else { - lockdep_hardirq_threaded(); + static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG); + + /* + * vmbus_isr is never force-threaded and always invoked at hard + * IRQ level. __vmbus_isr() below can acquire a spinlock_t + * which becomes a sleeping lock and must not be acquired in + * this context. Therefore on PREEMPT_RT this will be threaded + * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets + * lockdep know that acquiring a spinlock_t is not an issue. + */ + lock_map_acquire_try(&vmbus_map); __vmbus_isr(); + lock_map_release(&vmbus_map); } } From a920ead0bc2f13b8493ef95273ee97c3cac33abc Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:49 +0000 Subject: [PATCH 2941/3297] mshv: Fix duplicate GSI detection for GSI 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 649dd135491945afa544351e3e6d4a727de87020 ] The duplicate routing entry check in mshv_update_routing_table() uses guest_irq_num != 0 to detect whether a GSI slot is already occupied. This fails for GSI 0 because its guest_irq_num is 0 both when the slot is unused (zero-initialized) and when legitimately assigned. As a result, duplicate entries for GSI 0 are silently accepted, with the second entry overwriting the first — corrupting the routing table without any error reported to userspace. While GSI 0 (legacy timer) is unlikely to appear in MSI-based routing in practice, the check is semantically wrong — it conflates "uninitialized" with "GSI number 0." Use girq_entry_valid instead, which is explicitly set to true when an entry is populated and remains zero for unused slots regardless of the GSI number. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/mshv_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c index d0fb9ef734f42..743777b4886d3 100644 --- a/drivers/hv/mshv_irq.c +++ b/drivers/hv/mshv_irq.c @@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition, /* * Allow only one to one mapping between GSI and MSI routing. */ - if (girq->guest_irq_num != 0) { + if (girq->girq_entry_valid) { r = -EINVAL; goto out; } From db1c4a8e9080f7f246673bde64bfa42bb508f3f0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:59 +0000 Subject: [PATCH 2942/3297] mshv: Fix sleeping under spinlock in mshv_portid_alloc [ Upstream commit a9708e550d11a53b22d932cdbfaa10c26346a2d0 ] idr_alloc() is called with GFP_KERNEL inside idr_lock(), which holds a spinlock. GFP_KERNEL allows the allocator to sleep, triggering a sleeping-while-atomic bug. Fix by using idr_preload(GFP_KERNEL) before taking the lock to pre-allocate memory in a sleepable context, then idr_alloc() with GFP_NOWAIT inside the spinlock-protected section. Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/mshv_portid_table.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c index c349af1f0aaac..6f59b3e376247 100644 --- a/drivers/hv/mshv_portid_table.c +++ b/drivers/hv/mshv_portid_table.c @@ -40,12 +40,14 @@ mshv_port_table_fini(void) int mshv_portid_alloc(struct port_table_info *info) { - int ret = 0; + int ret; + idr_preload(GFP_KERNEL); idr_lock(&port_table_idr); ret = idr_alloc(&port_table_idr, info, PORTID_MIN, - PORTID_MAX, GFP_KERNEL); + PORTID_MAX, GFP_NOWAIT); idr_unlock(&port_table_idr); + idr_preload_end(); return ret; } From 31d491f9da948a4831b50acebfdac609eb1d99c6 Mon Sep 17 00:00:00 2001 From: Alexandru Elisei Date: Wed, 22 Jul 2026 10:03:54 +0100 Subject: [PATCH 2943/3297] KVM: arm64: Reject guest_memfd memslots when the VM has MTE [ Upstream commit 679d7201c1f09e37fa1c12ce28d84079c17fc87f ] The user cannot use MTE on VMAs created by mapping a guest_memfd file, as arch_calc_vm_flag_bits() does not set VM_MTE_ALLOWED. When creating a guest_memfd backed memslot, kvm_arch_prepare_memory_region() rejects the memslot if MTE is enabled for the VM and if guest_memfd has been mapped in a VMA that intersects the memslot. However, the documentation for KVM_SET_USER_MEMORY_REGION2 explicitly states that the only condition for userspace_addr is for it to be a legal userspace address, but the mapping is not required to be valid nor populated at memslot creation. If userspace sets userspace_addr to an address that hasn't been mapped, or if userspace_addr belongs to a VMA that isn't backed by the guest_memfd file, or if the VMA doesn't intersect the memslot, memslot creation is successful and KVM ends up with a VM with MTE and guest_memfd-backed memslots. The same happens if the order is reversed: when userspace enables MTE, KVM does not check if memslots backed by guest_memfd are already present. Fix both issues by rejecting guest_memfd-backed memslots when MTE is enabled, and by rejecting MTE when guest_memfd-backed memslots are already present. Fixes: 32e200bd6e44 ("KVM: arm64: Enable support for guest_memfd backed memory") Tested-by: Fuad Tabba Reviewed-by: Fuad Tabba Signed-off-by: Alexandru Elisei Link: https://patch.msgid.link/20260722090354.94245-1-alexandru.elisei@arm.com Signed-off-by: Marc Zyngier Signed-off-by: Sasha Levin --- Documentation/virt/kvm/api.rst | 6 ++++++ arch/arm64/kvm/arm.c | 25 +++++++++++++++++++------ arch/arm64/kvm/mmu.c | 4 ++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 9f835f68b4fb9..10d8a366b2072 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8259,6 +8259,12 @@ When this capability is enabled all memory in memslots must be mapped as attempts to create a memslot with an invalid mmap will result in an -EINVAL return. +``guest_memfd``, even though it is an anonymous file, is not supported with MTE. +Attempting to create a memslot backed by ``guest_memfd`` when the MTE capability +is enabled, or attempting to enable the MTE capability after +``guest_memfd``-backed memslots have been created, will result in an -EINVAL +return. + When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to perform a bulk copy of tags to/from the guest. diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 10fc6783cd608..5d2435dd76f35 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -96,14 +96,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, set_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER, &kvm->arch.flags); break; - case KVM_CAP_ARM_MTE: - mutex_lock(&kvm->lock); - if (system_supports_mte() && !kvm->created_vcpus) { - r = 0; - set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); + case KVM_CAP_ARM_MTE: { + struct kvm_memory_slot *memslot; + int bkt; + + guard(mutex)(&kvm->lock); + if (!system_supports_mte() || kvm->created_vcpus) + break; + + r = 0; + guard(mutex)(&kvm->slots_lock); + kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm)) { + if (kvm_slot_has_gmem(memslot)) { + r = -EINVAL; + break; + } } - mutex_unlock(&kvm->lock); + if (r == 0) + set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); break; + + } case KVM_CAP_ARM_SYSTEM_SUSPEND: r = 0; set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags); diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 403a635483513..b4237699705b4 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -2323,6 +2323,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new)) return -EINVAL; + /* guest_memfd is incompatible with MTE. */ + if (kvm_slot_has_gmem(new) && kvm_has_mte(kvm)) + return -EINVAL; + hva = new->userspace_addr; reg_end = hva + (new->npages << PAGE_SHIFT); From 6469ad3005087127f1b55cb7e72cc577fb07fc87 Mon Sep 17 00:00:00 2001 From: Fabrice Derepas Date: Wed, 22 Jul 2026 19:22:30 +0300 Subject: [PATCH 2944/3297] KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return type [ Upstream commit 35d661c98fe4733490f20b4311616a3c2c30abc0 ] Two correctness and type-hygiene issues exist in the DCP trusted keys implementation. First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob without checking if it exceeds MAX_KEY_SIZE. If a crafted blob provides a payload_len larger than 128, the subsequent do_aead_crypto() call writes past the end of the p->key array into the adjacent p->blob buffer within the same struct trusted_key_payload -- the caller's own input, not unrelated kernel memory. While not exploitable, this violates strict array bounds and triggers static analyzers. Fix this by adding a validation check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the length, matching the checks already done in trusted_core.c. Second, calc_blob_len() calculates a sum in size_t that truncates to unsigned int on 64-bit platforms. Because the DCP hardware is only present on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally equivalent in production, making this truncation harmless in practice. Nevertheless, updating the return type to size_t (and subsequently updating 'blen' in the seal/unseal paths) resolves type-narrowing warnings and improves overall code hygiene. Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys") Signed-off-by: Fabrice Derepas Reviewed-by: David Gstir Reviewed-by: Richard Weinberger Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c index 7b6eb655df0cb..c078adebe190e 100644 --- a/security/keys/trusted-keys/trusted_dcp.c +++ b/security/keys/trusted-keys/trusted_dcp.c @@ -69,7 +69,7 @@ static bool skip_zk_test; module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0); MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed"); -static unsigned int calc_blob_len(unsigned int payload_len) +static size_t calc_blob_len(unsigned int payload_len) { return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN; } @@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key) static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) { struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; - int blen, ret; + size_t blen; + int ret; u8 *plain_blob_key; blen = calc_blob_len(p->key_len); @@ -242,7 +243,8 @@ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) { struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; - int blen, ret; + size_t blen; + int ret; u8 *plain_blob_key = NULL; if (b->fmt_version != DCP_BLOB_VERSION) { @@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) } p->key_len = le32_to_cpu(b->payload_len); + if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) { + ret = -EINVAL; + goto out; + } + blen = calc_blob_len(p->key_len); if (blen != p->blob_len) { - pr_err("DCP blob has bad length: %i != %i\n", blen, + pr_err("DCP blob has bad length: %zu != %u\n", blen, p->blob_len); ret = -EINVAL; goto out; From e9417d21a22ad2ec398e78fcf084b717ce92cf2f Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Jul 2026 12:15:03 -0400 Subject: [PATCH 2945/3297] keys: fix out-of-bounds read in keyring_get_key_chunk() [ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] For description-level chunks keyring_get_key_chunk() advances the read pointer by level * sizeof(long) past the inline prefix but only bounds-checks the prefix, so a long enough key description is read past its kmemdup(desc, desc_len + 1) allocation. Compute the full byte offset and bounds-check the description against it before reading. The walk only reaches a description-level chunk when two keys collide through the hash, x, type and domain_tag chunks, so this is reached from an unprivileged add_key(2) with a crafted pair of same-type keys whose index hashes collide; KASAN reports a slab-out-of-bounds read. Fixes: f771fde82051 ("keys: Simplify key description management") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- security/keys/keyring.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index e105349794f23..b9ca9dc9b0c3f 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) unsigned long chunk = 0; const u8 *d; int desc_len = index_key->desc_len, n = sizeof(chunk); + unsigned int offset; level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; switch (level) { @@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) return (unsigned long)index_key->domain_tag; default: level -= 4; - if (desc_len <= sizeof(index_key->desc)) + offset = sizeof(index_key->desc) + level * sizeof(long); + if (desc_len <= offset) return 0; - d = index_key->description + sizeof(index_key->desc); - d += level * sizeof(long); - desc_len -= sizeof(index_key->desc); + d = index_key->description + offset; + desc_len -= offset; if (desc_len > n) desc_len = n; do { From 3d9f16c0b643ceac305526b2e2fe25c2c6166926 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Jul 2026 12:15:04 -0400 Subject: [PATCH 2946/3297] keys: make keyring key-chunk byte order agree with keyring_diff_objects() [ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] keyring_get_key_chunk() loads description bytes into the index chunk low address first, while keyring_diff_objects() numbers the first differing bit from the low end and folds the absolute byte index into the level without removing the inline-prefix offset the level already carries. The two disagree on byte order and bit position, so the array can be told two keys first differ at a bit that does not differ in the chunk the walker uses, letting crafted descriptions collide into one node. Load the chunk in the order keyring_diff_objects() assumes and drop the inline-prefix length when folding the byte index into the level. This only changes the in-memory ordering used to place keys within a keyring; add, search and read of non-colliding keys are unaffected. Fixes: f771fde82051 ("keys: Simplify key description management") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- security/keys/keyring.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index b9ca9dc9b0c3f..fd95a0eb7a466 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) desc_len -= offset; if (desc_len > n) desc_len = n; + d += desc_len; do { chunk <<= 8; - chunk |= *d++; + chunk |= *--d; } while (--desc_len > 0); return chunk; } @@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) return -1; differ_plus_i: - level += i; + level += i - (int)sizeof(a->desc); differ: i = level * 8 + __ffs(seg_a ^ seg_b); return i; From adf1a3ba27ad1ea57f1fd31e07f00f76bc464b2d Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Jul 2026 12:15:05 -0400 Subject: [PATCH 2947/3297] assoc_array: trim the final shortcut word using the current chunk end [ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] assoc_array_walk() masks off the bits past shortcut->skip_to_level in the word that contains skip_to_level, gated on round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. That guard is wrong in two opposite ways: - When sc_level is word-aligned (every word after the first) round_up() is a no-op, so the guard is sc_level > skip_to_level and never fires for the word that holds skip_to_level. A shortcut that spans more than one word and ends in the middle of its last word leaves that word untrimmed, and its stale high bits leak into the dissimilarity word and can steer the walk down the wrong descendant. - When sc_level is unaligned (the first word) and skip_to_level sits on the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears the whole dissimilarity word and makes a differing shortcut compare equal. Use the end of the chunk that contains sc_level instead: skip_to_level < round_down(sc_level, CHUNK) + CHUNK For an aligned sc_level whose word holds skip_to_level this now fires (the first bug); for an unaligned sc_level with skip_to_level on the following boundary it does not, so shift is never 0 when the branch runs and the trim never clears the whole word. Fixes: 3cb989501c26 ("Add a generic associative array implementation.") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com Signed-off-by: Jarkko Sakkinen Signed-off-by: Sasha Levin --- lib/assoc_array.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/assoc_array.c b/lib/assoc_array.c index 388e656ac9743..01619e88a52c9 100644 --- a/lib/assoc_array.c +++ b/lib/assoc_array.c @@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; dissimilarity = segments ^ sc_segments; - if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { + if (shortcut->skip_to_level < round_down(sc_level, + ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { /* Trim segments that are beyond the shortcut */ int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; dissimilarity &= ~(ULONG_MAX << shift); From 63ba12b664a2cd3220ed43e22c717715f4cc2ae8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 16 Jul 2026 10:13:37 +0200 Subject: [PATCH 2948/3297] netfilter: nf_tables: make nft_object rhltable per table [ Upstream commit f4f699790590bd0896c48a71e9232a65198f92f0 ] The nft_object rhltable is global, this allows for accessing objects that are being dismangled from lookup path by other existing netns. Given the nft_obj_destroy() releases the object inmediately, this might lead to use-after-free of these objects that are being released. Make the existing rhltable per table to address this issue to deal with with the nft_rcv_nl_event() path too. Update nft_obj_lookup() to take the table as non-const, otherwise, compiler complains when passing the objname_ht to rhltable_lookup(). Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable") Suggested-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/netfilter/nf_tables.h | 4 +++- net/netfilter/nf_tables_api.c | 34 +++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index fda5bc82dd504..e6b31aa9d6b7d 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1288,6 +1288,7 @@ static inline void nft_use_inc_restore(u32 *use) * @sets: sets in the table * @objects: stateful objects in the table * @flowtables: flow tables in the table + * @objname_ht: hashtable for objects lookup by name * @hgenerator: handle generator state * @handle: table handle * @use: number of chain references to this table @@ -1307,6 +1308,7 @@ struct nft_table { struct list_head sets; struct list_head objects; struct list_head flowtables; + struct rhltable objname_ht; u64 hgenerator; u64 handle; u32 use; @@ -1394,7 +1396,7 @@ static inline void *nft_obj_data(const struct nft_object *obj) #define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr)) struct nft_object *nft_obj_lookup(const struct net *net, - const struct nft_table *table, + struct nft_table *table, const struct nlattr *nla, u32 objtype, u8 genmask); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 51d3d4d44918c..ca6d2041eee66 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -44,8 +44,6 @@ enum { NFT_VALIDATE_DO, }; -static struct rhltable nft_objname_ht; - static u32 nft_chain_hash(const void *data, u32 len, u32 seed); static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed); static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *); @@ -1600,6 +1598,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, if (err) goto err_chain_ht; + err = rhltable_init(&table->objname_ht, &nft_objname_ht_params); + if (err < 0) + goto err_obj_ht; + INIT_LIST_HEAD(&table->chains); INIT_LIST_HEAD(&table->sets); INIT_LIST_HEAD(&table->objects); @@ -1618,6 +1620,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, list_add_tail_rcu(&table->list, &nft_net->tables); return 0; err_trans: + rhltable_destroy(&table->objname_ht); +err_obj_ht: rhltable_destroy(&table->chains_ht); err_chain_ht: kfree(table->udata); @@ -1784,6 +1788,7 @@ static void nf_tables_table_destroy(struct nft_table *table) return; rhltable_destroy(&table->chains_ht); + rhltable_destroy(&table->objname_ht); kfree(table->name); kfree(table->udata); kfree(table); @@ -7957,7 +7962,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type) EXPORT_SYMBOL_GPL(nft_unregister_obj); struct nft_object *nft_obj_lookup(const struct net *net, - const struct nft_table *table, + struct nft_table *table, const struct nlattr *nla, u32 objtype, u8 genmask) { @@ -7973,7 +7978,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, !lockdep_commit_lock_is_held(net)); rcu_read_lock(); - list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params); + list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params); if (!list) goto out; @@ -8252,7 +8257,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info, if (err < 0) goto err_trans; - err = rhltable_insert(&nft_objname_ht, &obj->rhlhead, + err = rhltable_insert(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); if (err < 0) goto err_obj_ht; @@ -8437,8 +8442,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info, struct netlink_ext_ack *extack = info->extack; u8 genmask = nft_genmask_cur(info->net); u8 family = info->nfmsg->nfgen_family; - const struct nft_table *table; struct net *net = info->net; + struct nft_table *table; struct nft_object *obj; struct sk_buff *skb2; u32 objtype; @@ -10270,9 +10275,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain) nf_tables_commit_chain_free_rules_old(g0); } -static void nft_obj_del(struct nft_object *obj) +static void nft_obj_del(struct nft_table *table, struct nft_object *obj) { - rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params); + rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); list_del_rcu(&obj->list); } @@ -10960,7 +10965,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) break; case NFT_MSG_DELOBJ: case NFT_MSG_DESTROYOBJ: - nft_obj_del(nft_trans_obj(trans)); + nft_obj_del(table, nft_trans_obj(trans)); nf_tables_obj_notify(&ctx, nft_trans_obj(trans), trans->msg_type); break; @@ -11251,7 +11256,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) nft_trans_destroy(trans); } else { nft_use_dec_restore(&table->use); - nft_obj_del(nft_trans_obj(trans)); + nft_obj_del(table, nft_trans_obj(trans)); } break; case NFT_MSG_DELOBJ: @@ -11877,7 +11882,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table) nft_set_destroy(&ctx, set); } list_for_each_entry_safe(obj, ne, &table->objects, list) { - nft_obj_del(obj); + nft_obj_del(table, obj); nft_use_dec(&table->use); nft_obj_destroy(&ctx, obj); } @@ -12059,10 +12064,6 @@ static int __init nf_tables_module_init(void) if (err < 0) goto err_netdev_notifier; - err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params); - if (err < 0) - goto err_rht_objname; - err = nft_offload_init(); if (err < 0) goto err_offload; @@ -12085,8 +12086,6 @@ static int __init nf_tables_module_init(void) err_netlink_notifier: nft_offload_exit(); err_offload: - rhltable_destroy(&nft_objname_ht); -err_rht_objname: unregister_netdevice_notifier(&nf_tables_flowtable_notifier); err_netdev_notifier: nf_tables_core_module_exit(); @@ -12108,7 +12107,6 @@ static void __exit nf_tables_module_exit(void) unregister_pernet_subsys(&nf_tables_net_ops); cancel_work_sync(&trans_gc_work); rcu_barrier(); - rhltable_destroy(&nft_objname_ht); nf_tables_core_module_exit(); } From d186f77d18bdfb252d401ff992ca3001a6a65a0f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 21 Jul 2026 22:02:46 +0200 Subject: [PATCH 2949/3297] netfilter: xt_hashlimit: validate hashtable supports XT_HASHLIMIT_RATE_MATCH [ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the dsthash_ent structure which represents an entry in the hashtable. There is a union area which uses a different layout to express the rate match mode. Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode flag is requested by two or more different rules that refer to the same hashtable. Otherwise, uninitialized access to the burst field in the union is possible. Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by revision less than 3 too. Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") Reported-and-tested-by: Talha Berk Arslan Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 3b507694e81e5..b3f15bbf259e2 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -117,6 +117,7 @@ struct xt_hashlimit_htable { refcount_t use; u_int8_t family; bool rnd_initialized; + bool ratematch; struct hashlimit_cfg3 cfg; /* config */ @@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, kvfree(hinfo); return -ENOMEM; } + hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); spin_lock_init(&hinfo->lock); switch (revision) { @@ -870,7 +872,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, } /* Check for overflow. */ - if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { + if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { + if (revision < 3) + return -EINVAL; + if (cfg->avg == 0 || cfg->avg > U32_MAX) { pr_info_ratelimited("invalid rate\n"); return -ERANGE; @@ -903,6 +908,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, mutex_unlock(&hashlimit_mutex); return ret; } + } else { + if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && + !(*hinfo)->ratematch) || + (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && + (*hinfo)->ratematch)) { + mutex_unlock(&hashlimit_mutex); + htable_put(*hinfo); + return -EINVAL; + } } mutex_unlock(&hashlimit_mutex); From 00eb23829fd08df1b5e057cb6b625996a70e7e65 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 22 Jul 2026 13:15:15 +0300 Subject: [PATCH 2950/3297] ipvs: fix the checksum validations [ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ] ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 packets from clients. In fact, as for TCP/UDP we should validate the checksum for ICMP packets only when we mangle the packets on MASQ or on reply for tunnel. Also, Sashiko points out that handle_response_icmp() being common for IPv4 and IPv6 is missing the pseudo-header calculation while validating ICMPv6 messages from real servers which is a problem if checksum is not validated by the hardware. Fix the problems by creating ip_vs_checksum_common_check() helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. Rely on the nf_checksum() for validating the ICMP messages but use it also for TCP and UDP. Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP. IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum validation on LOCAL_OUT (local clients or local real servers) and on FORWARD (traffic from servers on LAN). Do it only on LOCAL_IN, in case nf_checksum() is not called on PRE_ROUTING. Also, ip_vs_checksum_complete() can be marked static. Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/ip_vs.h | 31 +++++++++++++++-- net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- 5 files changed, 74 insertions(+), 86 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 2be04ca409d1f..7dcaadc32a76f 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -24,7 +24,9 @@ #include /* for union nf_inet_addr */ #include #include /* for struct ipv6hdr */ +#include #include +#include #if IS_ENABLED(CONFIG_NF_CONNTRACK) #include #endif @@ -1724,8 +1726,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir); #endif -__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); - static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) { __be32 diff[2] = { ~old, new }; @@ -1751,6 +1751,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) return csum_partial(diff, sizeof(diff), oldsum); } +static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) +{ + /* Checksum unnecessary or already validated? */ + if (skb_csum_unnecessary(skb)) + return false; + /* LOCAL_OUT ? */ + if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) + return false; + /* !LOCAL_IN (FORWARD) ? */ + if (af == AF_INET6) { + if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) + return false; + } else { + if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) + return false; + } + return true; +} + +static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, + int offset, int proto, int af) +{ + if (!ip_vs_checksum_needed(skb, af)) + return true; + return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); +} + /* Forget current conntrack (unconfirmed) and attach notrack entry */ static inline void ip_vs_notrack(struct sk_buff *skb) { diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 90619453cb6f0..306a8227d300e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; } #endif -__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) +static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) { return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0)); } @@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, unsigned int offset, unsigned int ihl, unsigned int hooknum) { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; unsigned int verdict = NF_DROP; if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) goto after_nat; /* Ensure the checksum is correct */ - if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { + if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { /* Failed checksum! */ IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", IP_VS_DBG_ADDR(af, snet)); @@ -1720,7 +1721,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, verdict = NF_DROP; /* Ensure the checksum is correct */ - if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { + if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) && + !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { /* Failed checksum! */ IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", &iph->saddr); @@ -1886,6 +1888,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, goto out; } + verdict = NF_DROP; + + /* Ensure the checksum is correct */ + if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ && + !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6, + AF_INET6)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n", + &iph->saddr); + goto out; + } + /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index c67317be17dfa..f6f732b7dfa86 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -11,7 +11,7 @@ static int sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int sctphoff); + struct ip_vs_iphdr *iph); static int sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, @@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) + if (!sctp_csum_check(cp->af, skb, pp, iph)) return 0; /* Call application helper if needed */ @@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) + if (!sctp_csum_check(cp->af, skb, pp, iph)) return 0; /* Call application helper if needed */ @@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, static int sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int sctphoff) + struct ip_vs_iphdr *iph) { + unsigned int sctphoff = iph->len; struct sctphdr *sh; __le32 cmp, val; + if (!ip_vs_checksum_needed(skb, af)) + return 1; sh = (struct sctphdr *)(skb->data + sctphoff); cmp = sh->checksum; val = sctp_compute_cksum(skb, sctphoff); if (val != cmp) { /* CRC failure, dump it. */ - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); + IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, + "Failed checksum for"); return 0; } return 1; diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index b382810156b2c..bf31127338aa0 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -30,7 +30,7 @@ static int tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int tcphoff); + struct ip_vs_iphdr *iph); static int tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, @@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) + if (!tcp_csum_check(cp->af, skb, pp, iph)) return 0; /* Call application helper if needed */ @@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) + if (!tcp_csum_check(cp->af, skb, pp, iph)) return 0; /* @@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, static int tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int tcphoff) + struct ip_vs_iphdr *iph) { - switch (skb->ip_summed) { - case CHECKSUM_NONE: - skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); - fallthrough; - case CHECKSUM_COMPLETE: -#ifdef CONFIG_IP_VS_IPV6 - if (af == AF_INET6) { - if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - skb->len - tcphoff, - IPPROTO_TCP, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - } else -#endif - if (csum_tcpudp_magic(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, - skb->len - tcphoff, - ip_hdr(skb)->protocol, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - break; - default: - /* No need to checksum. */ - break; + if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) { + IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, + "Failed checksum for"); + return 0; } - return 1; } diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index dbd4155bb0752..40d30649b3048 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -26,7 +26,7 @@ static int udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int udphoff); + struct ip_vs_iphdr *iph); static int udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, @@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!udp_csum_check(cp->af, skb, pp, udphoff)) + if (!udp_csum_check(cp->af, skb, pp, iph)) return 0; /* @@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!udp_csum_check(cp->af, skb, pp, udphoff)) + if (!udp_csum_check(cp->af, skb, pp, iph)) return 0; /* @@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, static int udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int udphoff) + struct ip_vs_iphdr *iph) { struct udphdr _udph, *uh; - uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); + uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph); if (uh == NULL) return 0; - if (uh->check != 0) { - switch (skb->ip_summed) { - case CHECKSUM_NONE: - skb->csum = skb_checksum(skb, udphoff, - skb->len - udphoff, 0); - fallthrough; - case CHECKSUM_COMPLETE: -#ifdef CONFIG_IP_VS_IPV6 - if (af == AF_INET6) { - if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - skb->len - udphoff, - IPPROTO_UDP, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - } else -#endif - if (csum_tcpudp_magic(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, - skb->len - udphoff, - ip_hdr(skb)->protocol, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - break; - default: - /* No need to checksum. */ - break; - } + if (!uh->check) + return 1; + if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) { + IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, + "Failed checksum for"); + return 0; } return 1; } From ce96c40a049be2c539b8bfabb43b33d1775a549e Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 22 Jul 2026 13:15:16 +0300 Subject: [PATCH 2951/3297] ipvs: fix places with wrong packet offsets [ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ] The offsets we use to packet headers and payloads should be based on skb->data. We even already respect non-zero network offset in ip_vs_fill_iph_skb() but some places do it wrongly and support only zero offset which is expected for the IP layer where IPVS has hooks. Change all places that instead of skb->data use offsets based on the network header (skb_network_header, ip_hdr, etc) because this doubles the network offset as noted by Sashiko. For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header parsing done by the caller. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/ip_vs.h | 15 +-- net/netfilter/ipvs/ip_vs_app.c | 4 +- net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- 7 files changed, 97 insertions(+), 93 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 7dcaadc32a76f..71ffbba542464 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1639,8 +1639,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, - unsigned int hooknum, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph); void ip_vs_dest_dst_rcu_free(struct rcu_head *head); #ifdef CONFIG_IP_VS_IPV6 @@ -1653,8 +1654,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, - unsigned int hooknum, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph); #endif #ifdef CONFIG_SYSCTL @@ -1719,11 +1721,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) } void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir); + struct ip_vs_conn *cp, int dir, unsigned int toff); #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir); + struct ip_vs_conn *cp, int dir, unsigned int toff, + struct ip_vs_iphdr *ciph); #endif static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index f9132b359f0c6..0c690a30a85dc 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + ipvsh->len); /* * Remember seq number in case this pkt gets resized @@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + ipvsh->len); /* * Remember seq number in case this pkt gets resized diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 306a8227d300e..784c00ec01ef7 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, * - inout: 1=in->out, 0=out->in */ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int inout) + struct ip_vs_conn *cp, int inout, unsigned int toff) { struct iphdr *iph = ip_hdr(skb); - unsigned int icmp_offset = iph->ihl*4; - struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + - icmp_offset); + struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); struct iphdr *ciph = (struct iphdr *)(icmph + 1); + unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); if (inout) { iph->saddr = cp->vaddr.ip; @@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, /* And finally the ICMP checksum */ icmph->checksum = 0; - icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset); + icmph->checksum = ip_vs_checksum_complete(skb, toff); skb->ip_summed = CHECKSUM_UNNECESSARY; if (inout) - IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, - "Forwarding altered outgoing ICMP"); + IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + "Forwarding altered outgoing ICMP"); else - IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, - "Forwarding altered incoming ICMP"); + IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + "Forwarding altered incoming ICMP"); } #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int inout) + struct ip_vs_conn *cp, int inout, unsigned int toff, + struct ip_vs_iphdr *ciph) { struct ipv6hdr *iph = ipv6_hdr(skb); - unsigned int icmp_offset = 0; - unsigned int offs = 0; /* header offset*/ int protocol; struct icmp6hdr *icmph; - struct ipv6hdr *ciph; - unsigned short fragoffs; + struct ipv6hdr *cih; - ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); - icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); - offs = icmp_offset + sizeof(struct icmp6hdr); - ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); + icmph = (struct icmp6hdr *)(skb->data + toff); + cih = (struct ipv6hdr *)(skb->data + ciph->off); - protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); + protocol = ciph->protocol; if (inout) { iph->saddr = cp->vaddr.in6; - ciph->daddr = cp->vaddr.in6; + cih->daddr = cp->vaddr.in6; } else { iph->daddr = cp->daddr.in6; - ciph->saddr = cp->daddr.in6; + cih->saddr = cp->daddr.in6; } /* the TCP/UDP/SCTP port */ - if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || - IPPROTO_SCTP == protocol)) { - __be16 *ports = (void *)(skb_network_header(skb) + offs); + if (!ciph->fragoffs && + (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || + protocol == IPPROTO_SCTP)) { + __be16 *ports = (void *)(skb->data + ciph->len); IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, ntohs(inout ? ports[1] : ports[0]), @@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, /* And finally the ICMP checksum */ icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, - skb->len - icmp_offset, + skb->len - toff, IPPROTO_ICMPV6, 0); - skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset; + skb->csum_start = skb_headroom(skb) + toff; skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); skb->ip_summed = CHECKSUM_PARTIAL; if (inout) - IP_VS_DBG_PKT(11, AF_INET6, pp, skb, - (void *)ciph - (void *)iph, + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, "Forwarding altered outgoing ICMPv6"); else - IP_VS_DBG_PKT(11, AF_INET6, pp, skb, - (void *)ciph - (void *)iph, + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, "Forwarding altered incoming ICMPv6"); } #endif @@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, */ static int handle_response_icmp(int af, struct sk_buff *skb, union nf_inet_addr *snet, - __u8 protocol, struct ip_vs_conn *cp, + struct ip_vs_conn *cp, struct ip_vs_protocol *pp, - unsigned int offset, unsigned int ihl, - unsigned int hooknum) + struct ip_vs_iphdr *ciph, + unsigned int toff, unsigned int hooknum) { int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; unsigned int verdict = NF_DROP; + unsigned int ctoff = ciph->len; if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) goto after_nat; /* Ensure the checksum is correct */ - if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { + if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) { /* Failed checksum! */ IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", IP_VS_DBG_ADDR(af, snet)); goto out; } - if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || - IPPROTO_SCTP == protocol) - offset += 2 * sizeof(__u16); - if (skb_ensure_writable(skb, offset)) + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || + ciph->protocol == IPPROTO_SCTP) + ctoff += 2 * sizeof(__u16); + if (skb_ensure_writable(skb, ctoff)) goto out; #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) - ip_vs_nat_icmp_v6(skb, pp, cp, 1); + ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1); + ip_vs_nat_icmp(skb, pp, cp, 1, toff); if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; @@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb, * Currently handles error types - unreachable, quench, ttl exceeded. */ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, - int *related, unsigned int hooknum) + int *related, unsigned int hooknum, + struct ip_vs_iphdr *ipvsh) { - struct iphdr *iph; struct icmphdr _icmph, *ic; struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ struct ip_vs_iphdr ciph; @@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, if (ip_is_fragment(ip_hdr(skb))) { if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) return NF_STOLEN; + if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh)) + return NF_ACCEPT; } - iph = ip_hdr(skb); - offset = ihl = iph->ihl * 4; + ihl = ipvsh->len; + offset = ipvsh->len; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) return NF_DROP; IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n", ic->type, ntohs(icmp_id(ic)), - &iph->saddr, &iph->daddr); + &ipvsh->saddr.ip, &ipvsh->daddr.ip); /* * Work through seeing if this is for us. @@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ pp = ip_vs_proto_get(cih->protocol); @@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, if (!cp) return NF_ACCEPT; - snet.ip = iph->saddr; - return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, - pp, ciph.len, ihl, hooknum); + snet.ip = ipvsh->saddr.ip; + return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, + hooknum); } #ifdef CONFIG_IP_VS_IPV6 @@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, struct ip_vs_conn *cp; struct ip_vs_protocol *pp; union nf_inet_addr snet; - unsigned int offset; *related = 1; ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph); @@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; snet.in6 = ciph.saddr.in6; - offset = ciph.len; - return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, - pp, offset, ipvsh->len, hooknum); + return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph, + ipvsh->len, hooknum); } #endif @@ -1368,7 +1363,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat #endif if (unlikely(iph.protocol == IPPROTO_ICMP)) { int related; - int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum); + int verdict = ip_vs_out_icmp(ipvs, skb, &related, + hooknum, &iph); if (related) return verdict; @@ -1576,9 +1572,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, */ static int ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, - unsigned int hooknum) + unsigned int hooknum, struct ip_vs_iphdr *iph) { - struct iphdr *iph; struct icmphdr _icmph, *ic; struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ struct ip_vs_iphdr ciph; @@ -1588,7 +1583,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, unsigned int offset, offset2, ihl, verdict; bool tunnel, new_cp = false; union nf_inet_addr *raddr; - char *outer_proto = "IPIP"; + char *outer_proto __maybe_unused = "IPIP"; unsigned int hlen_ipip; int ulen = 0; @@ -1598,17 +1593,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (ip_is_fragment(ip_hdr(skb))) { if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) return NF_STOLEN; + if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) + return NF_ACCEPT; } - iph = ip_hdr(skb); - offset = ihl = iph->ihl * 4; + ihl = iph->len; + offset = iph->len; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) return NF_DROP; IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n", ic->type, ntohs(icmp_id(ic)), - &iph->saddr, &iph->daddr); + &iph->saddr.ip, &iph->daddr.ip); /* * Work through seeing if this is for us. @@ -1725,7 +1722,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { /* Failed checksum! */ IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", - &iph->saddr); + &iph->saddr.ip); goto out; } @@ -1796,7 +1793,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || IPPROTO_SCTP == cih->protocol) offset += 2 * sizeof(__u16); - verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph); + verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, + &ciph); out: if (likely(!new_cp)) @@ -1909,7 +1907,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, IPPROTO_SCTP == ciph.protocol) offset += 2 * sizeof(__u16); /* Also mangle ports */ - verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph); + verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, + &ciph); out: if (likely(!new_cp)) @@ -1988,7 +1987,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state if (unlikely(iph.protocol == IPPROTO_ICMP)) { int related; int verdict = ip_vs_in_icmp(ipvs, skb, &related, - hooknum); + hooknum, &iph); if (related) return verdict; @@ -2124,6 +2123,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct netns_ipvs *ipvs = net_ipvs(state->net); + struct ip_vs_iphdr iphdr; int r; /* ipvs enabled in this netns ? */ @@ -2133,10 +2133,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, if (state->pf == NFPROTO_IPV4) { if (ip_hdr(skb)->protocol != IPPROTO_ICMP) return NF_ACCEPT; + ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr); #ifdef CONFIG_IP_VS_IPV6 } else { - struct ip_vs_iphdr iphdr; - ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr); if (iphdr.protocol != IPPROTO_ICMPV6) @@ -2146,7 +2145,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, #endif } - return ip_vs_in_icmp(ipvs, skb, &r, state->hook); + return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); } static const struct nf_hook_ops ip_vs_ops4[] = { diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index f6f732b7dfa86..3dbd3096e1637 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *) skb_network_header(skb) + sctphoff; + sctph = (void *)skb->data + sctphoff; /* Only update csum if we really have to */ if (sctph->source != cp->vport || payload_csum || @@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *) skb_network_header(skb) + sctphoff; + sctph = (void *)skb->data + sctphoff; /* Only update csum if we really have to */ if (sctph->dest != cp->dport || payload_csum || diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index bf31127338aa0..1ac9c233537d3 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header(skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->source = cp->vport; /* Adjust TCP checksums */ @@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header(skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->dest = cp->dport; /* diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 40d30649b3048..96ac882df15c1 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header(skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->source = cp->vport; /* @@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header(skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->dest = cp->dport; /* diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index f564b4197cee9..fba791d140e70 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1499,8 +1499,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, */ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, unsigned int hooknum, - struct ip_vs_iphdr *iph) + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph) { struct rtable *rt; /* Route to the other host */ int rc; @@ -1512,7 +1513,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, translate address/port back */ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (cp->packet_xmit) - rc = cp->packet_xmit(skb, cp, pp, iph); + rc = cp->packet_xmit(skb, cp, pp, ciph); else rc = NF_ACCEPT; /* do not touch skb anymore */ @@ -1530,7 +1531,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, - NULL, iph); + NULL, ciph); if (local < 0) goto tx_error; rt = skb_rtable(skb); @@ -1562,13 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, } /* copy-on-write the packet before mangling it */ - if (skb_ensure_writable(skb, offset)) + if (skb_ensure_writable(skb, wlen)) goto tx_error; if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0); + ip_vs_nat_icmp(skb, pp, cp, 0, toff); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; @@ -1584,8 +1585,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, unsigned int hooknum, - struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph) { struct rt6_info *rt; /* Route to the other host */ int rc; @@ -1597,7 +1599,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, translate address/port back */ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (cp->packet_xmit) - rc = cp->packet_xmit(skb, cp, pp, ipvsh); + rc = cp->packet_xmit(skb, cp, pp, ciph); else rc = NF_ACCEPT; /* do not touch skb anymore */ @@ -1614,7 +1616,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, - &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); + &cp->daddr.in6, NULL, ciph, 0, rt_mode); if (local < 0) goto tx_error; rt = dst_rt6_info(skb_dst(skb)); @@ -1646,13 +1648,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, } /* copy-on-write the packet before mangling it */ - if (skb_ensure_writable(skb, offset)) + if (skb_ensure_writable(skb, wlen)) goto tx_error; if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp_v6(skb, pp, cp, 0); + ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; From e6f4b4b40db87ef35bf141ad2fb8815b8b746508 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 22 Jul 2026 13:15:17 +0300 Subject: [PATCH 2952/3297] ipvs: do not mangle ICMP replies for non-first fragments [ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ] Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the payload for embedded non-first IPv4 fragments. The problem is in the very old inverted pp->dont_defrag check which should not continue when embedded is a non-first TCP/UDP/SCTP fragment. Check for embedded non-first fragment is also missing from ip_vs_out_icmp_v6(), it is needed before any connection lookups that expect ports after the network headers. Drop the blocking code from ip_vs_in_icmp_v6() which prevents ICMPv6 from local clients to use non-MASQ forwarding. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/net/ip_vs.h | 11 +++--- net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- 3 files changed, 48 insertions(+), 52 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 71ffbba542464..17a97086b294f 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1640,8 +1640,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph); + unsigned int hooknum, struct ip_vs_iphdr *ciph); void ip_vs_dest_dst_rcu_free(struct rcu_head *head); #ifdef CONFIG_IP_VS_IPV6 @@ -1655,8 +1654,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph); + unsigned int hooknum, struct ip_vs_iphdr *ciph); #endif #ifdef CONFIG_SYSCTL @@ -1721,12 +1719,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) } void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir, unsigned int toff); + struct ip_vs_conn *cp, int dir, unsigned int toff, + bool has_ports); #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir, unsigned int toff, - struct ip_vs_iphdr *ciph); + bool has_ports, struct ip_vs_iphdr *ciph); #endif static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 784c00ec01ef7..6207a91e93f3b 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, * - inout: 1=in->out, 0=out->in */ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int inout, unsigned int toff) + struct ip_vs_conn *cp, int inout, unsigned int toff, + bool has_ports) { struct iphdr *iph = ip_hdr(skb); struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); @@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, } /* the TCP/UDP/SCTP port */ - if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol || - IPPROTO_SCTP == ciph->protocol) { + if (has_ports) { __be16 *ports = (void *)ciph + ciph->ihl*4; if (inout) @@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int inout, unsigned int toff, - struct ip_vs_iphdr *ciph) + bool has_ports, struct ip_vs_iphdr *ciph) { struct ipv6hdr *iph = ipv6_hdr(skb); - int protocol; struct icmp6hdr *icmph; struct ipv6hdr *cih; icmph = (struct icmp6hdr *)(skb->data + toff); cih = (struct ipv6hdr *)(skb->data + ciph->off); - protocol = ciph->protocol; - if (inout) { iph->saddr = cp->vaddr.in6; cih->daddr = cp->vaddr.in6; @@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, } /* the TCP/UDP/SCTP port */ - if (!ciph->fragoffs && - (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || - protocol == IPPROTO_SCTP)) { + if (has_ports) { __be16 *ports = (void *)(skb->data + ciph->len); IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, @@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; unsigned int verdict = NF_DROP; unsigned int ctoff = ciph->len; + bool has_ports = false; if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) goto after_nat; @@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb, } if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || - ciph->protocol == IPPROTO_SCTP) + ciph->protocol == IPPROTO_SCTP) { ctoff += 2 * sizeof(__u16); + has_ports = true; + } if (skb_ensure_writable(skb, ctoff)) goto out; #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) - ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); + ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1, toff); + ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; @@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; /* Is the embedded protocol header present? */ - if (unlikely(cih->frag_off & htons(IP_OFFSET) && - pp->dont_defrag)) + if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) return NF_ACCEPT; IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, @@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, if (!pp) return NF_ACCEPT; + /* Is the embedded protocol header present? */ + if (unlikely(ciph.fragoffs && !pp->dont_defrag)) + return NF_ACCEPT; + /* The embedded headers contain source and dest in reverse order */ cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, ipvs, AF_INET6, skb, &ciph); @@ -1687,8 +1688,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, pp = pd->pp; /* Is the embedded protocol header present? */ - if (unlikely(cih->frag_off & htons(IP_OFFSET) && - pp->dont_defrag)) + if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) return NF_ACCEPT; IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, @@ -1696,7 +1696,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, offset2 = offset; ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); - offset = ciph.len; /* The embedded headers contain source and dest in reverse order. * For IPIP/UDP/GRE tunnel this is error for request, not for reply. @@ -1790,11 +1789,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); - if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || - IPPROTO_SCTP == cih->protocol) - offset += 2 * sizeof(__u16); - verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, - &ciph); + verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); out: if (likely(!new_cp)) @@ -1854,8 +1849,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; pp = pd->pp; - /* Cannot handle fragmented embedded protocol */ - if (ciph.fragoffs) + /* Is the embedded protocol header present? */ + if (ciph.fragoffs && !pp->dont_defrag) return NF_ACCEPT; IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, @@ -1879,13 +1874,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, new_cp = true; } - /* VS/TUN, VS/DR and LOCALNODE just let it go */ - if ((hooknum == NF_INET_LOCAL_OUT) && - (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { - verdict = NF_ACCEPT; - goto out; - } - verdict = NF_DROP; /* Ensure the checksum is correct */ @@ -1901,14 +1889,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); - /* Need to mangle contained IPv6 header in ICMPv6 packet */ - offset = ciph.len; - if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || - IPPROTO_SCTP == ciph.protocol) - offset += 2 * sizeof(__u16); /* Also mangle ports */ - - verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, - &ciph); + verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); out: if (likely(!new_cp)) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index fba791d140e70..ac1827ef78949 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1500,13 +1500,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph) + unsigned int hooknum, struct ip_vs_iphdr *ciph) { struct rtable *rt; /* Route to the other host */ int rc; int local; int rt_mode, was_input; + bool has_ports = false; + unsigned int wlen; /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be forwarded directly here, because there is no need to @@ -1562,6 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || + ciph->protocol == IPPROTO_SCTP) { + wlen += 2 * sizeof(__u16); /* Also mangle ports */ + has_ports = true; + } + /* copy-on-write the packet before mangling it */ if (skb_ensure_writable(skb, wlen)) goto tx_error; @@ -1569,7 +1577,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0, toff); + ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; @@ -1586,10 +1594,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph) + unsigned int hooknum, struct ip_vs_iphdr *ciph) { + bool has_ports = false; struct rt6_info *rt; /* Route to the other host */ + unsigned int wlen; int rc; int local; int rt_mode; @@ -1647,6 +1656,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || + ciph->protocol == IPPROTO_SCTP) { + wlen += 2 * sizeof(__u16); /* Also mangle ports */ + has_ports = true; + } + /* copy-on-write the packet before mangling it */ if (skb_ensure_writable(skb, wlen)) goto tx_error; @@ -1654,7 +1670,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); + ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; From 16b553c46e347bc9de9946c4960654d5884a86de Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Sun, 19 Jul 2026 22:15:23 +0000 Subject: [PATCH 2953/3297] netfilter: nft_payload: fix mask build for partial field offload [ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] nft_payload_offload_mask() builds the offload match mask for a payload expression that covers only part of a header field. For a partial IPv6 address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which is undefined on the 32-bit int operand. It also trims only one word, so the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 the trim is skipped entirely), leaving the mask covering more bytes than the rule matches. UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 shift exponent 120 is too large for 32-bit type 'int' ... The match is byte-granular and struct nft_data is zero-initialised, so the correct mask is simply the first priv_len bytes set to 0xff. Set those bytes directly and drop the word/shift trimming; this removes the undefined shift and no longer over-masks the trailing bytes. Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_payload.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 86a21c2b954f8..e07888aaf1475 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -259,9 +259,7 @@ static int nft_payload_dump(struct sk_buff *skb, static bool nft_payload_offload_mask(struct nft_offload_reg *reg, u32 priv_len, u32 field_len) { - unsigned int remainder, delta, k; struct nft_data mask = {}; - __be32 remainder_mask; if (priv_len == field_len) { memset(®->mask, 0xff, priv_len); @@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, return false; } - memset(&mask, 0xff, field_len); - remainder = priv_len % sizeof(u32); - if (remainder) { - k = priv_len / sizeof(u32); - delta = field_len - priv_len; - remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); - mask.data[k] = (__force u32)remainder_mask; - } - + memset(&mask, 0xff, priv_len); memcpy(®->mask, &mask, field_len); return true; From 4147866087fbef2ea602e274c046c79d469fa478 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:35:00 +0100 Subject: [PATCH 2954/3297] ASoC: SDCA: Ensure that Control Range is large enough for header [ Upstream commit 951e921b039b793bef7050eaf5c5fb1a4a5341d1 ] When reading the Ranges structure from an SDCA Control, ensure that the read data is large enough to encompass the required header before accessing it. Fixes: 64fb5af1d1bb ("ASoC: SDCA: Add parsing for Control range structures") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sdca/sdca_functions.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 4417278e39bb1..e0cae47641ba5 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -849,6 +849,8 @@ static int find_sdca_control_range(struct device *dev, return 0; else if (num_range < 0) return num_range; + else if (num_range < 2 * sizeof(*limits)) + return -EINVAL; range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL); if (!range_list) From f6787fdffcae5490c779f0f3f33b11597525d1ae Mon Sep 17 00:00:00 2001 From: Ilia Gavrilov Date: Thu, 9 Jul 2026 16:27:54 +0000 Subject: [PATCH 2955/3297] rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled [ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ] When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst is never initialized because inet6_init() exits before addrconf_init() is called to initialize it. An attempt to bind an RDS socket to an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 Call Trace: ipv6_chk_addr+0x3b/0x50 rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] rds_trans_get_preferred+0x15d/0x2d0 [rds] ? trace_hardirqs_on+0x2d/0x110 rds_bind+0x1433/0x1d60 [rds] ? rds_remove_bound+0xd50/0xd50 [rds] ? aa_af_perm+0x250/0x250 ? __might_fault+0xde/0x190 ? __sys_bind+0x1dc/0x210 __sys_bind+0x1dc/0x210 ? __ia32_sys_socketpair+0x100/0x100 ? restore_fpregs_from_fpstate+0x53/0x100 __x64_sys_bind+0x73/0xb0 ? syscall_enter_from_user_mode+0x1c/0x50 do_syscall_64+0x34/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 RIP: 0033:0x7f47f8269ea9 The following code reproduces the issue: struct sockaddr_in6 addr; s = socket(PF_RDS, SOCK_SEQPACKET, 0); memset(&addr, 0, sizeof(addr)); inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); addr.sin6_family = AF_INET6; addr.sin6_port = htons(PORT); bind(s, &addr, sizeof(addr)); Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") Signed-off-by: Ilia Gavrilov Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru Signed-off-by: Jakub Kicinski Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()") Signed-off-by: Sasha Levin --- net/rds/ib.c | 4 ++++ net/rds/ib_cm.c | 4 ++++ net/rds/tcp.c | 8 +++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/net/rds/ib.c b/net/rds/ib.c index ce5be43c5fbac..1061bcf7d1315 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, sa = (struct sockaddr *)&sin; } else { #if IS_ENABLED(CONFIG_IPV6) + if (!ipv6_mod_enabled()) { + ret = -EADDRNOTAVAIL; + goto out; + } memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr; diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 5289afbb61aa7..e50e01abb0799 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, dp = event->param.conn.private_data; if (isv6) { #if IS_ENABLED(CONFIG_IPV6) + if (!ipv6_mod_enabled()) { + err = -EOPNOTSUPP; + goto out; + } dp_cmn = &dp->ricp_v6.dp_cmn; saddr6 = &dp->ricp_v6.dp_saddr; daddr6 = &dp->ricp_v6.dp_daddr; diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 06a2d8d48bbac..87e6ab0e93b71 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -342,9 +342,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, rcu_read_unlock(); } #if IS_ENABLED(CONFIG_IPV6) - ret = ipv6_chk_addr(net, addr, dev, 0); - if (ret) - return 0; + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); + if (ret) + return 0; + } #endif return -EADDRNOTAVAIL; } From b1d480fce05f857dc438080cd8c9244b84a83494 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 22 Jul 2026 14:02:03 -0700 Subject: [PATCH 2956/3297] rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check() [ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] rds_tcp_laddr_check() looks up a scoped IPv6 interface with dev_get_by_index_rcu(), drops the RCU read-side lock, and only then passes the bare struct net_device * into ipv6_chk_addr(). dev_get_by_index_rcu() only keeps the device alive within the same RCU read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can free the net_device; ipv6_chk_addr() then dereferences the stale pointer in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading freed memory. Keep the RCU read-side lock held across the ipv6_chk_addr() call instead of dropping it right after the lookup, so the device cannot be freed while it is in use. BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) Read of size 8 at addr ffff8880106ec000 by task exploit/153 Call Trace: ... kasan_report (mm/kasan/report.c:595) __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) rds_tcp_laddr_check (net/rds/tcp.c:370) rds_bind (net/rds/bind.c:248) __sys_bind (net/socket.c:1920) __x64_sys_bind (net/socket.c:1956) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/rds/tcp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 87e6ab0e93b71..1980a197034ba 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -331,23 +331,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, /* If the scope_id is specified, check only those addresses * hosted on the specified interface. */ + rcu_read_lock(); if (scope_id != 0) { - rcu_read_lock(); dev = dev_get_by_index_rcu(net, scope_id); /* scope_id is not valid... */ if (!dev) { rcu_read_unlock(); return -EADDRNOTAVAIL; } - rcu_read_unlock(); } #if IS_ENABLED(CONFIG_IPV6) if (ipv6_mod_enabled()) { ret = ipv6_chk_addr(net, addr, dev, 0); - if (ret) + if (ret) { + rcu_read_unlock(); return 0; + } } #endif + rcu_read_unlock(); return -EADDRNOTAVAIL; } From 5c170577049f9f3db07a6733b956069fb2cd5b53 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 18 Jul 2026 14:29:01 -0400 Subject: [PATCH 2957/3297] af_unix: fix listen() succeeding on sockets in the wrong state [ Upstream commit f0d9c3ffc2b5fc2ffacb56b3036155ce7a940a12 ] Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err = -EINVAL and the socket-state check in unix_listen(). Since prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is already connected) now silently returns success without doing anything, instead of failing with EINVAL as it did before. Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") Signed-off-by: John Ericson Link: https://patch.msgid.link/20260718182903.2295560-1-John.Ericson@Obsidian.Systems Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/unix/af_unix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index b65479a738f4c..52b17331e9d51 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -835,6 +835,7 @@ static int unix_listen(struct socket *sock, int backlog) if (err) goto out; unix_state_lock(sk); + err = -EINVAL; if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) goto out_unlock; if (backlog > sk->sk_max_ack_backlog) From 0372a52191127c38a84909babb5e9b397b451975 Mon Sep 17 00:00:00 2001 From: Sunday Adelodun Date: Thu, 13 Nov 2025 12:28:02 +0100 Subject: [PATCH 2958/3297] selftests: af_unix: Add tests for ECONNRESET and EOF semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 45a1cd8346ca245a1ca475b26eb6ceb9d8b7c6f0 ] Add selftests to verify and document Linux’s intended behaviour for UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. The tests verify that: 1. SOCK_STREAM returns EOF when the peer closes normally. 2. SOCK_STREAM returns ECONNRESET if the peer closes with unread data. 3. SOCK_SEQPACKET returns EOF when the peer closes normally. 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data. 5. SOCK_DGRAM does not return ECONNRESET when the peer closes. This follows up on review feedback suggesting a selftest to clarify Linux’s semantics. Suggested-by: Kuniyuki Iwashima Signed-off-by: Sunday Adelodun Link: https://patch.msgid.link/20251113112802.44657-1-adelodunolaoluwa@yahoo.com Signed-off-by: Paolo Abeni Stable-dep-of: 7f57c650d08b ("selftests/net/af_unix: test listen() rejects wrong socket states") Signed-off-by: Sasha Levin --- tools/testing/selftests/net/.gitignore | 1 + tools/testing/selftests/net/af_unix/Makefile | 1 + .../selftests/net/af_unix/unix_connreset.c | 177 ++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 8f9850a71f542..f627f1a2a2b8c 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -66,3 +66,4 @@ udpgso udpgso_bench_rx udpgso_bench_tx unix_connect +unix_connreset diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile index 2889403e35468..4c0375e28bbee 100644 --- a/tools/testing/selftests/net/af_unix/Makefile +++ b/tools/testing/selftests/net/af_unix/Makefile @@ -13,6 +13,7 @@ TEST_GEN_PROGS := \ scm_rights \ so_peek_off \ unix_connect \ + unix_connreset \ # end of TEST_GEN_PROGS include ../../lib.mk diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c new file mode 100644 index 0000000000000..bffef2b54bfd1 --- /dev/null +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Selftest for AF_UNIX socket close and ECONNRESET behaviour. + * + * This test verifies: + * 1. SOCK_STREAM returns EOF when the peer closes normally. + * 2. SOCK_STREAM returns ECONNRESET if peer closes with unread data. + * 3. SOCK_SEQPACKET returns EOF when the peer closes normally. + * 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data. + * 5. SOCK_DGRAM does not return ECONNRESET when the peer closes. + * + * These tests document the intended Linux behaviour. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "../../kselftest_harness.h" + +#define SOCK_PATH "/tmp/af_unix_connreset.sock" + +static void remove_socket_file(void) +{ + unlink(SOCK_PATH); +} + +FIXTURE(unix_sock) +{ + int server; + int client; + int child; +}; + +FIXTURE_VARIANT(unix_sock) +{ + int socket_type; + const char *name; +}; + +FIXTURE_VARIANT_ADD(unix_sock, stream) { + .socket_type = SOCK_STREAM, + .name = "SOCK_STREAM", +}; + +FIXTURE_VARIANT_ADD(unix_sock, dgram) { + .socket_type = SOCK_DGRAM, + .name = "SOCK_DGRAM", +}; + +FIXTURE_VARIANT_ADD(unix_sock, seqpacket) { + .socket_type = SOCK_SEQPACKET, + .name = "SOCK_SEQPACKET", +}; + +FIXTURE_SETUP(unix_sock) +{ + struct sockaddr_un addr = {}; + int err; + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, SOCK_PATH); + remove_socket_file(); + + self->server = socket(AF_UNIX, variant->socket_type, 0); + ASSERT_LT(-1, self->server); + + err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr)); + ASSERT_EQ(0, err); + + if (variant->socket_type == SOCK_STREAM || + variant->socket_type == SOCK_SEQPACKET) { + err = listen(self->server, 1); + ASSERT_EQ(0, err); + } + + self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0); + ASSERT_LT(-1, self->client); + + err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr)); + ASSERT_EQ(0, err); +} + +FIXTURE_TEARDOWN(unix_sock) +{ + if (variant->socket_type == SOCK_STREAM || + variant->socket_type == SOCK_SEQPACKET) + close(self->child); + + close(self->client); + close(self->server); + remove_socket_file(); +} + +/* Test 1: peer closes normally */ +TEST_F(unix_sock, eof) +{ + char buf[16] = {}; + ssize_t n; + + if (variant->socket_type == SOCK_STREAM || + variant->socket_type == SOCK_SEQPACKET) { + self->child = accept(self->server, NULL, NULL); + ASSERT_LT(-1, self->child); + + close(self->child); + } else { + close(self->server); + } + + n = recv(self->client, buf, sizeof(buf), 0); + + if (variant->socket_type == SOCK_STREAM || + variant->socket_type == SOCK_SEQPACKET) { + ASSERT_EQ(0, n); + } else { + ASSERT_EQ(-1, n); + ASSERT_EQ(EAGAIN, errno); + } +} + +/* Test 2: peer closes with unread data */ +TEST_F(unix_sock, reset_unread_behavior) +{ + char buf[16] = {}; + ssize_t n; + + /* Send data that will remain unread */ + send(self->client, "hello", 5, 0); + + if (variant->socket_type == SOCK_DGRAM) { + /* No real connection, just close the server */ + close(self->server); + } else { + self->child = accept(self->server, NULL, NULL); + ASSERT_LT(-1, self->child); + + /* Peer closes before client reads */ + close(self->child); + } + + n = recv(self->client, buf, sizeof(buf), 0); + ASSERT_EQ(-1, n); + + if (variant->socket_type == SOCK_STREAM || + variant->socket_type == SOCK_SEQPACKET) { + ASSERT_EQ(ECONNRESET, errno); + } else { + ASSERT_EQ(EAGAIN, errno); + } +} + +/* Test 3: closing unaccepted (embryo) server socket should reset client. */ +TEST_F(unix_sock, reset_closed_embryo) +{ + char buf[16] = {}; + ssize_t n; + + if (variant->socket_type == SOCK_DGRAM) + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); + + /* Close server without accept()ing */ + close(self->server); + + n = recv(self->client, buf, sizeof(buf), 0); + + ASSERT_EQ(-1, n); + ASSERT_EQ(ECONNRESET, errno); +} + +TEST_HARNESS_MAIN + From 643ec3e24a490e3f07cc1b96282830e38155a0a8 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 24 Nov 2025 21:26:39 +0000 Subject: [PATCH 2959/3297] selftest: af_unix: Create its own .gitignore. [ Upstream commit adb6b68c50604f3113d62758e839cc0186b94ce8 ] Somehow AF_UNIX tests have reused ../.gitignore, but now NIPA warns about it. Let's create .gitignore under af_unix/. Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20251124212805.486235-2-kuniyu@google.com Signed-off-by: Jakub Kicinski Stable-dep-of: 7f57c650d08b ("selftests/net/af_unix: test listen() rejects wrong socket states") Signed-off-by: Sasha Levin --- tools/testing/selftests/net/.gitignore | 8 -------- tools/testing/selftests/net/af_unix/.gitignore | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 tools/testing/selftests/net/af_unix/.gitignore diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index f627f1a2a2b8c..e2e2063399675 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -4,7 +4,6 @@ bind_timewait bind_wildcard busy_poller cmsg_sender -diag_uid epoll_busy_poll fin_ack_lat gro @@ -18,7 +17,6 @@ ipv6_flowlabel ipv6_flowlabel_mgr ipv6_fragmentation log.txt -msg_oob msg_zerocopy netlink-dumps nettest @@ -35,9 +33,6 @@ reuseport_bpf_numa reuseport_dualstack rxtimestamp sctp_hello -scm_inq -scm_pidfd -scm_rights sk_bind_sendto_listen sk_connect_zero_addr sk_so_peek_off @@ -45,7 +40,6 @@ skf_net_off socket so_incoming_cpu so_netns_cookie -so_peek_off so_txtime so_rcv_listener stress_reuseport_listen @@ -65,5 +59,3 @@ txtimestamp udpgso udpgso_bench_rx udpgso_bench_tx -unix_connect -unix_connreset diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore new file mode 100644 index 0000000000000..240b26740c9e0 --- /dev/null +++ b/tools/testing/selftests/net/af_unix/.gitignore @@ -0,0 +1,8 @@ +diag_uid +msg_oob +scm_inq +scm_pidfd +scm_rights +so_peek_off +unix_connect +unix_connreset From 814c5b1590037330381a4ae83aaf40c0986ba2bb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 18 Jul 2026 14:29:02 -0400 Subject: [PATCH 2960/3297] selftests/net/af_unix: test listen() rejects wrong socket states [ Upstream commit 7f57c650d08b8793bb551bdb33ad876535ef9fe8 ] Add a regression test for the unix_listen() state check. The key case is listen() on a bound socket that has already been connected: it is no longer in TCP_CLOSE or TCP_LISTEN, so it must fail with EINVAL. A prepare_peercred() call slipped in ahead of that check once left err at 0 and made listen() silently succeed there instead; this guards against a repeat. The neighbouring outcomes are covered too so they cannot regress the same way: a bound socket in TCP_CLOSE listens fine, calling listen() again on a socket already in TCP_LISTEN is allowed, and an unbound socket fails with EINVAL. Each case runs for both listenable socket types (SOCK_STREAM and SOCK_SEQPACKET) and both pathname and abstract addresses. Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") Signed-off-by: John Ericson Link: https://patch.msgid.link/20260718182903.2295560-2-John.Ericson@Obsidian.Systems Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../testing/selftests/net/af_unix/.gitignore | 1 + tools/testing/selftests/net/af_unix/Makefile | 1 + .../selftests/net/af_unix/unix_listen.c | 187 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore index 240b26740c9e0..9731766441036 100644 --- a/tools/testing/selftests/net/af_unix/.gitignore +++ b/tools/testing/selftests/net/af_unix/.gitignore @@ -6,3 +6,4 @@ scm_rights so_peek_off unix_connect unix_connreset +unix_listen diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile index 4c0375e28bbee..57d159803a3ab 100644 --- a/tools/testing/selftests/net/af_unix/Makefile +++ b/tools/testing/selftests/net/af_unix/Makefile @@ -14,6 +14,7 @@ TEST_GEN_PROGS := \ so_peek_off \ unix_connect \ unix_connreset \ + unix_listen \ # end of TEST_GEN_PROGS include ../../lib.mk diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c new file mode 100644 index 0000000000000..416fa3e5bfe9b --- /dev/null +++ b/tools/testing/selftests/net/af_unix/unix_listen.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tests for the state checks in AF_UNIX listen(). + * + * The central case is a regression test: listen() on a bound socket that + * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must + * fail with EINVAL. A prior change accidentally let it return success + * without doing anything, because a helper called in between reset the + * error code to 0. The neighbouring checks (unbound, already listening) + * are tested too so they cannot silently regress the same way. + * + * Every case runs for both listenable socket types (SOCK_STREAM and + * SOCK_SEQPACKET) and both pathname and abstract addresses. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include + +#include "kselftest_harness.h" + +#define SK_NAME "unix_listen_sk" +#define SRV_NAME "unix_listen_srv" + +FIXTURE(unix_listen) +{ + int sk; /* socket under test */ + int server; /* a listening peer, when a test needs one */ + struct sockaddr_un addr, srv_addr; + socklen_t addrlen, srv_addrlen; +}; + +FIXTURE_VARIANT(unix_listen) +{ + int type; + int abstract; +}; + +FIXTURE_VARIANT_ADD(unix_listen, stream_pathname) +{ + .type = SOCK_STREAM, + .abstract = 0, +}; + +FIXTURE_VARIANT_ADD(unix_listen, stream_abstract) +{ + .type = SOCK_STREAM, + .abstract = 1, +}; + +FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname) +{ + .type = SOCK_SEQPACKET, + .abstract = 0, +}; + +FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract) +{ + .type = SOCK_SEQPACKET, + .abstract = 1, +}; + +/* Fill @addr with a pathname or abstract address named @name. */ +static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name, + int abstract) +{ + size_t len = strlen(name); + + memset(addr, 0, sizeof(*addr)); + addr->sun_family = AF_UNIX; + /* An abstract address leads with a NUL and has no filesystem entry. */ + memcpy(addr->sun_path + (abstract ? 1 : 0), name, len); + + return offsetof(struct sockaddr_un, sun_path) + len + 1; +} + +FIXTURE_SETUP(unix_listen) +{ + self->sk = -1; + self->server = -1; + self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract); + self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME, + variant->abstract); +} + +FIXTURE_TEARDOWN(unix_listen) +{ + if (self->sk >= 0) + close(self->sk); + if (self->server >= 0) + close(self->server); + + /* Pathname sockets leave a filesystem entry behind; abstract ones do not. */ + if (!variant->abstract) { + remove(SK_NAME); + remove(SRV_NAME); + } +} + +/* A bound socket in TCP_CLOSE is the normal, allowed case. */ +TEST_F(unix_listen, bound_is_ok) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + EXPECT_EQ(0, err); +} + +/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */ +TEST_F(unix_listen, relisten_is_ok) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + ASSERT_EQ(0, err); + + err = listen(self->sk, 16); + EXPECT_EQ(0, err); +} + +/* listen() on an unbound socket fails: there is nothing to listen on. */ +TEST_F(unix_listen, unbound_is_einval) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = listen(self->sk, 8); + EXPECT_EQ(-1, err); + EXPECT_EQ(EINVAL, errno); +} + +/* + * The regression: a bound socket that has already been connected is not in + * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather + * than quietly succeeding. + */ +TEST_F(unix_listen, connected_is_einval) +{ + int err; + + self->server = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->server); + + err = bind(self->server, (struct sockaddr *)&self->srv_addr, + self->srv_addrlen); + ASSERT_EQ(0, err); + + err = listen(self->server, 8); + ASSERT_EQ(0, err); + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + /* Bind first so the unbound check does not mask the state check. */ + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = connect(self->sk, (struct sockaddr *)&self->srv_addr, + self->srv_addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + EXPECT_EQ(-1, err); + EXPECT_EQ(EINVAL, errno); +} + +TEST_HARNESS_MAIN From 05e283466b86e0b25a5209a3eafe87aa9886064a Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Sun, 19 Jul 2026 15:56:04 +0200 Subject: [PATCH 2961/3297] xsk: fix buffer leak in xsk_drop_skb() for AF_XDP multi-buffer Tx [ Upstream commit a3c8382ebce4780c6b3ace2c09bc342313ac0186 ] This patch is inspired by the check[1] from sashiko. It says when overflow happens, the address of cq to be published is invalid. Actually the severer thing is the whole process of publishing the address of cq in this particular case is not right: it should truely publish the address and advance the cached_prod in cq as long as it reads descriptors from txq. The following is the full analysis. xsk_drop_skb() is called in three places, which all discard a partially built multi-buffer skb: 1) xsk_build_skb() -EOVERFLOW error path: packet exceeds MAX_SKB_FRAGS 2) __xsk_generic_xmit() post-loop cleanup: an invalid descriptor in the TX ring prevents the partial packet from completing 3) xsk_release(): socket close while xs->skb holds an incomplete packet In all three cases, the TX descriptors for the already-processed frags have been consumed from the TX ring (xskq_cons_release), and CQ slots have been reserved. However, xsk_drop_skb() calls xsk_consume_skb() which cancels the CQ reservations via xsk_cq_cancel_locked(). Since the buffer addresses never appear in the completion queue, userspace permanently loses track of these buffers. Fix this by letting consume_skb() trigger the existing xsk_destruct_skb destructor, which already submits buffer addresses to the CQ via xsk_cq_submit_addr_locked(). Note that cancelling the descriptors back to the TX ring (via xskq_cons_cancel_n) is not a appropriate option because an oversized packet that always exceeds MAX_SKB_FRAGS would be retried indefinitely, which is an obviously deadlock bug in the TX path. Also move the desc->addr assignment in xsk_build_skb() above the overflow check so that the current descriptor's address is recorded before a potential -EOVERFLOW jump to free_err, consistent with the zerocopy path in xsk_build_skb_zerocopy(). [1]: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") Acked-by: Maciej Fijalkowski Signed-off-by: Jason Xing Acked-by: Stanislav Fomichev Link: https://patch.msgid.link/20260719135609.147823-2-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/xdp/xsk.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 9e0a486d54fb3..024e23c08b8db 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -676,8 +676,11 @@ static void xsk_consume_skb(struct sk_buff *skb) static void xsk_drop_skb(struct sk_buff *skb) { - xdp_sk(skb->sk)->tx->invalid_descs += xsk_get_num_desc(skb); - xsk_consume_skb(skb); + struct xdp_sock *xs = xdp_sk(skb->sk); + + xs->tx->invalid_descs += xsk_get_num_desc(skb); + consume_skb(skb); + xs->skb = NULL; } static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, @@ -768,7 +771,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, } /* in case of -EOVERFLOW that could happen below, - * xsk_consume_skb() will release this node as whole skb + * xsk_drop_skb() will release this node as whole skb * would be dropped, which implies freeing all list elements */ xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; @@ -867,6 +870,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg; } + xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; + if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) { err = -EOVERFLOW; goto free_err; @@ -884,8 +889,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, skb_add_rx_frag(skb, nr_frags, page, 0, len, PAGE_SIZE); refcount_add(PAGE_SIZE, &xs->sk.sk_wmem_alloc); - - xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; } } From 411554cb868b879268e377c96bab3802d63e0b97 Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Thu, 30 Oct 2025 08:06:46 +0800 Subject: [PATCH 2962/3297] xsk: use a smaller new lock for shared pool case [ Upstream commit 30ed05adca4a05c50594384cff18910858dd1d35 ] - Split cq_lock into two smaller locks: cq_prod_lock and cq_cached_prod_lock - Avoid disabling/enabling interrupts in the hot xmit path In either xsk_cq_cancel_locked() or xsk_cq_reserve_locked() function, the race condition is only between multiple xsks sharing the same pool. They are all in the process context rather than interrupt context, so now the small lock named cq_cached_prod_lock can be used without handling interrupts. While cq_cached_prod_lock ensures the exclusive modification of @cached_prod, cq_prod_lock in xsk_cq_submit_addr_locked() only cares about @producer and corresponding @desc. Both of them don't necessarily be consistent with @cached_prod protected by cq_cached_prod_lock. That's the reason why the previous big lock can be split into two smaller ones. Please note that SPSC rule is all about the global state of producer and consumer that can affect both layers instead of local or cached ones. Frequently disabling and enabling interrupt are very time consuming in some cases, especially in a per-descriptor granularity, which now can be avoided after this optimization, even when the pool is shared by multiple xsks. With this patch, the performance number[1] could go from 1,872,565 pps to 1,961,009 pps. It's a minor rise of around 5%. [1]: taskset -c 1 ./xdpsock -i enp2s0f1 -q 0 -t -S -s 64 Signed-off-by: Jason Xing Acked-by: Maciej Fijalkowski Link: https://patch.msgid.link/20251030000646.18859-3-kerneljasonxing@gmail.com Signed-off-by: Paolo Abeni Stable-dep-of: bd44a6dcd424 ("xsk: drain continuation descs after overflow in xsk_build_skb()") Signed-off-by: Sasha Levin --- include/net/xsk_buff_pool.h | 13 +++++++++---- net/xdp/xsk.c | 15 ++++++--------- net/xdp/xsk_buff_pool.c | 3 ++- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h index cac56e6b0869b..92a2358c6ce34 100644 --- a/include/net/xsk_buff_pool.h +++ b/include/net/xsk_buff_pool.h @@ -85,11 +85,16 @@ struct xsk_buff_pool { bool unaligned; bool tx_sw_csum; void *addrs; - /* Mutual exclusion of the completion ring in the SKB mode. Two cases to protect: - * NAPI TX thread and sendmsg error paths in the SKB destructor callback and when - * sockets share a single cq when the same netdev and queue id is shared. + /* Mutual exclusion of the completion ring in the SKB mode. + * Protect: NAPI TX thread and sendmsg error paths in the SKB + * destructor callback. */ - spinlock_t cq_lock; + spinlock_t cq_prod_lock; + /* Mutual exclusion of the completion ring in the SKB mode. + * Protect: when sockets share a single cq when the same netdev + * and queue id is shared. + */ + spinlock_t cq_cached_prod_lock; struct xdp_buff_xsk *free_heads[]; }; diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 024e23c08b8db..72d83b671f844 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -547,12 +547,11 @@ static int xsk_wakeup(struct xdp_sock *xs, u8 flags) static int xsk_cq_reserve_locked(struct xsk_buff_pool *pool) { - unsigned long flags; int ret; - spin_lock_irqsave(&pool->cq_lock, flags); + spin_lock(&pool->cq_cached_prod_lock); ret = xskq_prod_reserve(pool->cq); - spin_unlock_irqrestore(&pool->cq_lock, flags); + spin_unlock(&pool->cq_cached_prod_lock); return ret; } @@ -603,7 +602,7 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, unsigned long flags; u32 idx, i; - spin_lock_irqsave(&pool->cq_lock, flags); + spin_lock_irqsave(&pool->cq_prod_lock, flags); idx = xskq_get_prod(pool->cq); if (unlikely(num_descs > 1)) { @@ -621,16 +620,14 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, descs_processed++; } xskq_prod_submit_n(pool->cq, descs_processed); - spin_unlock_irqrestore(&pool->cq_lock, flags); + spin_unlock_irqrestore(&pool->cq_prod_lock, flags); } static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n) { - unsigned long flags; - - spin_lock_irqsave(&pool->cq_lock, flags); + spin_lock(&pool->cq_cached_prod_lock); xskq_prod_cancel_n(pool->cq, n); - spin_unlock_irqrestore(&pool->cq_lock, flags); + spin_unlock(&pool->cq_cached_prod_lock); } static void xsk_destruct_skb(struct sk_buff *skb) diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index a129ce6f1c25f..c9688735b643c 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -96,7 +96,8 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, INIT_LIST_HEAD(&pool->xskb_list); INIT_LIST_HEAD(&pool->xsk_tx_list); spin_lock_init(&pool->xsk_tx_list_lock); - spin_lock_init(&pool->cq_lock); + spin_lock_init(&pool->cq_prod_lock); + spin_lock_init(&pool->cq_cached_prod_lock); refcount_set(&pool->users, 1); pool->fq = xs->fq_tmp; From ceb00cb87a22c42d20c39336213db2b843ca9e34 Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Sun, 19 Jul 2026 15:56:05 +0200 Subject: [PATCH 2963/3297] xsk: drain continuation descs after overflow in xsk_build_skb() [ Upstream commit bd44a6dcd4248883de90f5dad53ae80066e27096 ] Fix generic xmit path multi-buffer logic when packets are either too big (count of descriptors exceed MAX_SKB_FRAGS) or an invalid descriptor is included in fragmented packet. Introduce xdp_sock::drain_cont and act upon this flag - when it is set, keep on consuming descriptors from AF_XDP Tx ring and put them directly onto Cq. Previously these descriptors were silently lost and could never be reached again. Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") Closes: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ Reviewed-by: Jason Xing Co-developed-by: Maciej Fijalkowski # wrapped cq addr submission onto routine Signed-off-by: Maciej Fijalkowski Signed-off-by: Jason Xing Acked-by: Stanislav Fomichev Link: https://patch.msgid.link/20260719135609.147823-3-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/xdp_sock.h | 1 + net/xdp/xsk.c | 45 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 7c2bc46c67050..8a0967af8f0c4 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -80,6 +80,7 @@ struct xdp_sock { * call of __xsk_generic_xmit(). */ struct sk_buff *skb; + bool drain_cont; struct list_head map_list; /* Protects map_list */ diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 72d83b671f844..70c57c5d8e1da 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -623,6 +623,19 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, spin_unlock_irqrestore(&pool->cq_prod_lock, flags); } +static void xsk_cq_submit_addr_single_locked(struct xsk_buff_pool *pool, + struct xdp_desc *desc) +{ + unsigned long flags; + u32 idx; + + spin_lock_irqsave(&pool->cq_prod_lock, flags); + idx = xskq_get_prod(pool->cq); + xskq_prod_write_addr(pool->cq, idx, desc->addr); + xskq_prod_submit_n(pool->cq, 1); + spin_unlock_irqrestore(&pool->cq_prod_lock, flags); +} + static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n) { spin_lock(&pool->cq_cached_prod_lock); @@ -913,13 +926,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, static int __xsk_generic_xmit(struct sock *sk) { struct xdp_sock *xs = xdp_sk(sk); - bool sent_frame = false; struct xdp_desc desc; struct sk_buff *skb; + u32 cached_cons; u32 max_batch; int err = 0; mutex_lock(&xs->mutex); + cached_cons = xs->tx->cached_cons; /* Since we dropped the RCU read lock, the socket state might have changed. */ if (unlikely(!xsk_is_bound(xs))) { @@ -948,11 +962,21 @@ static int __xsk_generic_xmit(struct sock *sk) goto out; } + if (unlikely(xs->drain_cont)) { + xsk_cq_submit_addr_single_locked(xs->pool, &desc); + xs->tx->invalid_descs++; + xskq_cons_release(xs->tx); + xs->drain_cont = xp_mb_desc(&desc); + continue; + } + skb = xsk_build_skb(xs, &desc); if (IS_ERR(skb)) { err = PTR_ERR(skb); if (err != -EOVERFLOW) goto out; + if (xp_mb_desc(&desc)) + xs->drain_cont = true; err = 0; continue; } @@ -981,18 +1005,33 @@ static int __xsk_generic_xmit(struct sock *sk) goto out; } - sent_frame = true; xs->skb = NULL; } if (xskq_has_descs(xs->tx)) { + bool drain = xs->skb || xs->drain_cont || xp_mb_desc(&desc); + + err = xsk_cq_reserve_locked(xs->pool); + if (err) { + xs->tx->invalid_descs--; + if (xs->skb) + xsk_drop_skb(xs->skb); + xs->drain_cont = drain; + err = -EAGAIN; + goto out; + } + if (xs->skb) xsk_drop_skb(xs->skb); + + xsk_cq_submit_addr_single_locked(xs->pool, &desc); + xskq_cons_release(xs->tx); + xs->drain_cont = xp_mb_desc(&desc); } out: - if (sent_frame) + if (xs->tx->cached_cons != cached_cons) __xsk_tx_release(xs); mutex_unlock(&xs->mutex); From 8e0996418590ba614b5d7a473bdd17cf9bcf45ef Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 20 Jul 2026 11:28:44 -0500 Subject: [PATCH 2964/3297] pinctrl-amd: Don't clear S4 wake bits at probe [ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ] commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") introduced a regression where Wake-on-LAN no longer works after suspend or shutdown on some AMD platforms. Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME does not use GPIO IRQ infrastructure and relies on firmware configuration. The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") was to clear spurious wake bits left by firmware to prevent unwanted wakeups. However, S4 wake bits are used for hardware-level wake sources like WoL that bypass the kernel's IRQ wake API. Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits: - Firmware-configured S4 wake sources (WoL) continue working - Kernel maintains control of S3/S0i3 wake policy via set_wake() - S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl: amd: Take suspend type into consideration which pins are non-wake") The trade-off is that firmware-programmed spurious S4 wake bits remain set, but this is less problematic than breaking WoL. Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") Signed-off-by: Mario Limonciello Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/pinctrl-amd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 2dac5c71eb008..d962f2e1d2acd 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -886,8 +886,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) u32 pin_reg, mask; int i; - mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | - BIT(WAKE_CNTRL_OFF_S4); + mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); for (i = 0; i < desc->npins; i++) { int pin = desc->pins[i].number; From 3ef209ca0b4b68c75e9a814d90cc916026b5a6ac Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 14 Jul 2026 19:49:34 +0900 Subject: [PATCH 2965/3297] scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer [ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the target-supplied data segment. The segment carries a 2-byte sense length followed by the sense bytes, so it must hold 2 + senselen bytes, but the bounds check only requires datalen >= senselen: senselen = get_unaligned_be16(data); if (datalen < senselen) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); A target that returns a SCSI Response whose datalen equals senselen (with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + 2 read up to two bytes past the received data. Those bytes are stale conn->data contents and end up in the command's sense buffer, which is returned to userspace. Account for the 2-byte sense length prefix in the check. Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") Suggested-by: Sashiko AI Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Acked-by: Chris Leech Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index c9f410c509783..e2adebc80b3a9 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } senselen = get_unaligned_be16(data); - if (datalen < senselen) + if (datalen < senselen + 2) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, From 72815741715bd41556dac5eeb068bf0f8af06ee7 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 16 Jul 2026 15:58:48 +0900 Subject: [PATCH 2966/3297] scsi: libiscsi_tcp: Bound SCSI Response data segment to the connection buffer [ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] iscsi_tcp_hdr_dissect() receives the data segment of several PDU types into the fixed-size conn->data buffer, which is allocated for ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU whose DataSegmentLength exceeds that buffer. The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its data segment (sense/response data) into conn->data via iscsi_tcp_data_recv_prep(), but it does so without the same check. The only upstream bound on in.datalen is conn->max_recv_dlength, the initiator's advertised MaxRecvDataSegmentLength, which is commonly negotiated well above 8192 (open-iscsi defaults to 262144). A target that returns a SCSI Response with a DataSegmentLength between 8193 and max_recv_dlength therefore overflows the 8192-byte conn->data buffer. Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly like those responses: bound the data segment, receive it into conn->data when present, and otherwise complete the PDU with no data. Fold the opcode into that case group rather than duplicating the check. Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") Suggested-by: Chris Leech Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Acked-by: Chris Leech Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/libiscsi_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e90805ba868fb..7223bb18b0480 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); spin_unlock(&conn->session->back_lock); break; - case ISCSI_OP_SCSI_CMD_RSP: - if (tcp_conn->in.datalen) { - iscsi_tcp_data_recv_prep(tcp_conn); - return 0; - } - rc = iscsi_complete_pdu(conn, hdr, NULL, 0); - break; case ISCSI_OP_R2T: if (ahslen) { rc = ISCSI_ERR_AHSLEN; @@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) } rc = iscsi_tcp_r2t_rsp(conn, hdr); break; + case ISCSI_OP_SCSI_CMD_RSP: case ISCSI_OP_LOGIN_RSP: case ISCSI_OP_TEXT_RSP: case ISCSI_OP_REJECT: From 9e24b47ef81d43b3fb1b14294f09991640c79fcc Mon Sep 17 00:00:00 2001 From: Xingui Yang Date: Thu, 16 Jul 2026 16:11:45 +0800 Subject: [PATCH 2967/3297] scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race [ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ] Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI devices and waits for the host to become runtime-active. But the host cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the drain is blocked on that very handler. However skipping the drain reintroduces a race: hisi_sas returns from resume before all PHY UP work and libsas discovery work finish. The controller may then autosuspend while disks are still waking up. The disks issue IO to a suspended controller, the IO fails, and the disks get disabled. Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT notification to after sas_drain_work(). By then the host resume is about to complete, so device removal through device_link no longer blocks on the resume and the cycle is broken. With the deadlock gone, restore sas_resume_ha() (the draining variant) in hisi_sas and remove sas_resume_ha_no_sync(). The reorder is safe for the other libsas consumers (isci, pm8001, aic94xx, mvsas). During suspend, sas_suspend_devices() calls sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a timed-out phy's disk is immediately rejected by the LLDD before reaching hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both complete directly via scsi_done() without entering SCSI EH. This is identical in both the old and new ordering since lldd_dev_gone runs during suspend, before resume. The reorder only affects when the PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() vs. asynchronous after resume returns), not whether I/O can reach the device. aic94xx and mvsas do not register any PM ops and never reach this code path. Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") Signed-off-by: Xingui Yang Reviewed-by: John Garry Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- include/scsi/libsas.h | 1 - 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index f30d4a58d7ab5..16256a0529253 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device) return rc; } phys_init_v3_hw(hisi_hba); - - /* - * If a directly-attached disk is removed during suspend, a deadlock - * may occur, as the PHYE_RESUME_TIMEOUT processing will require the - * hisi_hba->device to be active, which can only happen when resume - * completes. So don't wait for the HA event workqueue to drain upon - * resume. - */ - sas_resume_ha_no_sync(sha); + sas_resume_ha(sha); clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); dev_warn(dev, "end of resuming controller\n"); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 8566bb1208a05..ac157ab6a3011 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -409,7 +409,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) } } -static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) +void sas_resume_ha(struct sas_ha_struct *ha) { const unsigned long tmo = msecs_to_jiffies(25000); int i; @@ -425,6 +425,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", i, i > 1 ? "s" : ""); wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); + + /* + * All phys are back up or timed out. Turn on I/O and drain + * pending work. + */ + scsi_unblock_requests(ha->shost); + sas_drain_work(ha); + + /* + * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler + * calls sas_deform_port() -> sas_destruct_devices(), which removes + * SCSI devices and, for LLDDs using device_link() PM sync, waits + * for the host to be runtime-active. Sending it before the drain + * would deadlock: the drain waits for the handler, the handler + * waits for host resume, and host resume waits for the drain to + * finish. + */ for (i = 0; i < ha->num_phys; i++) { struct asd_sas_phy *phy = ha->sas_phy[i]; @@ -435,12 +452,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) } } - /* all phys are back up or timed out, turn on i/o so we can - * flush out disks that did not return - */ - scsi_unblock_requests(ha->shost); - if (drain) - sas_drain_work(ha); clear_bit(SAS_HA_RESUMING, &ha->state); sas_queue_deferred_work(ha); @@ -449,20 +460,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) */ sas_resume_insert_broadcast_ha(ha); } - -void sas_resume_ha(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, true); -} EXPORT_SYMBOL(sas_resume_ha); -/* A no-sync variant, which does not call sas_drain_ha(). */ -void sas_resume_ha_no_sync(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, false); -} -EXPORT_SYMBOL(sas_resume_ha_no_sync); - void sas_suspend_ha(struct sas_ha_struct *ha) { int i; diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index a0635b128d7ab..cc0a1f320f17d 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -684,7 +684,6 @@ extern int sas_register_ha(struct sas_ha_struct *); extern int sas_unregister_ha(struct sas_ha_struct *); extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); extern void sas_resume_ha(struct sas_ha_struct *sas_ha); -extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); int sas_phy_reset(struct sas_phy *phy, int hard_reset); From b2851429afc5b39aded7a2c14dd91a51c1bd926e Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sun, 28 Jun 2026 14:59:09 +0800 Subject: [PATCH 2968/3297] smb: client: fix buffer leaks in SMB1 read and write [ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request buffer before checking whether tcon->ses->server is NULL. If that defensive check ever fails, the helper returns -ECONNABORTED without releasing the request buffer. Fix these leaks by releasing the allocated request buffer before returning from these error paths. Use cifs_small_buf_release() for the buffers allocated by small_smb_init() and cifs_buf_release() for the buffer allocated by smb_init(). 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.1. An x86_64 allyesconfig build showed no new warnings. Runtime validation used a temporary fault-injection hook to force tcon->ses->server to NULL after request-buffer initialization. On the unfixed kernel, the harness observed two leaked small request buffers and one leaked large request buffer, with directed kmemleak dumps confirming the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer deltas remained. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Dawei Feng Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/cifssmb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 7694ca283fdc3..db9d9a1983c73 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -1515,8 +1515,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_small_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; @@ -1628,8 +1630,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; @@ -1908,8 +1912,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_small_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; From fcc3d77fef02ceabde756bf9b93339c82f207c72 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Mon, 20 Jul 2026 16:16:16 +0800 Subject: [PATCH 2969/3297] ASoC: tas2781: Use correct calibration data for SINEGAIN2 register [ Upstream commit dd88cf6273de61f2f7206c2066af97798dbb38b0 ] The SINEGAIN2_REG case in cali_reg_update() references t->sin_gn[] rather than t->sin_gn2[], causing the second pilot tone gain calibration to be programmed with the wrong register address. These are distinct fields in struct fct_param_address and are populated from separate firmware parameters by the parser in tas2781-fmwlib.c. Fixes: 84d6a465f211 ("ASoC: tas2781: Support dsp firmware Alpha and Beta seaies") Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260720081616.631413-1-wangdich9700@163.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/tas2781-i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index a3b4d2c3b4789..0b83446da45ea 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1303,8 +1303,8 @@ static void cali_reg_update(struct bulk_reg_val *p, t->sin_gn[2]); break; case TAS2781_PRM_SINEGAIN2_REG: - reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1], - t->sin_gn[2]); + reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1], + t->sin_gn2[2]); break; default: reg = 0; From d3337eefab626c52511ea640e9545b7c14e979d4 Mon Sep 17 00:00:00 2001 From: Jun Guo Date: Fri, 31 Oct 2025 15:30:02 +0800 Subject: [PATCH 2970/3297] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32 [ Upstream commit 4e00135b2dd1d7924a58bffa551b6ceb3bd836f2 ] The default FIFO data width of the Cadence SPI IP is 8 bits, but the hardware supports configurations of 16 bits and 32 bits. This patch enhances the driver to support communication with both 16-bits and 32-bits FIFO data widths. Signed-off-by: Jun Guo Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com Signed-off-by: Mark Brown Stable-dep-of: d9eadfce2fac ("spi: spi-cadence: Move TX FIFO full busy-wait into FIFO") Signed-off-by: Sasha Levin --- drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 7a745c2e0892c..75803a7f4cb11 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -109,6 +109,7 @@ * @rxbuf: Pointer to the RX buffer * @tx_bytes: Number of bytes left to transfer * @rx_bytes: Number of bytes requested + * @n_bytes: Number of bytes per word * @dev_busy: Device busy flag * @is_decoded_cs: Flag for decoder property set or not * @tx_fifo_depth: Depth of the TX FIFO @@ -120,16 +121,24 @@ struct cdns_spi { struct clk *pclk; unsigned int clk_rate; u32 speed_hz; - const u8 *txbuf; - u8 *rxbuf; + const void *txbuf; + void *rxbuf; int tx_bytes; int rx_bytes; + u8 n_bytes; u8 dev_busy; u32 is_decoded_cs; unsigned int tx_fifo_depth; struct reset_control *rstc; }; +enum cdns_spi_frame_n_bytes { + CDNS_SPI_N_BYTES_NULL = 0, + CDNS_SPI_N_BYTES_U8 = 1, + CDNS_SPI_N_BYTES_U16 = 2, + CDNS_SPI_N_BYTES_U32 = 4 +}; + /* Macros for the SPI controller read/write */ static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset) { @@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi, return 0; } +static u8 cdns_spi_n_bytes(struct spi_transfer *transfer) +{ + if (transfer->bits_per_word <= 8) + return CDNS_SPI_N_BYTES_U8; + else if (transfer->bits_per_word <= 16) + return CDNS_SPI_N_BYTES_U16; + else + return CDNS_SPI_N_BYTES_U32; +} + +static inline void cdns_spi_reader(struct cdns_spi *xspi) +{ + u32 rxw = 0; + + if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) { + pr_err("%s: rxbuf address is not aligned for %d bytes\n", + __func__, xspi->n_bytes); + return; + } + + rxw = cdns_spi_read(xspi, CDNS_SPI_RXD); + if (xspi->rxbuf) { + switch (xspi->n_bytes) { + case CDNS_SPI_N_BYTES_U8: + *(u8 *)xspi->rxbuf = rxw; + break; + case CDNS_SPI_N_BYTES_U16: + *(u16 *)xspi->rxbuf = rxw; + break; + case CDNS_SPI_N_BYTES_U32: + *(u32 *)xspi->rxbuf = rxw; + break; + default: + pr_err("%s invalid n_bytes %d\n", __func__, + xspi->n_bytes); + return; + } + xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes; + } +} + +static inline void cdns_spi_writer(struct cdns_spi *xspi) +{ + u32 txw = 0; + + if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) { + pr_err("%s: txbuf address is not aligned for %d bytes\n", + __func__, xspi->n_bytes); + return; + } + + if (xspi->txbuf) { + switch (xspi->n_bytes) { + case CDNS_SPI_N_BYTES_U8: + txw = *(u8 *)xspi->txbuf; + break; + case CDNS_SPI_N_BYTES_U16: + txw = *(u16 *)xspi->txbuf; + break; + case CDNS_SPI_N_BYTES_U32: + txw = *(u32 *)xspi->txbuf; + break; + default: + pr_err("%s invalid n_bytes %d\n", __func__, + xspi->n_bytes); + return; + } + cdns_spi_write(xspi, CDNS_SPI_TXD, txw); + xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes; + } +} + /** * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO * @xspi: Pointer to the cdns_spi structure @@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) while (ntx || nrx) { if (nrx) { - u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); - - if (xspi->rxbuf) - *xspi->rxbuf++ = data; - + cdns_spi_reader(xspi); nrx--; } if (ntx) { - if (xspi->txbuf) - cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); - else - cdns_spi_write(xspi, CDNS_SPI_TXD, 0); - + cdns_spi_writer(xspi); ntx--; } - } } @@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr, if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) udelay(10); + xspi->n_bytes = cdns_spi_n_bytes(transfer); + xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); + xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); + cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); @@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_CPOL | SPI_CPHA; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6")) + ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32); + if (!spi_controller_is_target(ctlr)) { ctlr->mode_bits |= SPI_CS_HIGH; ctlr->set_cs = cdns_spi_chipselect; @@ -812,6 +891,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = { static const struct of_device_id cdns_spi_of_match[] = { { .compatible = "xlnx,zynq-spi-r1p6" }, + { .compatible = "cix,sky1-spi-r1p6" }, { .compatible = "cdns,spi-r1p6" }, { /* end of table */ } }; From e28d478c003d7a44f3ffba6ca6330d03fcc3ee08 Mon Sep 17 00:00:00 2001 From: Srikanth Boyapally Date: Mon, 20 Jul 2026 18:25:10 +0530 Subject: [PATCH 2971/3297] spi: spi-cadence: Move TX FIFO full busy-wait into FIFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ] SPI host transfers could intermittently stall with spi_transfer timeouts. The TXFULL condition was checked only once in cdns_transfer_one() before cdns_spi_process_fifo(), so if the FIFO became full again during refill, writes could be dropped and the transfer would never complete. Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so the 10µs back-off is applied per FIFO entry during filling, ensuring forward progress and eliminating spurious timeouts. Restrict the delay to host mode using spi_controller_is_target(), the controller is passed into cdns_spi_process_fifo() so the check is made at the point of use. In target mode this delay must not run as it causes the target to miss its transfer window and corrupt data. Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()") Signed-off-by: Srikanth Boyapally Reviewed-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-cadence.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 75803a7f4cb11..403e8141dff89 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi) /** * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO + * @ctlr: Pointer to the spi_controller structure * @xspi: Pointer to the cdns_spi structure * @ntx: Number of bytes to pack into the TX FIFO * @nrx: Number of bytes to drain from the RX FIFO */ -static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) +static void cdns_spi_process_fifo(struct spi_controller *ctlr, + struct cdns_spi *xspi, int ntx, int nrx) { ntx = clamp(ntx, 0, xspi->tx_bytes); nrx = clamp(nrx, 0, xspi->rx_bytes); @@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) } if (ntx) { + /* When xspi in busy condition, bytes may send failed, + * then spi control didn't work thoroughly, add one byte + * delay. Only in host mode; in target mode this delay + * causes data corruption as the target fails to prepare + * data in time. + */ + if (!spi_controller_is_target(ctlr) && + (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)) + udelay(10); + cdns_spi_writer(xspi); ntx--; } @@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) cdns_spi_write(xspi, CDNS_SPI_THLD, 1); if (xspi->tx_bytes) { - cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); + cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt); } else { /* Fixed delay due to controller limitation with * RX_NEMPTY incorrect status * Xilinx AR:65885 contains more details */ udelay(10); - cdns_spi_process_fifo(xspi, 0, trans_cnt); + cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt); cdns_spi_write(xspi, CDNS_SPI_IDR, CDNS_SPI_IXR_DEFAULT); spi_finalize_current_transfer(ctlr); @@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr, cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); } - /* When xspi in busy condition, bytes may send failed, - * then spi control didn't work thoroughly, add one byte delay - */ - if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) - udelay(10); - xspi->n_bytes = cdns_spi_n_bytes(transfer); xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); - cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); + cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0); cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); return transfer->len; From a42d727dae5701deac8bb2a75effadae7d681153 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 22 Jul 2026 07:14:36 -0700 Subject: [PATCH 2972/3297] hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ] Unlike NCT6106, NCT6116 only has three temperature registers, and with it only three temperature source and temperature source configuration registers. The register addresses match those of NCT6106 and can be re-used. The code used a separate array to list the temperature source registers for NCT6116, but used the size of the NCT6106 register array to set the number of registers. The NCT6106 register array provides six addresses, while the temperature source register array for NCT6116 only provides three addresses. This causes a KASAN report. BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 ... Call Trace: dump_stack+0x7d/0xa7 print_address_description.constprop.0+0x1c/0x220 ? __kasan_kmalloc.constprop.0+0xc9/0xd0 ? __kmalloc_node_track_caller+0x194/0x5b0 ? nct6775_probe+0x936/0x46f0 [nct6775] ? nct6775_probe+0x936/0x46f0 [nct6775] ... Fix the problem by hard-coding the number of temperature and temperature configuration registers to three for NCT6116. Drop the unnecessary NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. Reported-by: Florian Bezdeka Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") Cc: Björn Gerhart Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/nct6775-core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 79bc67ffb9986..506d57025c3bd 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; -static const u16 NCT6116_REG_TEMP_SOURCE[] = { - 0xb0, 0xb1, 0xb2 }; static const u16 NCT6116_REG_CRITICAL_TEMP[] = { 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; @@ -3650,7 +3648,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, = NCT6106_CRITICAL_PWM_ENABLE_MASK; data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; + data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; @@ -3664,13 +3662,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp = NCT6106_REG_TEMP; reg_temp_mon = NCT6106_REG_TEMP_MON; - num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); + num_reg_temp = 3; num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); reg_temp_over = NCT6106_REG_TEMP_OVER; reg_temp_hyst = NCT6106_REG_TEMP_HYST; reg_temp_config = NCT6106_REG_TEMP_CONFIG; - num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); + num_reg_temp_config = 3; reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; reg_temp_crit = NCT6106_REG_TEMP_CRIT; reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; From 8da94361f9ae149926b23c29ebcd08c1f5592845 Mon Sep 17 00:00:00 2001 From: Ian Ray Date: Fri, 20 Feb 2026 13:20:21 +0200 Subject: [PATCH 2973/3297] hwmon: (ina2xx) Make it easier to add more devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f6e14b5bcabf4ee97a2d535c3c2d7e72c8da4c15 ] * Make sysfs entries documentation easier to maintain. * Use multi-line enum. * Correct "has_power_average" comment. Create a new "has_update_interval" member for chips which support averaging. Signed-off-by: Ian Ray Reviewed-by: Bence Csókás # v2 Tested-by: Jens Almer Link: https://lore.kernel.org/r/20260220112024.97446-3-ian.ray@gehealthcare.com Signed-off-by: Guenter Roeck Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") Signed-off-by: Sasha Levin --- Documentation/hwmon/ina2xx.rst | 12 ++++++++++-- drivers/hwmon/ina2xx.c | 18 ++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst index a3860aae444c0..a4ddf4bd2b081 100644 --- a/Documentation/hwmon/ina2xx.rst +++ b/Documentation/hwmon/ina2xx.rst @@ -124,8 +124,16 @@ power1_input Power(uW) measurement channel shunt_resistor Shunt resistance(uOhm) channel (not for ina260) ======================= =============================================== -Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655 ------------------------------------------------------------------------- +Additional sysfs entries +------------------------ + +Additional entries are available for the following chips: + + * ina226 + * ina230 + * ina231 + * ina260 + * sy24655 ======================= ==================================================== curr1_lcrit Critical low current diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index bc3c1f7314b3e..afd0018a17e03 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -135,13 +135,19 @@ static const struct regmap_config ina2xx_regmap_config = { .writeable_reg = ina2xx_writeable_reg, }; -enum ina2xx_ids { ina219, ina226, ina260, sy24655 }; +enum ina2xx_ids { + ina219, + ina226, + ina260, + sy24655 +}; struct ina2xx_config { u16 config_default; bool has_alerts; /* chip supports alerts and limits */ bool has_ishunt; /* chip has internal shunt resistor */ - bool has_power_average; /* chip has internal shunt resistor */ + bool has_power_average; /* chip supports average power */ + bool has_update_interval; int calibration_value; int shunt_div; int bus_voltage_shift; @@ -172,6 +178,7 @@ static const struct ina2xx_config ina2xx_config[] = { .has_alerts = false, .has_ishunt = false, .has_power_average = false, + .has_update_interval = false, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, @@ -183,6 +190,7 @@ static const struct ina2xx_config ina2xx_config[] = { .has_alerts = true, .has_ishunt = false, .has_power_average = false, + .has_update_interval = true, }, [ina260] = { .config_default = INA260_CONFIG_DEFAULT, @@ -193,6 +201,7 @@ static const struct ina2xx_config ina2xx_config[] = { .has_alerts = true, .has_ishunt = true, .has_power_average = false, + .has_update_interval = true, }, [sy24655] = { .config_default = SY24655_CONFIG_DEFAULT, @@ -204,6 +213,7 @@ static const struct ina2xx_config ina2xx_config[] = { .has_alerts = true, .has_ishunt = false, .has_power_average = true, + .has_update_interval = false, }, }; @@ -713,7 +723,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type const struct ina2xx_data *data = _data; bool has_alerts = data->config->has_alerts; bool has_power_average = data->config->has_power_average; - enum ina2xx_ids chip = data->chip; + bool has_update_interval = data->config->has_update_interval; switch (type) { case hwmon_in: @@ -775,7 +785,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type case hwmon_chip: switch (attr) { case hwmon_chip_update_interval: - if (chip == ina226 || chip == ina260) + if (has_update_interval) return 0644; break; default: From fdfde077e025c72d49ae9be064eeefb48729a829 Mon Sep 17 00:00:00 2001 From: Ian Ray Date: Fri, 20 Feb 2026 13:20:22 +0200 Subject: [PATCH 2974/3297] hwmon: (ina2xx) Add support for INA234 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 88a928eebccdc5445d874ddcbf1683b76c9f1431 ] INA234 is register compatible to INA226 (excepting manufacturer and die or device id registers) but has different scaling. Signed-off-by: Ian Ray Reviewed-by: Bence Csókás # v2 Tested-by: Jens Almer Tested-by: Jonas Rebmann Link: https://lore.kernel.org/r/20260220112024.97446-4-ian.ray@gehealthcare.com Signed-off-by: Guenter Roeck Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") Signed-off-by: Sasha Levin --- Documentation/hwmon/ina2xx.rst | 13 ++++++++++++- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/ina2xx.c | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst index a4ddf4bd2b081..d64e7af46a124 100644 --- a/Documentation/hwmon/ina2xx.rst +++ b/Documentation/hwmon/ina2xx.rst @@ -74,6 +74,16 @@ Supported chips: https://us1.silergy.com/ + * Texas Instruments INA234 + + Prefix: 'ina234' + + Addresses: I2C 0x40 - 0x43 + + Datasheet: Publicly available at the Texas Instruments website + + https://www.ti.com/ + Author: Lothar Felten Description @@ -89,7 +99,7 @@ interface. The INA220 monitors both shunt drop and supply voltage. The INA226 is a current shunt and power monitor with an I2C interface. The INA226 monitors both a shunt voltage drop and bus supply voltage. -INA230 and INA231 are high or low side current shunt and power monitors +INA230, INA231, and INA234 are high or low side current shunt and power monitors with an I2C interface. The chips monitor both a shunt voltage drop and bus supply voltage. @@ -132,6 +142,7 @@ Additional entries are available for the following chips: * ina226 * ina230 * ina231 + * ina234 * ina260 * sy24655 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c8a7e45e8006c..4d852e68b4749 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2275,7 +2275,7 @@ config SENSORS_INA2XX select REGMAP_I2C help If you say yes here you get support for INA219, INA220, INA226, - INA230, INA231, INA260, and SY24655 power monitor chips. + INA230, INA231, INA234, INA260, and SY24655 power monitor chips. The INA2xx driver is configured for the default configuration of the part as described in the datasheet. diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index afd0018a17e03..e07f24901bfbb 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -138,6 +138,7 @@ static const struct regmap_config ina2xx_regmap_config = { enum ina2xx_ids { ina219, ina226, + ina234, ina260, sy24655 }; @@ -192,6 +193,18 @@ static const struct ina2xx_config ina2xx_config[] = { .has_power_average = false, .has_update_interval = true, }, + [ina234] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ + .bus_voltage_shift = 4, + .bus_voltage_lsb = 25600, + .power_lsb_factor = 32, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = false, + .has_update_interval = true, + }, [ina260] = { .config_default = INA260_CONFIG_DEFAULT, .shunt_div = 400, @@ -1000,6 +1013,7 @@ static const struct i2c_device_id ina2xx_id[] = { { "ina226", ina226 }, { "ina230", ina226 }, { "ina231", ina226 }, + { "ina234", ina234 }, { "ina260", ina260 }, { "sy24655", sy24655 }, { } @@ -1031,6 +1045,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { .compatible = "ti,ina231", .data = (void *)ina226 }, + { + .compatible = "ti,ina234", + .data = (void *)ina234 + }, { .compatible = "ti,ina260", .data = (void *)ina260 From e627f4ad9eea21786f4ac9eab039d0f91118bd67 Mon Sep 17 00:00:00 2001 From: Jonas Rebmann Date: Tue, 3 Mar 2026 12:07:02 +0100 Subject: [PATCH 2975/3297] hwmon: (ina2xx) Shift INA234 shunt and current registers [ Upstream commit eeca1114d1e2cc0eacaebb80f3f2afbaebfc60be ] The INA219 has the lowest three bits of the bus voltage register zero-reserved, the bus_voltage_shift ina2xx_config field was introduced to accommodate for that. The INA234 has four bits of the bus voltage, of the shunt voltage, and of the current registers zero-reserved but the latter two were implemented by choosing a 16x higher shunt_div instead of a separate field specifying a bit shift. This is possible because shunt voltage and current are divided by shunt_div, hence a 16x higher shunt_div results in a 16x smaller LSB for both the shunt voltage and the current register, perfectly accounting for the missing bit shift. For consistency and correctness, account for the reserved bits via shunt_voltage_shift and current_shift configuration fields as already done for voltage registers and use the conversion constants given in the INA234 datasheet. Signed-off-by: Jonas Rebmann Link: https://lore.kernel.org/r/20260303-ina234-shift-v1-2-318c33ac4480@pengutronix.de Signed-off-by: Guenter Roeck Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") Signed-off-by: Sasha Levin --- drivers/hwmon/ina2xx.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index e07f24901bfbb..a46a118406726 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -151,9 +151,11 @@ struct ina2xx_config { bool has_update_interval; int calibration_value; int shunt_div; + int shunt_voltage_shift; int bus_voltage_shift; int bus_voltage_lsb; /* uV */ int power_lsb_factor; + int current_shift; }; struct ina2xx_data { @@ -173,59 +175,69 @@ static const struct ina2xx_config ina2xx_config[] = { .config_default = INA219_CONFIG_DEFAULT, .calibration_value = 4096, .shunt_div = 100, + .shunt_voltage_shift = 0, .bus_voltage_shift = 3, .bus_voltage_lsb = 4000, .power_lsb_factor = 20, .has_alerts = false, .has_ishunt = false, .has_power_average = false, + .current_shift = 0, .has_update_interval = false, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, .calibration_value = 2048, .shunt_div = 400, + .shunt_voltage_shift = 0, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb_factor = 25, .has_alerts = true, .has_ishunt = false, .has_power_average = false, + .current_shift = 0, .has_update_interval = true, }, [ina234] = { .config_default = INA226_CONFIG_DEFAULT, .calibration_value = 2048, - .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ + .shunt_div = 25, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ + .shunt_voltage_shift = 4, .bus_voltage_shift = 4, .bus_voltage_lsb = 25600, .power_lsb_factor = 32, .has_alerts = true, .has_ishunt = false, .has_power_average = false, + .current_shift = 4, .has_update_interval = true, }, [ina260] = { .config_default = INA260_CONFIG_DEFAULT, .shunt_div = 400, + .shunt_voltage_shift = 0, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb_factor = 8, .has_alerts = true, .has_ishunt = true, .has_power_average = false, + .current_shift = 0, .has_update_interval = true, }, [sy24655] = { .config_default = SY24655_CONFIG_DEFAULT, .calibration_value = 4096, .shunt_div = 400, + .shunt_voltage_shift = 0, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb_factor = 25, .has_alerts = true, .has_ishunt = false, .has_power_average = true, + .current_shift = 0, .has_update_interval = false, }, }; @@ -279,7 +291,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, switch (reg) { case INA2XX_SHUNT_VOLTAGE: /* signed register */ - val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div); + val = (s16)regval >> data->config->shunt_voltage_shift; + val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); break; case INA2XX_BUS_VOLTAGE: val = (regval >> data->config->bus_voltage_shift) * @@ -291,7 +304,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, break; case INA2XX_CURRENT: /* signed register, result in mA */ - val = (s16)regval * data->current_lsb_uA; + val = ((s16)regval >> data->config->current_shift) * + data->current_lsb_uA; val = DIV_ROUND_CLOSEST(val, 1000); break; case INA2XX_CALIBRATION: @@ -385,6 +399,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) case INA2XX_SHUNT_VOLTAGE: val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); val *= data->config->shunt_div; + val <<= data->config->shunt_voltage_shift; return clamp_val(val, 0, SHRT_MAX); case INA2XX_BUS_VOLTAGE: val = clamp_val(val, 0, 200000); @@ -399,6 +414,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); /* signed register, result in mA */ val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); + val <<= data->config->current_shift; return clamp_val(val, SHRT_MIN, SHRT_MAX); default: /* programmer goofed */ From b60e8486c04def0712ba85a9ee058e5ed90d7633 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 10 Jun 2026 07:46:16 -0700 Subject: [PATCH 2976/3297] hwmon: (ina2xx) Fix various overflow issues [ Upstream commit e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 ] Sashiko reports several integer overflow problems in the ina2xx driver caused by unbounded multiplications and inadequate types for intermediate calculations. Specifically: - In ina2xx_get_value(), the return type is changed from int to long. Intermediate calculations for current are now performed using 64-bit types to prevent 32-bit integer overflow before the division by 1000. - When calculating power in ina2xx_get_value() and sy24655_average_power_read(), interim values are cast to u64 and clamped to LONG_MAX. This prevents overflow when regval or accumulator_24 is multiplied by power_lsb_uW. - In ina226_alert_to_reg(), the clamping logic is rewritten using min_t(). This safely avoids integer overflows when scaling user-provided values for shunt voltage, bus voltage, power, and current limits. Cc: Loic Poulain Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index a46a118406726..9c2dc9c0fd4d5 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -283,30 +284,34 @@ static u16 ina226_interval_to_reg(long interval) return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); } -static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, - unsigned int regval) +static long ina2xx_get_value(struct ina2xx_data *data, u8 reg, + unsigned int regval) { - int val; + s64 val64; + long val; switch (reg) { case INA2XX_SHUNT_VOLTAGE: /* signed register */ - val = (s16)regval >> data->config->shunt_voltage_shift; - val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); + val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift, + data->config->shunt_div); break; case INA2XX_BUS_VOLTAGE: - val = (regval >> data->config->bus_voltage_shift) * - data->config->bus_voltage_lsb; - val = DIV_ROUND_CLOSEST(val, 1000); + val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) * + data->config->bus_voltage_lsb, 1000); break; case INA2XX_POWER: - val = regval * data->power_lsb_uW; + val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX); break; case INA2XX_CURRENT: /* signed register, result in mA */ - val = ((s16)regval >> data->config->current_shift) * + val64 = (s64)((s16)regval >> data->config->current_shift) * data->current_lsb_uA; - val = DIV_ROUND_CLOSEST(val, 1000); + if (val64 < 0) + val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000); + else + val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000); + val = clamp_val(val64, LONG_MIN, LONG_MAX); break; case INA2XX_CALIBRATION: val = regval; @@ -395,27 +400,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) */ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) { + long limit; + switch (reg) { case INA2XX_SHUNT_VOLTAGE: - val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); - val *= data->config->shunt_div; - val <<= data->config->shunt_voltage_shift; - return clamp_val(val, 0, SHRT_MAX); + val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div)); + return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift, + SHRT_MAX); case INA2XX_BUS_VOLTAGE: - val = clamp_val(val, 0, 200000); - val = (val * 1000) << data->config->bus_voltage_shift; - val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); - return clamp_val(val, 0, USHRT_MAX); + val = min_t(long, val, 130000); + return min_t(long, + DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift, + data->config->bus_voltage_lsb), + USHRT_MAX); case INA2XX_POWER: - val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); - val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); - return clamp_val(val, 0, USHRT_MAX); + val = min_t(long, val, LONG_MAX - data->power_lsb_uW); + return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX); case INA2XX_CURRENT: - val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); + limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000; + val = min_t(long, val, limit); /* signed register, result in mA */ val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); - val <<= data->config->current_shift; - return clamp_val(val, SHRT_MIN, SHRT_MAX); + limit = SHRT_MAX >> data->config->current_shift; + return (u16)(min_t(long, val, limit) << data->config->current_shift); default: /* programmer goofed */ WARN_ON_ONCE(1); @@ -560,6 +567,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va u8 template[6]; int ret; long accumulator_24, sample_count; + u64 val64; /* 48-bit register read */ ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); @@ -578,7 +586,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va return 0; } - *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; + val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; + *val = min_t(u64, val64, LONG_MAX); return 0; } From 08aee6d45eefc9e0b94c21bd442f47b21d64fa2c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 5 Feb 2025 12:27:15 -0800 Subject: [PATCH 2977/3297] hwmon: (ltc4282) Fix reading the minimum alarm voltage [ Upstream commit 00feb1cce93dab948a299b69753d99c681d45a0b ] Coverity reports an out-of-bounds access when reading the minimum alarm voltage for the VGPIO channel. Add the missing return statement to fix the problem. Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282") Cc: Nuno Sa Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/ltc4282.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index 58c2d3a624329..4000bcbc7353d 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -384,8 +384,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val, channel, val); case hwmon_in_min_alarm: if (channel == LTC4282_CHAN_VGPIO) - ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, - LTC4282_GPIO_ALARM_L_MASK, val); + return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, + LTC4282_GPIO_ALARM_L_MASK, val); return ltc4282_vdd_source_read_alm(st, LTC4282_VSOURCE_ALARM_L_MASK, From c498adfd4c3e85306ccdc19b76e639bfc215f0e7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jul 2026 09:34:46 -0700 Subject: [PATCH 2978/3297] hwmon: (sht3x) Fix unaligned accesses [ Upstream commit f46d5ab43a572b84773015a76966f5da56fc1748 ] Sashiko reports: In sht3x_update_client(), the 16-bit temperature and humidity values are extracted from a stack-allocated byte array using be16_to_cpup(). The pointers passed to this function are calculated as buf and buf + 3. Since the difference between the two pointers is an odd number of bytes, at least one of them is guaranteed to be at an unaligned offset. This will trigger an alignment fault on strict-alignment architectures such as ARMv5 or SPARC, resulting in a kernel panic. Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(), and put_unaligned_be16() instead of cpu_to_be16(). Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors") Reported-by: Sashiko Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/sht3x.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index f36c0229328fa..5d04013efb522 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -21,6 +21,7 @@ #include #include #include +#include /* commands (high repeatability mode) */ static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 }; @@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev) if (ret) goto out; - val = be16_to_cpup((__be16 *)buf); + val = get_unaligned_be16(buf); data->temperature = sht3x_extract_temperature(val); - val = be16_to_cpup((__be16 *)(buf + 3)); + val = get_unaligned_be16(buf + 3); data->humidity = sht3x_extract_humidity(val); data->last_update = jiffies; } @@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data) if (ret) return ret; - raw = be16_to_cpup((__be16 *)buffer); + raw = get_unaligned_be16(buffer); temperature = sht3x_extract_temperature((raw & 0x01ff) << 7); humidity = sht3x_extract_humidity(raw & 0xfe00); data->temperature_limits[index] = temperature; @@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev, raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7); raw |= ((humidity * 42950) >> 16) & 0xfe00; - *((__be16 *)position) = cpu_to_be16(raw); + put_unaligned_be16(raw, position); position += SHT3X_WORD_LEN; *position = crc8(sht3x_crc8_table, position - SHT3X_WORD_LEN, From 075fce376cf852db9293481edce07c181a9b1f46 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jul 2026 15:27:28 -0700 Subject: [PATCH 2979/3297] hwmon: (lm90) Only report alarms if driver is ready [ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ] Userspace can read sysfs attributes before driver registration is complete, immediately after devm_hwmon_device_register_with_info() has been called. At that time, data->hwmon_dev is not yet initialized. This can trigger a NULL pointer access since lm90_update_device() and with it lm90_update_alarms_locked() will be called. This call schedules report_work and lm90_report_alarms(), which passes the still-NULL data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer dereference. Fix the problem by only scheduling the report and alert workers data->hwmon_dev is set. Reported-by: Sashiko Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/lm90.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index a465a8a7ef5af..e1795ccde876c 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1196,7 +1196,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) check_enable = (client->irq || !(data->config_orig & 0x80)) && (data->config & 0x80); - if (force || check_enable) + if (data->hwmon_dev && (force || check_enable)) schedule_work(&data->report_work); /* @@ -1204,7 +1204,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) * alarms are all clear, and alerts are currently disabled. * Otherwise (re)schedule worker if needed. */ - if (check_enable) { + if (check_enable && data->hwmon_dev) { if (!(data->current_alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Re-enabling ALERT#\n"); lm90_update_confreg(data, data->config & ~0x80); From 2332d35aaf206c17acf848522817252732596676 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 09:54:23 -0700 Subject: [PATCH 2980/3297] hwmon: (nzxt-smart2) DMA-align output buffer [ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ] Sashiko reports: When send_output_report() calls hid_hw_output_report(), the underlying USB HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. When the DMA mapping flushes or invalidates the cacheline, it will corrupt the adjacent variables (mutex, update_interval) that were modified concurrently by the CPU. This causes memory corruption due to cacheline sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings for this violation. Any operation that triggers send_output_report() (like setting a fan speed or updating the interval) causes the USB DMA mapping. On systems with non-coherent caches, this structural bug causes immediate and deterministic memory corruption. Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. Reported-by: Sashiko Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") Cc: Aleksandr Mezin Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/nzxt-smart2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index e2316c46629d6..ff0c0bee0e839 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -203,7 +203,7 @@ struct drvdata { */ struct mutex mutex; long update_interval; - u8 output_buffer[OUTPUT_REPORT_SIZE]; + u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); }; static long scale_pwm_value(long val, long orig_max, long new_max) From 5ec5f00fc606a6df8434948c4552b3cb1176595d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 24 Jul 2026 07:29:01 +0000 Subject: [PATCH 2981/3297] net: do not send ICMP/NDISC Redirects when peer allocation fails [ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ] When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry under memory pressure or tree size caps, redirect handlers previously fell back to sending un-rate-limited ICMP/NDISC Redirect messages. In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into inet_peer_xrlim_allow(), which returned true when peer == NULL. Because ICMP/NDISC Redirects are not part of the default global rate limit mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates an un-rate-limited ICMP packet storm. Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and ndisc_send_redirect() when peer is NULL. Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/route.c | 2 -- net/ipv6/ip6_output.c | 2 +- net/ipv6/ndisc.c | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 11d990703d31a..778467bea476a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -892,8 +892,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif); if (!peer) { rcu_read_unlock(); - icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, - rt_nexthop(rt, ip_hdr(skb)->daddr)); return; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 8f37c9cc868b0..9ef6581168f0f 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -630,7 +630,7 @@ int ip6_forward(struct sk_buff *skb) /* Limit redirects both by destination (here) and by source (inside ndisc_send_redirect) */ - if (inet_peer_xrlim_allow(peer, 1*HZ)) + if (peer && inet_peer_xrlim_allow(peer, 1*HZ)) ndisc_send_redirect(skb, target); rcu_read_unlock(); } else { diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 9df1fba967c6e..7262356de10d9 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1706,6 +1706,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) } peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr); + if (!peer) + goto release; ret = inet_peer_xrlim_allow(peer, 1*HZ); if (!ret) From 1b722740ac5c2b2070f9ba922f4e0f227faf0246 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 13:35:37 -0700 Subject: [PATCH 2982/3297] hwmon: (nct6775-core) Prevent access to unsupported weight registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] Sashiko reports: During initialization of the nct6116 chip, the driver sets data->pwm_num to 5. However, it assigns several NCT6106 register arrays (such as NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. These arrays only contain 3 elements. In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If data->has_pwm has bits 3 or 4 set (which is structurally possible for nct6116), the loop attempts to read elements at index 3 and 4 from these 3-element arrays. This results in a global out-of-bounds read, which can be caught by KASAN. Furthermore, the driver uses these garbage out-of-bounds values as hardware register addresses for subsequent read and write operations. This leads to invalid hardware register access, potentially causing hardware misconfiguration or system crashes. The underlying problem is that the chip does support up to five fan control channels, but only the first three support weight control. Fix the problem by extending the affected weight register arrays with zeroed fields. The driver uses zeroed register addresses to determine if a register is supported or not, and skips accesses for unsupported registers. Reported-by: Sashiko Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") Cc: Björn Gerhart Cc: Florian Bezdeka Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/nct6775-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 506d57025c3bd..ce2cf1f229004 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; -static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; -static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; -static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; +static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; From 2e0c6761c055325772a25595caa589ae92f51e30 Mon Sep 17 00:00:00 2001 From: David Corvaglia Date: Sun, 26 Jul 2026 06:26:05 +0000 Subject: [PATCH 2983/3297] net: bridge: mrp: fix Option TLV length in MRP_Test frames [ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ] oui is a pointer, so sizeof(oui) is the pointer size. The MRA Option TLV thus advertises a wrong length (15 vs 10 on x86_64), causing misparsing of the frame on peers. Fix is to replace with sizeof(*oui). Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") Signed-off-by: David Corvaglia Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_mrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index 3c36fa24bc05a..92a0debf4a4a6 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, struct br_mrp_oui_hdr *oui = NULL; u8 length; - length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + + length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + MRP_OPT_PADDING; br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); From ae20a8a4de06a289d40b0a0633d8d573f1fcb049 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 17:26:37 +0800 Subject: [PATCH 2984/3297] forcedeth: fix UAF of txrx_stats in nv_remove [ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, and nv_close()/drain may still access txrx_stats, leading to a use-after-free. Free the stats only after unregister_netdev(). Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") Signed-off-by: Chenguang Zhao Reviewed-by: Vadim Fedorenko Reviewed-by: Zhu Yanjun Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 19aa1f1538aa3..c09fb7694facb 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -6187,10 +6187,10 @@ static void nv_remove(struct pci_dev *pci_dev) struct net_device *dev = pci_get_drvdata(pci_dev); struct fe_priv *np = netdev_priv(dev); - free_percpu(np->txrx_stats); - unregister_netdev(dev); + free_percpu(np->txrx_stats); + nv_restore_mac_addr(pci_dev); /* restore any phy related changes */ From 28548ecc2b4587969397aacc801895fd04a00682 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:17 -0300 Subject: [PATCH 2985/3297] hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors [ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ] During adt7470_read_temperatures(), the driver temporarily switches the PWM channels to manual mode, performs the temperature collection, and then restores the original configuration registers. However, if an I2C transaction fails at any point after entering manual mode, the function aborts and returns immediately. This leaves the configuration registers un-restored, permanently trapping the fans in manual mode. Introduce a recovery path to ensure that the original PWM configuration registers are always restored, even when intermediate I2C operations fail. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index dbee6926fa055..ef8f411df61be 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in /* Probe for temperature sensors. Assumes lock is held */ static int adt7470_read_temperatures(struct adt7470_data *data) { - unsigned long res; + struct device *dev = regmap_get_device(data->regmap); + u8 pwm[ADT7470_FAN_COUNT]; unsigned int pwm_cfg[2]; - int err; + unsigned long res; + int err, err2; int i; - u8 pwm[ADT7470_FAN_COUNT]; /* save pwm[1-4] config register */ err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); @@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), ADT7470_PWM_AUTO_MASK, 0); if (err < 0) - return err; + goto out_restore; /* write pwm control to whatever it was */ err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], ADT7470_PWM_COUNT); if (err < 0) - return err; + goto out_restore; /* start reading temperature sensors */ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); if (err < 0) - return err; + goto out_restore; /* Delay is 200ms * number of temp sensors. */ res = msleep_interruptible((data->num_temp_sensors >= 0 ? @@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ADT7470_T05_STB_MASK, 0); if (err < 0) - return err; + goto out_restore; +out_restore: /* restore pwm[1-4] config registers */ - err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); - if (err < 0) - return err; - err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); + if (err2 < 0) { + dev_warn_ratelimited(dev, + "failed to restore PWM{1,2} config (%d)\n", + err2); + + if (!err) + err = err2; + } + + err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + if (err2 < 0) { + dev_warn_ratelimited(dev, + "failed to restore PWM{3,4} config (%d)\n", + err2); + + if (!err) + err = err2; + } + if (err < 0) return err; From 3e06ff0c79ea3e9ad431e1b75c020aceb325d0f1 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:18 -0300 Subject: [PATCH 2986/3297] hwmon: (adt7470) Fix cache updated before hardware write on I2C error [ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ] adt7470_temp_write() and adt7470_pwm_write() update the driver's cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing the corresponding regmap_write(), and never check whether the write succeeded before committing that update. If the I2C transaction fails, the function correctly propagates the error to the caller, but the cache silently keeps the new value, which was never actually applied to the hardware. Subsequent reads then report a value that does not match the device state. Reorder both write paths to update the cache only after a successful regmap_write(), so the cache always reflects what was actually written to the hardware. Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index ef8f411df61be..06b19fd382457 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va switch (attr) { case hwmon_temp_min: mutex_lock(&data->lock); - data->temp_min[channel] = val; err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); + if (!err) + data->temp_min[channel] = val; mutex_unlock(&data->lock); break; case hwmon_temp_max: mutex_lock(&data->lock); - data->temp_max[channel] = val; err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); + if (!err) + data->temp_max[channel] = val; mutex_unlock(&data->lock); break; default: @@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val case hwmon_pwm_input: val = clamp_val(val, 0, 255); mutex_lock(&data->lock); - data->pwm[channel] = val; err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), - data->pwm[channel]); + val); + if (!err) + data->pwm[channel] = val; mutex_unlock(&data->lock); break; case hwmon_pwm_enable: @@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val val--; mutex_lock(&data->lock); - data->pwm_automatic[channel] = val; err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), pwm_auto_reg_mask, val ? pwm_auto_reg_mask : 0); + if (!err) + data->pwm_automatic[channel] = val; mutex_unlock(&data->lock); break; case hwmon_pwm_freq: From 82d65f7ef11edcea0228745440b8b4b1f222c34c Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:19 -0300 Subject: [PATCH 2987/3297] hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread [ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] When userspace configures 'auto_update_interval' to 0 via sysfs, the background kthread executes schedule_timeout_interruptible(0), which returns immediately. If 'num_temp_sensors' is concurrently or previously set to 0, the msleep_interruptible() delay inside adt7470_read_temperatures() also becomes 0. This combination forces the background thread into a tight, unbounded busy-loop, hogging the CPU and flooding the I2C bus with a continuous stream of transactions. Fix this vulnerability by raising the lower limit of the clamp_val in auto_update_interval_store() from 0 to 500 milliseconds. This guarantees a reasonable minimum sleep window between sensor updates, protecting the system from intentional or accidental I2C bus denial of service. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 06b19fd382457..a927e0b6d3319 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = clamp_val(temp, 0, 60000); + temp = clamp_val(temp, 500, 60000); mutex_lock(&data->lock); data->auto_update_interval = temp; From d5d4034bb6f6ee0aba4baf0daf9b81c8a3165bf0 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:21 -0300 Subject: [PATCH 2988/3297] hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() [ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ] During the conversion the alarm callback started interpreting the channel index as an alarm bitmask, resulting in incorrect alarm reporting. Compute the proper alarm bit instead. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index a927e0b6d3319..41fa6a0e8b937 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ALARM2(x) ((x) << 8) +/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and + * 0x72, or BIT(0)..BIT(6) of data->alarm. + * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and + * 0x73, or BIT(8)..BIT(10) of data->alarm. + */ +#define TEMP_ALARM_BIT(ch) ({ \ + typeof(ch) _ch = (ch); \ + (1 << (_ch < 7 ? _ch : _ch + 1)); \ +}) + +/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in + * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. + */ +#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) + #define ADT7470_VENDOR 0x41 #define ADT7470_DEVICE 0x70 /* datasheet only mentions a revision 2 */ @@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va *val = 1000 * data->temp_max[channel]; break; case hwmon_temp_alarm: - *val = !!(data->alarm & channel); + *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); break; default: return -EOPNOTSUPP; @@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val *val = 0; break; case hwmon_fan_alarm: - *val = !!(data->alarm & (1 << (12 + channel))); + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); break; default: return -EOPNOTSUPP; From 1d6b54dbe88500c927298f2cb0062b6a6fa4b865 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:20 -0300 Subject: [PATCH 2989/3297] hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks [ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are currently defined with swapped bit values. According to Table 22 of the ADT7470 datasheet, the Fan Control Mode Configuration for register 0x69 follows the exact same bit position layout as register 0x68: - 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 - 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 - 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 - 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. This typo did not cause any functional bugs because these specific macros are never referenced in the driver code. Instead, the driver correctly applies the configuration by relying on the modulo parity of the channel index (e.g., `channel % 2`) to selectively apply either ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). Since the bit layout is identical between the two configuration registers, the hardware is currently configured correctly. Fix the macro definitions to reflect the datasheet accurately and prevent future bugs or confusion during code review and refactoring. As this is a purely cosmetic fix with no functional impact, a backport to stable kernels is not necessary. Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 41fa6a0e8b937..f0a45538ded6d 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_PWM1_AUTO_MASK 0x80 #define ADT7470_PWM_AUTO_MASK 0xC0 #define ADT7470_REG_PWM34_CFG 0x69 -#define ADT7470_PWM3_AUTO_MASK 0x40 -#define ADT7470_PWM4_AUTO_MASK 0x80 +#define ADT7470_PWM4_AUTO_MASK 0x40 +#define ADT7470_PWM3_AUTO_MASK 0x80 #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E From ddd689bd7226435576e07346b7c4b0e1e808590c Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:22 -0300 Subject: [PATCH 2990/3297] hwmon: (adt7470) Use cached PWM frequency value [ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ] adt7470_pwm_read() currently ignores failures returned by pwm1_freq_get(). If the register read fails, the negative error code is returned through *val while the function itself reports success, potentially exposing a negative PWM frequency through sysfs. Fix this by using the cached PWM frequency maintained by the driver, eliminating the register access from the read path. Apart from the corrected error propagation and using the cached value, no functional change is intended. Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index f0a45538ded6d..43c0130342622 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -182,6 +182,7 @@ struct adt7470_data { u8 pwm_min[ADT7470_PWM_COUNT]; s8 pwm_tmin[ADT7470_PWM_COUNT]; u8 pwm_auto_temp[ADT7470_PWM_COUNT]; + u32 pwm_freq; struct task_struct *auto_update; unsigned int auto_update_interval; @@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, } /* These are the valid PWM frequencies to the nearest Hz */ -static const int adt7470_freq_map[] = { +static const u32 adt7470_freq_map[] = { 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 }; @@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val *val = 1 + data->pwm_automatic[channel]; break; case hwmon_pwm_freq: - *val = pwm1_freq_get(dev); + *val = data->pwm_freq; break; default: return -EOPNOTSUPP; @@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq) { struct adt7470_data *data = dev_get_drvdata(dev); unsigned int low_freq = ADT7470_CFG_LF; + u32 closest_freq; int index; int err; /* Round the user value given to the closest available frequency */ index = find_closest(freq, adt7470_freq_map, ARRAY_SIZE(adt7470_freq_map)); + closest_freq = adt7470_freq_map[index]; if (index >= 8) { index -= 8; @@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq) err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, ADT7470_FREQ_MASK, index << ADT7470_FREQ_SHIFT); + if (err < 0) + goto out; + + data->pwm_freq = closest_freq; out: mutex_unlock(&data->lock); @@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client) struct device *dev = &client->dev; struct adt7470_data *data; struct device *hwmon_dev; + int freq_val; int err; data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); @@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client) if (err < 0) return err; + freq_val = pwm1_freq_get(dev); + if (freq_val <= 0) { + err = freq_val < 0 ? freq_val : -EINVAL; + return err; + } + + data->pwm_freq = (u32)freq_val; + /* Register sysfs hooks */ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &adt7470_chip_info, From 96ad57d31763559d376416cdf3bf5ae79bbbebec Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:23 -0300 Subject: [PATCH 2991/3297] hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read [ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ] If the fan data becomes 0 between the FAN_DATA_VALID() check and the FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash due to a race with a concurrent update of the cached fan value. Fix a TOCTOU issue by reading fan data once. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 43c0130342622..3a2d408a45be8 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) { struct adt7470_data *data = adt7470_update_device(dev); + u16 fan_data; if (IS_ERR(data)) return PTR_ERR(data); switch (attr) { case hwmon_fan_input: - if (FAN_DATA_VALID(data->fan[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan[channel]); break; case hwmon_fan_min: - if (FAN_DATA_VALID(data->fan_min[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan_min[channel]); break; case hwmon_fan_max: - if (FAN_DATA_VALID(data->fan_max[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan_max[channel]); break; case hwmon_fan_alarm: *val = !!(data->alarm & FAN_ALARM_BIT(channel)); - break; + return 0; default: return -EOPNOTSUPP; } + if (FAN_DATA_VALID(fan_data)) + *val = FAN_PERIOD_TO_RPM(fan_data); + else + *val = 0; + return 0; } From 5a6b0ccb8b01819c24a8f0af4b67b6f5833032ec Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:24 -0300 Subject: [PATCH 2992/3297] hwmon: (adt7470) Fix PWM auto temp state array and bounds check [ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] In pwm_auto_temp_store(), the parsed user input was missing bounds checks, allowing values > 0xF to overflow into the adjacent channel's bits. Furthermore, the value was being incorrectly written to the pwm_automatic state array instead of pwm_auto_temp. Fix this by rejecting values > 0xF with -EINVAL, and assigning the value to the correct array only after a successful I2C write. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/adt7470.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 3a2d408a45be8..0fad4bfe53df6 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, if (temp < 0) return temp; + if (temp > 0xF) + return -EINVAL; + mutex_lock(&data->lock); - data->pwm_automatic[attr->index] = temp; if (!(attr->index % 2)) { mask = 0xF0; @@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, } err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); + if (!err) + data->pwm_auto_temp[attr->index] = temp; + mutex_unlock(&data->lock); return err < 0 ? err : count; From db986098f30881fafcc752800aa3b13fd289c922 Mon Sep 17 00:00:00 2001 From: Yun Lu Date: Tue, 21 Jul 2026 10:38:36 +0800 Subject: [PATCH 2993/3297] rtase: fix double free of multi-frag skb on DMA map failure [ Upstream commit 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 ] In rtase_start_xmit(), when the head buffer DMA mapping fails after rtase_xmit_frags() has mapped all fragments, the error path clears the fragment descriptors with rtase_tx_clear_range(), which frees the skb through the last-frag slot and accounts tx_dropped. Control then falls through to the common error label, which frees the same skb a second time and counts it again. Return right after clearing the fragments when the skb owns frags; the no-frag case still drops through and frees the head skb once. Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function") Signed-off-by: Yun Lu Reviewed-by: Jacob Keller Reviewed-by: Justin Lai Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index a57a525327a3b..bc9b14614f7a7 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -1620,6 +1620,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, err_dma_1: ring->skbuff[entry] = NULL; rtase_tx_clear_range(ring, ring->cur_idx + 1, frags); + if (frags) + /* the frags were cleared above, along with the skb */ + return NETDEV_TX_OK; err_dma_0: tp->stats.tx_dropped++; From 3d24f2e5641b24ce961c0161e8ec1bd33cd92142 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 2 Jul 2026 23:15:55 +0200 Subject: [PATCH 2994/3297] powerpc/boot: Fix simpleboot CPU node lookup check [ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly. Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev Signed-off-by: Sasha Levin --- arch/powerpc/boot/simpleboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c index c80691d83880b..27591df41e9e8 100644 --- a/arch/powerpc/boot/simpleboot.c +++ b/arch/powerpc/boot/simpleboot.c @@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, /* finally, setup the timebase */ node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", "cpu", sizeof("cpu")); - if (!node) + if (node < 0) fatal("Cannot find cpu node\n"); timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); if (timebase && (size == 4)) From b407b98cf665d230d9c89fc8c2b4ba87a7205b42 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 2 Jul 2026 23:15:56 +0200 Subject: [PATCH 2995/3297] powerpc/boot: Fix treeboot-currituck CPU node lookup check [ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly. Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev Signed-off-by: Sasha Levin --- arch/powerpc/boot/treeboot-currituck.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c index d53e8a592f816..5b5363b74f9f3 100644 --- a/arch/powerpc/boot/treeboot-currituck.c +++ b/arch/powerpc/boot/treeboot-currituck.c @@ -102,7 +102,7 @@ void platform_init(void) node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", "cpu", sizeof("cpu")); - if (!node) + if (node < 0) fatal("Cannot find cpu node\n"); timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); if (timebase && (size == 4)) From acbf711a20669395d34efa8070f57a1a43bc18ed Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 2 Jul 2026 23:15:57 +0200 Subject: [PATCH 2996/3297] powerpc/boot: Fix treeboot-akebono CPU node lookup check [ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly. Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev Signed-off-by: Sasha Levin --- arch/powerpc/boot/treeboot-akebono.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c index e3cc2599869cc..1b529037480fb 100644 --- a/arch/powerpc/boot/treeboot-akebono.c +++ b/arch/powerpc/boot/treeboot-akebono.c @@ -146,7 +146,7 @@ void platform_init(char *userdata) node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", "cpu", sizeof("cpu")); - if (!node) + if (node < 0) fatal("Cannot find cpu node\n"); timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); if (timebase && (size == 4)) From f82a2ded3d7a9098267c84f14eafdb98c4550ac5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 24 Jul 2026 09:11:37 +0000 Subject: [PATCH 2997/3297] net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister() [ Upstream commit 080695e6f005e2396f1207fd69d24c442cb230c6 ] syzbot reported a memory leak [1] in the UDP tunnel NIC offload code. When device registration fails (e.g. in register_netdevice()), netdev core unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister() returns early: if (utn->work_pending) return; Because failed registrations do not enter netdev_wait_allrefs_any(), no subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked permanently. Fix this by removing the early return. Instead, synchronously cancel any pending work with cancel_delayed_work_sync() before freeing @utn. To be able to call cancel_delayed_work_sync() while holding RTNL (the work also needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work()) to prevent high CPU contention while waiting for RTNL lock. The utn->work_pending bookkeeping is no longer needed and is removed, as the workqueue core already tracks the pending/running state of the work. [1] BUG: memory leak unreferenced object 0xffff888127d5f840 (size 96): comm "syz-executor", pid 5806, jiffies 4294942188 backtrace (crc 99fdb6c8): __kmalloc_noprof+0x3bf/0x550 udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline] udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline] udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931 notifier_call_chain+0x59/0x160 kernel/notifier.c:85 call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250 register_netdevice+0xc10/0xeb0 net/core/dev.c:11478 Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure") Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c index b13e8f7092f46..4360d159ee8a7 100644 --- a/net/ipv4/udp_tunnel_nic.c +++ b/net/ipv4/udp_tunnel_nic.c @@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry { * @lock: protects all fields * @need_sync: at least one port start changed * @need_replay: space was freed, we need a replay of all ports - * @work_pending: @work is currently scheduled * @n_tables: number of tables under @entries * @missed: bitmap of tables which overflown * @entries: table of tables of ports currently offloaded */ struct udp_tunnel_nic { - struct work_struct work; + struct delayed_work work; struct net_device *dev; @@ -46,7 +45,6 @@ struct udp_tunnel_nic { u8 need_sync:1; u8 need_replay:1; - u8 work_pending:1; unsigned int n_tables; unsigned long missed; @@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) static void udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) { - if (!utn->need_sync || utn->work_pending) + if (!utn->need_sync) return; - queue_work(udp_tunnel_nic_workqueue, &utn->work); - utn->work_pending = 1; + queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0); } static bool @@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn) static void udp_tunnel_nic_device_sync_work(struct work_struct *work) { struct udp_tunnel_nic *utn = - container_of(work, struct udp_tunnel_nic, work); + container_of(work, struct udp_tunnel_nic, work.work); - rtnl_lock(); + /* We cannot block on RTNL here, otherwise we would deadlock with + * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync() + * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended. + */ + if (!rtnl_trylock()) { + queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1); + return; + } mutex_lock(&utn->lock); - - utn->work_pending = 0; __udp_tunnel_nic_device_sync(utn->dev, utn); if (utn->need_replay) @@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info, if (!utn) return NULL; utn->n_tables = n_tables; - INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work); + INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work); mutex_init(&utn->lock); for (i = 0; i < n_tables; i++) { @@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn) udp_tunnel_nic_flush(dev, utn); udp_tunnel_nic_unlock(dev); - /* Wait for the work to be done using the state, netdev core will - * retry unregister until we give up our reference on this device. + /* Make sure no work is running or queued before freeing @utn. + * The work handler uses rtnl_trylock(), so it will not deadlock + * against the RTNL we are holding here. */ - if (utn->work_pending) - return; + cancel_delayed_work_sync(&utn->work); udp_tunnel_nic_free(utn); release_dev: From b558e07708d886acfcf4b0391ed7a8546e81d326 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Thu, 23 Jul 2026 09:09:28 +0800 Subject: [PATCH 2998/3297] wifi: mac80211: validate individual TWT params before driver setup [ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ] ieee80211_process_rx_twt_action() only partially validates a received S1G TWT setup frame before queueing it. An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() with twt->length too short for the full struct ieee80211_twt_params. The individual path passes twt to drv_add_twt_setup(). Both the tracepoint and the driver callback consume the complete parameters block, not merely req_type. Do not pass a short individual agreement to the driver. Broadcast agreements remain unchanged because they are rejected locally after accessing only req_type. Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") Assisted-by: Codex:gpt-5 Assisted-by: Claude:opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com [edit commit message to not overclaim lack of validation nor understate driver impact] Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/s1g.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c index 1f68df6e80670..7702cff84d546 100644 --- a/net/mac80211/s1g.c +++ b/net/mac80211/s1g.c @@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable; struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && + twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) + return; + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); /* broadcast TWT not supported yet */ From 627826ef4208042b0470d1a3fdb729ce35471a0d Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:12 +0100 Subject: [PATCH 2999/3297] netfs: clear PG_private_2 on copy-to-cache append failure [ Upstream commit a81fc9266e1c5fef9ccf675a9b44b2f4ab464923 ] netfs_pgpriv2_copy_to_cache() marks the folio with PG_private_2 before netfs_pgpriv2_copy_folio() appends it to the copy-to-cache rolling buffer. If the append fails, the folio is not queued for cache writeback, so the PG_private_2 state and its reference must be released immediately. Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-2-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/read_pgpriv2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index a1489aa29f782..7eacc58abadb7 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -54,6 +54,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio /* Attach the folio to the rolling buffer. */ if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { + folio_end_private_2(folio); clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); return; } From 291ebecdf315d222afd6ef3fc6b67c8bba51c2f1 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:13 +0100 Subject: [PATCH 3000/3297] netfs: handle single writeback rolling buffer allocation failure [ Upstream commit 37a1c535c80c67d98668d190c7432f9ebda43310 ] netfs_write_folio_single() takes an extra folio reference before appending the folio to the rolling buffer. rolling_buffer_append() can fail if it cannot allocate another folio_queue. Check the return value and drop the extra folio reference before returning the error. Fixes: 49866ce7ea8d ("netfs: Add support for caching single monolithic objects such as AFS dirs") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-3-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 76614339554ea..5f216907b1e50 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -730,6 +730,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, size_t iter_off = 0; size_t fsize = folio_size(folio), flen; loff_t fpos = folio_pos(folio); + ssize_t ret; bool to_eof = false; bool no_debug = false; @@ -758,7 +759,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, /* Attach the folio to the rolling buffer. */ folio_get(folio); - rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + if (ret < 0) { + folio_put(folio); + return ret; + } /* Move the submission point forward to allow for write-streaming data * not starting at the front of the page. We don't do write-streaming From 2c148a31ca01fc160aceec777bbc0041b0d0b8bd Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:14 +0100 Subject: [PATCH 3001/3297] netfs: release readahead folios on iterator preparation failure [ Upstream commit 87eb3d272dcbcbbfe5c1576c10e5dc72810cf1f6 ] netfs_prepare_read_iterator() batches readahead folios in put_batch so that the folio references can be dropped after the I/O iterator has been prepared. If rolling_buffer_load_from_ra() fails after earlier folios have been batched, the function returns immediately and leaves those references held. Release the batch before returning the error. Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-4-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index fab3181c7f869..221287f8925a9 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, added = rolling_buffer_load_from_ra(&rreq->buffer, ractl, &put_batch); - if (added < 0) + if (added < 0) { + folio_batch_release(&put_batch); return added; + } rreq->submitted += added; } folio_batch_release(&put_batch); From 276f1f180f55d56cf5992a581e20ed2b3dfa6ced Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 13:57:35 +0800 Subject: [PATCH 3002/3297] net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in poll_controller [ Upstream commit e095f249e2209674f6366f6db0383a2b96e19239 ] mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq cookie), but mtk_poll_controller incorrectly passed the net_device *. Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled would then crash. Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()") Signed-off-by: Chenguang Zhao Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 065f969ee44ef..98cf11a706e14 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3446,7 +3446,7 @@ static void mtk_poll_controller(struct net_device *dev) mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask); - mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev); + mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth); mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask); } From ae7120102e1bbe2c6167a98454bddde55ecc8357 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 28 Jul 2026 08:41:40 -0700 Subject: [PATCH 3003/3297] hwmon: (pmbus) Fix return value from pmbus_update_byte_data() [ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] pmbus_update_byte_data() is supposed to return a negative error code or 0. However, if no change is made to the register, it actually returns the register value. This can result in problems if the calling code explicitly expects to see an error code or 0. Fix it to return 0 on success or the error code as expected. Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/pmbus/pmbus_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index ddf64e72751cc..4e1c66670ead9 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -512,7 +512,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, if (tmp != rv) rv = _pmbus_write_byte_data(client, page, reg, tmp); - return rv; + return rv < 0 ? rv : 0; } EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); From d44081c61dc9244283c86f12fee037834f8b4600 Mon Sep 17 00:00:00 2001 From: Joshua Hay Date: Tue, 30 Jun 2026 16:56:19 -0700 Subject: [PATCH 3004/3297] idpf: adjust TxQ ring count minimum [ Upstream commit bef152db47debcd14cbacefc5767f6f026c4bc89 ] Set the TxQ ring count minimum to 128 descriptors. Any lower than this, and the queue will stall and trigger Tx timeouts in flow based scheduling mode. This is because next_to_clean might never be updated. In flow based scheduling mode, next_to_clean is only updated after a descriptor completion is processed, i.e. after the RE bit is set in the last descriptor of a Tx packet. This will never happen with a ring size of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value of last_re is initialized/set to, the calculated gap will be at most 63 and never trigger the RE bit. Even a ring size of 96 does not solve this. Because of how infrequent next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED will be much smaller on average. This increases the chance the queue will be stopped because a multi-descriptor packet, e.g. a large LSO packet, does not see enough resources on the ring. In this case, the queue will trigger the stop logic. The queue permanently stalls because there is no chance for a descriptor completion to update next_to_clean since it is dependent on a packet being sent. Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool") Signed-off-by: Joshua Hay Reviewed-by: Aleksandr Loktionov Tested-by: Samuel Salin Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +---- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c index c859665b2dc89..e6563ad31f5ca 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c @@ -3075,10 +3075,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb, tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE; tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP; - /* Set the RE bit to periodically "clean" the descriptor ring. - * MIN_GAP is set to MIN_RING size to ensure it will be set at - * least once each time around the ring. - */ + /* Set the RE bit periodically to "clean" the descriptor ring */ if (idpf_tx_splitq_need_re(tx_q)) { tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE; tx_q->txq_grp->num_completions_pending++; diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h index 423cc9486dce7..aa0b93b5c8599 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h @@ -21,7 +21,7 @@ /* Mailbox Queue */ #define IDPF_MAX_MBXQ 1 -#define IDPF_MIN_TXQ_DESC 64 +#define IDPF_MIN_TXQ_DESC 128 #define IDPF_MIN_RXQ_DESC 64 #define IDPF_MIN_TXQ_COMPLQ_DESC 256 #define IDPF_MAX_QIDS 256 From 6ebbf198e76cab27a41570b644b6c58246e8cf95 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Fri, 3 Jul 2026 01:03:32 -0400 Subject: [PATCH 3005/3297] idpf: Fix mailbox IRQ name leak on request failure [ Upstream commit 9bff30482c10f70d9e56c0633a6616e07140e217 ] idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling request_irq(). On success, the name is released later through kfree(free_irq()), but request_irq() failure returns without freeing it. Free the allocated name on the request_irq() failure path. Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request") Signed-off-by: Yuho Choi Reviewed-by: Aleksandr Loktionov Tested-by: Samuel Salin Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c index 131a8121839bd..590119f4097d0 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_lib.c +++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c @@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter) if (err) { dev_err(&adapter->pdev->dev, "IRQ request for mailbox failed, error: %d\n", err); - + kfree(name); return err; } From 4e1f45de5b313db2a3f2eea72d79b82463bb21e1 Mon Sep 17 00:00:00 2001 From: Przemyslaw Korba Date: Wed, 20 May 2026 13:50:06 +0200 Subject: [PATCH 3006/3297] ice: suppress DPLL errors during reset recovery [ Upstream commit b00be7c6b4bd7da3d510753b27ff6cb7ec647d07 ] During reset recovery, the admin queue returns EBUSY which is expected behavior. However, the DPLL subsystem was logging these as errors and incrementing the error counter, potentially leading to unnecessary warnings and even disabling the DPLL periodic worker if the threshold was reached. Suppress error logging and error counter increments when the admin queue returns EBUSY, as this is expected during reset recovery and not a real failure condition. test case: - ethtool --reset eth3 irq-shared dma-shared filter-shared offload-shared mac-shared phy-shared ram-shared - observe if dmesg EBUSY errors are gone Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: Przemyslaw Korba Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_dpll.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 023d0de12670c..2a47d9f3cf7ef 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -703,7 +703,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin, ret, libie_aq_str(pf->hw.adminq.sq_last_status), pin_type_name[pin_type], pin->idx); - else + else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) dev_err_ratelimited(ice_pf_to_dev(pf), "err:%d %s failed to update %s pin:%u\n", ret, @@ -2669,7 +2669,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf, *phase_offset_pins_updated = 0; ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas, ARRAY_SIZE(meas)); - if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) { + if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN || + pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) { return 0; } else if (ret) { dev_err(ice_pf_to_dev(pf), @@ -2731,10 +2732,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init) d->dpll_idx, d->prev_input_idx, d->input_idx, d->dpll_state, d->prev_dpll_state, d->mode); if (ret) { - dev_err(ice_pf_to_dev(pf), - "update dpll=%d state failed, ret=%d %s\n", - d->dpll_idx, ret, - libie_aq_str(pf->hw.adminq.sq_last_status)); + /* EBUSY is expected during reset recovery, don't log error */ + if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) + dev_err(ice_pf_to_dev(pf), + "update dpll=%d state failed, ret=%d %s\n", + d->dpll_idx, ret, + libie_aq_str(pf->hw.adminq.sq_last_status)); return ret; } if (init) { @@ -2803,7 +2806,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work) d->periodic_counter % dp->phase_offset_monitor_period == 0) ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf); if (ret) { - d->cgu_state_acq_err_num++; + /* EBUSY is expected during reset recovery */ + if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) + d->cgu_state_acq_err_num++; /* stop rescheduling this worker */ if (d->cgu_state_acq_err_num > ICE_CGU_STATE_ACQ_ERR_THRESHOLD) { From 63c0f396a18b767eb28e895eb95bbdce6c172c59 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Mon, 20 Jul 2026 17:53:33 +0300 Subject: [PATCH 3007/3297] Bluetooth: ISO: clear iso_data always when detaching conn from hcon [ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ] When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is necessary, otherwise later iso_conn_free() will UAF. Fix clearing of iso_data in iso_sock_disconn() Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on iso_sock_release() followed by hci_abort_conn_sync(). Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index f2ff10ae76e22..5868ad1f7128e 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -826,6 +826,7 @@ static void iso_sock_disconn(struct sock *sk) sk->sk_state = BT_DISCONN; iso_conn_lock(iso_pi(sk)->conn); hci_conn_drop(iso_pi(sk)->conn->hcon); + iso_pi(sk)->conn->hcon->iso_data = NULL; iso_pi(sk)->conn->hcon = NULL; iso_conn_unlock(iso_pi(sk)->conn); } From 58e3c5289ad230a7e24ae4b0c7b43f5ee6e32136 Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Thu, 23 Jul 2026 14:48:45 +0800 Subject: [PATCH 3008/3297] Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp [ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] l2cap_le_connect_rsp() obtains a channel via __l2cap_get_chan_by_ident() but neither holds a reference nor uses l2cap_chan_hold_unless_zero() before locking and operating on it. A concurrent l2cap_chan_del() triggered by a remote disconnect can free the channel between the lookup and l2cap_chan_lock(), causing a use-after-free. The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() to safely hold a reference, but l2cap_le_connect_rsp() was left unprotected. Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup and l2cap_chan_put() on the exit path, consistent with other L2CAP response handlers. Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") Assisted-by: Claude:deepseek-v4-pro Signed-off-by: Jiale Yao Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e04948fb6832e..6133c65b20172 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4831,6 +4831,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, if (!chan) return -EBADSLT; + chan = l2cap_chan_hold_unless_zero(chan); + if (!chan) + return -EBADSLT; + err = 0; l2cap_chan_lock(chan); @@ -4876,6 +4880,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, } l2cap_chan_unlock(chan); + l2cap_chan_put(chan); return err; } From 72d5bb1d77d7c3330146dc0cfd43f704c64b9594 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:25 +0300 Subject: [PATCH 3009/3297] Bluetooth: ISO: lock sk in iso_sock_getname [ Upstream commit 89cf154d7c18e6e94a3da83051f3cf2bac317ae2 ] Accessing iso_pi(sk)->conn requires lock_sock, which is not held here. Fix by adding the lock/release. Fixes: 2df108c227b2 ("Bluetooth: ISO: Fix using BT_SK_PA_SYNC to detect BIS sockets") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 5868ad1f7128e..4ea41b093ac65 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1392,6 +1392,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, BT_DBG("sock %p, sk %p", sock, sk); + lock_sock(sk); + addr->sa_family = AF_BLUETOOTH; if (peer) { @@ -1413,6 +1415,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, sa->iso_bdaddr_type = iso_pi(sk)->src_type; } + release_sock(sk); + return len; } From f1f167991a68fb4a458ebbd98e511733abdd0e11 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 2 Sep 2025 11:11:40 -0400 Subject: [PATCH 3010/3297] Bluetooth: HCI: Add initial support for PAST [ Upstream commit 33b2835f0b7e2a458473b0e3a23b54b92108b6b0 ] This adds PAST related commands (HCI_OP_LE_PAST, HCI_OP_LE_PAST_SET_INFO and HCI_OP_LE_PAST_PARAMS) and events (HCI_EV_LE_PAST_RECEIVED) along with handling of PAST sender and receiver features bits including new MGMG settings ( HCI_EV_LE_PAST_RECEIVED and MGMT_SETTING_PAST_RECEIVER) which userspace can use to determine if PAST is supported by the controller. Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 4311fd6f4290 ("Bluetooth: ISO: lock sk in iso_connect_ind") Signed-off-by: Sasha Levin --- include/net/bluetooth/hci.h | 54 ++++++++++++++++++++++ include/net/bluetooth/hci_core.h | 12 +++++ include/net/bluetooth/mgmt.h | 2 + net/bluetooth/hci_event.c | 79 ++++++++++++++++++++++++++++---- net/bluetooth/hci_sync.c | 3 ++ net/bluetooth/iso.c | 25 ++++++++++ net/bluetooth/mgmt.c | 12 +++++ 7 files changed, 177 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 2613cfe5b8a39..87b3f99e6ebf1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -647,6 +647,8 @@ enum { #define HCI_LE_EXT_ADV 0x10 #define HCI_LE_PERIODIC_ADV 0x20 #define HCI_LE_CHAN_SEL_ALG2 0x40 +#define HCI_LE_PAST_SENDER 0x01 +#define HCI_LE_PAST_RECEIVER 0x02 #define HCI_LE_CIS_CENTRAL 0x10 #define HCI_LE_CIS_PERIPHERAL 0x20 #define HCI_LE_ISO_BROADCASTER 0x40 @@ -2068,6 +2070,44 @@ struct hci_cp_le_set_privacy_mode { __u8 mode; } __packed; +#define HCI_OP_LE_PAST 0x205a +struct hci_cp_le_past { + __le16 handle; + __le16 service_data; + __le16 sync_handle; +} __packed; + +struct hci_rp_le_past { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_PAST_SET_INFO 0x205b +struct hci_cp_le_past_set_info { + __le16 handle; + __le16 service_data; + __u8 adv_handle; +} __packed; + +struct hci_rp_le_past_set_info { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_PAST_PARAMS 0x205c +struct hci_cp_le_past_params { + __le16 handle; + __u8 mode; + __le16 skip; + __le16 sync_timeout; + __u8 cte_type; +} __packed; + +struct hci_rp_le_past_params { + __u8 status; + __le16 handle; +} __packed; + #define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060 struct hci_rp_le_read_buffer_size_v2 { __u8 status; @@ -2800,6 +2840,20 @@ struct hci_evt_le_ext_adv_set_term { __u8 num_evts; } __packed; +#define HCI_EV_LE_PAST_RECEIVED 0x18 +struct hci_ev_le_past_received { + __u8 status; + __le16 handle; + __le16 service_data; + __le16 sync_handle; + __u8 sid; + __u8 bdaddr_type; + bdaddr_t bdaddr; + __u8 phy; + __le16 interval; + __u8 clock_accuracy; +} __packed; + #define HCI_EVT_LE_CIS_ESTABLISHED 0x19 struct hci_evt_le_cis_established { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b5640980fbf4e..a08d8910ba265 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2054,6 +2054,18 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define sync_recv_capable(dev) \ ((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER) #define sync_recv_enabled(dev) (le_enabled(dev) && sync_recv_capable(dev)) +#define past_sender_capable(dev) \ + ((dev)->le_features[3] & HCI_LE_PAST_SENDER) +#define past_receiver_capable(dev) \ + ((dev)->le_features[3] & HCI_LE_PAST_RECEIVER) +#define past_capable(dev) \ + (past_sender_capable(dev) || past_receiver_capable(dev)) +#define past_sender_enabled(dev) \ + (le_enabled(dev) && past_sender_capable(dev)) +#define past_receiver_enabled(dev) \ + (le_enabled(dev) && past_receiver_capable(dev)) +#define past_enabled(dev) \ + (past_sender_enabled(dev) || past_receiver_enabled(dev)) #define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \ (!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG))) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index f5be96f08b9d9..8234915854b68 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -119,6 +119,8 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_ISO_BROADCASTER BIT(20) #define MGMT_SETTING_ISO_SYNC_RECEIVER BIT(21) #define MGMT_SETTING_LL_PRIVACY BIT(22) +#define MGMT_SETTING_PAST_SENDER BIT(23) +#define MGMT_SETTING_PAST_RECEIVER BIT(24) #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 396aafa609c9a..ce89f7cbebc7d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -5950,6 +5950,71 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data, hci_dev_unlock(hdev); } +static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle) +{ + struct hci_cp_le_pa_term_sync cp; + + memset(&cp, 0, sizeof(cp)); + cp.handle = handle; + + return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp); +} + +static void hci_le_past_received_evt(struct hci_dev *hdev, void *data, + struct sk_buff *skb) +{ + struct hci_ev_le_past_received *ev = data; + int mask = hdev->link_mode; + __u8 flags = 0; + struct hci_conn *pa_sync, *conn; + + bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); + + hci_dev_lock(hdev); + + hci_dev_clear_flag(hdev, HCI_PA_SYNC); + + conn = hci_conn_hash_lookup_create_pa_sync(hdev); + if (!conn) { + bt_dev_err(hdev, + "Unable to find connection for dst %pMR sid 0x%2.2x", + &ev->bdaddr, ev->sid); + goto unlock; + } + + conn->sync_handle = le16_to_cpu(ev->sync_handle); + conn->sid = HCI_SID_INVALID; + + mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, PA_LINK, + &flags); + if (!(mask & HCI_LM_ACCEPT)) { + hci_le_pa_term_sync(hdev, ev->sync_handle); + goto unlock; + } + + if (!(flags & HCI_PROTO_DEFER)) + goto unlock; + + /* Add connection to indicate PA sync event */ + pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY, + HCI_ROLE_SLAVE); + + if (IS_ERR(pa_sync)) + goto unlock; + + pa_sync->sync_handle = le16_to_cpu(ev->sync_handle); + + if (ev->status) { + set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags); + + /* Notify iso layer */ + hci_connect_cfm(pa_sync, ev->status); + } + +unlock: + hci_dev_unlock(hdev); +} + static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) { @@ -6426,16 +6491,6 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data, hci_dev_unlock(hdev); } -static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle) -{ - struct hci_cp_le_pa_term_sync cp; - - memset(&cp, 0, sizeof(cp)); - cp.handle = handle; - - return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp); -} - static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) { @@ -7250,6 +7305,10 @@ static const struct hci_le_ev { /* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */ HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt, sizeof(struct hci_evt_le_ext_adv_set_term)), + /* [0x18 = HCI_EVT_LE_PAST_RECEIVED] */ + HCI_LE_EV(HCI_EV_LE_PAST_RECEIVED, + hci_le_past_received_evt, + sizeof(struct hci_ev_le_past_received)), /* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */ HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_established_evt, sizeof(struct hci_evt_le_cis_established)), diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 65c8a490c5553..87a2502fccd6e 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4450,6 +4450,9 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) if (ext_adv_capable(hdev)) events[2] |= 0x02; /* LE Advertising Set Terminated */ + if (past_receiver_capable(hdev)) + events[2] |= 0x80; /* LE PAST Received */ + if (cis_capable(hdev)) { events[3] |= 0x01; /* LE CIS Established */ if (cis_peripheral_capable(hdev)) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 4ea41b093ac65..f65e77f7e0349 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -80,6 +80,7 @@ static struct bt_iso_qos default_qos; static bool check_ucast_qos(struct bt_iso_qos *qos); static bool check_bcast_qos(struct bt_iso_qos *qos); static bool iso_match_sid(struct sock *sk, void *data); +static bool iso_match_sid_past(struct sock *sk, void *data); static bool iso_match_sync_handle(struct sock *sk, void *data); static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data); static void iso_sock_disconn(struct sock *sk); @@ -2147,6 +2148,16 @@ static bool iso_match_sid(struct sock *sk, void *data) return ev->sid == iso_pi(sk)->bc_sid; } +static bool iso_match_sid_past(struct sock *sk, void *data) +{ + struct hci_ev_le_past_received *ev = data; + + if (iso_pi(sk)->bc_sid == HCI_SID_INVALID) + return true; + + return ev->sid == iso_pi(sk)->bc_sid; +} + static bool iso_match_sync_handle(struct sock *sk, void *data) { struct hci_evt_le_big_info_adv_report *ev = data; @@ -2166,6 +2177,7 @@ static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data) int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) { struct hci_ev_le_pa_sync_established *ev1; + struct hci_ev_le_past_received *ev1a; struct hci_evt_le_big_info_adv_report *ev2; struct hci_ev_le_per_adv_report *ev3; struct sock *sk; @@ -2179,6 +2191,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) * SID to listen to and once sync is established its handle needs to * be stored in iso_pi(sk)->sync_handle so it can be matched once * receiving the BIG Info. + * 1a. HCI_EV_LE_PAST_RECEIVED: alternative to 1. * 2. HCI_EVT_LE_BIG_INFO_ADV_REPORT: When connect_ind is triggered by a * a BIG Info it attempts to check if there any listening socket with * the same sync_handle and if it does then attempt to create a sync. @@ -2198,6 +2211,18 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) goto done; } + ev1a = hci_recv_event_data(hdev, HCI_EV_LE_PAST_RECEIVED); + if (ev1a) { + sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN, + iso_match_sid_past, ev1a); + if (sk && !ev1a->status) { + iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle); + iso_pi(sk)->bc_sid = ev1a->sid; + } + + goto done; + } + ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT); if (ev2) { /* Check if BIGInfo report has already been handled */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 79eb605be2804..424e7980f32e1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -858,6 +858,12 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (ll_privacy_capable(hdev)) settings |= MGMT_SETTING_LL_PRIVACY; + if (past_sender_capable(hdev)) + settings |= MGMT_SETTING_PAST_SENDER; + + if (past_receiver_capable(hdev)) + settings |= MGMT_SETTING_PAST_RECEIVER; + settings |= MGMT_SETTING_PHY_CONFIGURATION; return settings; @@ -943,6 +949,12 @@ static u32 get_current_settings(struct hci_dev *hdev) if (ll_privacy_enabled(hdev)) settings |= MGMT_SETTING_LL_PRIVACY; + if (past_sender_enabled(hdev)) + settings |= MGMT_SETTING_PAST_SENDER; + + if (past_receiver_enabled(hdev)) + settings |= MGMT_SETTING_PAST_RECEIVER; + return settings; } From 3120664aa3330488386d3809af06e4944d073c5f Mon Sep 17 00:00:00 2001 From: SeungJu Cheon Date: Tue, 21 Apr 2026 11:51:22 +0900 Subject: [PATCH 3011/3297] Bluetooth: ISO: Fix data-race on iso_pi(sk) in socket and HCI event paths [ Upstream commit f958c7805b18e9d69f6b322b231ecee46ec6f331 ] Several iso_pi(sk) fields (qos, qos_user_set, bc_sid, base, base_len, sync_handle, bc_num_bis) are written under lock_sock in iso_sock_setsockopt() and iso_sock_bind(), but read and written under hci_dev_lock only in two other paths: - iso_connect_bis() / iso_connect_cis(), invoked from connect(2), read qos/base/bc_sid and reset qos to default_qos on the qos_user_set validation failure -- all without lock_sock. - iso_connect_ind(), invoked from hci_rx_work, writes sync_handle, bc_sid, qos.bcast.encryption, bc_num_bis, base and base_len on PA_SYNC_ESTABLISHED / PAST_RECEIVED / BIG_INFO_ADV_REPORT / PER_ADV_REPORT events. The BIG_INFO handler additionally passes &iso_pi(sk)->qos together with sync_handle / bc_num_bis / bc_bis to hci_conn_big_create_sync() while setsockopt may be mutating them. Acquire lock_sock around the affected accesses in both paths. The locking order hci_dev_lock -> lock_sock matches the existing iso_conn_big_sync() precedent, whose comment documents the same requirement for hci_conn_big_create_sync(). The HCI connect/bind helpers do not wait for command completion -- they enqueue work via hci_cmd_sync_queue{,_once}() / hci_le_create_cis_pending() and return -- so the added hold time is comparable to iso_conn_big_sync(). KCSAN report: BUG: KCSAN: data-race in iso_connect_cis / iso_sock_setsockopt read to 0xffffa3ae8ce3cdc8 of 1 bytes by task 335 on cpu 0: iso_connect_cis+0x49f/0xa20 iso_sock_connect+0x60e/0xb40 __sys_connect_file+0xbd/0xe0 __sys_connect+0xe0/0x110 __x64_sys_connect+0x40/0x50 x64_sys_call+0xcad/0x1c60 do_syscall_64+0x133/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f write to 0xffffa3ae8ce3cdc8 of 60 bytes by task 334 on cpu 1: iso_sock_setsockopt+0x69a/0x930 do_sock_setsockopt+0xc3/0x170 __sys_setsockopt+0xd1/0x130 __x64_sys_setsockopt+0x64/0x80 x64_sys_call+0x1547/0x1c60 do_syscall_64+0x133/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Reported by Kernel Concurrency Sanitizer on: CPU: 1 UID: 0 PID: 334 Comm: iso_setup_race Not tainted 7.0.0-10949-g8541d8f725c6 #44 PREEMPT(lazy) The iso_connect_ind() races were found by inspection. Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Signed-off-by: SeungJu Cheon Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 4311fd6f4290 ("Bluetooth: ISO: lock sk in iso_connect_ind") Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 54 +++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index f65e77f7e0349..4377fe6cf14df 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -355,6 +355,7 @@ static int iso_connect_bis(struct sock *sk) return -EHOSTUNREACH; hci_dev_lock(hdev); + lock_sock(sk); if (!bis_capable(hdev)) { err = -EOPNOTSUPP; @@ -407,13 +408,9 @@ static int iso_connect_bis(struct sock *sk) goto unlock; } - lock_sock(sk); - err = iso_chan_add(conn, sk, NULL); - if (err) { - release_sock(sk); + if (err) goto unlock; - } /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); @@ -429,9 +426,8 @@ static int iso_connect_bis(struct sock *sk) iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); } - release_sock(sk); - unlock: + release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -459,6 +455,7 @@ static int iso_connect_cis(struct sock *sk) return -EHOSTUNREACH; hci_dev_lock(hdev); + lock_sock(sk); if (!cis_central_capable(hdev)) { err = -EOPNOTSUPP; @@ -513,13 +510,9 @@ static int iso_connect_cis(struct sock *sk) goto unlock; } - lock_sock(sk); - err = iso_chan_add(conn, sk, NULL); - if (err) { - release_sock(sk); + if (err) goto unlock; - } /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); @@ -535,9 +528,8 @@ static int iso_connect_cis(struct sock *sk) iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); } - release_sock(sk); - unlock: + release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -2204,8 +2196,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN, iso_match_sid, ev1); if (sk && !ev1->status) { + lock_sock(sk); iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle); iso_pi(sk)->bc_sid = ev1->sid; + release_sock(sk); } goto done; @@ -2216,8 +2210,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN, iso_match_sid_past, ev1a); if (sk && !ev1a->status) { + lock_sock(sk); iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle); iso_pi(sk)->bc_sid = ev1a->sid; + release_sock(sk); } goto done; @@ -2244,27 +2240,35 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) ev2); if (sk) { - int err; - struct hci_conn *hcon = iso_pi(sk)->conn->hcon; + int err = 0; + bool big_sync; + struct hci_conn *hcon; + lock_sock(sk); + + hcon = iso_pi(sk)->conn->hcon; iso_pi(sk)->qos.bcast.encryption = ev2->encryption; if (ev2->num_bis < iso_pi(sk)->bc_num_bis) iso_pi(sk)->bc_num_bis = ev2->num_bis; - if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && - !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { + big_sync = !test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && + !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags); + + if (big_sync) err = hci_conn_big_create_sync(hdev, hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis, iso_pi(sk)->bc_bis); - if (err) { - bt_dev_err(hdev, "hci_le_big_create_sync: %d", - err); - sock_put(sk); - sk = NULL; - } + + release_sock(sk); + + if (big_sync && err) { + bt_dev_err(hdev, "hci_le_big_create_sync: %d", + err); + sock_put(sk); + sk = NULL; } } @@ -2318,8 +2322,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (!base || base_len > BASE_MAX_LENGTH) goto done; + lock_sock(sk); memcpy(iso_pi(sk)->base, base, base_len); iso_pi(sk)->base_len = base_len; + release_sock(sk); } else { /* This is a PA data fragment. Keep pa_data_len set to 0 * until all data has been reassembled. From e8e9cff6d80eeec28dec4cf7cc18662986945391 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:26 +0300 Subject: [PATCH 3012/3297] Bluetooth: ISO: lock sk in iso_connect_ind [ Upstream commit 4311fd6f429065a8ba208660360a895627a00cf3 ] Accessing iso_pi(sk)->conn requires lock_sock, which is not taken in the "ev3" part of iso_connect_ind. It may also be NULL if socket has transitioned away from the LISTEN/CONNECT states before locking. Fix by adding lock/release. Recheck hcon is valid after lock acquire where needed. Fixes: 168d9bf9c7f0 ("Bluetooth: ISO: Reassemble PA data for bcast sink") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 4377fe6cf14df..d667cbf8a1392 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2246,7 +2246,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) lock_sock(sk); - hcon = iso_pi(sk)->conn->hcon; + hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; iso_pi(sk)->qos.bcast.encryption = ev2->encryption; if (ev2->num_bis < iso_pi(sk)->bc_num_bis) @@ -2286,9 +2286,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (!sk) goto done; - hcon = iso_pi(sk)->conn->hcon; + lock_sock(sk); + + hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; if (!hcon) - goto done; + goto release3; if (ev3->data_status == LE_PA_DATA_TRUNCATED) { /* The controller was unable to retrieve PA data. */ @@ -2296,12 +2298,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) HCI_MAX_PER_AD_TOT_LEN); hcon->le_per_adv_data_len = 0; hcon->le_per_adv_data_offset = 0; - goto done; + goto release3; } if (hcon->le_per_adv_data_offset + ev3->length > HCI_MAX_PER_AD_TOT_LEN) - goto done; + goto release3; memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset, ev3->data, ev3->length); @@ -2320,18 +2322,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) &base_len); if (!base || base_len > BASE_MAX_LENGTH) - goto done; + goto release3; - lock_sock(sk); memcpy(iso_pi(sk)->base, base, base_len); iso_pi(sk)->base_len = base_len; - release_sock(sk); } else { /* This is a PA data fragment. Keep pa_data_len set to 0 * until all data has been reassembled. */ hcon->le_per_adv_data_len = 0; } + +release3: + release_sock(sk); } else { sk = iso_get_sock(&hdev->bdaddr, BDADDR_ANY, BT_LISTEN, NULL, NULL); From 1fc2132950c23d65c203ad9b9ecb463a7088f9e5 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:27 +0300 Subject: [PATCH 3013/3297] Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos [ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ] In iso.c check_bcast_qos(), missing bcast.timeout is not set to its default value, and appears typoed as bcast.sync_timeout. Fix the typo. Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- 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 d667cbf8a1392..a0ef76ecb80b8 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1709,7 +1709,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos) return false; if (!qos->bcast.timeout) - qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT; + qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT; if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000) return false; From dfce8d30fc5be816c06095868e1cc8fb8ee503cb Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:28 +0300 Subject: [PATCH 3014/3297] Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis() [ Upstream commit 4e20192d46a685d73e590a60a4a2419a0a8afcbf ] iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before validating the BIS values, so it's possible to end up with bc_num_bis inconsistent. Assign to iso_pi(sk)->bc_num_bis only after validation. Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index a0ef76ecb80b8..98fa94644b4a6 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1022,15 +1022,15 @@ static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa, goto done; } - iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; - - for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++) + for (int i = 0; i < sa->iso_bc->bc_num_bis; i++) if (sa->iso_bc->bc_bis[i] < 0x01 || sa->iso_bc->bc_bis[i] > 0x1f) { err = -EINVAL; goto done; } + iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; + memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis, iso_pi(sk)->bc_num_bis); From cde36776cfe6451736da07c4ad5e40d80295e812 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 6 Oct 2025 16:53:35 -0400 Subject: [PATCH 3015/3297] Bluetooth: ISO: Fix not updating BIS sender source address [ Upstream commit 577cf4c0a1e8471a0d6c0f36bb3716285e27ad5e ] The source address for a BIS sender/Broadcast Source shall be updated with the advertisement address since in case privacy is enabled it may use an RPA rather than an identity address. Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready") Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 98fa94644b4a6..0d2d9c07bf66b 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2001,6 +2001,25 @@ static void iso_conn_ready(struct iso_conn *conn) BT_DBG("conn %p", conn); if (sk) { + /* Attempt to update source address in case of BIS Sender if + * the advertisement is using a random address. + */ + if (conn->hcon->type == BIS_LINK && + conn->hcon->role == HCI_ROLE_MASTER && + !bacmp(&conn->hcon->dst, BDADDR_ANY)) { + struct hci_conn *bis = conn->hcon; + struct adv_info *adv; + + adv = hci_find_adv_instance(bis->hdev, + bis->iso_qos.bcast.bis); + if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) { + lock_sock(sk); + iso_pi(sk)->src_type = BDADDR_LE_RANDOM; + bacpy(&iso_pi(sk)->src, &adv->random_addr); + release_sock(sk); + } + } + iso_sock_ready(conn->sk); } else { hcon = conn->hcon; From 09a69828ae59430dcf03a9e654ae6d6c193bfb82 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:24 +0300 Subject: [PATCH 3016/3297] Bluetooth: ISO: fix CONNECTED -> CLOSED transition on shutdown/release [ Upstream commit 0786469ee242952008628ed0e2d386098e2065ab ] Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") merged a version of the UAF fix that breaks releasing connected ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't be called when the hci_conn disconnects, and the ISO socket does not emit POLLHUP correctly. Fix by retaining full hci_conn <-> iso_conn association while in BT_DISCONNECT state, so that local disconnect via shutdown() follows similar ISO socket code path as remote disconnect. Use a separate flag to track whether hci_conn_drop() is needed, instead of setting iso_conn::hcon = NULL In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED, in case hcon connects while its drop is pending. Fixes: d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready") Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 0d2d9c07bf66b..fb77a7e22a9dd 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -23,8 +23,14 @@ static struct bt_sock_list iso_sk_list = { }; /* ---- ISO connections ---- */ +enum { + ISO_CONN_DROPPED, + __ISO_CONN_NUM_FLAGS +}; + struct iso_conn { struct hci_conn *hcon; + DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS); /* @lock: spinlock protecting changes to iso_conn fields */ spinlock_t lock; @@ -106,7 +112,8 @@ static void iso_conn_free(struct kref *ref) if (conn->hcon) { conn->hcon->iso_data = NULL; - hci_conn_drop(conn->hcon); + if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) + hci_conn_drop(conn->hcon); } /* Ensure no more work items will run since hci_conn has been dropped */ @@ -305,6 +312,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, iso_pi(sk)->conn = conn; conn->sk = sk; + clear_bit(ISO_CONN_DROPPED, conn->flags); if (parent) bt_accept_enqueue(parent, sk, true); @@ -817,11 +825,8 @@ static void iso_sock_disconn(struct sock *sk) } sk->sk_state = BT_DISCONN; - iso_conn_lock(iso_pi(sk)->conn); - hci_conn_drop(iso_pi(sk)->conn->hcon); - iso_pi(sk)->conn->hcon->iso_data = NULL; - iso_pi(sk)->conn->hcon = NULL; - iso_conn_unlock(iso_pi(sk)->conn); + if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags)) + hci_conn_drop(iso_pi(sk)->conn->hcon); } static void __iso_sock_close(struct sock *sk) @@ -1964,9 +1969,18 @@ static void iso_sock_ready(struct sock *sk) return; lock_sock(sk); + + switch (sk->sk_state) { + case BT_DISCONN: + case BT_CLOSED: + release_sock(sk); + return; + } + iso_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); + release_sock(sk); } From 4e9b5e8669b3602a4e01b6d1e9539b72e42c84d5 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:29 +0300 Subject: [PATCH 3017/3297] Bluetooth: ISO: hold sk properly in iso_conn_ready [ Upstream commit 0d255e63fcf3f13a570d7ac11678fa1164ac015c ] sk deref in iso_conn_ready must be done either under conn->lock, or holding a refcount, to avoid concurrent close. conn->sk is currently accessed without either: [Task 1] [Task 2] iso_sock_release iso_conn_ready sk = conn->sk lock_sock(sk) conn->sk = NULL lock_sock(sk) release_sock(sk) iso_sock_kill(sk) UAF on sk deref Fix possible UAF by holding sk refcount in iso_conn_ready(). Also recheck after lock_sock that the socket is still valid. Adjust locking so conn->sk is cleared only under lock_sock. Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index fb77a7e22a9dd..cb629734062a5 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -787,11 +787,13 @@ static void iso_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->sk_state); /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ + lock_sock(sk); if (iso_pi(sk)->conn) { iso_conn_lock(iso_pi(sk)->conn); iso_pi(sk)->conn->sk = NULL; iso_conn_unlock(iso_pi(sk)->conn); } + release_sock(sk); /* Kill poor orphan */ bt_sock_unlink(&iso_sk_list, sk); @@ -1965,23 +1967,17 @@ static void iso_sock_ready(struct sock *sk) { BT_DBG("sk %p", sk); - if (!sk) - return; - - lock_sock(sk); + lockdep_assert(lockdep_sock_is_held(sk)); switch (sk->sk_state) { case BT_DISCONN: case BT_CLOSED: - release_sock(sk); return; } iso_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); - - release_sock(sk); } static bool iso_match_big(struct sock *sk, void *data) @@ -2006,7 +2002,7 @@ static bool iso_match_pa_sync_flag(struct sock *sk, void *data) static void iso_conn_ready(struct iso_conn *conn) { struct sock *parent = NULL; - struct sock *sk = conn->sk; + struct sock *sk; struct hci_ev_le_big_sync_established *ev = NULL; struct hci_ev_le_pa_sync_established *ev2 = NULL; struct hci_ev_le_per_adv_report *ev3 = NULL; @@ -2014,7 +2010,22 @@ static void iso_conn_ready(struct iso_conn *conn) BT_DBG("conn %p", conn); + iso_conn_lock(conn); + sk = iso_sock_hold(conn); + iso_conn_unlock(conn); + if (sk) { + lock_sock(sk); + + /* conn->sk may have become NULL if racing with sk close, but + * due to held hdev->lock, it can't become different sk. + */ + if (!conn->sk) { + release_sock(sk); + sock_put(sk); + return; + } + /* Attempt to update source address in case of BIS Sender if * the advertisement is using a random address. */ @@ -2027,14 +2038,15 @@ static void iso_conn_ready(struct iso_conn *conn) adv = hci_find_adv_instance(bis->hdev, bis->iso_qos.bcast.bis); if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) { - lock_sock(sk); iso_pi(sk)->src_type = BDADDR_LE_RANDOM; bacpy(&iso_pi(sk)->src, &adv->random_addr); - release_sock(sk); } } - iso_sock_ready(conn->sk); + iso_sock_ready(sk); + + release_sock(sk); + sock_put(sk); } else { hcon = conn->hcon; if (!hcon) From e76a0ae6542ae43241b2147bacf4990e7ae5316a Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:30 +0300 Subject: [PATCH 3018/3297] Bluetooth: ISO: fix leaking sk after socket release [ Upstream commit ce57442a379212fe3fda59c9437ee8217eceb5b1 ] iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || sock_flag(sk, SOCK_DEAD) for early return, but this is always true since sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket always leaks, iso_sock_destruct is never called. The socket reference also leaks when __iso_sock_close() does not set SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after zapping. Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for something else, and lock_sock to ensure iso_sock_kill() puts sk only after socket release only once. Release and iso_conn_del may run concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up after zapping. Remove call to iso_sock_kill() from iso_sock_close(), as it's generally no-op there. Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index cb629734062a5..001fb12ccee3b 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -61,6 +61,7 @@ static void iso_sock_kill(struct sock *sk); enum { BT_SK_BIG_SYNC, BT_SK_PA_SYNC, + BT_SK_KILLED, }; struct iso_pinfo { @@ -294,6 +295,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err) iso_sock_clear_timer(sk); iso_chan_del(sk, err); release_sock(sk); + iso_sock_kill(sk); sock_put(sk); } @@ -780,24 +782,29 @@ static void iso_sock_cleanup_listen(struct sock *parent) */ static void iso_sock_kill(struct sock *sk) { + lock_sock(sk); + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || - sock_flag(sk, SOCK_DEAD)) + test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) { + release_sock(sk); return; + } BT_DBG("sk %p state %d", sk, sk->sk_state); /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ - lock_sock(sk); if (iso_pi(sk)->conn) { iso_conn_lock(iso_pi(sk)->conn); iso_pi(sk)->conn->sk = NULL; iso_conn_unlock(iso_pi(sk)->conn); } - release_sock(sk); /* Kill poor orphan */ bt_sock_unlink(&iso_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); + set_bit(BT_SK_KILLED, &iso_pi(sk)->flags); + + release_sock(sk); sock_put(sk); } @@ -874,7 +881,6 @@ static void iso_sock_close(struct sock *sk) iso_sock_clear_timer(sk); __iso_sock_close(sk); release_sock(sk); - iso_sock_kill(sk); } static void iso_sock_init(struct sock *sk, struct sock *parent) @@ -1958,8 +1964,16 @@ static int iso_sock_release(struct socket *sock) release_sock(sk); } + /* Make sure sk is valid even if iso_conn_del() is concurrent */ + sock_hold(sk); + + lock_sock(sk); sock_orphan(sk); + release_sock(sk); + iso_sock_kill(sk); + + sock_put(sk); return err; } From 82e982f54f962f72646868ddbb2c3bd9ea178568 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:31 +0300 Subject: [PATCH 3019/3297] Bluetooth: ISO: avoid deadlocks in iso_sock_timeout [ Upstream commit 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 ] iso_sock_timeout() takes lock_sock, so sync disabling the timer while holding that lock may deadlock. iso_sock_timeout() may also run concurrently with iso_conn_del(), which leads to UAF [Task 1] [Task hdev->workqueue] iso_sock_timeout iso_conn_del iso_conn_hold_unless_zero iso_chan_del `------------> iso_conn_put caller frees hcon iso_conn_put iso_conn_free conn->hcon->iso_data = NULL; /* UAF */ Fix the deadlock by removing the disable from the lock_sock sections. Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn which may need to be freed in lock_sock section. Convert some of the clear_timer to disable_timer. Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 60 ++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 001fb12ccee3b..3320be2d66b55 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -36,8 +36,6 @@ struct iso_conn { spinlock_t lock; struct sock *sk; - struct delayed_work timeout_work; - struct sk_buff *rx_skb; __u32 rx_len; __u16 tx_sn; @@ -80,6 +78,7 @@ struct iso_pinfo { __u8 base_len; __u8 base[BASE_MAX_LENGTH]; struct iso_conn *conn; + struct delayed_work timeout_work; }; static struct bt_iso_qos default_qos; @@ -117,9 +116,6 @@ static void iso_conn_free(struct kref *ref) hci_conn_drop(conn->hcon); } - /* Ensure no more work items will run since hci_conn has been dropped */ - disable_delayed_work_sync(&conn->timeout_work); - kfree_skb(conn->rx_skb); kfree(conn); @@ -160,48 +156,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn) static void iso_sock_timeout(struct work_struct *work) { - struct iso_conn *conn = container_of(work, struct iso_conn, - timeout_work.work); - struct sock *sk; - - conn = iso_conn_hold_unless_zero(conn); - if (!conn) - return; - - iso_conn_lock(conn); - sk = iso_sock_hold(conn); - iso_conn_unlock(conn); - iso_conn_put(conn); - - if (!sk) - return; + struct iso_pinfo *pi = container_of(work, struct iso_pinfo, + timeout_work.work); + struct sock *sk = &pi->bt.sk; BT_DBG("sock %p state %d", sk, sk->sk_state); lock_sock(sk); - sk->sk_err = ETIMEDOUT; - sk->sk_state_change(sk); + if (!sock_flag(sk, SOCK_ZAPPED)) { + sk->sk_err = ETIMEDOUT; + sk->sk_state_change(sk); + } release_sock(sk); - sock_put(sk); } static void iso_sock_set_timer(struct sock *sk, long timeout) { + lockdep_assert(lockdep_sock_is_held(sk)); + + cancel_delayed_work(&iso_pi(sk)->timeout_work); + if (!iso_pi(sk)->conn) return; BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); - cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); - schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout); + schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout); } static void iso_sock_clear_timer(struct sock *sk) { - if (!iso_pi(sk)->conn) - return; + BT_DBG("sock %p state %d", sk, sk->sk_state); + cancel_delayed_work(&iso_pi(sk)->timeout_work); +} + +static void iso_sock_disable_timer(struct sock *sk) +{ + lockdep_assert(!lockdep_sock_is_held(sk)); BT_DBG("sock %p state %d", sk, sk->sk_state); - cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); + disable_delayed_work_sync(&iso_pi(sk)->timeout_work); } /* ---- ISO connections ---- */ @@ -226,7 +219,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) kref_init(&conn->ref); spin_lock_init(&conn->lock); - INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout); hcon->iso_data = conn; conn->hcon = hcon; @@ -291,8 +283,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err) return; } + iso_sock_disable_timer(sk); + lock_sock(sk); - iso_sock_clear_timer(sk); iso_chan_del(sk, err); release_sock(sk); iso_sock_kill(sk); @@ -782,6 +775,8 @@ static void iso_sock_cleanup_listen(struct sock *parent) */ static void iso_sock_kill(struct sock *sk) { + iso_sock_disable_timer(sk); + lock_sock(sk); if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || @@ -877,8 +872,9 @@ static void __iso_sock_close(struct sock *sk) /* Must be called on unlocked socket. */ static void iso_sock_close(struct sock *sk) { + iso_sock_disable_timer(sk); + lock_sock(sk); - iso_sock_clear_timer(sk); __iso_sock_close(sk); release_sock(sk); } @@ -947,6 +943,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock, iso_pi(sk)->qos = default_qos; iso_pi(sk)->sync_handle = -1; + INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout); + bt_sock_link(&iso_sk_list, sk); return sk; } From e941799c31f68e67ce0976efb38a79101f921b64 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:32 +0300 Subject: [PATCH 3020/3297] Bluetooth: ISO: ensure no dangling hcon references in iso_conn [ Upstream commit aa9f7cb2bd3a2be998ceb739fc9a2f986eba43eb ] After iso_conn_del(), ISO sockets should not dereference the hcon any more. Currently, clearing iso_conn::hcon relies on iso_conn_del() releasing the last reference to the iso_conn. Simplify this by explicitly clearing conn->hcon in iso_conn_del(), to avoid more complex reasoning on races about who holds the last reference. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: fdfde532ab1c ("Bluetooth: ISO: fix refcounting of iso_conn") Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 3320be2d66b55..b21627b8e958b 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -262,6 +262,7 @@ static void iso_chan_del(struct sock *sk, int err) } static void iso_conn_del(struct hci_conn *hcon, int err) + __must_hold(&hcon->hdev->lock) { struct iso_conn *conn = hcon->iso_data; struct sock *sk; @@ -276,11 +277,10 @@ static void iso_conn_del(struct hci_conn *hcon, int err) iso_conn_lock(conn); sk = iso_sock_hold(conn); iso_conn_unlock(conn); - iso_conn_put(conn); if (!sk) { iso_conn_put(conn); - return; + goto done; } iso_sock_disable_timer(sk); @@ -290,6 +290,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err) release_sock(sk); iso_sock_kill(sk); sock_put(sk); + +done: + /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */ + iso_conn_lock(conn); + conn->hcon = NULL; + hcon->iso_data = NULL; + iso_conn_unlock(conn); + + iso_conn_put(conn); } static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, @@ -305,6 +314,11 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, return -EBUSY; } + if (!conn->hcon) { + BT_ERR("conn->hcon missing"); + return -EIO; + } + iso_pi(sk)->conn = conn; conn->sk = sk; clear_bit(ISO_CONN_DROPPED, conn->flags); @@ -2410,6 +2424,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) } static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + __must_hold(&hcon->hdev->lock) { if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && hcon->type != PA_LINK) { @@ -2421,8 +2436,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) struct hci_link *link, *t; list_for_each_entry_safe(link, t, &hcon->link_list, - list) + list) { + lockdep_assert_held(&link->conn->hdev->lock); iso_conn_del(link->conn, bt_to_errno(status)); + } return; } @@ -2452,6 +2469,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) } static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason) + __must_hold(&hcon->hdev->lock) { if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && hcon->type != PA_LINK) From 3b921533e8aa95b77aadcf31737595578e735f3c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:33 +0300 Subject: [PATCH 3021/3297] Bluetooth: ISO: fix refcounting of iso_conn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit fdfde532ab1caa165fcd8985001157ac8b4db365 ] iso_conn_del() and iso_chan_del() have a race that results to double-put of iso_conn: [Task hdev->workqueue] [Task 2] iso_conn_del iso_chan_del iso_conn_hold_unless_zero iso_conn_lock iso_conn_lock conn->sk = NULL iso_conn_unlock sk = iso_sock_hold(conn) <---------´ if (!sk) iso_conn_put iso_conn_put iso_conn_put /* UAF */ The extra put for !sk in iso_conn_del() is currently required since failing iso_chan_add() may leave iso_conn not associated with any sk. Fix by having iso_pi(sk)->conn own refcount when non-NULL, so iso_conn_del does not need to put it. Adjust the iso_conn_add() refcounting so that conn is put if it does not get associated with an sk. Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/iso.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index b21627b8e958b..fcf4fd78c7cc9 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -107,9 +107,6 @@ static void iso_conn_free(struct kref *ref) BT_DBG("conn %p", conn); - if (conn->sk) - iso_pi(conn->sk)->conn = NULL; - if (conn->hcon) { conn->hcon->iso_data = NULL; if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) @@ -144,6 +141,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn) return conn; } +static struct iso_conn *iso_conn_hold(struct iso_conn *conn) +{ + BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref)); + + kref_get(&conn->ref); + return conn; +} + static struct sock *iso_sock_hold(struct iso_conn *conn) { if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk)) @@ -209,7 +214,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) conn->hcon = hcon; iso_conn_unlock(conn); } - iso_conn_put(conn); return conn; } @@ -278,10 +282,8 @@ static void iso_conn_del(struct hci_conn *hcon, int err) sk = iso_sock_hold(conn); iso_conn_unlock(conn); - if (!sk) { - iso_conn_put(conn); + if (!sk) goto done; - } iso_sock_disable_timer(sk); @@ -319,7 +321,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, return -EIO; } - iso_pi(sk)->conn = conn; + iso_pi(sk)->conn = iso_conn_hold(conn); conn->sk = sk; clear_bit(ISO_CONN_DROPPED, conn->flags); @@ -426,6 +428,7 @@ static int iso_connect_bis(struct sock *sk) } err = iso_chan_add(conn, sk, NULL); + iso_conn_put(conn); if (err) goto unlock; @@ -528,6 +531,7 @@ static int iso_connect_cis(struct sock *sk) } err = iso_chan_add(conn, sk, NULL); + iso_conn_put(conn); if (err) goto unlock; @@ -1228,10 +1232,9 @@ static int iso_listen_bis(struct sock *sk) } err = iso_chan_add(conn, sk, NULL); - if (err) { - hci_conn_drop(hcon); + iso_conn_put(conn); + if (err) goto unlock; - } unlock: release_sock(sk); @@ -2461,8 +2464,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) struct iso_conn *conn; conn = iso_conn_add(hcon); - if (conn) + if (conn) { iso_conn_ready(conn); + iso_conn_put(conn); + } } else { iso_conn_del(hcon, bt_to_errno(status)); } From c618a9a5b08ea2e17bddf4e948be9f75d408fca4 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:40 -0700 Subject: [PATCH 3022/3297] Bluetooth: btintel: Validate length before parsing diagnostics TLV [ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ] btintel_diagnostics() accesses tlv->val[0] without first validating that the diagnostics VSE is long enough to contain that field, so may cause reading data beyond the received frame. Fix by validating the length before access. Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btintel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 5e0a05edcbfd1..4d6b6bbb4267e 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -3694,6 +3694,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) { struct intel_tlv *tlv = (void *)&skb->data[5]; + if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0])) + goto recv_frame; + /* The first event is always an event type TLV */ if (tlv->type != INTEL_TLV_TYPE_ID) goto recv_frame; From de17305393ec82bc025fe63a71344c1e64708f38 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 25 Mar 2026 21:07:45 +0200 Subject: [PATCH 3023/3297] Bluetooth: hci_sync: make hci_cmd_sync_run_once return -EEXIST if exists [ Upstream commit d288f4db0909c22342eb50cd1632b4d850517281 ] hci_cmd_sync_run_once() needs to indicate whether a queue item was added, so caller can know if callbacks are called, so it can avoid leaking resources. Change the function to return -EEXIST if queue item already exists. Modify all callsites vs. the changes. The only callsite is hci_abort_conn(). Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Stable-dep-of: 5761d003daa9 ("Bluetooth: hci_conn: hold conn reference in abort_conn_sync()") Signed-off-by: Sasha Levin --- net/bluetooth/hci_conn.c | 4 +++- net/bluetooth/hci_sync.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ceeb19158a36c..f485cffe35735 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2968,6 +2968,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) int hci_abort_conn(struct hci_conn *conn, u8 reason) { struct hci_dev *hdev = conn->hdev; + int err; /* If abort_reason has already been set it means the connection is * already being aborted so don't attempt to overwrite it. @@ -2989,7 +2990,8 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does * already queue its callback on cmd_sync_work. */ - return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); + err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); + return (err == -EEXIST) ? 0 : err; } void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset, diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 87a2502fccd6e..ca002269306c4 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -825,7 +825,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, void *data, hci_cmd_sync_work_destroy_t destroy) { if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy)) - return 0; + return -EEXIST; return hci_cmd_sync_run(hdev, func, data, destroy); } From e8f9fef362bab431d95371d3406bc720350290c3 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:17 +0300 Subject: [PATCH 3024/3297] Bluetooth: hci_conn: hold conn reference in abort_conn_sync() [ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ] There is theoretical UAF if the conn is freed while the hci_sync task is running. Hold refcount to avoid that. Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_conn.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index f485cffe35735..e630d6369a0c3 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2965,6 +2965,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) return hci_abort_conn_sync(hdev, conn, conn->abort_reason); } +static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err) +{ + struct hci_conn *conn = data; + + hci_conn_put(conn); +} + int hci_abort_conn(struct hci_conn *conn, u8 reason) { struct hci_dev *hdev = conn->hdev; @@ -2990,7 +2997,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does * already queue its callback on cmd_sync_work. */ - err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); + err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn), + abort_conn_destroy); + if (err) + hci_conn_put(conn); return (err == -EEXIST) ? 0 : err; } From 5576afebf726c53d4bfe02d261ae8cc900be4f8b Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:22 +0300 Subject: [PATCH 3025/3297] Bluetooth: hci_sync: fix hci_conn_del() use in hci_le_create_conn_sync [ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ] hci_conn_del() caller must hold hdev->lock, check the conn was not concurrently deleted, and usually inform socket the conn is going to be deleted. Use hci_abort_conn_sync() instead of calling hci_conn_del() without locks etc. Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- 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 ca002269306c4..27c5cc1457e65 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6620,7 +6620,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && hdev->le_scan_type == LE_SCAN_ACTIVE && !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { - hci_conn_del(conn); + conn->state = BT_OPEN; + hci_abort_conn_sync(hdev, conn, + HCI_ERROR_REJ_LIMITED_RESOURCES); hci_conn_put(conn); return -EBUSY; } From 5ea70ad040c1b397da5900255e211c88817dfb4d Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Thu, 23 Jul 2026 20:08:04 -0300 Subject: [PATCH 3026/3297] x86/boot: Add volatile, clobbers and zero-length test in memcmp() [ Upstream commit a8c171c107c0b61a5e7e10cedab0fb72aeaf640d ] Add the volatile qualifier and clobbers parameter to prevent bugs with instruction reordering and optimization. Also add TEST for the zero-length case to set ZF, as, if the count register is zero, the REPE prefix does not run the CMPSB instruction, leaving the ZF flag undetermined. [ bp: Add a comment about the len==0 case. ] Fixes: 62bd0337d0c4 ("Top header file for new x86 setup code") Closes: https://sashiko.dev/#/patchset/20260701-pvh-kasan-inline-v6-0-ba99045dfa9f%40igalia.com Suggested-by: Borislav Petkov Signed-off-by: Mauricio Faria de Oliveira Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/all/20260721-pvh-kasan-inline-v7-2-38979a50cef0@igalia.com Signed-off-by: Sasha Levin --- arch/x86/boot/string.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c index b25c6a9303b73..3a2bba7c25e9d 100644 --- a/arch/x86/boot/string.c +++ b/arch/x86/boot/string.c @@ -32,8 +32,15 @@ int memcmp(const void *s1, const void *s2, size_t len) { bool diff; - asm("repe cmpsb" - : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)); + + /* + * Make sure ZF is properly set in the len==0 case because in it, + * RCX==0 and the REPE; CMPSB won't get executed. + */ + asm volatile("test %3, %3\n\t" + "repe cmpsb" + : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len) + : : "cc", "memory"); return diff; } From 613aaeeaf8cb129c7178bc8c73f2302137a05e25 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Sun, 26 Jul 2026 17:08:05 +0200 Subject: [PATCH 3027/3297] net: phylink: put link_gpio if phylink_create fails [ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] In phylink_create() if phylink_register_sfp() returns an error, link_gpio obtained by phylink_parse_fixedlink() is never released. While this is a very unlikely scenario, it's worth to fix/handle this. This was present from the very first implementation of phylink but got relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") where additional function were added after phylink_parse_fixedlink() making the release of link_gpio needed if such additional function errored out. While at it, restructure the exit condition of phylink_create() with the goto pattern to reduce code duplication on handling error conditions. Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") Signed-off-by: Christian Marangi Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 9182443082158..1b7fc17bf3919 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1861,8 +1861,8 @@ struct phylink *phylink_create(struct phylink_config *config, } else if (config->type == PHYLINK_DEV) { pl->dev = config->dev; } else { - kfree(pl); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto free_pl; } pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops); @@ -1895,28 +1895,29 @@ struct phylink *phylink_create(struct phylink_config *config, phylink_validate(pl, pl->supported, &pl->link_config); ret = phylink_parse_mode(pl, fwnode); - if (ret < 0) { - kfree(pl); - return ERR_PTR(ret); - } + if (ret < 0) + goto free_pl; if (pl->cfg_link_an_mode == MLO_AN_FIXED) { ret = phylink_parse_fixedlink(pl, fwnode); - if (ret < 0) { - kfree(pl); - return ERR_PTR(ret); - } + if (ret < 0) + goto release_link_gpio; } pl->req_link_an_mode = pl->cfg_link_an_mode; ret = phylink_register_sfp(pl, fwnode); - if (ret < 0) { - kfree(pl); - return ERR_PTR(ret); - } + if (ret < 0) + goto release_link_gpio; return pl; + +release_link_gpio: + if (pl->link_gpio) + gpiod_put(pl->link_gpio); +free_pl: + kfree(pl); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(phylink_create); From bdd8a1297ef101bb5ac57e53b4fbe5e364afd8b1 Mon Sep 17 00:00:00 2001 From: TanZheng Date: Fri, 24 Jul 2026 15:58:50 +0800 Subject: [PATCH 3028/3297] scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE [ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ] In the iblock_execute_pr_out() function, PRO_PREEMPT, PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability checks through ops->pr_clear. The error check allows unimplemented hooks to pass through the gate, resulting dereferencing a NULL function pointer. Check whether the hooks that need to be called are supported. Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock") Signed-off-by: TanZheng Reviewed-by: Mike Christie Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/target_core_iblock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 66c292b7d74bc..4ec322f371bc2 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -902,7 +902,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, break; case PRO_PREEMPT: case PRO_PREEMPT_AND_ABORT: - if (!ops->pr_clear) { + if (!ops->pr_preempt) { pr_err("block_device does not support pr_preempt.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } @@ -912,8 +912,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, sa == PRO_PREEMPT_AND_ABORT); break; case PRO_RELEASE: - if (!ops->pr_clear) { - pr_err("block_device does not support pr_pclear.\n"); + if (!ops->pr_release) { + pr_err("block_device does not support pr_release.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } From e504204489993d334675685d2d4c2f70da68d7dd Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Jul 2026 01:27:26 +0800 Subject: [PATCH 3029/3297] scsi: ufs: core: Cancel RTC work in active-active suspend [ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ] UFS RTC support schedules ufs_rtc_update_work to periodically update the device RTC. The work can issue query commands and access the UFS host controller. A previous change moved the RTC work cancellation before the PRE_CHANGE vendor suspend callback to close a race in the common suspend path. However, the active-active path jumps directly to vops_suspend after flushing exception handling work and therefore bypasses the cancellation. If the RTC work runs while the vendor suspend callback is gating or otherwise changing hardware state, it can access the controller during suspend and trigger an SError. Cancel the RTC work before entering the vendor suspend callback in the active-active path. Since this path now cancels the work, move the RTC work scheduling outside the device and link state restoration block in the resume path. This restarts RTC updates after an active-active suspend and resume cycle. Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend") Signed-off-by: Guangshuo Li Reviewed-by: Peter Wang Reviewed-by: Bean Huo Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/ufs/core/ufshcd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 8de3d0886a121..2545f7a51211b 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -9873,6 +9873,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) req_link_state == UIC_LINK_ACTIVE_STATE) { ufshcd_disable_auto_bkops(hba); flush_work(&hba->eeh_work); + cancel_delayed_work_sync(&hba->ufs_rtc_update_work); goto vops_suspend; } @@ -10082,10 +10083,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (ret) goto set_old_link_state; ufshcd_set_timestamp_attr(hba); - schedule_delayed_work(&hba->ufs_rtc_update_work, - msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); } + schedule_delayed_work(&hba->ufs_rtc_update_work, + msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) ufshcd_enable_auto_bkops(hba); else From c249cfe1d8df2e9c1de5c25b75245ea0c683e65a Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Fri, 6 Mar 2026 13:43:02 +0800 Subject: [PATCH 3030/3297] scsi: ufs: core: Avoid IRQ thread wakeup during active UIC command [ Upstream commit 6475cfb81fc4f6175b6d15d1c205a5168dc10b46 ] Only return IRQ_WAKE_THREAD when MCQ and ESI are not enabled and no UIC command is active. The default UIC command timeout is 500ms, Using threaded IRQs during an active UIC command increases the risk of timeout due to possible preemption by other system IRQs. Signed-off-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260306054419.3816557-1-peter.wang@mediatek.com Signed-off-by: Martin K. Petersen Stable-dep-of: 8a309036f557 ("scsi: ufs: core: Revert "Delegate the interrupt service routine to a threaded IRQ handler"") Signed-off-by: Sasha Levin --- drivers/ufs/core/ufshcd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 2545f7a51211b..482a9299a5894 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -7190,8 +7190,12 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) struct ufs_hba *hba = __hba; u32 intr_status, enabled_intr_status; - /* Move interrupt handling to thread when MCQ & ESI are not enabled */ - if (!hba->mcq_enabled || !hba->mcq_esi_enabled) + /* + * Handle interrupt in thread if MCQ or ESI is disabled, + * and no active UIC command. + */ + if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && + !hba->active_uic_cmd) return IRQ_WAKE_THREAD; intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); From ff333def314762b94e0933be7e84947d876f20bf Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 15 Jul 2026 10:12:28 -0700 Subject: [PATCH 3031/3297] scsi: ufs: core: Revert "Delegate the interrupt service routine to a threaded IRQ handler" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8a309036f557d3ff4efb2beea5132ba91172d934 ] There have been multiple reports of performance regressions caused by commit 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler"). Hence this revert. This patch reverts most of the following commits: * 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") * 6475cfb81fc4 ("scsi: ufs: core: Avoid IRQ thread wakeup during active UIC command") This patch preserves the following commits: * 034d319c8899 ("scsi: ufs: core: Fix interrupt handling for MCQ Mode") * eabcac808ca3 ("scsi: ufs: core: Fix IRQ lock inversion for the SCSI host lock") Cc: Neil Armstrong Cc: 孙魁 (Kui Sun) Cc: André Draszik Cc: Gregory CLEMENT Cc: Sebastian Andrzej Siewior Fixes: 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") Signed-off-by: Bart Van Assche Reviewed-by: Sebastian Andrzej Siewior Tested-by: André Draszik # on Pixel 6 Reviewed-by: André Draszik Link: https://patch.msgid.link/b70eb60a01f971bed68c42c5b555929db5f835df.1784135511.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/ufs/core/ufshcd.c | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 482a9299a5894..504600f1e08cd 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -7128,7 +7128,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) } /** - * ufshcd_threaded_intr - Threaded interrupt service routine + * ufshcd_intr - Main interrupt service routine * @irq: irq number * @__hba: pointer to adapter instance * @@ -7136,7 +7136,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ -static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) +static irqreturn_t ufshcd_intr(int irq, void *__hba) { u32 last_intr_status, intr_status, enabled_intr_status = 0; irqreturn_t retval = IRQ_NONE; @@ -7175,38 +7175,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) return retval; } -/** - * ufshcd_intr - Main interrupt service routine - * @irq: irq number - * @__hba: pointer to adapter instance - * - * Return: - * IRQ_HANDLED - If interrupt is valid - * IRQ_WAKE_THREAD - If handling is moved to threaded handled - * IRQ_NONE - If invalid interrupt - */ -static irqreturn_t ufshcd_intr(int irq, void *__hba) -{ - struct ufs_hba *hba = __hba; - u32 intr_status, enabled_intr_status; - - /* - * Handle interrupt in thread if MCQ or ESI is disabled, - * and no active UIC command. - */ - if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && - !hba->active_uic_cmd) - return IRQ_WAKE_THREAD; - - intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); - - /* Directly handle interrupts since MCQ ESI handlers does the hard job */ - return ufshcd_sl_intr(hba, enabled_intr_status); -} - static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) { int err = 0; @@ -10835,8 +10803,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ufshcd_readl(hba, REG_INTERRUPT_ENABLE); /* IRQ registration */ - err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr, - IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba); + err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); if (err) { dev_err(hba->dev, "request irq failed\n"); goto out_disable; From 54c6fb24c60218daca73332b5d489c9f97c4d188 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Mon, 20 Jul 2026 09:27:36 +0200 Subject: [PATCH 3032/3297] scsi: zfcp: Fix memory leak during adapter release by destroying gid_pn_req [ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] When releasing an adapter we don't free the mempool 'gid_pn_req' that is allocated during the enqueue. This leaks memory: unreferenced object 0xd8d29297de700 (size 256): comm "(udev-worker)", pid 2105, jiffies 4294945794 hex dump (first 32 bytes): 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... backtrace (crc 4a5b5da2): [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 [<000dc45f643d8238>] online_store+0x298/0x5b0 [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 [<000dc45f64dc2224>] system_call+0x74/0xa0 Fix this by destroying the mempool during the adapter's release. Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") Signed-off-by: Benjamin Block Tested-by: M Nikhil Acked-by: M Nikhil Reviewed-by: Chinmaya Kajagar Reviewed-by: Nihar Panda Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/s390/scsi/zfcp_aux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index dc2265ebb11b8..03f723042434b 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { mempool_destroy(adapter->pool.erp_req); + mempool_destroy(adapter->pool.gid_pn_req); mempool_destroy(adapter->pool.scsi_req); mempool_destroy(adapter->pool.scsi_abort); mempool_destroy(adapter->pool.qtcb_pool); From 69a258a5a322ed7991ac433676fdfc1e294a16f6 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 22 Jul 2026 09:30:10 +0300 Subject: [PATCH 3033/3297] scsi: target: Clear cmd_cnt when initial counter enrollment fails [ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ] When target_get_sess_cmd() fails during session shutdown because percpu_ref_tryget_live() returns false, the command keeps the se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier without owning a reference. Final release through target_release_cmd_kref() then issues an unmatched percpu_ref_put(). Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") moved the cmd_cnt assignment ahead of the reference acquisition. Clear se_cmd->cmd_cnt whenever the initial target_get_sess_cmd() fails in target_init_cmd() and target_submit_tmr(), so release performs exactly one matching put per acquired reference. Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") Signed-off-by: Leon Romanovsky Reviewed-by: Mike Christie Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/target_core_transport.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 88544c911949c..5ad2dcf730cd2 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1691,6 +1691,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, u32 data_length, int task_attr, int data_dir, int flags) { struct se_portal_group *se_tpg; + int ret; se_tpg = se_sess->se_tpg; BUG_ON(!se_tpg); @@ -1720,7 +1721,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second * kref_put() to happen during fabric packet acknowledgement. */ - return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) + se_cmd->cmd_cnt = NULL; + + return ret; } EXPORT_SYMBOL_GPL(target_init_cmd); @@ -1996,8 +2001,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, * allocation failure. */ ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); - if (ret < 0) + if (ret < 0) { + se_cmd->cmd_cnt = NULL; return -ENOMEM; + } if (tm_type == TMR_ABORT_TASK) se_cmd->se_tmr_req->ref_task_tag = tag; @@ -2005,6 +2012,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, /* See target_submit_cmd for commentary */ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); if (ret) { + se_cmd->cmd_cnt = NULL; core_tmr_release_req(se_cmd->se_tmr_req); return ret; } From 42b87cfd9666ef156637c90ff3f6f6dd9a5ad4cb Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 10:18:19 +0800 Subject: [PATCH 3034/3297] net: sxgbe: free TX rings on RX allocation failure [ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] When RX descriptor ring allocation fails, init_dma_desc_rings() only frees the partially allocated RX rings and returns. The TX rings that were allocated earlier in the same function are leaked. Rearrange error labels to clean up TX rings upon RX failures. Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") Signed-off-by: Chenguang Zhao Reviewed-by: Vadim Fedorenko Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 849c5a6c2af1e..5eb8e7c232f8b 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd) return 0; -txalloc_err: - while (queue_num--) - free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); - return ret; - rxalloc_err: while (queue_num--) free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); + queue_num = SXGBE_TX_QUEUES; +txalloc_err: + while (queue_num--) + free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); return ret; } From b4f8c33593f2586cd074fc2b72054e43774e37f0 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 10:18:20 +0800 Subject: [PATCH 3035/3297] net: sxgbe: check descriptor ring allocation failures [ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] sxgbe_open() ignores the return value of init_dma_desc_rings() and continues to program DMA with invalid ring addresses when allocation fails. Check the return value and disconnect the PHY on failure. Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") Signed-off-by: Chenguang Zhao Reviewed-by: Vadim Fedorenko Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 5eb8e7c232f8b..848a0578cea1f 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev) priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); priv->tx_tc = TC_DEFAULT; priv->rx_tc = TC_DEFAULT; - init_dma_desc_rings(dev); + ret = init_dma_desc_rings(dev); + if (ret) + goto init_phy_error; /* DMA initialization and SW reset */ ret = sxgbe_init_dma_engine(priv); @@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev) init_error: free_dma_desc_resources(priv); +init_phy_error: if (dev->phydev) phy_disconnect(dev->phydev); phy_error: From 50edffd0854fe49d3c65a81a728dfefab5e02694 Mon Sep 17 00:00:00 2001 From: Minhong He Date: Wed, 29 Jul 2026 16:56:56 +0800 Subject: [PATCH 3036/3297] can: isotp: check register_netdevice_notifier() error in module init [ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] Register the netdevice notifier before can_proto_register() and check the return value. If protocol registration fails, unregister the notifier before returning the error. Align isotp_module_init() with the reordering already done for raw.c (commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization in bcm_module_init()")). Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") Signed-off-by: Minhong He Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn Signed-off-by: Marc Kleine-Budde Signed-off-by: Sasha Levin --- net/can/isotp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index c21d816a52747..95e14631a1eb9 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1888,13 +1888,18 @@ static __init int isotp_module_init(void) pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size); + err = register_netdevice_notifier(&canisotp_notifier); + if (err) + return err; + err = can_proto_register(&isotp_can_proto); - if (err < 0) + if (err < 0) { pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); - else - register_netdevice_notifier(&canisotp_notifier); + unregister_netdevice_notifier(&canisotp_notifier); + return err; + } - return err; + return 0; } static __exit void isotp_module_exit(void) From 84b5aa55de7c868ddca36ff0dc3f14dc1d9e1181 Mon Sep 17 00:00:00 2001 From: Alexander Kaplan Date: Sat, 18 Jul 2026 12:52:07 +0200 Subject: [PATCH 3037/3297] drm/i915/dp: Ignore the sink's DSC max FRL rate without a PCON DSC encoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8891e39e89042e285fd82fdde325d1311ec750a1 ] intel_dp_hdmi_sink_max_frl() limits the sink's max FRL rate by its DSC max FRL rate whenever the sink supports DSC 1.2. However, the DSC max FRL rate (HF-VSDB DSC_Max_FRL_Rate) only applies to compressed video transport, which requires a DSC 1.2 encoder in the PCON (configured via intel_dp_pcon_dsc_configure()). Without such an encoder the HDMI link always carries uncompressed video, for which the regular Max_FRL_Rate is the correct limit. Applying the DSC limit unconditionally trains the FRL link at a lower rate than both the PCON and the sink support. E.g. an LG OLED G4 (Max_FRL_Rate 48 Gbps, DSC_Max_FRL_Rate 24 Gbps) behind a Synaptics VMM7100 PCON (PCON max FRL bw 48 Gbps, no DSC encoder): Sink max rate from EDID = 24 Gbps FRL trained with : 24 Gbps while Windows/macOS train the same hardware at 40/48 Gbps. The too low FRL rate needlessly constrains the formats available to the sink. Only apply the sink's DSC max FRL rate if the PCON has a DSC 1.2 encoder, matching the gate in intel_dp_pcon_dsc_configure(). PCONs with a DSC encoder keep the current conservative behavior, since the link is trained once and compressed transport may be used for any subsequent mode. With this the setup above trains at 48 Gbps. Tested on PTL (xe) with the above PCON/sink combo. Fixes: 10fec80b48c5 ("drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding") Cc: Ankit Nautiyal Cc: Ville Syrjälä Reviewed-by: Ankit Nautiyal Signed-off-by: Alexander Kaplan Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260718105207.5565-3-alexander.kaplan@sms-medipool.de (cherry picked from commit 71b57dd92f94569dca4bdf883fbd8ca5d4ed4bae) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 2a0750acf2053..84fca860c93ee 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3859,7 +3859,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) rate_per_lane = info->hdmi.max_frl_rate_per_lane; max_frl_rate = max_lanes * rate_per_lane; - if (info->hdmi.dsc_cap.v_1p2) { + /* + * The sink's DSC max FRL rate only applies to compressed video + * transport, which requires a DSC 1.2 encoder in the PCON. Without + * one the HDMI link always carries uncompressed video, for which + * the regular max FRL rate is the limit. + */ + if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && + info->hdmi.dsc_cap.v_1p2) { max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; if (max_dsc_lanes && dsc_rate_per_lane) From fc97dcb42fb46ac3d1b6bbd4a2570744c0bb19a0 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 08:27:33 +0900 Subject: [PATCH 3038/3297] fprobe: Fix module reference count leak on error in register_fprobe() [ Upstream commit 8cf2f40ceb85047ad8a84dffae3bebc9fed18216 ] In register_fprobe(), get_ips_from_filter() resolves target function addresses and increments module reference counts via try_module_get() for symbols in kernel modules. If get_ips_from_filter() fails on the second pass and returns an error, register_fprobe() returned directly without releasing module references acquired up to that point. Fix this by ensuring the cleanup loop executing module_put() runs even when get_ips_from_filter() returns a negative error. Link: https://lore.kernel.org/all/178528125360.101985.4144133640239273153.stgit@devnote2/ Fixes: d24fa977eec5 ("tracing: fprobe: Fix to lock module while registering fprobe") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- kernel/trace/fprobe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 01f98f8a86e60..1c9ae332afad1 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -868,10 +868,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter return -ENOMEM; ret = get_ips_from_filter(filter, notfilter, addrs, mods, num); - if (ret < 0) - return ret; - - ret = register_fprobe_ips(fp, addrs, ret); + if (ret >= 0) + ret = register_fprobe_ips(fp, addrs, ret); for (int i = 0; i < num; i++) { if (mods[i]) From 9b604041f1009bec2b598935ebd31d6fedf9c9e3 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 28 Jul 2026 21:49:51 +0900 Subject: [PATCH 3039/3297] tracing/mmiotrace: Reset dropped_count in mmio_reset_data() [ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] mmio_reset_data() is called during tracer initialization, reset, and start. While it resets overrun_detected and prev_overruns, it neglects to reset dropped_count. Consequently, dropped event counts from prior tracing sessions persist in dropped_count and corrupt overrun reports in subsequent runs. Fix this by explicitly calling atomic_set(&dropped_count, 0) in mmio_reset_data(). Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace_mmiotrace.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index dc8a1eafbe574..e16b15d7c84aa 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) { overrun_detected = false; prev_overruns = 0; + atomic_set(&dropped_count, 0); tracing_reset_online_cpus(&tr->array_buffer); } From a20a0010eb6485f60cd64e15ebc85a4bd388642e Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 28 Jul 2026 21:50:00 +0900 Subject: [PATCH 3040/3297] tracing/mmiotrace: Add NULL check for mmio_trace_array in logging functions [ Upstream commit 12b80cdbc54cf615b4717a4e8180063408091ea2 ] mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map(). If these functions are invoked while mmio_trace_array is NULL (e.g. before initialization or after disabled), accessing tr->array_buffer.buffer will result in a NULL pointer dereference crash. Fix this by adding an explicit NULL check for tr at the beginning of __trace_mmiotrace_rw() and __trace_mmiotrace_map(). Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2 Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace_mmiotrace.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index e16b15d7c84aa..32b7e1d972a47 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -294,11 +294,15 @@ device_initcall(init_mmio_trace); static void __trace_mmiotrace_rw(struct trace_array *tr, struct mmiotrace_rw *rw) { - struct trace_buffer *buffer = tr->array_buffer.buffer; + struct trace_buffer *buffer; struct ring_buffer_event *event; struct trace_mmiotrace_rw *entry; unsigned int trace_ctx; + if (!tr) + return; + + buffer = tr->array_buffer.buffer; trace_ctx = tracing_gen_ctx_flags(0); event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, sizeof(*entry), trace_ctx); @@ -321,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw) static void __trace_mmiotrace_map(struct trace_array *tr, struct mmiotrace_map *map) { - struct trace_buffer *buffer = tr->array_buffer.buffer; + struct trace_buffer *buffer; struct ring_buffer_event *event; struct trace_mmiotrace_map *entry; unsigned int trace_ctx; + if (!tr) + return; + + buffer = tr->array_buffer.buffer; trace_ctx = tracing_gen_ctx_flags(0); event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, sizeof(*entry), trace_ctx); From 01bd01b61ad9ea917e5629a7f14f57a8af0543bb Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Fri, 12 Jun 2026 11:24:43 -0500 Subject: [PATCH 3041/3297] riscv: drop __init from vec_check_unaligned_access_speed_all_cpus [ Upstream commit f51fed61eea0daba2f95f1a6074085e4cd513c7b ] This function runs within a kthread and need not necessarily finish before system finishes boot and free_initmem() unmaps the .init.text section. This function makes calls to SBI for probing unaligned access speed, and if this is slow for some reason (say some debug prints were added to SBI), the kthread can still be running at this point and result in an instruction page fault when trying to fetch from the freed region. [ 25.642087] Unable to handle kernel paging request at virtual address ffffffff80a04ef8 [ 25.646694] Current vec_check_unali pgtable: 4K pagesize, 48-bit VAs, pgdp=0x00004000316e9000 [ 25.653170] [ffffffff80a04ef8] pgd=000010004be7e401, p4d=000010004be7e401, pud=000010004be7e001, pmd=000010000c3000e3 [ 25.661244] Oops [#1] [ 25.662997] Modules linked in: [ 25.665357] CPU: 3 UID: 0 PID: 42 Comm: vec_check_unali Not tainted 7.0.0-tt-blackhole-asrinivasan-00007-g30ff73f18211 #570 PREEMPTLAZY [ 25.674669] Hardware name: Tenstorrent Blackhole (DT) [ 25.678545] epc : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.683458] ra : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.688372] epc : ffffffff80a04ef8 ra : ffffffff80a04ef8 sp : ffff8f8000203e20 [ 25.693874] gp : ffffffff814dc168 tp : ffffaf8001ad9900 t0 : 0000000000000000 [ 25.699401] t1 : fffffffffffffff0 t2 : ffffaf8001ad9a10 s0 : ffff8f8000203e30 [ 25.704912] s1 : ffffaf80018dc780 a0 : 0000000000000000 a1 : 0000000000000002 [ 25.710407] a2 : 00000000000001f0 a3 : 0000000000000018 a4 : 0000000000000000 [ 25.715917] a5 : 0000000000000000 a6 : ffffaf8001c03d98 a7 : ffffaf8001c03e30 [ 25.721419] s2 : ffff8f8000023c98 s3 : ffffaf8001aa1240 s4 : ffffffff80a04ee0 [ 25.726937] s5 : 0000000000000000 s6 : 0000000000000000 s7 : 0000000000000000 [ 25.732450] s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000000 [ 25.737944] s11: 0000000000000000 t3 : 0000000000000002 t4 : 0000000000000402 [ 25.743481] t5 : 0000000000000040 t6 : 0000000000000004 ssp : 0000000000000000 [ 25.749024] status: 0000000200000120 badaddr: ffffffff80a04ef8 cause: 000000000000000c [ 25.755060] [] vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.760964] [] kthread+0xd8/0xfc [ 25.764660] [] ret_from_fork_kernel+0x18/0x1c4 [ 25.769220] [] ret_from_fork_kernel_asm+0x16/0x18 [ 25.774018] Code: cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc (cccc) cccc Drop __init from its signature so that this doesn't happen. Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions") Signed-off-by: Anirudh Srinivasan Assisted-by: Claude:claude-opus-4-6 Link: https://patch.msgid.link/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/kernel/unaligned_access_speed.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 70b5e69276209..78fc74a416787 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -376,7 +376,7 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus } /* Measure unaligned access speed on all CPUs present at boot in parallel. */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) { schedule_on_each_cpu(check_vector_unaligned_access); riscv_hwprobe_complete_async_probe(); @@ -384,7 +384,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway return 0; } #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) { return 0; } From b297559dc2f291872543bf9ae0ffd297c49519b3 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Thu, 18 Jun 2026 02:25:20 +0500 Subject: [PATCH 3042/3297] accel/qaic: use sizeof(*trans_hdr) for transaction length check [ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ] In encode_message() the per-transaction lower-bound check compares trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer, instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr. Every other length check in this file (encode_message() at the loop guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so this is an inconsistency. On 64-bit builds the pointer and the struct are both 8 bytes, so the check is correct by coincidence and there is no behavioural change. On 32-bit builds the pointer is 4 bytes, which weakens the minimum-length check below the 8-byte header size. Use sizeof(*trans_hdr) so the check validates against the actual transaction header size on all builds. Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()") Signed-off-by: Muhammad Bilal Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com Signed-off-by: Sasha Levin --- drivers/accel/qaic/qaic_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index 8eae30fe14f98..8759b53ba38eb 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -782,7 +782,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, break; } trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len); - if (trans_hdr->len < sizeof(trans_hdr) || + if (trans_hdr->len < sizeof(*trans_hdr) || size_add(user_len, trans_hdr->len) > user_msg->len) { ret = -EINVAL; break; From 54e07a158f7aea2754849d3a5a4cba1ac338ae7e Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Wed, 29 Jul 2026 03:21:32 +0200 Subject: [PATCH 3043/3297] riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove [ Upstream commit a0188cc133696627857d16054e43f9ebc7efc821 ] remove_pud_mapping() and remove_p4d_mapping() obtain a child table base with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for addr. RISC-V folds page-table levels at runtime. When a level is folded, its offset helper returns the parent entry itself, but the index can still be nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39 folds both P4D and PUD, so memory hot-remove can descend into unrelated memory and pass an invalid page to __free_pages(). This can trigger: kernel BUG at include/linux/mm.h:1810! VM_BUG_ON_PAGE(page_ref_count(page) == 0) arch_remove_memory+0x1e/0x5c try_remove_memory+0x15e/0x200 remove_memory+0x24/0x3c Only add the index when the corresponding page-table level is enabled, matching p4d_offset() and pud_offset(). Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support") Assisted-by: Claude:claude-fable-5 Signed-off-by: Karl Mehltretter Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/mm/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index ee40ca01ac663..557bf160b503a 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1738,7 +1738,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un for (; addr < end; addr = next) { next = pud_addr_end(addr, end); - pudp = pud_base + pud_index(addr); + pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base; pud = pudp_get(pudp); if (!pud_present(pud)) continue; @@ -1769,7 +1769,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un for (; addr < end; addr = next) { next = p4d_addr_end(addr, end); - p4dp = p4d_base + p4d_index(addr); + p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base; p4d = p4dp_get(p4dp); if (!p4d_present(p4d)) continue; From fd9586881d4782b92887dfb4216b69820ab4d580 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 28 Jul 2026 05:52:14 +0100 Subject: [PATCH 3044/3297] net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend [ Upstream commit b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 ] bus->read() returns a negative errno on failure, but mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT into 0xff92, and returns success. The garbage word is then consumed as register data, and read-modify-write cycles write it back to the switch. Check both reads and propagate their errors. The same defect existed in mt7530_mii_read() since the driver was introduced and moved into the regmap backend unchanged. Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") Signed-off-by: Daniel Golle Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/mt7530-mdio.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c index 0286a6cecb6f5..6cc2c8975af66 100644 --- a/drivers/net/dsa/mt7530-mdio.c +++ b/drivers/net/dsa/mt7530-mdio.c @@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) if (ret < 0) return ret; - lo = bus->read(bus, priv->mdiodev->addr, r); - hi = bus->read(bus, priv->mdiodev->addr, 0x10); + ret = bus->read(bus, priv->mdiodev->addr, r); + if (ret < 0) + return ret; + lo = ret; + + ret = bus->read(bus, priv->mdiodev->addr, 0x10); + if (ret < 0) + return ret; + hi = ret; *val = (hi << 16) | (lo & 0xffff); From 1e0dfb7e7a5d9021282cdb4efd8c03fb442f9ef4 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 28 Jul 2026 05:52:29 +0100 Subject: [PATCH 3045/3297] net: dsa: mt7530: error out on failed reads in MT7531 PHY polling [ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ] The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through a helper which returns 0 when the underlying read fails, so a failed bus transaction clears MT7531_PHY_ACS_ST and the access carries on, returning garbage PHY register data to phylib. Poll using regmap_read_poll_timeout(), which stops on read errors and propagates them. These functions hold the MDIO bus lock across the whole sequence, so the unlocked regmap accesses remain correct. Remove the now-unused _mt7530_unlocked_read(). Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch") Signed-off-by: Daniel Golle Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/dsa/mt7530.c | 58 ++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 4571da0d7a8f6..2a70fa4b5fa3e 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -188,12 +188,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) mt7530_mutex_unlock(priv); } -static u32 -_mt7530_unlocked_read(struct mt7530_dummy_poll *p) -{ - return mt7530_mii_read(p->priv, p->reg); -} - static u32 _mt7530_read(struct mt7530_dummy_poll *p) { @@ -546,16 +540,13 @@ static int mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, int regnum) { - struct mt7530_dummy_poll p; u32 reg, val; int ret; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -565,8 +556,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad) | regnum; mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -576,8 +567,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad); mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -594,16 +585,13 @@ static int mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, int regnum, u16 data) { - struct mt7530_dummy_poll p; u32 val, reg; int ret; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -613,8 +601,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad) | regnum; mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -624,8 +612,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad) | data; mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -640,16 +628,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, static int mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) { - struct mt7530_dummy_poll p; int ret; u32 val; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -660,8 +645,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -678,16 +663,13 @@ static int mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, u16 data) { - struct mt7530_dummy_poll p; int ret; u32 reg; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, - !(reg & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, + !(reg & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -698,8 +680,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, - !(reg & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, + !(reg & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; From 6bf322ab07418bad56a4635e8972bc42b2a3ff17 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Mon, 27 Jul 2026 14:03:48 +0800 Subject: [PATCH 3046/3297] ptp: netc: fix potential interrupt storm caused by incorrect unbind order [ Upstream commit 54ad7ea45d63146a8e3c57375f8a269d4cf7ecea ] In netc_timer_remove(), hardware interrupts are disabled by clearing TMR_TEMASK before ptp_clock_unregister() is called. This may cause a race condition during driver unbind that could leave hardware interrupts active. For example, a concurrent PTP_CLK_REQ_EXTTS ioctl can re-enable TMR_TEMASK after it has been cleared, leaving a pending hardware interrupt when the driver unbinds. Since the NETC Timer does not support PCIe FLR, hardware state is not reset during probe. When the driver is rebound and the IRQ is registered, the pending interrupt fires immediately. At that point priv->tmr_emask is still zero, so netc_timer_isr() does not clear the interrupt status and unconditionally returns IRQ_HANDLED, resulting in an uninterruptible infinite interrupt storm. Fix this in several ways. First, request the IRQ with IRQF_NO_AUTOEN so it is not enabled when request_irq() runs, and clear TMR_TEMASK in netc_timer_init() before enabling it. The IRQ is only enabled at the end of probe once the timer has been reprogrammed and the PTP clock has been registered. This ensures a stale pending interrupt from a previous unbind or an unclean shutdown cannot be delivered before the driver is fully initialized. Second, in netc_timer_remove() call disable_irq() before ptp_clock_unregister() and move the TMR_TEMASK/TMR_CTRL clearing after it. disable_irq() masks the line and waits for any in-flight netc_timer_isr() to finish, so no ISR can dereference priv->clock after ptp_clock_unregister() has freed it. Unregistering the PTP clock before clearing the mask also guarantees that no in-flight or concurrent ioctl can re-enable hardware interrupts. Finally, return IRQ_NONE from netc_timer_isr() when the masked event status is zero, so the kernel's spurious interrupt detection can disable a stuck line instead of looping forever. Fixes: 671e266835b8 ("ptp: netc: add periodic pulse output support") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260720012508.23227-1-wei.fang%40oss.nxp.com Signed-off-by: Wei Fang Link: https://patch.msgid.link/20260727060348.1887464-1-wei.fang@oss.nxp.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/ptp/ptp_netc.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c index 5e381c354d746..1c20d7efab929 100644 --- a/drivers/ptp/ptp_netc.c +++ b/drivers/ptp/ptp_netc.c @@ -769,6 +769,7 @@ static void netc_timer_init(struct netc_timer *priv) TMR_CTRL_TE | TMR_CTRL_FS; netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl); netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc); + netc_timer_wr(priv, NETC_TMR_TEMASK, 0); /* Disable FIPER by default */ fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); @@ -901,6 +902,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data) /* Clear interrupts status */ netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event); + if (!tmr_event) { + spin_unlock(&priv->lock); + return IRQ_NONE; + } + if (tmr_event & TMR_TEVENT_ALMEN(0)) netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0); @@ -936,7 +942,8 @@ static int netc_timer_init_msix_irq(struct netc_timer *priv) } priv->irq = pci_irq_vector(pdev, 0); - err = request_irq(priv->irq, netc_timer_isr, 0, priv->irq_name, priv); + err = request_irq(priv->irq, netc_timer_isr, IRQF_NO_AUTOEN, + priv->irq_name, priv); if (err) { dev_err(&pdev->dev, "request_irq() failed\n"); pci_free_irq_vectors(pdev); @@ -951,7 +958,6 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv) { struct pci_dev *pdev = priv->pdev; - disable_irq(priv->irq); free_irq(priv->irq, priv); pci_free_irq_vectors(pdev); } @@ -1005,6 +1011,8 @@ static int netc_timer_probe(struct pci_dev *pdev, goto free_msix_irq; } + enable_irq(priv->irq); + return 0; free_msix_irq: @@ -1019,9 +1027,10 @@ static void netc_timer_remove(struct pci_dev *pdev) { struct netc_timer *priv = pci_get_drvdata(pdev); + disable_irq(priv->irq); + ptp_clock_unregister(priv->clock); netc_timer_wr(priv, NETC_TMR_TEMASK, 0); netc_timer_wr(priv, NETC_TMR_CTRL, 0); - ptp_clock_unregister(priv->clock); netc_timer_free_msix_irq(priv); netc_timer_pci_remove(pdev); } From b90916156b47221e02dc8188ae60cdaa90465332 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Fri, 24 Jul 2026 15:46:57 +0800 Subject: [PATCH 3047/3297] net: libwx: fix FDIR ATR queue mismatch for software VLAN packets [ Upstream commit 732ed8f75ce583d115716f668dc80d730f3ad610 ] When TX VLAN hardware offload is disabled, VLAN tags are embedded in the packet payload (software VLAN). Previously, the driver failed to set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission. This missing flag caused the txgbe FDIR ATR logic to fall through to the default hash calculation path. This resulted in asymmetric hash values for Tx and Rx flows, preventing return packets from being steered to the same queue as the transmit packets. Fix this by detecting software VLANs via eth_type_vlan(skb->protocol) and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects the correct hashing algorithm to maintain Tx/Rx queue symmetry. Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support") Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 678dce480f156..380edc64e45e4 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1549,6 +1549,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb, if (skb_vlan_tag_present(skb)) { tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT; tx_flags |= WX_TX_FLAGS_HW_VLAN; + } else if (eth_type_vlan(skb->protocol)) { + tx_flags |= WX_TX_FLAGS_SW_VLAN; } if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && From 5a73e8c632c3147f3e9890ea94e76eca5b2283f6 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Fri, 24 Jul 2026 12:58:31 +0530 Subject: [PATCH 3048/3297] octeontx2-pf: Set correct sequence for carrier off and tx queue stop [ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] During link down event, we were doing netif_tx_stop_all_queues() first and then netif_carrier_off(). This can cause a potential race since carrier is still on during down event. This patch reverse the calling order to fix the issue. Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") Signed-off-by: Suman Ghosh Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index ae7bded777688..7e42d3291a9fd 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -889,8 +889,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) netif_carrier_on(netdev); netif_tx_start_all_queues(netdev); } else { - netif_tx_stop_all_queues(netdev); netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); } } From caeaaf23f7c369a62d3c30a5e29b48f981d20677 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Fri, 22 May 2026 14:58:33 +0200 Subject: [PATCH 3049/3297] sched/deadline: Use revised wakeup rule only for running dl_server [ Upstream commit 1842bf97af109f5ebf830175c9725bf81ebb78b1 ] Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") applies the revised wakeup rule to any server, as a result servers that are not running (dl_defer_running == 0) and start with a deadline overflow get enqueued and can boost tasks as if they were running, invalidating the defer rule and the documented state model. Apply the revised wakeup rule only for deferrable servers that are marked as running. Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") Signed-off-by: Gabriele Monaco Signed-off-by: Peter Zijlstra (Intel) Acked-by: Juri Lelli Tested-by: Andrea Righi Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com Signed-off-by: Sasha Levin --- kernel/sched/deadline.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index d5052f238adf7..e8b9cf0767b27 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1036,7 +1036,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se) if (dl_time_before(dl_se->deadline, rq_clock(rq)) || dl_entity_overflow(dl_se, rq_clock(rq))) { - if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) && + if (unlikely((!dl_is_implicit(dl_se) || + (dl_se->dl_defer && dl_se->dl_defer_running)) && !dl_time_before(dl_se->deadline, rq_clock(rq)) && !is_dl_boosted(dl_se))) { update_dl_revised_wakeup(dl_se, rq); From 51c52e493346fd8d0305d8891a8984ada584e28d Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 28 Jul 2026 18:18:08 +0800 Subject: [PATCH 3050/3297] spi: spi-nxp-fspi: add per-SoC SDR/DTR clock rate limits for all supported SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9c19d60fea9f46ed0c3394653ef4941f1a459968 ] The commit f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") introduced a global 166MHz cap for DTR mode (RXCLKSRC=3), based on the i.MX8MN datasheet timing specification (Section 3.9.9, page 65). After reviewing the FlexSPI timing parameters in the datasheets for all supported SoCs, the following corrections and additions are needed: 1. SDR mode (RXCLKSRC=0) limits vary per SoC: - i.MX8MN/MM/MP/95: 66MHz (IMX8MNCEC §3.9.9, IMX8MMCEC §3.9.10, IMX8MPCEC, IMX95CEC Rev.8 §4.11.7) - i.MX8QXP/QM/DXL/ULP: 60MHz (IMX8QXPCEC, IMX8QMCEC, IMX8DXLCEC, IMX8ULPCEC §7.3.1 ND mode) - LX2160A: 100MHz (LX2160ACEC FlexSPI timing parameters) 2. DTR mode (RXCLKSRC=3) limits vary per SoC: - i.MX8MN/MM/MP/ULP: 166MHz - i.MX8QXP/QM/DXL: 200MHz (same FlexSPI IP across this family) - i.MX95: 200MHz (IMX95CEC §4.11.7.3.2.3 Table 106) - LX2160A: DTR disabled (FSPI_QUIRK_DISABLE_DTR) Update related platform data with correct speed limation according to datasheet. Fixes: f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20260728-fspi-clock-v2-1-dbe786a4a6eb@nxp.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-nxp-fspi.c | 83 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 95ccb1f7dfafa..b504a5c86692a 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data { unsigned int quirks; unsigned int lut_num; bool little_endian; + /* + * The max clock rate (Hz) that FlexSPI can output to the device + * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero. + * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode. + */ + unsigned long max_sdr_rate; + /* + * The max clock rate (Hz) that FlexSPI can output to the device + * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero. + * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz. + */ + unsigned long max_dtr_rate; }; static struct nxp_fspi_devtype_data lx2160a_data = { @@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = { .quirks = FSPI_QUIRK_DISABLE_DTR, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk. + */ + .max_sdr_rate = 100000000, }; static struct nxp_fspi_devtype_data imx8mm_data = { @@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = { .quirks = 0, .lut_num = 32, .little_endian = true, /* little-endian */ + /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 166000000, +}; + +static struct nxp_fspi_devtype_data imx8mp_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ + /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 166000000, }; static struct nxp_fspi_devtype_data imx8qxp_data = { @@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = { .quirks = 0, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz. + * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 200000000, }; static struct nxp_fspi_devtype_data imx8dxl_data = { @@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = { .quirks = FSPI_QUIRK_USE_IP_ONLY, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz, + * DDR RXCLKSRC=3 max 200MHz. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 200000000, }; static struct nxp_fspi_devtype_data imx8ulp_data = { @@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = { .quirks = 0, .lut_num = 16, .little_endian = true, /* little-endian */ + /* + * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode: + * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz. + * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR + * but is not the default use case. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 166000000, +}; + +static struct nxp_fspi_devtype_data imx95_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ + /* + * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz, + * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode). + */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 200000000, }; struct nxp_fspi { @@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f, reg = fspi_readl(f, f->iobase + FSPI_MCR0); if (op_is_dtr) { reg |= FSPI_MCR0_RXCLKSRC(3); - f->max_rate = 166000000; + /* + * Use the SoC-specific DTR max rate if provided, otherwise + * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9). + */ + f->max_rate = f->devtype_data->max_dtr_rate ? + f->devtype_data->max_dtr_rate : 166000000; } else { /*select mode 0 */ reg &= ~FSPI_MCR0_RXCLKSRC(3); - f->max_rate = 66000000; + /* + * Use the SoC-specific SDR max rate if provided, otherwise + * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9). + */ + f->max_rate = f->devtype_data->max_sdr_rate ? + f->devtype_data->max_sdr_rate : 66000000; } fspi_writel(f, reg, f->iobase + FSPI_MCR0); } @@ -1446,10 +1522,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = { static const struct of_device_id nxp_fspi_dt_ids[] = { { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, }, { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, - { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, }, + { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, }, { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, }, { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, }, { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, }, + { .compatible = "nxp,imx95-fspi", .data = (void *)&imx95_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); From e51becb8f3377a377171ed5bf0082b96e22e6292 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Sun, 26 Jul 2026 12:43:11 +0200 Subject: [PATCH 3051/3297] qede: sync udp_tunnel ports outside qede_lock in the recovery path [ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports configured wedges the rtnetlink control plane of the whole machine: NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! [qede_recovery_handler:2665(ens6f0)]Starting a recovery process The recovery path deadlocks on the driver's own mutex: qede_sp_task rtnl_lock() mutex_lock(&edev->qede_lock) <- taken qede_recovery_handler qede_load udp_tunnel_nic_reset_ntf __udp_tunnel_nic_device_sync info->sync_table == qede_udp_tunnel_sync mutex_lock(&edev->qede_lock) <- same task: deadlock The mutex is not recursive, so the kworker blocks on itself with rtnl_lock held, and neither lock is ever released. Every task that calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 addrconf, sshd) blocks forever while the node still answers ping. In a vmcore from an affected production node rtnl_mutex.owner decodes to the very kworker blocked at the innermost mutex_lock() above. Re-sync the tunnel ports from qede_sp_task() after the internal lock is dropped, still under rtnl_lock as the udp_tunnel API requires. This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() under rtnl without the internal lock. qede_recovery_handler() now returns whether it has successfully reloaded an open device, and the caller re-syncs the ports only in that case. This keeps the old gating exactly: a device that was down or a failed recovery returns false, as those paths never reached the udp_tunnel_nic_reset_ntf() call before either. This was the only user of the qede_lock()/qede_unlock() helpers, so remove them. Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") Signed-off-by: Denis V. Lunev CC: Andrew Lunn CC: "David S. Miller" CC: Eric Dumazet CC: Jakub Kicinski CC: Paolo Abeni Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index b5d744d2586f7..59a80f7193723 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev); static void qede_shutdown(struct pci_dev *pdev); static void qede_link_update(void *dev, struct qed_link_output *link); static void qede_schedule_recovery_handler(void *dev); -static void qede_recovery_handler(struct qede_dev *edev); +static bool qede_recovery_handler(struct qede_dev *edev); static void qede_schedule_hw_err_handler(void *dev, enum qed_hw_err_type err_type); static void qede_get_eth_tlv_data(void *edev, void *data); @@ -1065,21 +1065,6 @@ void __qede_unlock(struct qede_dev *edev) mutex_unlock(&edev->qede_lock); } -/* This version of the lock should be used when acquiring the RTNL lock is also - * needed in addition to the internal qede lock. - */ -static void qede_lock(struct qede_dev *edev) -{ - rtnl_lock(); - __qede_lock(edev); -} - -static void qede_unlock(struct qede_dev *edev) -{ - __qede_unlock(edev); - rtnl_unlock(); -} - static void qede_periodic_task(struct work_struct *work) { struct qede_dev *edev = container_of(work, struct qede_dev, @@ -1116,6 +1101,8 @@ static void qede_sp_task(struct work_struct *work) */ if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { + bool reloaded; + cancel_delayed_work_sync(&edev->periodic_task); #ifdef CONFIG_QED_SRIOV /* SRIOV must be disabled outside the lock to avoid a deadlock. @@ -1124,9 +1111,17 @@ static void qede_sp_task(struct work_struct *work) if (pci_num_vf(edev->pdev)) qede_sriov_configure(edev->pdev, 0); #endif - qede_lock(edev); - qede_recovery_handler(edev); - qede_unlock(edev); + rtnl_lock(); + __qede_lock(edev); + reloaded = qede_recovery_handler(edev); + __qede_unlock(edev); + + /* The udp_tunnel core synchronously calls back into + * qede_udp_tunnel_sync(), which takes the qede lock. + */ + if (reloaded) + udp_tunnel_nic_reset_ntf(edev->ndev); + rtnl_unlock(); } __qede_lock(edev); @@ -2667,9 +2662,13 @@ static void qede_recovery_failed(struct qede_dev *edev) edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); } -static void qede_recovery_handler(struct qede_dev *edev) +/* Returns true if an open device was successfully reloaded and its + * udp_tunnel ports need to be re-synced by the caller. + */ +static bool qede_recovery_handler(struct qede_dev *edev) { u32 curr_state = edev->state; + bool reloaded = false; int rc; DP_NOTICE(edev, "Starting a recovery process\n"); @@ -2699,17 +2698,18 @@ static void qede_recovery_handler(struct qede_dev *edev) goto err; qede_config_rx_mode(edev->ndev); - udp_tunnel_nic_reset_ntf(edev->ndev); + reloaded = true; } edev->state = curr_state; DP_NOTICE(edev, "Recovery handling is done\n"); - return; + return reloaded; err: qede_recovery_failed(edev); + return false; } static void qede_atomic_hw_err_handler(struct qede_dev *edev) From cebba11df714bde20d55a4d7cca2ec7fdfd52a00 Mon Sep 17 00:00:00 2001 From: Jerome Tollet Date: Wed, 20 May 2026 07:55:44 +0530 Subject: [PATCH 3052/3297] drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status [ Upstream commit 1afb8eaeec44fd011f2b93ccd9fd426d753d963b ] HDMI 2.0 section 6.1.3.1 specifies that after enabling Scrambling_Enable and starting scrambled video transmission, the source should poll Scrambling_Status until it reads 1 or until a timeout of 200 ms expires. Add a polling step after enabling the HDMI port to check the scrambling status when HDMI scrambling is enabled. On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz (594 MHz) failing to come up correctly because the sink has not yet finished its scrambling setup. In practice, waiting for the scrambling status here fixes such sinks. While this synchronous polling is not itself explicitly required for correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it as the way for the source to verify that the TMDS link is functioning correctly with scrambling enabled. v3: - Add explicit HDMI 2.0 section reference in code comment - Clarify commit message around the observed sink fix v2: - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed delay Reported-by: Jerome Tollet Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868 Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/ Signed-off-by: Jerome Tollet Signed-off-by: Ankit Nautiyal Reviewed-by: Arun R Murthy Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com (cherry picked from commit b7d51d65e4f12a48392d260613108ec262bc7774) Fixes: 15953637886d ("drm/i915: enable scrambling") Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 ++ drivers/gpu/drm/i915/display/intel_hdmi.c | 26 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_hdmi.h | 2 ++ 3 files changed, 30 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index c09aa759f4d4f..3de3e9167d0d6 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3502,6 +3502,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, } intel_ddi_buf_enable(encoder, buf_ctl); + + intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); } static void intel_ddi_enable(struct intel_atomic_state *state, diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 4ab7e2e3bfd42..e77c6422b001f 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2680,6 +2680,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ drm_connector_attach_max_bpc_property(&connector->base, 8, 12); } +/* + * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after + * enabling Scrambling_Enable and starting scrambled video + * transmission, poll Scrambling_Status for up to 200 ms. + */ +void +intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector) +{ + struct intel_connector *connector = to_intel_connector(_connector); + struct intel_display *display = to_intel_display(crtc_state); + bool scrambling_enabled = false; + int ret; + + if (!crtc_state->hdmi_scrambling) + return; + + /* Poll for a max of 200 msec as per HDMI spec */ + ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), + scrambling_enabled, 1000, 200 * 1000, false); + if (ret) + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", + connector->base.base.id, connector->base.name); +} + /* * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup * @encoder: intel_encoder diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index dec2ad7dd8a22..2de9056367916 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -69,5 +69,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, unsigned int type, void *frame, ssize_t len); +void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector); #endif /* __INTEL_HDMI_H__ */ From 213b4568f6e5d5b2c6402bd820f5d9ea3c7a8f91 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 21 Jun 2026 19:41:08 +0900 Subject: [PATCH 3053/3297] ksmbd: return success for deferred final close [ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ] ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table reference. If another in-flight request still holds a reference, the final close is deferred until that request drops its reference. The function currently returns -EINVAL in that deferred-final-close case because fp is cleared when the reference count does not reach zero. That turns a valid close into STATUS_FILE_CLOSED. smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then closes the same directory handle before receiving the find response. The query holds a reference while it builds the response, so close must mark the handle closed and return success even though final teardown is delayed. Track whether the handle was successfully transitioned to FP_CLOSED and return success when only the final close is deferred. Signed-off-by: Namjae Jeon Signed-off-by: Steve French Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()") Signed-off-by: Sasha Levin --- fs/smb/server/vfs_cache.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 5b6d8bb8edb27..0d3341927b483 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -487,6 +487,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) { struct ksmbd_file *fp; struct ksmbd_file_table *ft; + bool closed = false; if (!has_file_id(id)) return 0; @@ -501,6 +502,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp = NULL; else { fp->f_state = FP_CLOSED; + closed = true; if (!atomic_dec_and_test(&fp->refcount)) fp = NULL; } @@ -508,7 +510,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) write_unlock(&ft->lock); if (!fp) - return -EINVAL; + return closed ? 0 : -EINVAL; __put_fd_final(work, fp); return 0; From 9be4a66f019ea90bd9deca70511f4f9ffebf5c6f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 22 Jul 2026 10:04:19 +0900 Subject: [PATCH 3054/3297] ksmbd: fix use-after-free in __close_file_table_ids() [ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ] A ksmbd_file can remain alive after logical close while another session holds a temporary reference obtained through ksmbd_lookup_fd_inode(). ksmbd_close_fd() currently marks the file closed and drops the idr-owned reference, but leaves the pointer published in the closing session's idr until the final reference is dropped. If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() supplies the foreign session's file table to __ksmbd_close_fd(). The object is then freed without being removed from its owner's idr, and the owner session later dereferences the stale pointer during file-table teardown. Remove the volatile id from the owner's idr while ksmbd_close_fd() still holds that table's lock, and clear volatile_id before dropping the idr-owned reference. A later foreign final put then only performs physical destruction and cannot remove the object from the wrong table. Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") Reported-by: Yunseong Kim Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/vfs_cache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 0d3341927b483..51e37e89d1aa5 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp = NULL; else { fp->f_state = FP_CLOSED; + idr_remove(ft->idr, id); + fp->volatile_id = KSMBD_NO_FID; closed = true; if (!atomic_dec_and_test(&fp->refcount)) fp = NULL; From cfc686a1174aa904dcad9b9a7c5e46b484d27e4e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 29 Jun 2026 14:52:29 +0200 Subject: [PATCH 3055/3297] iomap: add a separate bio_set for iomap_split_ioend [ Upstream commit c679ce3be6cb63763d68ab9b5d9d73ddc0a40762 ] iomap_split_ioend can split bios that already come from iomap_ioend_bioset and thus deadlock when the bioset is exhausted. Add a separate bio_set to avoid this deadlock. Christian Brauner says: Mark iomap_ioend_split_bioset static as it is only used in ioend.c, fixing the sparse warning reported by the kernel test robot. Fixes: 5fcbd555d483 ("iomap: split bios to zone append limits in the submission handlers") Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260629125229.3400726-1-hch@lst.de Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Sasha Levin --- fs/iomap/ioend.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 6c75fbf4d5af4..2a86b05c88efc 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -11,6 +11,7 @@ struct bio_set iomap_ioend_bioset; EXPORT_SYMBOL_GPL(iomap_ioend_bioset); +static struct bio_set iomap_ioend_split_bioset; struct iomap_ioend *iomap_init_ioend(struct inode *inode, struct bio *bio, loff_t file_offset, u16 ioend_flags) @@ -409,7 +410,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT, i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT; - split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset); + split = bio_split(bio, sector_offset, GFP_NOFS, + &iomap_ioend_split_bioset); if (IS_ERR(split)) return ERR_CAST(split); split->bi_private = bio->bi_private; @@ -432,8 +434,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend); static int __init iomap_ioend_init(void) { - return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE), + const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE); + int error; + + error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries, offsetof(struct iomap_ioend, io_bio), BIOSET_NEED_BVECS); + if (error) + return error; + error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries, + offsetof(struct iomap_ioend, io_bio), + BIOSET_NEED_BVECS); + if (error) + goto out_exit_ioend_bioset; + return 0; + +out_exit_ioend_bioset: + bioset_exit(&iomap_ioend_bioset); + return error; } fs_initcall(iomap_ioend_init); From 72a90ce4918b5a2d4820fe20677ba9af780d8826 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:15 +0000 Subject: [PATCH 3056/3297] mshv: Fix race in mshv_irqfd_deassign [ Upstream commit 0762262ac3e70f65b3bb843fe892f8bac1562d08 ] mshv_irqfd_deactivate() and the hlist traversal of pt_irqfds_list require pt->pt_irqfds_lock to be held, but mshv_irqfd_deassign() omits it. This races with the EPOLLHUP path in mshv_irqfd_wakeup(), which does take the lock before calling mshv_irqfd_deactivate(). Additionally, mshv_irqfd_deactivate() uses hlist_del() which poisons the node pointers rather than resetting them. Since mshv_irqfd_is_active() relies on hlist_unhashed() (checks pprev == NULL), a poisoned node still appears active. If a concurrent path calls mshv_irqfd_deactivate() again on the same irqfd, the guard fails to prevent a double hlist_del() on poisoned pointers. Fix both issues: - Add the missing spin_lock_irq/spin_unlock_irq around the list traversal in mshv_irqfd_deassign(), matching mshv_irqfd_release(). - Use hlist_del_init() instead of hlist_del() so the node is properly marked as unhashed after removal, making the is_active guard reliable. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/mshv_eventfd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 40b7179fee805..586a9f99b3405 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -280,7 +280,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd) if (!mshv_irqfd_is_active(irqfd)) return; - hlist_del(&irqfd->irqfd_hnode); + hlist_del_init(&irqfd->irqfd_hnode); queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown); } @@ -535,13 +535,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt, if (IS_ERR(eventfd)) return PTR_ERR(eventfd); + spin_lock_irq(&pt->pt_irqfds_lock); hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list, irqfd_hnode) { if (irqfd->irqfd_eventfd_ctx == eventfd && irqfd->irqfd_irqnum == args->gsi) - mshv_irqfd_deactivate(irqfd); } + spin_unlock_irq(&pt->pt_irqfds_lock); eventfd_ctx_put(eventfd); From fc362bfcb060eb6050aca772f60904153b6c228b Mon Sep 17 00:00:00 2001 From: Jinank Jain Date: Mon, 24 Nov 2025 14:25:59 +0000 Subject: [PATCH 3057/3297] mshv: adjust interrupt control structure for ARM64 [ Upstream commit 9d70ef7a18e0ec1653ac63020a13a5d4dda7cc0d ] Interrupt control structure (union hv_interupt_control) has different fields when it comes to x86 vs ARM64. Bring in the correct structure from HyperV header files and adjust the existing interrupt routing code accordingly. Signed-off-by: Jinank Jain Signed-off-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu Stable-dep-of: 0289a67cd70b ("mshv: Fix level-triggered check on uninitialized data") Signed-off-by: Sasha Levin --- drivers/hv/mshv_eventfd.c | 6 ++++++ drivers/hv/mshv_irq.c | 4 ++++ drivers/hv/mshv_root_hv_call.c | 6 ++++++ include/hyperv/hvhdk.h | 6 ++++++ 4 files changed, 22 insertions(+) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 586a9f99b3405..311aa9fecbc48 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -164,8 +164,10 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd) if (hv_scheduler_type != HV_SCHEDULER_TYPE_ROOT) return -EOPNOTSUPP; +#if IS_ENABLED(CONFIG_X86) if (irq->lapic_control.logical_dest_mode) return -EOPNOTSUPP; +#endif vp = partition->pt_vp_array[irq->lapic_apic_id]; @@ -197,8 +199,10 @@ static void mshv_assert_irq_slow(struct mshv_irqfd *irqfd) unsigned int seq; int idx; +#if IS_ENABLED(CONFIG_X86) WARN_ON(irqfd->irqfd_resampler && !irq->lapic_control.level_triggered); +#endif idx = srcu_read_lock(&partition->pt_irq_srcu); if (irqfd->irqfd_girq_ent.guest_irq_num) { @@ -469,6 +473,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); spin_lock_irq(&pt->pt_irqfds_lock); +#if IS_ENABLED(CONFIG_X86) if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { /* @@ -479,6 +484,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, ret = -EINVAL; goto fail; } +#endif ret = 0; hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c index 743777b4886d3..7422a36199b40 100644 --- a/drivers/hv/mshv_irq.c +++ b/drivers/hv/mshv_irq.c @@ -119,6 +119,10 @@ void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent, lirq->lapic_vector = ent->girq_irq_data & 0xFF; lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF; lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8; +#if IS_ENABLED(CONFIG_X86) lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1; lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1; +#elif IS_ENABLED(CONFIG_ARM64) + lirq->lapic_control.asserted = 1; +#endif } diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c index c9c274f29c3c6..a23b5ba09fb40 100644 --- a/drivers/hv/mshv_root_hv_call.c +++ b/drivers/hv/mshv_root_hv_call.c @@ -388,7 +388,13 @@ int hv_call_assert_virtual_interrupt(u64 partition_id, u32 vector, memset(input, 0, sizeof(*input)); input->partition_id = partition_id; input->vector = vector; + /* + * NOTE: dest_addr only needs to be provided while asserting an + * interrupt on x86 platform + */ +#if IS_ENABLED(CONFIG_X86) input->dest_addr = dest_addr; +#endif input->control = control; status = hv_do_hypercall(HVCALL_ASSERT_VIRTUAL_INTERRUPT, input, NULL); local_irq_restore(flags); diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h index 1057455b84f28..cdc74199ba3fb 100644 --- a/include/hyperv/hvhdk.h +++ b/include/hyperv/hvhdk.h @@ -540,9 +540,15 @@ union hv_interrupt_control { u64 as_uint64; struct { u32 interrupt_type; /* enum hv_interrupt_type */ +#if IS_ENABLED(CONFIG_X86) u32 level_triggered : 1; u32 logical_dest_mode : 1; u32 rsvd : 30; +#elif IS_ENABLED(CONFIG_ARM64) + u32 rsvd1 : 2; + u32 asserted : 1; + u32 rsvd2 : 29; +#endif } __packed; }; From f50f5d3972da0c4f888be2ea66e470e317afb0df Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:43 +0000 Subject: [PATCH 3058/3297] mshv: Fix level-triggered check on uninitialized data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0289a67cd70bf9d3807e289f4efd643e16a6c6b4 ] In mshv_irqfd_assign(), the level-triggered validation for resample irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before mshv_irqfd_update() has populated the field. Since the irqfd struct is zero-allocated, level_triggered is always 0 at that point, causing the check to always reject resample irqfds with -EINVAL. This makes level-triggered interrupt resampling — used to avoid interrupt storms with assigned devices — completely non-functional. Move the check after the mshv_irqfd_update() call, which resolves the IRQ routing entry and populates irqfd_lapic_irq with the actual trigger mode. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/mshv_eventfd.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 311aa9fecbc48..65b969b82fb97 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -473,6 +473,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); spin_lock_irq(&pt->pt_irqfds_lock); + ret = 0; + hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { + if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) + continue; + /* This fd is used for another irq already. */ + ret = -EBUSY; + spin_unlock_irq(&pt->pt_irqfds_lock); + goto fail; + } + + idx = srcu_read_lock(&pt->pt_irq_srcu); + mshv_irqfd_update(pt, irqfd); + #if IS_ENABLED(CONFIG_X86) if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { @@ -481,22 +494,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, * Otherwise return with failure */ spin_unlock_irq(&pt->pt_irqfds_lock); + srcu_read_unlock(&pt->pt_irq_srcu, idx); ret = -EINVAL; goto fail; } #endif - ret = 0; - hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { - if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) - continue; - /* This fd is used for another irq already. */ - ret = -EBUSY; - spin_unlock_irq(&pt->pt_irqfds_lock); - goto fail; - } - idx = srcu_read_lock(&pt->pt_irq_srcu); - mshv_irqfd_update(pt, irqfd); hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); spin_unlock_irq(&pt->pt_irqfds_lock); From 062aa5dcc49a9ad96726a80c2a0ab0a1233bc2b9 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 23 Jul 2026 00:28:11 +0000 Subject: [PATCH 3059/3297] mshv: Order pt_vp_array publish against irqfd assertion path [ Upstream commit b098dc869219c15dc49bf9cf63fb5fc1481d3373 ] mshv_partition_ioctl_create_vp() initialises a VP struct (allocations, mutex_init, init_waitqueue_head, page mappings) and then publishes the pointer into partition->pt_vp_array. Several ISR paths read this array locklessly: the intercept ISR, the two scheduler ISRs, and mshv_try_assert_irq_fast() on the irqfd fast path. Of these, only mshv_try_assert_irq_fast() can structurally race the publish. It runs from an eventfd waker without holding pt_mutex, and MSHV_IRQFD does not require the target lapic_apic_id (== vp_index) to refer to an existing VP at registration time. A user can therefore register an irqfd targeting a yet-to-be-created VP, then trigger mshv_try_assert_irq_fast() concurrently with MSHV_CREATE_VP for the same index. On weakly-ordered architectures the reader can observe a non-NULL pointer in pt_vp_array before the initialising stores to the VP struct become visible, leading to use of partially-initialised fields (e.g. vp_register_page). The other ISR readers cannot reach this race: the hypervisor will not generate intercept or scheduler messages for a VP that has never been told to run, and the user can only call MSHV_RUN_VP on the VP fd returned by MSHV_CREATE_VP, which by construction is returned after the publish. Leave those readers as plain loads. Use smp_store_release() in mshv_partition_ioctl_create_vp() to publish the pointer, and pair it with smp_load_acquire() in mshv_try_assert_irq_fast(). On x86 these compile to plain accesses under TSO; on ARM64 they emit one-instruction acquire/release barriers, acceptable on this fast path. The destroy-side path (destroy_partition() clearing pt_vp_array[i] to NULL after kfree(vp)) has a separate ordering and lifetime concern that is out of scope here. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu Signed-off-by: Sasha Levin --- drivers/hv/mshv_eventfd.c | 9 ++++++++- drivers/hv/mshv_root_main.c | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 65b969b82fb97..161a9655aa0b1 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd) return -EOPNOTSUPP; #endif - vp = partition->pt_vp_array[irq->lapic_apic_id]; + /* + * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp(). + * MSHV_IRQFD does not require the target lapic_apic_id to refer to an + * existing VP, so this read can race a concurrent VP creation; the + * acquire ensures that a non-NULL pointer implies the VP's + * initialising stores are visible. + */ + vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]); if (!vp->vp_register_page) return -EOPNOTSUPP; diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 4e04bef544379..3a356b34774bd 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -979,7 +979,13 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, /* already exclusive with the partition mutex for all ioctls */ partition->pt_vp_count++; - partition->pt_vp_array[args.vp_index] = vp; + /* + * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which + * can run concurrently from an irqfd waker without holding pt_mutex. + * The release ensures the VP's initialising stores are visible to any + * reader that observes a non-NULL pointer in pt_vp_array. + */ + smp_store_release(&partition->pt_vp_array[args.vp_index], vp); return ret; From 294b464b2be7e57872864cb6936e1d9c7294f89d Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:09 -0700 Subject: [PATCH 3060/3297] iommufd/viommu: Release the igroup lock on the vdevice_size error path commit 339bd11591593ab7ce88136ab7fd01ef3813b724 upstream. iommufd_vdevice_alloc_ioctl() takes idev->igroup->lock, then validates the driver's vdevice_size against the core structure size with a WARN_ON_ONCE. On failure that guard jumps to out_put_idev, below out_unlock_igroup, so it skips the mutex_unlock(), leaving the igroup lock held and deadlocking the next vDEVICE operation on that group. Jump to out_unlock_igroup instead. Fixes: ed42eee797ff3 ("iommufd/viommu: Add driver-defined vDEVICE support") Link: https://patch.msgid.link/r/e903f775d491296a525097e2a90b3eb6a47cf2ef.1783311134.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/viommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 462b457ffd0c4..71ad8ddf2106e 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -189,7 +189,7 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) if (WARN_ON_ONCE(viommu->ops->vdevice_size < vdev_size || !viommu->ops->vdevice_init)) { rc = -EOPNOTSUPP; - goto out_put_idev; + goto out_unlock_igroup; } vdev_size = viommu->ops->vdevice_size; } From ee2212b482320d657b40c63864e028fdc259de64 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:10 -0700 Subject: [PATCH 3061/3297] iommufd/viommu: Publish a vDEVICE only after vdevice_init() succeeds commit 9be311cfbe6154da146a7408e0d5e518a9321ed3 upstream. iommufd_vdevice_alloc_ioctl() adds the vDEVICE to the viommu->vdevs xarray with xa_cmpxchg() before the driver's vdevice_init() op runs. That op is where a driver validates the device and may reject it, but the xarray entry is already live by then: a concurrent IOMMU_HWPT_INVALIDATE can look it up with iommufd_viommu_find_dev() and run the driver invalidation path against a device that vdevice_init() would have refused. Reserve the index with xa_insert(): it stores a zero entry that reads back as NULL, and returns -EBUSY on a duplicate virt_id. Run vdevice_init() and store the vDEVICE pointer only once it succeeds. A failed vdevice_init() releases the reservation, so lookups observe the vDEVICE only after it is fully initialized and accepted. Fixes: ed42eee797ff3 ("iommufd/viommu: Add driver-defined vDEVICE support") Link: https://patch.msgid.link/r/1e05999347f4bf583edbc6a1312c857d5548708c.1783311134.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/viommu.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 71ad8ddf2106e..f80582f25e1ea 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -143,7 +143,7 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj) int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) { struct iommu_vdevice_alloc *cmd = ucmd->cmd; - struct iommufd_vdevice *vdev, *curr; + struct iommufd_vdevice *vdev; size_t vdev_size = sizeof(*vdev); struct iommufd_viommu *viommu; struct iommufd_device *idev; @@ -218,18 +218,28 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) */ idev->vdev = vdev; - curr = xa_cmpxchg(&viommu->vdevs, virt_id, NULL, vdev, GFP_KERNEL); - if (curr) { - rc = xa_err(curr) ?: -EEXIST; + /* + * Reserve the slot with a zero entry (reads back as NULL) until the + * vdevice_init() op accepts the vDEVICE. Only the xa_* helpers hide a + * reserved entry, so never use a raw xas_* iterator on this xarray. + */ + rc = xa_insert(&viommu->vdevs, virt_id, NULL, GFP_KERNEL); + if (rc) { + if (rc == -EBUSY) + rc = -EEXIST; goto out_abort; } if (viommu->ops && viommu->ops->vdevice_init) { rc = viommu->ops->vdevice_init(vdev); - if (rc) + if (rc) { + xa_release(&viommu->vdevs, virt_id); goto out_abort; + } } + xa_store(&viommu->vdevs, virt_id, vdev, GFP_KERNEL); + cmd->out_vdevice_id = vdev->obj.id; rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); if (rc) From 564ac339c0f8bada4e77a57a92bab9d3df635e07 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Fri, 10 Jul 2026 20:29:52 +0800 Subject: [PATCH 3062/3297] iommufd: Fix wrong hwpt passed to iommufd_auto_response_faults on replace commit ba5c0f28a26e7d9be1e0997f8920dd638e2782fd upstream. iommufd_hwpt_replace_device() calls: iommufd_auto_response_faults(hwpt, old_handle); passing the *new* hwpt together with the handle of the device's *old* domain. This should be a parameter mismatch: 1. Semantically, iommufd_auto_response_faults(x, handle) scans x->fault's deliver list and response xarray for groups matching "handle". A group is queued under the hwpt that was attached at fault-delivery time. old_handle is fetched *before* the domain switch, so its group lives on old->fault, not on the new hwpt->fault. 2. Historically, the first argument was "old". The routine was introduced by commit b7d8833677ba ("iommufd: Fault-capable hwpt attach/detach/replace") as __fault_domain_replace_dev() in fault.c, correctly calling iommufd_auto_response_faults(old, curr). Commit fb21b1568ada ("iommufd: Make attach_handle generic than fault specific") moved this into iommufd_hwpt_replace_device() in device.c and swapped it to "hwpt". This should be a refactor regression, not an intentional change. Fix this by passing "old" instead. Link: https://patch.msgid.link/r/9D652384339C69D5+20260710122952.885325-1-peiyang_he@smail.nju.edu.cn Fixes: fb21b1568ada ("iommufd: Make attach_handle generic than fault specific") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/iommufd/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 4c842368289f0..5c7ead633edd3 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -589,7 +589,7 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev, if (rc) goto out_free_handle; - iommufd_auto_response_faults(hwpt, old_handle); + iommufd_auto_response_faults(old, old_handle); kfree(old_handle); return 0; From 6da8f37419dd4c456f26fc203f04e000186f4b3d Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Mon, 20 Jul 2026 16:50:16 +0800 Subject: [PATCH 3063/3297] iommu/iommufd: Fix IOPF group ownership UAF commit 738e6f32e61d80b554e37015ecb7bc620b88001c upstream. iopf_group_alloc() links each last-page IOPF group into the generic IOPF pending list before invoking the domain fault handler. iommufd_fault_iopf_handler() also queued an accepted group in the IOMMUFD deliver list without removing it from the generic pending list. When detach or HWPT replacement drops the device's IOPF reference count to zero, an IOMMU driver may call iopf_queue_remove_device(). That function responds to and frees groups through the generic pending list without removing the same groups from IOMMUFD's deliver list or response xarray. A later read, response, or cleanup can then access the freed group and cause a UAF. Fix this by dequeuing an accepted group from the generic pending list before IOMMUFD queues it for userspace response. Make iopf_group_response() send a response regardless of pending-list membership, so the dequeued group can still be completed by IOMMUFD. Link: https://patch.msgid.link/r/3CFD314D0FE4D7EC+20260720085017.3998878-2-peiyang_he@smail.nju.edu.cn Closes: https://lore.kernel.org/all/B4F28798E2E784CA+d29f723c-b2b5-4b67-8d1c-4f7b9b0b27cb@smail.nju.edu.cn/ Fixes: 34765cbc679c ("iommufd: Associate fault object with iommufd_hw_pgtable") Cc: stable@vger.kernel.org Tested-by: Peiyang He Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/io-pgfault.c | 24 +++++++++++++++++++----- drivers/iommu/iommufd/eventq.c | 2 ++ include/linux/iommu.h | 5 +++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c index 8b5926c1452ed..afe977235d72f 100644 --- a/drivers/iommu/io-pgfault.c +++ b/drivers/iommu/io-pgfault.c @@ -332,16 +332,30 @@ void iopf_group_response(struct iopf_group *group, .code = status, }; - /* Only send response if there is a fault report pending */ mutex_lock(&fault_param->lock); - if (!list_empty(&group->pending_node)) { - ops->page_response(dev, &group->last_fault, &resp); - list_del_init(&group->pending_node); - } + ops->page_response(dev, &group->last_fault, &resp); + list_del_init(&group->pending_node); mutex_unlock(&fault_param->lock); } EXPORT_SYMBOL_GPL(iopf_group_response); +/** + * iopf_group_dequeue - Dequeue a page fault group from the pending list + * @group: the group to dequeue + * + * The fault handler is responsible for responding to the group after + * this function returns. + */ +void iopf_group_dequeue(struct iopf_group *group) +{ + struct iommu_fault_param *fault_param = group->fault_param; + + mutex_lock(&fault_param->lock); + list_del_init(&group->pending_node); + mutex_unlock(&fault_param->lock); +} +EXPORT_SYMBOL_GPL(iopf_group_dequeue); + /** * iopf_queue_discard_partial - Remove all pending partial fault * @queue: the queue whose partial faults need to be discarded diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index a934f02528b0b..8971360c955ae 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -484,6 +484,8 @@ int iommufd_fault_iopf_handler(struct iopf_group *group) hwpt = group->attach_handle->domain->iommufd_hwpt; fault = hwpt->fault; + iopf_group_dequeue(group); + spin_lock(&fault->common.lock); list_add_tail(&group->node, &fault->common.deliver); spin_unlock(&fault->common.lock); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 84cf41f51f05d..6e51f5a48c74e 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -1657,6 +1657,7 @@ void iopf_free_group(struct iopf_group *group); int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt); void iopf_group_response(struct iopf_group *group, enum iommu_page_response_code status); +void iopf_group_dequeue(struct iopf_group *group); #else static inline int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev) @@ -1702,5 +1703,9 @@ static inline void iopf_group_response(struct iopf_group *group, enum iommu_page_response_code status) { } + +static inline void iopf_group_dequeue(struct iopf_group *group) +{ +} #endif /* CONFIG_IOMMU_IOPF */ #endif /* __LINUX_IOMMU_H */ From 93d934668047f9457e3aaae0a1b118864e429cff Mon Sep 17 00:00:00 2001 From: Benjamin Boortz Date: Sun, 19 Jul 2026 11:41:46 +0200 Subject: [PATCH 3064/3297] pinctrl: microchip-sgpio: add missing select REGMAP_MMIO commit 25cb6e9a13123d1039cdc75b446ac52e1ebdc26d upstream. The driver calls ocelot_regmap_from_resource() via , which internally uses devm_regmap_init_mmio() and requires REGMAP_MMIO. The Kconfig entry does not select REGMAP_MMIO, causing a build failure when no other driver in the config happens to pull in REGMAP_MMIO: include/linux/mfd/ocelot.h:34:24: error: implicit declaration of function 'devm_regmap_init_mmio' Found by randconfig testing on arm64; tinyconfig reproducer below. Fixes: 2afbbab45c26 ("pinctrl: microchip-sgpio: update to support regmap") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Boortz Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 4f8507ebbdacd..cd3960e38dcec 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -414,6 +414,7 @@ config PINCTRL_MICROCHIP_SGPIO select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS select OF_GPIO + select REGMAP_MMIO help Support for the serial GPIO interface used on Microsemi and Microchip SoCs. By using a serial interface, the SIO From ad0ad3c228b6f76fde10f32047e0ec5fbc109dc8 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sun, 19 Jul 2026 14:11:40 +0200 Subject: [PATCH 3065/3297] pinctrl: devicetree: don't free uninitialized dev_name on error path commit 015b5bcbcb622b32317642be91a7f79aa5413649 upstream. dt_remember_or_free_map() duplicates dev_name for each map entry. If kstrdup_const() fails, dt_free_map() frees dev_name in all num_maps entries, including entries that have not been initialized. Some pinctrl drivers, including pinctrl-imx, allocate the map with kmalloc() and leave dev_name for the core to initialize. The untouched entries therefore contain uninitialized data which is passed to kfree_const(). Reproduced on qemu's mcimx6ul-evk (pinctrl-imx) with failslab injection while binding the pinctrl-consuming device, under KASAN: BUG: KASAN: double-free in dt_free_map+0x34/0xa4 Free of addr c425a900 by task init/1 kfree from dt_free_map+0x34/0xa4 dt_free_map from dt_remember_or_free_map+0x184/0x198 dt_remember_or_free_map from pinctrl_dt_to_map+0x33c/0x4c8 pinctrl_dt_to_map from create_pinctrl+0x9c/0x5c0 Initialize all dev_name fields to NULL before duplicating the device name, making the full-map cleanup safe after a partial failure. Fixes: be4c60b563ed ("pinctrl: devicetree: Avoid taking direct reference to device name string") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-fable-5 Signed-off-by: Karl Mehltretter Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/devicetree.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 0b7f74beb6a6a..fdb5ff0e4b0be 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -69,6 +69,10 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename, int i; struct pinctrl_dt_map *dt_map; + /* Initialize dev_name before any allocation can fail */ + for (i = 0; i < num_maps; i++) + map[i].dev_name = NULL; + /* Initialize common mapping table entry fields */ for (i = 0; i < num_maps; i++) { const char *devname; From 5aaa06dfc10f8398c8807453dbec738ea9af10e4 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 14 Jul 2026 07:47:29 -0400 Subject: [PATCH 3066/3297] erofs: cap LZMA stream pool size commit c9b47e6b23114e939b17f818471c7a46e59006e7 upstream. fs/erofs/decompressor_lzma.c sizes the module-global MicroLZMA stream pool from num_possible_cpus() when the lzma_streams module parameter is unset, then z_erofs_load_lzma_config() preallocates one image-supplied dictionary per stream, accepting dictionaries up to 8 MiB. On high-CPU systems, a small EROFS image can pin hundreds of MiB of vmalloc-backed decoder state until the erofs module is unloaded. Impact: An EROFS image mounted by the system can pin up to 8 MiB of vmalloc memory per LZMA stream, either as intended or unexpectedly. Bound the default stream count by a new CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS option, default 16, so the worst-case default preallocation is 128 MiB if the number of CPUs is no less than 16 while preserving the existing per-image dictionary limit. An explicit lzma_streams module parameter is still honoured as-is, so administrators who deliberately size the pool are not affected. Fixes: 622ceaddb764 ("erofs: lzma compression support") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Gao Xiang Signed-off-by: Gao Xiang Signed-off-by: Greg Kroah-Hartman --- fs/erofs/Kconfig | 14 ++++++++++++++ fs/erofs/decompressor_lzma.c | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index d81f3318417df..0d199f6698fff 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -120,6 +120,20 @@ config EROFS_FS_ZIP_LZMA If unsure, say N. +config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS + int "EROFS LZMA default maximum decompression streams" + depends on EROFS_FS_ZIP_LZMA + range 1 NR_CPUS + default 16 + help + By default EROFS allocates one LZMA decompression stream per CPU. + Each stream can hold a dictionary of up to 8 MiB taken from the + mounted image, so on systems with many CPUs this can reserve a lot + of memory. This caps the default; the lzma_streams module parameter + still overrides it. + + If unsure, keep the default of 16. + config EROFS_FS_ZIP_DEFLATE bool "EROFS DEFLATE compressed data support" depends on EROFS_FS_ZIP diff --git a/fs/erofs/decompressor_lzma.c b/fs/erofs/decompressor_lzma.c index 832cffb83a662..90bdc86381999 100644 --- a/fs/erofs/decompressor_lzma.c +++ b/fs/erofs/decompressor_lzma.c @@ -51,7 +51,8 @@ static int __init z_erofs_lzma_init(void) /* by default, use # of possible CPUs instead */ if (!z_erofs_lzma_nstrms) - z_erofs_lzma_nstrms = num_possible_cpus(); + z_erofs_lzma_nstrms = min_t(unsigned int, num_possible_cpus(), + CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS); for (i = 0; i < z_erofs_lzma_nstrms; ++i) { struct z_erofs_lzma *strm = kzalloc(sizeof(*strm), GFP_KERNEL); From 96b0aa79b0e1e49f557b190d3fff1fd59bec72dc Mon Sep 17 00:00:00 2001 From: Benjamin Boortz Date: Mon, 20 Jul 2026 19:51:04 +0200 Subject: [PATCH 3067/3297] pinctrl: bm1880: add missing select GENERIC_PINCONF commit dad6e107b3cd9d20514e7799b7ad8674f81e3f30 upstream. drivers/pinctrl/pinctrl-bm1880.c initialises its pinconf_ops with .is_generic = true, but that field is only present when CONFIG_GENERIC_PINCONF is enabled (guarded by #ifdef in pinconf.h). The Kconfig entry for PINCTRL_BM1880 never selects GENERIC_PINCONF, so any config that enables CONFIG_PINCTRL_BM1880=y without CONFIG_GENERIC_PINCONF=y fails to compile: drivers/pinctrl/pinctrl-bm1880.c:1288:10: error: 'const struct pinconf_ops' has no member named 'is_generic' Found by randconfig testing on arm64; tinyconfig reproducer below. Add the missing select to fix the build. Fixes: 49bd61ebce5f ("pinctrl: Add pinconf support for BM1880 SoC") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Boortz Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index cd3960e38dcec..80db4195a5593 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -162,6 +162,7 @@ config PINCTRL_BM1880 depends on OF && (ARCH_BITMAIN || COMPILE_TEST) default ARCH_BITMAIN select PINMUX + select GENERIC_PINCONF help Pinctrl driver for Bitmain BM1880 SoC. From 2205263b1e013a82ac0e6abf899968ff2889fc1d Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 23 Jun 2026 13:23:46 -0700 Subject: [PATCH 3068/3297] fortify: Disable -Wstringop-overread in tests commit c1f3e770eec26d6f96dd6d2ea30555ba7c09a244 upstream. clang recently added support for -Wstringop-overread [1], which is on by default like -Wfortify-source. This breaks the usage of -Werror in the fortify tests, resulting in the following false positive warnings in the kernel build: warning: unsafe memcmp() usage lacked '__read_overflow2' warning in lib/test_fortify/read_overflow2-memcmp.c warning: unsafe memcmp() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memcmp.c warning: unsafe memchr() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memchr.c Examining the fortify test logs shows a warning like the following in each of the failed logs: In file included from lib/test_fortify/read_overflow2-memcmp.c:5: lib/test_fortify/test_fortify.h:34:2: error: 'memcmp' reading 17 bytes from a region of size 16 [-Werror,-Wstringop-overread] 34 | TEST; | ^ lib/test_fortify/read_overflow2-memcmp.c:3:2: note: expanded from macro 'TEST' 3 | memcmp(large, small, sizeof(small) + 1) | ^ 1 error generated. Disable -Wstringop-overread for the fortify tests, as it defeats the purpose of testing the Linux specific implementation of fortify, like -Wfortify-source. Cc: stable@vger.kernel.org Closes: https://github.com/ClangBuiltLinux/linux/issues/2168 Link: https://github.com/llvm/llvm-project/commit/86f2e71cb8d165b59ad31a442b2391e23826133e [1] Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20260623-fix-test_fortify-for-clang-stringop-overread-v1-1-15ee8342a953@kernel.org Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- lib/test_fortify/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/test_fortify/Makefile b/lib/test_fortify/Makefile index 399cae880e1dc..44cd5df41a810 100644 --- a/lib/test_fortify/Makefile +++ b/lib/test_fortify/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 ccflags-y := $(call cc-disable-warning,fortify-source) +ccflags-y += $(call cc-disable-warning,stringop-overread) quiet_cmd_test_fortify = TEST $@ cmd_test_fortify = $(CONFIG_SHELL) $(src)/test_fortify.sh \ From 2be94d6b20789b8865b1864dae5fe458ca85e019 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Mon, 6 Jul 2026 19:19:58 +0800 Subject: [PATCH 3069/3297] mm: migrate_device: fix pte_pfn/pte_dirty called on non-present PTE commit 63867c82d0c0c2d182016a32b1cc0103116b0ea5 upstream. pte_pfn() and pte_dirty() have undefined behaviour when called on a non-present PTE. In migrate_vma_collect_pmd(), these functions may be invoked on non-present entries (e.g., device-private entries), leading to potential crashes from pte_pfn() or incorrect dirty folio accounting from pte_dirty(). Fix both by guarding with pte_present() checks. Link: https://lore.kernel.org/20260708003955.4024340-1-wangkefeng.wang@huawei.com Link: https://lore.kernel.org/20260706111958.3649651-1-wangkefeng.wang@huawei.com Fixes: fd35ca3d12cc ("mm/migrate_device.c: copy pte dirty bit to page") Fixes: 6c287605fd56 ("mm: remember exclusively mapped anonymous pages with PG_anon_exclusive") Signed-off-by: Kefeng Wang Reviewed-by: Balbir Singh Acked-by: Zi Yan Cc: Alistair Popple Cc: Byungchul Park Cc: David Hildenbrand Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Matthew Brost Cc: Rakie Kim Cc: Ying Huang Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/migrate_device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/migrate_device.c b/mm/migrate_device.c index abd9f6850db65..4abceeae55ab0 100644 --- a/mm/migrate_device.c +++ b/mm/migrate_device.c @@ -209,7 +209,8 @@ static int migrate_vma_collect_pmd(pmd_t *pmdp, bool anon_exclusive; pte_t swp_pte; - flush_cache_page(vma, addr, pte_pfn(pte)); + if (pte_present(pte)) + flush_cache_page(vma, addr, pte_pfn(pte)); anon_exclusive = folio_test_anon(folio) && PageAnonExclusive(page); if (anon_exclusive) { @@ -230,7 +231,7 @@ static int migrate_vma_collect_pmd(pmd_t *pmdp, migrate->cpages++; /* Set the dirty flag on the folio now the pte is gone. */ - if (pte_dirty(pte)) + if (pte_present(pte) && pte_dirty(pte)) folio_mark_dirty(folio); /* Setup special migration page table entry */ From 9668ffe0e2a5e2399dce281620198a2e415871fc Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Thu, 9 Jul 2026 01:49:54 +0530 Subject: [PATCH 3070/3297] mm/util: don't read __page_2 for order-1 folios in snapshot_page() commit 7441d6348c70738e9ed307510db171c7a9b3f4bf upstream. snapshot_page() currently reads __page_2 after checking nr_pages > 1, but it should only do so when nr_pages > 2. If an order-1 folio is allocated at the end of a vmemmap section, __page_2 will not exist and reading it will cause a fault. During DLPAR memory remove on a 22 TB ppc64le LPAR, snapshot_page() oopsed on the page isolation path while reading an order-1 folio's __page_2 from an adjacent absent section (unmapped vmemmap). Fix this to avoid reading memmap that doesn't exist (e.g., a vmemmap hole). Link: https://lore.kernel.org/20260708201954.686111-1-aboorvad@linux.ibm.com Fixes: 31a31da8a618 ("mm: move _pincount in folio to page[2] on 32bit") Signed-off-by: Aboorva Devarajan Reported-by: Sourabh Jain Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Reviewed-by: Matthew Wilcox (Oracle) Reviewed-by: Luiz Capitulino Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: "Ritesh Harjani (IBM)" Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: # v6.15+ Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/util.c b/mm/util.c index 8989d57675286..31a59c038ba0a 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1263,7 +1263,7 @@ void snapshot_page(struct page_snapshot *ps, const struct page *page) if (ps->idx < MAX_FOLIO_NR_PAGES) { memcpy(&ps->folio_snapshot, foliop, 2 * sizeof(struct page)); nr_pages = folio_nr_pages(&ps->folio_snapshot); - if (nr_pages > 1) + if (nr_pages > 2) memcpy(&ps->folio_snapshot.__page_2, &foliop->__page_2, sizeof(struct page)); set_ps_flags(ps, foliop, page); From c091462e46f75ef1e1a22c7f605fe8f8b72d2a0c Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Fri, 10 Jul 2026 19:17:35 +0200 Subject: [PATCH 3071/3297] selftest: fix headers in fclog.c commit e3a0127eee04db8769e53c8102c4e76aa49be8c3 upstream. fclog.c does not compile because it is missing fcntl.h, needed for O_RDONLY etc. There are also some redundant includes that are also in kselftest_harness.h. Link: https://lore.kernel.org/20260710171741.837308-1-jkoolstra@xs4all.nl Signed-off-by: Jori Koolstra Cc: Aleksa Sarai Cc: Shuah Khan Cc: Wei Yang Cc: Christian Brauner Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/filesystems/fclog.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/testing/selftests/filesystems/fclog.c b/tools/testing/selftests/filesystems/fclog.c index 912a8b755c3b6..3e6fb609ef56a 100644 --- a/tools/testing/selftests/filesystems/fclog.c +++ b/tools/testing/selftests/filesystems/fclog.c @@ -6,10 +6,8 @@ #include #include +#include #include -#include -#include -#include #include #include From 7d3e1d3a0dce9d842288e50294839a611454d0e9 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Wed, 15 Jul 2026 15:42:33 +0100 Subject: [PATCH 3072/3297] fs/proc/task_mmu: fix PAGEMAP_SCAN written state for PMD holes commit 40de8160ca7f67d14619ee0351ce5d68fc4a237a upstream. PAGEMAP_SCAN reports an unpopulated PTE in a uffd-wp VMA as written, but a range with no page table at all -- a PMD hole -- is skipped: pagemap_scan_pte_hole() tests p->cur_vma_category, which never carries PAGE_IS_WRITTEN, so the hole is neither reported nor (under PM_SCAN_WP_MATCHING) armed. In a uffd-wp VMA, WP_UNPOPULATED installs uffd-wp markers when protecting a range, allocating page tables as needed, so an unpopulated slot is treated as written -- see the pte_none() handling in pagemap_page_category(). A missing marker therefore means the range was zapped, e.g. via MADV_DONTNEED. This applies to anon and shmem VMAs. An anonymous THP is write-protected in place as a huge PMD, so a full-PMD MADV_DONTNEED clears it to pmd_none -- a hole with no page table -- and pagemap_scan_pte_hole() misses it. For a MAP_PRIVATE|MAP_ANON mapping MADV_DONTNEED has fill-with-zeros semantics, so a write-tracking checkpoint/migration tool (e.g. CRIU) treats the range as unchanged and keeps its previous contents; after restore or live migration the process reads stale data instead of zeroes -- data corruption. Report a hole in a non-hugetlb uffd-wp VMA as written, matching the pte_none handling in pagemap_page_category(); the existing PM_SCAN_WP_MATCHING path then arms it via uffd_wp_range(). hugetlb is excluded: pagemap_hugetlb_category() reports an empty hugetlb entry (huge_pte_none) as not-written, unlike pagemap_page_category(), which reports pte_none as written. pagemap_scan_pte_hole() fires for a hugetlb slot only when it has no page table; keeping that not-written matches how an allocated-but-empty hugetlb entry reads, so the hole and the empty-entry cases agree within the VMA. Link: https://lore.kernel.org/20260715144234.442721-2-kirill@shutemov.name Fixes: 2bad466cc9d9 ("mm/uffd: UFFD_FEATURE_WP_UNPOPULATED") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Closes: https://sashiko.dev/#/patchset/20260707151349.92143-1-kirill@shutemov.name Tested-by: Muhammad Usama Anjum Acked-by: David Hildenbrand (Arm) Cc: Peter Xu Cc: Jann Horn Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zenghui Yu Assisted-by: Claude:claude-fable-5 Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 858b11009c269..0cec16f9e9afd 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2918,12 +2918,28 @@ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, { struct pagemap_scan_private *p = walk->private; struct vm_area_struct *vma = walk->vma; + unsigned long categories; int ret, err; - if (!vma || !pagemap_scan_is_interesting_page(p->cur_vma_category, p)) + if (!vma) + return 0; + + /* + * In a uffd-wp VMA an unpopulated range is treated as written: + * uffd-wp registration populates page tables and installs markers + * with WP_UNPOPULATED, so a missing marker means the range was + * zapped. See the pte_none() handling in pagemap_page_category(). + * + * hugetlb differs, see pagemap_hugetlb_category(). + */ + categories = p->cur_vma_category; + if (userfaultfd_wp(vma) && !is_vm_hugetlb_page(vma)) + categories |= PAGE_IS_WRITTEN; + + if (!pagemap_scan_is_interesting_page(categories, p)) return 0; - ret = pagemap_scan_output(p->cur_vma_category, p, addr, &end); + ret = pagemap_scan_output(categories, p, addr, &end); if (addr == end) return ret; From 32134cf9211b83bed9076d0739c5906fbea4c763 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Thu, 9 Jul 2026 15:12:01 -0400 Subject: [PATCH 3073/3297] mm/percpu-km: fix bitmap overflow and accounting in pcpu_create_chunk() commit 89b1b79c308818a715e75f28744b70d8940a07c9 upstream. In pcpu_create_chunk(), nr_pages is the total contiguous backing allocation, i.e., nr_units * pcpu_unit_pages, but pcpu_chunk_populated() uses it to set chunk->populated, whose size is pcpu_unit_pages, bitmap. Since bit N in chunk->populated means page offset N inside every unit is backed. When nr_units > 1, the function writes beyond chunk->populated. Fix it by using chunk->nr_pages. It also fixes the global pcpu_nr_empty_pop_pages accounting, since pcpu_balance_free() only iterates up to chunk->nr_pages. Commit a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap properly") introduced the bitmap overflow issue. Later, commit b539b87fed37f ("percpu: implmeent pcpu_nr_empty_pop_pages and chunk->nr_populated") added pcpu_nr_empty_pop_pages and caused the accounting issue. Link: https://lore.kernel.org/20260709-fix-pcpu_create_chunk-in-percpu-km-v1-1-1f64745a84cc@nvidia.com Fixes: a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap properly") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260703-keep-subpage-private-zero-at-free-v2-0-2970fe777dd6%40nvidia.com?part=1 Assisted-by: Codex:GPT-5 Signed-off-by: Zi Yan Acked-by: Dennis Zhou Cc: Christoph Lameter Cc: Tejun Heo Cc: Zi Yan Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/percpu-km.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/percpu-km.c b/mm/percpu-km.c index 4efa74a495cb6..dc096b5a6ce40 100644 --- a/mm/percpu-km.c +++ b/mm/percpu-km.c @@ -75,7 +75,7 @@ static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp) chunk->base_addr = page_address(pages); spin_lock_irqsave(&pcpu_lock, flags); - pcpu_chunk_populated(chunk, 0, nr_pages); + pcpu_chunk_populated(chunk, 0, chunk->nr_pages); spin_unlock_irqrestore(&pcpu_lock, flags); pcpu_stats_chunk_alloc(); From 126a70bf1a08ddc9d79c471ebdaa2b08cfbab8df Mon Sep 17 00:00:00 2001 From: Xiangfeng Cai Date: Tue, 14 Jul 2026 01:14:55 +0800 Subject: [PATCH 3074/3297] mm/hugetlb: fix list corruption in allocate_file_region_entries() commit dd9623f58ec702a07b2d67179d6fcea79c52231a upstream. allocate_file_region_entries() tops up resv->region_cache with freshly allocated file_region descriptors. The allocation uses GFP_KERNEL, so resv->lock is dropped around it: the new entries are gathered on a stack-local list head, allocated_regions, and spliced into resv->region_cache once the lock is re-acquired. The splice used list_splice(), which moves the entries but does not re-initialize the source head, so allocated_regions is left pointing at an entry that now lives on resv->region_cache. The top-up runs in a while loop that re-checks the cache deficit after re-acquiring the lock. For a shared mapping the resv_map is shared by every mapper of the hugetlbfs inode, so a concurrent region_chg()/region_add()/region_del() on the same resv_map can consume cache entries during the unlocked window and force a second iteration. That iteration calls list_add() on the stale head and corrupts the list; with CONFIG_DEBUG_LIST the __list_add_valid() check trips: list_add corruption. next->prev should be prev (ffffc900011ff7f8), but was ffff88814c281460. (next=ffff88814c545640). kernel BUG at lib/list_debug.c:31! allocate_file_region_entries+0x191/0x420 region_chg+0x267/0x300 hugetlb_reserve_pages+0x387/0xc80 hugetlbfs_file_mmap+0x2ce/0x3f0 mmap_region+0x1348/0x1a80 do_mmap+0x85e/0xb90 vm_mmap_pgoff+0x18c/0x330 ksys_mmap_pgoff+0x2a1/0x3e0 do_syscall_64+0xd7/0x420 Without CONFIG_DEBUG_LIST the bad list_add() silently links a kernel-stack address into resv->region_cache, leading to later use-after-free. This was observed as a real host panic on a dense KVM host where a QEMU guest-RAM hugetlbfs file was mapped MAP_SHARED by both QEMU and a separate SPDK/DPDK vhost-user target, generating concurrent region_* traffic on one shared resv_map. Use list_splice_init() so the source head is re-initialized empty after each splice, making the retry loop safe. Link: https://lore.kernel.org/20260713171456.300518-2-caixiangfeng@bytedance.com Fixes: d3ec7b6e09e5 ("mm/hugetlb: use list_splice to merge two list at once") Signed-off-by: Xiangfeng Cai Reviewed-by: Muchun Song Cc: Baoquan He Cc: David Hildenbrand Cc: Oscar Salvador Cc: Shuah Khan Cc: Wei Yang Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/hugetlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index e0b36a634eb62..0b3fd6943836e 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -705,7 +705,7 @@ static int allocate_file_region_entries(struct resv_map *resv, spin_lock(&resv->lock); - list_splice(&allocated_regions, &resv->region_cache); + list_splice_init(&allocated_regions, &resv->region_cache); resv->region_cache_count += to_allocate; } From e0fa737783b5b7c25f4f497be12670681878a060 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Sat, 27 Jun 2026 16:22:43 -0400 Subject: [PATCH 3075/3297] mm/vmstat: fold stranded per-cpu node stats when a node comes online commit ea3034b2b00fa50c8d2518d0804c9d427bbafa86 upstream. A per-node vmstat counter is pgdat->vm_stat[] plus per-cpu deltas. A balanced counter can sit split as global=+N / per-cpu=-N. The folds reconciling the split only walk online nodes, so when try_offline_node() marks a node offline the per-cpu deltas are stranded. A subsequent online resets the per-cpu area but not pgdat->vm_stat[], orphaning the +N permanently. All NR_VM_NODE_STAT_ITEMS are affected. The existing code zeroes the per-cpu counters and causes a permanent skew. Fold the stranded deltas instead, before the node rejoins the online set. The node is not online yet and the hotplug lock is held, so the remote access to per-cpu values is safe. Discovered when node compaction hung for a nearly empty node, as the math to determine throttling broke. Reproduced by repeated memory hotplug/unplug cycles on a node under pressure: NR_ISOLATED_ANON ratchets up and never returns to zero. Link: https://lore.kernel.org/20260627202243.758289-1-gourry@gourry.net Fixes: 75ef71840539 ("mm, vmstat: add infrastructure for per-node vmstats") Signed-off-by: Gregory Price Cc: Johannes Weiner Cc: Mel Gorman Cc: Mike Rapoport Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/mm_init.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 1b977fa9e8bee..d2d915984611a 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1565,7 +1565,7 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) { int nid = pgdat->node_id; enum zone_type z; - int cpu; + int cpu, i; pgdat_init_internals(pgdat); @@ -1583,10 +1583,17 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) pgdat->node_start_pfn = 0; pgdat->node_present_pages = 0; - for_each_online_cpu(cpu) { - struct per_cpu_nodestat *p; + /* + * Hot-unplug can leave per-cpu vmstat deltas unfolded (folders skip + * offline nodes) - reconcile this at online. Foreign access to counters + * is safe: the node is not online yet and we hold the hotplug lock. + */ + for_each_possible_cpu(cpu) { + struct per_cpu_nodestat *p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu); - p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu); + for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) + if (p->vm_node_stat_diff[i]) + node_page_state_add(p->vm_node_stat_diff[i], pgdat, i); memset(p, 0, sizeof(*p)); } From 3b2e08e0ede729277455ca7e972ceedd20956b21 Mon Sep 17 00:00:00 2001 From: Raushan Patel Date: Fri, 24 Jul 2026 11:14:35 +0530 Subject: [PATCH 3076/3297] tracing/probes: Reject $arg0 in meta argument expansion commit 00a8ce2a2a9fa17674e1feec4d9105c1a5d6a419 upstream. traceprobe_expand_meta_args() parses $argN with simple_strtoul() and calls sprint_nth_btf_arg(n - 1, ...). For $arg0, n is 0 so the index is -1. Because ctx->nr_params is signed, the "idx >= nr_params" guard in sprint_nth_btf_arg() does not catch the negative index, and ctx->params[-1].name_off is read out of bounds. The normal per-argument path (parse_probe_vars()) already rejects $arg0 via its argument-number check, but meta-argument expansion runs before per-argument parsing and substitutes the value first, bypassing that check. Reject $arg0 explicitly during expansion. Link: https://lore.kernel.org/all/20260724054435.146279-1-raushan.jhon@gmail.com/ Fixes: 18b1e870a496 ("tracing/probes: Add $arg* meta argument for all function args") Cc: stable@vger.kernel.org Signed-off-by: Raushan Patel Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_probe.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 918fafc5d45c5..8e1df280d3114 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -1906,7 +1906,11 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], trace_probe_log_err(0, BAD_VAR); return ERR_PTR(-ENOENT); } - /* Note: $argN starts from $arg1 */ + /* Note: $argN starts from $arg1, so $arg0 is invalid. */ + if (n == 0) { + trace_probe_log_err(0, BAD_ARG_NUM); + return ERR_PTR(-EINVAL); + } ret = sprint_nth_btf_arg(n - 1, type, buf + used, bufsize - used, ctx); if (ret < 0) From e768ea3a422d1304a0588e7dc704ce739c2944a5 Mon Sep 17 00:00:00 2001 From: Raushan Patel Date: Fri, 24 Jul 2026 12:12:08 +0530 Subject: [PATCH 3077/3297] tracing/fprobe: Roll back on enable_trace_fprobe() failure commit aca0cd1bf16574327ef64f3178e5f5e37a61ef0b upstream. enable_trace_fprobe() sets the file link or the TP_FLAG_PROFILE flag and then registers each trace_fprobe in the probe list. If __register_trace_fprobe() fails partway through, the function returns immediately without unregistering the trace_fprobes it already registered or undoing the file link / flag it set, leaving the event half-enabled and leaking the registered fprobe(s). enable_trace_kprobe() already handles this with a rollback path. Do the same for fprobe: on failure, unregister all probes and clear the file link or profile flag. Link: https://lore.kernel.org/all/20260724064208.480030-1-raushan.jhon@gmail.com/ Fixes: 334e5519c375 ("tracing/probes: Add fprobe events for tracing function entry and exit.") Cc: stable@vger.kernel.org Signed-off-by: Raushan Patel Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_fprobe.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index 8001dbf168919..f7eb875811bea 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -1481,11 +1481,21 @@ static int enable_trace_fprobe(struct trace_event_call *call, list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { ret = __register_trace_fprobe(tf); if (ret < 0) - return ret; + goto err; } } return 0; + +err: + /* Failed to enable one of them. Roll back all */ + list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) + __unregister_trace_fprobe(tf); + if (file) + trace_probe_remove_file(tp, file); + else + trace_probe_clear_flag(tp, TP_FLAG_PROFILE); + return ret; } /* From 5acc92947baa21b05a6efaf36887efb17c0f7914 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 21 Jul 2026 18:31:49 +0200 Subject: [PATCH 3078/3297] KVM: VMX: add memory clobber to asm for VMX instructions commit 0e65cd9e5d41c34f86b7c347967bedac54926041 upstream. VMCLEAR/VMREAD/VMWRITE/VMPTRLD access the internal VMCS cache, which is not visible to the compiler; without a memory clobber, the compiler can reorder them in troublesome ways because "asm volatile" and "asm goto" only protect against removal of the asm. For example, placing a VMWRITE before the corresponding VMCS pointer is loaded can lead to corruption. While none of this has been observed, it is better to prevent than cure. Likewise, INVEPT and INVVPID access the TLB and, even though in their case the effect is only visible to the next VMLAUNCH/VMRESUME, it is technically correct to add the clobber there too. So avoid any urge to special case them, and simply hardcode "memory" into the clobber list of vmx_asm1() and vmx_asm2(). __vmcs_readl() open-codes its own asm, so add the clobber there as well. Link: https://lore.kernel.org/kvm/CABgObfbL3t21yVeSwiLSjjOUER+rTYDPHYAH9YU4TWGRjx6XHg@mail.gmail.com/ Cc: Sean Christopherson Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx/vmx_ops.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx_ops.h b/arch/x86/kvm/vmx/vmx_ops.h index 96677576c836d..e131c7b555dcf 100644 --- a/arch/x86/kvm/vmx/vmx_ops.h +++ b/arch/x86/kvm/vmx/vmx_ops.h @@ -101,7 +101,7 @@ static __always_inline unsigned long __vmcs_readl(unsigned long field) : [output] "=r" (value) : [field] "r" (field) - : "cc" + : "cc", "memory" : do_fail, do_exception); return value; @@ -146,7 +146,7 @@ static __always_inline unsigned long __vmcs_readl(unsigned long field) : ASM_CALL_CONSTRAINT, [output] "=&r" (value) : [field] "r" (field) - : "cc"); + : "cc", "memory"); return value; #endif /* CONFIG_CC_HAS_ASM_GOTO_OUTPUT */ @@ -194,7 +194,7 @@ do { \ ".byte 0x2e\n\t" /* branch not taken hint */ \ "jna %l[error]\n\t" \ _ASM_EXTABLE(1b, %l[fault]) \ - : : op1 : "cc" : error, fault); \ + : : op1 : "cc", "memory" : error, fault); \ return; \ error: \ instrumentation_begin(); \ @@ -211,7 +211,7 @@ do { \ ".byte 0x2e\n\t" /* branch not taken hint */ \ "jna %l[error]\n\t" \ _ASM_EXTABLE(1b, %l[fault]) \ - : : op1, op2 : "cc" : error, fault); \ + : : op1, op2 : "cc", "memory" : error, fault);\ return; \ error: \ instrumentation_begin(); \ From 7668c58dcf465559dc7a0d2e95e9cb79cf47454b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 10 Jul 2026 09:20:51 -0700 Subject: [PATCH 3079/3297] KVM: SVM: Update x2APIC MSR intercepts if AVIC is inhibited while L2 is active commit 7d3aae206663c4e006b25a1c7a20a4029e67da76 upstream. Always update x2APIC MSR intercepts for L1 when AVIC is deactivated, even if L2 is active and KVM is using a separate MSR bitmap to run L2. If AVIC is fully enabled prior to running L2, and is then inhibited while L2 is active (for a VM-scoped inhibit), then KVM will run L1 with AVIC disabled, but with x2APIC MSR intercepts disabled, i.e. will allow L1 to read most of the host's APIC state, send arbitrary interrupts, change task priority, and ultimately trivially DoS the host. E.g. sending a self-IPI in L1 on HYPERV_REENLIGHTENMENT_VECTOR, 0xee, with CONFIG_HYPERV=n in the host kernel as a "safe" PoC, yields: Spurious interrupt (vector 0xee) on CPU#425. Acked And hacking KVM to abuse kvm_set_posted_intr_wakeup_handler() to register a handler and WARN on POSTED_INTR_WAKEUP_VECTOR yields: ------------[ cut here ]------------ WARNING: arch/x86/kvm/svm/svm.c:5594 at pi_wakeup_handler+0x9/0x10 [kvm_amd], CPU#156: nested_x2apic_t/316940 CPU: 156 UID: 0 PID: 316940 Comm: nested_x2apic_t Tainted: G S U Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER Hardware name: Google Astoria-Turin/astoria, BIOS 0.20260209.0-0 02/09/2026 RIP: 0010:pi_wakeup_handler+0x9/0x10 [kvm_amd] Call Trace: sysvec_kvm_posted_intr_wakeup_ipi+0x64/0x80 asm_sysvec_kvm_posted_intr_wakeup_ipi+0x1a/0x20 RIP: 0010:vcpu_run+0x1430/0x1e40 [kvm] kvm_arch_vcpu_ioctl_run+0x2c1/0x600 [kvm] kvm_vcpu_ioctl+0x580/0x6b0 [kvm] __se_sys_ioctl+0x6d/0xb0 do_syscall_64+0x10a/0x480 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x46ff4b ---[ end trace 0000000000000000 ]--- Fixes: 091abbf578f9 ("KVM: x86: nSVM: optimize svm_set_x2apic_msr_interception") Cc: stable@vger.kernel.org Cc: Yosry Ahmed Signed-off-by: Sean Christopherson Link: https://patch.msgid.link/20260729213558.639074-1-pbonzini@redhat.com/ Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/avic.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index a700f3795c176..62eb7b871a84d 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -220,14 +220,6 @@ static void avic_deactivate_vmcb(struct vcpu_svm *svm) if (!sev_es_guest(svm->vcpu.kvm)) svm_set_intercept(svm, INTERCEPT_CR8_WRITE); - /* - * If running nested and the guest uses its own MSR bitmap, there - * is no need to update L0's msr bitmap - */ - if (is_guest_mode(&svm->vcpu) && - vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT)) - return; - /* Enabling MSR intercept for x2APIC registers */ avic_set_x2apic_msr_interception(svm, true); } From 6837f0ae85fd54cf64c8a0c7c530bba2fae0a207 Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:04 -0700 Subject: [PATCH 3080/3297] KVM: s390: pci: Reject adapter interrupt forwarding if already enabled commit 8fa01be5a6149404adb82c0979a78f6347edd3ef upstream. The MPCIFC instruction doesn't allow registering adapter interrupts without first unregistering. So reject any request to enable interrupt forwarding if its already enabled for the zPCI device. This also fixes overwriting and thus leaking resources when the ioctl is called multiple times for the same device. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 20f738fa65569..a406e92530ec1 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -238,6 +238,10 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, if (zdev->gisa == 0) return -EINVAL; + /* AIF already enabled for the device */ + if (zdev->kzdev->fib.fmt0.aibv != 0) + return -EINVAL; + kvm = zdev->kzdev->kvm; msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi); From 70871b121f81d08879363cb1238a4c85c5c2800c Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:05 -0700 Subject: [PATCH 3081/3297] KVM: s390: pci: Fix memory accounting for pinned/unpinned pages commit 36f6999ecde3976731a8bfc0b8e667da6f593069 upstream. The account_mem() and unaccount_mem() functions call get_uid() which increments the reference count of struct user_struct on every invocation. But we don't decrement the count by calling free_uid(). It also accounted/unaccounted the pages against the current->mm. But its possible the unaccount_mem() can be called from a different process context than the one that originally pinned the pages. Let's fix this by storing the pinning process user_struct and mm_struct when accounting for pinned pages, and subsequently free these resources when the pages are unpinned. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato [borntraeger@linux.ibm.com: Fixed whitespace] Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 43 ++++++++++++++++++++++++++++++++----------- arch/s390/kvm/pci.h | 2 ++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index a406e92530ec1..a3f8c228baf8d 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -191,33 +191,54 @@ static int kvm_zpci_clear_airq(struct zpci_dev *zdev) return cc ? -EIO : 0; } -static inline void unaccount_mem(unsigned long nr_pages) +static inline void unaccount_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) { - struct user_struct *user = get_uid(current_user()); + struct user_struct *user = kzdev->user_account; + struct mm_struct *mm_account = kzdev->mm_account; - if (user) + if (user) { atomic_long_sub(nr_pages, &user->locked_vm); - if (current->mm) - atomic64_sub(nr_pages, ¤t->mm->pinned_vm); + free_uid(user); + kzdev->user_account = NULL; + } + + if (mm_account) { + atomic64_sub(nr_pages, &mm_account->pinned_vm); + mmdrop(mm_account); + kzdev->mm_account = NULL; + } } -static inline int account_mem(unsigned long nr_pages) +static inline int account_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) { struct user_struct *user = get_uid(current_user()); unsigned long page_limit, cur_pages, new_pages; + int rc = 0; page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; cur_pages = atomic_long_read(&user->locked_vm); do { new_pages = cur_pages + nr_pages; - if (new_pages > page_limit) - return -ENOMEM; + if (new_pages > page_limit) { + rc = -ENOMEM; + goto out; + } } while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages)); - atomic64_add(nr_pages, ¤t->mm->pinned_vm); + if (current->mm) { + mmgrab(current->mm); + atomic64_add(nr_pages, ¤t->mm->pinned_vm); + } + + kzdev->user_account = user; + kzdev->mm_account = current->mm; return 0; + +out: + free_uid(user); + return rc; } static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, @@ -280,7 +301,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, } /* Account for pinned pages, roll back on failure */ - if (account_mem(pcount)) + if (account_mem(zdev->kzdev, pcount)) goto unpin2; /* AISB must be allocated before we can fill in GAITE */ @@ -401,7 +422,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force) pcount++; } if (pcount > 0) - unaccount_mem(pcount); + unaccount_mem(kzdev, pcount); out: mutex_unlock(&aift->aift_lock); diff --git a/arch/s390/kvm/pci.h b/arch/s390/kvm/pci.h index ff0972dd5e71d..fdf8c7bf4ed08 100644 --- a/arch/s390/kvm/pci.h +++ b/arch/s390/kvm/pci.h @@ -22,6 +22,8 @@ struct kvm_zdev { struct kvm *kvm; struct zpci_fib fib; struct list_head entry; + struct user_struct *user_account; + struct mm_struct *mm_account; }; struct zpci_gaite { From 1abf9ce39a862f54a32b24002c09dc44f058972a Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:06 -0700 Subject: [PATCH 3082/3297] KVM: s390: pci: Fix missing error codes and memory unaccounting commit f86842e4d6c482300f4567f492d512c9ccf5bc4f upstream. In kvm_s390_pci_aif_enable() two error paths failed to set an error code, causing the function to return 0 on failure. It also failed to rollback memory accounting on failure. Fix both by propagating an error code on failure and calling unaccount_mem() in the cleanup path. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index a3f8c228baf8d..6a278d5f6a557 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -301,14 +301,17 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, } /* Account for pinned pages, roll back on failure */ - if (account_mem(zdev->kzdev, pcount)) + rc = account_mem(zdev->kzdev, pcount); + if (rc) goto unpin2; /* AISB must be allocated before we can fill in GAITE */ mutex_lock(&aift->aift_lock); bit = airq_iv_alloc_bit(aift->sbv); - if (bit == -1UL) + if (bit == -1UL) { + rc = -ENOMEM; goto unlock; + } zdev->aisb = bit; /* store the summary bit number */ zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK | @@ -352,6 +355,8 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, return rc; unlock: + if (pcount > 0) + unaccount_mem(zdev->kzdev, pcount); mutex_unlock(&aift->aift_lock); unpin2: if (fib->fmt0.sum == 1) From e137d082325bbcae780087b57501d38585e625d9 Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:07 -0700 Subject: [PATCH 3083/3297] KVM: s390: pci: Fix NULL dereference on AIBV allocation failure commit 8bf09b9b7d3232806df95f409581f8a9fd99a3fa upstream. The airq_iv_create() can return NULL on failure, but the return value was never checked. If it fails, zdev->aibv will be NULL and fail when dereferenced in kvm_zpci_set_airq(). Add a NULL check and free the previously allocated AISB bit and zdev->aisb on failure. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 6a278d5f6a557..09dd63c05d6fb 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -318,6 +318,11 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, AIRQ_IV_GUESTVEC, phys_to_virt(fib->fmt0.aibv)); + if (!zdev->aibv) { + rc = -ENOMEM; + goto free_aisb; + } + spin_lock_irq(&aift->gait_lock); gaite = aift->gait + zdev->aisb; @@ -354,6 +359,9 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, rc = kvm_zpci_set_airq(zdev); return rc; +free_aisb: + airq_iv_free_bit(aift->sbv, zdev->aisb); + zdev->aisb = 0; unlock: if (pcount > 0) unaccount_mem(zdev->kzdev, pcount); From b878ba7e28144c9a857bc847d31f3c45413450ac Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:09 -0700 Subject: [PATCH 3084/3297] KVM: s390: pci: Validate AIBV and AISB before pinning guest pages commit 868d32ac72cba21c5c6d8a66a814b7c25a3a5c01 upstream. The AIBV holds one bit per MSI-X vector for a given function. The size of the bit vector is derived from the NOI and the AIBVO. If the size of the AIBV exceeds a single page boundary, then reject the request as we cannot safely pin the guest AIBV. Similarly reject the request if the AISB address is not 8-byte aligned as the architecture requires doubleword alignment for the summary bit address. Since the AISBO can address up to 64 bits, the size of the AISB can only be 8 bytes for the function. This also ensures the AISB doesn't exceed a single page boundary. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/pci.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 09dd63c05d6fb..4cc554c9d3283 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -245,7 +245,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, bool assist) { struct page *pages[1], *aibv_page, *aisb_page = NULL; - unsigned int msi_vecs, idx; + unsigned int msi_vecs, idx, size; struct zpci_gaite *gaite; unsigned long hva, bit; struct kvm *kvm; @@ -272,6 +272,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, return gisc; /* Replace AIBV address */ + size = BITS_TO_LONGS(msi_vecs + fib->fmt0.aibvo) * sizeof(unsigned long); + npages = DIV_ROUND_UP((fib->fmt0.aibv & ~PAGE_MASK) + size, PAGE_SIZE); + /* AIBV cannot span more than 1 page */ + if (npages > 1) { + rc = -EINVAL; + goto out; + } + idx = srcu_read_lock(&kvm->srcu); hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages); @@ -287,6 +295,12 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, /* Pin the guest AISB if one was specified */ if (fib->fmt0.sum == 1) { + /* AISB must be dword aligned */ + if (fib->fmt0.aisb & 0x7) { + rc = -EINVAL; + goto unpin1; + } + idx = srcu_read_lock(&kvm->srcu); hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, From c0837aeace96152d14b17fdd19d70102b6631a7d Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Mon, 27 Jul 2026 11:35:30 +0200 Subject: [PATCH 3085/3297] dibs: fix use-after-free of dmb_node in loopback attach/detach/unregister commit a10ea943356b9d70c5616a0a06f6fa97cfdaccb1 upstream. dibs_lo_attach_dmb(), dibs_lo_detach_dmb() and dibs_lo_unregister_dmb() look up the dmb_node under dmb_ht_lock, drop the lock and only then operate on the node's refcount. Nothing keeps the node alive across that window: __dibs_lo_unregister_dmb() removes the node from the hash table under the write lock and immediately frees it. A concurrent final put can therefore free the node between the lookup and the refcount operation: CPU0 (attach) CPU1 (owner unregisters) read_lock_bh(&dmb_ht_lock) find dmb_node (refcnt == 1) read_unlock_bh(&dmb_ht_lock) refcount_dec_and_test() 1 -> 0 write_lock_bh(&dmb_ht_lock) hash_del(&dmb_node->list) write_unlock_bh(&dmb_ht_lock) kfree(dmb_node) refcount_inc_not_zero(&dmb_node->refcnt) <-- use-after-free The same window exists for the refcount_dec_and_test() calls in the detach and unregister paths. Close the race structurally by making hash table membership and the refcount transitions atomic with respect to each other: - Perform the final refcount_dec_and_test() and hash_del() in a single dmb_ht_lock write-side critical section, in both the unregister and the detach path. Freeing the node still happens after the lock is dropped, which is safe because a node whose refcount reached zero has left the hash table and can no longer be found. - This establishes the invariant that any node found in the hash table holds at least one reference, and that the final reference can only be dropped under the write lock. dibs_lo_attach_dmb() can thus take its reference with a plain refcount_inc() while still holding the read lock; refcount_inc_not_zero() is no longer needed. __dibs_lo_unregister_dmb() no longer touches the hash table and is renamed to dibs_lo_free_dmb() accordingly. Note: commit cc21191b584c ("dibs: Move data path to dibs layer") moved the code to its current location; the race was introduced earlier by commit c3a910f2380f ("net/smc: implement DMB-merged operations of loopback-ism"). Tested SMC-D via ISM and dibs loopback. Cc: stable@vger.kernel.org Fixes: c3a910f2380f ("net/smc: implement DMB-merged operations of loopback-ism") Reported-by: Rahul Chandelkar Signed-off-by: Hidayath Khan Reviewed-by: Alexandra Winter Link: https://patch.msgid.link/20260727093530.968834-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/dibs/dibs_loopback.c | 47 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index 8e165bdedb33b..73ca4fbca097a 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -118,14 +118,9 @@ static int dibs_lo_register_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb, return rc; } -static void __dibs_lo_unregister_dmb(struct dibs_lo_dev *ldev, - struct dibs_lo_dmb_node *dmb_node) +static void dibs_lo_free_dmb(struct dibs_lo_dev *ldev, + struct dibs_lo_dmb_node *dmb_node) { - /* remove dmb from hash table */ - write_lock_bh(&ldev->dmb_ht_lock); - hash_del(&dmb_node->list); - write_unlock_bh(&ldev->dmb_ht_lock); - clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask); folio_put(virt_to_folio(dmb_node->cpu_addr)); kfree(dmb_node); @@ -139,27 +134,33 @@ static int dibs_lo_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb) struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node; struct dibs_lo_dev *ldev; unsigned long flags; + bool last; ldev = dibs->drv_priv; /* find dmb from hash table */ - read_lock_bh(&ldev->dmb_ht_lock); + write_lock_bh(&ldev->dmb_ht_lock); hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) { if (tmp_node->token == dmb->dmb_tok) { dmb_node = tmp_node; break; } } - read_unlock_bh(&ldev->dmb_ht_lock); - if (!dmb_node) + if (!dmb_node) { + write_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; + } + last = refcount_dec_and_test(&dmb_node->refcnt); + if (last) + hash_del(&dmb_node->list); + write_unlock_bh(&ldev->dmb_ht_lock); - if (refcount_dec_and_test(&dmb_node->refcnt)) { + if (last) { spin_lock_irqsave(&dibs->lock, flags); dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT; spin_unlock_irqrestore(&dibs->lock, flags); - __dibs_lo_unregister_dmb(ldev, dmb_node); + dibs_lo_free_dmb(ldev, dmb_node); } return 0; } @@ -188,14 +189,9 @@ static int dibs_lo_attach_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb) read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } + refcount_inc(&dmb_node->refcnt); read_unlock_bh(&ldev->dmb_ht_lock); - if (!refcount_inc_not_zero(&dmb_node->refcnt)) - /* the dmb is being unregistered, but has - * not been removed from the hash table. - */ - return -EINVAL; - /* provide dmb information */ dmb->idx = dmb_node->sba_idx; dmb->dmb_tok = dmb_node->token; @@ -209,11 +205,12 @@ static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token) { struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node; struct dibs_lo_dev *ldev; + bool last; ldev = dibs->drv_priv; /* find dmb_node according to dmb->dmb_tok */ - read_lock_bh(&ldev->dmb_ht_lock); + write_lock_bh(&ldev->dmb_ht_lock); hash_for_each_possible(ldev->dmb_ht, tmp_node, list, token) { if (tmp_node->token == token) { dmb_node = tmp_node; @@ -221,13 +218,17 @@ static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token) } } if (!dmb_node) { - read_unlock_bh(&ldev->dmb_ht_lock); + write_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } - read_unlock_bh(&ldev->dmb_ht_lock); + last = refcount_dec_and_test(&dmb_node->refcnt); + if (last) + hash_del(&dmb_node->list); + write_unlock_bh(&ldev->dmb_ht_lock); + + if (last) + dibs_lo_free_dmb(ldev, dmb_node); - if (refcount_dec_and_test(&dmb_node->refcnt)) - __dibs_lo_unregister_dmb(ldev, dmb_node); return 0; } From 17b412468c7a44f66a385bda48cdc1e94e39bd6d Mon Sep 17 00:00:00 2001 From: Charles Vosburgh Date: Mon, 27 Jul 2026 19:17:30 -0400 Subject: [PATCH 3086/3297] sctp: validate Adaptation Indication parameter length commit 74b21f52c5c5a71a05c0ff70e513f4f04ff28b17 upstream. The Adaptation Layer Indication parameter contains a fixed 32-bit Adaptation Code Point after its parameter header. However, sctp_verify_param() accepts a header-only parameter because the generic parameter walker only requires the header to be present. sctp_process_param() then reads adaptation_ind beyond the declared parameter. When the malformed parameter is last in an INIT, the read starts at the receive skb tail, and the value is copied into the state cookie returned in the INIT ACK. This may disclose four receive-buffer tail bytes. Require the declared parameter length to match the fixed structure size and abort the association through the existing invalid parameter length path otherwise. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Charles Vosburgh Acked-by: Xin Long Link: https://patch.msgid.link/20260727-sctp-adaptation-length-v1-1-0ab58b2810a5@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 1f8a1e6069b3c..401e5b2e78bc2 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2168,7 +2168,13 @@ static enum sctp_ierror sctp_verify_param(struct net *net, case SCTP_PARAM_HEARTBEAT_INFO: case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: case SCTP_PARAM_ECN_CAPABLE: + break; case SCTP_PARAM_ADAPTATION_LAYER_IND: + if (ntohs(param.p->length) != sizeof(*param.aind)) { + sctp_process_inv_paramlength(asoc, param.p, + chunk, err_chunk); + retval = SCTP_IERROR_ABORT; + } break; case SCTP_PARAM_SUPPORTED_EXT: From 185c784c98094e05277a196a19a825192eb38afb Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Sat, 18 Jul 2026 13:09:22 +0800 Subject: [PATCH 3087/3297] audit: fix potential integer overflow in audit_log_n_string() commit f865c143629d4094866a811dba5f329250bad486 upstream. audit_log_n_string() computes new_len as "slen + 3" (enclosing quotes plus the NUL terminator) and stores it into an int, while slen is a size_t. For a sufficiently large slen the addition can overflow and/or the result be truncated when assigned to the int new_len, so the "new_len > avail" check can be bypassed and the subsequent memcpy(ptr, string, slen) can write past the skb tail. This is the same class of bug that was fixed for the hex sibling in commit 65dfde57d1e2 ("audit: fix potential integer overflow in audit_log_n_hex()"); both helpers are reached through audit_log_n_untrustedstring() with the same length source. Make new_len a size_t and use check_add_overflow() to catch the overflow, mirroring the audit_log_n_hex() fix. No functional change for the in-tree callers, which all pass bounded lengths. Cc: stable@vger.kernel.org Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") Signed-off-by: Zhan Xusheng Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index e175fce41ef93..b2612f7ca6311 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -2115,7 +2115,8 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, void audit_log_n_string(struct audit_buffer *ab, const char *string, size_t slen) { - int avail, new_len; + int avail; + size_t new_len; unsigned char *ptr; struct sk_buff *skb; @@ -2125,7 +2126,13 @@ void audit_log_n_string(struct audit_buffer *ab, const char *string, BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); - new_len = slen + 3; /* enclosing quotes + null terminator */ + + /* enclosing quotes + null terminator */ + if (check_add_overflow(slen, 3, &new_len)) { + audit_log_format(ab, "?"); + return; + } + if (new_len > avail) { avail = audit_expand(ab, new_len); if (!avail) From cae0dfed5d307b240bff71c3cf206652d1b6f215 Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Tue, 21 Jul 2026 23:37:41 +0800 Subject: [PATCH 3088/3297] audit: fix potential use-after-free in audit_del_rule() commit 246df90b5f1a8a6e6abbd2f058b029558720adec upstream. `audit_del_rule()` destroys `e->rule.exe` via `audit_remove_mark_rule()` before unlinking the rule from RCU-visible filter lists and waiting for a grace period. Concurrent readers in `audit_filter()` and `audit_filter_rules()` still dereference `e->rule.exe`, while the fsnotify mark can be freed on an independent lifetime path. This creates a use-after-free window during rule deletion. Fix this by unlinking the rule from the RCU-visible lists and invoking `synchronize_rcu()` before calling `audit_remove_mark_rule()` (and other rule removal helpers). This ensures that all existing RCU readers have exited the critical section before any underlying resources are destroyed. Cc: stable@vger.kernel.org Fixes: 34d99af52ad4 ("audit: implement audit by executable") Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- kernel/auditfilter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index d0bd6e549634f..f9de58df00fe8 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1047,6 +1047,10 @@ int audit_del_rule(struct audit_entry *entry) goto out; } + list_del_rcu(&e->list); + list_del(&e->rule.list); + synchronize_rcu(); + if (e->rule.watch) audit_remove_watch_rule(&e->rule); @@ -1064,8 +1068,6 @@ int audit_del_rule(struct audit_entry *entry) audit_signals--; #endif - list_del_rcu(&e->list); - list_del(&e->rule.list); call_rcu(&e->rcu, audit_free_rule_rcu); out: From f14d41dbc2fdf18a86bd4fc23146fc42686c8c03 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Jul 2026 17:57:34 +0200 Subject: [PATCH 3089/3297] Bluetooth: btusb: Fix short read errors in btusb_qca_send_vendor_req() commit cac43d360c928bc0cbbd18809632388265649761 upstream. If btusb_qca_send_vendor_req() gets a "short" read from a device, it will accidentally treat that as a "real" read and populate the returned value with some unknown and probably totally invalid data. Fix this logic error up by calling usb_control_msg_recv() which guarantees a "full" read happens, and then simplify the error checking for when btusb_qca_send_vendor_req() is called. Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index fea3cb502a4e0..cb8f14b9cae80 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3345,28 +3345,16 @@ static const char *qca_get_fw_subdirectory(const struct qca_version *ver) static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request, void *data, u16 size) { - int pipe, err; - u8 *buf; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; + int err; /* Found some of USB hosts have IOT issues with ours so that we should * not wait until HCI layer is ready. */ - pipe = usb_rcvctrlpipe(udev, 0); - err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN, - 0, 0, buf, size, USB_CTRL_GET_TIMEOUT); - if (err < 0) { + err = usb_control_msg_recv(udev, 0, request, USB_TYPE_VENDOR | USB_DIR_IN, + 0, 0, data, size, USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); + if (err) dev_err(&udev->dev, "Failed to access otp area (%d)", err); - goto done; - } - - memcpy(data, buf, size); - -done: - kfree(buf); return err; } @@ -3573,7 +3561,7 @@ static bool btusb_qca_need_patch(struct usb_device *udev) struct qca_version ver; if (btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, - sizeof(ver)) < 0) + sizeof(ver))) return false; /* only low ROM versions need patches */ return !(le32_to_cpu(ver.rom_version) & ~0xffffU); @@ -3591,7 +3579,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, sizeof(ver)); - if (err < 0) + if (err) return err; ver_rom = le32_to_cpu(ver.rom_version); @@ -3614,7 +3602,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) err = btusb_qca_send_vendor_req(udev, QCA_CHECK_STATUS, &status, sizeof(status)); - if (err < 0) + if (err) return err; if (!(status & QCA_PATCH_UPDATED)) { @@ -3625,7 +3613,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, sizeof(ver)); - if (err < 0) + if (err) return err; btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); From 6936b367ee6d40e3afd839bda9b1427822507811 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Jul 2026 17:57:32 +0200 Subject: [PATCH 3090/3297] Bluetooth: btmtk: Fix short read errors in btmtk_usb_uhw_reg_read() commit b186c18c4843dd58adc29443369bddc71cb626a3 upstream. If btmtk_usb_uhw_reg_read() gets a "short" read from a device, it will accidentally treat that as a "real" read and populate the returned value with some unknown and probably totally invalid data. Fix this logic error up by calling usb_control_msg_recv() which guarantees a "full" read happens, and then simplify the error checking for when btmtk_usb_uhw_reg_read() is called. Note, one caller of btmtk_usb_uhw_reg_read() does not check the return value, but as we pre-initialize the return value as 0, an incorrect read will not do anything wrong. Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btmtk.c | 50 +++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index a908e7b46ecaf..df0b59e6f968b 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -763,30 +763,24 @@ static int btmtk_usb_uhw_reg_write(struct hci_dev *hdev, u32 reg, u32 val) static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) { struct btmtk_data *data = hci_get_priv(hdev); - int pipe, err; - void *buf; - - buf = kzalloc(4, GFP_KERNEL); - if (!buf) - return -ENOMEM; + u8 buf[sizeof(u32)]; + int err; - pipe = usb_rcvctrlpipe(data->udev, 0); - err = usb_control_msg(data->udev, pipe, 0x01, - 0xDE, - reg >> 16, reg & 0xffff, - buf, 4, USB_CTRL_GET_TIMEOUT); - if (err < 0) { + *val = 0; + err = usb_control_msg_recv(data->udev, 0, 0x01, + 0xDE, + reg >> 16, reg & 0xffff, + buf, sizeof(buf), USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); + if (err) { bt_dev_err(hdev, "Failed to read uhw reg(%d)", err); - goto err_free_buf; + return err; } *val = get_unaligned_le32(buf); bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val); -err_free_buf: - kfree(buf); - - return err; + return 0; } static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) @@ -836,7 +830,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (dev_id == 0x7922) { err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); - if (err < 0) + if (err) return err; val |= 0x00002020; err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val); @@ -846,7 +840,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); - if (err < 0) + if (err) return err; val |= BIT(0); err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val); @@ -855,14 +849,14 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) msleep(100); } else if (dev_id == 0x7925) { err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); - if (err < 0) + if (err) return err; val |= (1 << 5); err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val); if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); - if (err < 0) + if (err) return err; val &= 0xFFFF00FF; val |= (1 << 13); @@ -873,7 +867,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); - if (err < 0) + if (err) return err; val |= (1 << 0); err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val); @@ -883,13 +877,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val); - if (err < 0) + if (err) return err; err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF); if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val); - if (err < 0) + if (err) return err; msleep(100); } else { @@ -899,7 +893,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_WDT_STATUS, &val); - if (err < 0) + if (err) return err; /* Reset the bluetooth chip via USB interface. */ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 1); @@ -909,13 +903,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val); - if (err < 0) + if (err) return err; err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF); if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val); - if (err < 0) + if (err) return err; /* MT7921 need to delay 20ms between toggle reset bit */ msleep(20); @@ -923,7 +917,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); - if (err < 0) + if (err) return err; } From 814f82f432dc6ee4d15f94756554ff94e6e3ef05 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Fri, 24 Jul 2026 00:43:46 +0800 Subject: [PATCH 3091/3297] Bluetooth: mgmt: fix pending command UAF in EIR updates commit 8f2f62855a41d1730fb9e8122912bd2c8d6bed5d upstream. MGMT_OP_SET_LOCAL_NAME is handled asynchronously on powered controllers and can run set_name_sync(). When the controller is BR/EDR capable, set_name_sync() updates the local name and then rebuilds EIR data through eir_create(). The EIR builder walks hdev->uuids, but the UUID list can be changed and entries can be freed by MGMT_OP_ADD_UUID and MGMT_OP_REMOVE_UUID. pending_eir_or_class() is meant to serialize management commands that can change EIR or the class of device, but it did not include MGMT_OP_SET_LOCAL_NAME. In addition, it walked hdev->mgmt_pending without hdev->mgmt_pending_lock even though pending commands are added and removed under that mutex. A racing command completion can therefore remove and free a pending command while pending_eir_or_class() is still inspecting it, leading to a use-after-free in the pending-command list or allowing a local name update to rebuild EIR while UUID entries are being removed. Take hdev->mgmt_pending_lock while scanning hdev->mgmt_pending and treat MGMT_OP_SET_LOCAL_NAME as an EIR/class-affecting pending command on the powered asynchronous path. Check for a conflicting pending command before copying the new short name so a rejected SET_LOCAL_NAME request does not modify hdev->short_name. Fixes: 6fe26f694c82 ("Bluetooth: MGMT: Protect mgmt_pending list with its own lock") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Reported-by: Vega Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/mgmt.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 424e7980f32e1..03dd807dd7335 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2699,18 +2699,28 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev, static bool pending_eir_or_class(struct hci_dev *hdev) { struct mgmt_pending_cmd *cmd; + bool pending = false; + + mutex_lock(&hdev->mgmt_pending_lock); list_for_each_entry(cmd, &hdev->mgmt_pending, list) { switch (cmd->opcode) { case MGMT_OP_ADD_UUID: case MGMT_OP_REMOVE_UUID: case MGMT_OP_SET_DEV_CLASS: + case MGMT_OP_SET_LOCAL_NAME: case MGMT_OP_SET_POWERED: - return true; + pending = true; + break; } + + if (pending) + break; } - return false; + mutex_unlock(&hdev->mgmt_pending_lock); + + return pending; } static const u8 bluetooth_base_uuid[] = { @@ -4046,6 +4056,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + if (hdev_is_powered(hdev) && pending_eir_or_class(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, + MGMT_STATUS_BUSY); + goto failed; + } + memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); if (!hdev_is_powered(hdev)) { From a33bc07b4730b6cd5681ac77d18ae0de3e739690 Mon Sep 17 00:00:00 2001 From: Aldo Ariel Panzardo Date: Sat, 25 Jul 2026 16:52:30 -0300 Subject: [PATCH 3092/3297] Bluetooth: SCO: give the socket its own sco_conn reference commit abd93c85c8667add738ee82aeab95dd9fc8265a2 upstream. sco_conn_del() drops a reference it does not own. It takes one transient reference via sco_conn_hold_unless_zero() and releases it with the sco_conn_put() that follows sco_sock_hold(); the additional put in the !sk branch releases a second one: conn = sco_conn_hold_unless_zero(conn); ... sk = sco_sock_hold(conn); sco_conn_unlock(conn); sco_conn_put(conn); if (!sk) { sco_conn_put(conn); return; } When close() races the controller's Disconnection Complete, sco_chan_del() clears conn->sk and drops the socket's reference while sco_conn_del() is running. sco_conn_del() then sees sk == NULL, its own put drops the count to zero and frees the conn, and the second put writes to the freed kref: BUG: KASAN: slab-use-after-free in sco_conn_put.part.0+0x1a/0x190 Write of size 4 at addr ffff8881099dec74 by task kworker/u17:3/413 Workqueue: hci1 hci_rx_work Call Trace: sco_conn_put.part.0+0x1a/0x190 hci_disconn_complete_evt+0x1ee/0x3e0 hci_event_packet+0x54a/0x650 hci_rx_work+0x321/0x3d0 Allocated by task 413: sco_conn_add+0x72/0x1a0 sco_connect_cfm+0x88/0x670 Freed by task 413: sco_conn_del.isra.0+0x3f/0xf0 hci_disconn_complete_evt+0x1ee/0x3e0 refcount_t: underflow; use-after-free. The root cause is that the socket stores the connection without holding a reference of its own. __sco_chan_add() does: sco_pi(sk)->conn = conn; so the socket borrows whatever reference its caller happened to hold, and the callers paper over that with ad-hoc holds and puts. Give the socket a counted reference instead: __sco_chan_add() takes one and it is released together with the channel (sco_chan_del()) and in sco_sock_destruct(). With the socket holding its own reference, sco_conn_del() no longer needs the extra put and the redundant hold in sco_conn_ready() goes away. Making the socket own its reference means the connection is now actually freed on the error paths of sco_connect() where it used to leak, which in turn runs sco_conn_free() and its hci_conn_drop(conn->hcon). To keep the hci_conn accounting balanced, make that ownership explicit as well: sco_conn_add() consumes one hci_conn reference and the sco_conn owns it for its lifetime. sco_connect() hands over the reference returned by hci_connect_sco() and no longer drops it on the error paths; sco_connect_cfm(), which is not given a reference, takes one with hci_conn_hold() before handing it to sco_conn_add() (and drops it again if the allocation fails); and the explicit hci_conn_hold() in sco_conn_ready() is removed. Every reference then has a single, clear owner. Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn") Cc: stable@vger.kernel.org Suggested-by: Pauli Virtanen Signed-off-by: Aldo Ariel Panzardo Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/sco.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 285a98594ba43..a25b5f6263589 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -190,6 +190,9 @@ static void sco_sock_clear_timer(struct sock *sk) } /* ---- SCO connections ---- */ +/* Consumes a reference on @hcon, which the returned sco_conn owns until it is + * freed. On failure (NULL return) the reference is left for the caller to drop. + */ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) { struct sco_conn *conn = hcon->sco_data; @@ -200,6 +203,9 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) sco_conn_lock(conn); conn->hcon = hcon; sco_conn_unlock(conn); + } else { + /* conn already owns a reference on hcon */ + hci_conn_drop(hcon); } return conn; } @@ -267,10 +273,8 @@ static void sco_conn_del(struct hci_conn *hcon, int err) sco_conn_unlock(conn); sco_conn_put(conn); - if (!sk) { - sco_conn_put(conn); + if (!sk) return; - } /* Kill socket */ lock_sock(sk); @@ -285,7 +289,7 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, { BT_DBG("conn %p", conn); - sco_pi(sk)->conn = conn; + sco_pi(sk)->conn = sco_conn_hold(conn); conn->sk = sk; if (parent) @@ -368,15 +372,15 @@ static int sco_connect(struct sock *sk) */ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { release_sock(sk); - hci_conn_drop(hcon); + sco_conn_put(conn); err = -EBADFD; goto unlock; } err = sco_chan_add(conn, sk, NULL); + sco_conn_put(conn); if (err) { release_sock(sk); - hci_conn_drop(hcon); goto unlock; } @@ -1452,8 +1456,6 @@ static void sco_conn_ready(struct sco_conn *conn) bacpy(&sco_pi(sk)->src, &conn->hcon->src); bacpy(&sco_pi(sk)->dst, &conn->hcon->dst); - sco_conn_hold(conn); - hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) @@ -1509,10 +1511,12 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status) if (!status) { struct sco_conn *conn; - conn = sco_conn_add(hcon); + conn = sco_conn_add(hci_conn_hold(hcon)); if (conn) { sco_conn_ready(conn); sco_conn_put(conn); + } else { + hci_conn_drop(hcon); } } else sco_conn_del(hcon, bt_to_errno(status)); From c569def320aa8b1fde89227e2ea96606790fd86d Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Tue, 21 Jul 2026 22:36:07 +0800 Subject: [PATCH 3093/3297] Bluetooth: mgmt: fix UAF in pair command cancellation commit d0a7b48ad0921bd88effaee10bf970ab1d5d0ddd upstream. The pairing completion and authentication failure callbacks look up the pending MGMT_OP_PAIR_DEVICE command by walking hdev->mgmt_pending. The lookup returned a command that was still linked on the shared pending list, without keeping mgmt_pending_lock held for the later dereference and removal. A concurrent MGMT_OP_CANCEL_PAIR_DEVICE request can remove and free the same pending command before the callback uses it. The reverse race is also possible when cancel_pair_device() gets a command from pending_find() and a callback removes it before the cancel path dereferences it. This can lead to a use-after-free and a second list_del(). Make the pairing lookup helpers transfer ownership of the pending command by removing it from hdev->mgmt_pending while holding mgmt_pending_lock. The callbacks and cancel path then complete the command and free it directly, so racing paths cannot find or free the same command again. Take a temporary hci_conn reference in cancel_pair_device() because the command completion drops the reference stored in the pending command. Fixes: e9a416b5ce0c ("Bluetooth: Add mgmt_pair_device command") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Reviewed-by: Ren Wei Reported-by: Vega Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/mgmt.c | 64 +++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 03dd807dd7335..831fff026b0ff 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3527,11 +3527,13 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, NULL, 0); } -static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn) +static struct mgmt_pending_cmd *remove_pairing(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; struct mgmt_pending_cmd *cmd; + mutex_lock(&hdev->mgmt_pending_lock); + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { if (cmd->opcode != MGMT_OP_PAIR_DEVICE) continue; @@ -3539,9 +3541,39 @@ static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn) if (cmd->user_data != conn) continue; + list_del(&cmd->list); + mutex_unlock(&hdev->mgmt_pending_lock); return cmd; } + mutex_unlock(&hdev->mgmt_pending_lock); + + return NULL; +} + +static struct mgmt_pending_cmd *remove_pairing_by_addr(struct hci_dev *hdev, + bdaddr_t *bdaddr) +{ + struct mgmt_pending_cmd *cmd; + struct hci_conn *conn; + + mutex_lock(&hdev->mgmt_pending_lock); + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (cmd->opcode != MGMT_OP_PAIR_DEVICE) + continue; + + conn = cmd->user_data; + if (bacmp(bdaddr, &conn->dst) != 0) + continue; + + list_del(&cmd->list); + mutex_unlock(&hdev->mgmt_pending_lock); + return cmd; + } + + mutex_unlock(&hdev->mgmt_pending_lock); + return NULL; } @@ -3579,10 +3611,10 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete) u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED; struct mgmt_pending_cmd *cmd; - cmd = find_pairing(conn); + cmd = remove_pairing(conn); if (cmd) { cmd->cmd_complete(cmd, status); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } } @@ -3592,14 +3624,14 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) BT_DBG("status %u", status); - cmd = find_pairing(conn); + cmd = remove_pairing(conn); if (!cmd) { BT_DBG("Unable to find a pending command"); return; } cmd->cmd_complete(cmd, mgmt_status(status)); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) @@ -3611,14 +3643,14 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) if (!status) return; - cmd = find_pairing(conn); + cmd = remove_pairing(conn); if (!cmd) { BT_DBG("Unable to find a pending command"); return; } cmd->cmd_complete(cmd, mgmt_status(status)); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, @@ -3775,23 +3807,17 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev); + cmd = remove_pairing_by_addr(hdev, &addr->bdaddr); if (!cmd) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, MGMT_STATUS_INVALID_PARAMS); goto unlock; } - conn = cmd->user_data; - - if (bacmp(&addr->bdaddr, &conn->dst) != 0) { - err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } + conn = hci_conn_get(cmd->user_data); cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr, sizeof(*addr)); @@ -3809,6 +3835,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (conn->conn_reason == CONN_REASON_PAIR_DEVICE) hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); + hci_conn_put(conn); + unlock: hci_dev_unlock(hdev); return err; @@ -10135,14 +10163,14 @@ void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status) ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type); ev.status = status; - cmd = find_pairing(conn); + cmd = remove_pairing(conn); mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); if (cmd) { cmd->cmd_complete(cmd, status); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } } From eb1d8318764de7216e6dbba29a24d69f7ce51348 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Thu, 23 Jul 2026 23:34:40 +0800 Subject: [PATCH 3094/3297] Bluetooth: hci_sync: Fix advertising data UAFs commit cdc36db204ffd97b947d64374cf23a210dc74777 upstream. hci_find_adv_instance() returns an adv_info pointer that is valid only while hdev->lock is held. The advertising command-sync paths perform instance lookups without that lock and, in some cases, retain the pointer while waiting for a controller response. An advertising termination event can therefore interleave as follows: hci_cmd_sync_work hci_rx_work hci_find_adv_instance() __hci_cmd_sync_status() wait for controller reply hci_dev_lock() hci_remove_adv_instance() kfree(adv) adv->scan_rsp_changed = false KASAN reported: BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88 Workqueue: hci0 hci_cmd_sync_work Call Trace: hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 hci_schedule_adv_instance_sync+0x390/0x4c0 hci_cmd_sync_work+0x173/0x300 Allocated by task 87: hci_add_adv_instance+0x538/0xac0 add_advertising+0x885/0x1160 Freed by task 89: kfree+0x131/0x3c0 hci_remove_adv_instance+0x1d8/0x3b0 hci_le_ext_adv_term_evt+0x17b/0x730 Protect the instance lookup and payload construction in the extended advertising, scan response, and periodic advertising data paths. Snapshot the advertising parameters under hdev->lock, but release the lock before waiting for the controller. Clear advertising-data dirty bits before issuing their commands and restore them after a failure using a fresh lookup. Likewise, update the reported transmit power through a fresh lookup after the parameter command completes. No adv_info pointer then survives an HCI command wait. Fixes: cba6b758711c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 2") Cc: stable@vger.kernel.org Suggested-by: Luiz Augusto von Dentz Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hci_sync.c | 131 +++++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 34 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 27c5cc1457e65..580b4a8583d50 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -1233,10 +1233,11 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance, } static int -hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv, +hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance, const struct hci_cp_le_set_ext_adv_params *cp, struct hci_rp_le_set_ext_adv_params *rp) { + struct adv_info *adv; struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp), @@ -1264,11 +1265,15 @@ hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv, if (!rp->status) { hdev->adv_addr_type = cp->own_addr_type; - if (!cp->handle) { + if (!instance) { /* Store in hdev for instance 0 */ hdev->adv_tx_power = rp->tx_power; - } else if (adv) { - adv->tx_power = rp->tx_power; + } else { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (adv) + adv->tx_power = rp->tx_power; + hci_dev_unlock(hdev); } } @@ -1284,9 +1289,13 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance) int err; if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); - if (!adv || !adv->adv_data_changed) + if (!adv || !adv->adv_data_changed) { + hci_dev_unlock(hdev); return 0; + } } len = eir_create_adv_data(hdev, instance, pdu->data, @@ -1297,16 +1306,27 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG; + if (adv) { + adv->adv_data_changed = false; + hci_dev_unlock(hdev); + } + err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA, struct_size(pdu, data, len), pdu, HCI_CMD_TIMEOUT); - if (err) + if (err) { + if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (adv) + adv->adv_data_changed = true; + hci_dev_unlock(hdev); + } + return err; + } - /* Update data if the command succeed */ - if (adv) { - adv->adv_data_changed = false; - } else { + if (!instance) { memcpy(hdev->adv_data, pdu->data, len); hdev->adv_data_len = len; } @@ -1360,22 +1380,22 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) struct adv_info *adv; bool secondary_adv; - if (instance > 0) { - adv = hci_find_adv_instance(hdev, instance); - if (!adv) - return -EINVAL; - } else { - adv = NULL; - } - /* Updating parameters of an active instance will return a - * Command Disallowed error, so we must first disable the - * instance if it is active. + * Command Disallowed error, so disable it before taking a snapshot. */ - if (adv) { + if (instance > 0) { err = hci_disable_ext_adv_instance_sync(hdev, instance); if (err) return err; + + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (!adv) { + hci_dev_unlock(hdev); + return -EINVAL; + } + } else { + adv = NULL; } flags = hci_adv_instance_flags(hdev, instance); @@ -1386,8 +1406,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) || mgmt_get_connectable(hdev); - if (!is_advertising_allowed(hdev, connectable)) + if (!is_advertising_allowed(hdev, connectable)) { + if (instance) + hci_dev_unlock(hdev); return -EPERM; + } /* Set require_privacy to true only when non-connectable * advertising is used and it is not periodic. @@ -1398,8 +1421,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) err = hci_get_random_address(hdev, require_privacy, adv_use_rpa(hdev, flags), adv, &own_addr_type, &random_addr); - if (err < 0) + if (err < 0) { + if (instance) + hci_dev_unlock(hdev); return err; + } memset(&cp, 0, sizeof(cp)); @@ -1450,6 +1476,9 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.channel_map = hdev->le_adv_channel_map; cp.handle = adv ? adv->handle : instance; + if (instance) + hci_dev_unlock(hdev); + if (flags & MGMT_ADV_FLAG_SEC_2M) { cp.primary_phy = HCI_ADV_PHY_1M; cp.secondary_phy = HCI_ADV_PHY_2M; @@ -1462,12 +1491,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.secondary_phy = HCI_ADV_PHY_1M; } - err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp); + err = hci_set_ext_adv_params_sync(hdev, instance, &cp, &rp); if (err) return err; /* Update adv data as tx power is known now */ - err = hci_set_ext_adv_data_sync(hdev, cp.handle); + err = hci_set_ext_adv_data_sync(hdev, instance); if (err) return err; @@ -1475,9 +1504,14 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) && bacmp(&random_addr, BDADDR_ANY)) { /* Check if random address need to be updated */ - if (adv) { - if (!bacmp(&random_addr, &adv->random_addr)) + if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (!adv || !bacmp(&random_addr, &adv->random_addr)) { + hci_dev_unlock(hdev); return 0; + } + hci_dev_unlock(hdev); } else { if (!bacmp(&random_addr, &hdev->random_addr)) return 0; @@ -1499,9 +1533,13 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) int err; if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); - if (!adv || !adv->scan_rsp_changed) + if (!adv || !adv->scan_rsp_changed) { + hci_dev_unlock(hdev); return 0; + } } len = eir_create_scan_rsp(hdev, instance, pdu->data); @@ -1511,15 +1549,27 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG; + if (adv) { + adv->scan_rsp_changed = false; + hci_dev_unlock(hdev); + } + err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA, struct_size(pdu, data, len), pdu, HCI_CMD_TIMEOUT); - if (err) + if (err) { + if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (adv) + adv->scan_rsp_changed = true; + hci_dev_unlock(hdev); + } + return err; + } - if (adv) { - adv->scan_rsp_changed = false; - } else { + if (!instance) { memcpy(hdev->scan_rsp_data, pdu->data, len); hdev->scan_rsp_data_len = len; } @@ -1534,8 +1584,14 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) memset(&cp, 0, sizeof(cp)); + if (instance) + hci_dev_lock(hdev); + len = eir_create_scan_rsp(hdev, instance, cp.data); + if (instance) + hci_dev_unlock(hdev); + if (hdev->scan_rsp_data_len == len && !memcmp(cp.data, hdev->scan_rsp_data, len)) return 0; @@ -1670,9 +1726,13 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) struct adv_info *adv = NULL; if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); - if (!adv || !adv->periodic) + if (!adv || !adv->periodic) { + hci_dev_unlock(hdev); return 0; + } } len = eir_create_per_adv_data(hdev, instance, pdu->data); @@ -1681,6 +1741,9 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->handle = adv ? adv->handle : instance; pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; + if (adv) + hci_dev_unlock(hdev); + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA, struct_size(pdu, data, len), pdu, HCI_CMD_TIMEOUT); @@ -6449,7 +6512,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev, if (err) return err; - err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp); + err = hci_set_ext_adv_params_sync(hdev, 0, &cp, &rp); if (err) return err; From 2ebf63aa557a69990b4e9ea22be224d58aabce96 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 23 Jul 2026 12:28:06 +0900 Subject: [PATCH 3095/3297] Bluetooth: HIDP: reject frames without a transaction header commit 47778d2c2087b5d192398f6fddf692d16a5431cf upstream. hidp_recv_ctrl_frame() and hidp_recv_intr_frame() read skb->data[0] before checking that the L2CAP SDU contains a transaction header. A connected HIDP peer can send an empty basic-mode SDU and make both paths use an uninitialized byte from skb tailroom. KMSAN reports the use in hidp_session_run(), with the uninitialized value originating in __alloc_skb() through vhci_write(). The control path produces two reports and the interrupt path produces one. The byte can also be controlled by a malformed lower-layer packet. If an HCI ACL packet contains an L2CAP PDU with a declared zero-length payload followed by an extra 0x15 byte, l2cap_recv_acldata() reduces skb->len to the declared PDU length before dispatch. The current HIDP path nevertheless consumes the extra byte as HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG and terminates the HIDP session. With this change, the same packet is discarded and a subsequent feature report request succeeds. Pull the transaction header with skb_pull_data() and discard frames that do not contain it. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Sangho Lee Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hidp/core.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 90d32ea8f9792..ae11107d11908 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -563,16 +563,18 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, static void hidp_recv_ctrl_frame(struct hidp_session *session, struct sk_buff *skb) { - unsigned char hdr, type, param; + unsigned char type, param; + u8 *hdr; int free_skb = 1; BT_DBG("session %p skb %p len %u", session, skb, skb->len); - hdr = skb->data[0]; - skb_pull(skb, 1); + hdr = skb_pull_data(skb, 1); + if (!hdr) + goto free; - type = hdr & HIDP_HEADER_TRANS_MASK; - param = hdr & HIDP_HEADER_PARAM_MASK; + type = *hdr & HIDP_HEADER_TRANS_MASK; + param = *hdr & HIDP_HEADER_PARAM_MASK; switch (type) { case HIDP_TRANS_HANDSHAKE: @@ -593,6 +595,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, break; } +free: if (free_skb) kfree_skb(skb); } @@ -600,14 +603,15 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, static void hidp_recv_intr_frame(struct hidp_session *session, struct sk_buff *skb) { - unsigned char hdr; + u8 *hdr; BT_DBG("session %p skb %p len %u", session, skb, skb->len); - hdr = skb->data[0]; - skb_pull(skb, 1); + hdr = skb_pull_data(skb, 1); + if (!hdr) + goto free; - if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { + if (*hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { hidp_set_timer(session); if (session->input) @@ -619,9 +623,10 @@ static void hidp_recv_intr_frame(struct hidp_session *session, BT_DBG("report len %d", skb->len); } } else { - BT_DBG("Unsupported protocol header 0x%02x", hdr); + BT_DBG("Unsupported protocol header 0x%02x", *hdr); } +free: kfree_skb(skb); } From 27cc0e603355c585f1e5da8398faa4d36d498188 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 23 Jul 2026 12:28:07 +0900 Subject: [PATCH 3096/3297] Bluetooth: HIDP: validate numbered report payloads commit 34f53d27b81a16a02828c8fdfa4e02badc326f17 upstream. When hidp_get_raw_report() waits for a numbered report, hidp_process_data() compares the expected report number with skb->data[0]. A connected HIDP peer can reply with only a DATA transaction header, leaving the skb empty after the header is removed. KMSAN reports an uninitialized-value use in hidp_session_run(), with the value originating in __alloc_skb() through vhci_write(). The transaction header checks remove the empty-frame reports, but this report remains until the payload check is added. The comparison can also consume a peer-controlled byte beyond the declared L2CAP PDU. A DATA | FEATURE response followed by an extra 0x01 byte made the current code accept that byte as report ID 1 and complete HIDIOCGFEATURE with a zero-byte result. With this change the malformed response is rejected with -EIO, while a subsequent valid response still succeeds. Require a payload byte before comparing a numbered report ID. Unnumbered reports continue to accept an empty payload. Fixes: 0ff1731a1ae5 ("HID: bt: Add support for hidraw HIDIOCGFEATURE and HIDIOCSFEATURE") Cc: stable@vger.kernel.org Signed-off-by: Sangho Lee Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hidp/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ae11107d11908..4c32191ec8302 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -546,9 +546,10 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, } if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && - param == session->waiting_report_type) { + param == session->waiting_report_type) { if (session->waiting_report_number < 0 || - session->waiting_report_number == skb->data[0]) { + (skb->len && + session->waiting_report_number == skb->data[0])) { /* hidp_get_raw_report() is waiting on this report. */ session->report_return = skb; done_with_skb = 0; From 5c7fdcbecbab2b0a08d294238dec45c1a78aaa1b Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Thu, 23 Jul 2026 14:04:45 +0800 Subject: [PATCH 3097/3297] bpf: lwt: Fix dst reference leak on reroute failure commit 88c17de85ddb459c3fe1e3c65d61fa366b1cf0a8 upstream. bpf_lwt_xmit_reroute() obtains a referenced dst from the route lookup. When skb_cow_head() fails before that dst is installed on the skb, the error path only frees the skb. The skb still owns its previous dst, so the newly looked up dst reference is leaked. Release the new dst reference before freeing the skb on this error path. Fixes: 3bd0b15281af ("bpf: add handling of BPF_LWT_REROUTE to lwt_bpf.c") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Link: https://patch.msgid.link/20260723060445.21926-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/core/lwt_bpf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index 2efcda93d1397..5fffdd4e71add 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -251,8 +251,10 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb) * if there is enough header space in skb. */ err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); - if (unlikely(err)) + if (unlikely(err)) { + dst_release(dst); goto err; + } skb_dst_drop(skb); skb_dst_set(skb, dst); From b53face003b4d790fa5c4ff746134e738f115150 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:46 +0100 Subject: [PATCH 3098/3297] afs: Fix afs_fs_fetch_data() to set call->async commit d568a43f6dbba3ba006304d95fd09862bd482a2f upstream. Fix afs_fs_fetch_data() to set call->async on an async operation as does afs_fs_fetch_data64(). Fixes: eddf51f2bb2c ("afs: Make {Y,}FS.FetchData an asynchronous operation") Link: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-2-dhowells@redhat.com cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/fsclient.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index bc9556991d7cd..1b40ae39ad45b 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -477,6 +477,9 @@ void afs_fs_fetch_data(struct afs_operation *op) if (!call) return afs_op_nomem(op); + if (op->flags & AFS_OPERATION_ASYNC) + call->async = true; + /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHDATA); From d703f022a28a27c9431c1ad7958216709e253365 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:47 +0100 Subject: [PATCH 3099/3297] afs: Fix afs_fs_fetch_data() to subtract transferred from len commit 222052c6be186f2074b3a4d741d5de200f654c43 upstream. Fix afs_fs_fetch_data() to subtract subreq->transferred from subreq->len rather than adding it. Fixes: f28fc2010d62 ("afs: Eliminate afs_read") Link: https://sashiko.dev/#/patchset/20260713081022.2186481-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-3-dhowells@redhat.com cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/fsclient.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 1b40ae39ad45b..2dfcd4a20d107 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -487,7 +487,7 @@ void afs_fs_fetch_data(struct afs_operation *op) bp[2] = htonl(vp->fid.vnode); bp[3] = htonl(vp->fid.unique); bp[4] = htonl(lower_32_bits(subreq->start + subreq->transferred)); - bp[5] = htonl(lower_32_bits(subreq->len + subreq->transferred)); + bp[5] = htonl(lower_32_bits(subreq->len - subreq->transferred)); call->fid = vp->fid; trace_afs_make_fs_call(call, &vp->fid); From c0d3b81f703b2a9e37fe1347610a50cdf0078c27 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:48 +0100 Subject: [PATCH 3100/3297] afs: Fix UAF when sending a message commit 4af1ec68d54b3871155914d584fb10669c41a861 upstream. In afs_make_call(), there's a race with async call reception and destruction. If a call is dispatched that doesn't have call->write_iter set (used to specify the data content for FS.StoreData), then the first rxrpc_kernel_send_data() will not set MSG_MORE in the msghdr. Once rxrpc_send_data() queues the last request packet, the response could come in at any time and cause the call to be completed and put. However, afs_make_call() will look at the call again to see it ->write_iter should be handled - something it's only allowed to do if it has its own ref on the call. Whilst this is the case for synchronous calls, it isn't true for async calls such as FS.FetchData. There's also a potential UAF in afs_make_call() in the event that an asynchronous call is being sent, but the call fails in some way (e.g. it gets aborted from the server). The problem there is that afs_make_call() tries to abort a call if the rxrpc send fails, but the asynchronous notification from rxrpc may have caused the afs_call to be torn down. generic/650 plays games with randomly taking CPUs offline, and can interject a significant delay such that the call is deallocated before afs_make_call() gets to check call->write_iter - and a UAF ensues (caught by KASAN). BUG: KASAN: slab-use-after-free in afs_make_call+0x1c90/0x2210 [kafs] Read of size 8 at addr ffff888035e050e8 by task fsstress/1409 Fix this by making afs_make_op_call() give the op->call its own ref rather than transferring the caller's ref to it and then dropping the ref when afs_make_call() returns. This also means that the afs_make_call() func never loses its ref on the call now. Fixes: eddf51f2bb2c ("afs: Make {Y,}FS.FetchData an asynchronous operation") Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Link: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-4-dhowells@redhat.com cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/afs/internal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/internal.h b/fs/afs/internal.h index bea8aae5364ec..b0ebcb820867a 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1420,7 +1420,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c { struct afs_addr_list *alist = op->estate->addresses; - op->call = call; + op->call = afs_get_call(call, afs_call_trace_get); op->type = call->type; call->op = op; call->key = op->key; @@ -1428,6 +1428,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer); call->service_id = op->server->service_id; afs_make_call(call, gfp); + afs_put_call(call); } static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size) From 11e2953d9f4c7c3d2af94a889c2d805c537d633f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 26 Jul 2026 09:48:19 +0200 Subject: [PATCH 3101/3297] ALSA: 6fire: Fix UAF at error handling during probe commit a54bf16965f896415c3337bc4fbb40fb11941d99 upstream. Although 6fire driver had a few fixes for dealing with the early error handling during the probe phase, it forgot a pending URB before freeing the resources, which may lead to a UAF. This patch addresses it by doing the almost same cleanup procedure like the normal disconnect phase at the error path. Reported-and-tested-by: Shuangpeng Bai Closes: https://lore.kernel.org/20260724030900.1984491-1-shuangpeng.kernel@gmail.com Cc: Link: https://patch.msgid.link/20260726074821.2288158-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/6fire/chip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 874f6cd503ca5..e6cfe0d7ab0be 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -156,6 +156,10 @@ static int usb6fire_chip_probe(struct usb_interface *intf, return 0; destroy_chip: + chip->shutdown = true; + if (card) + snd_card_disconnect(card); + usb6fire_chip_abort(chip); snd_card_free(card); return ret; } From 7484669d1fbab9a499817b5fef686f6417e65fa2 Mon Sep 17 00:00:00 2001 From: Eckhart Mohr Date: Fri, 24 Jul 2026 21:00:13 +0200 Subject: [PATCH 3102/3297] ALSA: hda/realtek: Add quirk for TongFang X6SP45xU commit 26a94400ffa4fcbeff32e23abebb83a1a20eb401 upstream. TongFang X6KK45xU and X6SP45xU have actually different PCI IDs. This patch Adds the missing PCI ID to fix headphone detection and clarifies the naming. Fixes: d595255241e5 ("ALSA: hda/realtek: Add quirk for TongFang X6xx45xU") Signed-off-by: Eckhart Mohr Cc: stable@vger.kernel.org Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260724190109.169889-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/realtek/alc269.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index d84d17c6e049d..7a047bdbae462 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7560,7 +7560,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6xx45xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), From 4969533a1b950e4dea47fb9cbd4c4dc83619a867 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 23 Jul 2026 16:57:10 +0800 Subject: [PATCH 3103/3297] ALSA: lx6464es: fix period byte count for 16-bit streams commit 6437033bffe8bd2af174d139af552d90d40c7ac6 upstream. The lx6464es driver advertises both 16-bit and packed 24-bit PCM formats, but lx_trigger_start() and lx_interrupt_request_new_buffer() calculate the DMA period size as runtime->period_size * runtime->channels * 3. That is only correct for the packed 24-bit formats. For 16-bit streams the driver submits buffers that are 50% larger than the actual ALSA period and advances the DMA address by the same wrong amount. For example, with 2 channels, 256 frames and 4 periods, the third buffer already extends beyond the ALSA buffer and the fourth buffer starts outside it. Use snd_pcm_lib_period_bytes() so the byte count matches the runtime format, channel count and period size. Fixes: 02bec4904508 ("ALSA: lx6464es - driver for the digigram lx6464es interface") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/8BB12E8D92A7CDBA+20260723085710.2567463-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/lx6464es/lx6464es.c | 5 +---- sound/pci/lx6464es/lx_core.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 96df00db51d53..3b40ec071379e 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -402,11 +402,8 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream) int err; - const u32 channels = substream->runtime->channels; - const u32 bytes_per_frame = channels * 3; - const u32 period_size = substream->runtime->period_size; const u32 periods = substream->runtime->periods; - const u32 period_bytes = period_size * bytes_per_frame; + const u32 period_bytes = snd_pcm_lib_period_bytes(substream); dma_addr_t buf = substream->dma_buffer.addr; int i; diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index 6f0843cfb3be4..9909f10678716 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -969,10 +969,7 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip, const unsigned int is_capture = lx_stream->is_capture; int err; - const u32 channels = substream->runtime->channels; - const u32 bytes_per_frame = channels * 3; - const u32 period_size = substream->runtime->period_size; - const u32 period_bytes = period_size * bytes_per_frame; + const u32 period_bytes = snd_pcm_lib_period_bytes(substream); const u32 pos = lx_stream->frame_pos; const u32 next_pos = ((pos+1) == substream->runtime->periods) ? 0 : pos + 1; From 2940cc3cf43c72126b74ee6376314c195382023a Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Tue, 28 Jul 2026 14:50:01 +0200 Subject: [PATCH 3104/3297] ALSA: pcm: wake linked drain waiters on unlink commit f495b6c4c8594122918552c9be2b51eb71647cd9 upstream. snd_pcm_drain() on a linked stream parks an on-stack wait entry on the drained peer's runtime->sleep, and after schedule_timeout() removes it only if that peer is still found in the caller's group. If group membership changes during the wait and the sleep ends by signal or timeout (so autoremove_wake_function() does not run), finish_wait() is skipped and snd_pcm_drain() returns with the entry still queued on that stream's sleep list; a later wake_up() then walks a freed stack frame. This is reachable by unlinking either the drained or the draining stream. Unlike the close path (snd_pcm_drop() -> snd_pcm_post_stop()), snd_pcm_unlink() never wakes the sleep queues. Wake every group member under the group lock before the membership change, so a linked drainer is released and drops its entry while the streams are still grouped. The window was opened when snd_pcm_link_rwsem stopped being held across the wait and the removal became conditional on group membership (see Fixes). The later switch to finish_wait() kept that conditional removal, so the signal/timeout case remained. Fixes: f57f3df03a8e ("ALSA: pcm: More fine-grained PCM link locking") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/A0705100-D10B-4286-9980-0142ABEEAD51@doyensec.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm_native.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0ae95a4670891..0e69127bb10bb 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2334,6 +2334,7 @@ static void relink_to_local(struct snd_pcm_substream *substream) static int snd_pcm_unlink(struct snd_pcm_substream *substream) { + struct snd_pcm_substream *s; struct snd_pcm_group *group; bool nonatomic = substream->pcm->nonatomic; bool do_free = false; @@ -2346,6 +2347,12 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) group = substream->group; snd_pcm_group_lock_irq(group, nonatomic); + /* release drain waiters before changing membership, else snd_pcm_drain() + * leaves its on-stack wait entry queued on a member's sleep list + */ + snd_pcm_group_for_each_entry(s, substream) + wake_up(&s->runtime->sleep); + relink_to_local(substream); refcount_dec(&group->refs); From 5260e195c53e898a4a76527d4bb2178f31795e78 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Sat, 25 Jul 2026 08:33:45 +0200 Subject: [PATCH 3105/3297] ALSA: seq: Fix division by zero in initialize_timer() commit 21e19688433452dfbbbe6b2bb670dea6eb92f0f6 upstream. A userspace-driven ALSA timer (SND_UTIMER) lets an unprivileged user set the backing snd_timer's hardware resolution to an arbitrary 64-bit value via SNDRV_TIMER_IOCTL_CREATE. snd_utimer_create() only rejects zero. When such a timer is bound to a sequencer queue, initialize_timer() computes the tick period as tmr->ticks = 1000000000 / (r * freq); where r is that user-controlled resolution and freq is the sequencer update rate in Hz, clamped to MIN_FREQUENCY..MAX_FREQUENCY (10..6250). A resolution of 2^63 makes the 64-bit product r * freq wrap to zero for any even freq, including DEFAULT_FREQUENCY (1000), so the division faults with a divide-by-zero. The division runs under tmr->lock with interrupts disabled, so the oops leaves the spinlock held and hangs the CPU. It is reachable by an unprivileged user with access to /dev/snd/timer and /dev/snd/seq. Oops: divide error: 0000 [#1] SMP KASAN PTI CPU: 7 UID: 1000 PID: 456 Comm: alsa_seq_utimer Not tainted 7.2.0-rc4+ RIP: 0010:initialize_timer.constprop.0+0x20a/0x2d0 snd_seq_timer_start+0x15e/0x2b0 snd_seq_control_queue+0x56f/0xba0 snd_seq_write+0x3e0/0x730 Reject an overflowing product with check_mul_overflow() and fall back to a single tick, which also avoids feeding a wrapped-but-nonzero divisor (e.g. 2^63 * 1000 mod 2^64 == 0, or other resolutions wrapping to a small value) into the period computation. Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Cc: Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/DF8A3844-AD5E-4B8A-9CFC-BD83C212BA38@doyensec.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_timer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 0256caa61c294..a89451f47872e 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -362,11 +362,10 @@ static int initialize_timer(struct snd_seq_timer *tmr) tmr->ticks = 1; if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { unsigned long r = snd_timer_resolution(tmr->timeri); - if (r) { - tmr->ticks = (unsigned int)(1000000000uL / (r * freq)); - if (! tmr->ticks) - tmr->ticks = 1; - } + unsigned long den; + + if (r && !check_mul_overflow(r, freq, &den)) + tmr->ticks = max(1U, (unsigned int)(1000000000uL / den)); } tmr->initialized = 1; return 0; From a26a2e52736f9e39843ca66a2e1ce6bf1adbcd1e Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Sun, 26 Jul 2026 10:01:45 +0200 Subject: [PATCH 3106/3297] ALSA: timer: Clear SNDRV_TIMER_IFLG_DEAD once the close completes commit c2744d5f3aea474513fd2298daecb94a952ce441 upstream. snd_timer_close_locked() marks an instance with SNDRV_TIMER_IFLG_DEAD and returns early when the flag is already set, but the flag is never cleared again. A completed close ends in remove_slave_links(), which leaves timeri->timer NULL, so a second close is already harmless through the timer == NULL path; the early return can only be reached by an instance that was opened again in between. For such an instance the close unlinks nothing, so snd_timer_instance_free() frees an object that is still on timer->open_list_head, still on snd_timer_master_list if it was opened with a slave key, still owns any adopted slaves, and still holds its timer and module references. snd_seq_timer_open() reopens an instance exactly like that: it retries its fallback open on the same object after a failure that has already run snd_timer_close_locked() internally. An unprivileged user with access to /dev/snd/timer and /dev/snd/seq can force that failure, since snd_timer_check_master() returns -EBUSY when a pending slave matches the new master's (slave_class, slave_id) key and the target timer has reached max_instances, and SNDRV_TIMER_IOCTL_SELECT with dev_class = SNDRV_TIMER_CLASS_SLAVE keeps the caller-supplied dev_sclass, so a sequencer queue's key can be forged. The freed instance is afterwards dereferenced by any further snd_timer_open() on that timer, by snd_timer_check_slave(), and by /proc/asound/timers, which faults on the stale ti->owner pointer. The flag only has to be visible while the close is in progress, which is all its other users need. Clear it in remove_slave_links(), under the same timer->lock that sets it, once the instance is off every list. Fixes: da3039e91d1f ("ALSA: timer: Forcibly close timer instances at closing") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/CA41AA48-75BF-45E9-A36D-3A5D2F124F60@doyensec.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/timer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index a2c344b4cc73a..c5f0e895ea024 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -409,6 +409,8 @@ static void remove_slave_links(struct snd_timer_instance *timeri, list_del_init(&slave->ack_list); list_del_init(&slave->active_list); } + /* the close is done; a reopen must not see the mark */ + timeri->flags &= ~SNDRV_TIMER_IFLG_DEAD; } /* From 032746c2dd9a4ea0774b04ac8a29e2ea628f106e Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 14:16:33 +0900 Subject: [PATCH 3107/3297] ALSA: ump: fix double free of out_cvts on rawmidi error commit 70c977815af0d997feb2d0c5d284d55689bf7051 upstream. snd_ump_attach_legacy_rawmidi() allocates the legacy conversion array ump->out_cvts and, on the snd_rawmidi_new() error path, frees it with kfree() but leaves ump->out_cvts pointing at the freed memory. When the endpoint is later torn down, snd_ump_endpoint_free() frees ump->out_cvts a second time, resulting in a double free. The host snd-usb-audio driver attaches the legacy rawmidi for any USB MIDI 2.0 (UMP) device, so a device that makes snd_rawmidi_new() fail reaches this path on enumeration. Clear ump->out_cvts after freeing it on the error path so it is not freed again during teardown. Discovered by XBOW, triaged by Baul Lee Fixes: 33cd7630782d ("ALSA: ump: Export MIDI1 / UMP conversion helpers") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726051633.41206-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/ump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/ump.c b/sound/core/ump.c index 8d8681a42ca5f..0ba253f50b5a5 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -1366,6 +1366,7 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, &rmidi); if (err < 0) { kfree(ump->out_cvts); + ump->out_cvts = NULL; return err; } From 6df5b32881602a5019a65c3b51450fc99dca6370 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 10:47:57 +0800 Subject: [PATCH 3108/3297] ASoC: fsl_asrc: fix m2m_init error path to use goto instead of bare return commit 890b4253134f3a39883af7d5bea67af9c494c56d upstream. When fsl_asrc_m2m_init() fails in fsl_asrc_probe(), the code did a bare return ret, bypassing pm_runtime_disable() in err_pm_get_sync. Use goto err_pm_get_sync to ensure proper cleanup on failure. Fixes: 286d658477a4 ("ASoC: fsl_asrc: register m2m platform device") Cc: stable@vger.kernel.org Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715024758.1252801-2-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/fsl/fsl_asrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 92fb16f7be456..40279becb8d96 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1388,7 +1388,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) ret = fsl_asrc_m2m_init(asrc); if (ret) { dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); - return ret; + goto err_pm_get_sync; } return 0; From cac7d2066b2f052328eb81171d7657be631a9ff1 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 10:47:58 +0800 Subject: [PATCH 3109/3297] ASoC: fsl_easrc: fix m2m_init error path to use goto instead of bare return commit a54bc0eef90ea760039c14bb7f3b5db42529f84d upstream. When fsl_asrc_m2m_init() fails in fsl_easrc_probe(), the code did a bare return ret, bypassing pm_runtime_disable() in err_pm_disable. Use goto err_pm_disable to ensure proper cleanup on failure. Fixes: b62eaff0650d ("ASoC: fsl_easrc: register m2m platform device") Cc: stable@vger.kernel.org Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715024758.1252801-3-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/fsl/fsl_easrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 59cdf18731a0a..70136b2335ced 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2266,7 +2266,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) ret = fsl_asrc_m2m_init(easrc); if (ret) { dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); - return ret; + goto err_pm_disable; } return 0; From 35d5f1852e390c9a51f515b05b3ca8b2b5bcc404 Mon Sep 17 00:00:00 2001 From: Haidar Lee Date: Wed, 15 Jul 2026 14:04:40 +0800 Subject: [PATCH 3110/3297] ASoC: tas2562: fix DVC coefficient write order commit 8e957e4907c58e9ca944f98799524f2bbb9cf68a upstream. The TAS2562 applies the 32-bit digital volume coefficient to the playback path when the last byte, DVC_CFG4 (book 0 page 2 reg 0x0F), is written. tas2562_volume_control_put() wrote DVC_CFG4 first and DVC_CFG1 (the MSB) last, so every volume change latched a value made of the previous coefficient's upper three bytes combined with the new LSB; the remaining bytes only took effect on the next volume change. In practice the control was unusable: the first setting after power-on always played at roughly 0 dB no matter what value was requested (the chip's default upper bytes were still latched), and most subsequent changes muted the output entirely or produced a distorted, over-unity gain. Verified on a TAS2562 (ADLINK OSM-520 / MT8189 board) by tracing the I2C writes with ftrace and by writing the same coefficients manually in both byte orders: written MSB-first the register block behaves exactly as the driver expects, LSB-first reproduces the broken behaviour. Write the bytes MSB first with DVC_CFG4 last so the complete new coefficient is latched atomically. Fixes: bf726b1c86f2 ("ASoC: tas2562: Add support for digital volume control") Cc: stable@vger.kernel.org Signed-off-by: Haidar Lee Link: https://patch.msgid.link/20260715-tas2562-dvc-fix-v1-1-072b13901b20@adlinktech.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/tas2562.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 795c66cb47134..5dedf6987423a 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -475,20 +475,27 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, u32 reg_val; reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, - (reg_val & 0xff)); - if (ret) - return ret; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, - ((reg_val >> 8) & 0xff)); + /* + * The device applies the 32-bit coefficient to the playback path on + * the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the + * bytes must be written MSB first and DVC_CFG4 last. Writing CFG4 + * first latches a mix of the previous coefficient's upper bytes and + * the new LSB instead of the requested value. + */ + ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, + ((reg_val >> 24) & 0xff)); if (ret) return ret; ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, ((reg_val >> 16) & 0xff)); if (ret) return ret; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, - ((reg_val >> 24) & 0xff)); + ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, + ((reg_val >> 8) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, + (reg_val & 0xff)); if (ret) return ret; From 0a02b0c8780714c3b1c3a8444fdf9261601c5494 Mon Sep 17 00:00:00 2001 From: Haidar Lee Date: Wed, 15 Jul 2026 14:04:41 +0800 Subject: [PATCH 3111/3297] ASoC: tas2562: fix broken entries in the volume lookup table commit bdb0fd6de403fcea7b85dc9d38f0a571583ebe80 upstream. The float_vol_db_lookup table is supposed to hold round(10^(dB/20) * 2^30) for every 2 dB step from -110 dB to 0 dB, which is 56 entries, but it only has 55: the -90 dB entry duplicates the -92 dB value (0x0000695b) and the -20 dB entry (0x06666666) is missing altogether. As a result every step between -90 dB and -22 dB is off by 2 dB, and the control's maximum raw value of 110 indexes one element past the end of the array. Replace the duplicated -90 dB entry with the correct value 0x000084a3 and add the missing -20 dB entry, bringing the table to the full 56 entries so index 55 (raw value 110, 0 dB) is in range again. Fixes: bf726b1c86f2 ("ASoC: tas2562: Add support for digital volume control") Cc: stable@vger.kernel.org Signed-off-by: Haidar Lee Link: https://patch.msgid.link/20260715-tas2562-dvc-fix-v1-2-072b13901b20@adlinktech.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/tas2562.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 5dedf6987423a..ec1c8d077efcd 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -32,15 +32,16 @@ static const unsigned int float_vol_db_lookup[] = { 0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151, 0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b, -0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, +0x000084a3, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, 0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f, 0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7, 0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d, 0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a, 0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27, 0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68, -0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362, -0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000 +0x06666666, 0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, +0x143d1362, 0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, +0x40000000 }; struct tas2562_data { From 9562ddbc6ed8f0372b0ef376cd593cceee4ac1bc Mon Sep 17 00:00:00 2001 From: Matt Vollrath Date: Fri, 24 Jul 2026 03:39:42 -0400 Subject: [PATCH 3112/3297] ata: libata-eh: Increase STANDBY IMMEDIATE timeout commit 1e024d2b41ee32bc06818f7f09a3562c58842cf9 upstream. Correct a previous change (see Fixes) which reduced the standby timeout from 30 to 5 seconds. Increase it to 15 seconds. I was troubleshooting an error spotted during system suspend: [ 1217.152867] ata1.00: Entering standby power mode [ 1222.322948] ata1.00: qc timeout after 5000 msecs (cmd 0xe0) [ 1222.324010] ata1.00: STANDBY IMMEDIATE failed (err_mask=0x4) This drive is a Samsung 870 EVO SSD in good SMART standing, and I wasn't aware of any reason it should be taking so long to standby. The issue is intermittent, but I observed it sometimes taking 7 seconds to manually standby. I assume this was interruption of background maintenance after a power outage. As a desktop user, I would prefer to wait the extra 2 seconds at suspend to let the drive finish its business rather than drop the rails from under it. The change from 30 to 5 seconds was implicit when switching suspend from START STOP UNIT to an internal command with no timeout table entry. No reason was stated for the change. Fixes: aa3998dbeb3a ("ata: libata-scsi: Disable scsi device manage_system_start_stop") Cc: stable@vger.kernel.org Signed-off-by: Matt Vollrath Assisted-by: Claude:claude-5-fable Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-eh.c | 8 ++++++++ include/linux/libata.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 5e8a63206108e..44daae8f378a8 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -106,6 +106,12 @@ static const unsigned int ata_eh_flush_timeouts[] = { UINT_MAX, }; +static const unsigned int ata_eh_standby_timeouts[] = { + 15000, /* Some drives may be slow to standby */ + /* but don't hold up a suspend too long waiting for them */ + UINT_MAX, +}; + static const unsigned int ata_eh_other_timeouts[] = { 5000, /* same rationale as identify timeout */ 10000, /* ditto */ @@ -147,6 +153,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { .timeouts = ata_eh_other_timeouts, }, { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT), .timeouts = ata_eh_flush_timeouts }, + { .commands = CMDS(ATA_CMD_STANDBYNOW1), + .timeouts = ata_eh_standby_timeouts }, { .commands = CMDS(ATA_CMD_VERIFY), .timeouts = ata_eh_reset_timeouts }, }; diff --git a/include/linux/libata.h b/include/linux/libata.h index 5c3f98a418c38..7244d8e155364 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -389,7 +389,7 @@ enum { /* This should match the actual table size of * ata_eh_cmd_timeout_table in libata-eh.c. */ - ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 8, + ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 9, /* * Quirk flags: may be set by libata or controller drivers on drives. From 79f8720029f261371796c6ce3219074ac614c58f Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 28 Jul 2026 13:38:42 +0200 Subject: [PATCH 3113/3297] ata: libata-sata: fix ata_scsi_lpm_supported() iteration commit 3fd70e96914d761c17c376aadd0b0d1a3c9badba upstream. The inner loop of ata_scsi_lpm_supported() uses the wrong variable when iterating. It should obviously use the link that we are currently iterating over, rather than always using the host link. ata_scsi_lpm_supported() is used to control if a user should be allowed to change lpm policy (from the default) via sysfs. Thus, this bug could potentially disallow users to change the LPM policy for certain SATA devices via sysfs. Cc: stable@vger.kernel.org Fixes: 0060beec0bfa ("ata: libata-sata: Add link_power_management_supported sysfs attribute") Reported-by: Sashiko Link: https://lore.kernel.org/linux-ide/20260728112200.B99F21F000E9@smtp.kernel.org/ Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-sata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index 04e1e774645e2..9b374e8f99384 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -913,7 +913,7 @@ static bool ata_scsi_lpm_supported(struct ata_port *ap) return false; ata_for_each_link(link, ap, EDGE) { - ata_for_each_dev(dev, &ap->link, ENABLED) { + ata_for_each_dev(dev, link, ENABLED) { if (dev->quirks & ATA_QUIRK_NOLPM) return false; } From cc014ebf803174f0e5d15956dfc5a38413c945ae Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 14:13:37 +0900 Subject: [PATCH 3114/3297] ALSA: usb-audio: fix use-after-free in ump_to_endpoint() commit 4a05b2d1b4642df74f30b6f54843e825c4a2bfd3 upstream. create_midi2_ump() registers a card-owned snd_ump_endpoint and stores a back-pointer to its per-interface snd_usb_midi2_ump object in ump->private_data, but it never installs an ump->private_free hook and never clears that pointer. If a later step of snd_usb_midi_v2_create() fails, its error path calls free_all_midi2_umps(), which kfree()s the snd_usb_midi2_ump object while the already-registered endpoint keeps pointing at it. The created /dev/snd/umpC*D* node stays exposed, so the first operation of any UMP open, ump_to_endpoint(), dereferences the dangling ump->private_data and reads rmidi->eps[dir] out of freed memory. A malicious USB MIDI 2.0 device that makes creation fail after the endpoint is registered can thus trigger a slab use-after-free read on a subsequent open of the UMP node. Clear the endpoint's back-pointer before freeing the object, and let ump_to_endpoint() tolerate a NULL private_data so the open/close/trigger callbacks fail cleanly (their callers already handle a NULL endpoint) instead of dereferencing a stale pointer. Discovered by XBOW, triaged by Baul Lee Fixes: ff49d1df79ae ("ALSA: usb-audio: USB MIDI 2.0 UMP support") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726051337.41124-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/midi2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 50b04e5a190b6..646333f0cea95 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -329,7 +329,7 @@ ump_to_endpoint(struct snd_ump_endpoint *ump, int dir) { struct snd_usb_midi2_ump *rmidi = ump->private_data; - return rmidi->eps[dir]; + return rmidi ? rmidi->eps[dir] : NULL; } /* ump open callback */ @@ -685,6 +685,8 @@ static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi) rmidi = list_first_entry(&umidi->rawmidi_list, struct snd_usb_midi2_ump, list); list_del(&rmidi->list); + if (rmidi->ump) + rmidi->ump->private_data = NULL; kfree(rmidi); } } From 7ba01e0d3539d9cf0aef3e82938f1648147744cc Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 15:50:20 +0900 Subject: [PATCH 3115/3297] ALSA: usb-audio: fix stack info leak in RME Digiface status commit 441aaad150c57edaf57ee482a79a3bf4c5b7e353 upstream. snd_rme_digiface_read_status() reads a four-word status block from the device into an uninitialised on-stack __le32 buf[4] and, whenever the vendor control-IN transfer does not return a negative error, copies all four words into the caller's status[]. snd_usb_ctl_msg() copies the full requested size back into the caller's buffer regardless of how many bytes the data stage actually delivered: buf = kmemdup(data, size, GFP_KERNEL); err = usb_control_msg(dev, pipe, request, requesttype, value, index, buf, size, timeout); memcpy(data, buf, size); usb_control_msg() returns the transferred length on a short control-IN, which is a non-negative value, and writes only that many bytes. The remainder of the copy back is the kmemdup()ed image of the caller's buffer, so a device answering with a short data stage leaves the trailing words of buf[] holding leftover kernel stack. The only guard in the caller is err < 0, so those words are stored into status[]. They then reach user space: snd_rme_digiface_get_status_val() selects a 16-bit halfword of status[] per the control's reg/mask, and the eight Digiface status controls together expose the whole 16-byte frame to an unprivileged reader of /dev/snd/controlC*. Zero-initialise the buffer so a short read yields zeros instead of stack residue. This mirrors snd_rme_get_status1(), which already clears its output word before the same kind of vendor read. Discovered by XBOW, triaged by Baul Lee Fixes: 611a96f6acf2 ("ALSA: usb-audio: Add mixer quirk for RME Digiface USB") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726065020.46070-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 02d9ede674670..ce057bbb2e116 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3352,7 +3352,7 @@ static int snd_rme_digiface_read_status(struct snd_kcontrol *kcontrol, u32 statu struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip; struct usb_device *dev = chip->dev; - __le32 buf[4]; + __le32 buf[4] = {}; int err; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), From b5305a0d0bb8e90a6fc9f88270d5f6c9b8c40081 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 16:45:00 +0900 Subject: [PATCH 3116/3297] ALSA: usb-audio: fix OOB write in snd_usbmidi_akai_output() commit 0970274613fb463d376211450cab066d34ebfe6a upstream. snd_usbmidi_akai_output() computes its fill-loop bound buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1; as a signed int, so a small device-advertised bulk-OUT max_transfer makes buf_end negative. The loop guard then compares the u32 urb->transfer_buffer_length against that negative int: the usual arithmetic conversion turns buf_end into a large unsigned value, so the guard stays true and each iteration keeps appending SysEx framing and payload bytes past the end of the URB transfer buffer, which is only max_transfer bytes long. A USB device that advertises a tiny bulk-OUT endpoint can therefore trigger an attacker-length- and content-controlled heap out-of-bounds write when a process writes to the created /dev/snd/midiC*D* node. Return early when there is no room for even one SysEx, so the loop is never entered with a bound that would wrap. The loop is the last statement of the function, so bailing out is equivalent to it not running. Discovered by XBOW, triaged by Baul Lee Fixes: 4434ade8c933 ("ALSA: usb-audio: add support for Akai MPD16") Suggested-by: Takashi Iwai Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726074500.50145-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/midi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/midi.c b/sound/usb/midi.c index e09761b6c91f3..01ed18b7b48a8 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -793,6 +793,8 @@ static void snd_usbmidi_akai_output(struct snd_usb_midi_out_endpoint *ep, msg = urb->transfer_buffer + urb->transfer_buffer_length; buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1; + if (buf_end <= 0) + return; /* only try adding more data when there's space for at least 1 SysEx */ while (urb->transfer_buffer_length < buf_end) { From 04595233e5606d452f9f47e6989fc7ae7440fd40 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Tue, 28 Jul 2026 20:17:16 +0000 Subject: [PATCH 3117/3297] ALSA: usb-audio: Fix DMA buffer out-of-bounds write when fill_max is set commit d0199ae1666ff9ae2d1d568d64c3430d4c47f0e5 upstream. When a USB audio endpoint requests full packet transfers via the fill_max descriptor flag, data_ep_set_params() promotes ep->curpacksize to ep->maxpacksize. However, maxsize is left at the original sample-rate derived value. Since u->buffer_size is allocated as maxsize * packets, the resulting DMA buffer is far too small for the requested transfer length. When the USB host controller streams up to curpacksize bytes per packet, it writes past the end of the buffer via DMA, corrupting kernel heap memory. Update maxsize to curpacksize when fill_max is set so that the allocated DMA buffer size matches the actual transfer request size. [ changed to reassign maxsize only when ep->fill_max is set -- tiwai ] Fixes: 8fdff6a319e7 ("ALSA: snd-usb: implement new endpoint streaming model") Cc: stable@vger.kernel.org Assisted-by: Jetski:Gemini-3.6-Flash Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260728201716.2347726-1-sonalipradhan@google.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/endpoint.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 15490a87b460e..9788f4bdc5326 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1164,10 +1164,12 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) << (16 - ep->datainterval); } - if (ep->fill_max) + if (ep->fill_max) { ep->curpacksize = ep->maxpacksize; - else + maxsize = ep->curpacksize; + } else { ep->curpacksize = maxsize; + } if (snd_usb_get_speed(chip->dev) != USB_SPEED_FULL) { packs_per_ms = 8 >> ep->datainterval; From 2db4535d6af79276a64449201c5be5feffb31c64 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Tue, 28 Jul 2026 20:24:32 +0000 Subject: [PATCH 3118/3297] ALSA: usb-audio: Clamp frame size in implicit-feedback mode commit 8d7a30c50c2e58a6839634ed0acde14466d1dc61 upstream. snd_usb_handle_sync_urb() scales received sync packet sizes by the sender's stride and stores the result directly in out_packet->packet_size[i]. If a connected USB device sends an oversized sync packet, this frame count can exceed ep->maxframesize. The un-clamped frame count then propagates to the playback endpoint queue, potentially driving packet transfers beyond the endpoint's hardware frame limits. Cap the calculated frame count against ep->maxframesize in snd_usb_handle_sync_urb() to prevent oversized packets from entering the playback queue. Fixes: 28acb12014fb ("ALSA: usb-audio: use sender stride for implicit feedback") Cc: stable@vger.kernel.org Assisted-by: Jetski:Gemini-3.6-Flash Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260728202432.2354994-1-sonalipradhan@google.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/endpoint.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 9788f4bdc5326..7d96cebba53f5 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1806,11 +1806,13 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, out_packet->packets = in_ctx->packets; for (i = 0; i < in_ctx->packets; i++) { - if (urb->iso_frame_desc[i].status == 0) - out_packet->packet_size[i] = + if (urb->iso_frame_desc[i].status == 0) { + unsigned int frames = urb->iso_frame_desc[i].actual_length / sender->stride; - else + out_packet->packet_size[i] = min(frames, ep->maxframesize); + } else { out_packet->packet_size[i] = 0; + } } spin_unlock_irqrestore(&ep->lock, flags); From b0bdca3a49cf31db4abf7fcb995d14a1f4188b8b Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Mon, 15 Jun 2026 11:39:08 +0530 Subject: [PATCH 3119/3297] dmaengine: qcom: bam_dma: Fix command element mask field for BAM v1.6.0+ commit 867621ba203027338b525af6729719c544135336 upstream. BAM version 1.6.0 and later changed the behavior of the mask field in command elements for read operations. In older BAM versions, or prior implementation assumptions, the mask field was effectively ignored for read commands. However, starting from BAM v1.6.0, the mask field for read commands is repurposed to carry the upper 4 bits of the destination address, enabling support for 36-bit addressing. For write commands, the mask field continues to function as a traditional write mask. The current driver sets mask = 0xffffffff for all command elements. While this works for write operations, it breaks read operations on BAM v1.6.0+ hardware. In such cases, the hardware interprets the upper address bits as 0xf, resulting in an invalid destination address (0xf_xxxxxxxx instead of 0x0_xxxxxxxx). This leads to failures such as NAND enumeration issues observed on platforms like IPQ5424. Fix this by assigning the mask field based on command type: - For read commands: set mask = 0 (upper address bits = 0) - For write commands: retain mask = 0xffffffff Also update the bam_cmd_element structure documentation to reflect the dual purpose of the mask field across BAM versions. This ensures correct behavior on BAM v1.6.0+ while maintaining backward compatibility with older hardware. Fixes: dfebb055f73a2 ("dmaengine: qcom: bam_dma: wrapper functions for command descriptor") Tested-by: Lakshmi Sowjanya D Signed-off-by: Md Sadre Alam Reviewed-by: Frank Li Reviewed-by: Dmitry Baryshkov Cc: stable@vger.kernel.org Signed-off-by: Varadarajan Narayanan Link: https://patch.msgid.link/20260615060908.1263171-1-varadarajan.narayanan@oss.qualcomm.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- include/linux/dma/qcom_bam_dma.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/include/linux/dma/qcom_bam_dma.h b/include/linux/dma/qcom_bam_dma.h index 68fc0e643b1b9..d9d07a9ab3132 100644 --- a/include/linux/dma/qcom_bam_dma.h +++ b/include/linux/dma/qcom_bam_dma.h @@ -13,9 +13,12 @@ * supported by BAM DMA Engine. * * @cmd_and_addr - upper 8 bits command and lower 24 bits register address. - * @data - for write command: content to be written into peripheral register. - * for read command: dest addr to write peripheral register value. - * @mask - register mask. + * @data - For write command: content to be written into peripheral register. + * For read command: lower 32 bits of destination address. + * @mask - For write command: register write mask. + * For read command on BAM v1.6.0+: upper 4 bits of destination address. + * For read command on BAM < v1.6.0: ignored by hardware. + * Setting to 0 ensures 32-bit addressing compatibility. * @reserved - for future usage. * */ @@ -42,6 +45,10 @@ enum bam_command_type { * @addr: target address * @cmd: BAM command * @data: actual data for write and dest addr for read in le32 + * + * For BAM v1.6.0+, the mask field behavior depends on command type: + * - Write commands: mask = write mask (typically 0xffffffff) + * - Read commands: mask = upper 4 bits of destination address (0 for 32-bit) */ static inline void bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, @@ -50,7 +57,11 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, bam_ce->cmd_and_addr = cpu_to_le32((addr & 0xffffff) | ((cmd & 0xff) << 24)); bam_ce->data = data; - bam_ce->mask = cpu_to_le32(0xffffffff); + if (cmd == BAM_READ_COMMAND) + bam_ce->mask = cpu_to_le32(0x0); /* 32-bit addressing */ + else + bam_ce->mask = cpu_to_le32(0xffffffff); /* Write mask */ + bam_ce->reserved = 0; } /* @@ -60,7 +71,7 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, * @bam_ce: BAM command element * @addr: target address * @cmd: BAM command - * @data: actual data for write and dest addr for read + * @data: actual data for write and destination address for read */ static inline void bam_prep_ce(struct bam_cmd_element *bam_ce, u32 addr, From b10bb77e91e9702575af719a4d511f8754a3f739 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sun, 7 Jun 2026 22:57:06 +0800 Subject: [PATCH 3120/3297] e1000: fix memory leak in e1000_probe() commit 816419dfea5c88126f35eb7a1b429a1bf546665e upstream. In the e1000_probe() path, e1000_sw_init() allocates adapter->tx_ring and adapter->rx_ring. If the subsequent CE4100-specific MDIO BAR mapping fails, the error handling jumps past the ring cleanup code, leaking both allocations. Fix this leak by moving the err_mdio_ioremap label above the ring deallocation logic. This guarantees the proper release of these resources and prevents the memory leak. 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-rc6. An x86_64 allyesconfig build showed no new warnings. As we do not have a CE4100 reference platform to test with, no runtime testing was able to be performed. Fixes: 5377a4160bb65 ("e1000: Add support for the CE4100 reference platform") Cc: stable@vger.kernel.org Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Reviewed-by: Dima Ruinskiy Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/e1000/e1000_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 15160427c8b30..a668afc183938 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -1222,11 +1222,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (hw->flash_address) iounmap(hw->flash_address); +err_mdio_ioremap: kfree(adapter->tx_ring); kfree(adapter->rx_ring); err_dma: err_sw_init: -err_mdio_ioremap: iounmap(hw->ce4100_gbe_mdio_base_virt); iounmap(hw->hw_addr); err_ioremap: From 845a9cdd9b03b7b6fa8de3ee80579780350a7f65 Mon Sep 17 00:00:00 2001 From: Matt Vollrath Date: Thu, 16 Apr 2026 23:34:52 -0400 Subject: [PATCH 3121/3297] igbvf: Fix leak in TX DMA error cleanup commit 0565052b7e2f436b7f1541f4849da96dc0aa7a0e upstream. If an error is encountered while mapping TX buffers, the driver should unmap any buffers already mapped for that skb. Because count is incremented before each frag mapping, it will always match the correct number of unmappings needed when dma_error is reached. Decrementing count before the while loop in dma_error causes an off-by-one error. If any mapping was successful before an unsuccessful mapping, exactly one DMA mapping (the head) would leak. This bug was introduced by a 2010 fix for an endless loop in dma_error. All other affected drivers have already been fixed. Fixes: c1fa347f20f1 ("e1000/e1000e/igb/igbvf/ixgb/ixgbe: Fix tests of unsigned in *_tx_map()") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-4-7-opus Signed-off-by: Matt Vollrath Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igbvf/netdev.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 61dfcd8cb370a..c1f2eff615f11 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2191,8 +2191,6 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter, buffer_info->time_stamp = 0; buffer_info->length = 0; buffer_info->mapped_as_page = false; - if (count) - count--; /* clear timestamp and dma mappings for remaining portion of packet */ while (count--) { From 3b5aee6fcbf6b58112d40d19c8d31fa3f78ee668 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 12 Jul 2026 14:22:42 +0100 Subject: [PATCH 3122/3297] igc: remove napi_synchronize() in igc_down() commit 5ffab5b9589c50e4cfc0cf36ffd76c89422d4019 upstream. When an AF_XDP zero-copy application is killed abruptly, the XSK pool is torn down but NAPI keeps polling. igc_clean_rx_irq_zc() then returns the full budget on every poll, so napi_complete_done() never clears NAPI_STATE_SCHED. igc_down() calls napi_synchronize() before napi_disable(), so it spins forever waiting for that bit and the interface never goes down. Drop the napi_synchronize() and let napi_disable() do the job -- it sets NAPI_STATE_DISABLE, which forces the stuck poll to complete. Reorder it ahead of igc_set_queue_napi() so the NAPI mapping is cleared only after polling has stopped, matching the recent igb fix b1e067240379. Fixes: fc9df2a0b520 ("igc: Enable RX via AF_XDP zero-copy") Suggested-by: Maciej Fijalkowski Cc: stable@vger.kernel.org Signed-off-by: David Carlier Reviewed-by: Maciej Fijalkowski Reviewed-by: Dima Ruinskiy Tested-by: Moriya Kadosh Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igc/igc_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index ebc00ce2e3a6b..4ca242ebf3e34 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -5354,9 +5354,8 @@ void igc_down(struct igc_adapter *adapter) for (i = 0; i < adapter->num_q_vectors; i++) { if (adapter->q_vector[i]) { - napi_synchronize(&adapter->q_vector[i]->napi); - igc_set_queue_napi(adapter, i, NULL); napi_disable(&adapter->q_vector[i]->napi); + igc_set_queue_napi(adapter, i, NULL); } } From b5ee5b266f833601ac4817f6df0bc496fc376a28 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 13 Jul 2026 19:52:32 +0800 Subject: [PATCH 3123/3297] ipvs: do not propagate one-packet flag to synced conns commit a63d2dbaeb50a85d4c976b15a36e6b0c7113db5b upstream. Synced connections can be created before their destination exists. When the destination is later added, ip_vs_bind_dest() copies connection flags from the destination into cp->flags. IP_VS_CONN_F_ONE_PACKET connections are not synced. If a synced connection inherits IP_VS_CONN_F_ONE_PACKET while it is already hashed, expiry can treat it as a one-packet connection and skip unlinking the existing conn_tab node, leaving stale hash nodes pointing at a freed struct ip_vs_conn. Drop IP_VS_CONN_F_ONE_PACKET from destination flags when binding synced connections. Fixes: 26ec037f9841 ("IPVS: one-packet scheduling") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Suggested-by: Julian Anastasov Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipvs/ip_vs_conn.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index bc8ff5f15702b..83b28fe9ea8f5 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -599,6 +599,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) flags = cp->flags; /* Bind with the destination and its corresponding transmitter */ if (flags & IP_VS_CONN_F_SYNC) { + /* Synced conns are hashed, so they can not get this flag */ + conn_flags &= ~IP_VS_CONN_F_ONE_PACKET; + /* if the connection is not template and is created * by sync, preserve the activity flag. */ From a60b5da05e318d9a364dbac38c347c7f24e625e7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 23 Jul 2026 23:07:14 +0900 Subject: [PATCH 3124/3297] ksmbd: reject repeated SMB2 NEGOTIATE requests commit cb469993b3a61a72653770856d37af616d72d05f upstream. Unauthenticated client can send multiple successful SMB2 NEGOTIATE requests on one connection before SESSION_SETUP. While the connection is in KSMBD_SESS_NEED_SETUP, smb2_handle_negotiate() accepts another SMB3.1.1 NEGOTIATE and overwrites conn->preauth_info with a new allocation. Only the final allocation is freed when the connection is released, leaking one object for every additional successful request. A repeated SMB2 NEGOTIATE after a dialect has been selected is a protocol violation. MS-SMB2 section 3.3.5.4 requires the server to disconnect without replying in this case. Set the connection exiting when rejecting the request, in addition to suppressing the response. Reject SMB2 NEGOTIATE unless the connection is new or is waiting for the SMB2 NEGOTIATE that follows an SMB1 multi-protocol negotiate. Serialize both SMB1 and SMB2 negotiation paths under conn->srv_mutex, since they update connection-wide dialect and negotiation state. Move the locking contract to ksmbd_smb_negotiate_common(), where the state and dialect are selected, and add ksmbd_conn_new() for consistent state access. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Reported-by: Runa Takemoto Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/connection.h | 5 +++++ fs/smb/server/smb2pdu.c | 10 ++-------- fs/smb/server/smb_common.c | 37 ++++++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 8d985568a82fc..f8f88651166b9 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -197,6 +197,11 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn); * This is a hack. We will move status to a proper place once we land * a multi-sessions support. */ +static inline bool ksmbd_conn_new(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEW; +} + static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) { return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 64a1cfc4e8099..b610cad470ea0 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1102,6 +1102,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, * smb2_handle_negotiate() - handler for smb2 negotiate command * @work: smb work containing smb request buffer * + * The caller holds conn->srv_mutex. + * * Return: 0 */ int smb2_handle_negotiate(struct ksmbd_work *work) @@ -1115,13 +1117,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ksmbd_debug(SMB, "Received negotiate request\n"); conn->need_neg = false; - if (ksmbd_conn_good(conn)) { - pr_err("conn->tcp_status is already in CifsGood State\n"); - work->send_no_response = 1; - return rc; - } - - ksmbd_conn_lock(conn); smb2_buf_len = get_rfc1002_len(work->request_buf); smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); if (smb2_neg_size > smb2_buf_len) { @@ -1272,7 +1267,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ksmbd_conn_set_need_setup(conn); err_out: - ksmbd_conn_unlock(conn); if (rc) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index b23203a1c2865..4ad01a0fa1cf6 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -592,23 +592,46 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) struct ksmbd_conn *conn = work->conn; int ret; - conn->dialect = - ksmbd_negotiate_smb_dialect(work->request_buf); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - if (command == SMB2_NEGOTIATE_HE) { + /* + * An SMB2 NEGOTIATE is valid for a new connection, or after an + * SMB1 multi-protocol negotiate has selected SMB2. Do not allow + * a second SMB2 NEGOTIATE to replace connection-wide state + * while a session setup is pending. KSMBD_SESS_NEED_RECONNECT + * is a transient session state and does not restart transport + * negotiation. + */ + ksmbd_conn_lock(conn); + if (!ksmbd_conn_new(conn) && + !ksmbd_conn_need_negotiate(conn)) { + work->send_no_response = 1; + ksmbd_conn_set_exiting(conn); + ksmbd_conn_unlock(conn); + return 0; + } + + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); ret = smb2_handle_negotiate(work); + ksmbd_conn_unlock(conn); return ret; } if (command == SMB_COM_NEGOTIATE) { + ksmbd_conn_lock(conn); + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); if (__smb2_negotiate(conn)) { init_smb3_11_server(conn); - init_smb2_neg_rsp(work); + ret = init_smb2_neg_rsp(work); ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); - return 0; + } else { + ret = smb_handle_negotiate(work); } - return smb_handle_negotiate(work); + ksmbd_conn_unlock(conn); + return ret; } pr_err("Unknown SMB negotiation command: %u\n", command); From 2060764e0f46c3af8dc43996a790a7c2e09191d7 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Tue, 30 Jun 2026 14:57:54 -0700 Subject: [PATCH 3125/3297] mshv: fix hv_input_get_system_property struct commit d6f0248f04a96249660591e47fcf37ba98ac7ea3 upstream. Keep it in sync with the correct definition. The old code worked by chance. Fixes: e68bda71a2384 ("hyperv: Add new Hyper-V headers in include/hyperv") Cc: stable@kernel.org Signed-off-by: Wei Liu Signed-off-by: Greg Kroah-Hartman --- include/hyperv/hvhdk_mini.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index 858f6a3925b30..45827d49c9a50 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -126,8 +126,9 @@ enum hv_dynamic_processor_feature_property { struct hv_input_get_system_property { u32 property_id; /* enum hv_system_property */ + u32 reserved; union { - u32 as_uint32; + u64 as_uint64; #if IS_ENABLED(CONFIG_X86) /* enum hv_dynamic_processor_feature_property */ u32 hv_processor_feature; From f807a63d0d95680c34f677700da9148a07d7c78f Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Thu, 23 Jul 2026 18:54:54 +0800 Subject: [PATCH 3126/3297] net/smc: fix socket use-after-free during link group termination commit f621d6ebeebb6374342571e4ddf45fdbc420f6cd upstream. __smc_lgr_terminate() drops conns_lock after finding a connection in lgr->conns_all, but before taking a reference on its socket. The connection is embedded in the socket, and its registration reference protects it only while the connection remains in the tree. A concurrent close can unregister the connection and drop that reference, freeing the socket before the termination worker reaches sock_hold(). The race is reachable when close overlaps link group termination. Local stress testing reproduced the use-after-free and KASAN reported: BUG: KASAN: slab-use-after-free in __smc_lgr_terminate.part.0 [smc] Write of size 4 by task kworker/3:3 Workqueue: events smc_lgr_terminate_work [smc] __smc_lgr_terminate.part.0 [smc] The socket was allocated by smc_create(), freed through slab_free_after_rcu_debug(), and was followed by: refcount_t: addition on 0; use-after-free. __smc_lgr_terminate.part.0 [smc] Take the socket reference while conns_lock still protects the tree entry. The unregister path then cannot drop the last reference until termination has finished using the socket. Fixes: 69318b5215f2 ("net/smc: improve abnormal termination locking") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Reviewed-by: Mahanta Jambigi Link: https://patch.msgid.link/20260723105454.87016-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/smc/smc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index be0c2da83d2bf..5df4838a6386f 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1558,10 +1558,10 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft) read_lock_bh(&lgr->conns_lock); node = rb_first(&lgr->conns_all); while (node) { - read_unlock_bh(&lgr->conns_lock); conn = rb_entry(node, struct smc_connection, alert_node); smc = container_of(conn, struct smc_sock, conn); sock_hold(&smc->sk); /* sock_put below */ + read_unlock_bh(&lgr->conns_lock); lock_sock(&smc->sk); smc_conn_kill(conn, soft); release_sock(&smc->sk); From 4ae701848e4ba9e9713375fb7d82218cbd309da2 Mon Sep 17 00:00:00 2001 From: David Lee Date: Mon, 13 Jul 2026 09:59:15 +0000 Subject: [PATCH 3127/3297] netfilter: ipset: do not update comments from kernel-side hash adds commit f30415929be8aeb002d557c8d3f7ab2d2188003a upstream. mtype_resize() copies comment pointers with memcpy(), not the comment objects themselves. During the window after an entry has been copied but before the table swap and backlog replay, the old table is still published for packet-side updates while the replacement-table entry already holds the same ip_set_comment_rcu pointer. If xt_SET --add-set ... --exist hits that old entry in this window, mtype_add() calls ip_set_init_comment() even though packet-side adds carry no comment payload. That call frees the shared comment through the old entry, so the replacement-table entry now holds a stale pointer. When the queued add is replayed on the new table, mtype_add() calls ip_set_init_comment() again and strlen() dereferences the stale pointer. Fix this in mtype_add() by skipping ip_set_init_comment() when ext->target marks a packet-side add. Userspace adds still update comments, while packet-side adds can no longer free comment storage shared with a resize copy. Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports") Cc: stable@vger.kernel.org Signed-off-by: David Lee Assisted-by: Codex:gpt-5.5 Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 0426d012185db..4c1082e38e3d1 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -984,7 +984,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, #endif if (SET_WITH_COUNTER(set)) ip_set_init_counter(ext_counter(data, set), ext); - if (SET_WITH_COMMENT(set)) + if (SET_WITH_COMMENT(set) && !ext->target) ip_set_init_comment(set, ext_comment(data, set), ext); if (SET_WITH_SKBINFO(set)) ip_set_init_skbinfo(ext_skbinfo(data, set), ext); From 88752b811f72aa7a16de6dad4b25031d7308673e Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 27 Jul 2026 10:36:59 +0200 Subject: [PATCH 3128/3297] of/address: Fix NULL bus dereference in of_pci_range_parser_one() commit bba13ad17b1a11b3f1ed9b3a5d556191d7755a59 upstream. The bus matching rework made of_match_bus() return NULL for nodes with ranges/dma-ranges but no local #address-cells. parser_init() stored that NULL bus, and the range iterator later dereferenced it. Reject such nodes in parser_init(), leaving an explicit empty iterator for callers that ignore the init return, and make of_dma_get_max_cpu_address() honour the init failure so a rejected node cannot clamp the DMA limit. Fixes: 64ee3cf096ac ("of/address: Rework bus matching to avoid warnings") Cc: stable@vger.kernel.org Signed-off-by: Carlo Caione Link: https://patch.msgid.link/20260727-of-range-parser-null-bus-v3-1-be01b708a4ce@baylibre.com Signed-off-by: Rob Herring (Arm) Signed-off-by: Greg Kroah-Hartman --- drivers/of/address.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/of/address.c b/drivers/of/address.c index f0f8f0dd191cf..07d90831dec08 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -757,6 +757,7 @@ EXPORT_SYMBOL(of_property_read_reg); static int parser_init(struct of_pci_range_parser *parser, struct device_node *node, const char *name) { + const __be32 *range; int rlen; parser->node = node; @@ -765,12 +766,20 @@ static int parser_init(struct of_pci_range_parser *parser, parser->ns = of_bus_n_size_cells(node); parser->dma = !strcmp(name, "dma-ranges"); parser->bus = of_match_bus(node); + parser->range = NULL; + parser->end = NULL; - parser->range = of_get_property(node, name, &rlen); - if (parser->range == NULL) + range = of_get_property(node, name, &rlen); + if (!range) return -ENOENT; - parser->end = parser->range + rlen / sizeof(__be32); + if (!parser->bus || + !OF_CHECK_COUNTS(parser->na, parser->ns) || + !OF_CHECK_ADDR_COUNT(parser->pna)) + return -EINVAL; + + parser->range = range; + parser->end = range + rlen / sizeof(__be32); return 0; } @@ -796,7 +805,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, int na = parser->na; int ns = parser->ns; int np = parser->pna + na + ns; - int busflag_na = parser->bus->flag_cells; + int busflag_na; if (!range) return NULL; @@ -804,6 +813,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, if (!parser->range || parser->range + np > parser->end) return NULL; + busflag_na = parser->bus->flag_cells; + range->flags = parser->bus->get_flags(parser->range); range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na); @@ -980,8 +991,7 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) np = of_root; ranges = of_get_property(np, "dma-ranges", &len); - if (ranges && len) { - of_dma_range_parser_init(&parser, np); + if (ranges && len && !of_dma_range_parser_init(&parser, np)) { for_each_of_range(&parser, &range) if (range.cpu_addr + range.size > cpu_end) cpu_end = range.cpu_addr + range.size - 1; From bed792737b5f1ba773054dbe984502958bdfe6ce Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Fri, 24 Jul 2026 00:38:41 +0800 Subject: [PATCH 3129/3297] tipc: avoid use-after-free in poll trace queue dumps commit b4f1719dfea023220e0e6bd892b087d76b2a6a49 upstream. TIPC socket tracepoints dump queue state through tipc_sk_dump(). Most queue-dump callsites already serialize that walk under the socket lock or sk->sk_lock.slock, but tipc_poll() calls trace_tipc_sk_poll(..., TIPC_DUMP_ALL, ...) without holding either lock. That lets the poll trace path reach tipc_list_dump() and backlog head/tail dumping while another context dequeues and frees an skb, leaving the trace helper dereferencing a stale queue entry. Stop the unlocked poll trace site from requesting queue dumps. Other queue dump trace callsites keep their existing output under the locking they already provide, while poll still emits the event itself without walking live queue members from an unlocked context. Fixes: b4b9771bcbbd ("tipc: enable tracepoints in tipc") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/f8119abd5e5ecc400597de667ae9d39656de56d0.1784794294.git.zihanx@nebusec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/tipc/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 24013455fbf0e..7823bbcd482be 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -795,7 +795,7 @@ static __poll_t tipc_poll(struct file *file, struct socket *sock, __poll_t revents = 0; sock_poll_wait(file, sock, wait); - trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_ALL, " "); + trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_NONE, " "); if (sk->sk_shutdown & RCV_SHUTDOWN) revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; From c9dcfe6b8b71369e1d732e2ff622c3696a2f032c Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 28 Jul 2026 19:53:25 +0800 Subject: [PATCH 3130/3297] wifi: mwifiex: use the subframe length when parsing A-MSDU TDLS frames commit 99a948382af8a225e2d5e54a7052158cd6281cc6 upstream. mwifiex_11n_dispatch_amsdu_pkt() splits an A-MSDU with ieee80211_amsdu_to_8023s() and walks the resulting subframes. For each subframe it passes the subframe data pointer to mwifiex_process_tdls_action_frame(), but pairs it with skb->len, the length of the A-MSDU parent, instead of rx_skb->len: rx_skb = __skb_dequeue(&list); rx_hdr = (struct rx_packet_hdr *)rx_skb->data; if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { mwifiex_process_tdls_action_frame(priv, (u8 *)rx_hdr, skb->len); } The parent is not a valid description of that buffer, and may not be valid memory at all. ieee80211_amsdu_to_8023s() ends with if (!reuse_skb) dev_kfree_skb(skb); and it only sets reuse_skb when the parent is linear, is not a head_frag, and is being consumed as the *last* subframe. So when the parent does not qualify for reuse it has already been freed, and the read of skb->len is a use-after-free. When it is reused, skb->len is the length of the last subframe, applied to every earlier subframe, which over-states the buffer whenever an earlier subframe is shorter. The callee cannot absorb a wrong length, because it derives its own ceiling from the value it is given. Each frame type computes ies_len = len - sizeof(struct ethhdr) - TDLS_*_FIX_LEN; and the element walk is then bounded entirely against that ceiling, for (end = pos + ies_len; pos + 1 < end; pos += 2 + pos[1]) { u8 ie_len = pos[1]; if (pos + 2 + ie_len > end) break; so a too-large len moves end past the end of the subframe and the walk reads and copies beyond it. The A-MSDU layout is chosen by the sender, which makes the difference between the last subframe and a shorter earlier one remotely selectable. Reaching this requires TDLS support in firmware and the TDLS ethertype on the subframe. The other caller, mwifiex_process_rx_packet(), is correct: it passes a pointer and a length that describe the same region of the RX buffer. Pass rx_skb->len, the length of the subframe actually being parsed. Fixes: 776f742040ca ("mwifiex: fix AMPDU not setup on TDLS link problem") Assisted-by: Codex:gpt-5.6-sol Assisted-by: Kimi:K3 Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260728115325.19128-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index f3397dc6c422e..c27814c1ccb1b 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -44,7 +44,7 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { mwifiex_process_tdls_action_frame(priv, (u8 *)rx_hdr, - skb->len); + rx_skb->len); } if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) From fdc1d702bf3001586221fa07e598e876a0a854c5 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:02 +0200 Subject: [PATCH 3131/3297] binfmt_misc: restore write access when removing an entry commit db1856ea9196cf6e015d12199a34c0b9313c7bfa upstream. Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache. Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag. Restore write access in put_binfmt_handler() before closing the interpreter file. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-1-a162f7cb58d6@kernel.org Fixes: 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_misc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index d44529d5ecf5f..c95b6217dbc29 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -161,8 +161,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc, static void put_binfmt_handler(Node *e) { if (refcount_dec_and_test(&e->users)) { - if (e->flags & MISC_FMT_OPEN_FILE) + if (e->flags & MISC_FMT_OPEN_FILE) { + exe_file_allow_write_access(e->interp_file); filp_close(e->interp_file, NULL); + } kfree(e); } } From 255a758697da87a205e072e0cfc35897b8f743b1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:03 +0200 Subject: [PATCH 3132/3297] binfmt_misc: use exe_file_deny_write_access() for the interpreter clone commit fa5990ca8fd917003e526036bcc50413edb9722c upstream. For MISC_FMT_OPEN_FILE entries load_misc_binary() clones the registered interpreter file and denies write access to the clone via plain deny_write_access(). The clone is installed as bprm->interpreter and later released by the exec machinery through exe_file_allow_write_access() which skips the i_writecount increment for files with FMODE_FSNOTIFY_HSM set. The deny and allow side can therefore come to different conclusions when pre-content watches are in play: if a pre-content watch is added to the interpreter after registration every subsequent exec through that entry takes a write denial on the clone that is never paired with a write allowance, driving the interpreter inode's i_writecount further down with each exec and leaving the interpreter unwritable even after the entry and all its users are gone. Take the write denial via exe_file_deny_write_access() so both sides of the pairing base their decision on the same file mode, and propagate failure instead of silently ignoring it: an interpreter that is concurrently open for writing now fails the exec with ETXTBSY, exactly like an interpreter freshly opened via open_exec() would. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-2-a162f7cb58d6@kernel.org Fixes: 0357ef03c94e ("fs: don't block write during exec on pre-content watched files") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_misc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index c95b6217dbc29..c8dd523ea389f 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -248,8 +248,14 @@ static int load_misc_binary(struct linux_binprm *bprm) if (fmt->flags & MISC_FMT_OPEN_FILE) { interp_file = file_clone_open(fmt->interp_file); - if (!IS_ERR(interp_file)) - deny_write_access(interp_file); + if (!IS_ERR(interp_file)) { + int err = exe_file_deny_write_access(interp_file); + + if (err) { + fput(interp_file); + interp_file = ERR_PTR(err); + } + } } else { interp_file = open_exec(fmt->interpreter); } From 840bb9c49c3e75fb32b593d67ab32f6b77122262 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:04 +0200 Subject: [PATCH 3133/3297] binfmt_misc: reject a flag character as the field delimiter commit 8e85d50ba1117fd446bf9a250bd8a97d48384bdc upstream. The registration string starts with a user chosen delimiter that separates the individual fields. So that the field parsers terminate even on a truncated string create_entry() pads the buffer with that same delimiter: memset(buf + count, del, 8); Most fields are scanned for the delimiter with strchr()/scanarg() and happily stop on the padding. The flags field is different: instead of scanning for the delimiter check_special_flags() consumes the flag characters 'P', 'O', 'C' and 'F' and stops at the first byte that is none of them, relying on the trailing delimiter to end the scan. If the delimiter is itself a flag character the padding no longer acts as a terminator. The scan swallows all eight padding bytes and keeps reading past the end of the allocation until it hits a byte that is not a flag character. For example registering PaPEPPxPPiP with 'P' as the delimiter (name "a", type extension, magic "x", interpreter "i", empty flags) leaves the flag scan running off the end of the buffer. The registration is rejected in the end because the parser does not stop exactly at buf + count, but only after the out of bounds read has already happened. With an unlucky allocation layout the scan can walk into an unmapped page; under KASAN it is reported as a slab out of bounds read. binfmt_misc mounts are available to unprivileged users in a user namespace so the read is reachable without privileges. Reject a delimiter that is one of the flag characters up front. Such a registration was always rejected anyway, only after the out of bounds read, so no valid registration string changes meaning. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-3-a162f7cb58d6@kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index c8dd523ea389f..4364849294792 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -383,6 +383,10 @@ static Node *create_entry(const char __user *buffer, size_t count) pr_debug("register: delim: %#x {%c}\n", del, del); + /* A flag-char delimiter runs the flag scan off the buffer. */ + if (del == 'P' || del == 'O' || del == 'C' || del == 'F') + goto einval; + /* Pad the buffer with the delim to simplify parsing below. */ memset(buf + count, del, 8); From 4dad8ca637d44d5a6d5c23fa80c9e2e455198c2b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 28 Jul 2026 14:26:32 +0200 Subject: [PATCH 3134/3297] binfmt_misc: don't let an 'F' entry pin its own instance commit 79055d82772b9584f259b747fe40ff56a076678d upstream. An entry registered with 'F' opens its interpreter at registration time and holds that file until the entry is freed. Any entry nobody removes by hand only gets closed once the binfmt_misc superblock is shut down. If the interpreter lives on a mount that keeps that superblock alive the two pin each other: binfmt_misc sb -> inode -> entry -> interp_file -> vfsmount -> binfmt_misc sb TL;DR the file is never closed. Once the mount namespace is gone there is nothing left to unregister through either. There are two ways to trigger this bug: - Point the interpreter at the instance itself. Its files are regular files owned by the mounter and both bm_get_inode() and simple_fill_super() leave i_op at empty_iops. So notify_change() falls back to simple_setattr() and chmod +x works. We never set SB_I_NOEXEC and so open_exec() accepts it. - Use the instance as an overlayfs lower layer. The overlay superblock holds a clone_private_mount() of every layer until it is destroyed and that clone is in no namespace. So umount_tree() never reaches it. That's a DoS. And it isn't only the superblock that leaks. It pins the user namespace it was mounted in, so every iteration permanently eats one of the caller's user namespace charges. So let's just do the sane thing. SB_I_NOEXEC makes open_exec() fail on the instance's own files and s_stack_depth makes overlayfs reject the layer before it ever takes a clone. That also covers the ecryptfs and fuse passthrough variants. What 'F' promises is unchanged. The stable tag is narrower than the Fixes tags on purpose. Before sandboxed mounts this needed global root against the single instance everyone shares, and the change doesn't apply to those trees anyway. Note that SB_I_NODEV is implicitly raised for userns mounts but raise it explicitly here as well. Link: https://patch.msgid.link/20260728-work-binfmt_misc-selfpin-v1-1-74df5daeca5b@kernel.org Fixes: 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers") Fixes: 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts") Cc: stable@vger.kernel.org # v6.7+ Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 4364849294792..5edc41bb100ec 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -956,6 +956,10 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) if (WARN_ON(user_ns != current_user_ns())) return -EINVAL; + /* Never exec off this instance and never let anything stack on it. */ + sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; + sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH; + /* * Lazily allocate a new binfmt_misc instance for this namespace, i.e. * do it here during the first mount of binfmt_misc. We don't need to From 63b361f228866b97e64026cae16d61b24add4c59 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 26 Jul 2026 08:12:38 -0600 Subject: [PATCH 3135/3297] io_uring/net: initialize mshot_len for send commit c77ffbc980efb337fd750c337d8157d532ea14e5 upstream. Commit: 6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap") changed how io_mshot_prep_retry() set sr->len, and added the same initialization in io_mshot_prep_retry(). But it neglected to touch the send path, which may also uses the mshot retry path. Ensure that sr->mshot_len always gets initialized correctly. Fixes: 6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap") Cc: stable@vger.kernel.org Reported-by: Sung Keum Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/io_uring/net.c b/io_uring/net.c index 3ab2bfca1bd5d..f8b7cf2b2fa54 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -433,6 +433,7 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) req->flags |= REQ_F_NOWAIT; if (req->flags & REQ_F_BUFFER_SELECT) sr->buf_group = req->buf_index; + sr->mshot_total_len = sr->mshot_len = 0; if (sr->flags & IORING_RECVSEND_BUNDLE) { if (req->opcode == IORING_OP_SENDMSG) return -EINVAL; From b11907c905fa08eda925395f0724b7a409870f65 Mon Sep 17 00:00:00 2001 From: Link Lin Date: Tue, 21 Jul 2026 00:55:33 +0000 Subject: [PATCH 3136/3297] mm/page_reporting: use system_freezable_wq to fix UAF during suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0b45f6927a14914ff685fe0e6f9d11232a1e03df upstream. During PM freeze (e.g. S3 suspend or S4 hibernation), device drivers like virtio_balloon reset their underlying virtio devices and delete their virtqueues via vdev->config->del_vqs(). However, page reporting work (page_reporting_process) was scheduled on the global system_wq. Because system_wq lacks the WQ_FREEZABLE flag, the PM freezer skips it, leaving page_reporting_process active during suspend. If pages are freed into the buddy allocator while suspending (for example, when core MM invokes the balloon shrinker during S4 hibernation image saving), page reporting triggers virtballoon_free_page_report() on deleted virtqueues, resulting in a Use-After-Free / General Protection Fault: [ 196.795226] general protection fault, probably for non-canonical address 0xaa1436fe70dae6df: 0000 [#1] SMP NOPTI [ 196.825967] Workqueue: events page_reporting_process [ 196.831038] RIP: 0010:virtqueue_add_split+0x233/0x4c0 [virtio_ring] [ 196.927073] virtballoon_free_page_report+0x3a/0xe0 [virtio_balloon] [ 196.946943] page_reporting_process+0x370/0x4f0 Fix this by switching page reporting work to system_freezable_wq. This ensures that the PM freezer pauses page_reporting_process before device drivers destroy their reporting virtqueues. Because the reporting worker is frozen, memory reclamation/freeing (e.g. via shrinker execution) can safely return pages to MM during freeze without triggering unfrozen reporting work on deleted virtqueues. This aligns with the driver's existing design. The comment in virtballoon_freeze() states: /* * The workqueue is already frozen by the PM core before this * function is called. */ Testing: I have verified these fixes using Google’s virtualization infrastructure by running continuous suspend/resume iterations (40+ cycles) while churning memory using stress-ng (`stress-ng --vm 4 --vm-bytes 60% --timeout 1`) to constantly create free pages for the buddy allocator. We also set the `page_reporting_order` parameter to 0 to make the page reporting worker highly sensitive, forcing it to pick up any 4K free pages. This confirmed that the UAF crashes are no longer reproducible. Link: https://lore.kernel.org/20260721005603.1710551-1-linkl@google.com Fixes: 36e66c554b5c ("mm: introduce Reported pages") Signed-off-by: Link Lin Suggested-by: David Hildenbrand (Arm) Suggested-by: Michael S. Tsirkin Acked-by: David Rientjes Acked-by: David Hildenbrand (Arm) Acked-by: Michael S. Tsirkin Cc: Alexander Duyck Cc: Greg Thelen Cc: James Houghton Cc: Jason Wang Cc: Jiaqi Yan Cc: Vlastimil Babka Cc: Xuan Zhuo Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/page_reporting.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mm/page_reporting.c b/mm/page_reporting.c index e4c428e61d8c1..68c791d243b1e 100644 --- a/mm/page_reporting.c +++ b/mm/page_reporting.c @@ -80,7 +80,8 @@ __page_reporting_request(struct page_reporting_dev_info *prdev) * now we are limiting this to running no more than once every * couple of seconds. */ - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY); + queue_delayed_work(system_freezable_wq, &prdev->work, + PAGE_REPORTING_DELAY); } /* notify prdev of free page reporting request */ @@ -343,7 +344,8 @@ static void page_reporting_process(struct work_struct *work) */ state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE); if (state == PAGE_REPORTING_REQUESTED) - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY); + queue_delayed_work(system_freezable_wq, &prdev->work, + PAGE_REPORTING_DELAY); } static DEFINE_MUTEX(page_reporting_mutex); From 332a546b4ee567d182f641fedbe8a5f24b7f3f86 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 16 Jul 2026 06:42:18 -0700 Subject: [PATCH 3137/3297] mm: memcg: initialize *locked in memcg1_oom_prepare() stub commit 1833ce36b35426504c64600c94f322437ea44bb2 upstream. mem_cgroup_oom() passes an uninitialized "locked" to memcg1_oom_prepare() and reads it back in memcg1_oom_finish(): bool locked, ret; ... if (!memcg1_oom_prepare(memcg, &locked)) return false; ret = mem_cgroup_out_of_memory(memcg, mask, order); memcg1_oom_finish(memcg, locked); This relies on memcg1_oom_prepare() setting *locked whenever it returns true. The CONFIG_MEMCG_V1=y version does, but the stub used when CONFIG_MEMCG_V1=n returns true without touching *locked, so memcg1_oom_finish() consumes an uninitialized value. On a memcg OOM this is reported by UBSAN: UBSAN: invalid-load in mm/memcontrol.c:1932:27 load of value 0 is not a valid value for type 'bool' (aka '_Bool') Initialize *locked to false in the stub; with cgroup v1 compiled out there is no OOM lock to take. Link: https://lore.kernel.org/20260716-memcg-oom-uninit-locked-v2-1-63631d878eb4@debian.org Fixes: e93d4166b40a ("mm: memcg: put cgroup v1-specific code under a config option") Signed-off-by: Breno Leitao Reviewed-by: Joshua Hahn Acked-by: Johannes Weiner Reviewed-by: SeongJae Park Acked-by: Shakeel Butt Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Shakeel Butt Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memcontrol-v1.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol-v1.h b/mm/memcontrol-v1.h index 6358464bb4160..4cdbab2bffe69 100644 --- a/mm/memcontrol-v1.h +++ b/mm/memcontrol-v1.h @@ -103,7 +103,11 @@ static inline void memcg1_remove_from_trees(struct mem_cgroup *memcg) {} static inline void memcg1_soft_limit_reset(struct mem_cgroup *memcg) {} static inline void memcg1_css_offline(struct mem_cgroup *memcg) {} -static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) { return true; } +static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) +{ + *locked = false; + return true; +} static inline void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked) {} static inline void memcg1_oom_recover(struct mem_cgroup *memcg) {} From 0309ebbc570000ea0df11c06b69798e5860c5f6f Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Fri, 24 Jul 2026 00:52:48 +0800 Subject: [PATCH 3138/3297] net: bridge: stop fast-leave after deleting a port group commit a39789f211b8a4125f0c70e05b30cf715f4f187d upstream. br_multicast_leave_group() iterates mp->ports with pp = &p->next in its fast-leave path. After br_multicast_del_pg() removes p, continuing the loop advances pp through the deleted entry. If multicast-to-unicast was enabled, the bridge can hold multiple port groups for the same port and group with different source MAC addresses. Once multicast-to-unicast is disabled, br_port_group_equal() matches those entries by port only. A fast leave can then delete one entry and continue from its stale next pointer, leaving mp->ports pointing at a deleted port group. Fast leave only needs to remove one matching port group. Break after br_multicast_del_pg() so the loop stops before dereferencing the removed entry. Fixes: 6db6f0eae605 ("bridge: multicast to unicast") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/1cf0898872ef7c72d5f4c0304414a192c6dac591.1784707712.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_multicast.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index b77b3250afbd2..ce34881a9d96e 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -3686,6 +3686,7 @@ br_multicast_leave_group(struct net_bridge_mcast *brmctx, p->flags |= MDB_PG_FLAGS_FAST_LEAVE; br_multicast_del_pg(mp, p, pp); + break; } goto out; } From dc3ab04220667f254f4348572b2a0b3febff89fb Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Fri, 24 Jul 2026 00:48:52 +0800 Subject: [PATCH 3139/3297] net: ipv6: clear suppressed fib6 rule result commit 6aea62e433fe1b586202a5fee8b5807ce635e1d7 upstream. fib6_rule_suppress() drops a suppressed route with ip6_rt_put_flags(), but leaves res->rt6 pointing at the released rt6_info. If no later rule supplies a replacement, fib6_rule_lookup() still sees res.rt6 and returns that stale dst to its caller. A suppressing rule can therefore leak a released route back to rt6_lookup(), and the next put hits rcuref_put_slowpath() from dst_release(). Clear res->rt6 when suppressing the route so suppressed lookups fall through to the null dst instead of reusing the released one. Fixes: cdef485217d3 ("ipv6: fix memory leak in fib6_rule_suppress") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/4b8acb7787d54e440155585dd32ebdf0bef7d122.1784710966.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/fib6_rules.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index fd5f7112a51ff..3024e77b3847b 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -305,6 +305,7 @@ INDIRECT_CALLABLE_SCOPE bool fib6_rule_suppress(struct fib_rule *rule, suppress_route: ip6_rt_put_flags(rt, flags); + res->rt6 = NULL; return true; } From 4ef801b838d85c0ea5852c50667f7344ce3b6cd0 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Sun, 19 Jul 2026 22:57:40 +0800 Subject: [PATCH 3140/3297] net: pktgen: fix proc entry use-after-free commit 817ff6efdb7f484ea547218e11e17d8e43daa3b4 upstream. pktgen_change_name() replaces pkt_dev->entry while holding t->if_lock. pktgen_remove_device() removes the same entry before _rem_dev_from_if_list() takes that lock. This allows the following interleaving: CPU 0 (NETDEV_CHANGENAME) CPU 1 (kpktgend) if_lock(t) proc_remove(pkt_dev->entry) proc_remove(pkt_dev->entry) pkt_dev->entry = proc_create_data(...) if_unlock(t) The kthread can pass the stale proc_dir_entry to proc_remove() after the rename path has freed it. A reproducer with a widened race window reports: BUG: KASAN: slab-use-after-free in proc_remove+0x78/0x80 Read of size 8 at addr ffff8881478fea70 by task kpktgend_0/67 Call Trace: proc_remove+0x78/0x80 pktgen_remove_device.isra.0+0x11c/0x4c0 pktgen_thread_worker+0x1214/0x6bc0 kthread+0x2c6/0x3b0 Allocated by task 95: __proc_create+0x204/0x790 proc_create_data+0x72/0xe0 pktgen_thread_write+0xd61/0x1510 Freed by task 28: kmem_cache_free+0xcb/0x3d0 proc_free_inode+0x5b/0x80 rcu_core+0x50a/0x1850 The buggy address belongs to the object at ffff8881478fea00 which belongs to the cache proc_dir_entry of size 192 Move proc_remove() into the if_lock-protected list removal helper. Keep it before list_del_rcu() to preserve the ordering required by add_device(). The rename path must then finish replacing the entry before removal, or it observes that the device is no longer on the list. Fixes: 39df232f1a9b ("[PKTGEN]: fix device name handling") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260719145740.2888967-1-nicoyip.dev@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/pktgen.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index d41b03fd1f636..a180c061a02d9 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3971,6 +3971,7 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t, struct pktgen_dev *p; if_lock(t); + proc_remove(pkt_dev->entry); list_for_each_safe(q, n, &t->if_list) { p = list_entry(q, struct pktgen_dev, list); if (p == pkt_dev) @@ -4000,9 +4001,6 @@ static int pktgen_remove_device(struct pktgen_thread *t, * list to determine if interface already exist, avoid race * with proc_create_data() */ - proc_remove(pkt_dev->entry); - - /* And update the thread if_list */ _rem_dev_from_if_list(t, pkt_dev); #ifdef CONFIG_XFRM From e4b98f9778dfca9463cab234b9996a42182fe3f3 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 11 Jul 2026 15:09:32 +0200 Subject: [PATCH 3141/3297] powerpc/ps3: Fix map failure path in dma_ioc0_map_pages() commit 0bb024f11d120abff3e8db9144a585b9d7fb8459 upstream. If lv1_put_iopte() fails in dma_ioc0_map_pages(), the error path decrements iopage but keeps using the failed mapping's offset. As a result, it repeatedly tries to invalidate the failed IOPTE slot and leaves the already installed IOPTEs valid. Recompute offset and invalidate the installed IOPTEs instead. Fixes: 6bb5cf102541 ("[POWERPC] PS3: System-bus rework") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Geert Uytterhoeven Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260711130931.740719-3-thorsten.blum@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/ps3/mm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index 1326de55fda65..e04952bb4360b 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -615,6 +615,7 @@ static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, fail_map: for (iopage--; 0 <= iopage; iopage--) { + offset = (1 << r->page_size) * iopage; lv1_put_iopte(0, c->bus_addr + offset, c->lpar_addr + offset, From 180ff4c81faf01ec4e06082c9daa7c40518ead89 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 22 Jun 2026 08:47:22 -0400 Subject: [PATCH 3142/3297] um: vector: fix use-after-free in vector_mmsg_rx() commit af421e9aed3920c7ac88c24daa48606c7112feca upstream. When vector_mmsg_rx() discards a packet whose overlay header fails verify_header(), it frees the skb and continues the loop: if (header_check < 0) { dev_kfree_skb_irq(skb); vp->estats.rx_encaps_errors++; continue; } The normal and short-packet paths fall through to the bottom of the loop body, which clears the consumed slot and advances the cursors: (*skbuff_vector) = NULL; mmsg_vector++; skbuff_vector++; The verify_header() < 0 path skips that via continue, so the freed skb is left in skbuff_vector[] and the cursors do not advance. The next iteration reads the same slot, gets the freed skb, and frees it again, producing a refcount underflow / use-after-free in the RX path. Discard the slot the same way the other paths do before continuing. Only transports whose verify_header() can return negative are affected: GRE and L2TPv3 do so on a cookie/session-id mismatch (raw/tap do not), so any peer on such a transport can trigger it without authentication. Fixes: 49da7e64f33e ("High Performance UML Vector Network Driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Richard Weinberger Signed-off-by: Greg Kroah-Hartman --- arch/um/drivers/vector_kern.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c index 25d9258fa5926..02d1ad3052f1d 100644 --- a/arch/um/drivers/vector_kern.c +++ b/arch/um/drivers/vector_kern.c @@ -1000,6 +1000,9 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget) */ dev_kfree_skb_irq(skb); vp->estats.rx_encaps_errors++; + (*skbuff_vector) = NULL; + mmsg_vector++; + skbuff_vector++; continue; } if (header_check > 0) { From 3bd35a5e272a1b7a3fb43acad9bdc599281b13fa Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 29 Jul 2026 07:44:40 -0700 Subject: [PATCH 3143/3297] uprobes: Fix NULL pointer dereference in hprobe_expire() commit cc679d7a6303e84d769f2afcde1fc51c51f127cd upstream. Forking a task that has a pending uretprobe can oops the kernel with a NULL pointer dereference in the clone() path: BUG: kernel NULL pointer dereference, address: 0000000000000018 Oops: 0002 [#1] SMP NOPTI RIP: 0010:hprobe_expire CR2: 0000000000000018 Call Trace: uprobe_copy_process copy_process kernel_clone __x64_sys_clone do_syscall_64 entry_SYSCALL_64_after_hwframe This was found on real hosts on Meta fleet. I've got the impression that this is what is happening: CPU 1 CPU 2 (traced task) ----- ------------------- hit uprobe, prepare_uretprobe(): hprobe LEASED, refcount >= 1 uprobe_unregister() put_uprobe(): refcount -> 0 fork() -> dup_utask() hprobe_expire(hprobe, true) try_get_uprobe() -> NULL get_uprobe(NULL) <-- Oops Only take the extra reference when the uprobe is non-NULL; a NULL means it is gone and is the correct value to return. Fixes: dd1a7567784e ("uprobes: SRCU-protect uretprobe lifetime (with timeout)") Signed-off-by: Breno Leitao Signed-off-by: Peter Zijlstra (Intel) Acked-by: Oleg Nesterov Acked-by: Andrii Nakryiko Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260729-uprobe-v1-1-61896b87c867@debian.org Signed-off-by: Greg Kroah-Hartman --- kernel/events/uprobes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 725c5772429dc..a5b31443550b5 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -830,7 +830,7 @@ static struct uprobe *hprobe_expire(struct hprobe *hprobe, bool get) if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) { /* We won the race, we are the ones to unlock SRCU */ __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx); - return get ? get_uprobe(uprobe) : uprobe; + return get && uprobe ? get_uprobe(uprobe) : uprobe; } /* From b24ba0bbffe3e23eb2f6838881c1fabcb29fb9fb Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Wed, 22 Jul 2026 20:19:25 +0100 Subject: [PATCH 3144/3297] veth: convert frag_list skbs before running XDP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d0d6415963040c401e7a7e4e482a698ba52448cb upstream. A frag_list skb can reach veth with data_len set but nr_frags zero. veth_convert_skb_to_xdp_buff() only converts skbs that are shared, locked, have frags[], or do not have enough headroom. It later uses skb_is_nonlinear() to decide whether to set XDP_FLAGS_HAS_FRAGS and xdp_frags_size. That exposes frag_list data to XDP as if it were stored in frags[], but frags[] is empty. AF_XDP copy mode can then trust the bogus XDP fragment metadata, walk an empty fragment entry, and crash in memcpy() from __xsk_rcv(). Route non-linear skbs through skb_pp_cow_data() before exposing them to XDP, and only advertise XDP frags when the resulting skb has frags[]. skb_copy_bits() already handles frag_list input, and skb_pp_cow_data() builds frags[] output with skb_add_rx_frag(), which is the representation XDP multi-buffer expects. Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb") Cc: stable@vger.kernel.org Signed-off-by: Matt Fleming Reviewed-by: Toke Høiland-Jørgensen Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260722191925.2192070-1-matt@readmodwrite.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/veth.c | 4 ++-- net/core/skbuff.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 4595c61c0790b..a72b9504b8e6d 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -756,7 +756,7 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, u32 frame_sz; if (skb_shared(skb) || skb_head_is_locked(skb) || - skb_shinfo(skb)->nr_frags || + skb_is_nonlinear(skb) || skb_headroom(skb) < XDP_PACKET_HEADROOM) { if (skb_pp_cow_data(rq->page_pool, pskb, XDP_PACKET_HEADROOM)) goto drop; @@ -771,7 +771,7 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, xdp_prepare_buff(xdp, skb->head, skb_headroom(skb), skb_headlen(skb), true); - if (skb_is_nonlinear(skb)) { + if (skb_shinfo(skb)->nr_frags) { skb_shinfo(skb)->xdp_frags_size = skb->data_len; xdp_buff_set_frags_flag(xdp); } else { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 43dca2c045766..e75baa72a22e3 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -895,6 +895,18 @@ static void skb_clone_fraglist(struct sk_buff *skb) skb_get(list); } +/** + * skb_pp_cow_data() - copy skb data into page-pool backed storage + * @pool: page pool to allocate from + * @pskb: pointer to skb pointer, replaced with the copied skb on success + * @headroom: headroom to reserve in the copied skb + * + * skb_copy_bits() handles both frags[] and frag_list input. If the copied + * skb remains non-linear, it uses frags[], which is the representation used + * by XDP multi-buffer. + * + * Return: 0 on success or a negative errno on failure. + */ int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb, unsigned int headroom) { @@ -904,12 +916,6 @@ int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb, int err, i, head_off; void *data; - /* XDP does not support fraglist so we need to linearize - * the skb. - */ - if (skb_has_frag_list(skb)) - return -EOPNOTSUPP; - max_head_size = SKB_WITH_OVERHEAD(PAGE_SIZE - headroom); if (skb->len > max_head_size + MAX_SKB_FRAGS * PAGE_SIZE) return -ENOMEM; From 1235e017aa11cf01e91b613c4c5ed6aa28934fff Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:45 +0000 Subject: [PATCH 3145/3297] vxlan: re-fetch eth header after route_shortcircuit() commit 1395a676ec15a0a02a2a6d86602324f2d5fd41d5 upstream. Before route_shortcircuit(), the eth header pointer is cached from eth_hdr(skb). Inside route_shortcircuit(), pskb_may_pull() can be called, which may reallocate skb->head. In this case, returning to vxlan_xmit() leaves the cached eth pointer pointing to freed memory, leading to a use-after-free when dereferencing eth->h_dest. Fix this by updating eth = eth_hdr(skb) after calling route_shortcircuit(). Fixes: ae8840825605 ("VXLAN: Allow L2 redirection with L3 switching") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260723144249.759100-2-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/vxlan/vxlan_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index cb7b01e492fa1..c845e0550a0c2 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2798,6 +2798,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) (ntohs(eth->h_proto) == ETH_P_IP || ntohs(eth->h_proto) == ETH_P_IPV6)) { did_rsc = route_shortcircuit(dev, skb); + eth = eth_hdr(skb); if (did_rsc) f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni); } From adeed09eeb3b6aa52ed0109df198cb393a9fc2a1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:46 +0000 Subject: [PATCH 3146/3297] vxlan: unclone skb head before modifying eth header in route_shortcircuit() commit 760d36e737f2b3867762f42af36c663f55babcc4 upstream. When route_shortcircuit() performs L3 short-circuit routing, it modifies the Ethernet header of the skb in-place: memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len); memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len); If the incoming skb is cloned (for example by packet sockets, tcpdump, or dev_queue_xmit), modifying the Ethernet header without uncloning can corrupt the packet header for other readers holding a reference to the cloned skb. Ensure the skb header is writable and unshared by calling skb_cow_head(skb, 0) prior to updating the Ethernet header. If skb_cow_head() fails, abort short-circuiting and return false to allow standard packet processing fallback. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260723144249.759100-3-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/vxlan/vxlan_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index c845e0550a0c2..9c244e0f5bdf8 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2167,6 +2167,10 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha); if (diff) { + if (skb_cow_head(skb, 0)) { + neigh_release(n); + return false; + } memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len); memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len); From ff89415d34c3ab9f5312316423122e664ed3524f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:47 +0000 Subject: [PATCH 3147/3297] vxlan: use neigh_ha_snapshot() in route_shortcircuit() commit 8eca411347e1d38964f9ed2c8d3b6ab0e7e4473d upstream. The neighbour hardware address n->ha can be updated asynchronously by the neighbour subsystem, protected by n->ha_lock seqlock. Reading n->ha without holding the seqlock loop can lead to torn reads or reading a partially updated MAC address. Use neigh_ha_snapshot() in route_shortcircuit() to safely copy n->ha under read_seqbegin()/read_seqretry() lock protection before using it. Note that arp_reduce() and neigh_reduce() seem to have the same issue left for future patches. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260723144249.759100-4-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/vxlan/vxlan_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 9c244e0f5bdf8..ab50876f1831c 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2163,9 +2163,11 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) } if (n) { + u8 haddr[ETH_ALEN]; bool diff; - diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha); + neigh_ha_snapshot(haddr, n, dev); + diff = !ether_addr_equal_unaligned(eth_hdr(skb)->h_dest, haddr); if (diff) { if (skb_cow_head(skb, 0)) { neigh_release(n); @@ -2173,7 +2175,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) } memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len); - memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len); + memcpy(eth_hdr(skb)->h_dest, haddr, dev->addr_len); } neigh_release(n); return diff; From 94dee751aad627b3645d424b5d0c736d394573e9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:49 +0000 Subject: [PATCH 3148/3297] vxlan: use pskb_network_may_pull() for transmit path header pulls commit b9553558b48db54ac9273e6b98d7263ef5c1a329 upstream. In vxlan_xmit(), arp_reduce(), and vxlan_mdb_entry_skb_get(), pskb_may_pull() was being called to verify the availability of network layer headers (ARP, IPv6/ND, IP/IPv6 MDB keys). However, during transmit skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, len) only checks len bytes from skb->data rather than skb_network_offset(skb) + len, which can leave part of the network header in non-linear frags. Replace these remaining pskb_may_pull() calls with pskb_network_may_pull() to properly account for the MAC header offset. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Fixes: f564f45c4518 ("vxlan: add ipv6 proxy support") Fixes: 0f83e69f44bf ("vxlan: Add MDB data path support") Signed-off-by: Eric Dumazet Cc: stable@vger.kernel.org Reviewed-by: Vadim Fedorenko Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260723144249.759100-6-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/vxlan/vxlan_core.c | 6 +++--- drivers/net/vxlan/vxlan_mdb.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index ab50876f1831c..dd39576f4d450 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1852,7 +1852,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (dev->flags & IFF_NOARP) goto out; - if (!pskb_may_pull(skb, arp_hdr_len(dev))) { + if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) { dev_dstats_tx_dropped(dev); vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); @@ -2765,8 +2765,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) return arp_reduce(dev, skb, vni); #if IS_ENABLED(CONFIG_IPV6) else if (ntohs(eth->h_proto) == ETH_P_IPV6 && - pskb_may_pull(skb, sizeof(struct ipv6hdr) + - sizeof(struct nd_msg)) && + pskb_network_may_pull(skb, sizeof(struct ipv6hdr) + + sizeof(struct nd_msg)) && ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) { struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1); diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 4e02a669d045a..2ddcbf102b894 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -1631,7 +1631,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, switch (skb->protocol) { case htons(ETH_P_IP): - if (!pskb_may_pull(skb, sizeof(struct iphdr))) + if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) return NULL; group.dst.sa.sa_family = AF_INET; group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr; @@ -1640,7 +1640,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, break; #if IS_ENABLED(CONFIG_IPV6) case htons(ETH_P_IPV6): - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) return NULL; group.dst.sa.sa_family = AF_INET6; group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr; From ee799977d7941dbfb11049e17edd9eaf4f8820f7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:48 +0000 Subject: [PATCH 3149/3297] vxlan: use pskb_network_may_pull() in route_shortcircuit() commit 26bb2dd0a8839617e2c79ffbbe1923f8e4bab9fb upstream. route_shortcircuit() currently calls pskb_may_pull(skb, sizeof(struct iphdr)) (or ipv6hdr), which checks if bytes are available starting from skb->data. However, in vxlan_xmit(), skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, 20) only checks 20 bytes from skb->data (which is 14 bytes MAC header + 6 bytes of IP header), leaving the rest of the IP header potentially un-pulled in non-linear frags. Subsequent dereferences of ip_hdr(skb)->daddr can read beyond the pulled linear buffer length. Fix this by using pskb_network_may_pull(), which adds skb_network_offset(skb) to the length check to ensure the full network header is present in the linear buffer. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260723144249.759100-5-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- 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 dd39576f4d450..4527d2d653b11 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2115,7 +2115,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) { struct iphdr *pip; - if (!pskb_may_pull(skb, sizeof(struct iphdr))) + if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) return false; pip = ip_hdr(skb); n = neigh_lookup(&arp_tbl, &pip->daddr, dev); @@ -2141,7 +2141,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) */ if (!ipv6_stub->nd_tbl) return false; - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) return false; pip6 = ipv6_hdr(skb); n = neigh_lookup(ipv6_stub->nd_tbl, &pip6->daddr, dev); From 205feb72e5beb3140e4e1403b6cff30cf739bab9 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 26 Jul 2026 09:50:25 -0500 Subject: [PATCH 3150/3297] ublk: reset kernel-owned dev_info fields in ublk_ctrl_add_dev() commit e65848e4ce352bac9e3465099354c8b8f845391f upstream. ublk_ctrl_add_dev() memcpy()s the userspace ublksrv_ctrl_dev_info into ub->dev_info and then fixes up the fields the driver owns, but misses ->state and ->ublksrv_pid. A device added with ->state = UBLK_S_DEV_LIVE passes the "->state != UBLK_S_DEV_DEAD" test that ublk_stop_dev_unlocked() uses as its proxy for "a disk is attached", while ->ub_disk is still NULL, so DEL_DEV right after ADD_DEV oopses in del_gendisk(). UBLK_S_DEV_QUIESCED plus UBLK_F_USER_RECOVERY dies one step earlier, in ublk_force_abort_dev(). A poisoned ->state also gets START_USER_RECOVERY and the char device read/write path onto a device that was never started, and wedges START_DEV at -EEXIST. A poisoned ->ublksrv_pid just makes GET_DEV_INFO report an unrelated task as the ublk server. Reset both after the memcpy(), as ublk_detach_disk() does. Userspace only ever reads these back, so correcting them silently breaks nothing. ADD_DEV has copied ->state in unsanitized since ublk was merged, but back then it was harmless: the gendisk was allocated during ADD_DEV, and both teardown and the START_DEV -EEXIST check keyed off disk_live() rather than ->state. The oops became reachable once the disk allocation moved to START_DEV and those checks switched to ->state. Fixes: 6d9e6dfdf3b2 ("ublk: defer disk allocation") Cc: stable@vger.kernel.org Signed-off-by: Ming Lei Reviewed-by: Caleb Sander Mateos Link: https://patch.msgid.link/20260726145025.1507383-1-tom.leiming@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/ublk_drv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index cb31e96f01cdd..615d3d28e7c6f 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -3287,6 +3287,15 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) /* update device id */ ub->dev_info.dev_id = ub->ub_number; + /* + * ->state and ->ublksrv_pid are owned by the driver and only read back + * by userspace, but they come from the copied-in dev_info, so reset + * them. Otherwise a device added with ->state != DEAD looks live while + * ->ub_disk is still NULL. + */ + ub->dev_info.state = UBLK_S_DEV_DEAD; + ub->dev_info.ublksrv_pid = -1; + /* * 64bit flags will be copied back to userspace as feature * negotiation result, so have to clear flags which driver From cbb5ed3be9cae70e1c12b1991009b4e12bf4a4ca Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 09:27:58 +0900 Subject: [PATCH 3151/3297] tracing: Check return value of __register_event() in trace_module_add_events() commit ac8719969e6c3c54e939834df812bc41f25453cf upstream. trace_module_add_events() ignores the return value of __register_event() and unconditionally calls __add_event_to_tracers() for each event. If __register_event() fails (for example, if event_init() fails), the trace_event_call is not added to ftrace_events list, but __add_event_to_tracers() still creates a trace_event_file pointing to it. If module loading subsequently fails and module memory is freed, tracing state retains a stale trace_event_call pointer in trace_event_file, leading to a use-after-free when tracefs or tracing subsystem operations are later executed. Fix this by checking the return value of __register_event() and only calling __add_event_to_tracers() if event registration succeeded. Fixes: ae63b31e4d0e ("tracing: Separate out trace events from global variables") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178528487878.124250.14170824576025743236.stgit@devnote2 Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index b0842df4d6e90..a0a02b4828460 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -3771,8 +3771,8 @@ static void trace_module_add_events(struct module *mod) end = mod->trace_events + mod->num_trace_events; for_each_event(call, start, end) { - __register_event(*call, mod); - __add_event_to_tracers(*call); + if (!__register_event(*call, mod)) + __add_event_to_tracers(*call); } update_cache_events(mod); From c26a6477e149cdc7dd81915c9f3e54218fbc6bb3 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 09:28:07 +0900 Subject: [PATCH 3152/3297] tracing/filters: Fix false positive match in regex_match_full() commit c22c7b735f9810ad276014f788f9aa5c879ec238 upstream. regex_match_full() calls strncmp(str, r->pattern, len) where len is the target field buffer size. When len is smaller than r->len (the filter pattern length), strncmp() checks only len bytes of r->pattern against str. If those len bytes match, strncmp() returns 0, resulting in a false-positive match where a shorter string in a fixed-size field matches a longer filter pattern. For example, a 4-byte static string field containing "abcd" matched the filter pattern "abcdefgh" because strncmp("abcd", "abcdefgh", 4) returned 0. In this case, @len does NOT include '\0' because it is fixed-size array. Fix this by returning 0 (no match) early when len < r->len. Fixes: 1889d20922d1 ("tracing/filters: Provide basic regex support") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178528488779.124250.5571741156199253769.stgit@devnote2 Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_events_filter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index c6679caf2e9b6..9c63ea51c8bef 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1027,6 +1027,9 @@ static int regex_match_full(char *str, struct regex *r, int len) if (!len) return strcmp(str, r->pattern) == 0; + if (len < r->len) + return 0; + return strncmp(str, r->pattern, len) == 0; } From 581e5166f0780103dc91c0d8ebc801f9af4824b0 Mon Sep 17 00:00:00 2001 From: Stanislaw Pal Date: Mon, 27 Jul 2026 18:32:16 +0200 Subject: [PATCH 3153/3297] spi: spi-qpic-snand: write the feature value before executing SET_FEATURE commit 8fd62901d6bf03f274a49dd0060793cc07dd51b0 upstream. qcom_spi_send_cmdaddr() programs NAND_FLASH_CMD/NAND_EXEC_CMD and submits the descriptors, which makes the controller execute the command immediately. For SPINAND_SET_FEATURE the value to be written is only placed into NAND_FLASH_FEATURES afterwards, by qcom_spi_io_op(), in a second submission - so the chip is programmed with whatever that register happened to hold from a previous operation, and the intended value is only applied by the *next* SET_FEATURE. Measured on a TP-Link Archer AX55 v1 (IPQ5018, ESMT F50L1G41LB): writing 0x40 to the configuration register (0xb0) leaves the chip at 0x00, and the subsequent write of 0x00 leaves it at 0x40 - every write lands one operation late. This stayed unnoticed until v6.18 added SPI-NAND OTP support together with OTP entries for ESMT chips. spinand_otp_rw() enables OTP mode, reads, and disables it again, and mtd_otp_nvmem_add() does this during MTD registration. With the off-by-one, the "disable" write actually applies the previously requested value, so CFG_OTP_ENABLE ends up set: the chip stays in OTP mode, every subsequent array read returns the OTP area instead of the array (UBI reports an empty device) and all writes fail with -EIO because the OTP area is write protected. On this board that makes the whole flash unusable and the device unbootable. Write the feature value into NAND_FLASH_FEATURES as part of the same transaction, before NAND_EXEC_CMD. While at it, copy only the bytes the operation actually carries - the previous code dereferenced a 4-byte pointer on a one-byte buffer (spinand->scratchbuf). With this patch the flash contents read back bit-identical to a known-good dump of the same board taken under the vendor firmware (md5-verified across partitions), and writes work. Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Pal Reviewed-by: Md Sadre Alam Link: https://patch.msgid.link/20260727163216.109938-1-kuncy7@gmail.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-qpic-snand.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 58ceea1ea8fb4..42f8fb272fa68 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -1362,6 +1362,22 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, snandc->regs->addr0 = cpu_to_le32(op->addr.val); snandc->regs->addr1 = cpu_to_le32(0); + /* + * The feature value has to reach NAND_FLASH_FEATURES before the + * command is executed, otherwise the controller programs the chip + * with whatever the register happened to hold from a previous + * operation. + */ + if (opcode == SPINAND_SET_FEATURE) { + u32 ftr = 0; + + memcpy(&ftr, op->data.buf.out, + min_t(size_t, op->data.nbytes, sizeof(ftr))); + snandc->regs->flash_feature = cpu_to_le32(ftr); + qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, + NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); + } + qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -1399,10 +1415,8 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_ copy_ftr = true; break; case SPINAND_SET_FEATURE: - snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out); - qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, - NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); - break; + /* fully handled by qcom_spi_send_cmdaddr() */ + return 0; case SPINAND_PROGRAM_EXECUTE: case SPINAND_WRITE_EN: case SPINAND_RESET: From 2e047b4171de4c99c73d45add36210c4a5e8abf6 Mon Sep 17 00:00:00 2001 From: Vijaya Krishna Nivarthi Date: Wed, 22 Jul 2026 14:53:58 +0530 Subject: [PATCH 3154/3297] spi: qcom-qspi: Correct max DMA length to avoid 64K boundary failure commit 90ef2f2961c2dc55957dafe2f53b3efdb4675efc upstream. The maximum size for a DMA data descriptor is 64KB-1 because the size field in HW is 16 bits wide. For this reason, transfers fail at 64KB and beyond. Lower max_dma_len to 60KB so larger transfers are split into multiple DMA blocks and do not hit the failing 64KB boundary. 60KB is chosen as a safe round number below the 64KB-1 hardware limit while satisfying alignment requirements. Tested on x1e80100 (Hamoa) with SPI-NOR flash (/dev/mtd0): Without patch: dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=32768 count=2 # works dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # fails With patch: dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # works Fixes: b5762d95607e ("spi: spi-qcom-qspi: Add DMA mode support") Cc: stable@vger.kernel.org Signed-off-by: Vijaya Krishna Nivarthi Link: https://patch.msgid.link/20260722092358.459943-1-vnivarth@qti.qualcomm.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-qcom-qspi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index 3aeddada58e1c..3c08dad8bd3f9 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -759,7 +759,8 @@ static int qcom_qspi_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "could not set DMA mask\n"); host->max_speed_hz = 300000000; - host->max_dma_len = 65536; /* as per HPG */ + /* as per HPG, it is 64KB, limit to 60KB to avoid boundary condition failures */ + host->max_dma_len = 0xf000; host->dma_alignment = QSPI_ALIGN_REQ; host->num_chipselect = QSPI_NUM_CS; host->bus_num = -1; From a0bc578641d74f36315803b63748eaad0a9a623f Mon Sep 17 00:00:00 2001 From: Chris Gellermann Date: Wed, 22 Jul 2026 15:02:46 +0200 Subject: [PATCH 3155/3297] selftests/mm: fix potential wild pointer access of getline due to missing init commit 9f1d75a4ce04095afdb63d8e540092ff8151dacf upstream. This is another occurrence of using getline where the code assumes that getline allocates memory to store the line, but the pointer passed to it is uninitialized and potentially a non-null pointer. This violates the Open Group Spec[1] and caused a segfault in a similar situation in selftest/clone3/clone3_set_tid. Fix it by initializing the line pointer to NULL. The issue has been found by simply grepping through the selftest code after running into the issue in clone3_set_tid. Whether it segfaults in its current state is unknown to me. But it's good to be addressed due to defensive reasons. Link: https://lore.kernel.org/20260722130246.2135563-3-christian.gellermann@codasip.com Link: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html [1] Fixes: 26b4224d9961 ("selftests: expanding more mlock selftest") Signed-off-by: Chris Gellermann Acked-by: David Hildenbrand (arm) Reviewed-by: Lorenzo Stoakes Cc: Christian Brauner Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport 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/mlock-random-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/mlock-random-test.c b/tools/testing/selftests/mm/mlock-random-test.c index b8d7e966f44c6..aa720c52c6509 100644 --- a/tools/testing/selftests/mm/mlock-random-test.c +++ b/tools/testing/selftests/mm/mlock-random-test.c @@ -84,7 +84,7 @@ int get_proc_locked_vm_size(void) int get_proc_page_size(unsigned long addr) { FILE *smaps; - char *line; + char *line = NULL; unsigned long mmupage_size = 0; size_t size; From 5b4d4d5a29f92745dd1be8d3bf10013edf653cc7 Mon Sep 17 00:00:00 2001 From: Chris Gellermann Date: Wed, 22 Jul 2026 15:02:45 +0200 Subject: [PATCH 3156/3297] selftests/clone3: fix wild pointer access of getline due to missing init commit 8f6f9fd93cd7a5dd607ad5cd910476dd68fff3ed upstream. Patch series "selftests: Add missing initalization of pointer passed to getline", v2. This patch (of 2): Clone3_set_tid uses getline(&line, ...) in a loop to read the child's process status. The code expects that getline allocates the buffer for the line on the first loop iteration. According to the Open Group Spec[1], char *line has to be null pointer for this: > ssize_t getline(char **restrict lineptr, ...); > If *lineptr is a null pointer or if the object pointed to by *lineptr > is of insufficient size, an object shall be allocated as if by malloc() > or the object shall be reallocated as if by realloc()[...]. However, char *line is only declared, leading to an undefined value that is potentially non-null. In an example run with Musl v1.2.6, the realloc call[2] of getdelim, which implements getline, triggers a segfault: ./run_kselftest.sh --test clone3:clone3_set_tid [ 1366.165898] kselftest: Running tests in clone3 ... [ 1367.799244] clone3_set_tid[811]: unhandled signal 11 code 0x1 at 0x0000000000000000 in libc.so[68184,3fbf69f000+4c000] [ 1367.802808] CPU: 0 UID: 0 PID: 811 Comm: clone3_set_tid Not tainted .. [ 1367.804188] epc: 0x0000003fbf6b0184 [ 1367.804188] ra : 0x0000003fbf6d4664 [ 1367.804188] sp : 0x0000003fce5f2e40 [ 1367.805314] gp : 0x0000002aaab0dfb8 [ 1367.805314] tp : 0x0000003fbf6f14a8 [ 1367.805314] t0 : 0x0000003fbf63d000 ... Looking at the realloc implementation, Musl mallocs for a null pointer memory. But for a non-null pointer, it assumes it's passed a valid pointer to the heap and tries to access its meta-data. This leads to the segfault we see: void *realloc(void *p, size_t n) { if (!p) return malloc(n); if (size_overflows(n)) return 0; struct meta *g = get_meta(p); ... } Fix this by properly initializing the line pointer to NULL. Link: https://lore.kernel.org/20260722130246.2135563-1-christian.gellermann@codasip.com Link: https://lore.kernel.org/20260722130246.2135563-2-christian.gellermann@codasip.com Link: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html [1] Link: https://git.musl-libc.org/cgit/musl/tree/src/stdio/getdelim.c#n38 [2] Fixes: 41585bbeeef9 ("selftests: add tests for clone3() with *set_tid") Signed-off-by: Chris Gellermann Acked-by: David Hildenbrand (arm) Reviewed-by: Lorenzo Stoakes Cc: Christian Brauner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/clone3/clone3_set_tid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/clone3/clone3_set_tid.c b/tools/testing/selftests/clone3/clone3_set_tid.c index bfb0da2b4fdd1..4debe0756b671 100644 --- a/tools/testing/selftests/clone3/clone3_set_tid.c +++ b/tools/testing/selftests/clone3/clone3_set_tid.c @@ -141,7 +141,7 @@ int main(int argc, char *argv[]) { FILE *f; char buf; - char *line; + char *line = NULL; int status; int ret = -1; size_t len = 0; From 2047ed09bf13453b7d6f9431b112ec07984dd69b Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Sun, 12 Jul 2026 20:37:39 +0200 Subject: [PATCH 3157/3297] scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write commit 93dde0bf2f39a0f9f57fd610aa3201ce5b753433 upstream. resp_report_zones() sizes the reply buffer from the CDB allocation length. The v3 fix rounds alloc_len up with ALIGN() before deriving the descriptor count: rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> ilog2(RZONES_DESC_HD); arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1); For alloc_len in 0xFFFFFFC1..0xFFFFFFFF, ALIGN() rounds up to 0x100000000, so arr_len is 4 GB. On 32-bit, kzalloc()'s size_t is 32-bit and truncates 0x100000000 to 0; kzalloc(0) returns ZERO_SIZE_PTR, which passes the !arr check, and desc = arr + 64 is then dereferenced in the loop -> out-of-bounds write / panic. Clamp rep_max_zones to devip->nr_zones. The loop already stops at sdebug_capacity (after nr_zones zones), so a report can never hold more than nr_zones descriptors; the clamp does not change the report, it only bounds arr_len to (nr_zones + 1) * RZONES_DESC_HD, a real device property that can never reach 0x100000000. Fixes: 7db0e0c8190a ("scsi: scsi_debug: Fix buffer size of REPORT ZONES command") Suggested-by: Damien Le Moal Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Assisted-by: AuditCode-AI:2026.07 Reviewed-by: Damien Le Moal Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260712183739.83915-1-security@auditcode.ai Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_debug.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 047d56d23beab..bd7c5b60fc38f 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -5896,6 +5896,7 @@ static int resp_report_zones(struct scsi_cmnd *scp, u32 alloc_len, rep_opts, rep_len; bool partial; u64 lba, zs_lba; + u64 arr_len; u8 *arr = NULL, *desc; u8 *cmd = scp->cmnd; struct sdeb_zone_state *zsp = NULL; @@ -5917,9 +5918,12 @@ static int resp_report_zones(struct scsi_cmnd *scp, return check_condition_result; } - rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD); + rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> + ilog2(RZONES_DESC_HD); + rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones); + arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1); - arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN); + arr = kzalloc(arr_len, GFP_ATOMIC | __GFP_NOWARN); if (!arr) { mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); From a0d1693923f41d6f49083aa2446686aed09d1d79 Mon Sep 17 00:00:00 2001 From: Yuxiang Yang Date: Thu, 23 Jul 2026 22:56:23 +0000 Subject: [PATCH 3158/3297] sctp: reject stale cookies with mismatched verification tags commit 9d8da8e0a9bce4a340af60dd0446bc7eb8d07587 upstream. sctp_unpack_cookie() skips cookie expiration checks whenever an association already exists. This is broader than the exception in RFC 9260 Section 5.2.4. For an existing association, Section 5.2.4 permits an expired State Cookie only when both Verification Tags in the cookie match the current association. Otherwise, the packet SHOULD be discarded and a Stale Cookie ERROR MUST be sent. The broad check lets an expired Action A restart cookie reach sctp_sf_do_dupcook_a(). In a runtime test with the default 60 second cookie lifetime, replaying such a cookie after 65 seconds returned a COOKIE-ACK and restarted the association. Check cookie expiration unless both Verification Tags match. This preserves the Action D exception for a lost COOKIE ACK while rejecting expired cookies in all other cases. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Yuxiang Yang Acked-by: Xin Long Link: https://patch.msgid.link/20260723225623.2658868-1-yangyx22@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sctp/sm_make_chunk.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 401e5b2e78bc2..721e147091a97 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1802,9 +1802,9 @@ struct sctp_association *sctp_unpack_cookie( goto fail; } - /* Check to see if the cookie is stale. If there is already - * an association, there is no need to check cookie's expiration - * for init collision case of lost COOKIE ACK. + /* Check to see if the cookie is stale. RFC 9260 Section 5.2.4 + * exempts an expired cookie only when both Verification Tags match + * the current association. * If skb has been timestamped, then use the stamp, otherwise * use current time. This introduces a small possibility that * a cookie may be considered expired, but this would only slow @@ -1815,7 +1815,10 @@ struct sctp_association *sctp_unpack_cookie( else kt = ktime_get_real(); - if (!asoc && ktime_before(bear_cookie->expiration, kt)) { + if ((!asoc || + asoc->c.my_vtag != bear_cookie->my_vtag || + asoc->c.peer_vtag != bear_cookie->peer_vtag) && + ktime_before(bear_cookie->expiration, kt)) { suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration)); __be32 n = htonl(usecs); From 4ba5bf7ed50f235ea4581de8e7a0002f4ed287b0 Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Sat, 25 Jul 2026 03:21:06 +0000 Subject: [PATCH 3159/3297] sctp: prevent peer transport count overflow commit bd0e9289e2642f6a5c54faad304ce0f41e926d22 upstream. sctp_assoc_add_peer() increments the association's 16-bit transport_count for every new unique peer. Adding the 65,536th transport wraps the count to zero. SCTP sock_diag uses transport_count to reserve the INET_DIAG_PEERS payload, then copies one sockaddr_storage for every entry in transport_addr_list. After the wrap, a diagnostic dump reserves an empty payload and writes 8 MiB of peer addresses past the skb tail. Reject a new unique peer when transport_count has reached U16_MAX. Perform the check after the existing-peer lookup so a duplicate address continues to return its existing transport at the limit. Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Cc: stable@vger.kernel.org Signed-off-by: Asim Viladi Oglu Manizada Acked-by: Xin Long Link: https://patch.msgid.link/20260725032053.521705-1-manizada@pm.me Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 5793d71852b83..e560f7ee4adb3 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -614,6 +614,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, return peer; } + if (asoc->peer.transport_count == U16_MAX) + return NULL; + peer = sctp_transport_new(asoc->base.net, addr, gfp); if (!peer) return NULL; From 30ae663746378471460e798bf342cf90ca4055a2 Mon Sep 17 00:00:00 2001 From: Hongyan Xu Date: Wed, 29 Jul 2026 18:01:16 +0800 Subject: [PATCH 3160/3297] hwmon: (npcm750-pwm-fan): stop fan timer on device detach commit f27f6976ea269219c1259a7c2f8c6dfe782540a3 upstream. When a fan tach channel is present, npcm7xx_pwm_fan_probe() starts fan_timer. The timer callback polls tach state and rearms the timer, but the driver has no remove callback or devm cleanup action to stop it. On device detach, the devm-managed driver data and I/O mappings can be released while the timer is still pending or running. Register a devm cleanup action before starting the timer and shut the timer down synchronously from that action. This issue was found by a static analysis tool. Fixes: f1fd4a4db777 ("hwmon: Add NPCM7xx PWM and Fan driver") Cc: stable@vger.kernel.org Signed-off-by: Hongyan Xu Link: https://lore.kernel.org/r/20260729100116.790-1-getshell@seu.edu.cn Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/npcm750-pwm-fan.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 802c73def428b..bb390f6c7c3f5 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -364,6 +364,11 @@ static void npcm7xx_fan_polling(struct timer_list *t) add_timer(&data->fan_timer); } +static void npcm7xx_fan_cleanup(void *timer) +{ + timer_shutdown_sync(timer); +} + static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data, u8 fan, u8 cmp, u8 fan_id, u8 flag_int, u8 flag_mode, u8 flag_clear) @@ -1027,6 +1032,12 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS); timer_setup(&data->fan_timer, npcm7xx_fan_polling, 0); + ret = devm_add_action_or_reset(dev, + npcm7xx_fan_cleanup, + &data->fan_timer); + if (ret) + return ret; + add_timer(&data->fan_timer); break; } From 7a91d07939e07c435a79eb980eda95f71409d316 Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Thu, 23 Jul 2026 17:44:56 +0200 Subject: [PATCH 3161/3297] hwmon: (pmbus/core) notify on the hwmon device, not the i2c client commit a64a7e8a0b012ba81b0eadbd7afc84ab0dbfd70c upstream. pmbus_notify() calls sysfs_notify() and kobject_uevent() on the i2c client's kobject, but the alarm attributes live on the hwmon class device registered by pmbus_do_probe(). Notifying the parent i2c device is a no-op for both poll(POLLPRI) waiters and udev listeners: the named attribute does not exist on that kobject. Notify the hwmon device instead, so poll() wakes up and "change" uevents fire on the inX_alarm/tempX_alarm attributes when SMBALERT# reports a fault. Fixes: f469bde9afd1 ("hwmon: (pmbus/core) Notify hwmon events") Cc: stable@vger.kernel.org # v6.4+ Signed-off-by: Vincent Jardin Link: https://lore.kernel.org/r/20260723-fix_hwmon_notify_v1-v1-1-5a24c528686d@free.fr Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/pmbus_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 4e1c66670ead9..c9e3ec4552efd 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2977,8 +2977,9 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags) if (reg == sreg && page == spage && (smask & flags)) { dev_dbg(data->dev, "sysfs notify: %s", da->attr.name); - sysfs_notify(&data->dev->kobj, NULL, da->attr.name); - kobject_uevent(&data->dev->kobj, KOBJ_CHANGE); + sysfs_notify(&data->hwmon_dev->kobj, NULL, + da->attr.name); + kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE); flags &= ~smask; } From 1883a09a37fed497b9efacf736c23624a472246b Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 21 Jul 2026 23:41:47 +0900 Subject: [PATCH 3162/3297] i2c: amd-mp2: Unregister callback on adapter add failure commit 82048795242f04275a3f49ffc66ad851b6120954 upstream. amd_mp2_register_cb() stores the platform I2C context in the MP2 PCI driver's callback table before the adapter is registered. If i2c_add_adapter() fails, probe returns and devres frees the context, but the PCI driver can still dereference the stale pointer from its IRQ and system-sleep callbacks. Unregister the callback before returning the adapter registration error. Fixes: 529766e0a011 ("i2c: Add drivers for the AMD PCIe MP2 I2C controller") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Cc: # v5.2+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260721144147.31150-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-amd-mp2-plat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c index 188e24cc4d35f..9fdd6a5fb8b65 100644 --- a/drivers/i2c/busses/i2c-amd-mp2-plat.c +++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c @@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platform_device *pdev) amd_mp2_pm_runtime_put(mp2_dev); - if (ret < 0) + if (ret < 0) { dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret); + amd_mp2_unregister_cb(&i2c_dev->common); + } return ret; } From 2e4bc8422cdee2441f37dc3aa3e2c09395ba34cf Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 27 Jul 2026 15:02:05 +0700 Subject: [PATCH 3163/3297] gpio: pca953x: fix cache_only and IRQ state on restore_context() failure commit d233087c19f6607ef926ac3f47d776e2406ffd1f upstream. When pca953x_restore_context() fails, cache_only is left disabled and the IRQ left enabled, even though register synchronization may not have completed successfully. Restore cache_only and disable the IRQ again on failure, matching the state set by pca953x_save_context(). Fixes: ec5bde62019b ("gpio: pca953x: Split pca953x_restore_context() and pca953x_save_context()") Fixes: 3e38f946062b ("gpio: pca953x: fix IRQ storm on system wake up") Cc: stable@vger.kernel.org Reviewed-by: Linus Walleij Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260727080205.16353-1-phucduc.bui@gmail.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-pca953x.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index f7daeadaea57c..b18a2d0f6984e 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1370,9 +1370,20 @@ static int pca953x_restore_context(struct pca953x_chip *chip) regcache_mark_dirty(chip->regmap); ret = pca953x_regcache_sync(chip); if (ret) - return ret; + goto err; + + ret = regcache_sync(chip->regmap); + if (ret) + goto err; + + return 0; - return regcache_sync(chip->regmap); +err: + if (chip->client->irq > 0) + disable_irq(chip->client->irq); + regcache_cache_only(chip->regmap, true); + + return ret; } static void pca953x_save_context(struct pca953x_chip *chip) From 466ab0c41d5f54f71cee60619d07c4abd0ffd2cd Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Thu, 23 Jul 2026 09:41:29 +0800 Subject: [PATCH 3164/3297] gpio: pch: use raw_spinlock_t for the register lock commit a02b8950d619123da64f69b70fe1dadef217dfe4 upstream. pch_irq_type() is registered as the irq_chip .irq_set_type callback and takes chip->spinlock with spin_lock_irqsave(). This callback is reached from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is an rtmutex-backed sleeping lock, so acquiring it there is invalid. This was confirmed on a PREEMPT_RT kernel with lockdep (PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored pch_irq_type()'s locking and drove it through the real genirq carrier irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. the same __irq_set_trigger() edge that __setup_irq() takes for a requested IRQ. With the original spin_lock_irqsave() edge lockdep reported an invalid wait context, immediately followed by: BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 rt_spin_lock+0x3a/0x1c0 repro_irq_set_type+0x64/0xa0 [pch_repro] __irq_set_trigger+0x69/0x140 irq_set_irq_type+0x78/0xd0 Switching the mirrored lock to raw_spinlock_t made both splats go away. Convert the register lock to raw_spinlock_t. The same lock also serializes the GPIO direction/value callbacks and the suspend/resume register save/restore, but all of those critical sections only perform MMIO register accesses (ioread32()/iowrite32()) and irq_set_handler_locked(); none of them contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. This is the same class of issue and fix as recently addressed for other GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use raw_spinlock_t in the irq startup path"). Fixes: 38eb18a6f92d ("gpio-pch: Support interrupt function") Cc: stable@vger.kernel.org Signed-off-by: Junjie Cao Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260723014129.1129730-1-junjie.cao@intel.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 9925687e05fb4..1a7e422675295 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -96,7 +96,7 @@ struct pch_gpio { struct pch_gpio_reg_data pch_gpio_reg; int irq_base; enum pch_type_t ioh; - spinlock_t spinlock; + raw_spinlock_t spinlock; }; static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) @@ -105,7 +105,7 @@ static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) struct pch_gpio *chip = gpiochip_get_data(gpio); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); reg_val = ioread32(&chip->reg->po); if (val) reg_val |= BIT(nr); @@ -113,7 +113,7 @@ static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->po); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -133,7 +133,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, u32 reg_val; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); reg_val = ioread32(&chip->reg->po); if (val) @@ -147,7 +147,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, pm |= BIT(nr); iowrite32(pm, &chip->reg->pm); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -158,12 +158,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) u32 pm; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->pm); pm &= BIT(gpio_pins[chip->ioh]) - 1; pm &= ~BIT(nr); iowrite32(pm, &chip->reg->pm); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -265,7 +265,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) return 0; } - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); /* Set interrupt mode */ im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); @@ -277,7 +277,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) else if (type & IRQ_TYPE_EDGE_BOTH) irq_set_handler_locked(d, handle_edge_irq); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -374,7 +374,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, chip->ioh = id->driver_data; chip->reg = chip->base; pci_set_drvdata(pdev, chip); - spin_lock_init(&chip->spinlock); + raw_spin_lock_init(&chip->spinlock); pch_gpio_setup(chip); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); @@ -407,9 +407,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pch_gpio_save_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -419,11 +419,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->reset); iowrite32(0x00, &chip->reg->reset); pch_gpio_restore_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } From efbcecdecefc26fa062c0e8210533ccad7d6bd4a Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Sat, 25 Jul 2026 21:04:44 +0000 Subject: [PATCH 3165/3297] cifs: add fscache_resize_cookie() to cifs_setsize() commit fa724e235cfdb0fb0bb427d0f9dfe864ae27403e upstream. Several code paths update the VFS inode size by calling netfs_resize_file() and cifs_setsize(), but omit the corresponding fscache_resize_cookie() call, leaving the fscache cookie out of sync with the actual file size: - cifs_file_set_size() in inode.c: server-side truncation via setattr - cifs_do_truncate() in file.c: truncates to zero on O_TRUNC open - smb2_duplicate_extents() in smb2ops.c: file clone extending EOF - smb3_simple_falloc() in smb2ops.c: two branches that extend EOF via write-range and SMB2_set_eof respectively Since every caller of cifs_setsize() must resize the fscache cookie, add the call to cifs_setsize() itself, consistent with how truncate_pagecache() is already consolidated there. Fixes: 70431bfd825d ("cifs: Support fscache indexing rewrite") Fixes: 93a43155127f ("cifs: Fix missing set of remote_i_size") Fixes: 110fee6b9bb5 ("smb: client: fix missing timestamp updates with O_TRUNC") Fixes: 7a06d3b816d7 ("smb/client: emulate small EOF-extending mode 0 fallocate ranges") Cc: stable@vger.kernel.org Cc: David Howells Cc: Paulo Alcantara Cc: Huiwen He Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 594798a6b0fb4..c9e955d0d799e 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3039,6 +3039,7 @@ void cifs_setsize(struct inode *inode, loff_t offset) inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); truncate_pagecache(inode, offset); netfs_wait_for_outstanding_io(inode); + fscache_resize_cookie(cifs_inode_cookie(inode), offset); } int cifs_file_set_size(const unsigned int xid, struct dentry *dentry, From 437b38a08c0a83f09de3ce2f074bd31db6262de8 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 27 Jul 2026 15:05:51 +0530 Subject: [PATCH 3166/3297] cpufreq: powernow-k8: Fix possible memory leak in powernowk8_cpu_init() commit d5f8e5f6040d052d44fcbf4f31dd35145c0c8d7d upstream. The memory allocated for data->powernow_table inside powernow_k8_cpu_init_acpi() or find_psb_table() is not freed in one of the error paths in powernowk8_cpu_init(). Fix that by adding a kfree(). Fixes: 1ff6e97f1d99 ("[CPUFREQ] cpumask: avoid playing with cpus_allowed in powernow-k8.c") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Acked-by: Viresh Kumar Reviewed-by: Zhongqiu Han Link: https://patch.msgid.link/20260727093553.98246-1-nihaal@cse.iitm.ac.in Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/powernow-k8.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index f7512b4e923e7..0012c3e3a9151 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -1083,6 +1083,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol) err_out_exit_acpi: powernow_k8_cpu_exit_acpi(data); + kfree(data->powernow_table); err_out: kfree(data); From 63d6c855b27df5add128f8112f23cdb2aca98889 Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Thu, 16 Jul 2026 19:51:58 +0800 Subject: [PATCH 3167/3297] cpufreq: schedutil: Publish util hooks only after all sg_cpu are initialized commit f0a3f042293a8c5a2152346b3637ea60866c503a upstream. Commit 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") merged the per-CPU initialization and the utilization-hook registration into a single loop in sugov_start(). For a shared cpufreq policy this re-introduces the race originally fixed by commit ab2f7cf141aa ("cpufreq: schedutil: Fix sugov_start() versus sugov_update_shared() race"). The scheduler's util path reaches the hook under RCU-sched and never takes policy->rwsem, so the rwsem held across sugov_start() cannot serialize the two. Once the first CPU's hook is published, sugov_update_shared() may run and, via sugov_next_freq_shared(), read/write each sibling sugov_cpu (iowait_boost, util, bw_min, ...) concurrently with the memset() still initializing them, with no lock common to both sides: the update side holds sg_policy->update_lock while the init side holds only policy->rwsem, which the scheduler's util path never takes. The walk only accesses scalar members, never a pointer like ->sg_policy, so it does not crash today; it merely uses stale (or zero on first start) values that skew the frequency selection and tracepoints. It is still a genuine data race, and a latent crash once any pointer member is dereferenced there. Restore the two-phase approach: initialize all per-CPU structures first, and only then publish the per-CPU utilization update hooks. Fixes: 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") Cc: stable@vger.kernel.org Signed-off-by: Zhongqiu Han Reviewed-by: Christian Loehle Link: https://patch.msgid.link/20260716115159.848403-1-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- kernel/sched/cpufreq_schedutil.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index e82bb019b9202..5d8fc382c4a17 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -870,8 +870,19 @@ static int sugov_start(struct cpufreq_policy *policy) memset(sg_cpu, 0, sizeof(*sg_cpu)); sg_cpu->cpu = cpu; sg_cpu->sg_policy = sg_policy; + } + + /* + * Publish the hooks only after all per-CPU data is initialized, so a + * shared policy's sugov_update_shared() never reads an uninitialized + * sibling sugov_cpu. + */ + for_each_cpu(cpu, policy->cpus) { + struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu); + cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu); } + return 0; } From 6d89f33a646751632ab47b9f77ff1fb58c8cb054 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 23 Jul 2026 14:54:44 +0800 Subject: [PATCH 3168/3297] power: supply: bq25890: fix the -10 C NTC lookup entry commit 160a783aa65b74782bc17cb874af1a6d3f5fba3c upstream. The TSPCT lookup table is monotonically decreasing except for ADC code 121, where the sequence reads -9.0 C, -1.0 C, -12.0 C. This makes the reported battery temperature jump upward by eight degrees for one code and then downward by eleven degrees for the next code. The entry is a missing zero: use -10.0 C so the sequence remains monotonic between -9.0 C and -12.0 C. Fixes: 9652c02428f3 ("power: bq25890: add POWER_SUPPLY_PROP_TEMP") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/0619C8BF15F43B7C+20260723065444.1796002-1-raoxu@uniontech.com Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 868e86e1749b7..f246ec871d5c8 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -320,7 +320,7 @@ static const u32 bq25890_tspct_tbl[] = { 145, 140, 130, 120, 115, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0, -10, -20, -30, -40, -60, -70, -80, - -90, -10, -120, -140, -150, -170, -190, -210, + -90, -100, -120, -140, -150, -170, -190, -210, }; #define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl) From b039f13e095d28a64ca6b21d0ee5440d8b048f37 Mon Sep 17 00:00:00 2001 From: Jianing Li Date: Wed, 1 Jul 2026 14:10:42 +0800 Subject: [PATCH 3169/3297] power: supply: max17040: handle missing status supplier commit 725668c6b6aa3971fe850659102c250d0d676e18 upstream. MAX17040 does not report charger state itself, so the driver forwards POWER_SUPPLY_PROP_STATUS to a supplier power supply. If no supplier is registered, power_supply_get_property_from_supplier() returns -ENODEV and leaves the output value untouched. max17040_get_property() currently ignores that error and returns success, so userspace can read an uninitialized status value from the battery power supply. This happens on systems that use the fuel gauge without a charger supplier relationship in firmware. Return POWER_SUPPLY_STATUS_UNKNOWN when no supplier provides STATUS, and propagate other supplier lookup errors. Fixes: f4b782af61ae ("power: max17040: pass status property from supplier") Cc: stable@vger.kernel.org # 6.7+ Signed-off-by: Jianing Li Link: https://patch.msgid.link/20260701061042.1008-1-m13940358460@163.com Signed-off-by: Sebastian Reichel Signed-off-by: Greg Kroah-Hartman --- drivers/power/supply/max17040_battery.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 48453508688a4..f4748adec0924 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -405,7 +405,11 @@ static int max17040_get_property(struct power_supply *psy, val->intval = chip->low_soc_alert; break; case POWER_SUPPLY_PROP_STATUS: - power_supply_get_property_from_supplier(psy, psp, val); + ret = power_supply_get_property_from_supplier(psy, psp, val); + if (ret == -ENODEV) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else if (ret) + return ret; break; case POWER_SUPPLY_PROP_TEMP: if (!chip->channel_temp) From ef1aa7cfb8c630d6bf2784d9a2541160ee0d413c Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 22 Jul 2026 15:08:34 +0200 Subject: [PATCH 3170/3297] s390/pci: Fix s390_pci_mmio_write syscall error return without MIO commit b7ab86bdc65eadcfc43a0e3faf682a3f750cfb96 upstream. On a machine without PCI memory-I/O (MIO) support or when running with pci=nomio the s390 specific PCI MMIO write syscall checks if the MMIO cookie is above ZPCI_IOMAP_ADDR_BASE as a sanity check before even trying to perform the MMIO. If this check fails the return value was left unchanged and thus 0 from prior operations falsely indicating success. This could potentially confuse user-space into falsely believing the MMIO, on a mapping not valid for MMIO was successful. Fix this by setting the return value to -EFAULT prior to the check following the same pattern as elsewhere in the same function. Cc: stable@vger.kernel.org Reviewed-by: Julian Ruess Reviewed-by: Farhan Ali Fixes: a67a88b0b8de ("s390/pci: remove races against pte updates") Signed-off-by: Niklas Schnelle Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- arch/s390/pci/pci_mmio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/pci/pci_mmio.c b/arch/s390/pci/pci_mmio.c index 51e7a28af8994..f3f79ba78410a 100644 --- a/arch/s390/pci/pci_mmio.c +++ b/arch/s390/pci/pci_mmio.c @@ -188,6 +188,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr, goto out_unlock_mmap; } + ret = -EFAULT; io_addr = (void __iomem *)((args.pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); From bd63c7879eaa87f1958f7ee027813356fcd9ff11 Mon Sep 17 00:00:00 2001 From: Aswin Karuvally Date: Thu, 23 Jul 2026 16:00:50 +0200 Subject: [PATCH 3171/3297] s390/qeth: Check CAP_NET_ADMIN for private ioctls commit d211028bac1bd0fff0026bfa2a8328e5b78cd0e6 upstream. Gate the SIOCDEVPRIVATE ioctl commands SIOC_QETH_ADP_SET_SNMP_CONTROL, SIOC_QETH_GET_CARD_TYPE and SIOC_QETH_QUERY_OAT with CAP_NET_ADMIN capable check to ensure unprivileged users cannot invoke them. Fixes: 18787eeebd71 ("qeth: use ndo_siocdevprivate") Cc: stable@vger.kernel.org Suggested-by: Christian Borntraeger Reviewed-by: Christian Borntraeger Reviewed-by: Alexandra Winter Signed-off-by: Aswin Karuvally Link: https://patch.msgid.link/20260723140050.762991-1-aswin@linux.ibm.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/s390/net/qeth_core_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index edc0bcd46923c..64ca42ca78c4a 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6528,6 +6528,9 @@ int qeth_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *d struct qeth_card *card = dev->ml_priv; int rc = 0; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + switch (cmd) { case SIOC_QETH_ADP_SET_SNMP_CONTROL: rc = qeth_snmp_command(card, data); From 86cdfd061509bcde84f3145f7221ba64e3e53b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 27 Jul 2026 16:28:39 +0200 Subject: [PATCH 3172/3297] s390/dasd: Fix potential NULL pointer dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9973026f572db6b67570cadc30942f3014e41079 upstream. dasd_release_space() checks the implementation of the is_ese() discipline function before calling it to determine if a given device is an ESE DASD. The current usage of the logical AND operator will lead to a NULL pointer dereference as the function is called even if the function pointer is NULL. Fix this by using the logical OR operator. Fixes: 91dc4a197569 ("s390/dasd: Add new ioctl to release space") Cc: stable@vger.kernel.org # v5.3+ Reported-by: Vasily Gorbik Acked-by: Eduard Shishkin Reviewed-by: Stefan Haberland Signed-off-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-3-sth@linux.ibm.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/s390/block/dasd_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 8308046a9f8fb..a9edc0468a314 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -325,7 +325,7 @@ static int dasd_ioctl_check_format(struct block_device *bdev, void __user *argp) static int dasd_release_space(struct dasd_device *device, struct format_data_t *rdata) { - if (!device->discipline->is_ese && !device->discipline->is_ese(device)) + if (!device->discipline->is_ese || !device->discipline->is_ese(device)) return -ENOTSUPP; if (!device->discipline->release_space) return -ENOTSUPP; From e16e0fc54120cee3c6f0362de95aab6792865857 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 27 Jul 2026 16:28:40 +0200 Subject: [PATCH 3173/3297] s390/dasd: Fix undersized format-check buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7f40b346462f563a0d6e841a77b5163d2a882a04 upstream. fmt_buffer_size in dasd_eckd_check_device_format() is declared as int, even though one of the multiplicands, sizeof(struct eckd_count), is a size_t. The expression trkcount * rpt_max * sizeof(struct eckd_count) is therefore correctly evaluated at 64-bit width, but the result is silently truncated when it is stored back into the 32-bit fmt_buffer_size variable. For a sufficiently large track range (start_unit/stop_unit are caller-controlled) this truncation yields a buffer size far smaller than the number of tracks actually requested. kzalloc() then succeeds with an undersized allocation, while the subsequent channel program build still operates on the untruncated track count and writes past the end of that buffer. Compute the buffer size with check_mul_overflow() and keep it in a size_t, so that a value that no longer fits results in -EINVAL instead of a silently truncated allocation size. Fixes: 8fd575200db5 ("s390/dasd: Add new ioctl BIODASDCHECKFMT") Cc: stable@vger.kernel.org #4.7 Reviewed-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-4-sth@linux.ibm.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/s390/block/dasd_eckd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 40bbbada67f96..9dfa14eb09d28 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -3476,11 +3477,11 @@ static int dasd_eckd_check_device_format(struct dasd_device *base, { struct dasd_eckd_private *private = base->private; struct eckd_count *fmt_buffer; - struct irb irb; + size_t fmt_buffer_size; + unsigned int trkcount; int rpt_max, rpt_exp; - int fmt_buffer_size; + struct irb irb; int trk_per_cyl; - int trkcount; int tpm = 0; int rc; @@ -3491,7 +3492,9 @@ static int dasd_eckd_check_device_format(struct dasd_device *base, rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize); trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; - fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count); + if (check_mul_overflow(trkcount, rpt_max, &fmt_buffer_size) || + check_mul_overflow(fmt_buffer_size, sizeof(struct eckd_count), &fmt_buffer_size)) + return -EINVAL; fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA); if (!fmt_buffer) From 672b12940e3f1336dfed5287412a71500adf2a76 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 23 Jul 2026 11:54:52 +0200 Subject: [PATCH 3174/3297] s390/zcrypt: Fix wrong domain value verification with EP11 CPRBs commit 983279d7f86ade73db86f886e09172dd567031b5 upstream. There is a wrong upper limit check for the domain value when an EP11 CPRB is processed for sending to a crypto card. This check is only active on custom device nodes but may lead to access heap memory behind perms->adm when an administrative CPRB is sent. Add correct limit (AP_DOMAINS = 256) checking to fix this. Fixes: cfd68b33094e ("s390/zcrypt: Filter admin CPRBs on custom devices") Cc: stable@vger.kernel.org Reviewed-by: Finn Callies Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/zcrypt_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 4e6bf1cb34750..a25b58e06f923 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1083,7 +1083,7 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms, print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1, ap_msg.msg, ap_msg.len, false); - if (perms != &ap_perms && domain < AUTOSEL_DOM) { + if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; From a57fd7fcdb63e2d5ceac78bbe825ec986062a9da Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 29 Jul 2026 13:40:09 +0200 Subject: [PATCH 3175/3297] s390/zcrypt: Fix buffer over-read in cca_cipher2protkey commit 36b230835b8a008266aad22168ca52afacc8a58d upstream. Add validation of both the actual key buffer size and token length fields in all the cca_check_sec*token() functions. Additionally check in cca_gencipherkey() for possible underflow with returned key size. The CCA token structures contain user-controlled len fields that were used in operations without proper validation against both the actual buffer size and minimum token structure size. An attacker could set this field larger than the actual buffer size, leading to reading beyond buffer boundaries. This may result in a kernel crash or exposure of memory via sending this as part of a request down to the crypto card. Also an attacker could have used a very small len value and thus enforce a buffer under-run which may produce similar effects as a over-read. So now a key must - key buf length must be at least sizeof the token struct - the key len field inside the token must fit into the range of sizeof key token struct ... key buf length Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Cc: stable@vger.kernel.org Reviewed-by: Ingo Franzki Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/pkey_cca.c | 15 +++---- drivers/s390/crypto/zcrypt_ccamisc.c | 64 +++++++++++++++++++++++----- drivers/s390/crypto/zcrypt_ccamisc.h | 6 +-- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/drivers/s390/crypto/pkey_cca.c b/drivers/s390/crypto/pkey_cca.c index 6c7897a93f273..d780ea60c9e1a 100644 --- a/drivers/s390/crypto/pkey_cca.c +++ b/drivers/s390/crypto/pkey_cca.c @@ -233,22 +233,16 @@ static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns, if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_AES) { /* CCA AES data key */ - if (keylen < sizeof(struct secaeskeytoken)) - return -EINVAL; - if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0)) + if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_VLSC) { /* CCA AES cipher key */ - if (keylen < hdr->len) - return -EINVAL; if (cca_check_secaescipherkey(pkey_dbf_info, - 3, key, 0, 1)) + 3, key, keylen, 0, 1)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { /* CCA ECC (private) key */ - if (keylen < sizeof(struct eccprivkeytoken)) - return -EINVAL; if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1)) return -EINVAL; } else { @@ -476,7 +470,7 @@ static int cca_verifykey(const u8 *key, u32 keylen, hdr->version == TOKVER_CCA_AES) { struct secaeskeytoken *t = (struct secaeskeytoken *)key; - rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0); + rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0); if (rc) goto out; *keytype = PKEY_TYPE_CCA_DATA; @@ -504,7 +498,8 @@ static int cca_verifykey(const u8 *key, u32 keylen, hdr->version == TOKVER_CCA_VLSC) { struct cipherkeytoken *t = (struct cipherkeytoken *)key; - rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1); + rc = cca_check_secaescipherkey(pkey_dbf_info, 3, + key, keylen, 0, 1); if (rc) goto out; *keytype = PKEY_TYPE_CCA_CIPHER; diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index fe61df34f6146..add0a9fb42fec 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -63,12 +63,18 @@ static DEFINE_MUTEX(dev_status_mem_mutex); * also checked. Returns 0 on success or errno value on failure. */ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize) + const u8 *token, u32 keysize, int keybitsize) { struct secaeskeytoken *t = (struct secaeskeytoken *)token; #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -102,14 +108,20 @@ EXPORT_SYMBOL(cca_check_secaeskeytoken); * Returns 0 on success or errno value on failure. */ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize, - int checkcpacfexport) + const u8 *token, u32 keysize, + int keybitsize, int checkcpacfexport) { struct cipherkeytoken *t = (struct cipherkeytoken *)token; bool keybitsizeok = true; #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -122,6 +134,18 @@ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, __func__, (int)t->version, TOKVER_CCA_VLSC); return -EINVAL; } + if (t->len > keysize) { + if (dbg) + DBF("%s token check failed, len %d > keysize %u\n", + __func__, (int)t->len, keysize); + return -EINVAL; + } + if (t->len < sizeof(*t)) { + if (dbg) + DBF("%s token check failed, len %d < min token size %zu\n", + __func__, (int)t->len, sizeof(*t)); + return -EINVAL; + } if (t->algtype != 0x02) { if (dbg) DBF("%s token check failed, algtype 0x%02x != 0x02\n", @@ -196,6 +220,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl, #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL_PKA) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -208,6 +238,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl, __func__, (int)t->len, keysize); return -EINVAL; } + if (t->len < sizeof(*t)) { + if (dbg) + DBF("%s token check failed, len %d < min token size %zu\n", + __func__, (int)t->len, sizeof(*t)); + return -EINVAL; + } if (t->secid != 0x20) { if (dbg) DBF("%s token check failed, secid 0x%02x != 0x20\n", @@ -444,7 +480,8 @@ int cca_genseckey(u16 cardnr, u16 domain, /* check secure key token */ rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR, - prepparm->lv3.keyblock.tok, 8 * keysize); + prepparm->lv3.keyblock.tok, + seckeysize, 8 * keysize); if (rc) { rc = -EIO; goto out; @@ -583,7 +620,8 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize, /* check secure key token */ rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR, - prepparm->lv3.keyblock.tok, 8 * keysize); + prepparm->lv3.keyblock.tok, + seckeysize, 8 * keysize); if (rc) { rc = -EIO; goto out; @@ -843,6 +881,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags, } kb; } __packed * prepparm; struct cipherkeytoken *t; + u32 keybuflen; /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, @@ -937,23 +976,28 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags, } /* and some checks on the generated key */ + t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key; + if (prepparm->kb.tlv1.len < 2 * sizeof(uint16_t) + sizeof(*t)) { + rc = -EIO; + goto out; + } + keybuflen = prepparm->kb.tlv1.len - 2 * sizeof(uint16_t); rc = cca_check_secaescipherkey(zcrypt_dbf_info, DBF_ERR, prepparm->kb.tlv1.gen_key, - keybitsize, 1); + keybuflen, keybitsize, 1); if (rc) { rc = -EIO; goto out; } /* copy the generated vlsc key token */ - t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key; if (keybuf) { - if (*keybufsize >= t->len) - memcpy(keybuf, t, t->len); + if (*keybufsize >= keybuflen) + memcpy(keybuf, t, keybuflen); else rc = -EINVAL; } - *keybufsize = t->len; + *keybufsize = keybuflen; out: free_cprbmem(mem, PARMBSIZE, false, xflags); diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h index 1ecc4e37e9ad3..5f5bd93e09318 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.h +++ b/drivers/s390/crypto/zcrypt_ccamisc.h @@ -136,7 +136,7 @@ struct eccprivkeytoken { * also checked. Returns 0 on success or errno value on failure. */ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize); + const u8 *token, u32 keysize, int keybitsize); /* * Simple check if the token is a valid CCA secure AES cipher key @@ -146,8 +146,8 @@ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, * Returns 0 on success or errno value on failure. */ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize, - int checkcpacfexport); + const u8 *token, u32 keysize, + int keybitsize, int checkcpacfexport); /* * Simple check if the token is a valid CCA secure ECC private From fbb0410986e8ad214121e51a4a28c3d0a10b7644 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 29 Jul 2026 16:01:34 +0200 Subject: [PATCH 3176/3297] s390/zcrypt: Fix missing mem scrub at clear key import in cca_clr2cipherkey() commit 01476391aecef36a3b789ee844357b22fbc90665 upstream. The helper function _ip_cprb_helper() uses internal buffer memory for building and processing CPRBs. After use this buffer was never scrubbed which could lead to leaving for example clear key material in memory which could be exposed via tricky reuse of this same memory. Extend the _ip_cprb_helper() function with another parameter 'scrub' used to steer scrubbing of this buffer. So now the caller has the opportunity to decide if scrubbing is needed or not. Extend the clear key to secure key token import process in function cca_clr2cipherkey() to tell the helper function from above to scrub the cprb buffer when the clear key value is part of the request data. Add explicit scrubbing on return from function cca_clr2cipherkey() for the random EXOR buffer and the cprb buffer. Overall this cleans the internal used buffer in case of clear key import to prevent sensitive data to get exposed. Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Cc: stable@vger.kernel.org Reviewed-by: Holger Dengler Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/zcrypt_ccamisc.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index add0a9fb42fec..59805c4eed9a5 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1016,7 +1016,8 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain, int clr_key_bit_size, u8 *key_token, int *key_token_size, - u32 xflags) + u32 xflags, + bool scrub) { int rc, n; u8 *mem, *ptr; @@ -1156,7 +1157,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain, *key_token_size = t->len; out: - free_cprbmem(mem, PARMBSIZE, false, xflags); + free_cprbmem(mem, PARMBSIZE, scrub, xflags); return rc; } @@ -1207,28 +1208,32 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags, * 4/4 COMPLETE the secure cipher key import */ rc = _ip_cprb_helper(card, dom, "AES ", "FIRST ", "MIN3PART", - exorbuf, keybitsize, token, &tokensize, xflags); + exorbuf, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL, - clrkey, keybitsize, token, &tokensize, xflags); + clrkey, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL, - exorbuf, keybitsize, token, &tokensize, xflags); + exorbuf, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "COMPLETE", NULL, - NULL, keybitsize, token, &tokensize, xflags); + NULL, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); @@ -1245,6 +1250,8 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags, *keybufsize = tokensize; out: + memzero_explicit(exorbuf, sizeof(exorbuf)); + memzero_explicit(mem, CPRB_MEMPOOL_ITEM_SIZE); mempool_free(mem, cprb_mempool); return rc; } From ad93a1f1a45652478c0cf4eb029114e03af57f3b Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 29 Jul 2026 11:36:15 +0200 Subject: [PATCH 3177/3297] s390/zcrypt: Validate length for CCA AES cipher key requests commit 06afe425d5283b9764303de47f554da5a808ce8a upstream. cca_cipher2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block. Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Signed-off-by: Holger Dengler Cc: stable@vger.kernel.org # 5.4+ Reviewed-by: Harald Freudenberger Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 59805c4eed9a5..dc52e0b4a15dd 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1313,6 +1313,9 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey, } __packed * prepparm; int keytoklen = ((struct cipherkeytoken *)ckey)->len; + if (keytoklen > PARMBSIZE - sizeof(struct aureqparm)) + return -EINVAL; + /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk, xflags); From 013a4484f061a2b41f052e25c0015203287e63f1 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 29 Jul 2026 11:36:16 +0200 Subject: [PATCH 3178/3297] s390/zcrypt: Validate length for CCA ECC private key requests commit a9ae0f6dd45c3ccc1d69363f7aea8af179122730 upstream. cca_ecc2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block. Fixes: fa6999e326fe ("s390/pkey: support CCA and EP11 secure ECC private keys") Signed-off-by: Holger Dengler Cc: stable@vger.kernel.org # 5.10+ Reviewed-by: Harald Freudenberger Signed-off-by: Vasily Gorbik Signed-off-by: Greg Kroah-Hartman --- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index dc52e0b4a15dd..1de67038d1640 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1480,6 +1480,9 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key, } __packed * prepparm; int keylen = ((struct eccprivkeytoken *)key)->len; + if (keylen > PARMBSIZE - sizeof(struct aureqparm)) + return -EINVAL; + /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk, xflags); From 4211450f0fecb36121ff1a35c752a9947db8c7d5 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:27 +0530 Subject: [PATCH 3179/3297] phy: zynqmp: fix L0_TM_DISABLE_SCRAMBLE_ENCODER mask commit 6cb22477929489a412df8d153e550e77a012e701 upstream. The L0_TX_DIG_61 register bit 2 is a reserved read-only field. The previous mask value 0x0f incorrectly included bit 2, causing unintended writes to a reserved bit on every scrambler bypass operation. Correct the mask to (BIT(3) | GENMASK(1, 0)) to cover only the valid scramble bypass control bits. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-2-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/xilinx/phy-zynqmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 2138f5399821a..58fa2f8376893 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -53,7 +53,7 @@ #define L0_TM_DIG_6 0x106c #define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f #define L0_TX_DIG_61 0x00f4 -#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f +#define L0_TM_DISABLE_SCRAMBLE_ENCODER (BIT(3) | GENMASK(1, 0)) /* PLL Test Mode register parameters */ #define L0_TM_PLL_DIG_37 0x2094 From 79a312664118f092b95ea07a2499115121da5dbd Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:28 +0530 Subject: [PATCH 3180/3297] phy: zynqmp: use read-modify-write for SERDES scrambler bypass commit 21e0749f931702765b9d52d05740092bc87fcd8d upstream. xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which performs a full register write, silently clearing any bits beyond the intended bypass control fields. Switch to xpsgtr_clr_set_phy() with clr=mask, set=mask to set only the bypass bits while preserving the remaining bits in each register. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-3-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/xilinx/phy-zynqmp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 58fa2f8376893..0e99b2cc2a2db 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -505,8 +505,12 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) /* Bypass (de)scrambler and 8b/10b decoder and encoder. */ static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) { - xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER); - xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, + L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, + L0_TM_DISABLE_SCRAMBLE_ENCODER); } /* DP-specific initialization. */ From 74b30e7ef46188c5f3bf1d9b4f527a2a8f0b1858 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:29 +0530 Subject: [PATCH 3181/3297] phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB commit 7eb61caf45607e1e1270f51f8f93f0ded53146da upstream. USB Gen1 requires scrambling and 8b/10b encoding to be performed in the physical layer. Do not bypass PHY-side scrambler or encoder/decoder for USB operation, as mandated by the USB 3.x specification. Scrambler and 8b/10b bypass remain restricted to SATA and SGMII modes, where encoding is handled in the controller. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-4-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/xilinx/phy-zynqmp.c | 39 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 0e99b2cc2a2db..240626b554750 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -502,15 +502,30 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) } } -/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ -static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) +/** + * xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior + * @gtr_phy: pointer to lane context + * @bypass: true to enable scrambler/encoder bypass (SATA/SGMII), + * false to disable scrambler/encoder bypass (USB3) + * + * Uses RMW to preserve reserved and unrelated register fields. + */ +static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy, + bool bypass) { - xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, - L0_TM_DIS_DESCRAMBLE_DECODER, - L0_TM_DIS_DESCRAMBLE_DECODER); - xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, - L0_TM_DISABLE_SCRAMBLE_ENCODER, - L0_TM_DISABLE_SCRAMBLE_ENCODER); + if (bypass) { + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, + L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, + L0_TM_DISABLE_SCRAMBLE_ENCODER); + } else { + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, 0); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, 0); + } } /* DP-specific initialization. */ @@ -531,7 +546,7 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy) { struct xpsgtr_dev *gtr_dev = gtr_phy->dev; - xpsgtr_bypass_scrambler_8b10b(gtr_phy); + xpsgtr_bypass_scrambler_8b10b(gtr_phy, true); writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); } @@ -547,7 +562,7 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy) xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val); xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val); - xpsgtr_bypass_scrambler_8b10b(gtr_phy); + xpsgtr_bypass_scrambler_8b10b(gtr_phy, true); } /* Configure TX de-emphasis and margining for DP. */ @@ -708,6 +723,10 @@ static int xpsgtr_phy_init(struct phy *phy) case ICM_PROTOCOL_SGMII: xpsgtr_phy_init_sgmii(gtr_phy); break; + + case ICM_PROTOCOL_USB: + xpsgtr_bypass_scrambler_8b10b(gtr_phy, false); + break; } goto out; From 90623c9499627803ef3f04fa25a3199402d4fb95 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 27 Jul 2026 14:10:21 +0200 Subject: [PATCH 3182/3297] net: openvswitch: fix potential UAF on meter attach failure commit a58a2b0ce354df531ebc71fc870058c2feb59f6b upstream. While attaching a newly created meter attach_meter() function makes the new meter visible to other CPUs but can still fail afterwards. On failure, it detaches the meter back and returns an error. However, this is an unexpected behavior for the ovs_meter_cmd_set() that uses a plain kfree(meter) on attach failure without waiting for RCU readers to stop using it, assuming it was never visible. This is never a problem for ovs-vswitchd as it always creates meters before creating any flows that use them. But the UAF can be triggered with a custom application using uAPI: BUG: KASAN: slab-use-after-free in ovs_meter_execute (net/openvswitch/meter.c:653) Read of size 8 at addr ffff88810d152650 by task meter/2508 Call Trace: ovs_meter_execute (net/openvswitch/meter.c:653) do_execute_actions (net/openvswitch/actions.c:1407) ovs_execute_actions (net/openvswitch/actions.c:1584) ovs_packet_cmd_execute (net/openvswitch/datapath.c:703) ... netlink_sendmsg (af_netlink.c:1900) Allocated by task 2519: __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415) ovs_meter_cmd_set (net/openvswitch/meter.c:422) ... netlink_sendmsg (af_netlink.c:1900) Freed by task 2519: kfree (mm/slub.c:2705 mm/slub.c:6405 mm/slub.c:6720) ovs_meter_cmd_set (net/openvswitch/meter.c:479) ... netlink_sendmsg (af_netlink.c:1900) Fix that by making sure attach_meter() doesn't make the meter visible until all the checks are done and the function can't fail anymore. This also makes sure the "hash" value is calculated after the potential re-sizing of the table. Reported by Trend Micro's Zero Day Initiative as ZDI-CAN-31642. Fixes: c7c4c44c9a95 ("net: openvswitch: expand the meters supported number") Cc: stable@vger.kernel.org Signed-off-by: Ilya Maximets Reviewed-by: Eelco Chaudron Link: https://patch.msgid.link/20260727121022.198461-1-i.maximets@ovn.org Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/openvswitch/meter.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/net/openvswitch/meter.c b/net/openvswitch/meter.c index cc08e0403909a..671984edfe1eb 100644 --- a/net/openvswitch/meter.c +++ b/net/openvswitch/meter.c @@ -133,18 +133,10 @@ static void dp_meter_instance_remove(struct dp_meter_instance *ti, static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) { - struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti); - u32 hash = meter_hash(ti, meter->id); + struct dp_meter_instance *ti; + u32 hash; int err; - /* In generally, slots selected should be empty, because - * OvS uses id-pool to fetch a available id. - */ - if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash]))) - return -EBUSY; - - dp_meter_instance_insert(ti, meter); - /* That function is thread-safe. */ tbl->count++; if (tbl->count >= tbl->max_meters_allowed) { @@ -152,16 +144,29 @@ static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) goto attach_err; } - if (tbl->count >= ti->n_meters && - dp_meter_instance_realloc(tbl, ti->n_meters * 2)) { - err = -ENOMEM; + ti = rcu_dereference_ovsl(tbl->ti); + if (tbl->count >= ti->n_meters) { + err = dp_meter_instance_realloc(tbl, ti->n_meters * 2); + if (err) + goto attach_err; + + ti = rcu_dereference_ovsl(tbl->ti); + } + + hash = meter_hash(ti, meter->id); + + /* In general, selected slots should be empty, because + * OvS uses id-pool to fetch available ids. + */ + if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash]))) { + err = -EBUSY; goto attach_err; } + dp_meter_instance_insert(ti, meter); return 0; attach_err: - dp_meter_instance_remove(ti, meter); tbl->count--; return err; } From 9c7246cc509fdb5877decbf2d44856577207f67c Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 27 Jul 2026 20:18:30 +0200 Subject: [PATCH 3183/3297] net: openvswitch: fix skb leak on flow key update failure during recirculation commit e1cf066244dad576221b7123a0e5005967f25a20 upstream. do_execute_actions() returns right away when execute_recirc() fails on the last action as it assumes this function always takes ownership of the skb when 'last' is true. But when the flow key update fails, the function doesn't free the skb and it ends up leaked. This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless. Reported by Sashiko. Fixes: 971427f353f3 ("openvswitch: Add recirc and hash action.") Cc: stable@vger.kernel.org Signed-off-by: Ilya Maximets Reviewed-by: Aaron Conole Link: https://patch.msgid.link/20260727181851.306076-2-i.maximets@ovn.org Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/openvswitch/actions.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 56ea40d23e280..e0661f6a596fa 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -1107,6 +1107,10 @@ static int execute_masked_set_action(struct sk_buff *skb, return err; } +/* When 'last' is true, recirc() should always consume the 'skb'. + * Otherwise, recirc() should keep 'skb' intact regardless what + * actions are executed on recirculation. + */ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *a, bool last) @@ -1117,8 +1121,12 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, int err; err = ovs_flow_key_update(skb, key); - if (err) + if (err) { + if (last) + ovs_kfree_skb_reason(skb, + OVS_DROP_ACTION_ERROR); return err; + } } BUG_ON(!is_flow_key_valid(key)); From e0ba8eaef2a0d02a7a485e6a7157e47272b65cea Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 27 Jul 2026 20:18:31 +0200 Subject: [PATCH 3184/3297] net: openvswitch: fix skb leak on flow key update failure during ct commit bc62e843bc48f933da765ce47079fd992e535794 upstream. ovs_ct_execute() always steals or frees the skb on failure while ovs_flow_key_update() does not. So, if it fails and we return right away, the skb ends up leaked. Fix that by breaking instead and letting the common error handling code at the bottom of the loop to free the skb properly. This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless. Reported by Sashiko. Fixes: ec0d043d05e6 ("openvswitch: Ensure flow is valid before executing ct") Cc: stable@vger.kernel.org Signed-off-by: Ilya Maximets Reviewed-by: Aaron Conole Link: https://patch.msgid.link/20260727181851.306076-3-i.maximets@ovn.org Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/openvswitch/actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index e0661f6a596fa..5d6a4c6b7ea6f 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -1379,7 +1379,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, if (!is_flow_key_valid(key)) { err = ovs_flow_key_update(skb, key); if (err) - return err; + break; } err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key, From 7e8789f5b5d8ac16b0dfd38e50ff47bf14e84da8 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Wed, 29 Apr 2026 11:48:49 +0800 Subject: [PATCH 3185/3297] ice: wait for reset completion in ice_resume() commit c2816d613f388814d27bc9fd6dbd931a88056e19 upstream. ice_resume() schedules an asynchronous PF reset and returns immediately. The reset runs later in ice_service_task(). If userspace tries to bring up the net device before the reset finishes, ice_open() fails with -EBUSY: ice_resume() ice_schedule_reset() # sets ICE_PFR_REQ, returns ... ice_open() ice_is_reset_in_progress() # ICE_PFR_REQ still set, -EBUSY ... ice_service_task() ice_do_reset() ice_rebuild() # clears ICE_PFR_REQ, too late Reproduced on E800 series NICs during suspend/resume with irdma enabled, where the aux device probe widens the race window. ice 0000:81:00.0: can't open net device while reset is in progress Add a best-effort wait (10s timeout, matching ice_devlink_info_get()) for the reset to complete before returning from ice_resume(). In practice the reset completes in ~300ms. Fixes: 769c500dcc1e ("ice: Add advanced power mgmt for WoL") Cc: stable@vger.kernel.org Reviewed-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Reviewed-by: Przemek Kitszel Signed-off-by: Aaron Ma Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index b89041743f6fc..2f1dd9eea2b23 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -5713,6 +5713,16 @@ static int ice_resume(struct device *dev) /* Restart the service task */ mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period)); + /* Best-effort wait for the scheduled reset to finish so that the + * device is operational before returning. Without this, userspace + * (e.g. NetworkManager) may try to open the net device while the + * asynchronous reset is still in progress, hitting -EBUSY. + */ + ret = ice_wait_for_reset(pf, secs_to_jiffies(10)); + if (ret) + dev_err(dev, "Wait for reset timed out (10s) during resume: %d\n", + ret); + return 0; } From 326c89ea2685a66d51d6873d443d224fbce77326 Mon Sep 17 00:00:00 2001 From: Dawid Osuchowski Date: Thu, 14 May 2026 18:35:55 +0200 Subject: [PATCH 3186/3297] ice: fix VF interrupts cleanup commit fb096882095e5a8d6b5159e43793d4a38a0c5b1f upstream. When a virtual function sends an IRQ map command, the PF will set up interrupts according to that request. However, because these interrupts are never reset, the next time Virtual Function initializes, the interrupts are still enabled for a given VF, which leads to performance degradation in certain cases due to interrupts being unexpectedly enabled and thus causing interrupt floods. Cc: stable@vger.kernel.org Fixes: 1071a8358a28 ("ice: Implement virtchnl commands for AVF support") Suggested-by: Vladimir Medvedkin Reviewed-by: Aleksandr Loktionov Signed-off-by: Dawid Osuchowski Reviewed-by: Simon Horman Tested-by: Patryk Holda Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 27 +++++++++++++++++++ .../ethernet/intel/ice/ice_vf_lib_private.h | 1 + drivers/net/ethernet/intel/ice/virt/queues.c | 21 +++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index bab968b05540b..54f59bed9645e 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -848,6 +848,30 @@ static void ice_notify_vf_reset(struct ice_vf *vf) NULL); } +/** + * ice_reset_interrupts - clear all queue interrupt configuration for a VSI + * @vsi: the VSI whose interrupt registers should be cleared + * + * Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues + * in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX, + * CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only + * clears the CAUSE_ENA bit. + */ +void ice_reset_interrupts(struct ice_vsi *vsi) +{ + struct ice_pf *pf = vsi->back; + struct ice_hw *hw = &pf->hw; + int i; + + ice_for_each_alloc_rxq(vsi, i) + wr32(hw, QINT_RQCTL(vsi->rxq_map[i]), 0); + + ice_for_each_alloc_txq(vsi, i) + wr32(hw, QINT_TQCTL(vsi->txq_map[i]), 0); + + ice_flush(hw); +} + /** * ice_reset_vf - Reset a particular VF * @vf: pointer to the VF structure @@ -919,6 +943,9 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags) ice_dis_vf_qs(vf); + /* cleanup interrupt registers */ + ice_reset_interrupts(vsi); + /* Call Disable LAN Tx queue AQ whether or not queues are * enabled. This is needed for successful completion of VFR. */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h index 5392b04049862..321d29c25b7c1 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h @@ -26,6 +26,7 @@ void ice_initialize_vf_entry(struct ice_vf *vf); void ice_deinitialize_vf_entry(struct ice_vf *vf); void ice_dis_vf_qs(struct ice_vf *vf); +void ice_reset_interrupts(struct ice_vsi *vsi); int ice_check_vf_init(struct ice_vf *vf); enum virtchnl_status_code ice_err_to_virt_err(int err); struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf); diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c index a82024bf388bf..0765ee901b7c7 100644 --- a/drivers/net/ethernet/intel/ice/virt/queues.c +++ b/drivers/net/ethernet/intel/ice/virt/queues.c @@ -224,6 +224,24 @@ void ice_vf_ena_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx) wr32(hw, QINT_RQCTL(pfq), reg | QINT_RQCTL_CAUSE_ENA_M); } +/** + * ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL + * @vsi: VSI of the VF to configure + * @q_idx: VF queue index used to determine the queue in the PF's space + */ +static void ice_vf_dis_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx) +{ + struct ice_hw *hw = &vsi->back->hw; + u32 pfq = vsi->rxq_map[q_idx]; + u32 reg; + + reg = rd32(hw, QINT_RQCTL(pfq)); + reg &= ~QINT_RQCTL_CAUSE_ENA_M; + wr32(hw, QINT_RQCTL(pfq), reg); + + ice_flush(hw); +} + /** * ice_vc_ena_qs_msg * @vf: pointer to the VF info @@ -416,6 +434,8 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg) goto error_param; } + for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) + ice_vf_dis_rxq_interrupt(vsi, vf_q_id); bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF); } else if (q_map) { for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) { @@ -436,6 +456,7 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg) goto error_param; } + ice_vf_dis_rxq_interrupt(vsi, vf_q_id); /* Clear enabled queues flag */ clear_bit(vf_q_id, vf->rxq_ena); } From 6a5cc2b4e6fafaa9e8ae5faabfeedf1265e779eb Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 16 Jun 2026 23:57:42 +0800 Subject: [PATCH 3187/3297] ice: fix memory leak in ice_lbtest_prepare_rings() commit 3a9de5590da4ffd9e9c541c4c4d492aa2b54cf6e upstream. ice_lbtest_prepare_rings() frees Rx rings only when ice_vsi_start_all_rx_rings() fails. If ice_vsi_setup_rx_rings() fails after allocating some descriptors, or if ice_vsi_cfg_lan() fails after the Rx rings were prepared, the function reaches the Tx cleanup path without releasing the initialized Rx resources. Fix this by adding separate unwind paths for Rx setup failure and LAN configuration failure. The Rx setup failure path releases the partially prepared Rx rings before freeing Tx rings, while later failures first undo the LAN Tx configuration and then release the Rx rings in reverse setup order. 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-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have an Intel E800 Series adapter available to run the ethtool offline loopback selftest, no runtime testing was able to be performed. Fixes: 0e674aeb0b77 ("ice: Add handler for ethtool selftest") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Reviewed-by: Jacob Keller Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_ethtool.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index ce0f8106dcd4e..5944ab6efd448 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -1077,18 +1077,18 @@ static int ice_lbtest_prepare_rings(struct ice_vsi *vsi) status = ice_vsi_cfg_lan(vsi); if (status) - goto err_setup_rx_ring; + goto err_cfg_lan; status = ice_vsi_start_all_rx_rings(vsi); if (status) - goto err_start_rx_ring; + goto err_cfg_lan; return 0; -err_start_rx_ring: - ice_vsi_free_rx_rings(vsi); -err_setup_rx_ring: +err_cfg_lan: ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, 0); +err_setup_rx_ring: + ice_vsi_free_rx_rings(vsi); err_setup_tx_ring: ice_vsi_free_tx_rings(vsi); From d9b5419df0654a2bbbee3b92a732feca6a66c8ef Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 23 Jul 2026 10:11:40 +0800 Subject: [PATCH 3188/3297] i2c: spacemit: request IRQ after controller initialization commit cdac670237258c8ca063aa8a16998f680d81b80d upstream. spacemit_i2c_probe() requests the IRQ before it enables the clocks, resets the controller and runs init_completion(). If an interrupt is already pending, the handler runs too early: it reads registers while the clocks are still off and calls complete() on an uninitialized completion. Request the IRQ after the controller and completion are initialized, but still before the adapter is registered. Fixes: 5ea558473fa3 ("i2c: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Linmao Li Cc: # v6.15+ Reviewed-by: Troy Mitchell Reviewed-by: Alex Elder Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260723021140.2293844-1-lilinmao@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-k1.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 1c58c39b92795..f2bb04a1b4935 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -565,11 +565,6 @@ static int spacemit_i2c_probe(struct platform_device *pdev) if (i2c->irq < 0) return dev_err_probe(dev, i2c->irq, "failed to get irq resource"); - ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, - IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c); - if (ret) - return dev_err_probe(dev, ret, "failed to request irq"); - clk = devm_clk_get_enabled(dev, "func"); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock"); @@ -592,6 +587,11 @@ static int spacemit_i2c_probe(struct platform_device *pdev) init_completion(&i2c->complete); + ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, + IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq"); + platform_set_drvdata(pdev, i2c); ret = i2c_add_numbered_adapter(&i2c->adapt); From 40dd7174459983efcd3b3a72aedbd17fe52b3ede Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Thu, 25 Jun 2026 17:42:45 +0800 Subject: [PATCH 3189/3297] i2c: qcom-cci: drop custom suspend/resume and rely on runtime PM helpers commit b08c9857aa1f5f3a81d375d6d4bb1d8b92f22ebc upstream. cci_resume() unconditionally calls cci_resume_runtime() regardless of the runtime PM state. If the device is already runtime-suspended before system suspend, the clock is re-enabled while runtime_status remains RPM_SUSPENDED. As a result, pm_request_autosuspend() does not arm the timer, leaving the clock permanently enabled. Fixes: e517526195de ("i2c: Add Qualcomm CCI I2C driver") Signed-off-by: Wenmeng Liu Cc: # v5.8+ Reviewed-by: Vladimir Zapolskiy Reviewed-by: Konrad Dybcio Reviewed-by: Loic Poulain Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260625-cci-v1-1-a100cda673ce@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-qcom-cci.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 9d83358f2ae24..791e1a9fd1ba2 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -496,24 +496,8 @@ static int __maybe_unused cci_resume_runtime(struct device *dev) return 0; } -static int __maybe_unused cci_suspend(struct device *dev) -{ - if (!pm_runtime_suspended(dev)) - return cci_suspend_runtime(dev); - - return 0; -} - -static int __maybe_unused cci_resume(struct device *dev) -{ - cci_resume_runtime(dev); - pm_request_autosuspend(dev); - - return 0; -} - static const struct dev_pm_ops qcom_cci_pm = { - SET_SYSTEM_SLEEP_PM_OPS(cci_suspend, cci_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(cci_suspend_runtime, cci_resume_runtime, NULL) }; From 19b783335d62e7a2367436a6e1f1b37da1878360 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Sun, 19 Jul 2026 22:19:43 +0200 Subject: [PATCH 3190/3297] i2c: jz4780: Cache host clock rate at probe to prevent CCF prepare_lock deadlock commit d99607c888f26e8a4e9fe9772860cef4aff86bb4 upstream. Fix a severe AB/BA deadlock between the Common Clock Framework (CCF) and the I2C adapter lock, which triggers when an I2C-controlled clock generator client (like the Si5351) is registered or modified under the CCF. During an i2c client clock (generator) frequency change, the CCF acquires its global 'prepare_lock' mutex and the driver calls i2c_transfer() to update the client's chip registers, stalling for the adapter's I2C bus lock. Concurrently, an independent, parallel transfer on the same bus (e.g., a GPIO expander handling LEDs) can hold the I2C adapter lock. Inside this parallel transfer path, jz4780_i2c_set_speed() calls clk_get_rate() on the host controller's input clock to calculate bus timings. This call attempts to acquire the blocked CCF 'prepare_lock', creating a circular dependency that freezes the system. The jz4780 host controller clock itself is static and never changes at runtime. However, calling clk_get_rate() inside the active transfer path introduces an unnecessary dependency on the CCF internal locks. Eliminate this synchronous clk_get_rate() call from the active transfer path by caching the static host peripheral clock rate once - inside the private jz4780_i2c structure during jz4780_i2c_probe(). Update jz4780_i2c_set_speed() to use this cached value, safely decoupling active I2C transactions from the CCF internal locks without any risk of stale timings. Assisted-by web based Google AI (pinpointing the bug and writing the message). Fixes: ba92222ed63a12 ("i2c: jz4780: Add i2c bus controller driver for Ingenic JZ4780") Signed-off-by: H. Nikolaus Schaller Cc: # v4.1+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/2db6fd233aceb7238474e4833f4d25ca681c3ffb.1784492382.git.hns@goldelico.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-jz4780.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 664a5471d9335..695be3b214608 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -141,6 +141,7 @@ struct jz4780_i2c { void __iomem *iomem; int irq; struct clk *clk; + unsigned long clk_rate_khz; struct i2c_adapter adap; const struct ingenic_i2c_config *cdata; @@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(struct jz4780_i2c *i2c, unsigned char address) static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c) { - int dev_clk_khz = clk_get_rate(i2c->clk) / 1000; + int dev_clk_khz = i2c->clk_rate_khz; int cnt_high = 0; /* HIGH period count of the SCL clock */ int cnt_low = 0; /* LOW period count of the SCL clock */ int cnt_period = 0; /* period count of the SCL clock */ @@ -796,6 +797,8 @@ static int jz4780_i2c_probe(struct platform_device *pdev) if (IS_ERR(i2c->clk)) return PTR_ERR(i2c->clk); + i2c->clk_rate_khz = clk_get_rate(i2c->clk) / 1000; + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clk_freq); if (ret) { From 82233ff0e36df6ce4d26c7a743954307e1f4adc9 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Fri, 17 Jul 2026 10:55:07 +0200 Subject: [PATCH 3191/3297] i2c: iproc: reset bus after timeout if START_BUSY is stuck commit 98f2e9e6d6f91a6abb43f166b244b428ba85fa2b upstream. If a transaction times out, the START_BUSY signal can stay up, and subsequent transactaction attempts will fail as the bus is still considered busy. I can easily trigger this by attempting to read from an address with no device, e.g. when running i2cdetect. After the first read times out, all subsequent read attempts return busy. To get to a working state again, the controller needs to be reset to clear the START_BUSY signal. So check for START_BUSY still asserted on a timeout, and do reset in case it is, This is also done by the original non-upstream iproc-smbus driver implementation [1]. Works around situations like: bcm-iproc-2c 1803b000.i2c: transaction timed out bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy ... where the bus never recovers after a timeout. [1] https://github.com/opencomputeproject/onie/blob/master/patches/kernel/3.2.69/driver-iproc-smbus.patch Fixes: e6e5dd3566e0 ("i2c: iproc: Add Broadcom iProc I2C Driver") Signed-off-by: Jonas Gorski Cc: # v4.0+ Acked-by: Ray Jui Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260717085507.34209-1-jonas.gorski@bisdn.de Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-bcm-iproc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index e418a4f23f156..19300c098af8f 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -803,6 +803,17 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, } if (!time_left && !iproc_i2c->xfer_is_done) { + /* + * The controller may fail to clear START_BUSY after a timeout, + * reset the controller to recover in that case. + */ + if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) & + BIT(M_CMD_START_BUSY_SHIFT))) { + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + bcm_iproc_i2c_init(iproc_i2c); + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + } + /* flush both TX/RX FIFOs */ val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); From 7a5db225ab5a6b095f6c37197b3b114921d85541 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 11:04:00 +0800 Subject: [PATCH 3192/3297] i2c: imx: mark I2C adapter when hardware is powered down commit 00d86dd5c2034e0e139e4806137b3b43e07ddd83 upstream. On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers. During system suspend/resume, there exists a time window between: - suspend_noirq and the system entering suspend - the system starting to resume and resume_noirq In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored. If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang. Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available. Fixes: 358025ac091e ("i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()") Signed-off-by: Carlos Song Cc: # v6.14+ Reviewed-by: Frank Li Acked-by: Oleksij Rempel Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260525030400.3182911-1-carlos.song@oss.nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx.c | 45 ++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index d5e6e2eca3b34..d549d630b41a0 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1952,6 +1952,47 @@ static int i2c_imx_runtime_resume(struct device *dev) return 0; } +static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&i2c_imx->adapter); + + /* + * Cancel the slave timer before powering down to prevent + * i2c_imx_slave_timeout() from accessing hardware registers + * while the clock is disabled. + */ + hrtimer_cancel(&i2c_imx->slave_timer); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&i2c_imx->adapter); + if (i2c_imx->slave) { + hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY); + hrtimer_restart(&i2c_imx->slave_timer); + } + return ret; + } + + return 0; +} + +static int __maybe_unused i2c_imx_resume_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + i2c_mark_adapter_resumed(&i2c_imx->adapter); + + return 0; +} + static int i2c_imx_suspend(struct device *dev) { /* @@ -1985,8 +2026,8 @@ static int i2c_imx_resume(struct device *dev) } static const struct dev_pm_ops i2c_imx_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq, + i2c_imx_resume_noirq) SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume) RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL) }; From 12a4f0950a158d98552cbaeacc35edccd8d975fa Mon Sep 17 00:00:00 2001 From: Liem Date: Mon, 29 Jun 2026 10:38:28 +0800 Subject: [PATCH 3193/3297] i2c: imx: Fix slave registration race and error handling commit d64ec362c369bbc33833f7936d5f3a706b0d5c45 upstream. In i2c_imx_reg_slave(), the slave pointer was assigned before pm_runtime_resume_and_get(). If pm_runtime_resume_and_get() failed, the error path returned without clearing i2c_imx->slave, leaving it non-NULL and causing all subsequent registration attempts to fail with -EBUSY. Additionally, because this driver uses a shared IRQ, the interrupt handler i2c_imx_isr() can execute concurrently and, after acquiring slave_lock, dereference i2c_imx->slave. The previous fix attempt added a lockless i2c_imx->slave = NULL on the error path, but that could race with the ISR under the lock and still cause a NULL pointer dereference. Fix both issues by deferring the assignment of i2c_imx->slave and i2c_imx->last_slave_event to after a successful resume, and by performing the assignment inside the slave_lock critical section. This guarantees that the slave pointer is never left stale on the error path and is always valid when observed by the interrupt handler. Fixes: f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver") Signed-off-by: Liem Cc: # v5.11+ Reviewed-by: Frank Li Acked-by: Carlos Song Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260629023829.152651-2-liem16213@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index d549d630b41a0..49859ee494c69 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -930,9 +930,6 @@ static int i2c_imx_reg_slave(struct i2c_client *client) if (i2c_imx->slave) return -EBUSY; - i2c_imx->slave = client; - i2c_imx->last_slave_event = I2C_SLAVE_STOP; - /* Resume */ ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent); if (ret < 0) { @@ -940,6 +937,11 @@ static int i2c_imx_reg_slave(struct i2c_client *client) return ret; } + scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) { + i2c_imx->slave = client; + i2c_imx->last_slave_event = I2C_SLAVE_STOP; + } + i2c_imx_slave_init(i2c_imx); return 0; From affd62f5719a78135b7441aa49c8cab3c3b5e838 Mon Sep 17 00:00:00 2001 From: Liem Date: Mon, 29 Jun 2026 10:38:29 +0800 Subject: [PATCH 3194/3297] i2c: imx: Cancel hrtimer before clearing slave pointer commit 6ac7702b6cc2b94aaed9ef2d95bfbefcdc90061f upstream. In i2c_imx_unreg_slave(), the slave pointer is set to NULL after disabling interrupts. However, a pending interrupt might already have started the hrtimer (i2c_imx_slave_timeout) before the pointer was cleared. If the hrtimer fires after i2c_imx->slave is set to NULL, the timer callback i2c_imx_slave_finish_op() will call i2c_imx_slave_event() with a NULL slave pointer, which results in a use-after-free / NULL pointer dereference. Fix by canceling the hrtimer and waiting for it to complete after disabling interrupts, before clearing the slave pointer. Fixes: f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver") Signed-off-by: Liem Cc: # v5.11+ Acked-by: Carlos Song Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260629023829.152651-3-liem16213@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 49859ee494c69..9477d814fde94 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -960,6 +960,7 @@ static int i2c_imx_unreg_slave(struct i2c_client *client) i2c_imx_reset_regs(i2c_imx); + hrtimer_cancel(&i2c_imx->slave_timer); i2c_imx->slave = NULL; /* Suspend */ From 26cf99713a96f3518e7829758f7212d7c83b76ae Mon Sep 17 00:00:00 2001 From: Lucas Martins Alves Date: Tue, 14 Jul 2026 16:48:57 +0000 Subject: [PATCH 3195/3297] can: c_can: c_can_chip_config(): keep controller in init mode until bittiming is configured commit 26504844613fb44c7cab1c5f6fcff77861709baa upstream. c_can_chip_config() was programming C_CAN_CTRL_REG without CONTROL_INIT, which may allow the controller to become active before c_can_set_bittiming() finishes. That creates a short timing window where the peripheral can interact with the bus using a different/default bitrate, potentially generating bus errors and corrupting traffic. Set CONTROL_INIT together with the control-mode writes in c_can_chip_config() (normal, loopback and listen-only paths), so the controller stays halted until bit timing is fully programmed. This prevents transient bus disturbance during startup when the configured bitrate differs from the active bus bitrate. Signed-off-by: Lucas Martins Alves Link: https://patch.msgid.link/20260714164839.771123-1-lucas.alves@lumal21.com.br Fixes: 881ff67ad450 ("can: c_can: Added support for Bosch C_CAN controller") Cc: stable@kernel.org [mkl: remove space before close parenthesis] Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/c_can/c_can_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c index cc371d0c9f3c7..2ea5224ebfcb5 100644 --- a/drivers/net/can/c_can/c_can_main.c +++ b/drivers/net/can/c_can/c_can_main.c @@ -597,20 +597,20 @@ static int c_can_chip_config(struct net_device *dev) return err; /* enable automatic retransmission */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR | CONTROL_INIT); if ((priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) && (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)) { /* loopback + silent mode : useful for hot self-test */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT); priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK | TEST_SILENT); } else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { /* loopback mode : useful for self-test function */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT); priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK); } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { /* silent mode : bus-monitoring mode */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT); priv->write_reg(priv, C_CAN_TEST_REG, TEST_SILENT); } From 0b23144c59c126beb4a7761a85a194ae0fe668a5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 17:27:52 +0800 Subject: [PATCH 3196/3297] can: ems_usb: validate CPC message lengths commit 02925f51377f2a42a6724f00549167499c9302e5 upstream. ems_usb_read_bulk_callback() walks CPC messages packed in one USB receive buffer. Check that each declared message fits in the URB payload. Also require the type-specific payload to cover the fields used by the CAN, state, error and overrun handlers. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706092752.79600-1-pengpeng@iscas.ac.cn Fixes: 702171adeed3 ("ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface") Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/ems_usb.c | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index d8c881130e900..4b130c96c9ab2 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -409,6 +409,40 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) netif_rx(skb); } +static bool ems_usb_rx_msg_len_valid(struct ems_cpc_msg *msg) +{ + size_t len = msg->length; + size_t can_len; + + switch (msg->type) { + case CPC_MSG_TYPE_CAN_STATE: + return len >= sizeof(msg->msg.can_state); + + case CPC_MSG_TYPE_CAN_FRAME: + case CPC_MSG_TYPE_EXT_CAN_FRAME: + case CPC_MSG_TYPE_RTR_FRAME: + case CPC_MSG_TYPE_EXT_RTR_FRAME: + if (len < CPC_CAN_MSG_MIN_SIZE) + return false; + + if (msg->type == CPC_MSG_TYPE_RTR_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) + return true; + + can_len = can_cc_dlc2len(msg->msg.can_msg.length & 0xf); + return len >= CPC_CAN_MSG_MIN_SIZE + can_len; + + case CPC_MSG_TYPE_CAN_FRAME_ERROR: + return len >= sizeof(msg->msg.error); + + case CPC_MSG_TYPE_OVERRUN: + return len >= sizeof(msg->msg.overrun); + + default: + return true; + } +} + /* * callback for bulk IN urb */ @@ -451,6 +485,15 @@ static void ems_usb_read_bulk_callback(struct urb *urb) } msg = (struct ems_cpc_msg *)&ibuf[start]; + if (msg->length > + urb->actual_length - start - CPC_MSG_HEADER_LEN) { + netdev_err(netdev, "format error\n"); + break; + } + if (!ems_usb_rx_msg_len_valid(msg)) { + netdev_err(netdev, "format error\n"); + break; + } switch (msg->type) { case CPC_MSG_TYPE_CAN_STATE: From c311f17c261fd375ddf5755f2ebe1f022c19c5b0 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 6 Jul 2026 09:46:01 +0800 Subject: [PATCH 3197/3297] can: etas_es58x: es58x_read_bulk_callback(): fix RX buffer leak on URB resubmit failure commit 7a0cf2b2497c757c3cb1286eddf2986abb0d387b upstream. es58x_read_bulk_callback() resubmits the RX URB after processing a received packet. If the resubmit succeeds, the URB remains anchored and will be handled by the normal RX path or by teardown. However, if usb_submit_urb() fails, the callback unanchors the URB and then returns directly. This skips the existing free_urb path, so the coherent transfer buffer allocated with usb_alloc_coherent() is not released. Reuse the existing free_urb path after a resubmit failure so that the RX coherent buffer is freed before leaving the callback. Fixes: 5eaad4f76826 ("can: usb: etas_es58x: correctly anchor the urb in the read bulk callback") Signed-off-by: Guangshuo Li Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/20260706014601.415445-1-lgs201920130244@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/etas_es58x/es58x_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index 1e44bd1dfee9b..70d9cd11cb855 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -1476,7 +1476,6 @@ static void es58x_read_bulk_callback(struct urb *urb) dev_err_ratelimited(dev, "Failed resubmitting read bulk urb: %pe\n", ERR_PTR(ret)); - return; free_urb: usb_free_coherent(urb->dev, urb->transfer_buffer_length, From 996eb21acdc9cac1eb6645f0aa020de98ade281a Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 9 Jul 2026 09:54:26 +0200 Subject: [PATCH 3198/3297] can: gs_usb: gs_usb_receive_bulk_callback(): resubmit URB on skb allocation failure commit 68c5724ecd159992f76edb7b57dc508a44c8b7da upstream. If the allocation of the SKB in gs_usb_receive_bulk_callback() fails, the driver returns from the callback without resubmitting the URB in order to receive further USB in URBs. This results in a silent performance degradation which, if it occurs repeatedly, results in starvation of USB in traffic. Instead of returning immediately, try to resend the URB. If this also fails, this is logged as an info message. Fixes: d08e973a77d1 ("can: gs_usb: Added support for the GS_USB CAN devices") Fixes: 26949ac935e3 ("can: gs_usb: add CAN-FD support") Link: https://patch.msgid.link/20260709-gs_usb-resubmit-urb-v1-1-4dd40030cc84@pengutronix.de Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/gs_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index e336703b941ef..241ffa8daaee5 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -674,7 +674,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) if (hf->flags & GS_CAN_FLAG_FD) { skb = alloc_canfd_skb(netdev, &cfd); if (!skb) - return; + goto resubmit_urb; cfd->can_id = le32_to_cpu(hf->can_id); cfd->len = data_length; @@ -687,7 +687,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) } else { skb = alloc_can_skb(netdev, &cf); if (!skb) - return; + goto resubmit_urb; cf->can_id = le32_to_cpu(hf->can_id); can_frame_set_cc_len(cf, hf->can_dlc, dev->can.ctrlmode); From 8604a3b81b9d0ceaf04fee5f52e701f623a179f9 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 28 Jul 2026 07:58:35 +0200 Subject: [PATCH 3199/3297] can: j1939: transport: j1939_session_fresh_new(): initialize receive buffer commit eb96c58907922546e415e545fe9a14ea63b02719 upstream. Zero the allocated buffer in j1939_session_fresh_new() to ensure it contains no residual data. While there is a potential performance impact if users allocate maximum sized ETP buffers, most real-world use cases are not noticeably affected since the maximum known buffer size is typically around 65K. Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Reported-by: Ji'an Zhou Message-ID: Signed-off-by: Oleksij Rempel Link: https://patch.msgid.link/20260728055835.1151785-3-o.rempel@pengutronix.de Cc: stable@kernel.org [mkl: add Message-ID] Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/j1939/transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 9ec929bc0c0da..f64f931dd0e46 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -1558,7 +1558,7 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, } /* alloc data area */ - skb_put(skb, size); + skb_put_zero(skb, size); /* skb is recounted in j1939_session_new() */ return session; } From 54258ea8d61fc2ff52e151cdecc3fb6c50dc8b8a Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 28 Jul 2026 07:58:34 +0200 Subject: [PATCH 3200/3297] can: j1939: use netdevice_tracker for j1939_{priv,session,ecu} tracking commit d2fb981384b3a45f690616d550b29046e8ad16a4 upstream. syzbot is still reporting unregister_netdevice: waiting for vcan0 to become free. Usage count = 2 problem. A debug printk() patch in linux-next-20260508 identified that there is dev_hold()/dev_put() imbalance in j1939_priv management. Call trace for vcan0[26] +4 at __dev_hold include/linux/netdevice.h:4470 [inline] netdev_hold include/linux/netdevice.h:4513 [inline] dev_hold include/linux/netdevice.h:4536 [inline] j1939_priv_create net/can/j1939/main.c:140 [inline] j1939_netdev_start+0x36b/0xc10 net/can/j1939/main.c:268 j1939_sk_bind+0x853/0xb30 net/can/j1939/socket.c:506 __sys_bind_socket net/socket.c:1948 [inline] __sys_bind+0x2e9/0x410 net/socket.c:1979 Call trace for vcan0[28] -3 at __dev_put include/linux/netdevice.h:4456 [inline] netdev_put include/linux/netdevice.h:4523 [inline] dev_put include/linux/netdevice.h:4548 [inline] __j1939_priv_release net/can/j1939/main.c:166 [inline] kref_put include/linux/kref.h:65 [inline] j1939_priv_put+0x128/0x270 net/can/j1939/main.c:172 j1939_sk_sock_destruct+0x52/0x90 net/can/j1939/socket.c:388 __sk_destruct+0x8d/0x9d0 net/core/sock.c:2352 rcu_do_batch kernel/rcu/tree.c:2617 [inline] rcu_core kernel/rcu/tree.c:2869 [inline] rcu_cpu_kthread+0x99e/0x1470 kernel/rcu/tree.c:2957 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 This refcount leak in j1939_priv might be caused by a refcount leak in j1939_{session,ecu} because j1939_{session,ecu} holds a ref on j1939_priv. For further investigation using upstream kernels, enable netdevice_tracker in j1939_{priv,session,ecu} management. Signed-off-by: Tetsuo Handa Acked-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Link: https://patch.msgid.link/20260728055835.1151785-2-o.rempel@pengutronix.de Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- net/can/j1939/bus.c | 2 ++ net/can/j1939/j1939-priv.h | 3 +++ net/can/j1939/main.c | 8 ++++---- net/can/j1939/transport.c | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c index 797719cb227ec..e6d81bcabc897 100644 --- a/net/can/j1939/bus.c +++ b/net/can/j1939/bus.c @@ -20,6 +20,7 @@ static void __j1939_ecu_release(struct kref *kref) struct j1939_priv *priv = ecu->priv; list_del(&ecu->list); + netdev_put(priv->ndev, &ecu->priv_dev_tracker); kfree(ecu); j1939_priv_put(priv); } @@ -155,6 +156,7 @@ struct j1939_ecu *j1939_ecu_create_locked(struct j1939_priv *priv, name_t name) if (!ecu) return ERR_PTR(-ENOMEM); kref_init(&ecu->kref); + netdev_hold(priv->ndev, &ecu->priv_dev_tracker, gfp_any()); ecu->addr = J1939_IDLE_ADDR; ecu->name = name; diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h index 81f58924b4acd..cf26352d1d8ce 100644 --- a/net/can/j1939/j1939-priv.h +++ b/net/can/j1939/j1939-priv.h @@ -38,6 +38,7 @@ struct j1939_ecu { struct hrtimer ac_timer; struct kref kref; struct j1939_priv *priv; + netdevice_tracker priv_dev_tracker; /* count users, to help transport protocol decide for interaction */ int nusers; @@ -60,6 +61,7 @@ struct j1939_priv { rwlock_t lock; struct net_device *ndev; + netdevice_tracker dev_tracker; /* list of 256 ecu ptrs, that cache the claimed addresses. * also protected by the above lock @@ -230,6 +232,7 @@ enum j1939_session_state { struct j1939_session { struct j1939_priv *priv; + netdevice_tracker priv_dev_tracker; struct list_head active_session_list_entry; struct list_head sk_session_queue_entry; struct kref kref; diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c index a93af55df5fd5..21186932f8f61 100644 --- a/net/can/j1939/main.c +++ b/net/can/j1939/main.c @@ -137,7 +137,7 @@ static struct j1939_priv *j1939_priv_create(struct net_device *ndev) priv->ndev = ndev; kref_init(&priv->kref); kref_init(&priv->rx_kref); - dev_hold(ndev); + netdev_hold(ndev, &priv->dev_tracker, GFP_KERNEL); netdev_dbg(priv->ndev, "%s : 0x%p\n", __func__, priv); @@ -163,7 +163,7 @@ static void __j1939_priv_release(struct kref *kref) WARN_ON_ONCE(!list_empty(&priv->ecus)); WARN_ON_ONCE(!list_empty(&priv->j1939_socks)); - dev_put(ndev); + netdev_put(ndev, &priv->dev_tracker); kfree(priv); } @@ -281,7 +281,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) */ kref_get(&priv_new->rx_kref); mutex_unlock(&j1939_netdev_lock); - dev_put(ndev); + netdev_put(ndev, &priv->dev_tracker); kfree(priv); return priv_new; } @@ -298,7 +298,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) j1939_priv_set(ndev, NULL); mutex_unlock(&j1939_netdev_lock); - dev_put(ndev); + netdev_put(ndev, &priv->dev_tracker); kfree(priv); return ERR_PTR(ret); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index f64f931dd0e46..72f6c06e1e5b0 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -282,6 +282,7 @@ static void j1939_session_destroy(struct j1939_session *session) kfree_skb(skb); } __j1939_session_drop(session); + netdev_put(session->priv->ndev, &session->priv_dev_tracker); j1939_priv_put(session->priv); kfree(session); } @@ -1510,6 +1511,7 @@ static struct j1939_session *j1939_session_new(struct j1939_priv *priv, INIT_LIST_HEAD(&session->active_session_list_entry); INIT_LIST_HEAD(&session->sk_session_queue_entry); kref_init(&session->kref); + netdev_hold(priv->ndev, &session->priv_dev_tracker, gfp_any()); j1939_priv_get(priv); session->priv = priv; From 2e90b2b406077e7c2a2573edc11a0c3032ba5225 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Wed, 22 Jul 2026 16:09:03 +0530 Subject: [PATCH 3201/3297] can: kvaser_usb: kvaser_usb_hydra_get_busparams(): fix memory leak in kvaser_usb_hydra_get_busparams() commit 941eaf9a6d3b33dea49f2c0a1da7546a03b6ff71 upstream. The memory allocated for cmd is not freed after the call to kvaser_usb_send_cmd() in both the normal and error paths. Fix that by adding a kfree() immediately after the call. Fixes: 39d3df6b0ea8 ("can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Link: https://patch.msgid.link/20260722103906.108571-1-nihaal@cse.iitm.ac.in Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index a59f20dad692a..6f8d3bc42733c 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -1626,6 +1626,7 @@ static int kvaser_usb_hydra_get_busparams(struct kvaser_usb_net_priv *priv, reinit_completion(&priv->get_busparams_comp); err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + kfree(cmd); if (err) return err; From 185cb1fa38142a3cbf223dd8b3abb24217f330d3 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 22 Jul 2026 12:22:21 +0800 Subject: [PATCH 3202/3297] can: kvaser_usb_leaf: kvaser_usb_leaf_wait_cmd(): validate received command extents commit 0293dd153f9dbc1ddf5dacdccc76b363bce4a8ee upstream. The wait and bulk receive paths walk variable-length commands from a USB buffer. A nonzero command shorter than CMD_HEADER_LEN can still be dispatched, and the wait path copies a matching command into a fixed caller-owned struct kvaser_cmd using the device-provided length. Reject nonzero commands that do not contain the fixed header or that extend beyond the current USB buffer item. In the wait path, also reject a matching command that exceeds the destination before copying it. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260722042221.44066-1-pengpeng@iscas.ac.cn Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 1167d38344f1d..c9322f279e3e8 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -691,13 +691,22 @@ static int kvaser_usb_leaf_wait_cmd(const struct kvaser_usb *dev, u8 id, continue; } - if (pos + tmp->len > actual_len) { + if (tmp->len < CMD_HEADER_LEN || + tmp->len > actual_len - pos) { dev_err_ratelimited(&dev->intf->dev, "Format error\n"); break; } if (tmp->id == id) { + if (tmp->len > sizeof(*cmd)) { + dev_err_ratelimited(&dev->intf->dev, + "Received command %u too large (%u)\n", + tmp->id, tmp->len); + err = -EIO; + goto end; + } + memcpy(cmd, tmp, tmp->len); goto end; } @@ -1737,7 +1746,7 @@ static void kvaser_usb_leaf_read_bulk_callback(struct kvaser_usb *dev, continue; } - if (pos + cmd->len > len) { + if (cmd->len < CMD_HEADER_LEN || cmd->len > len - pos) { dev_err_ratelimited(&dev->intf->dev, "Format error\n"); break; } From 2ee477e541a6d5e434d6a4041c6b677ab42e1d82 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 22 Jul 2026 12:43:47 +0800 Subject: [PATCH 3203/3297] can: softing: fw_parse(): validate firmware record spans commit 856d6cb04e5407523566b075841dcd6423757d1c upstream. fw_parse() reads a fixed record header, a firmware-provided payload, and a trailing checksum without knowing the end of the firmware blob. A truncated record can therefore make those reads exceed the blob. The same record also supplies addresses and lengths for writes into DPRAM. The generic loader uses wrap-prone mixed signed arithmetic for its bounds check, while the application loader does not bound the staging copy at all. Pass the firmware end to the parser and validate the full source record. Use a signed wide offset for generic DPRAM records and validate the application staging span against the mapped DPRAM before copying. Fixes: 03fd3cf5a179 ("can: add driver for Softing card") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260722044347.2708-1-pengpeng@iscas.ac.cn Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/softing/softing_fw.c | 46 +++++++++++++++++++--------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c index 721df91cdbfbc..282570daf3efe 100644 --- a/drivers/net/can/softing/softing_fw.c +++ b/drivers/net/can/softing/softing_fw.c @@ -91,12 +91,12 @@ int softing_bootloader_command(struct softing *card, int16_t cmd, return ret; } -static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, - uint16_t *plen, const uint8_t **pdat) +static int fw_parse(const u8 **pmem, const u8 *limit, u16 *ptype, + u32 *paddr, u16 *plen, const u8 **pdat) { uint16_t checksum[2]; - const uint8_t *mem; - const uint8_t *end; + const u8 *mem; + const u8 *record_end; /* * firmware records are a binary, unaligned stream composed of: @@ -114,14 +114,21 @@ static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, * endianness & alignment. */ mem = *pmem; + /* A record needs an 8-byte prefix and a 2-byte checksum. */ + if (mem > limit || limit - mem < 10) + return -EINVAL; + *ptype = le16_to_cpup((void *)&mem[0]); *paddr = le32_to_cpup((void *)&mem[2]); *plen = le16_to_cpup((void *)&mem[6]); + if (*plen > limit - mem - 10) + return -EINVAL; + *pdat = &mem[8]; /* verify checksum */ - end = &mem[8 + *plen]; - checksum[0] = le16_to_cpup((void *)end); - for (checksum[1] = 0; mem < end; ++mem) + record_end = &mem[8 + *plen]; + checksum[0] = le16_to_cpup((void *)record_end); + for (checksum[1] = 0; mem < record_end; ++mem) checksum[1] += *mem; if (checksum[0] != checksum[1]) return -EINVAL; @@ -139,6 +146,7 @@ int softing_load_fw(const char *file, struct softing *card, uint16_t type, len; uint32_t addr; uint8_t *buf = NULL, *new_buf; + s64 dpram_offset; int buflen = 0; int8_t type_end = 0; @@ -153,7 +161,7 @@ int softing_load_fw(const char *file, struct softing *card, mem = fw->data; end = &mem[fw->size]; /* look for header record */ - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret < 0) goto failed; if (type != 0xffff) @@ -164,7 +172,7 @@ int softing_load_fw(const char *file, struct softing *card, } /* ok, we had a header */ while (mem < end) { - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret < 0) goto failed; if (type == 3) { @@ -179,9 +187,13 @@ int softing_load_fw(const char *file, struct softing *card, goto failed; } - if ((addr + len + offset) > size) + dpram_offset = (s64)addr + offset; + if (dpram_offset < 0 || dpram_offset > size || + len > size - dpram_offset) { + ret = -EINVAL; goto failed; - memcpy_toio(&dpram[addr + offset], dat, len); + } + memcpy_toio(&dpram[dpram_offset], dat, len); /* be sure to flush caches from IO space */ mb(); if (len > buflen) { @@ -195,7 +207,7 @@ int softing_load_fw(const char *file, struct softing *card, buf = new_buf; } /* verify record data */ - memcpy_fromio(buf, &dpram[addr + offset], len); + memcpy_fromio(buf, &dpram[dpram_offset], len); if (memcmp(buf, dat, len)) { /* is not ok */ dev_alert(&card->pdev->dev, "DPRAM readback failed\n"); @@ -237,7 +249,7 @@ int softing_load_app_fw(const char *file, struct softing *card) mem = fw->data; end = &mem[fw->size]; /* look for header record */ - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret) goto failed; ret = -EINVAL; @@ -253,7 +265,7 @@ int softing_load_app_fw(const char *file, struct softing *card) } /* ok, we had a header */ while (mem < end) { - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret) goto failed; @@ -279,6 +291,12 @@ int softing_load_app_fw(const char *file, struct softing *card) /* work in 16bit (target) */ sum &= 0xffff; + if (card->pdat->app.offs > card->dpram_size || + len > card->dpram_size - card->pdat->app.offs) { + ret = -EINVAL; + goto failed; + } + memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len); iowrite32(card->pdat->app.offs + card->pdat->app.addr, &card->dpram[DPRAM_COMMAND + 2]); From 1acab790b7cecd4e144d1d18bdfe549e282f6b0b Mon Sep 17 00:00:00 2001 From: James Gao Date: Wed, 20 May 2026 13:40:03 +0800 Subject: [PATCH 3204/3297] can: peak_usb: add bounds check for USB channel index commit 39132f166ca8ce00ae60d8a9068e06a60943cc4b upstream. The channel control index ctrl_idx is derived from rx->len which comes directly from a device USB payload. The mask 0x0f allows values 0-15, but the array size of usb_if->dev[] is only 2. Values 2-15 cause heap out-of-bounds read, eventually causing kernel panic in the IRQ context. Add bounds checking for ctrl_idx before the array access in both pcan_usb_pro_handle_canmsg() and pcan_usb_pro_handle_error(). Fixes: d8a199355f8f ("can: usb: PEAK-System Technik PCAN-USB Pro specific part") Signed-off-by: James Gao Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/TYWPR01MB8559DBAAAA6A7F410400329CF0012@TYWPR01MB8559.jpnprd01.prod.outlook.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index 7be286293b1a2..c6023acd05e5a 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -534,12 +534,18 @@ static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if, struct pcan_usb_pro_rxmsg *rx) { const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f; - struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; - struct net_device *netdev = dev->netdev; + struct peak_usb_device *dev; + struct net_device *netdev; struct can_frame *can_frame; struct sk_buff *skb; struct skb_shared_hwtstamps *hwts; + if (ctrl_idx >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; + skb = alloc_can_skb(netdev, &can_frame); if (!skb) return -ENOMEM; @@ -573,14 +579,20 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, { const u16 raw_status = le16_to_cpu(er->status); const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f; - struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; - struct net_device *netdev = dev->netdev; + struct peak_usb_device *dev; + struct net_device *netdev; struct can_frame *can_frame; enum can_state new_state = CAN_STATE_ERROR_ACTIVE; u8 err_mask = 0; struct sk_buff *skb; struct skb_shared_hwtstamps *hwts; + if (ctrl_idx >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; + /* nothing should be sent while in BUS_OFF state */ if (dev->can.state == CAN_STATE_BUS_OFF) return 0; From 92d0de80ca2223b9c7da78020155b6cb27824cc0 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 02:15:31 +0800 Subject: [PATCH 3205/3297] can: peak_usb: peak_usb_start(): fix double free of transfer buffer on URB submit error commit 9b3d5a6d952c38bbcf07f903cbeadefdb56b9bc9 upstream. In peak_usb_start(), each RX URB transfer buffer is allocated with kmalloc() and the URB is flagged URB_FREE_BUFFER so that the final usb_free_urb() also frees the transfer buffer. If usb_submit_urb() fails, the error path frees the buffer explicitly with kfree(buf) and then calls usb_free_urb(urb). Because URB_FREE_BUFFER is set, usb_free_urb() -> urb_destroy() frees the same buffer a second time, a double free of the transfer buffer. BUG: KASAN: double-free in usb_free_urb.part.0+0x91/0xb0 Free of addr ffff8881069ccb80 by task trigger.sh/285 Call Trace: kfree+0x113/0x3c0 usb_free_urb.part.0+0x91/0xb0 Drop the redundant kfree(buf); usb_free_urb() already releases the transfer buffer. This mirrors commit 03819abbeb11 ("net: usb: lan78xx: Fix double free issue with interrupt buffer allocation"). Fixes: bb4785551f64 ("can: usb: PEAK-System Technik USB adapters driver core") Closes: https://lore.kernel.org/linux-can/178159320216.2154888.16953451793788581739@maoyixie.com/T/#u Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/178163373110.2507866.216458825145756798@maoyixie.com Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/peak_usb/pcan_usb_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index c74302ca7cee9..529e9cb2570fe 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -470,7 +470,6 @@ static int peak_usb_start(struct peak_usb_device *dev) netif_device_detach(dev->netdev); usb_unanchor_urb(urb); - kfree(buf); usb_free_urb(urb); break; } From 2427ef427bdd78d862c7c76597bfd9eda88b81f1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 17:28:36 +0800 Subject: [PATCH 3206/3297] can: peak_usb: validate uCAN receive record lengths commit 93fcab2c6968446316bbb49548848df604d6346f upstream. pcan_usb_fd_decode_buf() walks uCAN records packed in one USB receive buffer. Require each record to contain the fixed header for its type, and verify CAN payload bytes before copying them into the skb. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706092836.79754-1-pengpeng@iscas.ac.cn Fixes: 0a25e1f4f185 ("can: peak_usb: add support for PEAK new CANFD USB adapters") Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 40 +++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index be84191cde56f..f92a3b404ec18 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -566,6 +566,13 @@ static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if, dev->can.ctrlmode); } + if (!(rx_msg_flags & PUCAN_MSG_RTR) && + le16_to_cpu(rx_msg->size) - offsetof(struct pucan_rx_msg, d) < + cfd->len) { + kfree_skb(skb); + return -EBADMSG; + } + cfd->can_id = le32_to_cpu(rm->can_id); if (rx_msg_flags & PUCAN_MSG_EXT_ID) @@ -714,6 +721,24 @@ static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if, peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low)); } +static size_t pcan_usb_fd_rx_msg_min_size(u16 rx_msg_type) +{ + switch (rx_msg_type) { + case PUCAN_MSG_CAN_RX: + return offsetof(struct pucan_rx_msg, d); + case PCAN_UFD_MSG_CALIBRATION: + return sizeof(struct pcan_ufd_ts_msg); + case PUCAN_MSG_ERROR: + return sizeof(struct pucan_error_msg); + case PUCAN_MSG_STATUS: + return sizeof(struct pucan_status_msg); + case PCAN_UFD_MSG_OVERRUN: + return sizeof(struct pcan_ufd_ovr_msg); + default: + return sizeof(struct pucan_msg); + } +} + /* callback for bulk IN urb */ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) { @@ -728,6 +753,12 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) msg_end = urb->transfer_buffer + urb->actual_length; for (; msg_ptr < msg_end;) { u16 rx_msg_type, rx_msg_size; + size_t rx_msg_min_size; + + if (msg_end - msg_ptr < sizeof(*rx_msg)) { + err = -EBADMSG; + break; + } rx_msg = (struct pucan_msg *)msg_ptr; if (!rx_msg->size) { @@ -739,13 +770,20 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) rx_msg_type = le16_to_cpu(rx_msg->type); /* check if the record goes out of current packet */ - if (msg_ptr + rx_msg_size > msg_end) { + if (rx_msg_size > msg_end - msg_ptr) { netdev_err(netdev, "got frag rec: should inc usb rx buf sze\n"); err = -EBADMSG; break; } + rx_msg_min_size = pcan_usb_fd_rx_msg_min_size(rx_msg_type); + if (rx_msg_size < rx_msg_min_size) { + netdev_err(netdev, "got short rec\n"); + err = -EBADMSG; + break; + } + switch (rx_msg_type) { case PUCAN_MSG_CAN_RX: err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg); From aa5e790bf185e870ee2f852ab4d9dc7d28f797fa Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 23:19:57 +0800 Subject: [PATCH 3207/3297] can: ctucanfd: add missing MODULE_DEVICE_TABLE() commit d937bdb244a751fe5967052ea2d64a7b2c476cc0 upstream. The driver has a match table for the pci bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the driver registration structure, and the missing module alias publication. Signed-off-by: Pengpeng Hou Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260704151957.48194-1-pengpeng@iscas.ac.cn Fixes: 792a5b678e81 ("can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support.") Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/ctucanfd/ctucanfd_pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/ctucanfd/ctucanfd_pci.c b/drivers/net/can/ctucanfd/ctucanfd_pci.c index 9da09e7dd63a0..20e6415c91d7c 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_pci.c +++ b/drivers/net/can/ctucanfd/ctucanfd_pci.c @@ -274,6 +274,7 @@ static const struct pci_device_id ctucan_pci_tbl[] = { CTUCAN_WITH_CTUCAN_ID)}, {}, }; +MODULE_DEVICE_TABLE(pci, ctucan_pci_tbl); static struct pci_driver ctucan_pci_driver = { .name = KBUILD_MODNAME, From cae2880f8ffaeb3f2901301363e91ddd7c65c203 Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Wed, 22 Jul 2026 22:27:26 +0300 Subject: [PATCH 3208/3297] can: ctucanfd: use self-test mode for PRESUME_ACK commit c31a435933f18be0f874302161333e9f16e200a0 upstream. Use self-test mode for CAN_CTRLMODE_PRESUME_ACK so transmitted frames can complete without receiving an ACK. ACK forbidden mode prevents the controller from acknowledging received frames and does not implement the presume-ack behavior. Fixes: 2dcb8e8782d8 ("can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.") Signed-off-by: Avi Weiss Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260722192726.230729-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/ctucanfd/ctucanfd_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c index 33a5ec4346278..8e94142e77dd0 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_base.c +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c @@ -340,8 +340,8 @@ static void ctucan_set_mode(struct ctucan_priv *priv, const struct can_ctrlmode (mode_reg & ~REG_MODE_FDE); mode_reg = (mode->flags & CAN_CTRLMODE_PRESUME_ACK) ? - (mode_reg | REG_MODE_ACF) : - (mode_reg & ~REG_MODE_ACF); + (mode_reg | REG_MODE_STM) : + (mode_reg & ~REG_MODE_STM); mode_reg = (mode->flags & CAN_CTRLMODE_FD_NON_ISO) ? (mode_reg | REG_MODE_NISOFD) : From 46fc5aecde5cd9f9edabffc07d664b66b515fc1c Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Thu, 23 Jul 2026 12:59:34 +0300 Subject: [PATCH 3209/3297] can: ctucanfd: unmap BAR0 using base address commit a6873910f983096746d1a2e0af94f36b8003e839 upstream. BAR0 is mapped into bar0_base, while cra_addr points to an offset within that mapping and is used for other purposes. Pass bar0_base to pci_iounmap(), instead of cra_addr, on the probe error path so the address returned by pci_iomap() is used for unmapping. Fixes: 792a5b678e81 ("can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support.") Signed-off-by: Avi Weiss Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260723095934.181042-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/ctucanfd/ctucanfd_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_pci.c b/drivers/net/can/ctucanfd/ctucanfd_pci.c index 20e6415c91d7c..8cc9956492874 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_pci.c +++ b/drivers/net/can/ctucanfd/ctucanfd_pci.c @@ -202,7 +202,7 @@ static int ctucan_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, NULL); kfree(bdata); err_pci_iounmap_bar0: - pci_iounmap(pdev, cra_addr); + pci_iounmap(pdev, bar0_base); err_pci_iounmap_bar1: pci_iounmap(pdev, addr); err_release_regions: From 7d1619f56a75a2013bbfaf9e3a019137dc58b0c6 Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Thu, 23 Jul 2026 10:44:03 +0300 Subject: [PATCH 3210/3297] can: ctucanfd: handle bus error interrupts commit e74bae899529f49c0f375307983d12e8ecad7d4b upstream. Include REG_INT_STAT_BEI in the top-level error interrupt condition. BEI is enabled when CAN_CTRLMODE_BERR_REPORTING is requested and ctucan_err_interrupt() already handles it. Without checking and clearing BEI in the top-level handler, bus error interrupts are not handled or acknowledged. Fixes: 2dcb8e8782d8 ("can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.") Signed-off-by: Avi Weiss Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260723074403.131575-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/ctucanfd/ctucanfd_base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c index 8e94142e77dd0..c90cccf4447f9 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_base.c +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c @@ -1136,8 +1136,12 @@ static irqreturn_t ctucan_interrupt(int irq, void *dev_id) /* Error interrupts */ if (FIELD_GET(REG_INT_STAT_EWLI, isr) || FIELD_GET(REG_INT_STAT_FCSI, isr) || - FIELD_GET(REG_INT_STAT_ALI, isr)) { - icr = isr & (REG_INT_STAT_EWLI | REG_INT_STAT_FCSI | REG_INT_STAT_ALI); + FIELD_GET(REG_INT_STAT_ALI, isr) || + FIELD_GET(REG_INT_STAT_BEI, isr)) { + icr = isr & (REG_INT_STAT_EWLI | + REG_INT_STAT_FCSI | + REG_INT_STAT_ALI | + REG_INT_STAT_BEI); ctucan_netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x\n", icr); ctucan_write32(priv, CTUCANFD_INT_STAT, icr); From e8b797940536c42f4c04c623f89c6a5b65434846 Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Thu, 23 Jul 2026 18:55:43 +0300 Subject: [PATCH 3211/3297] can: ctucanfd: mark error-active controller status valid commit 4e735cbe3affe88001428fdd9cae8e685ce92f21 upstream. In the CAN_STATE_ERROR_ACTIVE case, cf->data[1] is set to CAN_ERR_CRTL_ACTIVE, but cf->can_id is not set with CAN_ERR_CRTL in that path. Set CAN_ERR_CRTL so consumers know the controller-status information in cf->data[1] is valid. Fixes: 9bd24927e3ee ("can: ctucanfd: handle skb allocation failure") Signed-off-by: Avi Weiss Link: https://patch.msgid.link/20260723155543.318414-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/ctucanfd/ctucanfd_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c index c90cccf4447f9..178fa05bac66e 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_base.c +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c @@ -869,7 +869,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, u32 isr) break; case CAN_STATE_ERROR_ACTIVE: if (skb) { - cf->can_id |= CAN_ERR_CNT; + cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; cf->data[1] = CAN_ERR_CRTL_ACTIVE; cf->data[6] = bec.txerr; cf->data[7] = bec.rxerr; From 58d2bb394e88497e10fc039c0429c0f6aa762fe7 Mon Sep 17 00:00:00 2001 From: Alexander Kaplan Date: Wed, 10 Jun 2026 21:38:25 +0200 Subject: [PATCH 3212/3297] drm/dp: Read the PCON max FRL bandwidth only for HDMI DFPs commit e40e20ac089e32f1d910636155dc82e61e61dcf3 upstream. The PCON max FRL bandwidth field lives in byte 2 of the DFP Detailed Capability Info (DPCD 0x82 for the first DFP). The DP standard defines the meaning of descriptor bytes 1-3 strictly per DFP type, and for a DisplayPort type DFP all of them are reserved, with "read all 0s" semantics (DP v2.0, section 2.12.3, Table 2-183). The FRL bandwidth field is an HDMI DFP extension added by the VESA DP-to-HDMI PCON specification. drm_dp_get_pcon_max_frl_bw() however parses the byte without checking the DFP type, the branch presence or DETAILED_CAP_INFO_AVAILABLE. Without the latter the port descriptors are one byte wide and port_cap[2] is not even the right register. All neighbouring helpers parsing the same descriptor are scoped by the DFP type already, see for instance drm_dp_downstream_max_bpc() reading the same byte and returning 0 for a DP type DFP. amdgpu's DC parses the field only for HDMI(/DP++) detailed types as well. This is not theoretical. A Synaptics VMM7100 based USB-C to HDMI adapter with a macOS targeted firmware advertises a DisplayPort type DFP with the type byte replicated across the whole descriptor (08 08 08 08). i915 decodes that as "PCON limited to 18 Gbps FRL" and prunes every mode above ~750 MHz dotclock, including all the 4k@100/120 modes the sink EDID offers, while macOS drives 4k@120 through the same adapter just fine via DP DSC (and amdgpu's type-scoped parser would ignore the bogus field as well). Only parse the field for an HDMI DFP behind a DPCD 1.1+ branch device that reports detailed cap info, matching the type-scoped field layout of the spec and the rest of the helpers. Fixes: ce32a6239de6 ("drm/dp_helper: Add Helpers for FRL Link Training support for DP-HDMI2.1 PCON") Cc: Ankit Nautiyal Cc: Uma Shankar (v2) Cc: Jani Nikula Cc: Maarten Lankhorst Cc: dri-devel@lists.freedesktop.org Cc: # v5.12+ Signed-off-by: Alexander Kaplan Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260610193825.2933-1-alexander.kaplan@sms-medipool.de Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/display/drm_dp_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 4aaeae4fa03c3..8c858ba40c6c6 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -3457,6 +3457,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE], int bw; u8 buf; + if (!drm_dp_is_branch(dpcd)) + return 0; + + if (dpcd[DP_DPCD_REV] < 0x11) + return 0; + + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) + return 0; + + if ((port_cap[0] & DP_DS_PORT_TYPE_MASK) != DP_DS_PORT_TYPE_HDMI) + return 0; + buf = port_cap[2]; bw = buf & DP_PCON_MAX_FRL_BW; From 6cd5acf6f87c073622bd61e38fe99c47365cda9c Mon Sep 17 00:00:00 2001 From: Jose Maria Casanova Crespo Date: Mon, 27 Jul 2026 11:32:28 -0300 Subject: [PATCH 3213/3297] drm/vc4: Supply the overflow slot size in BPOS, not the whole bin BO size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6395789e4739aa5177bbec0fa0f07ccc38d249b0 upstream. vc4_overflow_mem_work() points BPOA at a 512KB slot inside the 16MB binner BO, but writes the size of the whole BO to BPOS. On every binner out-of-memory event the PTB is therefore authorized to write tile lists across all the other slots (which may hold the tile state, tile alloc and overflow memory of in-flight jobs) and, for any slot but the first, past the end of the binner BO into unrelated CMA memory. Since CMA pages are recycled into page cache and user allocations, this is arbitrary memory corruption by GPU DMA. In practice it shows up as GPU hangs with corrupted control list pointers, userspace heap corruption, a GPU that stays permanently wedged after the first hang, and occasional full system crashes, whenever a job overflows the initial binner slot. The bug dates back to the conversion from a dedicated overflow BO (where writing the full BO size was correct) to the slotted binner BO. Fixes: 553c942f8b2c ("drm/vc4: Allow using more than 256MB of CMA memory.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Jose Maria Casanova Crespo Reviewed-by: Maíra Canal Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260727-vc4-bin-oom-fixes-v2-1-0d8a5eddc7c9@igalia.com Signed-off-by: Maíra Canal Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vc4/vc4_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 69b399f3b8027..4fdad88e53e10 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -104,7 +104,7 @@ vc4_overflow_mem_work(struct work_struct *work) vc4->bin_alloc_overflow = BIT(bin_bo_slot); V3D_WRITE(V3D_BPOA, bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size); - V3D_WRITE(V3D_BPOS, bo->base.base.size); + V3D_WRITE(V3D_BPOS, vc4->bin_alloc_size); V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); spin_unlock_irqrestore(&vc4->job_lock, irqflags); From 57667eb7548faaac396c6e39f3b4444dab5b097c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Mon, 27 Jul 2026 11:32:29 -0300 Subject: [PATCH 3214/3297] drm/vc4: Zero the tile state data array before each BIN job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 48a570c964d8e37d353381e4195106277e17f5cb upstream. The binner BO is a single 16MB buffer split into 512KB slots that are handed out to jobs at submission time and recycled as jobs complete, without ever being cleared. Each slot holds the job's Tile State Data Array (TSDA) at its start, followed by the tile allocation pool. While the tile allocation pool is only walked by the render thread through branches the binner generated during the current job, the TSDA is the PTB's own per-tile bookkeeping and is consumed by the hardware itself. Although the kernel sets the "Auto-initialise Tile State Data Array" flag in the tile binning mode configuration, the PTB demonstrably still acts on stale tile state left by the slot's previous user: the binner ends up creating invalid command streams with invalid primitive streams and branches, which can cause GPU hangs as observed in [1][2]. Zero the TSDA when the job's binning slot is configured. This clears 48 bytes per tile (~24KB for a 1080p frame) in the submission path, and guarantees the PTB never sees another job's tile state. The tile count is only checked for being non-zero today, so the 8-bit fields it comes from can describe a tile state array almost six times larger than the slot it has to live in. Bound it before the slot is handed out, since such size decides how much of the slot is left for the tile alloc pool. Link: https://github.com/raspberrypi/linux/issues/3221 [1] Link: https://github.com/raspberrypi/linux/issues/5780 [2] Fixes: 553c942f8b2c ("drm/vc4: Allow using more than 256MB of CMA memory.") Cc: stable@vger.kernel.org Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260727-vc4-bin-oom-fixes-v2-2-0d8a5eddc7c9@igalia.com Signed-off-by: Maíra Canal Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vc4/vc4_validate.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 1e7bdda556984..205d360a007a2 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -384,6 +384,23 @@ validate_tile_binning_config(VALIDATE_ARGS) return -EINVAL; } + /* The tile state data array is 48 bytes per tile, and we put it at + * the start of a BO containing both it and the tile alloc. + */ + tile_state_size = 48 * tile_count; + + /* Since the tile alloc array will follow us, align. */ + tile_state_size = roundup(tile_state_size, 4096); + + /* Reject configurations whose tile state would leave no room for + * the tile alloc pool that follows it in the slot. + */ + if (tile_state_size >= vc4->bin_alloc_size) { + DRM_DEBUG("Tile binning config of %dx%d too large\n", + exec->bin_tiles_x, exec->bin_tiles_y); + return -EINVAL; + } + bin_slot = vc4_v3d_get_bin_slot(vc4); if (bin_slot < 0) { if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) { @@ -399,13 +416,13 @@ validate_tile_binning_config(VALIDATE_ARGS) exec->bin_slots |= BIT(bin_slot); bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size; - /* The tile state data array is 48 bytes per tile, and we put it at - * the start of a BO containing both it and the tile alloc. - */ - tile_state_size = 48 * tile_count; + exec->tile_alloc_offset = bin_addr + tile_state_size; - /* Since the tile alloc array will follow us, align. */ - exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096); + /* The TSDA area must be zeroed out before use, otherwise the PTB might + * consume a stale tile state. + */ + memset(vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size, 0, + tile_state_size); *(uint8_t *)(validated + 14) = ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | From da898bb6faf322345a593470ba4a20a21f62584c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 Jul 2026 21:48:36 +0300 Subject: [PATCH 3215/3297] drm/bridge: display-connector: Fix I2C adapter resource leak commit 6a39ca1286dd97ad02bb8e03bbb65ee05368d778 upstream. If the probe function returns an error after getting the I2C adapter for DDC, the reference to the adapter is never released. Fix it by releasing it in the bridge .destroy() handler. There is no need to test the ddc pointer with !IS_ERR(), as of_get_i2c_adapter_by_node() returns NULL on error. Fixes: 2e2bf3a5584d ("drm/bridge: display-connector: add DP support") Cc: stable@vger.kernel.org Signed-off-by: Laurent Pinchart Reviewed-by: Johan Hovold Reviewed-by: Luca Ceresoli Link: https://patch.msgid.link/20260717184836.2017386-1-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Luca Ceresoli Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/bridge/display-connector.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index e9f16dbc95353..9a64fefbc903c 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -40,6 +40,13 @@ static int display_connector_attach(struct drm_bridge *bridge, return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; } +static void display_connector_destroy(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + i2c_put_adapter(conn->bridge.ddc); +} + static enum drm_connector_status display_connector_detect(struct drm_bridge *bridge) { struct display_connector *conn = to_display_connector(bridge); @@ -177,6 +184,7 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs display_connector_bridge_funcs = { .attach = display_connector_attach, + .destroy = display_connector_destroy, .detect = display_connector_bridge_detect, .edid_read = display_connector_edid_read, .atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts, @@ -403,9 +411,6 @@ static void display_connector_remove(struct platform_device *pdev) regulator_disable(conn->supply); drm_bridge_remove(&conn->bridge); - - if (!IS_ERR(conn->bridge.ddc)) - i2c_put_adapter(conn->bridge.ddc); } static const struct of_device_id display_connector_match[] = { From 2a761b9be5863e1d26a584f0c2d1e114a684ed9a Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 16 Jul 2026 16:39:38 +0200 Subject: [PATCH 3216/3297] drm/panthor: reject firmware sections with oversized data commit a3caaa06809248b996254be5b47e10804a3494e2 upstream. In panthor_fw_load_section_entry(), the data size to copy is calculated without validating it against the allocated section_size: section->data.size = hdr.data.end - hdr.data.start; If a crafted firmware sets data.size larger than the allocated memory, this could cause a heap buffer overflow in panthor_fw_init_section_mem() memcpy(section->mem->kmap, section->data.buf, section->data.size); Additionally, if the section->data.size exceeds the BO size, could this memset underflow the size calculation, leading to a massive out-of-bounds zeroing of kernel memory? memset(section->mem->kmap + section->data.size, 0, panthor_kernel_bo_size(section->mem) - section->data.size); Reject section entries whose initial data is larger than the section size. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260716143939.21903-1-osama.abdelkader@gmail.com Signed-off-by: Steven Price Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/panthor/panthor_fw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index a8a5a8e260242..215e261e0e735 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -495,6 +495,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, struct panthor_fw_binary_section_entry_hdr hdr; struct panthor_fw_section *section; u32 section_size; + u32 data_size; u32 name_len; int ret; @@ -545,6 +546,13 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, return -EINVAL; } + section_size = hdr.va.end - hdr.va.start; + data_size = hdr.data.end - hdr.data.start; + if (data_size > section_size) { + drm_err(&ptdev->base, "Firmware corrupted, section data exceeds section size\n"); + return -EINVAL; + } + name_len = iter->size - iter->offset; section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL); @@ -553,7 +561,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, list_add_tail(§ion->node, &ptdev->fw->sections); section->flags = hdr.flags; - section->data.size = hdr.data.end - hdr.data.start; + section->data.size = data_size; if (section->data.size > 0) { void *data = drmm_kmalloc(&ptdev->base, section->data.size, GFP_KERNEL); @@ -576,7 +584,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, section->name = name; } - section_size = hdr.va.end - hdr.va.start; if (section_size) { u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK; struct panthor_gem_object *bo; From c835f2b0b7167584832b516c9b0a26e9180d1d0b Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Mon, 20 Jul 2026 13:49:17 +0200 Subject: [PATCH 3217/3297] drm/panthor: validate firmware interface structure sizes commit b921b8613790a3f9e78ab64017fa7149ef0b750c upstream. iface_fw_to_cpu_addr() only checks that the firmware-provided MCU virtual address points inside the shared section. The returned pointer is later used as a full firmware interface structure, so accepting an address near the end of the shared section can still lead to out-of-bounds accesses. Pass the expected object size to iface_fw_to_cpu_addr() and reject ranges that do not fit entirely in the shared section. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Steven Price Signed-off-by: Steven Price Link: https://patch.msgid.link/20260720114918.15973-1-osama.abdelkader@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/panthor/panthor_fw.c | 36 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 215e261e0e735..470e41297764b 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -812,18 +812,24 @@ static int panthor_fw_load(struct panthor_device *ptdev) * iface_fw_to_cpu_addr() - Turn an MCU address into a CPU address * @ptdev: Device. * @mcu_va: MCU address. + * @size: Size of the object pointed to by @mcu_va. * - * Return: NULL if the address is not part of the shared section, non-NULL otherwise. + * Return: NULL if the object is not part of the shared section, non-NULL otherwise. */ -static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va) +static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va, size_t size) { u64 shared_mem_start = panthor_kernel_bo_gpuva(ptdev->fw->shared_section->mem); - u64 shared_mem_end = shared_mem_start + - panthor_kernel_bo_size(ptdev->fw->shared_section->mem); - if (mcu_va < shared_mem_start || mcu_va >= shared_mem_end) + size_t shared_mem_size = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); + u64 offset; + + if (mcu_va < shared_mem_start) + return NULL; + + offset = mcu_va - shared_mem_start; + if (offset > shared_mem_size || size > shared_mem_size - offset) return NULL; - return ptdev->fw->shared_section->mem->kmap + (mcu_va - shared_mem_start); + return ptdev->fw->shared_section->mem->kmap + offset; } static int panthor_init_cs_iface(struct panthor_device *ptdev, @@ -845,8 +851,10 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev, spin_lock_init(&cs_iface->lock); cs_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va); - cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va); + cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va, + sizeof(*cs_iface->input)); + cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va, + sizeof(*cs_iface->output)); if (!cs_iface->input || !cs_iface->output) { drm_err(&ptdev->base, "Invalid stream control interface input/output VA"); @@ -896,8 +904,10 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev, spin_lock_init(&csg_iface->lock); csg_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va); - csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va); + csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va, + sizeof(*csg_iface->input)); + csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va, + sizeof(*csg_iface->output)); if (csg_iface->control->stream_num < MIN_CS_PER_CSG || csg_iface->control->stream_num > MAX_CS_PER_CSG) @@ -954,8 +964,10 @@ static int panthor_fw_init_ifaces(struct panthor_device *ptdev) return -EINVAL; } - glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va); - glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va); + glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va, + sizeof(*glb_iface->input)); + glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va, + sizeof(*glb_iface->output)); if (!glb_iface->input || !glb_iface->output) { drm_err(&ptdev->base, "Invalid global control interface input/output VA"); return -EINVAL; From 0f1ff05c58e1740367ac3c8145ccef2ff58e9751 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 22 Jul 2026 00:22:42 +0900 Subject: [PATCH 3218/3297] drm/mediatek: ovl_adaptor: balance component registrations commit 533e3469a57996905cdb95f178e7efe38c21aeb2 upstream. The OVL adaptor registers both an aggregate driver for its child devices and a component for the main DRM aggregate. Probe currently ignores an error from registering the child aggregate and leaves that aggregate registered if registering the DRM component fails. The remove callback also leaves the DRM component registered. These imbalances can leave component framework entries referring to a device whose probe failed or whose driver has been detached. The aggregate unbind callback also fails to undo component_bind_all(), leaving its child components marked as bound when the aggregate is removed. Check the aggregate registration result, unwind it when the component registration fails, and unregister the component before the aggregate on remove. Keep runtime PM enabled until both framework registrations have been removed, and unbind all child components from the aggregate unbind callback. Fixes: 453c3364632a ("drm/mediatek: Add ovl_adaptor support for MT8195") Cc: stable@vger.kernel.org # 6.4+ Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260721152242.47138-1-mhun512@gmail.com/ Signed-off-by: Chun-Kuang Hu Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c index c0af3e3b51d55..6a259872b0a91 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c @@ -625,6 +625,7 @@ static void mtk_disp_ovl_adaptor_master_unbind(struct device *dev) struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); priv->children_bound = false; + component_unbind_all(dev, priv->mmsys_dev); } static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = { @@ -651,12 +652,15 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) priv->mmsys_dev = pdev->dev.platform_data; - component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match); + ret = component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match); + if (ret) + return dev_err_probe(dev, ret, "Failed to add component master\n"); pm_runtime_enable(dev); ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops); if (ret != 0) { + component_master_del(dev, &mtk_disp_ovl_adaptor_master_ops); pm_runtime_disable(dev); return dev_err_probe(dev, ret, "Failed to add component\n"); } @@ -666,6 +670,7 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev) { + component_del(&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops); component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops); pm_runtime_disable(&pdev->dev); } From 18d21c9d04b00400054da6e21663129940637cdb Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Mon, 22 Jun 2026 10:11:22 +0200 Subject: [PATCH 3219/3297] drm/amdgpu: move debug_vm handling to amdgpu_cs_parser_fini MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d8726ef11512754a68c0ab53c57634a569b8feff upstream. The commit referenced below restarts the CS if the validation is still in progress. When debug_vm is enabled, all BOs from the CS are invalidated so we will hit an infinite loop. To avoid that, defer BO invalidation to amdgpu_cs_parser_fini. Fixes: 59720bfd8c6d ("drm/amdgpu: restart the CS if some parts of the VM are still invalidated") Signed-off-by: Pierre-Eric Pelloux-Prayer Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 8c990ee9daa295462df24982ce6878db997a380a) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 4b449b58d9073..8419473249031 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1187,19 +1187,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo); } - if (adev->debug_vm) { - /* Invalidate all BOs to test for userspace bugs */ - amdgpu_bo_list_for_each_entry(e, p->bo_list) { - struct amdgpu_bo *bo = e->bo; - - /* ignore duplicates */ - if (!bo) - continue; - - amdgpu_vm_bo_invalidate(bo, false); - } - } - return 0; } @@ -1389,6 +1376,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, /* Cleanup the parser structure */ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) { + struct amdgpu_device *adev = parser->adev; + struct amdgpu_bo_list_entry *e; unsigned int i; amdgpu_sync_free(&parser->sync); @@ -1404,8 +1393,21 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) if (parser->ctx) amdgpu_ctx_put(parser->ctx); - if (parser->bo_list) + if (parser->bo_list) { + if (adev->debug_vm) { + /* Invalidate all BOs to test for userspace bugs */ + amdgpu_bo_list_for_each_entry(e, parser->bo_list) { + struct amdgpu_bo *bo = e->bo; + + /* ignore duplicates */ + if (!bo) + continue; + + amdgpu_vm_bo_invalidate(bo, false); + } + } amdgpu_bo_list_put(parser->bo_list); + } for (i = 0; i < parser->nchunks; i++) kvfree(parser->chunks[i].kdata); From d330ac90d85f328dff61f9a11c44b35424dc3144 Mon Sep 17 00:00:00 2001 From: Candice Li Date: Tue, 21 Jul 2026 21:38:58 +0800 Subject: [PATCH 3220/3297] drm/amdgpu: restore UMD profile pstate after runtime resume commit f931c54b241ce2f36bfc34955aec43a188276b8d upstream. Runtime suspend runs GFX hw_fini and clears perfmon clock gating while the UMD profile DPM level remains set in software. Re-apply stable pstate after a successful runtime resume when a profile mode is active. Signed-off-by: Candice Li Reviewed-by: Hawking Zhang Reviewed-by: Yang Wang Signed-off-by: Alex Deucher (cherry picked from commit 138531c8850cc247aa12b104bb29ea387bcdcbb1) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 7248947835f10..99f22633abf53 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2891,6 +2891,19 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev) return 0; } +static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev) +{ + enum amd_dpm_forced_level level; + uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; + + level = amdgpu_dpm_get_performance_level(adev); + if (level & profile_mode_mask) + amdgpu_asic_update_umd_stable_pstate(adev, true); +} + static int amdgpu_pmops_runtime_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -2935,6 +2948,8 @@ static int amdgpu_pmops_runtime_resume(struct device *dev) if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX) drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; + + amdgpu_restore_umd_profile_pstate_after_runpm(adev); adev->in_runpm = false; return 0; } From 45ba7f091abf4b3118da3c9d9646f8073f115a68 Mon Sep 17 00:00:00 2001 From: Harkirat Gill Date: Mon, 27 Jul 2026 14:37:56 -0400 Subject: [PATCH 3221/3297] drm/amdgpu: cap GTT size to physical RAM on APUs commit 5e70f6804b4d6256058c360b10e044ee04ea4a4e upstream. On APUs, the GTT pool is backed by system RAM, but its size is not bound to the non-carveout memory that actually backs it. A user can end up with GTT + VRAM exceeding total physical memory through the following sequence: - Have a large non-carveout memory space (~128GB) and accordingly set a large GTT (~100GB) via the ttm module parameter. - Lower the non-carveout memory space in BIOS by increasing the UMA Frame Buffer Size (VRAM) to 64GB. - The previously set GTT value (~100GB) persists, even though the new non-carveout space (64GB) can no longer back it. This leads to a case where kernel reports GTT (100GB) + VRAM (64GB) despite the sum being greater than total physical memory (128GB). Cap the GTT size to totalram_pages() on APUs. totalram_pages() already excludes the VRAM carveout, so the resulting GTT can never exceed the system RAM that actually backs it. Signed-off-by: Harkirat Gill Reviewed-by: David Francis Assisted-by: Claude:claude-opus-4 Signed-off-by: Alex Deucher (cherry picked from commit 5dafdd649280c7dc6c22c8f877da3f54fcc441e1) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index a4b5fe35c3cda..d6d463c93fddd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2078,6 +2078,18 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) gtt_size = configured_size; } + /* Cap GTT so that it does not exceed total physical RAM. */ + if (adev->flags & AMD_IS_APU) { + u64 phys_ram = (u64)totalram_pages() << PAGE_SHIFT; + + if (gtt_size > phys_ram) { + gtt_size = phys_ram; + dev_info(adev->dev, + "Capping GTT to %uM to not exceed available system memory\n", + (unsigned int)(gtt_size / (1024 * 1024))); + } + } + /* Initialize GTT memory pool */ r = amdgpu_gtt_mgr_init(adev, gtt_size); if (r) { From 1860818feb4dbe413af2d53ddd539e2d93e97ca2 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 23 Jul 2026 23:18:33 +0800 Subject: [PATCH 3222/3297] drm/amd/pm: fix torn gpu metrics reads commit 048f4541b71fb19645fb79d6e62e6e4da23a4035 upstream. amdgpu_dpm_get_gpu_metrics() returns a pointer to the shared metrics cache after dropping adev->pm.mutex. The sysfs path then copies from that pointer. Another reader can refresh the cache in place during the copy and return a snapshot containing data from two generations. Pass caller-provided storage through the DPM interface and copy the metrics while the mutex is held. This keeps the cache pointer private and makes each sysfs read observe one complete sample. Fixes: 25c933b1c4fc ("drm/amd/powerplay: add new sysfs interface for retrieving gpu metrics(V2)") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit 862333bb48693ecafcae25af0c9d9ec31015ac77) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 12 +++++++++--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 8 +------- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 3 ++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 8253d2977408d..caf2cabeca6d0 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1442,17 +1442,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table) +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - int ret = 0; + void *table; + ssize_t ret; if (!pp_funcs->get_gpu_metrics) return 0; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle, - table); + &table); + if (ret > 0) { + ret = min_t(ssize_t, ret, size); + memcpy(buf, table, ret); + } mutex_unlock(&adev->pm.mutex); return ret; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 7c9e2003e2a04..94c534c1d0bed 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -1754,7 +1754,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - void *gpu_metrics; ssize_t size = 0; int ret; @@ -1762,15 +1761,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, if (ret) return ret; - size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics); + size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1); if (size <= 0) goto out; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, gpu_metrics, size); - out: amdgpu_pm_put_access(adev); diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index af48aead12f70..b0bbb23929bf7 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -517,7 +517,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev, char *buf); int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, long *input, uint32_t size); -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table); +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size); ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id, void *table); ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev, From 3c2ae9509717c4a140ae6217dddb76d67ce947f4 Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Fri, 3 Jul 2026 09:14:49 +0800 Subject: [PATCH 3223/3297] drm/amd/display: Increase HDMI AV mute wait from 2 to 3 frames commit c216b39fbbc4b007fd6984cffd85039d49a55154 upstream. Some HDMI sinks need additional GCP packets to properly process the mute state before the timing generator is disabled, especially after link re-establishment with HDMI 2.0 scrambling enabled. Waiting for only 2 frames is insufficient for certain monitor firmware, resulting in garbled display output on resume from suspend. Increase the AV mute wait in dcn30_set_avmute() from 2 to 3 frames to ensure the sink receives enough GCP packets. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5167 Assisted-by: Cursor:Claude-Opus-4.6 Reviewed-by: Wayne Lin Signed-off-by: Ray Wu Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 0c0d5174b09640d8b560764aa5a177630e076e93) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index 731645a2ab9aa..20b408b688248 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -837,13 +837,19 @@ void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) pipe_ctx->stream_res.stream_enc, enable); - /* Wait for two frame to make sure AV mute is sent out */ + /* Wait for three frames to make sure AV mute is sent out. + * Some HDMI sinks need additional GCP packets to properly + * process the mute state, especially after link re-establishment + * with HDMI 2.0 scrambling enabled. + */ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + int i; + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + for (i = 0; i < 3; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + } } } } From 02647d98340738f918690ed227fdcf154db1b84a Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Thu, 23 Jul 2026 06:25:48 +0200 Subject: [PATCH 3224/3297] drm/amd/display: use proper context for logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 114b42507b6a23d9d24e24e4ef165233332c64d4 upstream. The same as the rest of the code, get_ss_info_from_atombios() uses calc_pll_cs->ctx->logger for logging. But calc_pll_cs->ctx is initialized only later in calc_pll_max_vco_construct(). Therefore, any output using DC_LOG_SYNC() leads to a NULL pointer deference in get_ss_info_from_atombios(). According to Sashiko, the very same problem exists in dce112_get_pix_clk_dividers() and dcn3_get_pix_clk_dividers() too. To avoid accessing the NULL context, use clk_src->base.ctx->logger everywhere. That context in base is initialized earlier in dce110_clk_src_construct() and dce112_clk_src_construct(). Before get_ss_info_from_atombios() or Sashiko's get_pix_clk_dividers functions above are actually called. This is done by redefining DC_LOGGER to CTX->logger. Before: dce110_clk_src_construct() did: -> sets clk_src->base.ctx = ctx; -> ss_info_from_atombios_create() -> get_ss_info_from_atombios() <- uses calc_pll_cs->ctx # BOOM -> calc_pll_max_vco_construct() <- sets calc_pll_cs->ctx After: dce110_clk_src_construct() does: -> sets clk_src->base.ctx = ctx; -> ss_info_from_atombios_create() -> get_ss_info_from_atombios() <- uses clk_src->base.ctx Closes: https://bugzilla.suse.com/show_bug.cgi?id=1271175 Closes: https://lore.kernel.org/all/a9ee54e6-2413-4156-9bde-d528ae3c63a3@kernel.org/ Fixes: 1296423bf23c ("drm/amd/display: define DC_LOGGER for logger") Reviewed-by: Bhawanpreet Lakha Signed-off-by: Jiri Slaby (SUSE) Cc: Lakha, Bhawanpreet Cc: Harry Wentland Cc: Leo Li Cc: Rodrigo Siqueira Cc: Alex Deucher Cc: "Christian König" Cc: David Airlie Cc: Simona Vetter Cc: amd-gfx@lists.freedesktop.org Signed-off-by: Alex Deucher (cherry picked from commit 6f16fcbb0c46a87e3d9685407e906573d60104b0) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- .../drm/amd/display/dc/dce/dce_clock_source.c | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index b4f5b4a6331a1..20c52b0038d10 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -45,9 +45,7 @@ clk_src->base.ctx #define DC_LOGGER \ - calc_pll_cs->ctx->logger -#define DC_LOGGER_INIT() \ - struct calc_pll_clock_source *calc_pll_cs = &clk_src->calc_pll + CTX->logger #undef FN #define FN(reg_name, field_name) \ @@ -291,6 +289,7 @@ static bool calc_pll_dividers_in_range( } static uint32_t calculate_pixel_clock_pll_dividers( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct pll_settings *pll_settings) { @@ -479,7 +478,7 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( { uint32_t field = 0; uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); + /* Check if reference clock is external (not pcie/xtalin) * HW Dce80 spec: * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB @@ -522,12 +521,14 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( /*Calculate Dividers by HDMI object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll_hdmi, pll_settings); else /*Calculate Dividers by default object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll, pll_settings); @@ -572,7 +573,6 @@ static uint32_t dce110_get_pix_clk_dividers( { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -604,7 +604,6 @@ static uint32_t dce112_get_pix_clk_dividers( struct pll_settings *pll_settings) { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -1370,8 +1369,6 @@ static uint32_t dcn3_get_pix_clk_dividers( unsigned long long actual_pix_clk_100Hz = pix_clk_params ? pix_clk_params->requested_pix_clk_100hz : 0; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); - if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { DC_LOG_ERROR( @@ -1441,7 +1438,6 @@ static const struct clock_source_funcs dce110_clk_src_funcs = { .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz }; - static void get_ss_info_from_atombios( struct dce110_clk_src *clk_src, enum as_signal_type as_signal, @@ -1454,7 +1450,7 @@ static void get_ss_info_from_atombios( struct spread_spectrum_info *ss_info_cur; struct spread_spectrum_data *ss_data_cur; uint32_t i; - DC_LOGGER_INIT(); + if (ss_entries_num == NULL) { DC_LOG_SYNC( "Invalid entry !!!\n"); @@ -1589,6 +1585,7 @@ static void ss_info_from_atombios_create( } static bool calc_pll_max_vco_construct( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct calc_pll_clock_source_init_data *init_data) { @@ -1740,6 +1737,7 @@ bool dce110_clk_src_construct( ss_info_from_atombios_create(clk_src); if (!calc_pll_max_vco_construct( + clk_src, &clk_src->calc_pll, &calc_pll_cs_init_data)) { ASSERT_CRITICAL(false); @@ -1754,7 +1752,7 @@ bool dce110_clk_src_construct( if (!calc_pll_max_vco_construct( - &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + clk_src, &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { ASSERT_CRITICAL(false); goto unexpected_failure; } From 9e52212aff8ed1fd9087728f61243ce3efd9d0ac Mon Sep 17 00:00:00 2001 From: Gang Ba Date: Tue, 14 Jul 2026 15:08:57 -0400 Subject: [PATCH 3225/3297] drm/amdkfd: Fix missing authorization check in KFD_IOC_DBG_TRAP_DISABLE commit 99b2fe4f19e3be0a8d0a0b5ea98d855970889653 upstream. Prevent unauthorized termination of active GPU debug sessions. Previously, users with /dev/kfd access could terminate another process's debug session without proper ownership or ptrace authorization. Signed-off-by: Gang Ba Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 4db4c5ffd5585b72622ecf6ffedf2da258ee23f5) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index c4fd83ae17620..d3190c3bb1aee 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -2971,10 +2971,14 @@ static int kfd_ioctl_set_debug_trap(struct file *filep, struct kfd_process *p, v goto out; } - /* Check if target is still PTRACED. */ + /* + * Verify debugger has permission to debug target process. + * For cross-process debugging, require active ptrace relationship. + * This applies to ALL operations to prevent unauthorized interference. + */ rcu_read_lock(); - if (target != p && args->op != KFD_IOC_DBG_TRAP_DISABLE - && ptrace_parent(target->lead_thread) != current) { + if (target != p && ptrace_parent(target->lead_thread) != current + && target->debugger_process != p) { pr_err("PID %i is not PTRACED and cannot be debugged\n", args->pid); r = -EPERM; } From 5f0f2ddeac738e2ca9d12cb76a1ff2904e85ecc3 Mon Sep 17 00:00:00 2001 From: Vladimir Marioukhine Date: Mon, 20 Jul 2026 11:53:30 -0400 Subject: [PATCH 3226/3297] drm/amdkfd: fix QID bit leak in pqm_create_queue() commit 38b73293f38658a4685ffcea666462024f858ad9 upstream. When MES is enabled and amdgpu_amdkfd_alloc_kernel_mem() fails during the first queue creation for a process, pqm_create_queue() returns early via 'return retval' without going through the err_create_queue cleanup label. This means clear_bit(*qid, pqm->queue_slot_bitmap) is never called, leaving the reserved QID bit permanently set in queue_slot_bitmap. Over time this leaks QID slots, potentially exhausting all available queue slots. Fix this by replacing 'return retval' with 'goto err_allocate_pqn' so that clear_bit() is always called on the error path without touching the uninitialized pqn pointer. AILIKFD-813 Reported-by: Deucher, Alexander Signed-off-by: Vladimir Marioukhine Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit a107f74c38edbb80d6ab64dcaeeb292c14e9779f) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 0bfe64cdabe6a..e96488f6b02d2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -375,7 +375,7 @@ int pqm_create_queue(struct process_queue_manager *pqm, false); if (retval) { dev_err(dev->adev->dev, "failed to allocate process context bo\n"); - return retval; + goto err_allocate_pqn; } memset(pdd->proc_ctx_cpu_ptr, 0, AMDGPU_MES_PROC_CTX_SIZE); } From 6dc0b4b39ed4f11ef70f76ecea8537e35f45342b Mon Sep 17 00:00:00 2001 From: William Palacek Date: Mon, 20 Jul 2026 12:51:34 -0400 Subject: [PATCH 3227/3297] drm/amdkfd: fix uint32_t overflow in EOP ring buffer size alignment commit 83463a96ea3c7d8ae636a4d6a0ba63c9ce410724 upstream. eop_ring_buffer_size in struct queue_properties is a u32. In kfd_queue_acquire_buffers() the expected EOP buffer size is computed as ALIGN(eop_ring_buffer_size, PAGE_SIZE); ALIGN uses typeof(x), so the addition is done in 32-bit. A user-supplied size of 0xFFFFF001 wraps to 0, causing kfd_queue_buffer_get() to skip its exact-size check (gated on size != 0) and accept any BO mapped at the address. On GFX8/GFX9 the MQD cp_hqd_eop_control is then programmed for an 8KB EOP ring backed by a 4KB BO, so CP EOP writes can land past the buffer and fault the GPU. Cast the operand to u64 so the alignment is computed in 64-bit; the size check in kfd_queue_buffer_get() then rejects the oversized request. Fixes: 42ea9cf2f16b ("drm/amdkfd: Relax size checking during queue buffer get") Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ae443117b742c357bfef3a7bddabf76fcf86e9ef) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index 8e74e2bae5902..6f06516da32a8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -285,7 +285,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope } err = kfd_queue_buffer_get(vm, (void *)properties->eop_ring_buffer_address, &properties->eop_buf_bo, - ALIGN(properties->eop_ring_buffer_size, PAGE_SIZE)); + ALIGN((u64)properties->eop_ring_buffer_size, PAGE_SIZE)); if (err) goto out_err_unreserve; } From 6189ceca5ce75078743db456e133efbfa2dc6fce Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 21 Jul 2026 09:30:07 -0400 Subject: [PATCH 3228/3297] drm/amdkfd: Handle invalid event type in CRIU event restore commit a9cdc85839e4fe2c760aa4ca6cc341c31ad1918a upstream. In kfd_criu_restore_event, there was no handling for the event priv data having an invalid event type. The priv data here is untrusted and can be invalid. In that case, fail with EINVAL. Signed-off-by: David Francis Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 2e8e9963cd5c41aa14fd5316bf9ec92e7a0e3097) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 2fdee34ae5432..e869aff6e554a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -521,6 +521,9 @@ int kfd_criu_restore_event(struct file *devkfd, ret = create_other_event(p, ev, &ev_priv->event_id); break; + default: + ret = -EINVAL; + break; } mutex_unlock(&p->event_mutex); From 6a52f48157fa7fb81e0c146937fd6c8b0c1cfdbd Mon Sep 17 00:00:00 2001 From: William Palacek Date: Wed, 22 Jul 2026 11:20:56 -0400 Subject: [PATCH 3229/3297] drm/amdkfd: hold event_mutex while checkpointing CRIU events commit ff8bc5a68a9a70bdc38d61a72c7a49c56063f9d2 upstream. kfd_criu_checkpoint_events() counts the entries in p->event_idr via kfd_get_num_events(), allocates an array sized to that count, and then walks the same IDR to fill it. Neither the count nor the walk holds p->event_mutex. The CRIU checkpoint caller holds only p->mutex. Event create and destroy (kfd_event_create()/kfd_event_destroy()) take p->event_mutex and do not take p->mutex, so a second thread in the same process can insert or remove events between the count and the walk. If an event is inserted, the walk iterates more entries than were counted and writes past the end of the ev_privs allocation; if an event is removed, the walk dereferences an entry that is being freed. Hold p->event_mutex across the count and the walk so both observe a consistent view of p->event_idr. The lock is released before copy_to_user(), which only touches the local buffer. The caller already holds p->mutex and the create/destroy paths never take p->mutex, so the p->mutex -> p->event_mutex order is not inverted and no deadlock is introduced. Fixes: 40e8a766a761 ("drm/amdkfd: CRIU checkpoint and restore events") Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ff57e223ab105795b05d3ef3f3c35a5a441bcbaa) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index e869aff6e554a..b033c3a9ce545 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -545,15 +545,27 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, int ret = 0; struct kfd_event *ev; uint32_t ev_id; + uint32_t num_events; - uint32_t num_events = kfd_get_num_events(p); + /* Serialize the count and the walk below against concurrent event + * create/destroy. Those paths take only p->event_mutex, not the + * p->mutex held by the CRIU checkpoint caller, so without this the + * event_idr can grow between kfd_get_num_events() and the loop and the + * walk writes past the ev_privs allocation. + */ + mutex_lock(&p->event_mutex); - if (!num_events) + num_events = kfd_get_num_events(p); + if (!num_events) { + mutex_unlock(&p->event_mutex); return 0; + } ev_privs = kvzalloc(num_events * sizeof(*ev_privs), GFP_KERNEL); - if (!ev_privs) + if (!ev_privs) { + mutex_unlock(&p->event_mutex); return -ENOMEM; + } idr_for_each_entry(&p->event_idr, ev, ev_id) { @@ -594,6 +606,8 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, i++; } + mutex_unlock(&p->event_mutex); + ret = copy_to_user(user_priv_data + *priv_data_offset, ev_privs, num_events * sizeof(*ev_privs)); if (ret) { From 282f261cb035e5f01a486f76d356b7e9dbfba73f Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:22 -0400 Subject: [PATCH 3230/3297] drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size commit 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa upstream. Two sites in vmwgfx_resource.c assign boolean literals to res->guest_memory_size, which is an unsigned long allocation-size field; the intended target is the adjacent res->guest_memory_dirty bitfield. After the assignments the field holds 0 or 1 instead of the resource's MOB allocation size: - vmw_resource_release() writes 0 (false), and - vmw_resource_unbind_list() writes 1 (true). Subsequent revalidation paths read guest_memory_size when computing the dirty page range (vmw_bo_dirty_transfer_to_res()) and the buffer allocation size (vmw_resource_buf_alloc()), producing zero-length walks or wrap-around ranges that read or write past the MOB bitmap. The dirty-tracking intent of the original code (mark the resource as dirtied since the last sync) is also lost, since guest_memory_dirty is never updated. Rename both assignments to guest_memory_dirty. Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-2-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 3880116969417..e3a187a2c7a1c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -136,7 +136,7 @@ static void vmw_resource_release(struct kref *kref) val_buf.num_shared = 0; res->func->unbind(res, false, &val_buf); } - res->guest_memory_size = false; + res->guest_memory_dirty = false; vmw_resource_mob_detach(res); if (res->dirty) res->func->dirty_free(res); @@ -773,7 +773,7 @@ void vmw_resource_unbind_list(struct vmw_bo *vbo) if (!WARN_ON_ONCE(!res->func->unbind)) (void) res->func->unbind(res, res->res_dirty, &val_buf); - res->guest_memory_size = true; + res->guest_memory_dirty = true; res->res_dirty = false; vmw_resource_mob_detach(res); } From e479240a1e076ba1104236331abd62400bf1d495 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:23 -0400 Subject: [PATCH 3231/3297] drm/vmwgfx: reject DX_BIND_QUERY without a DX context commit 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db upstream. vmw_cmd_dx_bind_query() unconditionally dereferences sw_context->dx_ctx_node->ctx. Userspace can trigger a NULL pointer dereference from any render-node fd by submitting an execbuf with dx_context_handle == SVGA3D_INVALID_ID and a SVGA_3D_CMD_DX_BIND_QUERY opcode in the command stream: dx_ctx_node is left NULL and the kernel oopses on the assignment. The same NULL is then re-read in vmw_resources_reserve() via vmw_context_get_dx_query_mob(). All sibling DX handlers fail-close on a missing dx_ctx_node using VMW_GET_CTX_NODE(). Use the same pattern here, returning -EINVAL up front before any relocation state is published. Fixes: 9c079b8ce8bf ("drm/vmwgfx: Adapt execbuf to the new validation api") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-3-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index e1f18020170ab..b07f052474d02 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1272,9 +1272,13 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, SVGA3dCmdHeader *header) { VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindQuery); + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); struct vmw_bo *vmw_bo; int ret; + if (!ctx_node) + return -EINVAL; + cmd = container_of(header, typeof(*cmd), header); /* @@ -1288,7 +1292,7 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, return ret; sw_context->dx_query_mob = vmw_bo; - sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx; + sw_context->dx_query_ctx = ctx_node->ctx; return 0; } From 10460699c312e7a6c213478310b5ad36539b5f48 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:24 -0400 Subject: [PATCH 3232/3297] drm/vmwgfx: clamp dirty-page range with min, not max commit f47d542d5912f236273399d7139b522f6950e2e4 upstream. vmw_bo_dirty_transfer_to_res() and vmw_bo_dirty_clear() compute the intersection of a resource's page range with the BO's tracked dirty range, but clamp res_end against dirty->end with max() instead of min(). When dirty->end exceeds the resource end, the loop walks past the resource's pages, calls vmw_resource_dirty_update() for ranges owned by other resources sharing the same backing MOB and clears their pending dirty bits via bitmap_clear(). The result is silent loss of writeback for unrelated resources whenever two resources share a MOB. Use min() in both functions so the loop is bounded to the intersection of the resource and dirty ranges. Fixes: b7468b15d271 ("drm/vmwgfx: Implement an infrastructure for write-coherent resources") Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-4-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c index 45561bc1c9eff..8ab88f3886525 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c @@ -311,7 +311,7 @@ void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res) return; cur = max(res_start, dirty->start); - res_end = max(res_end, dirty->end); + res_end = min(res_end, dirty->end); while (cur < res_end) { unsigned long num; @@ -347,7 +347,7 @@ void vmw_bo_dirty_clear(struct vmw_bo *vbo) return; cur = max(res_start, dirty->start); - res_end = max(res_end, dirty->end); + res_end = min(res_end, dirty->end); while (cur < res_end) { unsigned long num; From b79e82ea18236a4be641d53f1b7493fd44e75be8 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:25 -0400 Subject: [PATCH 3233/3297] drm/vmwgfx: take fman->lock around fence list mutation in fifo_down commit 250af2e8c3e90dc978e062a936b633870a22e660 upstream. vmw_fence_fifo_down() drops fman->lock to wait on a fence and, on timeout, mutates fman->fence_list via list_del_init() and signals the fence without re-acquiring the lock. __vmw_fences_update() walks and removes entries from the same list under fman->lock from any other waiter, the fence-IRQ thread, or vmw_fences_update(), so the unlocked list_del_init() can corrupt the list head. Re-take fman->lock before manipulating fence->head and use dma_fence_signal_locked(). Wrap the locked signalling in dma_fence_begin_signalling() / dma_fence_end_signalling() so the lockdep annotation that dma_fence_signal() previously provided is preserved (the same pattern as __vmw_fences_update()). dma_fence_put() is moved outside the lock to avoid a recursive acquire from vmw_fence_obj_destroy(), which also takes fman->lock. Fixes: ae2a104058e2 ("vmwgfx: Implement fence objects") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-5-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 00be92da55097..fe59a613e1d05 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -366,13 +366,24 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman) ret = vmw_fence_obj_wait(fence, false, false, VMW_FENCE_WAIT_TIMEOUT); + spin_lock(&fman->lock); if (unlikely(ret != 0)) { + bool cookie = dma_fence_begin_signalling(); + list_del_init(&fence->head); - dma_fence_signal(&fence->base); + if (fence->waiter_added) { + vmw_seqno_waiter_remove(fman->dev_priv); + fence->waiter_added = false; + } + dma_fence_signal_locked(&fence->base); + dma_fence_end_signalling(cookie); } BUG_ON(!list_empty(&fence->head)); + spin_unlock(&fman->lock); + dma_fence_put(&fence->base); + spin_lock(&fman->lock); } spin_unlock(&fman->lock); From a8434b145b1e467940334c58c00af241e9494c5f Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:26 -0400 Subject: [PATCH 3234/3297] drm/vmwgfx: drop dma_buf reference on foreign-fd prime import commit f739416dc555fa205a785e5135d73fa39b26f35d upstream. ttm_prime_fd_to_handle() returns -ENOSYS when the imported fd's dma_buf->ops do not match the ttm_object_device's ops, but does so without releasing the reference acquired by dma_buf_get(). Any unprivileged renderD client passing a non-vmwgfx prime fd through the DRM_VMW_GB_SURFACE_REF{,_EXT} path leaks one dma_buf reference per call and indefinitely pins the foreign exporter's GEM resources. Funnel the error path through the existing dma_buf_put() so the reference is always dropped. Fixes: 65981f7681ab ("drm/ttm: Add a minimal prime implementation for ttm base objects") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-6-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/ttm_object.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c index 36d46b79562a3..95f7c5820aab8 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_object.c +++ b/drivers/gpu/drm/vmwgfx/ttm_object.c @@ -547,14 +547,17 @@ int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, if (IS_ERR(dma_buf)) return PTR_ERR(dma_buf); - if (dma_buf->ops != &tdev->ops) - return -ENOSYS; + if (dma_buf->ops != &tdev->ops) { + ret = -ENOSYS; + goto out; + } prime = (struct ttm_prime_object *) dma_buf->priv; base = &prime->base; *handle = base->handle; ret = ttm_ref_object_add(tfile, base, NULL, false); +out: dma_buf_put(dma_buf); return ret; From dc0be7662b7b0ce28cb5eea864737793ed7b9e70 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:27 -0400 Subject: [PATCH 3235/3297] drm/vmwgfx: validate DRAW_PRIMITIVES header size before division commit 85891d174707d8bddcec7a888fb4e1d17def34f3 upstream. vmw_cmd_draw() computes maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); where header->size is u32 and is taken straight from the user-supplied command stream. When header->size is less than sizeof(cmd->body) the unsigned subtraction wraps to nearly 4 GiB, producing a huge maxnum. Any user-controlled cmd->body.numVertexDecls then passes the bound and the loop dereferences decl[i] far past the end of the kernel command bounce buffer, producing an out-of-bounds read of kernel memory. Reject undersized headers up front. Fixes: 7a73ba7469cb ("drm/vmwgfx: Use TTM handles instead of SIDs as user-space surface handles.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-7-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index b07f052474d02..2410d53a75aa5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1571,11 +1571,17 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv, uint32_t maxnum; int ret; + cmd = container_of(header, typeof(*cmd), header); + + if (unlikely(header->size < sizeof(cmd->body))) { + VMW_DEBUG_USER("Illegal DRAW_PRIMITIVES header size.\n"); + return -EINVAL; + } + ret = vmw_cmd_cid_check(dev_priv, sw_context, header); if (unlikely(ret != 0)) return ret; - cmd = container_of(header, typeof(*cmd), header); maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); if (unlikely(cmd->body.numVertexDecls > maxnum)) { From 7e40e6120fb232a10b543ffd994e5c6d8f3a2cc6 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:28 -0400 Subject: [PATCH 3236/3297] drm/vmwgfx: bound DMA command body size against suffix pointer commit f4f1db96bfd68b81053693ba53405b6f510ac16c upstream. vmw_cmd_dma() locates the DMA suffix at (unsigned long) &cmd->body + header->size - sizeof(*suffix) without checking that header->size is large enough to contain both cmd->body and the suffix. An undersized header makes the suffix pointer underflow back into the previous command in the bounce buffer. The verifier later writes suffix->maximumOffset, clobbering verified fields of an already-relocated earlier command -- a TOCTOU on the device-visible command stream that lets one command rewrite another's GMR id, surface id, or other authenticated fields. Reject the command if the body is too small for the suffix to fit. Fixes: 4e4ddd477743 ("drm/vmwgfx: Fix queries if no dma buffer thrashing is occuring.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-8-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 2410d53a75aa5..a9136a6523cba 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1510,6 +1510,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, bool dirty; cmd = container_of(header, typeof(*cmd), header); + + if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) { + VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n"); + return -EINVAL; + } + suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body + header->size - sizeof(*suffix)); From 96efee36453b697ccbaf75091b7a1807c11809dd Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:29 -0400 Subject: [PATCH 3237/3297] drm/vmwgfx: avoid destroy_workqueue(NULL) on vkms init failure commit 05eaa887e7b4f40fba425f8a1d7a5a8a043092a6 upstream. Two paths through vmw_vkms_init() can leave vmw->crc_workq NULL while still leaving the rest of the driver in a state that calls vmw_vkms_cleanup() at module unload: 1. vmw_host_get_guestinfo(GUESTINFO_VBLANK, ...) failing or returning an oversized buffer -- the common case on hosts without a VBLANK guestinfo entry -- early-returned before the workqueue allocation. 2. alloc_ordered_workqueue() returning NULL on memory pressure. vmw_vkms_cleanup() then calls destroy_workqueue(NULL), which dereferences wq->name and panics. Fix the first case by removing the early return: vmw->vkms_enabled is already false on the rpci-failure path so no work will ever be queued, and allocating the workqueue unconditionally keeps the control flow simple. Fix the second case by guarding the cleanup with a NULL check, since alloc_ordered_workqueue() can still fail under low memory. Fixes: 7b0062036c3b ("drm/vmwgfx: Implement virtual crc generation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-9-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c index aec774fa4d7bf..87de8e43667db 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c @@ -214,14 +214,14 @@ vmw_vkms_init(struct vmw_private *vmw) vmw->vkms_enabled = false; ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len); - if (ret || buf_len > max_buf_len) - return; - buffer[buf_len] = '\0'; + if (!ret && buf_len <= max_buf_len) { + buffer[buf_len] = '\0'; - ret = kstrtobool(buffer, &vmw->vkms_enabled); - if (!ret && vmw->vkms_enabled) { - ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS); - vmw->vkms_enabled = (ret == 0); + ret = kstrtobool(buffer, &vmw->vkms_enabled); + if (!ret && vmw->vkms_enabled) { + ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS); + vmw->vkms_enabled = (ret == 0); + } } vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0); @@ -236,7 +236,8 @@ vmw_vkms_init(struct vmw_private *vmw) void vmw_vkms_cleanup(struct vmw_private *vmw) { - destroy_workqueue(vmw->crc_workq); + if (vmw->crc_workq) + destroy_workqueue(vmw->crc_workq); } bool From 1eb4f796695be39e0b5c4383350033ced9ade703 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:30 -0400 Subject: [PATCH 3238/3297] drm/vmwgfx: enforce cursor size limits for MOB cursors commit d5ed8749168ad13c0dbaa8300f68d854b6076966 upstream. vmw_cursor_plane_atomic_check() bounds cursor width and height only on the legacy update path; the SVGA_CAP2_CURSOR_MOB path -- the default on modern hosts -- accepts any size. When the requested size exceeds SVGA_REG_CURSOR_MAX_DIMENSION or SVGA_REG_MOB_MAX_SIZE, vmw_cursor_mob_get() returns -EINVAL and leaves vps->cursor.mob NULL. Its return value is then discarded in vmw_cursor_plane_prepare_fb(), so the subsequent vmw_cursor_update_mob() calls vmw_bo_map_and_cache(NULL) and oopses inside vmw_bo_map_and_cache_size() on the tbo.base.size load. Reachable from any DRM master via DRM_IOCTL_MODE_CURSOR2 with a sufficiently large width or height (e.g. cursor_max_dim + 1). Reject oversized cursors in atomic_check for both MOB-backed cursor update types. The MOB byte-size limit only applies to the SVGA_CAP2_CURSOR_MOB path (vmw_cursor_mob_size() returns 0 for GB_ONLY); compute the required MOB size in 64-bit to avoid overflow when very large dimensions are requested. In prepare_fb only call vmw_cursor_mob_get()/_map() for VMW_CURSOR_UPDATE_MOB -- the GB_ONLY path uses bo->map.virtual directly and would otherwise be silently downgraded to NONE on hosts without SVGA_CAP2_CURSOR_MOB (where vmw_cursor_mob_get() always returns -EINVAL). Degrade the update to NONE if vmw_cursor_mob_get() or vmw_cursor_mob_map() fails so the update path does not run with a NULL backing MOB. Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-10-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c | 49 ++++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c index c46f17ba7236d..c53bb9376b366 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c @@ -432,6 +432,7 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps) u32 size = vmw_cursor_mob_size(vps->cursor.update_type, vps->base.crtc_w, vps->base.crtc_h); struct vmw_bo *vbo = vps->cursor.mob; + void *map; if (!vbo) return -EINVAL; @@ -446,11 +447,15 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps) if (unlikely(ret != 0)) return -ENOMEM; - vmw_bo_map_and_cache(vbo); + map = vmw_bo_map_and_cache(vbo); + if (!map) { + vmw_bo_unmap(vbo); + ret = -ENOMEM; + } ttm_bo_unreserve(&vbo->tbo); - return 0; + return ret; } /** @@ -663,9 +668,15 @@ int vmw_cursor_plane_prepare_fb(struct drm_plane *plane, !vmw_cursor_buffer_changed(vps, old_vps)) { vps->cursor.update_type = VMW_CURSOR_UPDATE_NONE; - } else { - vmw_cursor_mob_get(vcp, vps); - vmw_cursor_mob_map(vps); + } else if (vps->cursor.update_type == + VMW_CURSOR_UPDATE_MOB && + (vmw_cursor_mob_get(vcp, vps) || + vmw_cursor_mob_map(vps))) { + /* + * Reset the cursor to avoid crashes later. + */ + vps->cursor.update_type = + VMW_CURSOR_UPDATE_NONE; } } } @@ -732,6 +743,34 @@ int vmw_cursor_plane_atomic_check(struct drm_plane *plane, "surface not suitable for cursor\n"); return -EINVAL; } + } else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY || + update_type == VMW_CURSOR_UPDATE_MOB) { + u32 cursor_max_dim = + vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION); + + if (new_state->crtc_w > cursor_max_dim || + new_state->crtc_h > cursor_max_dim) { + drm_warn(&vmw->drm, + "Cursor dimensions (%d, %d) exceed device max %u\n", + new_state->crtc_w, new_state->crtc_h, + cursor_max_dim); + return -EINVAL; + } + + if (update_type == VMW_CURSOR_UPDATE_MOB) { + u32 mob_max_size = + vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE); + u64 mob_size = (u64)new_state->crtc_w * + new_state->crtc_h * sizeof(u32) + + sizeof(SVGAGBCursorHeader); + + if (mob_size > mob_max_size) { + drm_warn(&vmw->drm, + "Cursor MOB size %llu exceeds device max %u\n", + mob_size, mob_max_size); + return -EINVAL; + } + } } return 0; From cfd163169af3be56eaef113c680ea251f0d09189 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:32 -0400 Subject: [PATCH 3239/3297] drm/vmwgfx: use check_add_overflow for shader size+offset bound commit 54d56d5b42d2e4c72ba6e365e9774da90698aa22 upstream. vmw_shader_define() validates the user-supplied shader window against its backing buffer with (u64)buffer->tbo.base.size < (u64)size + (u64)offset drm_vmw_shader_create_arg::offset is __u64 in the uapi; when it is near U64_MAX the unsigned addition wraps and the resulting tiny value passes the check. The unbounded offset is then stored in res->guest_memory_offset and forwarded to host SVGA shader-create commands. Use check_add_overflow() to detect the wrap and compare the resulting endpoint against the buffer size. Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-12-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index a8c8c9375d297..bc89a137ab6d9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -25,6 +25,8 @@ * **************************************************************************/ +#include + #include #include "vmwgfx_binding.h" @@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, static int vmw_user_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type, uint8_t num_input_sig, uint8_t num_output_sig, @@ -739,7 +741,7 @@ static int vmw_user_shader_alloc(struct vmw_private *dev_priv, static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type) { struct vmw_shader *shader; @@ -768,7 +770,7 @@ static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, enum drm_vmw_shader_type shader_type_drm, - u32 buffer_handle, size_t size, size_t offset, + u32 buffer_handle, size_t size, u64 offset, uint8_t num_input_sig, uint8_t num_output_sig, uint32_t *shader_handle) { @@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, int ret; if (buffer_handle != SVGA3D_INVALID_ID) { + u64 end; + ret = vmw_user_bo_lookup(file_priv, buffer_handle, &buffer); if (unlikely(ret != 0)) { VMW_DEBUG_USER("Couldn't find buffer for shader creation.\n"); return ret; } - if ((u64)buffer->tbo.base.size < (u64)size + (u64)offset) { + if (check_add_overflow((u64)size, (u64)offset, &end) || + end > buffer->tbo.base.size) { VMW_DEBUG_USER("Illegal buffer- or shader size.\n"); ret = -EINVAL; goto out_bad_arg; From 042ca38779554687fc32b66a28328e0d9a36c58f Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:33 -0400 Subject: [PATCH 3240/3297] drm/vmwgfx: validate external BO copy bounds for both stride paths commit 706c93c5813caabbb0d0a576c017d15aeec2c113 upstream. vmw_external_bo_copy() trusts caller-supplied offsets, strides, and heights and operates on imported dma-buf vmaps: - The equal-stride memcpy() bound was clamped after subtracting the offsets from dst_size and src_size; an offset larger than the BO size wraps the unsigned subtraction to a huge value and the resulting memcpy() runs off the end of the vmap. dst_stride * height is also a u32 multiplication that can overflow. - The non-equal-stride row-by-row path had no bound at all. The loop touches bytes through offset + (height - 1) * stride + width_in_bytes, with only a WARN_ON(dst_stride < width_in_bytes), and could likewise step past the end of either mapping. The offsets and strides are derived from STDU/SOU plane state, so a configured CRTC submitting a crafted atomic commit on an imported framebuffer can reach this path. Validate the exact row-copy endpoint against each BO's size up front using check_mul_overflow() and check_add_overflow(). Use the bulk memcpy() path only when width_in_bytes covers the whole stride; otherwise copy one row at a time so partial-row updates near the bottom of a framebuffer remain valid. Also reject zero strides and stride < width_in_bytes, both of which the row-by-row path cannot represent safely. Fixes: 50f119925091 ("drm/vmwgfx: Fix prime with external buffers") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-13-zack.rusin@broadcom.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_blit.c | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c index fa5841fda659d..d78874e7f5c76 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c @@ -30,6 +30,7 @@ #include "vmwgfx_bo.h" #include +#include /* * Template that implements find_first_diff() for a generic @@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, container_of(dst->tbo.bdev, struct vmw_private, bdev); size_t dst_size = dst->tbo.resource->size; size_t src_size = src->tbo.resource->size; + size_t dst_end, src_end; struct iosys_map dst_map = {0}; struct iosys_map src_map = {0}; + bool dst_mapped = false; + bool src_mapped = false; int ret, i; int x_in_bytes; u8 *vsrc; u8 *vdst; + if (!height || !width_in_bytes) + return 0; + + if (!dst_stride || !src_stride) + return -EINVAL; + if (dst_stride < width_in_bytes || src_stride < width_in_bytes) + return -EINVAL; + if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, &dst_end) || + check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) || + check_add_overflow((size_t)dst_offset, dst_end, &dst_end) || + dst_end > dst_size || + check_mul_overflow((size_t)src_stride, (size_t)height - 1, &src_end) || + check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) || + check_add_overflow((size_t)src_offset, src_end, &src_end) || + src_end > src_size) { + drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n"); + return -EINVAL; + } + vsrc = map_external(src, &src_map); if (!vsrc) { drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n"); ret = -ENOMEM; goto out; } + src_mapped = true; vdst = map_external(dst, &dst_map); if (!vdst) { @@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, ret = -ENOMEM; goto out; } + dst_mapped = true; vsrc += src_offset; vdst += dst_offset; - if (src_stride == dst_stride) { - dst_size -= dst_offset; - src_size -= src_offset; - memcpy(vdst, vsrc, - min(dst_stride * height, min(dst_size, src_size))); + if (src_stride == dst_stride && width_in_bytes == dst_stride) { + memcpy(vdst, vsrc, dst_stride * (size_t)height); } else { - WARN_ON(dst_stride < width_in_bytes); for (i = 0; i < height; ++i) { memcpy(vdst, vsrc, width_in_bytes); vsrc += src_stride; @@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, ret = 0; out: - unmap_external(src, &src_map); - unmap_external(dst, &dst_map); + if (src_mapped) + unmap_external(src, &src_map); + if (dst_mapped) + unmap_external(dst, &dst_map); return ret; } From 6be3dbe45b287fffcfd132ab5c531c47cb95251a Mon Sep 17 00:00:00 2001 From: Jun Guo Date: Thu, 15 Jan 2026 17:19:24 +0800 Subject: [PATCH 3241/3297] spi: spi-cadence: enable SPI_CONTROLLER_MUST_TX commit f6b625639e39bc384a7bddbf134a698d40258b3b upstream. During an SPI read operation, even if the xspi->txbuf passed to the cdns_spi_writerinterface is empty, it is still necessary to call cdns_spi_write(xspi, CDNS_SPI_TXD, txw); otherwise, the read operation will fail to obtain data correctly due to a lack of clocks. Fixes: 4e00135b2dd1 ("spi: spi-cadence: supports transmission with bits_per_word of 16 and 32") Reported-by: Rodrigo Alencar <455.rodrigo.alencar@gmail.com> Closes: https://lore.kernel.org/all/lbijvnnwsnddonmm5pveqzap6iibxhl4maneq43x4j6w64dev6@u75qhm5cwiob/ Signed-off-by: Jun Guo Link: https://patch.msgid.link/20260115091924.844179-1-jun.guo@cixtech.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-cadence.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 403e8141dff89..640e8723a08ee 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -735,6 +735,7 @@ static int cdns_spi_probe(struct platform_device *pdev) ctlr->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware; ctlr->mode_bits = SPI_CPOL | SPI_CPHA; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->flags = SPI_CONTROLLER_MUST_TX; if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6")) ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32); From 2b70bebc709489d29a31ac2935aeffb8d5228395 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 18 Jun 2026 15:37:37 +0900 Subject: [PATCH 3242/3297] HID: logitech-dj: Fix maxfield check in DJ short report validation commit 590cc4d782487632a52f37c2171bee1eeea29627 upstream. Commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") added validation for the DJ short output report, but the error path dereferences rep->field[0] even when rep->maxfield is zero. Commit 8b9a097eb2fc ("HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report") made the check conditional on rep being present, but a crafted descriptor can still create report ID 0x20 with only padding output items. hid-core registers the report, ignores the padding field, and leaves rep->maxfield as zero. In that case the validation enters the rep->maxfield < 1 branch and then dereferences rep->field[0]->report_count while printing the error message, causing a NULL pointer dereference during probe. This is reproducible with uhid by emulating a Logitech receiver with a padding-only DJ short output report: BUG: KASAN: null-ptr-deref in logi_dj_probe+0xb1/0x754 [hid_logitech_dj] Read of size 4 at addr 0000000000000028 by task kworker/4:1/129 ... Call Trace: logi_dj_probe+0xb1/0x754 [hid_logitech_dj] hid_device_probe+0x329/0x3f0 [hid] really_probe+0x162/0x570 __device_attach+0x137/0x2c0 bus_probe_device+0x38/0xc0 device_add+0xa56/0xce0 hid_add_device+0x19c/0x280 [hid] uhid_device_add_worker+0x2c/0xb0 [uhid] Reject the zero-field report before printing the field report_count. Fixes: b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-logitech-dj.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index f1e923ed9d1c5..7895f27c91351 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1797,8 +1797,13 @@ static int logi_dj_probe(struct hid_device *hdev, output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - if (rep && (rep->maxfield < 1 || - rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) { + if (rep && rep->maxfield < 1) { + hid_err(hdev, "Expected size of DJ short report is %d, but got 0", + DJREPORT_SHORT_LENGTH - 1); + return -EINVAL; + } + + if (rep && rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1) { hid_err(hdev, "Expected size of DJ short report is %d, but got %d", DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count); return -EINVAL; From 542d3b9fa8ec67b724c5219fd1934e0efe0f86fb Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:05 -0700 Subject: [PATCH 3243/3297] drm/xe/rtp: Refactor OAG MMIO trigger register whitelisting [ Upstream commit ed455775c5a68b75e5f6ad6c8e0e3e9c98fd3f64 ] Minor refactor of OAG MMIO trigger register whitelisting for code reuse with OAM MMIO trigger register whitelisting. Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20251202025115.373546-5-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 29 ++++++++++++--------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 21763dc51150b..47fb3e74b567c 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -66,27 +66,24 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(WHITELIST(CSBE_DEBUG_STATUS(RENDER_RING_BASE), 0)) }, - { XE_RTP_NAME("oa_reg_render"), + +#define WHITELIST_OA_MMIO_TRG(trg, status, head) \ + WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ + WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ + WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) + +#define WHITELIST_OAG_MMIO_TRG \ + WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR) + + { XE_RTP_NAME("oag_mmio_trg_rcs"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), ENGINE_CLASS(RENDER)), - XE_RTP_ACTIONS(WHITELIST(OAG_MMIOTRIGGER, - RING_FORCE_TO_NONPRIV_ACCESS_RW), - WHITELIST(OAG_OASTATUS, - RING_FORCE_TO_NONPRIV_ACCESS_RD), - WHITELIST(OAG_OAHEADPTR, - RING_FORCE_TO_NONPRIV_ACCESS_RD | - RING_FORCE_TO_NONPRIV_RANGE_4)) + XE_RTP_ACTIONS(WHITELIST_OAG_MMIO_TRG) }, - { XE_RTP_NAME("oa_reg_compute"), + { XE_RTP_NAME("oag_mmio_trg_ccs"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), ENGINE_CLASS(COMPUTE)), - XE_RTP_ACTIONS(WHITELIST(OAG_MMIOTRIGGER, - RING_FORCE_TO_NONPRIV_ACCESS_RW), - WHITELIST(OAG_OASTATUS, - RING_FORCE_TO_NONPRIV_ACCESS_RD), - WHITELIST(OAG_OAHEADPTR, - RING_FORCE_TO_NONPRIV_ACCESS_RD | - RING_FORCE_TO_NONPRIV_RANGE_4)) + XE_RTP_ACTIONS(WHITELIST_OAG_MMIO_TRG) }, }; From 7982678fa21eda02a9111d2646be6762b5e3a64d Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:06 -0700 Subject: [PATCH 3244/3297] drm/xe/rtp: Add RING_FORCE_TO_NONPRIV_DENY to OA whitelists [ Upstream commit e70086a3a06d276b4a5d9a2c51c9330c6cf72780 ] Unconditionally whitelisting OA registers is a security violation. Set RING_FORCE_TO_NONPRIV_DENY bit in OA nonpriv slots, so that OA registers don't get whitelisted by default after probe, gt reset, resume and engine reset. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Suggested-by: Umesh Nerlige Ramappa Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-2-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 47fb3e74b567c..5cb7e0e3a66bd 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -67,10 +67,12 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { XE_RTP_ACTIONS(WHITELIST(CSBE_DEBUG_STATUS(RENDER_RING_BASE), 0)) }, +#define WHITELIST_DENY(r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY) + #define WHITELIST_OA_MMIO_TRG(trg, status, head) \ - WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ - WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ - WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) + WHITELIST_DENY(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ + WHITELIST_DENY(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ + WHITELIST_DENY(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) #define WHITELIST_OAG_MMIO_TRG \ WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR) From f73e97080debddbcc7071e1d007814444737481e Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:07 -0700 Subject: [PATCH 3245/3297] drm/xe/rtp: Maintain OA whitelists separately [ Upstream commit 31e2437561621b4867c08efc890bf629d017df03 ] Contains minor modification of the above commit because of changed API. OA registers are dynamically whitelisted (and again dewhitelisted) on OA stream open/close. Maintaining OA whitelists separately from non-OA register whitlists simplifies this management of OA register whitelisting/dewhitelisting. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-3-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_gt_debugfs.c | 4 +++- drivers/gpu/drm/xe/xe_hw_engine.c | 2 ++ drivers/gpu/drm/xe/xe_hw_engine_types.h | 8 ++++++++ drivers/gpu/drm/xe/xe_reg_whitelist.c | 6 ++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c index f253e2df49077..1a4d8d4f44b4d 100644 --- a/drivers/gpu/drm/xe/xe_gt_debugfs.c +++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c @@ -174,8 +174,10 @@ static int register_save_restore(struct xe_gt *gt, struct drm_printer *p) drm_printf(p, "\n"); drm_printf(p, "Whitelist\n"); - for_each_hw_engine(hwe, gt, id) + for_each_hw_engine(hwe, gt, id) { xe_reg_whitelist_dump(&hwe->reg_whitelist, p); + xe_reg_whitelist_dump(&hwe->oa_whitelist, p); + } xe_pm_runtime_put(gt_to_xe(gt)); diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index d88770370eeb0..66a738f17a3ba 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -565,6 +565,8 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe, hw_engine_setup_default_state(hwe); xe_reg_sr_init(&hwe->reg_whitelist, hwe->name, gt_to_xe(gt)); + xe_reg_sr_init(&hwe->oa_whitelist, hwe->name, gt_to_xe(gt)); + xe_reg_sr_init(&hwe->oa_sr, hwe->name, gt_to_xe(gt)); xe_reg_whitelist_process_engine(hwe); } diff --git a/drivers/gpu/drm/xe/xe_hw_engine_types.h b/drivers/gpu/drm/xe/xe_hw_engine_types.h index e4191a7a2c318..04ca7545d22c3 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_types.h +++ b/drivers/gpu/drm/xe/xe_hw_engine_types.h @@ -128,6 +128,14 @@ struct xe_hw_engine { * @reg_whitelist: table with registers to be whitelisted */ struct xe_reg_sr reg_whitelist; + /** + * @oa_whitelist: oa registers to be whitelisted + */ + struct xe_reg_sr oa_whitelist; + /** + * @oa_sr: oa nonpriv whitelist registers, changed on oa stream open/close + */ + struct xe_reg_sr oa_sr; /** * @reg_lrc: LRC workaround registers */ diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 5cb7e0e3a66bd..6b7c90ba6d10c 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -66,6 +66,9 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(WHITELIST(CSBE_DEBUG_STATUS(RENDER_RING_BASE), 0)) }, +}; + +static const struct xe_rtp_entry_sr oa_whitelist[] = { #define WHITELIST_DENY(r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY) @@ -138,6 +141,9 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) xe_rtp_process_to_sr(&ctx, register_whitelist, ARRAY_SIZE(register_whitelist), &hwe->reg_whitelist); whitelist_apply_to_hwe(hwe); + + xe_rtp_process_to_sr(&ctx, oa_whitelist, ARRAY_SIZE(oa_whitelist), + &hwe->oa_whitelist); } /** From cc716d3ac560fa5b2b51e1bdf25798eb7b46b0d4 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:08 -0700 Subject: [PATCH 3246/3297] drm/xe/rtp: Keep track of non-OA nonpriv slots [ Upstream commit 60d49ea28bb190a640bd8dc3f4c946e0811a948c ] In order to dynamically whitelist/dewhitelist OA registers on OA stream open/close, we need to keep track of nonpriv slots occupied by non-OA register whitelists. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-4-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 6b7c90ba6d10c..a5658b2df0cc1 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -92,7 +92,7 @@ static const struct xe_rtp_entry_sr oa_whitelist[] = { }, }; -static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) +static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) { struct xe_reg_sr *sr = &hwe->reg_whitelist; struct xe_reg_sr_entry *entry; @@ -124,6 +124,8 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) slot++; } + + return slot; } /** @@ -137,10 +139,11 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) { struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); + int first_oa_slot; xe_rtp_process_to_sr(&ctx, register_whitelist, ARRAY_SIZE(register_whitelist), &hwe->reg_whitelist); - whitelist_apply_to_hwe(hwe); + first_oa_slot = whitelist_apply_to_hwe(hwe); xe_rtp_process_to_sr(&ctx, oa_whitelist, ARRAY_SIZE(oa_whitelist), &hwe->oa_whitelist); From 4bb92418e749ff69a6b71aed81de7b78674d195b Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:09 -0700 Subject: [PATCH 3247/3297] drm/xe/rtp: Generalize whitelist_apply_to_hwe [ Upstream commit 4fe2844b0f0c7cdc45ca4c4c62ca56b7f26c514c ] Generalize whitelist_apply_to_hwe to construct both non-OA and OA whitelist nonpriv registers. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-5-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index a5658b2df0cc1..cc9f0f598e49c 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -92,9 +92,10 @@ static const struct xe_rtp_entry_sr oa_whitelist[] = { }, }; -static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) +static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe, struct xe_reg_sr *in, + struct xe_reg_sr *out, int first_slot) { - struct xe_reg_sr *sr = &hwe->reg_whitelist; + struct xe_reg_sr *sr = in; struct xe_reg_sr_entry *entry; struct drm_printer p; unsigned long reg; @@ -103,7 +104,7 @@ static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) xe_gt_dbg(hwe->gt, "Add %s whitelist to engine\n", sr->name); p = xe_gt_dbg_printer(hwe->gt); - slot = 0; + slot = first_slot; xa_for_each(&sr->xa, reg, entry) { struct xe_reg_sr_entry hwe_entry = { .reg = RING_FORCE_TO_NONPRIV(hwe->mmio_base, slot), @@ -120,7 +121,7 @@ static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) } xe_reg_whitelist_print_entry(&p, 0, reg, entry); - xe_reg_sr_add(&hwe->reg_sr, &hwe_entry, hwe->gt); + xe_reg_sr_add(out, &hwe_entry, hwe->gt); slot++; } @@ -143,7 +144,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) xe_rtp_process_to_sr(&ctx, register_whitelist, ARRAY_SIZE(register_whitelist), &hwe->reg_whitelist); - first_oa_slot = whitelist_apply_to_hwe(hwe); + first_oa_slot = whitelist_apply_to_hwe(hwe, &hwe->reg_whitelist, &hwe->reg_sr, 0); xe_rtp_process_to_sr(&ctx, oa_whitelist, ARRAY_SIZE(oa_whitelist), &hwe->oa_whitelist); From c2cfee9bf8d46e22799894a252404f38daad2d80 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:10 -0700 Subject: [PATCH 3248/3297] drm/xe/rtp: Save OA nonpriv registers to register save/restore lists [ Upstream commit a19a83721a28ccaddace846da70da5c53d7dd052 ] Now we can save OA whitelisting nonpriv registers to register save/restore lists. OA nonpriv registers are saved to both hwe->oa_sr as well as hwe->reg_sr. During probe, resume and gt-reset flows KMD will apply hwe->reg_sr, ensuring OA registers are de-whitelisted after these events. For engine-reset, hwe->reg_sr is registered with GuC and GuC will apply these registers, ensuring OA registers are de-whitelisted after engine resets. hwe->oa_sr is used for whitelisting or de-whitelisting OA registers during OA operation, by toggling the 'deny' bit on oa stream open/close. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-6-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index cc9f0f598e49c..4f7391d787fb0 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -148,6 +148,17 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) xe_rtp_process_to_sr(&ctx, oa_whitelist, ARRAY_SIZE(oa_whitelist), &hwe->oa_whitelist); + /* + * Save oa nonpriv registers to hwe->oa_sr, from which oa registers are whitelisted + * or de-whitelisted, by toggling the 'deny' bit on oa stream open/close + */ + whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->oa_sr, first_oa_slot); + + /* + * Also save oa nonpriv registers to hwe->reg_sr, to ensure oa registers are not + * whitelisted by default after probe, gt reset, resume and engine reset + */ + whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } /** From d43abc858f08269b605d6400455aaf7933775df3 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:11 -0700 Subject: [PATCH 3249/3297] drm/xe/rtp: Toggle 'deny' bit to (de-)whitelist OA regs [ Upstream commit b422babd77fac2c96b92db484050e460899bddaf ] Whitelist or de-whitelist OA registers by setting or resetting the 'deny' bit in OA nonpriv registers and writing new register values to HW. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-7-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 4f7391d787fb0..4922b3561dbf4 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -161,6 +161,21 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } +__maybe_unused static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whitelist) +{ + struct xe_reg_sr_entry *entry; + unsigned long reg; + + xa_for_each(&hwe->oa_sr.xa, reg, entry) { + if (whitelist) + entry->set_bits &= ~RING_FORCE_TO_NONPRIV_DENY; + else + entry->set_bits |= RING_FORCE_TO_NONPRIV_DENY; + } + + xe_reg_sr_apply_mmio(&hwe->oa_sr, hwe->gt); +} + /** * xe_reg_whitelist_print_entry - print one whitelist entry * @p: DRM printer From f5966d9006623b4a7c754f807fca84ee2b976457 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:12 -0700 Subject: [PATCH 3250/3297] drm/xe/rtp: (De-)whitelist OA registers for all hwe's for a gt [ Upstream commit ebba7ce65252a4ab0e3794ff14854df2afca5c08 ] Whitelist or de-whitelist OA registers for all hwe's on the gt on which the OA stream is opened. This simplifies the case where an oa unit has 0 attached hwe's (but which monitors OA events on the associated GT). Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-8-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_reg_whitelist.c | 34 +++++++++++++++++++++++++-- drivers/gpu/drm/xe/xe_reg_whitelist.h | 4 ++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 4922b3561dbf4..e77f50da4a268 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -9,7 +9,7 @@ #include "regs/xe_gt_regs.h" #include "regs/xe_oa_regs.h" #include "regs/xe_regs.h" -#include "xe_gt_types.h" +#include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_platform_types.h" #include "xe_reg_sr.h" @@ -161,7 +161,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } -__maybe_unused static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whitelist) +static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whitelist) { struct xe_reg_sr_entry *entry; unsigned long reg; @@ -176,6 +176,36 @@ __maybe_unused static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool wh xe_reg_sr_apply_mmio(&hwe->oa_sr, hwe->gt); } +/** + * xe_reg_whitelist_oa_regs - whitelist oa registers for gt + * @gt: gt to whitelist oa registers for + * + * Whitelist OA registers by resetting RING_FORCE_TO_NONPRIV_DENY + */ +void xe_reg_whitelist_oa_regs(struct xe_gt *gt) +{ + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + + for_each_hw_engine(hwe, gt, id) + __whitelist_oa_regs(hwe, true); +} + +/** + * xe_reg_dewhitelist_oa_regs - dewhitelist oa registers for gt + * @gt: gt to dewhitelist oa registers for + * + * Dewhitelist OA registers by setting RING_FORCE_TO_NONPRIV_DENY + */ +void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt) +{ + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + + for_each_hw_engine(hwe, gt, id) + __whitelist_oa_regs(hwe, false); +} + /** * xe_reg_whitelist_print_entry - print one whitelist entry * @p: DRM printer diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.h b/drivers/gpu/drm/xe/xe_reg_whitelist.h index 69b121d377da0..f243b6ac60b7e 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.h +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.h @@ -9,12 +9,16 @@ #include struct drm_printer; +struct xe_gt; struct xe_hw_engine; struct xe_reg_sr; struct xe_reg_sr_entry; void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe); +void xe_reg_whitelist_oa_regs(struct xe_gt *gt); +void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt); + void xe_reg_whitelist_print_entry(struct drm_printer *p, unsigned int indent, u32 reg, struct xe_reg_sr_entry *entry); From 9783d8662b5658e9efdb3a0934cd0ce382816878 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:13 -0700 Subject: [PATCH 3251/3297] drm/xe/oa: (De-)whitelist OA registers on OA stream open/release [ Upstream commit 63ddb3ad08ff4e89c108499dfec5e9be5ddc25c9 ] Whitelist OA registers on stream open and de-whitelist on stream close/release. Whitelisting is only done when 'stream->sample' is true. 'stream->sample' is only true when (a) xe_observation_paranoid is set to false by system admin, or (b) the process is perfmon_capable(). This therefore enforces the OA register whitelisting security requirements. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-9-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_oa.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 7d04591e297a8..98df9ee1cac39 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -35,6 +35,7 @@ #include "xe_oa.h" #include "xe_observation.h" #include "xe_pm.h" +#include "xe_reg_whitelist.h" #include "xe_sched_job.h" #include "xe_sriov.h" #include "xe_sync.h" @@ -863,6 +864,9 @@ static void xe_oa_stream_destroy(struct xe_oa_stream *stream) mutex_destroy(&stream->stream_lock); + if (stream->sample) + xe_reg_dewhitelist_oa_regs(stream->gt); + xe_oa_disable_metric_set(stream); xe_exec_queue_put(stream->k_exec_q); @@ -1887,6 +1891,9 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa, goto err_disable; } + if (stream->sample) + xe_reg_whitelist_oa_regs(stream->gt); + /* Hold a reference on the drm device till stream_fd is released */ drm_dev_get(&stream->oa->xe->drm); From fc0c76b0450f200a894811ee789ecedbcfdbaeb9 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 4 Aug 2026 19:46:14 -0700 Subject: [PATCH 3252/3297] drm/xe/rtp: Ensure locking/ref counting for OA whitelists [ Upstream commit ef78e2a22f72c892fd6663f0760abd208d49a3e2 ] Since multiple OA streams might be open in parallel on a gt, ensure that proper locking is in place. Also ensure that OA registers are whitelisted when the first OA stream is open and de-whitelisted after the last OA stream is closed. Fixes: 828a8eaf37c3 ("drm/xe/oa: Add MMIO trigger support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Ashutosh Dixit Reviewed-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260615224227.34880-10-ashutosh.dixit@intel.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_oa_types.h | 3 +++ drivers/gpu/drm/xe/xe_reg_whitelist.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa_types.h b/drivers/gpu/drm/xe/xe_oa_types.h index daf701b5d48b9..48f9520f560c2 100644 --- a/drivers/gpu/drm/xe/xe_oa_types.h +++ b/drivers/gpu/drm/xe/xe_oa_types.h @@ -125,6 +125,9 @@ struct xe_oa_gt { /** @oa_unit: array of oa_units */ struct xe_oa_unit *oa_unit; + + /** @whitelist_count: number of open streams for which oa registers are whitelisted */ + u32 whitelist_count; }; /** diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index e77f50da4a268..eb1d6f52f75fb 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -9,6 +9,7 @@ #include "regs/xe_gt_regs.h" #include "regs/xe_oa_regs.h" #include "regs/xe_regs.h" +#include "xe_assert.h" #include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_platform_types.h" @@ -187,6 +188,10 @@ void xe_reg_whitelist_oa_regs(struct xe_gt *gt) struct xe_hw_engine *hwe; enum xe_hw_engine_id id; + lockdep_assert_held(>->oa.gt_lock); + if (gt->oa.whitelist_count++) + return; + for_each_hw_engine(hwe, gt, id) __whitelist_oa_regs(hwe, true); } @@ -202,6 +207,11 @@ void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt) struct xe_hw_engine *hwe; enum xe_hw_engine_id id; + lockdep_assert_held(>->oa.gt_lock); + xe_assert(gt_to_xe(gt), gt->oa.whitelist_count); + if (--gt->oa.whitelist_count) + return; + for_each_hw_engine(hwe, gt, id) __whitelist_oa_regs(hwe, false); } From 2b9a07002c2f296aa6a9c591213933d3492e3089 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Wed, 5 Aug 2026 14:43:46 +0100 Subject: [PATCH 3253/3297] mm/hugetlb: fix swap entry corruption when clearing uffd-wp at fork() [ Upstream commit 83abe2fd5b3aeb3123b5408a5a91709c5538fb23 ] copy_hugetlb_page_range() clears the uffd-wp bit of migration and hwpoison entries with huge_pte_clear_uffd_wp(), which operates on the present-PTE bit position. Swap entries keep the uffd-wp state elsewhere -- the migration branch reads and sets it with pte_swp_uffd_wp() and pte_swp_mkuffd_wp() -- and the present-PTE position falls into the swap payload. On x86-64 it lands in the inverted swap offset, where a naturally-aligned hugetlb PFN always has the affected bit set, so the clear advances the encoded PFN by two pages. No userfaultfd needs to be involved: the clear is guarded only by the child VMA not being uffd-wp registered, so a plain fork() with an in-flight hugetlb migration entry (or a poisoned hugetlb page) corrupts the entry copied into the child. Instrumenting the clear and forking after MADV_HWPOISON on a 2MB anon hugetlb page shows: offset before=120e00 offset after =120e02 The fallout is mostly latent: rmap walks match migration entries by folio range and remove_migration_pte() rebuilds the PTE from the folio, so a within-folio PFN skew heals once migration completes. But any path that re-encodes the corrupted offset -- e.g. hugetlb_change_protection() rewriting a writable migration entry via make_readable_migration_entry(swp_offset(entry)) -- propagates it. Migration entries legitimately carry uffd-wp, so clear it with pte_swp_clear_uffd_wp(), matching copy_nonpresent_pte() and move_huge_pte(). A hwpoison entry, on the other hand, never carries the uffd-wp bit: it is installed fresh by make_hwpoison_entry() (try_to_unmap_one() does not preserve uffd-wp on the hwpoison path) and hugetlb_change_protection() leaves hwpoison entries untouched. There was nothing to clear there, only the corruption, so drop the clear entirely. Link: https://lore.kernel.org/20260708090110.136162-1-kirill@shutemov.name Fixes: bc70fbf269fd ("mm/hugetlb: handle uffd-wp during fork()") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Closes: https://lore.kernel.org/all/20260703140011.99E601F000E9@smtp.kernel.org/ Suggested-by: David Hildenbrand Acked-by: David Hildenbrand (Arm) Assisted-by: Claude:claude-fable-5 Cc: Muchun Song Cc: Oscar Salvador Cc: Peter Xu Cc: Signed-off-by: Andrew Morton (cherry picked from commit 83abe2fd5b3aeb3123b5408a5a91709c5538fb23) [ kas: adapt to the pre-softleaf idiom ] Signed-off-by: Kiryl Shutsemau (Meta) Signed-off-by: Sasha Levin --- mm/hugetlb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0b3fd6943836e..5515870b5b155 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -5634,8 +5634,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, */ ; } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry))) { - if (!userfaultfd_wp(dst_vma)) - entry = huge_pte_clear_uffd_wp(entry); + /* + * A hwpoison entry never carries the uffd-wp bit: it is + * installed fresh by make_hwpoison_entry() and + * hugetlb_change_protection() leaves it untouched, so + * there is nothing to clear for the child. + */ set_huge_pte_at(dst, addr, dst_pte, entry, sz); } else if (unlikely(is_hugetlb_entry_migration(entry))) { swp_entry_t swp_entry = pte_to_swp_entry(entry); @@ -5654,7 +5658,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, set_huge_pte_at(src, addr, src_pte, entry, sz); } if (!userfaultfd_wp(dst_vma)) - entry = huge_pte_clear_uffd_wp(entry); + entry = pte_swp_clear_uffd_wp(entry); set_huge_pte_at(dst, addr, dst_pte, entry, sz); } else if (unlikely(is_pte_marker(entry))) { pte_marker marker = copy_pte_marker( From be11b4bf498a38671743da800cc24be9f43b17b7 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Wed, 5 Aug 2026 14:43:52 +0100 Subject: [PATCH 3254/3297] fs/proc/task_mmu: fix PAGEMAP_SCAN written state for unpopulated ptes [ Upstream commit 07b4377bdbe74a3ec0c8da5849d014f70e003384 ] PAGEMAP_SCAN reports an unpopulated pte differently depending on which path serves the request. The PAGE_IS_WRITTEN fast path in pagemap_scan_pmd_entry() reports a pte_none as written (and, under PM_SCAN_WP_MATCHING, arms a marker); pagemap_page_category() returns 0 for the same pte_none. A request that cannot take the fast path (an extra category bit, category_anyof_mask or category_inverted) therefore reports the pte as clean and skips arming it. A range that was populated and then MADV_DONTNEED'd reads as written via one mask and clean via another, and in the latter case is not re-armed for the next round -- an incremental-dump consumer (e.g. CRIU) using a richer mask drops the zapped range and stops tracking writes to it. Report pte_none as written in pagemap_page_category() too. A pte_none carries no uffd-wp marker, i.e. it is not write-protected -- the same condition under which the present and swap cases already report PAGE_IS_WRITTEN. The fast path applies no VMA test, so neither does this. The hugetlb and fully-unpopulated-PMD (no page table) scans have no PAGE_IS_WRITTEN fast path, so they do not exhibit the per-entry divergence and are left unchanged. Add a pagemap_ioctl selftest that populates a range, drops it with MADV_DONTNEED, and checks that the fast path and the generic (category_anyof_mask) path both report every page written. Link: https://lore.kernel.org/20260707151349.92143-1-kirill@shutemov.name Fixes: 12f6b01a0bcb ("fs/proc/task_mmu: add fast paths to get/clear PAGE_IS_WRITTEN flag") Signed-off-by: Kiryl Shutsemau Cc: Muhammad Usama Anjum Cc: David Hildenbrand Cc: Jann Horn Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Peter Xu Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Assisted-by: Claude:claude-fable-5 Cc: Signed-off-by: Andrew Morton (cherry picked from commit 07b4377bdbe74a3ec0c8da5849d014f70e003384) [ kas: fix only; selftest hunk dropped (pagemap_ioctl.c differs on this tree). Add the pte_none early-return before the pte_present branch ] Signed-off-by: Kiryl Shutsemau (Meta) Signed-off-by: Sasha Levin --- fs/proc/task_mmu.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 0cec16f9e9afd..48455e2a66a1e 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2326,6 +2326,19 @@ static unsigned long pagemap_page_category(struct pagemap_scan_private *p, { unsigned long categories = 0; + if (pte_none(pte)) { + /* + * An unpopulated pte carries no uffd-wp marker, i.e. it is not + * write-protected, the same condition under which the present + * and swap cases below report PAGE_IS_WRITTEN. Report it here + * too so this generic path agrees with the PAGE_IS_WRITTEN fast + * path in pagemap_scan_pmd_entry(), which reports pte_none as + * written and, under PM_SCAN_WP_MATCHING, arms a marker. The + * fast path applies no VMA test, so neither does this. + */ + return PAGE_IS_WRITTEN; + } + if (pte_present(pte)) { struct page *page; From 10065fb891651d9541e7a5a2db84c1e656ece4f9 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Wed, 5 Aug 2026 14:45:25 +0100 Subject: [PATCH 3255/3297] mm/huge_memory: unlock i_mmap_rwsem before releasing after-split folios [ Upstream commit e923bd21058ea02fd0dcd3549d151d143fd036e5 ] __folio_split() keeps dereferencing the mapping after the split: shmem_uncharge(mapping->host) and remap_page() while the folios are still frozen/locked, and i_mmap_unlock_read(mapping) at the very end, after the after-split folios have been unlocked and freed. Nothing holds an inode reference across that. The split relies on @folio -- which the beyond-EOF drop loop never removes, as it starts at folio_next(folio) -- staying locked and in the page cache to hold off eviction. But the unlock loop unlocks @folio before i_mmap_unlock_read() runs. If the caller's @lock_at is a tail beyond EOF, as memory_failure() passes when splitting a poisoned tail of a shmem THP that reaches past i_size during truncation, it too is gone from the page cache; so once @folio is unlocked no locked, in-cache folio pins the inode, and a concurrent final iput() can evict and RCU-free it before i_mmap_unlock_read() touches i_mmap_rwsem: BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790 i_mmap_unlock_read include/linux/fs.h:537 [inline] __folio_split+0x732/0x1640 mm/huge_memory.c:4100 try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675 memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470 Freed by task 4601: shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177 evict+0x57f/0xac0 fs/inode.c:870 Do every mapping dereference while @folio still pins the inode: drop i_mmap_rwsem right after remap_page(), before the loop that unlocks and frees the after-split folios, and clear @mapping so the exit path does not unlock it again. shmem_uncharge() and remap_page() already run before that point, so after this nothing past the unlock loop touches the inode or the mapping. This is now a rule the split depends on, alongside keeping @folio frozen until the page cache is updated: no inode or mapping dereference once the after-split folios start being unlocked. Link: https://lore.kernel.org/20260716095424.471052-1-kirill@shutemov.name Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()") Signed-off-by: Kiryl Shutsemau (Meta) Reported-by: Hao Zhang Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc Co-developed-by: Hao Zhang Signed-off-by: Hao Zhang Acked-by: David Hildenbrand (Arm) Reviewed-by: Zi Yan Reviewed-by: Baolin Wang Reviewed-by: Miaohe Lin Cc: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Naoya Horiguchi Cc: Nico Pache Cc: Ryan Roberts Cc: Signed-off-by: Andrew Morton (cherry picked from commit e923bd21058ea02fd0dcd3549d151d143fd036e5) Signed-off-by: Kiryl Shutsemau (Meta) Signed-off-by: Sasha Levin --- mm/huge_memory.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index c08aa3db96431..bd3093a6cc628 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3871,6 +3871,18 @@ static int __folio_split(struct folio *folio, unsigned int new_order, remap_flags = RMP_USE_SHARED_ZEROPAGE; remap_page(folio, 1 << order, remap_flags); + /* + * Drop the mapping while the inode is still pinned. @folio stays + * locked and present in the page cache until the loop below, so + * eviction cannot free the inode yet; @lock_at is not enough, it may + * be a tail beyond EOF that the split already dropped from the page + * cache. Nothing past this point may touch the inode or the mapping. + */ + if (mapping) { + i_mmap_unlock_read(mapping); + mapping = NULL; + } + /* * Unlock all after-split folios except the one containing * @lock_at page. If @folio is not split, it will be kept locked. From 10827847c40c18d5fa2c49e5791ca2b854439c17 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 6 Aug 2026 01:39:52 +0800 Subject: [PATCH 3256/3297] file: add FD_{ADD,PREPARE}() [ Upstream commit 011703a9acd76edc7c85d80dbccb6e50dba53aad ] I've been playing with this to allow for moderately flexible usage of the get_unused_fd_flags() + create file + fd_install() pattern that's used quite extensively. How callers allocate files is really heterogenous so it's not really convenient to fold them into a single class. It's possibe to split them into subclasses like for anon inodes. I think that's not necessarily nice as well. My take is to add two primites: (1) FD_ADD() the simple cases a file is installed: fd = FD_ADD(O_CLOEXEC, open_file(some, args))); if (fd >= 0) kvm_get_kvm(vcpu->kvm); return fd; (2) FD_PREPARE() that captures all the cases where access to fd or file or additional work before publishing the fd is needed: FD_PREPARE(fdf, open_flag, file_open_handle(&path, open_flag)); if (fdf.err) return fdf.err; if (copy_to_user(/* something something */)) return -EFAULT; return fd_publish(fdf); I've converted all of the easy cases over to it and it gets rid of an aweful lot of convoluted cleanup logic. It's centered around struct fd_prepare. FD_PREPARE() encapsulates all of allocation and cleanup logic and must be followed by a call to fd_publish() which associates the fd with the file and installs it into the callers fdtable. If fd_publish() isn't called both are deallocated. It mandates a specific order namely that first we allocate the fd and then instantiate the file. But that shouldn't be a problem nearly everyone I've converted uses this exact pattern anyway. There's a bunch of additional cases where it would be easy to convert them to this pattern. For example, the whole sync file stuff in dma currently retains the containing structure of the file instead of the file itself even though it's only used to allocate files. Changing that would make it fall into the FD_PREPARE() pattern easily. I've not done that work yet. There's room for extending this in a way that wed'd have subclasses for some particularly often use patterns but as I said I'm not even sure that's worth it. Link: https://patch.msgid.link/20251123-work-fd-prepare-v4-1-b6efa1706cfd@kernel.org Signed-off-by: Christian Brauner (cherry picked from commit 011703a9acd76edc7c85d80dbccb6e50dba53aad) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- include/linux/cleanup.h | 7 +++ include/linux/file.h | 126 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index a1194e44b5276..bacefa0f1512f 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -261,6 +261,10 @@ const volatile void * __must_check_fn(const volatile void *val) * CLASS(name, var)(args...): * declare the variable @var as an instance of the named class * + * CLASS_INIT(name, var, init_expr): + * declare the variable @var as an instance of the named class with + * custom initialization expression. + * * Ex. * * DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd) @@ -290,6 +294,9 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ class_##_name##_t var __cleanup(class_##_name##_destructor) = \ class_##_name##_constructor +#define CLASS_INIT(_name, _var, _init_expr) \ + class_##_name##_t _var __cleanup(class_##_name##_destructor) = (_init_expr) + #define __scoped_class(_name, var, _label, args...) \ for (CLASS(_name, var)(args); ; ({ goto _label; })) \ if (0) { \ diff --git a/include/linux/file.h b/include/linux/file.h index af1768d934a01..cf389fde9bc28 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -127,4 +127,130 @@ extern void __fput_sync(struct file *); extern unsigned int sysctl_nr_open_min, sysctl_nr_open_max; +/* + * fd_prepare: Combined fd + file allocation cleanup class. + * @err: Error code to indicate if allocation succeeded. + * @__fd: Allocated fd (may not be accessed directly) + * @__file: Allocated struct file pointer (may not be accessed directly) + * + * Allocates an fd and a file together. On error paths, automatically cleans + * up whichever resource was successfully allocated. Allows flexible file + * allocation with different functions per usage. + * + * Do not use directly. + */ +struct fd_prepare { + s32 err; + s32 __fd; /* do not access directly */ + struct file *__file; /* do not access directly */ +}; + +/* Typedef for fd_prepare cleanup guards. */ +typedef struct fd_prepare class_fd_prepare_t; + +/* + * Accessors for fd_prepare class members. + * _Generic() is used for zero-cost type safety. + */ +#define fd_prepare_fd(_fdf) \ + (_Generic((_fdf), struct fd_prepare: (_fdf).__fd)) + +#define fd_prepare_file(_fdf) \ + (_Generic((_fdf), struct fd_prepare: (_fdf).__file)) + +/* Do not use directly. */ +static inline void class_fd_prepare_destructor(const struct fd_prepare *fdf) +{ + if (unlikely(fdf->err)) { + if (likely(fdf->__fd >= 0)) + put_unused_fd(fdf->__fd); + if (unlikely(!IS_ERR_OR_NULL(fdf->__file))) + fput(fdf->__file); + } +} + +/* Do not use directly. */ +static inline int class_fd_prepare_lock_err(const struct fd_prepare *fdf) +{ + if (unlikely(fdf->err)) + return fdf->err; + if (unlikely(fdf->__fd < 0)) + return fdf->__fd; + if (unlikely(IS_ERR(fdf->__file))) + return PTR_ERR(fdf->__file); + if (unlikely(!fdf->__file)) + return -ENOMEM; + return 0; +} + +/* + * __FD_PREPARE_INIT - Helper to initialize fd_prepare class. + * @_fd_flags: flags for get_unused_fd_flags() + * @_file_owned: expression that returns struct file * + * + * Returns a struct fd_prepare with fd, file, and err set. + * If fd allocation fails, fd will be negative and err will be set. If + * fd succeeds but file_init_expr fails, file will be ERR_PTR and err + * will be set. The err field is the single source of truth for error + * checking. + */ +#define __FD_PREPARE_INIT(_fd_flags, _file_owned) \ + ({ \ + struct fd_prepare fdf = { \ + .__fd = get_unused_fd_flags((_fd_flags)), \ + }; \ + if (likely(fdf.__fd >= 0)) \ + fdf.__file = (_file_owned); \ + fdf.err = ACQUIRE_ERR(fd_prepare, &fdf); \ + fdf; \ + }) + +/* + * FD_PREPARE - Macro to declare and initialize an fd_prepare variable. + * + * Declares and initializes an fd_prepare variable with automatic + * cleanup. No separate scope required - cleanup happens when variable + * goes out of scope. + * + * @_fdf: name of struct fd_prepare variable to define + * @_fd_flags: flags for get_unused_fd_flags() + * @_file_owned: struct file to take ownership of (can be expression) + */ +#define FD_PREPARE(_fdf, _fd_flags, _file_owned) \ + CLASS_INIT(fd_prepare, _fdf, __FD_PREPARE_INIT(_fd_flags, _file_owned)) + +/* + * fd_publish - Publish prepared fd and file to the fd table. + * @_fdf: struct fd_prepare variable + */ +#define fd_publish(_fdf) \ + ({ \ + struct fd_prepare *fdp = &(_fdf); \ + VFS_WARN_ON_ONCE(fdp->err); \ + VFS_WARN_ON_ONCE(fdp->__fd < 0); \ + VFS_WARN_ON_ONCE(IS_ERR_OR_NULL(fdp->__file)); \ + fd_install(fdp->__fd, fdp->__file); \ + fdp->__fd; \ + }) + +/* Do not use directly. */ +#define __FD_ADD(_fdf, _fd_flags, _file_owned) \ + ({ \ + FD_PREPARE(_fdf, _fd_flags, _file_owned); \ + s32 ret = _fdf.err; \ + if (likely(!ret)) \ + ret = fd_publish(_fdf); \ + ret; \ + }) + +/* + * FD_ADD - Allocate and install an fd and file in one step. + * @_fd_flags: flags for get_unused_fd_flags() + * @_file_owned: struct file to take ownership of + * + * Returns the allocated fd number, or negative error code on failure. + */ +#define FD_ADD(_fd_flags, _file_owned) \ + __FD_ADD(__UNIQUE_ID(fd_prepare), _fd_flags, _file_owned) + #endif /* __LINUX_FILE_H */ From f00dd592abe77fcf1f1e40772834cf982c682406 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 6 Aug 2026 01:39:54 +0800 Subject: [PATCH 3257/3297] file: ensure cleanup [ Upstream commit d2ea4d254d04a89e17504af0230c7268e3cac6bf ] Brown paper bag time. This is a silly oversight where I missed to drop the error condition checking to ensure we clean up on early error returns. I have an internal unit testset coming up for this which will catch all such issues going forward. Reported-by: Chris Mason Reported-by: Jeff Layton Fixes: 011703a9acd7 ("file: add FD_{ADD,PREPARE}()") Signed-off-by: Christian Brauner Reviewed-by: Jeff Layton Signed-off-by: Linus Torvalds (cherry picked from commit d2ea4d254d04a89e17504af0230c7268e3cac6bf) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- include/linux/file.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/linux/file.h b/include/linux/file.h index cf389fde9bc28..27484b444d315 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -161,12 +161,10 @@ typedef struct fd_prepare class_fd_prepare_t; /* Do not use directly. */ static inline void class_fd_prepare_destructor(const struct fd_prepare *fdf) { - if (unlikely(fdf->err)) { - if (likely(fdf->__fd >= 0)) - put_unused_fd(fdf->__fd); - if (unlikely(!IS_ERR_OR_NULL(fdf->__file))) - fput(fdf->__file); - } + if (unlikely(fdf->__fd >= 0)) + put_unused_fd(fdf->__fd); + if (unlikely(!IS_ERR_OR_NULL(fdf->__file))) + fput(fdf->__file); } /* Do not use directly. */ @@ -230,7 +228,8 @@ static inline int class_fd_prepare_lock_err(const struct fd_prepare *fdf) VFS_WARN_ON_ONCE(fdp->__fd < 0); \ VFS_WARN_ON_ONCE(IS_ERR_OR_NULL(fdp->__file)); \ fd_install(fdp->__fd, fdp->__file); \ - fdp->__fd; \ + retain_and_null_ptr(fdp->__file); \ + take_fd(fdp->__fd); \ }) /* Do not use directly. */ From 97e745b4ea0557e671b7431c5c1d37184ba04325 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 6 Aug 2026 01:39:56 +0800 Subject: [PATCH 3258/3297] net/handshake: convert handshake_nl_accept_doit() to FD_PREPARE() [ Upstream commit fe67b063f687d8f32c466778aad57508bc1c618a ] Link: https://patch.msgid.link/20251123-work-fd-prepare-v4-27-b6efa1706cfd@kernel.org Signed-off-by: Christian Brauner (cherry picked from commit fe67b063f687d8f32c466778aad57508bc1c618a) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- net/handshake/netlink.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index d8211e0ba75c6..7b1d42607ae68 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -93,7 +93,7 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) struct handshake_net *hn = handshake_pernet(net); struct handshake_req *req = NULL; struct socket *sock; - int class, fd, err; + int class, err; err = -EOPNOTSUPP; if (!hn) @@ -106,27 +106,25 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) err = -EAGAIN; req = handshake_req_next(hn, class); - if (!req) - goto out_status; - - sock = req->hr_sk->sk_socket; - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - err = fd; - goto out_complete; - } - - err = req->hr_proto->hp_accept(req, info, fd); - if (err) { - put_unused_fd(fd); - goto out_complete; + if (req) { + sock = req->hr_sk->sk_socket; + + FD_PREPARE(fdf, O_CLOEXEC, sock->file); + if (fdf.err) { + err = fdf.err; + goto out_complete; + } + + get_file(sock->file); /* FD_PREPARE() consumes a reference. */ + err = req->hr_proto->hp_accept(req, info, fd_prepare_fd(fdf)); + if (err) + goto out_complete; /* Automatic cleanup handles fput */ + + trace_handshake_cmd_accept(net, req, req->hr_sk, fd_prepare_fd(fdf)); + fd_publish(fdf); + return 0; } - fd_install(fd, get_file(sock->file)); - - trace_handshake_cmd_accept(net, req, req->hr_sk, fd); - return 0; - out_complete: handshake_complete(req, -EIO, NULL); out_status: From 5ddfc47e1228d207602d945f63e6869ede081ef9 Mon Sep 17 00:00:00 2001 From: Wang Liang Date: Thu, 6 Aug 2026 01:39:58 +0800 Subject: [PATCH 3259/3297] net/handshake: Fix null-ptr-deref in handshake_complete() [ Upstream commit dabac51b8102e1643d8e297a8e6948dab190aa4c ] A null pointer dereference in handshake_complete() was observed [1]. When handshake_req_next() return NULL in handshake_nl_accept_doit(), function handshake_complete() will be called unexpectedly which triggers this crash. Fix it by goto out_status when req is NULL. [1] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000005: 0000 [#1] SMP KASAN PTI RIP: 0010:handshake_complete+0x36/0x2b0 net/handshake/request.c:288 Call Trace: handshake_nl_accept_doit+0x32d/0x7e0 net/handshake/netlink.c:129 genl_family_rcv_msg_doit+0x204/0x300 net/netlink/genetlink.c:1115 genl_family_rcv_msg+0x436/0x670 net/netlink/genetlink.c:1195 genl_rcv_msg+0xcc/0x170 net/netlink/genetlink.c:1210 netlink_rcv_skb+0x14c/0x430 net/netlink/af_netlink.c:2550 genl_rcv+0x2d/0x40 net/netlink/genetlink.c:1219 netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline] netlink_unicast+0x878/0xb20 net/netlink/af_netlink.c:1344 netlink_sendmsg+0x897/0xd70 net/netlink/af_netlink.c:1894 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg net/socket.c:742 [inline] ____sys_sendmsg+0xa39/0xbf0 net/socket.c:2592 ___sys_sendmsg+0x121/0x1c0 net/socket.c:2646 __sys_sendmsg+0x155/0x200 net/socket.c:2678 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x5f/0x350 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x76/0x7e Fixes: fe67b063f687 ("net/handshake: convert handshake_nl_accept_doit() to FD_PREPARE()") Reviewed-by: Chuck Lever Reported-by: Dan Carpenter Closes: https://lore.kernel.org/kernel-tls-handshake/aScekpuOYHRM9uOd@morisot.1015granger.net/T/#m7cfa5c11efc626d77622b2981591197a2acdd65e Signed-off-by: Wang Liang Reviewed-by: Simon Horman Link: https://patch.msgid.link/20251212012723.4111831-1-wangliang74@huawei.com Signed-off-by: Paolo Abeni (cherry picked from commit dabac51b8102e1643d8e297a8e6948dab190aa4c) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- net/handshake/netlink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 7b1d42607ae68..039344979de93 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -126,7 +126,8 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) } out_complete: - handshake_complete(req, -EIO, NULL); + if (req) + handshake_complete(req, -EIO, NULL); out_status: trace_handshake_cmd_accept_err(net, req, NULL, err); return err; From b913801ad9b9a51437d84d030ec6843e08976bd6 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 01:40:00 +0800 Subject: [PATCH 3260/3297] net/handshake: Take a long-lived file reference at submit [ Upstream commit 09dba37eee70d0596e26645015f1aa95a9848e9d ] handshake_nl_accept_doit() needs the file pointer backing req->hr_sk->sk_socket to survive the window between handshake_req_next() and the subsequent FD_PREPARE() and get_file(). The submit-side sock_hold() does not provide that. sk_refcnt keeps struct sock alive, but struct socket is owned by sock->file: when the consumer fputs the last file reference, sock_release() tears the socket down regardless of any sock_hold. Add an hr_file pointer to struct handshake_req and acquire an explicit reference on sock->file during handshake_req_submit(). handshake_complete() and handshake_req_cancel() release the reference on the completion-bit-winning path. The submit error path must also release the file reference, but after rhashtable insertion a concurrent handshake_req_cancel() can discover the request and race the error path. Gate the error-path cleanup -- sk_destruct restoration, fput, and request destruction -- with test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED), the same serialization handshake_complete() and handshake_req_cancel() already use. When cancel has already claimed ownership, the submit error path returns without touching the request; socket teardown handles final destruction. The accept-side dereferences are not yet retargeted; that change comes in the next patch. Signed-off-by: Chuck Lever Link: https://patch.msgid.link/20260525-handshake-file-pin-v3-4-66c616906ead@oracle.com Signed-off-by: Paolo Abeni (cherry picked from commit 09dba37eee70d0596e26645015f1aa95a9848e9d) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- net/handshake/handshake.h | 2 ++ net/handshake/netlink.c | 6 ------ net/handshake/request.c | 42 ++++++++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/net/handshake/handshake.h b/net/handshake/handshake.h index 2289b0e274f40..da61cadd1ad3e 100644 --- a/net/handshake/handshake.h +++ b/net/handshake/handshake.h @@ -24,6 +24,7 @@ enum hn_flags_bits { HANDSHAKE_F_NET_DRAINING, }; +struct file; struct handshake_proto; /* One handshake request */ @@ -32,6 +33,7 @@ struct handshake_req { struct rhash_head hr_rhash; unsigned long hr_flags; const struct handshake_proto *hr_proto; + struct file *hr_file; struct sock *hr_sk; void (*hr_odestruct)(struct sock *sk); diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 039344979de93..1a5821eb71847 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -210,12 +210,6 @@ static void __net_exit handshake_net_exit(struct net *net) while (!list_empty(&requests)) { req = list_first_entry(&requests, struct handshake_req, hr_list); list_del(&req->hr_list); - - /* - * Requests on this list have not yet been - * accepted, so they do not have an fd to put. - */ - handshake_complete(req, -ETIMEDOUT, NULL); } } diff --git a/net/handshake/request.c b/net/handshake/request.c index 62efb7e32730e..35bc6290e1203 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -215,9 +216,16 @@ EXPORT_SYMBOL_IF_KUNIT(handshake_req_next); * A zero return value from handshake_req_submit() means that * exactly one subsequent completion callback is guaranteed. * - * A negative return value from handshake_req_submit() means that - * no completion callback will be done and that @req has been - * destroyed. + * A negative return value from handshake_req_submit() guarantees that + * no completion callback will occur and that @req is no longer owned by + * the caller. If cancellation wins the completion race after the request + * has been published, final destruction is deferred until socket teardown. + * + * The caller must hold a reference on @sock->file for the duration + * of this call. Once the request is published to the accept side, a + * concurrent completion or cancellation may release the request's pin on + * @sock->file; the caller's reference is what keeps @sock->sk valid until + * handshake_req_submit() returns. */ int handshake_req_submit(struct socket *sock, struct handshake_req *req, gfp_t flags) @@ -236,6 +244,14 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, kfree(req); return -EINVAL; } + + /* + * Pin sock->file for the lifetime of the request so the + * accept side does not race a consumer that releases the + * socket while a handshake is pending. + */ + req->hr_file = get_file(sock->file); + req->hr_odestruct = req->hr_sk->sk_destruct; req->hr_sk->sk_destruct = handshake_sk_destruct; @@ -267,7 +283,11 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, goto out_err; } - /* Prevent socket release while a handshake request is pending */ + /* + * Pin struct sock so sk_destruct does not run until the + * handshake completion path releases it; struct socket is + * held separately via hr_file above. + */ sock_hold(req->hr_sk); trace_handshake_submit(net, req, req->hr_sk); @@ -276,10 +296,13 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, out_unlock: 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; trace_handshake_submit_err(net, req, req->hr_sk, ret); - handshake_req_destroy(req); + if (!test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) { + /* Restore original destructor so socket teardown still runs. */ + req->hr_sk->sk_destruct = req->hr_odestruct; + fput(req->hr_file); + handshake_req_destroy(req); + } return ret; } EXPORT_SYMBOL(handshake_req_submit); @@ -291,11 +314,15 @@ void handshake_complete(struct handshake_req *req, int status, struct net *net = sock_net(sk); if (!test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) { + struct file *file = req->hr_file; + trace_handshake_complete(net, req, sk, status); req->hr_proto->hp_done(req, status, info); /* Handshake request is no longer pending */ sock_put(sk); + + fput(file); } } EXPORT_SYMBOL_IF_KUNIT(handshake_complete); @@ -344,6 +371,7 @@ bool handshake_req_cancel(struct sock *sk) /* Handshake request is no longer pending */ sock_put(sk); + fput(req->hr_file); return true; } EXPORT_SYMBOL(handshake_req_cancel); From 68eba6519cbd6359fb554a9720f3a3b6b2eba23f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 01:40:02 +0800 Subject: [PATCH 3261/3297] net/handshake: hand off the pinned file reference to accept_doit [ Upstream commit f4251190e58b209999c1ba9e6d2976136a1be055 ] handshake_req_next() removes the request from the per-net pending list and drops hn_lock before handshake_nl_accept_doit() reads req->hr_sk->sk_socket and dereferences sock->file (once in FD_PREPARE() and again in get_file()). In that window a consumer running tls_handshake_cancel() followed by sockfd_put() (svc_sock_free) or __fput_sync() (xs_reset_transport) releases sock->file. sock_release() then runs sock_orphan(), zeroing sk_socket, and frees the struct socket. The accept-side code either reads NULL through sk_socket or chases freed memory. The submit-side sock_hold() does not prevent this. sk_refcnt protects struct sock, but struct socket and sock->file are independently refcounted via the file descriptor the consumer owns. Pinning sk leaves sock and sock->file unprotected. Retarget the accept-side dereferences at req->hr_file, which was pinned at submit time, instead of req->hr_sk->sk_socket->file. Pinning on its own is not sufficient: a consumer that cancels between handshake_req_next() returning and accept_doit reaching FD_PREPARE() takes the !remove_pending() branch in handshake_req_cancel() and drops hr_file before the accept side takes its own reference. Hand off an additional file reference inside handshake_req_next(), under hn_lock, so the accept side operates on a reference that no concurrent handshake_req_cancel() can revoke. FD_PREPARE() consumes that handed-off reference, either by transferring it to the new fd in fd_publish() or by dropping it in the cleanup destructor on error; the explicit get_file() that previously balanced FD_PREPARE() is therefore redundant and goes away. Update handshake_req_cancel_test2 and _test3 to simulate the FD_PREPARE() consumption with an fput() so the kunit file-count assertions stay balanced. Reported-by: Chris Mason 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-5-66c616906ead@oracle.com Signed-off-by: Paolo Abeni (cherry picked from commit f4251190e58b209999c1ba9e6d2976136a1be055) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- net/handshake/handshake-test.c | 8 ++++++++ net/handshake/netlink.c | 7 ++----- net/handshake/request.c | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/net/handshake/handshake-test.c b/net/handshake/handshake-test.c index df3948e807a0f..9cc7a95f41207 100644 --- a/net/handshake/handshake-test.c +++ b/net/handshake/handshake-test.c @@ -375,6 +375,10 @@ static void handshake_req_cancel_test2(struct kunit *test) /* Pretend to accept this request */ next = handshake_req_next(hn, HANDSHAKE_HANDLER_CLASS_TLSHD); KUNIT_ASSERT_PTR_EQ(test, req, next); + /* Simulate FD_PREPARE() consuming the file reference handed + * off by handshake_req_next(); see handshake_nl_accept_doit(). + */ + fput(filp); /* Act */ result = handshake_req_cancel(sock->sk); @@ -417,6 +421,10 @@ static void handshake_req_cancel_test3(struct kunit *test) /* Pretend to accept this request */ next = handshake_req_next(hn, HANDSHAKE_HANDLER_CLASS_TLSHD); KUNIT_ASSERT_PTR_EQ(test, req, next); + /* Simulate FD_PREPARE() consuming the file reference handed + * off by handshake_req_next(); see handshake_nl_accept_doit(). + */ + fput(filp); /* Pretend to complete this request */ handshake_complete(next, -ETIMEDOUT, NULL); diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 1a5821eb71847..21d6cbd52fcdb 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -92,7 +92,6 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) struct net *net = sock_net(skb->sk); struct handshake_net *hn = handshake_pernet(net); struct handshake_req *req = NULL; - struct socket *sock; int class, err; err = -EOPNOTSUPP; @@ -107,15 +106,13 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) err = -EAGAIN; req = handshake_req_next(hn, class); if (req) { - sock = req->hr_sk->sk_socket; - - FD_PREPARE(fdf, O_CLOEXEC, sock->file); + FD_PREPARE(fdf, O_CLOEXEC, req->hr_file); if (fdf.err) { + fput(req->hr_file); /* drop ref from handshake_req_next() */ err = fdf.err; goto out_complete; } - get_file(sock->file); /* FD_PREPARE() consumes a reference. */ err = req->hr_proto->hp_accept(req, info, fd_prepare_fd(fdf)); if (err) goto out_complete; /* Automatic cleanup handles fput */ diff --git a/net/handshake/request.c b/net/handshake/request.c index 35bc6290e1203..4bf98793491aa 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -178,6 +178,17 @@ static bool remove_pending(struct handshake_net *hn, struct handshake_req *req) return ret; } +/** + * handshake_req_next - Return the next queued handshake request + * @hn: per-net handshake state + * @class: handler class to match + * + * On a non-NULL return, the caller owns an extra reference + * on @req->hr_file. FD_PREPARE() consumes it on success; on + * the FD_PREPARE() failure path the caller must fput() it. + * + * Return: pointer to a removed handshake_req, or NULL. + */ struct handshake_req *handshake_req_next(struct handshake_net *hn, int class) { struct handshake_req *req, *pos; @@ -188,6 +199,13 @@ struct handshake_req *handshake_req_next(struct handshake_net *hn, int class) if (pos->hr_proto->hp_handler_class != class) continue; __remove_pending_locked(hn, pos); + /* Hand off a file reference to the accept side under + * hn_lock. A concurrent handshake_req_cancel() can drop + * hr_file before accept reaches FD_PREPARE(); this extra + * reference keeps the file alive until FD_PREPARE() takes + * ownership. + */ + get_file(pos->hr_file); req = pos; break; } From 6e7b52bd1394829c9cb4e5728dd64e41fa2d7d65 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 01:40:04 +0800 Subject: [PATCH 3262/3297] net/handshake: Close the submit-side sock_hold race [ Upstream commit 5da98f55b13173c08f003011b76531b25c821c07 ] handshake_req_submit() publishes the request via handshake_req_hash_add() and __add_pending_locked(), drops hn_lock, and calls handshake_genl_notify() (which can sleep) before taking sock_hold() on req->hr_sk. A fast tlshd ACCEPT followed by DONE can drive handshake_complete()'s sock_put() into the window between the spin_unlock and the late sock_hold(); on a system where the consumer's fd held the only sk reference, the late sock_hold() then operates on an sk whose refcount has reached zero. The preceding two patches install an explicit file reference on struct handshake_req. That file pins sock->file, which pins the embedded struct socket, which defers inet_release()'s sock_put(). As long as hr_file is held, sk cannot reach refcount zero from the consumer side, and the submit-side sock_hold() with its matching sock_put() calls in handshake_complete() and handshake_req_cancel() is now redundant. Drop all three. The file reference already keeps each request's socket alive, and the lifetime story is contained in a single get_file()/fput() pair. 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-6-66c616906ead@oracle.com Signed-off-by: Paolo Abeni (cherry picked from commit 5da98f55b13173c08f003011b76531b25c821c07) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- net/handshake/request.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/net/handshake/request.c b/net/handshake/request.c index 4bf98793491aa..035bb2d7471c9 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -301,13 +301,6 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, goto out_err; } - /* - * Pin struct sock so sk_destruct does not run until the - * handshake completion path releases it; struct socket is - * held separately via hr_file above. - */ - sock_hold(req->hr_sk); - trace_handshake_submit(net, req, req->hr_sk); return 0; @@ -337,9 +330,6 @@ void handshake_complete(struct handshake_req *req, int status, trace_handshake_complete(net, req, sk, status); req->hr_proto->hp_done(req, status, info); - /* Handshake request is no longer pending */ - sock_put(sk); - fput(file); } } @@ -387,8 +377,6 @@ bool handshake_req_cancel(struct sock *sk) out_true: trace_handshake_cancel(net, req, sk); - /* Handshake request is no longer pending */ - sock_put(sk); fput(req->hr_file); return true; } From 2bf24a7e190aae0ea47c78099938ae056c622e44 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 01:40:06 +0800 Subject: [PATCH 3263/3297] net/handshake: Drain pending requests at net namespace exit [ Upstream commit ea5fe6a73ca57e5150b8a38b341aef2636eb72f0 ] The arguments to list_splice_init() in handshake_net_exit() are reversed. The call moves the local empty "requests" list onto hn->hn_requests, leaving the local list empty, so the subsequent drain loop runs zero iterations. Pending handshake requests that had not yet been accepted are not torn down when the net namespace is destroyed; each one keeps a reference on a socket file and on the handshake_req allocation. Pass the source and destination in the documented order (list_splice_init(list, head) moves list onto head) so the pending list is transferred to the local scratch list and drained through handshake_complete(). Fixing the splice direction exposes a list-corruption race. After the splice each req->hr_list still has non-empty link pointers, threading the stack-local scratch list rather than hn_requests. A concurrent handshake_req_cancel() -- for example, from sunrpc's TLS timeout on a kernel socket whose netns reference was not taken -- finds the request through the rhashtable, calls remove_pending(), and sees !list_empty(&req->hr_list). __remove_pending_locked() then list_del_init()s an entry off the scratch list while the drain iterates, corrupting it. The same call arriving after the drain loop has run list_del() on an entry hits LIST_POISON instead. Have remove_pending() check HANDSHAKE_F_NET_DRAINING under hn_lock and report not-found when drain is in progress. The drain has already taken ownership; handshake_complete()'s existing test_and_set on HANDSHAKE_F_REQ_COMPLETED still arbitrates between drain and cancel for who calls the consumer's hp_done. Use list_del_init() rather than list_del() in the drain so req->hr_list does not carry LIST_POISON after drain releases the entry. The DRAINING guard in remove_pending() makes cancel return false, but cancel still falls through to test_and_set_bit on HANDSHAKE_F_REQ_COMPLETED and drops the request's hr_file reference. Without another pin, if that is the last reference, sk_destruct frees the request while it is still linked on the drain loop's local list. Pin each request's hr_file under hn_lock before releasing the list, and drop that drain pin after the loop finishes with the request. 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-8-66c616906ead@oracle.com Signed-off-by: Paolo Abeni (cherry picked from commit ea5fe6a73ca57e5150b8a38b341aef2636eb72f0) Signed-off-by: Wentao Guan Signed-off-by: Sasha Levin --- net/handshake/netlink.c | 10 ++++++++-- net/handshake/request.c | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 21d6cbd52fcdb..3fd4fef9bab1a 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -201,13 +201,19 @@ static void __net_exit handshake_net_exit(struct net *net) */ spin_lock_bh(&hn->hn_lock); set_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags); - list_splice_init(&requests, &hn->hn_requests); + list_splice_init(&hn->hn_requests, &requests); + list_for_each_entry(req, &requests, hr_list) + get_file(req->hr_file); spin_unlock_bh(&hn->hn_lock); while (!list_empty(&requests)) { + struct file *file; + req = list_first_entry(&requests, struct handshake_req, hr_list); - list_del(&req->hr_list); + file = req->hr_file; + list_del_init(&req->hr_list); handshake_complete(req, -ETIMEDOUT, NULL); + fput(file); } } diff --git a/net/handshake/request.c b/net/handshake/request.c index 035bb2d7471c9..d4a3a50f8a3b5 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -163,13 +163,16 @@ static void __remove_pending_locked(struct handshake_net *hn, * otherwise %false. * * If @req was on a pending list, it has not yet been accepted. + * Returns %false when the net namespace is draining; the drain + * loop has taken ownership of the pending list. */ static bool remove_pending(struct handshake_net *hn, struct handshake_req *req) { bool ret = false; spin_lock_bh(&hn->hn_lock); - if (!list_empty(&req->hr_list)) { + if (!test_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags) && + !list_empty(&req->hr_list)) { __remove_pending_locked(hn, req); ret = true; } From 58d9caa64f9c61721a0c35dc080b752bbb5ba625 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 30 Jul 2026 15:29:51 -0400 Subject: [PATCH 3264/3297] usb: typec: ucsi: split connector lock classes [ Upstream commit 8c22256bbafad3dc5fdbe9f684d045b67ff06a68 ] Lockdep detects a possible recursive locking scenario during ucsi init: [ 5.418616] ============================================ [ 5.418634] WARNING: possible recursive locking detected [ 5.418706] -------------------------------------------- [ 5.418725] kworker/4:1/82 is trying to acquire lock: [ 5.418759] ffff888119a34648 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi] [ 5.418801] but task is already holding lock: [ 5.418835] ffff888119a34080 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi] [ 5.418884] other info that might help us debug this: [ 5.418904] Possible unsafe locking scenario: [ 5.418937] CPU0 [ 5.418956] ---- [ 5.418991] lock(&con->lock); [ 5.419013] lock(&con->lock); [ 5.419033] *** DEADLOCK *** [ 5.419387] Call Trace: [ 5.419406] [ 5.419425] dump_stack_lvl+0x61/0xa0 [ 5.419448] print_deadlock_bug+0x4a6/0x650 [ 5.419483] __lock_acquire+0x62b6/0x7f50 [ 5.419507] lock_acquire+0x11b/0x390 [ 5.419654] __mutex_lock+0xbc/0xcd0 [ 5.419741] ucsi_init_work+0x1a78/0x2eb0 [ 5.419785] ? worker_thread+0xf53/0x2bc0 [ 5.419819] worker_thread+0xff4/0x2bc0 [ 5.419842] kthread+0x2a7/0x330 [ 5.419863] ? __pfx_worker_thread+0x10/0x10 [ 5.419896] ? __pfx_kthread+0x10/0x10 [ 5.419916] ret_from_fork+0x38/0x70 [ 5.419936] ? __pfx_kthread+0x10/0x10 [ 5.419969] ret_from_fork_asm+0x1b/0x30 [ 5.419991] [ 5.420009] ---[ end trace 0000000000000000 ]--- The problem is that all connector locks belong to the same lockdep lock class, so the following loop: for (i = 0; i < ucsi->cap.num_connectors; i++) ucsi_register_port(connector[i]) mutex_lock(&connector[i]->lock) looks like a recursive acquire of the same mutex. Put each connector lock into a dedicated lock class so that lockdep doesn't see it as a possible recursion. Signed-off-by: Sergey Senozhatsky Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260515060042.136083-1-senozhatsky@chromium.org Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 8 ++++++++ drivers/usb/typec/ucsi/ucsi.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 01a15ab5a734a..74a7d2ebe1de6 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1741,6 +1741,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) INIT_WORK(&con->work, ucsi_handle_connector_change); init_completion(&con->complete); mutex_init(&con->lock); + lockdep_set_class(&con->lock, &con->lock_key); INIT_LIST_HEAD(&con->partner_tasks); con->ucsi = ucsi; @@ -1981,6 +1982,9 @@ static int ucsi_init(struct ucsi *ucsi) goto err_reset; } + for (i = 0; i < ucsi->cap.num_connectors; i++) + lockdep_register_key(&connector[i].lock_key); + /* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) { connector[i].num = i + 1; @@ -2010,6 +2014,9 @@ static int ucsi_init(struct ucsi *ucsi) return 0; err_unregister: + for (i = 0; i < ucsi->cap.num_connectors; i++) + lockdep_unregister_key(&connector[i].lock_key); + for (con = connector; con->port; con++) { if (con->wq) destroy_workqueue(con->wq); @@ -2280,6 +2287,7 @@ void ucsi_unregister(struct ucsi *ucsi) usb_power_delivery_unregister(ucsi->connector[i].pd); ucsi->connector[i].pd = NULL; typec_unregister_port(ucsi->connector[i].port); + lockdep_unregister_key(&ucsi->connector[i].lock_key); } kfree(ucsi->connector); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index a9144c2997391..520a1a4f91143 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -502,6 +502,7 @@ struct ucsi_connector { struct ucsi *ucsi; struct mutex lock; /* port lock */ + struct lock_class_key lock_key; struct work_struct work; struct completion complete; struct workqueue_struct *wq; From 3f7b3728dd9011c915cbeaea77274ebe8366550d Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Thu, 30 Jul 2026 15:29:52 -0400 Subject: [PATCH 3265/3297] usb: typec: ucsi: Fix race condition and ordering in port unregistration [ Upstream commit 7aa7d4bf9d3fa9a6a47b640ad103ab433b7ff261 ] A synchronization issue exists during port unregistration where pending partner work items can race against workqueue destruction, leading to use-after-free conditions: cros_ec_ucsi cros_ec_ucsi.3.auto: error -ETIMEDOUT: PPM init failed BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:__queue_work+0x83/0x4a0 Call Trace: __cfi_delayed_work_timer_fn+0x10/0x10 run_timer_softirq+0x3b6/0xbd0 sched_clock_cpu+0xc/0x110 irq_exit_rcu+0x18d/0x330 fred_sysvec_apic_timer_interrupt+0x5e/0x80 Fix this by ensuring strict ordering and proper serialization during teardown: 1. Move ucsi_unregister_partner() to the beginning of the teardown sequence and protect it under the connector mutex lock. 2. Ensure all pending partner tasks are explicitly flushed and finished before the workqueue is destroyed. 3. Switch from mod_delayed_work() to a cancel_delayed_work() and queue_delayed_work() sequence. This guarantees that items currently marked as pending won't be scheduled an additional time, preventing a double release of resources which leads to the following crash: Oops: general protection fault, probably for non-canonical address 0xdead000000000122: 0000 [#1] SMP NOPTI Workqueue: cros_ec_ucsi.3.auto-con2 ucsi_poll_worker RIP: 0010:ucsi_poll_worker+0x65/0x1e0 Call Trace: process_scheduled_works+0x218/0x6d0 worker_thread+0x188/0x3f0 __cfi_worker_thread+0x10/0x10 kthread+0x226/0x2a0 To ensure these rules are applied identically across both the normal teardown and the ucsi_init() error paths, consolidate the cleanup logic into a new helper, ucsi_unregister_port(). Cc: stable Fixes: b9aa02ca39a4 ("usb: typec: ucsi: Add polling mechanism for partner tasks like alt mode checking") Fixes: b13abcb7ddd8 ("usb: typec: ucsi: Fix NULL pointer access") Fixes: fac4b8633fd6 ("usb: ucsi: Ensure connector delayed work items are flushed") Signed-off-by: Andrei Kuchynski Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260707141736.1635698-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 82 +++++++++++++++++------------------ 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 74a7d2ebe1de6..043a00a4f1c27 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1891,6 +1891,42 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) return ret; } +static void ucsi_unregister_port(struct ucsi_connector *con) +{ + struct ucsi_work *uwork; + + if (con->wq) { + mutex_lock(&con->lock); + ucsi_unregister_partner(con); + /* + * queue delayed items immediately so they can execute + * and free themselves before the wq is destroyed + */ + list_for_each_entry(uwork, &con->partner_tasks, node) { + if (cancel_delayed_work(&uwork->work)) + queue_delayed_work(con->wq, &uwork->work, 0); + } + mutex_unlock(&con->lock); + + destroy_workqueue(con->wq); + con->wq = NULL; + } else { + ucsi_unregister_partner(con); + } + + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); + ucsi_unregister_port_psy(con); + + usb_power_delivery_unregister_capabilities(con->port_sink_caps); + con->port_sink_caps = NULL; + usb_power_delivery_unregister_capabilities(con->port_source_caps); + con->port_source_caps = NULL; + usb_power_delivery_unregister(con->pd); + con->pd = NULL; + typec_unregister_port(con->port); + con->port = NULL; +} + static u64 ucsi_get_supported_notifications(struct ucsi *ucsi) { u16 features = ucsi->cap.features; @@ -2017,22 +2053,8 @@ static int ucsi_init(struct ucsi *ucsi) for (i = 0; i < ucsi->cap.num_connectors; i++) lockdep_unregister_key(&connector[i].lock_key); - for (con = connector; con->port; con++) { - if (con->wq) - destroy_workqueue(con->wq); - ucsi_unregister_partner(con); - ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); - ucsi_unregister_port_psy(con); - - usb_power_delivery_unregister_capabilities(con->port_sink_caps); - con->port_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(con->port_source_caps); - con->port_source_caps = NULL; - usb_power_delivery_unregister(con->pd); - con->pd = NULL; - typec_unregister_port(con->port); - con->port = NULL; - } + for (con = connector; con->port; con++) + ucsi_unregister_port(con); kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); @@ -2260,33 +2282,7 @@ void ucsi_unregister(struct ucsi *ucsi) for (i = 0; i < ucsi->cap.num_connectors; i++) { cancel_work_sync(&ucsi->connector[i].work); - - if (ucsi->connector[i].wq) { - struct ucsi_work *uwork; - - mutex_lock(&ucsi->connector[i].lock); - /* - * queue delayed items immediately so they can execute - * and free themselves before the wq is destroyed - */ - list_for_each_entry(uwork, &ucsi->connector[i].partner_tasks, node) - mod_delayed_work(ucsi->connector[i].wq, &uwork->work, 0); - mutex_unlock(&ucsi->connector[i].lock); - destroy_workqueue(ucsi->connector[i].wq); - } - - ucsi_unregister_partner(&ucsi->connector[i]); - ucsi_unregister_altmodes(&ucsi->connector[i], - UCSI_RECIPIENT_CON); - ucsi_unregister_port_psy(&ucsi->connector[i]); - - usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps); - ucsi->connector[i].port_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps); - ucsi->connector[i].port_source_caps = NULL; - usb_power_delivery_unregister(ucsi->connector[i].pd); - ucsi->connector[i].pd = NULL; - typec_unregister_port(ucsi->connector[i].port); + ucsi_unregister_port(&ucsi->connector[i]); lockdep_unregister_key(&ucsi->connector[i].lock_key); } From 276420a86e75f57642b633c1f7e4ee96a6ce9708 Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 4 Aug 2026 06:47:12 -0400 Subject: [PATCH 3266/3297] media: chips-media: wave5: Support CBP profile [ Upstream commit f8505f9d6b223a26854003bfdba1a0772457886d ] Constrained Baseline Profile (CBP) and Baseline Profile (BP) have been treated as the same. Introduce the ability to differentiate between the two. Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Tested-by: Brandon Brnich Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/chips-media/wave5/wave5-hw.c | 3 +++ drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c | 5 ++++- drivers/media/platform/chips-media/wave5/wave5-vpuapi.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index d94cf84c3ee5f..fd3c3c2168158 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -1762,6 +1762,9 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) (p_param->skip_intra_trans << 25) | (p_param->strong_intra_smooth_enable << 27) | (p_param->en_still_picture << 30); + else if (inst->std == W_AVC_ENC) + reg_val |= (p_param->constraint_set1_flag << 29); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val); reg_val = (p_param->lossless_enable) | diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index 50cba21fdcdce..03687a1e23c71 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -915,6 +915,8 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: inst->enc_param.profile = H264_PROFILE_BP; inst->bit_depth = 8; + if (ctrl->val == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) + inst->enc_param.constraint_set1_flag = 1; break; case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: inst->enc_param.profile = H264_PROFILE_MP; @@ -1190,6 +1192,7 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, open_param->wave_param.intra_period = input.avc_idr_period; } } else { + open_param->wave_param.constraint_set1_flag = input.constraint_set1_flag; open_param->wave_param.avc_idr_period = input.avc_idr_period; } open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode; @@ -1662,7 +1665,7 @@ static int wave5_vpu_open_enc(struct file *filp) -6, 6, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, - 0, 1, 1, 1); + 0, 1, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 0); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index bc101397204da..8112d4899ce46 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -570,6 +570,7 @@ struct enc_wave_param { u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */ + u32 constraint_set1_flag: 1; /* enable CBP */ }; struct enc_open_param { From f730ee0ef8facc47baede1e5778baa5c97d9d1b1 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 4 Aug 2026 20:08:32 -0400 Subject: [PATCH 3267/3297] media: qcom: camss: csid-340: Fix unused variables [ Upstream commit 0b61ce8dc4a71ea7baa19275c655677c612dde6d ] The CSID driver has some unused variables and function parameters that are no longer needed (due to refactoring). Clean up those unused elements: - Remove the `vc` parameter from `__csid_configure_rx()`. - Drop the unused `lane_cnt` variable. - Adjust call to `__csid_configure_rx()` accordingly. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil Stable-dep-of: cc1c35619c98 ("media: qcom: camss: Fix RDI streaming for CSID 340") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/qcom/camss/camss-csid-340.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 22a30510fb792..2b50f9b96a34e 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -55,8 +55,7 @@ #define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY 0 #define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY 1 -static void __csid_configure_rx(struct csid_device *csid, - struct csid_phy_config *phy, int vc) +static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) { u32 val; @@ -81,13 +80,9 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); - u8 lane_cnt = csid->phy.lane_cnt; u8 dt_id; u32 val; - if (!lane_cnt) - lane_cnt = 4; - /* * DT_ID is a two bit bitfield that is concatenated with * the four least significant bits of the five bit VC @@ -120,10 +115,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) { int i; + __csid_configure_rx(csid, &csid->phy); + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); __csid_ctrl_rdi(csid, enable, i); } } From 5c6d5d2484cab62e6c42511e060c60b138ba4838 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 4 Aug 2026 20:08:33 -0400 Subject: [PATCH 3268/3297] media: qcom: camss: Fix RDI streaming for CSID 340 [ Upstream commit cc1c35619c9895f918f9e388e3caa6ab2ff2fbe7 ] Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID 340 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: f0fc808a466a ("media: qcom: camss: Add CSID 340 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../media/platform/qcom/camss/camss-csid-340.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 2b50f9b96a34e..0231985746edf 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -74,9 +74,9 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -88,14 +88,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type); @@ -105,10 +105,11 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 if (enable) val |= CSID_RDI_CFG0_ENABLE; - dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x vc=%u)\n", - csid->id, enable ? "enable" : "disable", format->data_type, vc); + dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n", + csid->id, enable ? "enable" : "disable", format->data_type, + port, vc); - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -117,9 +118,10 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_rx(csid, &csid->phy); + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); + __csid_configure_rdi_stream(csid, enable, i, 0); __csid_ctrl_rdi(csid, enable, i); } } From d6f322d68abfdb89926fd9ad9291b65cc0bb6cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Thu, 6 Aug 2026 08:46:34 -0400 Subject: [PATCH 3269/3297] media: uapi: rkisp: Correct name version enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c4c01c4fd4a3916ffdfb35ad9f511c48e289f51c ] The name of the enum to hold the mapping of parameter buffer versions have a typo in the name, correct it. While this is a uAPI header the impact should be minimal as the enum is only used as a collection for the one version number supported. Fixes: e9d05e9d5db1 ("media: uapi: rkisp1-config: Add extensible params format") Cc: stable@vger.kernel.org Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260501190339.3449193-1-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil [ Adjusted context to keep the tree's `RKISP1_EXT_PARAM_BUFFER_V1 = 1` initializer instead of upstream's `V4L2_ISP_PARAMS_VERSION_V1`, which does not exist in this tree. ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/rkisp1-config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index 3b060ea6eed71..810e874639ca0 100644 --- a/include/uapi/linux/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -1583,11 +1583,11 @@ struct rkisp1_ext_params_wdr_config { sizeof(struct rkisp1_ext_params_wdr_config)) /** - * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version + * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version * * @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters */ -enum rksip1_ext_param_buffer_version { +enum rkisp1_ext_param_buffer_version { RKISP1_EXT_PARAM_BUFFER_V1 = 1, }; @@ -1667,7 +1667,7 @@ enum rksip1_ext_param_buffer_version { * +---------------------------------------------------------------------+ * * @version: The RkISP1 extensible parameters buffer version, see - * :c:type:`rksip1_ext_param_buffer_version` + * :c:type:`rkisp1_ext_param_buffer_version` * @data_size: The RkISP1 configuration data effective size, excluding this * header * @data: The RkISP1 extensible configuration data blocks From 02d378828af8bb74f6c2f4d2bee3c77cf16c861e Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 6 Aug 2026 09:17:40 -0400 Subject: [PATCH 3270/3297] wifi: brcmfmac: drain bus_reset work on device removal [ Upstream commit 43b25879f004c98defa2776bedc6ca4763c51945 ] brcmf_fw_crashed() and the debugfs "reset" entry both schedule drvr->bus_reset, whose callback recovers drvr through container_of() and dereferences it. The removal path frees drvr (brcmf_free -> wiphy_free) without draining the work, so a bus_reset callback pending or running during removal can outlive drvr. Cancellation cannot live in brcmf_detach() or brcmf_free(): the work callback reaches teardown through the bus .reset op (PCIe brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset -> brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for the running work and deadlock. Add a per-bus mutex (bus_reset_lock) and route all arming through brcmf_bus_schedule_reset(), which under the lock skips when the bus is marked removing. Each bus remove entry calls brcmf_bus_cancel_reset_work(), which under the same lock sets removing and cancels the work. Holding the mutex across cancel_work_sync() makes the set-removing + drain step atomic. Every producer reaches the arming path from process context -- the PCIe firmware-halt notification runs in the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail path runs from the data workqueue -- so the mutex is taken only in sleepable contexts. Where applicable the remove entry first stops the firmware-crash producer: on PCIe mask the mailbox and synchronize_irq; on SDIO unregister the bus interrupt and cancel the data worker, which also reports firmware halts through brcmf_fw_crashed(). The mutex is initialized at bus allocation. The SDIO suspend power-off path frees drvr through the same brcmf_sdiod_remove() and takes the same lock; resume re-allows the work only on a successful re-probe. Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire before brcmf_attach() wires up drvr, and it dereferences drvr (bphy_err/brcmf_dev_coredump) before reaching the arming gate. The bus_reset work is shared across buses, so the drain is applied to every remove path: PCIe (the .reset op introduced by the Fixes commit), SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the debugfs "reset" entry). cancel_work_sync() drains a running or pending bus_reset work item before removal frees drvr, and patch 1/2 makes the scratch-buffer release safe when reset teardown has already released those DMA buffers. This patch fixes the lifetime of the bus_reset work item itself. It does not attempt to address the separate, pre-existing lifetime of the asynchronous firmware completion started by the PCIe reset path. That callback needs its own lifetime/ownership protocol and is being tracked separately. This issue was found by an in-house static analysis tool. Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Assisted-by: Codex:gpt-5.6 Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260718024353.3147201-3-fanwu01@zju.edu.cn Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 13 ++++++ .../broadcom/brcm80211/brcmfmac/bus.h | 6 +++ .../broadcom/brcm80211/brcmfmac/core.c | 46 +++++++++++++++++-- .../broadcom/brcm80211/brcmfmac/pcie.c | 6 +++ .../broadcom/brcm80211/brcmfmac/sdio.c | 6 +++ .../broadcom/brcm80211/brcmfmac/sdio.h | 1 + .../broadcom/brcm80211/brcmfmac/usb.c | 3 ++ 7 files changed, 77 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 0beb38047df51..20b890f7ae7ff 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -1070,6 +1070,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, bus_if = kzalloc(sizeof(*bus_if), GFP_KERNEL); if (!bus_if) return -ENOMEM; + mutex_init(&bus_if->bus_reset_lock); sdiodev = kzalloc(sizeof(*sdiodev), GFP_KERNEL); if (!sdiodev) { kfree(bus_if); @@ -1131,6 +1132,14 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) if (func->num != 1) return; + /* Drain bus_reset before the shared brcmf_sdiod_remove() + * teardown, which the SDIO reset callback also reaches. The + * data worker can arm bus_reset via brcmf_fw_crashed(); cancel + * it first. + */ + brcmf_sdio_cancel_datawork(sdiodev->bus); + brcmf_bus_cancel_reset_work(bus_if); + /* only proceed with rest of cleanup if func 1 */ brcmf_sdiod_remove(sdiodev); @@ -1205,6 +1214,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev) } else { /* power will be cut so remove device, probe again in resume */ brcmf_sdiod_intr_unregister(sdiodev); + brcmf_sdio_cancel_datawork(sdiodev->bus); + brcmf_bus_cancel_reset_work(bus_if); ret = brcmf_sdiod_remove(sdiodev); if (ret) brcmf_err("Failed to remove device on suspend\n"); @@ -1230,6 +1241,8 @@ static int brcmf_ops_sdio_resume(struct device *dev) ret = brcmf_sdiod_probe(sdiodev); if (ret) brcmf_err("Failed to probe device on resume\n"); + else + brcmf_bus_allow_reset_work(bus_if); } else { if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported) disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index fe31051a9e11b..9371c1489948c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */ @@ -179,6 +180,8 @@ struct brcmf_bus { enum brcmf_fwvendor fwvid; bool always_use_fws_queue; bool wowl_supported; + bool removing; /* device removal in progress; quiesce async work */ + struct mutex bus_reset_lock; const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; @@ -186,6 +189,9 @@ struct brcmf_bus { struct list_head list; }; +void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if); +void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if); + /* * callback wrappers */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 862a0336a0b59..169e62b9d7731 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1167,6 +1167,35 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data) return 0; } +/* + * Serialize arming from debugfs reset and brcmf_fw_crashed() against + * teardown. The remove path sets ->removing and drains the work while + * holding bus_reset_lock, so a racing armer is either drained or skips it. + */ +static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing) + schedule_work(&bus_if->drvr->bus_reset); + mutex_unlock(&bus_if->bus_reset_lock); +} + +void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + bus_if->removing = true; + if (bus_if->drvr) + cancel_work_sync(&bus_if->drvr->bus_reset); + mutex_unlock(&bus_if->bus_reset_lock); +} + +void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + bus_if->removing = false; + mutex_unlock(&bus_if->bus_reset_lock); +} + static void brcmf_core_bus_reset(struct work_struct *work) { struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, @@ -1187,7 +1216,7 @@ static ssize_t bus_reset_write(struct file *file, const char __user *user_buf, if (value != 1) return -EINVAL; - schedule_work(&drvr->bus_reset); + brcmf_bus_schedule_reset(drvr->bus_if); return count; } @@ -1417,14 +1446,23 @@ void brcmf_dev_coredump(struct device *dev) void brcmf_fw_crashed(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_pub *drvr; + + /* May fire before brcmf_attach() wires up drvr, or after removal + * has cleared it; guard the derefs below (and the arming gate in + * brcmf_bus_schedule_reset() already checks drvr/->removing). + */ + if (!bus_if) + return; + drvr = bus_if->drvr; + if (!drvr) + return; bphy_err(drvr, "Firmware has halted or crashed\n"); brcmf_dev_coredump(dev); - if (drvr->bus_reset.func) - schedule_work(&drvr->bus_reset); + brcmf_bus_schedule_reset(bus_if); } void brcmf_detach(struct device *dev) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index fe4f23decc705..d2a1770540b00 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2504,6 +2504,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = -ENOMEM; goto fail; } + mutex_init(&bus->bus_reset_lock); bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL); if (!bus->msgbuf) { ret = -ENOMEM; @@ -2599,6 +2600,11 @@ brcmf_pcie_remove(struct pci_dev *pdev) if (devinfo->ci) brcmf_pcie_intr_disable(devinfo); + if (devinfo->irq_allocated) + synchronize_irq(pdev->irq); + + brcmf_bus_cancel_reset_work(bus); + brcmf_detach(&pdev->dev); brcmf_free(&pdev->dev); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 9ab62a2667c7b..936df04504ec0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4560,6 +4560,12 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) return ret; } +void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus) +{ + if (bus) + cancel_work_sync(&bus->datawork); +} + /* Detach and free everything */ void brcmf_sdio_remove(struct brcmf_sdio *bus) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 80180d5c6c879..b93d153a89634 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev); int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus, bool in_isr); +void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active); void brcmf_sdio_wowl_config(struct device *dev, bool enabled); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index f0129d10d2b95..b6b5f130c4398 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1260,6 +1260,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo, ret = -ENOMEM; goto fail; } + mutex_init(&bus->bus_reset_lock); bus->dev = dev; bus_pub->bus = bus; @@ -1329,6 +1330,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) return; brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo); + brcmf_bus_cancel_reset_work(devinfo->bus_pub.bus); + brcmf_detach(devinfo->dev); brcmf_free(devinfo->dev); kfree(devinfo->bus_pub.bus); From 165613191ad9d034bf17c00e3a142f9561597ec5 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 6 Aug 2026 23:34:59 -0400 Subject: [PATCH 3271/3297] userfaultfd: prevent registration of special VMAs [ Upstream commit 3c58f641e813c3c71039f8fd4d4e2a3aab713288 ] Vova Tokarev says: userfaultfd allows registration on shadow stack VMAs. With userfaultfd access, you can register on the shadow stack, discard a page ... and inject a page with chosen return addresses via UFFDIO_COPY. Update vma_can_userfault() to reject VM_SHADOW_STACK. While on it, also reject VM_SPECIAL so that if a driver would implement vm_uffd_ops, it wouldn't be possible to register special VMAs with userfaultfd. Since VM_SPECIAL includes VM_DONTEXPAND which is set but hugetlb, exclude hugetlb VMAs from the check for VM_SPECIAL. Link: https://lore.kernel.org/20260618095017.2553004-1-rppt@kernel.org Fixes: 54007f818206 ("mm: Introduce VM_SHADOW_STACK for shadow stack memory") Signed-off-by: Mike Rapoport (Microsoft) Reported-by: vova tokarev Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Cc: Al Viro Cc: Christian Brauner Cc: Jan Kara Cc: Linus Torvalds Cc: Mike Rapoport Cc: Oleg Nesterov Cc: Peter Xu Cc: Signed-off-by: Andrew Morton Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/userfaultfd_k.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index c0e716aec26aa..796a380664bd8 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -214,7 +214,10 @@ static inline bool vma_can_userfault(struct vm_area_struct *vma, { vm_flags &= __VM_UFFD_FLAGS; - if (vma->vm_flags & VM_DROPPABLE) + if (vma->vm_flags & (VM_DROPPABLE | VM_SHADOW_STACK)) + return false; + + if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL)) return false; if ((vm_flags & VM_UFFD_MINOR) && From 3f58077457985c622c014f438d8e00dbd03db338 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 30 Jul 2026 22:41:00 -0400 Subject: [PATCH 3272/3297] drm/fb-helper: Allocate and release fb_info in single place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 63c971af40365ee706c7e24f6a7900d693518f09 ] Move the calls to drm_fb_helper_alloc_info() from drivers into a single place in fbdev helpers. Allocates struct fb_info for a new framebuffer device. Then call drm_fb_helper_single_fb_probe() to create an fbdev screen buffer. Also release the instance on errors by calling drm_fb_helper_release_info(). Simplifies the code and fixes the error cleanup for some of the drivers. Regular release of the struct fb_info instance still happens in drm_fb_helper_fini() as before. v2: - remove error rollback in driver implementations (kernel test robot) - initialize info in TTM implementation (kernel test robot) Signed-off-by: Thomas Zimmermann Acked-by: Christian König # radeon Acked-by: Dmitry Baryshkov # msm Acked-by: Javier Martinez Canillas Link: https://patch.msgid.link/20251027081245.80262-1-tzimmermann@suse.de Stable-dep-of: a18b6e30ecd6 ("drm/tegra: fbdev: Remove offset into framebuffer memory") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/armada/armada_fbdev.c | 12 +------ drivers/gpu/drm/drm_fb_helper.c | 39 +++++++--------------- drivers/gpu/drm/drm_fbdev_dma.c | 12 ++----- drivers/gpu/drm/drm_fbdev_shmem.c | 12 ++----- drivers/gpu/drm/drm_fbdev_ttm.c | 12 ++----- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 9 +---- drivers/gpu/drm/gma500/fbdev.c | 12 +------ drivers/gpu/drm/i915/display/intel_fbdev.c | 9 +---- drivers/gpu/drm/msm/msm_fbdev.c | 9 +---- drivers/gpu/drm/omapdrm/omap_fbdev.c | 9 +---- drivers/gpu/drm/radeon/radeon_fbdev.c | 13 +------- drivers/gpu/drm/tegra/fbdev.c | 9 +---- include/drm/drm_fb_helper.h | 12 ------- 13 files changed, 26 insertions(+), 143 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index cb53cc91bafb0..4a8c432e311ee 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -45,10 +45,10 @@ int armada_fbdev_driver_fbdev_probe(struct drm_fb_helper *fbh, struct drm_fb_helper_surface_size *sizes) { struct drm_device *dev = fbh->dev; + struct fb_info *info = fbh->info; struct drm_mode_fb_cmd2 mode; struct armada_framebuffer *dfb; struct armada_gem_object *obj; - struct fb_info *info; int size, ret; void *ptr; @@ -92,12 +92,6 @@ int armada_fbdev_driver_fbdev_probe(struct drm_fb_helper *fbh, if (IS_ERR(dfb)) return PTR_ERR(dfb); - info = drm_fb_helper_alloc_info(fbh); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto err_fballoc; - } - info->fbops = &armada_fb_ops; info->fix.smem_start = obj->phys_addr; info->fix.smem_len = obj->obj.size; @@ -113,8 +107,4 @@ int armada_fbdev_driver_fbdev_probe(struct drm_fb_helper *fbh, (unsigned long long)obj->phys_addr); return 0; - - err_fballoc: - dfb->fb.funcs->destroy(&dfb->fb); - return ret; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0b3ee008523d1..18a00de2ec1fd 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -489,20 +489,7 @@ int drm_fb_helper_init(struct drm_device *dev, } EXPORT_SYMBOL(drm_fb_helper_init); -/** - * drm_fb_helper_alloc_info - allocate fb_info and some of its members - * @fb_helper: driver-allocated fbdev helper - * - * A helper to alloc fb_info and the member cmap. Called by the driver - * within the struct &drm_driver.fbdev_probe callback function. Drivers do - * not need to release the allocated fb_info structure themselves, this is - * automatically done when calling drm_fb_helper_fini(). - * - * RETURNS: - * fb_info pointer if things went okay, pointer containing error code - * otherwise - */ -struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper) +static struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper) { struct device *dev = fb_helper->dev->dev; struct fb_info *info; @@ -529,17 +516,8 @@ struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper) framebuffer_release(info); return ERR_PTR(ret); } -EXPORT_SYMBOL(drm_fb_helper_alloc_info); -/** - * drm_fb_helper_release_info - release fb_info and its members - * @fb_helper: driver-allocated fbdev helper - * - * A helper to release fb_info and the member cmap. Drivers do not - * need to release the allocated fb_info structure themselves, this is - * automatically done when calling drm_fb_helper_fini(). - */ -void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper) +static void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper) { struct fb_info *info = fb_helper->info; @@ -552,7 +530,6 @@ void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper) fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } -EXPORT_SYMBOL(drm_fb_helper_release_info); /** * drm_fb_helper_unregister_info - unregister fb_info framebuffer device @@ -1813,6 +1790,11 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper) height = dev->mode_config.max_height; drm_client_modeset_probe(&fb_helper->client, width, height); + + info = drm_fb_helper_alloc_info(fb_helper); + if (IS_ERR(info)) + return PTR_ERR(info); + ret = drm_fb_helper_single_fb_probe(fb_helper); if (ret < 0) { if (ret == -EAGAIN) { @@ -1821,13 +1803,12 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper) } mutex_unlock(&fb_helper->lock); - return ret; + goto err_drm_fb_helper_release_info; } drm_setup_crtcs_fb(fb_helper); fb_helper->deferred_setup = false; - info = fb_helper->info; info->var.pixclock = 0; /* Need to drop locks to avoid recursive deadlock in @@ -1850,6 +1831,10 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper) mutex_unlock(&kernel_fb_helper_lock); return 0; + +err_drm_fb_helper_release_info: + drm_fb_helper_release_info(fb_helper); + return ret; } /** diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c index 8bd626ef16c71..7b77c801a63a3 100644 --- a/drivers/gpu/drm/drm_fbdev_dma.c +++ b/drivers/gpu/drm/drm_fbdev_dma.c @@ -272,9 +272,9 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper, { struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; + struct fb_info *info = fb_helper->info; struct drm_client_buffer *buffer; struct drm_framebuffer *fb; - struct fb_info *info; u32 format; struct iosys_map map; int ret; @@ -304,12 +304,6 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper, fb_helper->buffer = buffer; fb_helper->fb = fb; - info = drm_fb_helper_alloc_info(fb_helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto err_drm_client_buffer_vunmap; - } - drm_fb_helper_fill_info(info, fb_helper, sizes); if (fb->funcs->dirty) @@ -317,12 +311,10 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper, else ret = drm_fbdev_dma_driver_fbdev_probe_tail(fb_helper, sizes); if (ret) - goto err_drm_fb_helper_release_info; + goto err_drm_client_buffer_vunmap; return 0; -err_drm_fb_helper_release_info: - drm_fb_helper_release_info(fb_helper); err_drm_client_buffer_vunmap: fb_helper->fb = NULL; fb_helper->buffer = NULL; diff --git a/drivers/gpu/drm/drm_fbdev_shmem.c b/drivers/gpu/drm/drm_fbdev_shmem.c index 1e827bf8b815d..77f3507008188 100644 --- a/drivers/gpu/drm/drm_fbdev_shmem.c +++ b/drivers/gpu/drm/drm_fbdev_shmem.c @@ -136,10 +136,10 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper, { struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; + struct fb_info *info = fb_helper->info; struct drm_client_buffer *buffer; struct drm_gem_shmem_object *shmem; struct drm_framebuffer *fb; - struct fb_info *info; u32 format; struct iosys_map map; int ret; @@ -169,12 +169,6 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper, fb_helper->buffer = buffer; fb_helper->fb = fb; - info = drm_fb_helper_alloc_info(fb_helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto err_drm_client_buffer_vunmap; - } - drm_fb_helper_fill_info(info, fb_helper, sizes); info->fbops = &drm_fbdev_shmem_fb_ops; @@ -195,12 +189,10 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper, info->fbdefio = &fb_helper->fbdefio; ret = fb_deferred_io_init(info); if (ret) - goto err_drm_fb_helper_release_info; + goto err_drm_client_buffer_vunmap; return 0; -err_drm_fb_helper_release_info: - drm_fb_helper_release_info(fb_helper); err_drm_client_buffer_vunmap: fb_helper->fb = NULL; fb_helper->buffer = NULL; diff --git a/drivers/gpu/drm/drm_fbdev_ttm.c b/drivers/gpu/drm/drm_fbdev_ttm.c index 85feb55bba113..1ba258f3c8542 100644 --- a/drivers/gpu/drm/drm_fbdev_ttm.c +++ b/drivers/gpu/drm/drm_fbdev_ttm.c @@ -176,8 +176,8 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, { struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; + struct fb_info *info = fb_helper->info; struct drm_client_buffer *buffer; - struct fb_info *info; size_t screen_size; void *screen_buffer; u32 format; @@ -205,12 +205,6 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, goto err_drm_client_framebuffer_delete; } - info = drm_fb_helper_alloc_info(fb_helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto err_vfree; - } - drm_fb_helper_fill_info(info, fb_helper, sizes); info->fbops = &drm_fbdev_ttm_fb_ops; @@ -227,12 +221,10 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper, info->fbdefio = &fb_helper->fbdefio; ret = fb_deferred_io_init(info); if (ret) - goto err_drm_fb_helper_release_info; + goto err_vfree; return 0; -err_drm_fb_helper_release_info: - drm_fb_helper_release_info(fb_helper); err_vfree: vfree(screen_buffer); err_drm_client_framebuffer_delete: diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 93de25b77e686..b0bd0697910e1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -59,18 +59,11 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes, struct exynos_drm_gem *exynos_gem) { - struct fb_info *fbi; + struct fb_info *fbi = helper->info; struct drm_framebuffer *fb = helper->fb; unsigned int size = fb->width * fb->height * fb->format->cpp[0]; unsigned long offset; - fbi = drm_fb_helper_alloc_info(helper); - if (IS_ERR(fbi)) { - DRM_DEV_ERROR(to_dma_dev(helper->dev), - "failed to allocate fb info.\n"); - return PTR_ERR(fbi); - } - fbi->fbops = &exynos_drm_fb_ops; drm_fb_helper_fill_info(fbi, helper, sizes); diff --git a/drivers/gpu/drm/gma500/fbdev.c b/drivers/gpu/drm/gma500/fbdev.c index a6af21514cff7..dacc118b12429 100644 --- a/drivers/gpu/drm/gma500/fbdev.c +++ b/drivers/gpu/drm/gma500/fbdev.c @@ -111,7 +111,7 @@ int psb_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, struct drm_device *dev = fb_helper->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct pci_dev *pdev = to_pci_dev(dev->dev); - struct fb_info *info; + struct fb_info *info = fb_helper->info; struct drm_framebuffer *fb; struct drm_mode_fb_cmd2 mode_cmd = { }; int size; @@ -170,12 +170,6 @@ int psb_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, fb_helper->funcs = &psb_fbdev_fb_helper_funcs; fb_helper->fb = fb; - info = drm_fb_helper_alloc_info(fb_helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto err_drm_framebuffer_unregister_private; - } - info->fbops = &psb_fbdev_fb_ops; /* Accessed stolen memory directly */ @@ -199,10 +193,6 @@ int psb_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, return 0; -err_drm_framebuffer_unregister_private: - drm_framebuffer_unregister_private(fb); - drm_framebuffer_cleanup(fb); - kfree(fb); err_drm_gem_object_put: drm_gem_object_put(obj); return ret; diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index 7daf72b69bae4..a42b0943ed95f 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -242,8 +242,8 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct intel_display *display = to_intel_display(helper->dev); struct intel_fbdev *ifbdev = to_intel_fbdev(helper); struct intel_framebuffer *fb = ifbdev->fb; + struct fb_info *info = helper->info; struct ref_tracker *wakeref; - struct fb_info *info; struct i915_vma *vma; unsigned long flags = 0; bool prealloc = false; @@ -296,13 +296,6 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, goto out_unlock; } - info = drm_fb_helper_alloc_info(helper); - if (IS_ERR(info)) { - drm_err(display->drm, "Failed to allocate fb_info (%pe)\n", info); - ret = PTR_ERR(info); - goto out_unpin; - } - helper->funcs = &intel_fb_helper_funcs; helper->fb = &fb->base; diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index b5969374d53fb..d86c89d03f4b8 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -93,9 +93,9 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, { struct drm_device *dev = helper->dev; struct msm_drm_private *priv = dev->dev_private; + struct fb_info *fbi = helper->info; struct drm_framebuffer *fb = NULL; struct drm_gem_object *bo; - struct fb_info *fbi = NULL; uint64_t paddr; uint32_t format; int ret, pitch; @@ -128,13 +128,6 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, goto fail; } - fbi = drm_fb_helper_alloc_info(helper); - if (IS_ERR(fbi)) { - DRM_DEV_ERROR(dev->dev, "failed to allocate fb info\n"); - ret = PTR_ERR(fbi); - goto fail; - } - DBG("fbi=%p, dev=%p", fbi, dev); helper->funcs = &msm_fbdev_helper_funcs; diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 948af7ec1130b..2b435c05bdb18 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -155,9 +155,9 @@ int omap_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct drm_device *dev = helper->dev; struct omap_drm_private *priv = dev->dev_private; struct omap_fbdev *fbdev = priv->fbdev; + struct fb_info *fbi = helper->info; struct drm_framebuffer *fb = NULL; union omap_gem_size gsize; - struct fb_info *fbi = NULL; struct drm_mode_fb_cmd2 mode_cmd = {0}; struct drm_gem_object *bo; dma_addr_t dma_addr; @@ -226,13 +226,6 @@ int omap_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, goto fail; } - fbi = drm_fb_helper_alloc_info(helper); - if (IS_ERR(fbi)) { - dev_err(dev->dev, "failed to allocate fb info\n"); - ret = PTR_ERR(fbi); - goto fail; - } - DBG("fbi=%p, dev=%p", fbi, dev); helper->funcs = &omap_fbdev_helper_funcs; diff --git a/drivers/gpu/drm/radeon/radeon_fbdev.c b/drivers/gpu/drm/radeon/radeon_fbdev.c index dc81b0c2dbff3..d5c621d48e20f 100644 --- a/drivers/gpu/drm/radeon/radeon_fbdev.c +++ b/drivers/gpu/drm/radeon/radeon_fbdev.c @@ -206,7 +206,7 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, struct radeon_device *rdev = fb_helper->dev->dev_private; const struct drm_format_info *format_info; struct drm_mode_fb_cmd2 mode_cmd = { }; - struct fb_info *info; + struct fb_info *info = fb_helper->info; struct drm_gem_object *gobj; struct radeon_bo *rbo; struct drm_framebuffer *fb; @@ -247,13 +247,6 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, fb_helper->funcs = &radeon_fbdev_fb_helper_funcs; fb_helper->fb = fb; - /* okay we have an object now allocate the framebuffer */ - info = drm_fb_helper_alloc_info(fb_helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto err_drm_framebuffer_unregister_private; - } - info->fbops = &radeon_fbdev_fb_ops; /* radeon resume is fragile and needs a vt switch to help it along */ @@ -279,10 +272,6 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, return 0; -err_drm_framebuffer_unregister_private: - fb_helper->fb = NULL; - drm_framebuffer_unregister_private(fb); - drm_framebuffer_cleanup(fb); err_kfree: kfree(fb); err_radeon_fbdev_destroy_pinned_object: diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 1b70f5e164afd..ba0971ec34b53 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -75,10 +75,10 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct tegra_drm *tegra = helper->dev->dev_private; struct drm_device *drm = helper->dev; struct drm_mode_fb_cmd2 cmd = { 0 }; + struct fb_info *info = helper->info; unsigned int bytes_per_pixel; struct drm_framebuffer *fb; unsigned long offset; - struct fb_info *info; struct tegra_bo *bo; size_t size; int err; @@ -99,13 +99,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, if (IS_ERR(bo)) return PTR_ERR(bo); - info = drm_fb_helper_alloc_info(helper); - if (IS_ERR(info)) { - dev_err(drm->dev, "failed to allocate framebuffer info\n"); - drm_gem_object_put(&bo->gem); - return PTR_ERR(info); - } - fb = tegra_fb_alloc(drm, drm_get_format_info(drm, cmd.pixel_format, cmd.modifier[0]), &cmd, &bo, 1); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index c1d38d54a1120..2faf8aaf98dc7 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -256,8 +256,6 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper); -struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper); -void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper); void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper); void drm_fb_helper_fill_info(struct fb_info *info, struct drm_fb_helper *fb_helper, @@ -340,16 +338,6 @@ drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) return 0; } -static inline struct fb_info * -drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper) -{ - return NULL; -} - -static inline void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper) -{ -} - static inline void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper) { } From 4d49ca777cf1ae8d17b4b1a6090aaa13b0986dec Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 30 Jul 2026 22:41:01 -0400 Subject: [PATCH 3273/3297] drm/tegra: fbdev: Remove offset into framebuffer memory [ Upstream commit a18b6e30ecd69096beda4a0c96d2570900c3879a ] The screen_buffer field in struct fb_info contains the kernel address of the first byte of framebuffer memory. Do not add the display offset. This offset only describes scrolling during scanout. Signed-off-by: Thomas Zimmermann Fixes: de2ba664c30f ("gpu: host1x: drm: Add memory manager and fb") Cc: dri-devel@lists.freedesktop.org Cc: linux-tegra@vger.kernel.org Cc: # v3.10+ Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-3-tzimmermann@suse.de Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/tegra/fbdev.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index ba0971ec34b53..d4ef5d5e42641 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -78,7 +78,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct fb_info *info = helper->info; unsigned int bytes_per_pixel; struct drm_framebuffer *fb; - unsigned long offset; struct tegra_bo *bo; size_t size; int err; @@ -118,9 +117,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, drm_fb_helper_fill_info(info, helper, sizes); - offset = info->var.xoffset * bytes_per_pixel + - info->var.yoffset * fb->pitches[0]; - if (bo->pages) { bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); @@ -132,9 +128,9 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, } info->flags |= FBINFO_VIRTFB; - info->screen_buffer = bo->vaddr + offset; + info->screen_buffer = bo->vaddr; info->screen_size = size; - info->fix.smem_start = (unsigned long)(bo->iova + offset); + info->fix.smem_start = (unsigned long)(bo->iova); info->fix.smem_len = size; return 0; From e06c39cc1c48dca68a5ffd971c23025a52d46634 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Fri, 31 Jul 2026 12:00:49 -0400 Subject: [PATCH 3274/3297] drm/amdgpu: Fix context pstate override handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c1dc4ccb82c9e56325d8e7514ca4c90bd1efb351 ] There are several problems in the context pstate handling code. The most serious ones are potential use-after-free and NULL pointer dereferences at context initialization time. Both are due amdgpu_ctx_init() not holding the adev->pm.stable_pstate_ctx_lock, which is otherwise used from both sysfs and the context code itself for modifying and clearing the stored context pointer. Second issue is that context fini can trample over the pstate configuration set via sysfs. This is due the restore state (ctx->stable_pstate) being saved at context init time, and not if, or when the context actually changes the pstate. As the context exits it will therefore incorrectly restore to what was set before the sysfs override was requested. The simplest fix is to drastically simplify how the state is tracked, by clearly defining the points at which pstate ownership is taken and released, and to handle all transitions under the correct lock. Instead of at context init time, the previous state is saved only at the point the context overrides the current state, and is restored on context exit only if the context is still the owner of the current override state. Signed-off-by: Tvrtko Ursulin Fixes: 79610d304133 ("drm/amdgpu: fix pstate setting issue") Cc: Chengming Gui Cc: Alex Deucher Cc: "Christian König" Signed-off-by: Alex Deucher (cherry picked from commit 1b5e413713c0a93bc1818394d0ce49aaad21bd27) Cc: # v6.1+ Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 71 +++++++++++++++---------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index afedea02188dd..32f3d7c49e1f4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -321,7 +321,6 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, struct drm_file *filp, struct amdgpu_ctx *ctx) { struct amdgpu_fpriv *fpriv = filp->driver_priv; - u32 current_stable_pstate; int r; r = amdgpu_ctx_priority_permit(filp, priority); @@ -339,37 +338,22 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, ctx->generation = amdgpu_vm_generation(mgr->adev, &fpriv->vm); ctx->init_priority = priority; ctx->override_priority = AMDGPU_CTX_PRIORITY_UNSET; - - r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); - if (r) - return r; - - if (mgr->adev->pm.stable_pstate_ctx) - ctx->stable_pstate = mgr->adev->pm.stable_pstate_ctx->stable_pstate; - else - ctx->stable_pstate = current_stable_pstate; + ctx->stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE; ctx->ctx_mgr = &(fpriv->ctx_mgr); return 0; } -static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, - u32 stable_pstate) +static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, + u32 stable_pstate) { struct amdgpu_device *adev = ctx->mgr->adev; enum amd_dpm_forced_level level; + struct amdgpu_ctx *current_ctx; u32 current_stable_pstate; - int r; + int r = 0; - mutex_lock(&adev->pm.stable_pstate_ctx_lock); - if (adev->pm.stable_pstate_ctx && adev->pm.stable_pstate_ctx != ctx) { - r = -EBUSY; - goto done; - } - - r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); - if (r || (stable_pstate == current_stable_pstate)) - goto done; + lockdep_assert_held(&adev->pm.stable_pstate_ctx_lock); switch (stable_pstate) { case AMDGPU_CTX_STABLE_PSTATE_NONE: @@ -388,17 +372,41 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; break; default: - r = -EINVAL; - goto done; + return -EINVAL; } + current_ctx = adev->pm.stable_pstate_ctx; + if (current_ctx && current_ctx != ctx) + return -EBUSY; + + r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); + if (r || current_stable_pstate == stable_pstate) + return r; + r = amdgpu_dpm_force_performance_level(adev, level); + if (r) + return r; - if (level == AMD_DPM_FORCED_LEVEL_AUTO) - adev->pm.stable_pstate_ctx = NULL; - else + if (!current_ctx) { adev->pm.stable_pstate_ctx = ctx; -done: + /* + * Serialized by context taking ownership for the first time + * while holding adev->pm.stable_pstate_ctx_lock). + */ + WRITE_ONCE(ctx->stable_pstate, current_stable_pstate); + } + + return 0; +} + +static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, + u32 stable_pstate) +{ + struct amdgpu_device *adev = ctx->mgr->adev; + int r; + + mutex_lock(&adev->pm.stable_pstate_ctx_lock); + r = __amdgpu_ctx_set_stable_pstate(ctx, stable_pstate); mutex_unlock(&adev->pm.stable_pstate_ctx_lock); return r; @@ -424,7 +432,12 @@ static void amdgpu_ctx_fini(struct kref *ref) } if (drm_dev_enter(adev_to_drm(adev), &idx)) { - amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); + mutex_lock(&adev->pm.stable_pstate_ctx_lock); + if (adev->pm.stable_pstate_ctx == ctx) { + __amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); + adev->pm.stable_pstate_ctx = NULL; + } + mutex_unlock(&adev->pm.stable_pstate_ctx_lock); drm_dev_exit(idx); } From 30b2d0843a4101934109c1e7f9240b153d631c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Fri, 31 Jul 2026 12:25:27 -0400 Subject: [PATCH 3275/3297] drm/amdgpu: Respect placement requirements in amdgpu_gtt_mgr functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 8882f8897e554053af9e72f4c2da8b1e2cce56c7 ] When testing intersection and compatibility, respect the actual placement requirements. This is a pre-requisite for ensuring that UVD CS BOs do not cross 256M segments. Fixes: ded910f368a5 ("drm/amdgpu: Implement intersect/compatible functions") Suggested-by: Christian König Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit bc06579ca29dee9c245a41b12e39c7bb6938af5d) Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 30 +++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index 0760e70402ec1..52ed0fa000d75 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -217,7 +217,20 @@ static bool amdgpu_gtt_mgr_intersects(struct ttm_resource_manager *man, const struct ttm_place *place, size_t size) { - return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res); + const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0]; + const u32 num_pages = PFN_UP(size); + + if (!place->lpfn) + return true; + + if (!amdgpu_gtt_mgr_has_gart_addr(res)) + return false; + + if (place->fpfn >= (node->start + num_pages) || + (place->lpfn && place->lpfn <= node->start)) + return false; + + return true; } /** @@ -235,7 +248,20 @@ static bool amdgpu_gtt_mgr_compatible(struct ttm_resource_manager *man, const struct ttm_place *place, size_t size) { - return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res); + const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0]; + const u32 num_pages = PFN_UP(size); + + if (!place->lpfn) + return true; + + if (!amdgpu_gtt_mgr_has_gart_addr(res)) + return false; + + if (node->start < place->fpfn || + (place->lpfn && (node->start + num_pages) > place->lpfn)) + return false; + + return true; } /** From b485bfb45555163bfa5f565d6a3415fcb3035b02 Mon Sep 17 00:00:00 2001 From: Tejas Upadhyay Date: Fri, 31 Jul 2026 20:45:07 -0400 Subject: [PATCH 3276/3297] drm/xe/guc: Fix buffer overflow in steered register list allocation [ Upstream commit 632ecc90e1ca5d3b6822bb4d08f84a175b6c42c0 ] The size calculation for the steered register extarray uses only the geometry DSS mask (g_dss_mask) to determine the number of entries to allocate: total = bitmap_weight(gt->fuse_topo.g_dss_mask, ...) * steer_reg_num; However, the filling loop uses for_each_dss_steering(), which iterates over for_each_dss(), defined as the union of g_dss_mask and c_dss_mask (geometry + compute DSS). On platforms with compute-only DSS bits, the loop writes past the allocated buffer, corrupting adjacent slab objects. This manifests as list_del corruption and SLUB redzone overwrites during drm_managed_release on device unbind, since the overflow corrupts the drmres list_head of neighboring allocations. Fix by computing the allocation size using the union of both DSS masks, matching the iteration pattern of for_each_dss_steering(). -- v2: - use bitmap_weighted_or() (Zhanjun) Fixes: b170d696c1e2 ("drm/xe/guc: Add XE_LP steered register lists") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/8049 Cc: Zhanjun Dong Cc: stable@vger.kernel.org Assisted-by: GitHub-Copilot:claude-opus-4.6 Reviewed-by: Zhanjun Dong Link: https://patch.msgid.link/20260612070401.543305-2-tejas.upadhyay@intel.com Signed-off-by: Tejas Upadhyay (cherry picked from commit 0a78a44f4901aa6c9263e66be7fce02282f1109f) Signed-off-by: Matthew Brost Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_guc_capture.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c index 243dad3e24185..3a1613886495c 100644 --- a/drivers/gpu/drm/xe/xe_guc_capture.c +++ b/drivers/gpu/drm/xe/xe_guc_capture.c @@ -437,8 +437,15 @@ static void guc_capture_alloc_steered_lists(struct xe_guc *guc) if (!list || guc->capture->extlists) return; - total = bitmap_weight(gt->fuse_topo.g_dss_mask, sizeof(gt->fuse_topo.g_dss_mask) * 8) * - guc_capture_get_steer_reg_num(guc_to_xe(guc)); + { + xe_dss_mask_t all_dss; + + bitmap_or(all_dss, gt->fuse_topo.g_dss_mask, gt->fuse_topo.c_dss_mask, + XE_MAX_DSS_FUSE_BITS); + + total = bitmap_weight(all_dss, XE_MAX_DSS_FUSE_BITS) * + guc_capture_get_steer_reg_num(guc_to_xe(guc)); + } if (!total) return; From dbbe08d73b8bcedf0eb8bacb4b3c43b09c92c032 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Wed, 29 Jul 2026 13:57:48 -0400 Subject: [PATCH 3277/3297] drm/amd/display: check GRPH_FLIP status before sending event [ Upstream commit 48ab86360af117123eb1b15e38f068acf3826400 ] [Why] After unifying DCN interrupt sources under VUPDATE_NO_LOCK, we have two remaining issues to clean up: 1. On DCN, flip completion is now delivered from VUPDATE_NO_LOCK (dm_crtc_high_irq_handler) instead of GRPH_PFLIP. But VUPDATE_NO_LOCK fires every frame, regardless of whether a flip has latched. 2. There is a window during commit where a flip is armed (pflip_status = SUBMITTED) but not yet programmed into HW. If the VUPDATE_NO_LOCK fires in that window, its handler would deliver a flip event to userspace before HW has latched to it. If userspace then renders to what it believes is now the back buffer (but HW is still latched to it!), it will cause display corruption. This issue seemed to have been introduced by: commit 1159898a88db ("drm/amd/display: Handle commit plane with no FB.") Enabling replay or psr extended the duration of this window, and hence made corruption more likely to be observed. [How] * Move acrtc->event/pflip_status arming to after update_planes_and_stream_adapter() has programmed the flip into HW. This closes the window where pflip_status is SUBMITTED but the flip is not yet programmed. * Add dc_get_flip_pending_on_otg(), which reads the HUBP flip-pending status straight from HW for the pipe(s) bound to an OTG instance. It is keyed only by otg_inst and does not take or mutate a dc_plane_state, so it is safe to call from the OTG interrupt handler without racing a concurrent commit that may be modifying plane state. * Optimistically query for flip-pending after programming, in the event that HW latched to the new fb between programming start and arming event. If it latched, send the vblank event immediately, rather than wait for the next vblank IRQ. * In the VUPDATE_NO_LOCK handler, only deliver flip completion once dc_get_flip_pending_on_otg() reports the flip is no longer pending. Otherwise leave the flip armed and retry on the next vupdate. * For DCE, maintain the existing behavior of arming flips before programming, and relying on GRPH_FLIP to fire at HW latch. v2: * Drop flip_programmed completion object, instead move event/pflip_status arming after programming. * For DCN, optimistically query for flip pending immediately after programming, and if it latched, send event right away. v3: * Fix event timestamps on optimistic flip latch detection, where it's possible for it to run *before* the vupdate IRQ updates the timestamp. * Add more docstrings for DCN vblank handling. * Clean up if conditions in dm_arm_vblank_event(). * Code style cleanup on braces surrounding multi-line statements. Fixes: 9b47278cec98 ("drm/amd/display: temp w/a for dGPU to enter idle optimizations") Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/3787 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141 Assisted-by: Copilot:claude-opus-4.8 Tested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Leo Li Signed-off-by: Alex Deucher (cherry picked from commit f64a9be5653689ff43e148cd8a6483077488c8e5) Cc: stable@vger.kernel.org # 8382cd234981: drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 191 ++++++++++++++---- drivers/gpu/drm/amd/display/dc/core/dc.c | 45 +++++ drivers/gpu/drm/amd/display/dc/dc.h | 1 + 3 files changed, 197 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 99759559cc0f6..be267f5b9f8cc 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -676,13 +676,30 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev, * Deliver pageflip completion events (DCN only). * * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch - * point. Deliver any pending pageflip completion event from here. + * point. Deliver any pending pageflip completion event from here, + * once HW has consumed the new address (the OTG no longer reports a + * pending flip). * - * NOTE: This can deliver an event for a flip that was armed but not yet - * programmed into HW; that race is closed in a follow-up change by - * checking the programmed flip status. + * Also handle the case here where there aren't any active planes and + * DCN HUBP may be clock-gated, so the flip-pending status may be + * undefined. */ - if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) { + if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && + acrtc->event) { + + if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) { + drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); + acrtc->event = NULL; + drm_crtc_vblank_put(&acrtc->base); + acrtc->pflip_status = AMDGPU_FLIP_NONE; + } + /* + * If the flip is still pending, leave it armed and + * retry on the next vupdate. + */ + } else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && + acrtc->dm_irq_params.active_planes == 0) { + if (acrtc->event) { drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); acrtc->event = NULL; @@ -9664,6 +9681,28 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_crtc *acrtc_attach, } } +static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc, + struct dm_crtc_state *acrtc_state, + bool pflip_update, + bool cursor_update) +{ + assert_spin_locked(&acrtc->base.dev->event_lock); + + if (!acrtc->base.state->event || acrtc_state->active_planes == 0) + return; + + if (pflip_update) { + drm_crtc_vblank_get(&acrtc->base); + WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); + /* Arm flip completion handling and event delivery after programming. */ + prepare_flip_isr(acrtc); + } else if (cursor_update) { + drm_crtc_vblank_get(&acrtc->base); + acrtc->event = acrtc->base.state->event; + acrtc->base.state->event = NULL; + } +} + static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, struct drm_device *dev, struct amdgpu_display_manager *dm, @@ -9687,6 +9726,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, bool cursor_update = false; bool pflip_present = false; bool immediate_flip = false; + bool flip_latched_during_prog = false; bool dirty_rects_changed = false; bool updated_planes_and_streams = false; struct { @@ -9922,39 +9962,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, usleep_range(1000, 1100); } - /** - * Prepare the flip event for the pageflip interrupt to handle. - * - * This only works in the case where we've already turned on the - * appropriate hardware blocks (eg. HUBP) so in the transition case - * from 0 -> n planes we have to skip a hardware generated event - * and rely on sending it from software. - */ - if (acrtc_attach->base.state->event && - acrtc_state->active_planes > 0) { - drm_crtc_vblank_get(pcrtc); - - spin_lock_irqsave(&pcrtc->dev->event_lock, flags); - - WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE); - prepare_flip_isr(acrtc_attach); - - spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); - } - if (acrtc_state->stream) { if (acrtc_state->freesync_vrr_info_changed) bundle->stream_update.vrr_infopacket = &acrtc_state->stream->vrr_infopacket; } - } else if (cursor_update && acrtc_state->active_planes > 0) { - spin_lock_irqsave(&pcrtc->dev->event_lock, flags); - if (acrtc_attach->base.state->event) { - drm_crtc_vblank_get(pcrtc); - acrtc_attach->event = acrtc_attach->base.state->event; - acrtc_attach->base.state->event = NULL; + } + + /* + * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for + * event delivery. Only GRPH_FLIP handler can send pflip events, and it + * only fires if HW latched to the flip. Maintain legacy behavior by + * arming event before programming. + */ + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { + scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + dm_arm_vblank_event(acrtc_attach, acrtc_state, + pflip_present, cursor_update); } - spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); } /* Update the planes if changed or disable if we don't have any. */ @@ -10056,17 +10081,103 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, amdgpu_dm_commit_cursors(state); /* - * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK. - * However, an immediate (tearing / async) flip is latched by HW right - * away and does not wait for the next vupdate, so deliver its - * completion event here after programming. + * DCN specific vblank handling + * ============================ + * + * With the event_lock held, arm the vblank event, and determine whether + * deliver it immediately, or in VUPDATE_NO_LOCK IRQ (i.e. HW latch + * point) handler. Do this *after* programming so that the IRQ handler + * will not deliver the event before HW laches onto the programmed + * values: + * + * Commit thread IRQ handler HW + * ----------------------------------------------------------------- + * arm_vblank_event() + * vupdate() + * vupdate_handler() + * cook_timestamp() + * # prev flip already latched, + * # so flip_latched == true. + * if event_armed && flip_latched: + * send_vblank_event() + * # sent before latch, **BAD!** + * hw_program() + * vupdate() + * **latch** + * + * There's a consequence of arming after: it's possible for HW to latch + * between start of HW programming and acrtc->event/pflip_status arming. + * When this happens, the IRQ handler will send the event on the next + * immediate latch point, even though HW has already latched. This is + * handled by optimistically checking for HW latch after programming, + * and if latched, send the event immediately: * - * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so - * this is DCN-only. + * Commit thread IRQ handler HW + * ----------------------------------------------------------------- + * hw_program() + * vupdate() + * **latch** + * vupdate_handler() + * cook_timestamp() + * # event_armed == false + * # **no event sent!** + * arm_vblank_event() + * if flip_latched: + * **send_vblank_event()** + * disarm_vblank_event() + * + * The IRQ handler is expected to cook the timestamp, but we need to + * cook the timestamp before optimistic sending as well. That's because + * the following sequence is possible: + * + * Commit thread IRQ handler HW + * ----------------------------------------------------------------- + * hw_program() + * arm_vblank_event() + * vupdate() + * **latch** + * if flip_latched: + * # Need cook before send! + * **cook_timestamp()** + * send_vblank_event() + * disarm_vblank_event() + * vupdate_handler() + * cook_timestamp() + * # event_armed == false + * # no event sent! + * + * Cooking twice is OK, since DRM scanout accurate timestamps report A) + * the previous vactive start if currently in vactive, or B) the next + * vactive start if currently in vblank (see &get_vblank_counter). 'A)' + * is what we want for the optimistic send, and for 'B)', we'll cook a + * timestamp no later than the next IRQ handler run. + * + * The more correct fix is to wrap programming and arming with the + * event_lock and thus serializing it with the IRQ handler. However, + * there are various sleep-waits within + * update_planes_and_stream_adapter() that makes spin locking illegal. + * And on full updates, it can take 1-2 frame-times to return (see + * commit_planes_for_stream). + * + * On DCE, GRPH_PFLIP IRQ is used and takes care of this. */ - if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) { + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) { spin_lock_irqsave(&pcrtc->dev->event_lock, flags); - if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED && + + if (updated_planes_and_streams) { + flip_latched_during_prog = + !dc_get_flip_pending_on_otg(dm->dc, acrtc_attach->otg_inst); + } + + dm_arm_vblank_event(acrtc_attach, acrtc_state, + pflip_present, cursor_update); + + /* + * Deliver the event immediately on immediate flip, or on a + * update that has already latched. + */ + if ((immediate_flip || flip_latched_during_prog) && + acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED && acrtc_attach->event) { drm_crtc_accurate_vblank_count(&acrtc_attach->base); drm_crtc_send_vblank_event(&acrtc_attach->base, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 0347174173f34..927837249479f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -5427,6 +5427,51 @@ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src) dal_irq_service_ack(dc->res_pool->irqs, src); } +/* + * dc_get_flip_pending_on_otg() - Check if a GRPH_FLIP is still pending on OTG + * + * @dc: display core context @otg_inst: OTG instance to query + * + * Reads the HUBP flip-pending status for the pipe(s) bound to @otg_inst, + * returning true if any of them has not yet latched its programmed surface + * address. + * + * Unlike dc_plane_get_status(), this does not take or mutate a dc_plane_state, + * so it is safe to call from interrupt context without racing a concurrent + * commit that may be updating plane state. + * + * Return: true if a flip is still pending on the OTG, false otherwise. + */ +bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst) +{ + bool flip_pending = false; + int i; + + if (!dc || !dc->current_state) + return false; + + dc_exit_ips_for_hw_access(dc); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + + if (!pipe_ctx->plane_state || !pipe_ctx->stream_res.tg) + continue; + + if (pipe_ctx->stream_res.tg->inst != otg_inst) + continue; + + if (hubp && hubp->funcs->hubp_is_flip_pending && + hubp->funcs->hubp_is_flip_pending(hubp)) { + flip_pending = true; + break; + } + } + + return flip_pending; +} + void dc_power_down_on_boot(struct dc *dc) { if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW && diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 98f0b6b3c2136..390f948dad393 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -2611,6 +2611,7 @@ enum dc_irq_source dc_interrupt_to_irq_source( uint32_t ext_id); bool dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable); void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src); +bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst); enum dc_irq_source dc_get_hpd_irq_source_at_index( struct dc *dc, uint32_t link_index); From 3cb42a973f889f13204f220612eb0be89f90a661 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Wed, 5 Aug 2026 12:33:14 -0400 Subject: [PATCH 3278/3297] drm/amd/display: Exit idle optimizations before programming [ Upstream commit 8419331e64d92a8de5fc4feef0e305f201fb8b33 ] [Why] We need to exit PSR/IPS before programming. Before calling DC for programming in amdgpu_dm_commit_planes(), there's a vblank_control_workqueue flush. This waits for IPS and PSR exit. (See drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()-> amdgpu_dm_crtc_vblank_control_worker()) Prior to the tagged "Fixes:" change, drm_vblank_get() was called before the workqueue flush. This ordering ensures that PSR exit occurred before programming. After the "Fixes:" change, drm_vblank_get() is called after the workqueue flush, leading to programming while idle optimizations are still active. This can lead to incorrect flip_pending detection used by vblank event delivery. [How] Split the vblank_get() component of `dm_arm_vblank_event()` into `dm_arm_vblank_event_pre_programming()`, which is called before programming. Call it before the vblank_control_workqueue flush. Includes a drive-by cleanup of prepare_flip_isr(): the only caller is dm_arm_vblank_event() and it's simple enough to roll-in. v2: Fix checkpatch formatting warning on drm_arm_vblank_event_pre_programming() arg alignment. Fixes: 48ab86360af1 ("drm/amd/display: check GRPH_FLIP status before sending event") Cc: stable@vger.kernel.org Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527 Assisted-by: Codex:gpt-5.6-sol Assisted-by: Claude:opus-5 Suggested-by: David Weber Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit 05984e29520a28c27f5a2388742c957a6a87ee7a) (cherry picked from commit 8419331e64d92a8de5fc4feef0e305f201fb8b33) Signed-off-by: Sasha Levin --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index be267f5b9f8cc..4f53297786623 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9326,25 +9326,6 @@ static void remove_stream(struct amdgpu_device *adev, acrtc->enabled = false; } -static void prepare_flip_isr(struct amdgpu_crtc *acrtc) -{ - - assert_spin_locked(&acrtc->base.dev->event_lock); - WARN_ON(acrtc->event); - - acrtc->event = acrtc->base.state->event; - - /* Set the flip status */ - acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; - - /* Mark this event as consumed */ - acrtc->base.state->event = NULL; - - drm_dbg_state(acrtc->base.dev, - "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", - acrtc->crtc_id); -} - static void update_freesync_state_on_stream( struct amdgpu_display_manager *dm, struct dm_crtc_state *new_crtc_state, @@ -9692,17 +9673,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc, return; if (pflip_update) { - drm_crtc_vblank_get(&acrtc->base); WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); - /* Arm flip completion handling and event delivery after programming. */ - prepare_flip_isr(acrtc); + WARN_ON(acrtc->event); + + acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; + acrtc->event = acrtc->base.state->event; + acrtc->base.state->event = NULL; + + drm_dbg_state(acrtc->base.dev, + "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", + acrtc->crtc_id); } else if (cursor_update) { - drm_crtc_vblank_get(&acrtc->base); acrtc->event = acrtc->base.state->event; acrtc->base.state->event = NULL; } } +/** + * dm_arm_vblank_event_pre_programming - Prepare for programming + * @acrtc: The amdgpu CRTC to prepare + * @acrtc_state: The new CRTC state + * @pflip_update: Whether a page flip is being programmed + * @cursor_update: Whether a cursor update is being programmed + * + * Grab a reference on the vblank counter if a page flip or cursor update is to + * be programmed. Do this before programming so the HW is not in any + * idle-optimized state (such as PSR). + */ +static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc, + struct dm_crtc_state *acrtc_state, + bool pflip_update, + bool cursor_update) +{ + assert_spin_locked(&acrtc->base.dev->event_lock); + + if (!acrtc->base.state->event || acrtc_state->active_planes == 0) + return; + + if (pflip_update || cursor_update) + drm_crtc_vblank_get(&acrtc->base); +} + static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, struct drm_device *dev, struct amdgpu_display_manager *dm, @@ -9969,16 +9980,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, } } - /* - * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for - * event delivery. Only GRPH_FLIP handler can send pflip events, and it - * only fires if HW latched to the flip. Maintain legacy behavior by - * arming event before programming. - */ - if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { - scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state, + pflip_present, + cursor_update); + /* + * DCE depends on a combination of GRPH_FLIP, VLINE0, and + * VUPDATE for event delivery. Only GRPH_FLIP handler can send + * pflip events, and it only fires if HW latched to the flip. + * Maintain legacy behavior by arming event before programming. + */ + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { dm_arm_vblank_event(acrtc_attach, acrtc_state, - pflip_present, cursor_update); + pflip_present, cursor_update); } } From 005b9b44316063564a83d6a3911b8ff67b3ed308 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Sat, 1 Aug 2026 10:03:55 -0400 Subject: [PATCH 3279/3297] drm/xe/pat: Add helper to query compression enable status [ Upstream commit b2bce0e551e89af37cfcb1b1158d80f369eeea3f ] Add xe_pat_index_get_comp_en() helper function to check whether compression is enabled for a given PAT index by extracting the XE2_COMP_EN bit from the PAT table entry. There are no current users, however there are multiple in-flight series which will all use this helper. CC: Nitin Gote CC: Sanjay Yadav CC: Matt Roper Suggested-by: Matthew Auld Signed-off-by: Xin Wang Reviewed-by: Matt Roper Reviewed-by: Nitin Gote Reviewed-by: Matthew Auld Reviewed-by: Sanjay Yadav Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20251110221458.1864507-2-x.wang@intel.com Stable-dep-of: 7bc597ce74ba ("drm/xe/vm: Fix BO prefetch with CONSULT_MEM_ADVISE_PREF_LOC") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_pat.c | 6 ++++++ drivers/gpu/drm/xe/xe_pat.h | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pat.c b/drivers/gpu/drm/xe/xe_pat.c index 2e7cb99ae87aa..9a321384b73da 100644 --- a/drivers/gpu/drm/xe/xe_pat.c +++ b/drivers/gpu/drm/xe/xe_pat.c @@ -160,6 +160,12 @@ u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index) return xe->pat.table[pat_index].coh_mode; } +bool xe_pat_index_get_comp_en(struct xe_device *xe, u16 pat_index) +{ + WARN_ON(pat_index >= xe->pat.n_entries); + return !!(xe->pat.table[pat_index].value & XE2_COMP_EN); +} + static void program_pat(struct xe_gt *gt, const struct xe_pat_table_entry table[], int n_entries) { diff --git a/drivers/gpu/drm/xe/xe_pat.h b/drivers/gpu/drm/xe/xe_pat.h index fa0dfbe525cd6..12cb6ec39d34c 100644 --- a/drivers/gpu/drm/xe/xe_pat.h +++ b/drivers/gpu/drm/xe/xe_pat.h @@ -58,4 +58,14 @@ void xe_pat_dump(struct xe_gt *gt, struct drm_printer *p); */ u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index); +/** + * xe_pat_index_get_comp_en - Extract the compression enable flag for + * the given pat_index. + * @xe: xe device + * @pat_index: The pat_index to query + * + * Return: true if compression is enabled for this pat_index, false otherwise. + */ +bool xe_pat_index_get_comp_en(struct xe_device *xe, u16 pat_index); + #endif From 0015b054b06d38442050e43682a40742a96291e8 Mon Sep 17 00:00:00 2001 From: Nitin Gote Date: Sat, 1 Aug 2026 10:03:56 -0400 Subject: [PATCH 3280/3297] drm/xe: add xe_migrate_resolve wrapper and is_vram_resolve support [ Upstream commit be97fd06458d66a53aefb6d9429db0df734c81c0 ] Introduce an internal __xe_migrate_copy(..., is_vram_resolve) path and expose a small wrapper xe_migrate_resolve() that calls it with is_vram_resolve=true. For resolve/decompression operations we must ensure the copy code uses the compression PAT index when appropriate; this change centralizes that behavior and allows callers to schedule a resolve (decompress) operation via the migrate API. v3: Fix kernel-doc warnings v2: (Matt) - Simplify xe_migrate_resolve(), use single BO/resource; remove copy_only_ccs argument as it's always false. Cc: Matthew Brost Cc: Matthew Auld Reviewed-by: Matthew Brost Signed-off-by: Nitin Gote Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260304123758.3050386-7-nitin.r.gote@intel.com Stable-dep-of: 7bc597ce74ba ("drm/xe/vm: Fix BO prefetch with CONSULT_MEM_ADVISE_PREF_LOC") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_migrate.c | 90 +++++++++++++++++++++++---------- drivers/gpu/drm/xe/xe_migrate.h | 4 ++ 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index e0a14360d312e..1e8c0b92d712a 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -765,31 +765,13 @@ static u32 xe_migrate_ccs_copy(struct xe_migrate *m, return flush_flags; } -/** - * xe_migrate_copy() - Copy content of TTM resources. - * @m: The migration context. - * @src_bo: The buffer object @src is currently bound to. - * @dst_bo: If copying between resources created for the same bo, set this to - * the same value as @src_bo. If copying between buffer objects, set it to - * the buffer object @dst is currently bound to. - * @src: The source TTM resource. - * @dst: The dst TTM resource. - * @copy_only_ccs: If true copy only CCS metadata - * - * Copies the contents of @src to @dst: On flat CCS devices, - * the CCS metadata is copied as well if needed, or if not present, - * the CCS metadata of @dst is cleared for security reasons. - * - * Return: Pointer to a dma_fence representing the last copy batch, or - * an error pointer on failure. If there is a failure, any copy operation - * started by the function call has been synced. - */ -struct dma_fence *xe_migrate_copy(struct xe_migrate *m, - struct xe_bo *src_bo, - struct xe_bo *dst_bo, - struct ttm_resource *src, - struct ttm_resource *dst, - bool copy_only_ccs) +static struct dma_fence *__xe_migrate_copy(struct xe_migrate *m, + struct xe_bo *src_bo, + struct xe_bo *dst_bo, + struct ttm_resource *src, + struct ttm_resource *dst, + bool copy_only_ccs, + bool is_vram_resolve) { struct xe_gt *gt = m->tile->primary_gt; struct xe_device *xe = gt_to_xe(gt); @@ -810,8 +792,15 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m, bool copy_ccs = xe_device_has_flat_ccs(xe) && xe_bo_needs_ccs_pages(src_bo) && xe_bo_needs_ccs_pages(dst_bo); bool copy_system_ccs = copy_ccs && (!src_is_vram || !dst_is_vram); - bool use_comp_pat = type_device && xe_device_has_flat_ccs(xe) && - GRAPHICS_VER(xe) >= 20 && src_is_vram && !dst_is_vram; + + /* + * For decompression operation, always use the compression PAT index. + * Otherwise, only use the compression PAT index for device memory + * when copying from VRAM to system memory. + */ + bool use_comp_pat = is_vram_resolve || (type_device && + xe_device_has_flat_ccs(xe) && + GRAPHICS_VER(xe) >= 20 && src_is_vram && !dst_is_vram); /* Copying CCS between two different BOs is not supported yet. */ if (XE_WARN_ON(copy_ccs && src_bo != dst_bo)) @@ -970,6 +959,53 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m, return fence; } +/** + * xe_migrate_copy() - Copy content of TTM resources. + * @m: The migration context. + * @src_bo: The buffer object @src is currently bound to. + * @dst_bo: If copying between resources created for the same bo, set this to + * the same value as @src_bo. If copying between buffer objects, set it to + * the buffer object @dst is currently bound to. + * @src: The source TTM resource. + * @dst: The dst TTM resource. + * @copy_only_ccs: If true copy only CCS metadata + * + * Copies the contents of @src to @dst: On flat CCS devices, + * the CCS metadata is copied as well if needed, or if not present, + * the CCS metadata of @dst is cleared for security reasons. + * + * Return: Pointer to a dma_fence representing the last copy batch, or + * an error pointer on failure. If there is a failure, any copy operation + * started by the function call has been synced. + */ +struct dma_fence *xe_migrate_copy(struct xe_migrate *m, + struct xe_bo *src_bo, + struct xe_bo *dst_bo, + struct ttm_resource *src, + struct ttm_resource *dst, + bool copy_only_ccs) +{ + return __xe_migrate_copy(m, src_bo, dst_bo, src, dst, copy_only_ccs, false); +} + +/** + * xe_migrate_resolve() - Resolve and decompress a buffer object if required. + * @m: The migrate context + * @bo: The buffer object to resolve + * @res: The reservation object + * + * Wrapper around __xe_migrate_copy() with is_vram_resolve set to true + * to trigger decompression if needed. + * + * Return: A dma_fence that signals on completion, or an ERR_PTR on failure. + */ +struct dma_fence *xe_migrate_resolve(struct xe_migrate *m, + struct xe_bo *bo, + struct ttm_resource *res) +{ + return __xe_migrate_copy(m, bo, bo, res, res, false, true); +} + /** * xe_migrate_lrc() - Get the LRC from migrate context. * @migrate: Migrate context. diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index 8bac669685ee9..9665a0aff547c 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -127,6 +127,10 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m, struct ttm_resource *dst, bool copy_only_ccs); +struct dma_fence *xe_migrate_resolve(struct xe_migrate *m, + struct xe_bo *bo, + struct ttm_resource *res); + int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, struct xe_bo *src_bo, struct ttm_resource *new_mem, From 1ba553ee0b521c035f02ca8f125dea16b9d6abef Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 1 Aug 2026 10:03:57 -0400 Subject: [PATCH 3281/3297] drm/xe/bo: Add purgeable bo state tracking and field madv to xe_bo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b67427f939ccf038a658badb02dd948c7b516248 ] Add infrastructure for tracking purgeable state of buffer objects. This includes: Introduce enum xe_madv_purgeable_state with three states: - XE_MADV_PURGEABLE_WILLNEED (0): BO is needed and should not be purged. This is the default state for all BOs. - XE_MADV_PURGEABLE_DONTNEED (1): BO is not currently needed and can be purged by the kernel under memory pressure to reclaim resources. Only non-shared BOs can be marked as DONTNEED. - XE_MADV_PURGEABLE_PURGED (2): BO has been purged by the kernel. Accessing a purged BO results in error. Follows i915 semantics where once purged, the BO remains permanently invalid ("once purged, always purged"). Add madv_purgeable field to struct xe_bo for state tracking of purgeable state across concurrent access paths Cc: Thomas Hellström Cc: Himal Prasad Ghimiray Reviewed-by: Matthew Brost Signed-off-by: Arvind Yadav Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260326130843.3545241-3-arvind.yadav@intel.com Stable-dep-of: 7bc597ce74ba ("drm/xe/vm: Fix BO prefetch with CONSULT_MEM_ADVISE_PREF_LOC") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_bo.h | 56 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_bo_types.h | 6 ++++ 2 files changed, 62 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 9c1ba37983d9d..fef60c8a4e5b1 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -84,6 +84,28 @@ #define XE_PCI_BARRIER_MMAP_OFFSET (0x50 << XE_PTE_SHIFT) +/** + * enum xe_madv_purgeable_state - Buffer object purgeable state enumeration + * + * This enum defines the possible purgeable states for a buffer object, + * allowing userspace to provide memory usage hints to the kernel for + * better memory management under pressure. + * + * @XE_MADV_PURGEABLE_WILLNEED: The buffer object is needed and should not be purged. + * This is the default state. + * @XE_MADV_PURGEABLE_DONTNEED: The buffer object is not currently needed and can be + * purged by the kernel under memory pressure. + * @XE_MADV_PURGEABLE_PURGED: The buffer object has been purged by the kernel. + * + * Accessing a purged buffer will result in an error. Per i915 semantics, + * once purged, a BO remains permanently invalid and must be destroyed and recreated. + */ +enum xe_madv_purgeable_state { + XE_MADV_PURGEABLE_WILLNEED, + XE_MADV_PURGEABLE_DONTNEED, + XE_MADV_PURGEABLE_PURGED, +}; + struct sg_table; struct xe_bo *xe_bo_alloc(void); @@ -213,6 +235,40 @@ static inline bool xe_bo_is_protected(const struct xe_bo *bo) return bo->pxp_key_instance; } +/** + * xe_bo_is_purged() - Check if buffer object has been purged + * @bo: The buffer object to check + * + * Checks if the buffer object's backing store has been discarded by the + * kernel due to memory pressure after being marked as purgeable (DONTNEED). + * Once purged, the BO cannot be restored and any attempt to use it will fail. + * + * Context: Caller must hold the BO's dma-resv lock + * Return: true if the BO has been purged, false otherwise + */ +static inline bool xe_bo_is_purged(struct xe_bo *bo) +{ + xe_bo_assert_held(bo); + return bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED; +} + +/** + * xe_bo_madv_is_dontneed() - Check if BO is marked as DONTNEED + * @bo: The buffer object to check + * + * Checks if userspace has marked this BO as DONTNEED (i.e., its contents + * are not currently needed and can be discarded under memory pressure). + * This is used internally to decide whether a BO is eligible for purging. + * + * Context: Caller must hold the BO's dma-resv lock + * Return: true if the BO is marked DONTNEED, false otherwise + */ +static inline bool xe_bo_madv_is_dontneed(struct xe_bo *bo) +{ + xe_bo_assert_held(bo); + return bo->madv_purgeable == XE_MADV_PURGEABLE_DONTNEED; +} + static inline void xe_bo_unpin_map_no_vm(struct xe_bo *bo) { if (likely(bo)) { diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h index 88e3d8b33c532..efff5519fc588 100644 --- a/drivers/gpu/drm/xe/xe_bo_types.h +++ b/drivers/gpu/drm/xe/xe_bo_types.h @@ -110,6 +110,12 @@ struct xe_bo { * from default */ u64 min_align; + + /** + * @madv_purgeable: user space advise on BO purgeability, protected + * by BO's dma-resv lock. + */ + u32 madv_purgeable; }; #endif From 8fa8b0a4637299090f764f372c4a15463786314a Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 1 Aug 2026 10:03:58 -0400 Subject: [PATCH 3282/3297] drm/xe/vm: Prevent binding of purged buffer objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4f44961eab8474a47de419113e1d46095f9b44e0 ] Add purge checking to vma_lock_and_validate() to block new mapping operations on purged BOs while allowing cleanup operations to proceed. Purged BOs have their backing pages freed by the kernel. New mapping operations (MAP, PREFETCH, REMAP) must be rejected with -EINVAL to prevent GPU access to invalid memory. Cleanup operations (UNMAP) must be allowed so applications can release resources after detecting purge via the retained field. REMAP operations require mixed handling - reject new prev/next VMAs if the BO is purged, but allow the unmap portion to proceed for cleanup. The check_purged flag in struct xe_vma_lock_and_validate_flags distinguishes between these cases: true for new mappings (must reject), false for cleanup (allow). Cc: Himal Prasad Ghimiray Cc: Matthew Brost Reviewed-by: Thomas Hellström Signed-off-by: Arvind Yadav Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260326130843.3545241-6-arvind.yadav@intel.com Stable-dep-of: 7bc597ce74ba ("drm/xe/vm: Fix BO prefetch with CONSULT_MEM_ADVISE_PREF_LOC") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_vm.c | 79 ++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index ed0c8acd0fe27..695a4f27d826d 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -2834,8 +2834,22 @@ static void vm_bind_ioctl_ops_unwind(struct xe_vm *vm, } } +/** + * struct xe_vma_lock_and_validate_flags - Flags for vma_lock_and_validate() + * @res_evict: Allow evicting resources during validation + * @validate: Perform BO validation + * @request_decompress: Request BO decompression + * @check_purged: Reject operation if BO is purged + */ +struct xe_vma_lock_and_validate_flags { + u32 res_evict : 1; + u32 validate : 1; + u32 request_decompress : 1; + u32 check_purged : 1; +}; + static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma, - bool res_evict, bool validate) + struct xe_vma_lock_and_validate_flags flags) { struct xe_bo *bo = xe_vma_bo(vma); struct xe_vm *vm = xe_vma_vm(vma); @@ -2844,10 +2858,19 @@ static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma, if (bo) { if (!bo->vm) err = drm_exec_lock_obj(exec, &bo->ttm.base); - if (!err && validate) + + /* Reject new mappings to DONTNEED/purged BOs; allow cleanup operations */ + if (!err && flags.check_purged) { + if (xe_bo_madv_is_dontneed(bo)) + err = -EBUSY; /* BO marked purgeable */ + else if (xe_bo_is_purged(bo)) + err = -EINVAL; /* BO already purged */ + } + + if (!err && flags.validate) err = xe_bo_validate(bo, vm, !xe_vm_in_preempt_fence_mode(vm) && - res_evict, exec); + flags.res_evict, exec); } return err; @@ -2933,9 +2956,13 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, case DRM_GPUVA_OP_MAP: if (!op->map.invalidate_on_bind) err = vma_lock_and_validate(exec, op->map.vma, - res_evict, - !xe_vm_in_fault_mode(vm) || - op->map.immediate); + (struct xe_vma_lock_and_validate_flags) { + .res_evict = res_evict, + .validate = !xe_vm_in_fault_mode(vm) || + op->map.immediate, + .request_decompress = false, + .check_purged = true, + }); break; case DRM_GPUVA_OP_REMAP: err = check_ufence(gpuva_to_vma(op->base.remap.unmap->va)); @@ -2944,13 +2971,28 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, err = vma_lock_and_validate(exec, gpuva_to_vma(op->base.remap.unmap->va), - res_evict, false); + (struct xe_vma_lock_and_validate_flags) { + .res_evict = res_evict, + .validate = false, + .request_decompress = false, + .check_purged = false, + }); if (!err && op->remap.prev) err = vma_lock_and_validate(exec, op->remap.prev, - res_evict, true); + (struct xe_vma_lock_and_validate_flags) { + .res_evict = res_evict, + .validate = true, + .request_decompress = false, + .check_purged = true, + }); if (!err && op->remap.next) err = vma_lock_and_validate(exec, op->remap.next, - res_evict, true); + (struct xe_vma_lock_and_validate_flags) { + .res_evict = res_evict, + .validate = true, + .request_decompress = false, + .check_purged = true, + }); break; case DRM_GPUVA_OP_UNMAP: err = check_ufence(gpuva_to_vma(op->base.unmap.va)); @@ -2959,7 +3001,12 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, err = vma_lock_and_validate(exec, gpuva_to_vma(op->base.unmap.va), - res_evict, false); + (struct xe_vma_lock_and_validate_flags) { + .res_evict = res_evict, + .validate = false, + .request_decompress = false, + .check_purged = false, + }); break; case DRM_GPUVA_OP_PREFETCH: { @@ -2972,9 +3019,19 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, region <= ARRAY_SIZE(region_to_mem_type)); } + /* + * Prefetch attempts to migrate BO's backing store without + * repopulating it first. Purged BOs have no backing store + * to migrate, so reject the operation. + */ err = vma_lock_and_validate(exec, gpuva_to_vma(op->base.prefetch.va), - res_evict, false); + (struct xe_vma_lock_and_validate_flags) { + .res_evict = res_evict, + .validate = false, + .request_decompress = false, + .check_purged = true, + }); if (!err && !xe_vma_has_no_bo(vma)) err = xe_bo_migrate(xe_vma_bo(vma), region_to_mem_type[region], From d256dac008d1d9e6378aa1a5454e89a8292d174c Mon Sep 17 00:00:00 2001 From: Himal Prasad Ghimiray Date: Sat, 1 Aug 2026 10:03:59 -0400 Subject: [PATCH 3283/3297] drm/xe/vm: Fix BO prefetch with CONSULT_MEM_ADVISE_PREF_LOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7bc597ce74bab4153b2009c92eccf889e9d74044 ] When prefetch region is DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC for a BO VMA, the code used it as an index into region_to_mem_type[], causing an out-of-bounds access since the value is -1. Resolve the preferred location for BO VMAs directly: local VRAM on dGFX (using the BO's tile placement) or system memory on iGPU. Discovered using AI-assisted static analysis confirmed by Intel Product Security. v2: -Fix null dereference Reported-by: Martin Hodo Fixes: c1bb69a2e8e2 ("drm/xe/svm: Consult madvise preferred location in prefetch") Cc: Matthew Brost Cc: stable@vger.kernel.org Reviewed-by: Matthew Brost Link: https://patchwork.freedesktop.org/patch/msgid/20260624174943.2808767-2-himal.prasad.ghimiray@intel.com Signed-off-by: Himal Prasad Ghimiray (cherry picked from commit d9a4906ac03be9f6ed3f3b45c56c866b867fd75b) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_vm.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 695a4f27d826d..1befcfb2c51aa 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -3032,11 +3032,26 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, .request_decompress = false, .check_purged = true, }); - if (!err && !xe_vma_has_no_bo(vma)) - err = xe_bo_migrate(xe_vma_bo(vma), - region_to_mem_type[region], - NULL, - exec); + if (!err && !xe_vma_has_no_bo(vma)) { + struct xe_bo *bo = xe_vma_bo(vma); + u32 mem_type; + + if (region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) { + unsigned int i; + + mem_type = XE_PL_TT; + for (i = 0; i < bo->placement.num_placement; i++) { + if (mem_type_is_vram(bo->placements[i].mem_type)) { + mem_type = bo->placements[i].mem_type; + break; + } + } + } else { + mem_type = region_to_mem_type[region]; + } + + err = xe_bo_migrate(bo, mem_type, NULL, exec); + } break; } default: From b8ad916ba4e18e428693ffd89ae66ac9754b5558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Sat, 1 Aug 2026 12:59:12 -0400 Subject: [PATCH 3284/3297] drm/exec: Remove the index parameter from drm_exec_for_each_locked_obj[_reverse] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ce44b78512e9102aea54ff6b6e521d6c8de9f31c ] Nobody makes any use of it. Possible internal future users can instead use the _index variable. External users shouldn't use it since the array it's pointing into is internal drm_exec state. v2: - Use a unique id for the loop variable (Christian) Assisted-by: GitHub Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellström Reviewed-by: Christian König Link: https://patch.msgid.link/20260520101616.41284-2-thomas.hellstrom@linux.intel.com Stable-dep-of: af80e2bfde93 ("drm/xe: Wait on external BO kernel fences in exec IOCTL") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 9 +++------ .../drm/amd/amdgpu/amdgpu_eviction_fence.c | 3 +-- drivers/gpu/drm/drm_exec.c | 6 ++---- drivers/gpu/drm/drm_gpuvm.c | 3 +-- drivers/gpu/drm/xe/xe_vm.c | 3 +-- include/drm/drm_exec.h | 20 +++++++++++-------- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 8419473249031..752b34cffdc5f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -864,7 +864,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry *e; struct drm_gem_object *obj; - unsigned long index; unsigned int i; int r; @@ -969,7 +968,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, goto out_free_user_pages; } - drm_exec_for_each_locked_object(&p->exec, index, obj) { + drm_exec_for_each_locked_object(&p->exec, obj) { r = amdgpu_cs_bo_validate(p, gem_to_amdgpu_bo(obj)); if (unlikely(r)) goto out_free_user_pages; @@ -1196,7 +1195,6 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) struct drm_gpu_scheduler *sched; struct drm_gem_object *obj; struct dma_fence *fence; - unsigned long index; unsigned int i; int r; @@ -1207,7 +1205,7 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) return r; } - drm_exec_for_each_locked_object(&p->exec, index, obj) { + drm_exec_for_each_locked_object(&p->exec, obj) { struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); struct dma_resv *resv = bo->tbo.base.resv; @@ -1276,7 +1274,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry *e; struct drm_gem_object *gobj; - unsigned long index; unsigned int i; uint64_t seq; int r; @@ -1327,7 +1324,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, } p->fence = dma_fence_get(&leader->base.s_fence->finished); - drm_exec_for_each_locked_object(&p->exec, index, gobj) { + drm_exec_for_each_locked_object(&p->exec, gobj) { ttm_bo_move_to_lru_tail_unlocked(&gem_to_amdgpu_bo(gobj)->tbo); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c index 23d7d0b0d6252..83fad3a371847 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c @@ -49,7 +49,6 @@ amdgpu_eviction_fence_replace_fence(struct amdgpu_eviction_fence_mgr *evf_mgr, { struct amdgpu_eviction_fence *old_ef, *new_ef; struct drm_gem_object *obj; - unsigned long index; int ret; if (evf_mgr->ev_fence && @@ -77,7 +76,7 @@ amdgpu_eviction_fence_replace_fence(struct amdgpu_eviction_fence_mgr *evf_mgr, spin_unlock(&evf_mgr->ev_fence_lock); /* Attach the new fence */ - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object(exec, obj) { struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); if (!bo) diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index 8d06014001828..746210f3f6c28 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -24,7 +24,6 @@ * * struct drm_gem_object *obj; * struct drm_exec exec; - * unsigned long index; * int ret; * * drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); @@ -40,7 +39,7 @@ * goto error; * } * - * drm_exec_for_each_locked_object(&exec, index, obj) { + * drm_exec_for_each_locked_object(&exec, obj) { * dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ); * ... * } @@ -56,9 +55,8 @@ static void drm_exec_unlock_all(struct drm_exec *exec) { struct drm_gem_object *obj; - unsigned long index; - drm_exec_for_each_locked_object_reverse(exec, index, obj) { + drm_exec_for_each_locked_object_reverse(exec, obj) { dma_resv_unlock(obj->resv); drm_gem_object_put(obj); } diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index 5c8bf010cd965..61c3df0711a35 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -1521,9 +1521,8 @@ drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm, enum dma_resv_usage extobj_usage) { struct drm_gem_object *obj; - unsigned long index; - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object(exec, obj) { dma_resv_assert_held(obj->resv); dma_resv_add_fence(obj->resv, fence, drm_gpuvm_is_extobj(gpuvm, obj) ? diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 1befcfb2c51aa..423705bcd94c1 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -355,7 +355,6 @@ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec, unsigned int num_fences) { struct drm_gem_object *obj; - unsigned long index; int ret; do { @@ -368,7 +367,7 @@ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec, return ret; } while (!list_empty(&vm->gpuvm.evict.list)); - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object(exec, obj) { ret = dma_resv_reserve_fences(obj->resv, num_fences); if (ret) return ret; diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index aa786b828a0a4..dee6ebdbe416f 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -65,31 +65,35 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) return index < exec->num_objects ? exec->objects[index] : NULL; } +/* Helper for drm_exec_for_each_locked_object(). Internal use only. */ +#define __drm_exec_for_each_locked_object(exec, obj, __index) \ + for (unsigned long __index = 0; ((obj) = drm_exec_obj(exec, __index)); ++__index) /** * drm_exec_for_each_locked_object - iterate over all the locked objects * @exec: drm_exec object - * @index: unsigned long index for the iteration * @obj: the current GEM object * * Iterate over all the locked GEM objects inside the drm_exec object. */ -#define drm_exec_for_each_locked_object(exec, index, obj) \ - for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index)) +#define drm_exec_for_each_locked_object(exec, obj) \ + __drm_exec_for_each_locked_object(exec, obj, __UNIQUE_ID(drm_exec)) +/* Helper for drm_exec_for_each_locked_object_reverse(). Internal use only. */ +#define __drm_exec_for_each_locked_object_reverse(exec, obj, __index) \ + for (unsigned long __index = (exec)->num_objects - 1; \ + ((obj) = drm_exec_obj(exec, __index)); --__index) /** * drm_exec_for_each_locked_object_reverse - iterate over all the locked * objects in reverse locking order * @exec: drm_exec object - * @index: unsigned long index for the iteration * @obj: the current GEM object * * Iterate over all the locked GEM objects inside the drm_exec object in - * reverse locking order. Note that @index may go below zero and wrap, + * reverse locking order. Note that the internal index may wrap around, * but that will be caught by drm_exec_obj(), returning a NULL object. */ -#define drm_exec_for_each_locked_object_reverse(exec, index, obj) \ - for ((index) = (exec)->num_objects - 1; \ - ((obj) = drm_exec_obj(exec, index)); --(index)) +#define drm_exec_for_each_locked_object_reverse(exec, obj) \ + __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) /** * drm_exec_until_all_locked - loop until all GEM objects are locked From 21976fe5258494ac38b63d103be81bf1180ae4ae Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Sat, 1 Aug 2026 12:59:13 -0400 Subject: [PATCH 3285/3297] drm/xe: Wait on external BO kernel fences in exec IOCTL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit af80e2bfde9312c76b60cf9274248dce0410b30d ] Before arming a user job, xe_exec_ioctl() only added the VM's dma-resv KERNEL slot as a dependency. That slot covers rebinds and the kernel operations of the VM's private BOs, but not external BOs (bo->vm == NULL), which carry their kernel operations (evictions, moves, ...) in their own dma-resv KERNEL slot. The DMA_RESV_USAGE_KERNEL slot is the cross-driver contract for memory management operations that must complete before the BO or its backing store may be used: any accessor is required to wait on the KERNEL fences before touching the resv. By skipping the external BOs' KERNEL slots, the exec path violated that contract and could schedule a user job while a kernel operation on an external BO mapped by the VM was still in flight, racing against it and potentially reading or writing memory that was being moved. Replace the VM-only dependency with an iteration over every object locked by the exec, adding each object's KERNEL slot as a job dependency. This covers the VM resv (rebinds and private BOs) as well as every external BO, mirroring the drm_gpuvm_resv_add_fence() call that later publishes the job fence to the same set of objects. Long-running mode continues to skip this, as before. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Assisted-by: GitHub_Copilot:claude-opus-4.8 Signed-off-by: Matthew Brost Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260702215805.4011228-1-matthew.brost@intel.com (cherry picked from commit a6b842acf3ddd1efc53a56de9260cfa718fb35e7) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_exec.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c index 87a32b61bece6..c156cf4661f24 100644 --- a/drivers/gpu/drm/xe/xe_exec.c +++ b/drivers/gpu/drm/xe/xe_exec.c @@ -286,13 +286,23 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) goto err_exec; } - /* Wait behind rebinds */ + /* + * Wait behind rebinds and any kernel operations (evictions, defrag + * moves, ...) on the VM and all external BOs. The VM's private BOs + * carry their kernel ops in the VM dma-resv KERNEL slot, while each + * external BO carries them in its own dma-resv KERNEL slot; both are + * covered by iterating every object locked by the exec, mirroring the + * drm_gpuvm_resv_add_fence() below. + */ if (!xe_vm_in_lr_mode(vm)) { - err = xe_sched_job_add_deps(job, - xe_vm_resv(vm), - DMA_RESV_USAGE_KERNEL); - if (err) - goto err_put_job; + struct drm_gem_object *obj; + + drm_exec_for_each_locked_object(exec, obj) { + err = xe_sched_job_add_deps(job, obj->resv, + DMA_RESV_USAGE_KERNEL); + if (err) + goto err_put_job; + } } for (i = 0; i < num_syncs && !err; i++) From 894d4a7395662059d2c6009fd17e1598af06405e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sat, 1 Aug 2026 20:07:03 -0400 Subject: [PATCH 3286/3297] drm/i915/vrr: Check HAS_VRR() first in intel_vrr_is_capable() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4b274b0b61ab2a529e5c22e9aa033f3028e639fc ] There's no point in doing all the other checks in intel_vrr_is_capable() if the platform doesn't support VRR at all Check HAS_VRR() before wasting time on the other checks. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20251020185038.4272-23-ville.syrjala@linux.intel.com Reviewed-by: Ankit Nautiyal Stable-dep-of: f8a9262c7a6f ("drm/i915/vrr: require valid min/max vfreq for VRR") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_vrr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 04a2ea086fb2e..2d66319aeb8d4 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -22,6 +22,9 @@ bool intel_vrr_is_capable(struct intel_connector *connector) const struct drm_display_info *info = &connector->base.display_info; struct intel_dp *intel_dp; + if (!HAS_VRR(display)) + return false; + /* * DP Sink is capable of VRR video timings if * Ignore MSA bit is set in DPCD. @@ -46,8 +49,7 @@ bool intel_vrr_is_capable(struct intel_connector *connector) return false; } - return HAS_VRR(display) && - info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10; + return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10; } bool intel_vrr_is_in_range(struct intel_connector *connector, int vrefresh) From df1582c0a101e2e2f133dd331d2a3258bb6a7518 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Sat, 1 Aug 2026 20:07:04 -0400 Subject: [PATCH 3287/3297] drm/i915/vrr: require valid min/max vfreq for VRR [ Upstream commit f8a9262c7a6fc2de9802e14b0228114f0333869e ] Ensure the EDID provided min/max vfreq are valid. Most scenarios are already covered (by coincidence) through the checks in intel_vrr_is_capable() and intel_vrr_is_in_range(), but be more explicit about it. At worst, a zero min_vfreq could lead to a division by zero in intel_vrr_compute_vmax(). Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: 117cd09ba528 ("drm/i915/display/dp: Compute VRR state in atomic_check") Cc: stable@vger.kernel.org # v5.12+ Cc: Ankit Nautiyal Reviewed-by: Ankit Nautiyal Link: https://patch.msgid.link/20260625131040.1051272-1-jani.nikula@intel.com Signed-off-by: Jani Nikula (cherry picked from commit 1765cf59f517b02f3b0591fe5120930d08bddeb6) Signed-off-by: Joonas Lahtinen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_vrr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 2d66319aeb8d4..3ac6cd74ae15b 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -49,6 +49,10 @@ bool intel_vrr_is_capable(struct intel_connector *connector) return false; } + if (!info->monitor_range.min_vfreq || !info->monitor_range.max_vfreq || + info->monitor_range.min_vfreq > info->monitor_range.max_vfreq) + return false; + return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10; } From 184de3d31f72845edde39ef20cf3a48290248088 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Sat, 1 Aug 2026 21:49:12 -0400 Subject: [PATCH 3288/3297] drm/xe: Use SVM range helpers in PT layer [ Upstream commit 9ea9b45701ab50049a722450abc28346d1121e6e ] We have helpers SVM range start, end, and size. Use them in the PT layer rather than directly looking at the struct. Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://lore.kernel.org/r/20251022230122.922382-1-matthew.brost@intel.com Stable-dep-of: 6384271ac1ac ("drm/xe/pt: Reset current_op in xe_pt_update_ops_init()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_pt.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index de0d104fc8b66..845db2adf3b14 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -715,7 +715,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, .vm = vm, .tile = tile, .curs = &curs, - .va_curs_start = range ? range->base.itree.start : + .va_curs_start = range ? xe_svm_range_start(range) : xe_vma_start(vma), .vma = vma, .wupd.entries = entries, @@ -734,7 +734,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, } if (xe_svm_range_has_dma_mapping(range)) { xe_res_first_dma(range->base.pages.dma_addr, 0, - range->base.itree.last + 1 - range->base.itree.start, + xe_svm_range_size(range), &curs); xe_svm_range_debug(range, "BIND PREPARE - MIXED"); } else { @@ -778,8 +778,8 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, walk_pt: ret = xe_pt_walk_range(&pt->base, pt->level, - range ? range->base.itree.start : xe_vma_start(vma), - range ? range->base.itree.last + 1 : xe_vma_end(vma), + range ? xe_svm_range_start(range) : xe_vma_start(vma), + range ? xe_svm_range_end(range) : xe_vma_end(vma), &xe_walk.base); *num_entries = xe_walk.wupd.num_used_entries; @@ -983,8 +983,8 @@ bool xe_pt_zap_ptes_range(struct xe_tile *tile, struct xe_vm *vm, if (!(pt_mask & BIT(tile->id))) return false; - (void)xe_pt_walk_shared(&pt->base, pt->level, range->base.itree.start, - range->base.itree.last + 1, &xe_walk.base); + (void)xe_pt_walk_shared(&pt->base, pt->level, xe_svm_range_start(range), + xe_svm_range_end(range), &xe_walk.base); return xe_walk.needs_invalidate; } @@ -1711,8 +1711,8 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile, struct xe_svm_range *range, struct xe_vm_pgtable_update *entries) { - u64 start = range ? range->base.itree.start : xe_vma_start(vma); - u64 end = range ? range->base.itree.last + 1 : xe_vma_end(vma); + u64 start = range ? xe_svm_range_start(range) : xe_vma_start(vma); + u64 end = range ? xe_svm_range_end(range) : xe_vma_end(vma); struct xe_pt_stage_unbind_walk xe_walk = { .base = { .ops = &xe_pt_stage_unbind_ops, @@ -1922,7 +1922,7 @@ static int bind_range_prepare(struct xe_vm *vm, struct xe_tile *tile, vm_dbg(&xe_vma_vm(vma)->xe->drm, "Preparing bind, with range [%lx...%lx)\n", - range->base.itree.start, range->base.itree.last); + xe_svm_range_start(range), xe_svm_range_end(range) - 1); pt_op->vma = NULL; pt_op->bind = true; @@ -1937,8 +1937,8 @@ static int bind_range_prepare(struct xe_vm *vm, struct xe_tile *tile, pt_op->num_entries, true); xe_pt_update_ops_rfence_interval(pt_update_ops, - range->base.itree.start, - range->base.itree.last + 1); + xe_svm_range_start(range), + xe_svm_range_end(range)); ++pt_update_ops->current_op; pt_update_ops->needs_svm_lock = true; @@ -2033,7 +2033,7 @@ static int unbind_range_prepare(struct xe_vm *vm, vm_dbg(&vm->xe->drm, "Preparing unbind, with range [%lx...%lx)\n", - range->base.itree.start, range->base.itree.last); + xe_svm_range_start(range), xe_svm_range_end(range) - 1); pt_op->vma = XE_INVALID_VMA; pt_op->bind = false; @@ -2044,8 +2044,8 @@ static int unbind_range_prepare(struct xe_vm *vm, xe_vm_dbg_print_entries(tile_to_xe(tile), pt_op->entries, pt_op->num_entries, false); - xe_pt_update_ops_rfence_interval(pt_update_ops, range->base.itree.start, - range->base.itree.last + 1); + xe_pt_update_ops_rfence_interval(pt_update_ops, xe_svm_range_start(range), + xe_svm_range_end(range)); ++pt_update_ops->current_op; pt_update_ops->needs_svm_lock = true; pt_update_ops->needs_invalidation |= xe_vm_has_scratch(vm) || From 6107b64cfccef21c48922bbd2d057c5482b2d8bb Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Sat, 1 Aug 2026 21:49:13 -0400 Subject: [PATCH 3289/3297] drm/xe: Stub out new pagefault layer [ Upstream commit 620a09fb0bddf387f418663478b48ca4ba62b6d6 ] Stub out the new page fault layer and add kernel documentation. This is intended as a replacement for the GT page fault layer, enabling multiple producers to hook into a shared page fault consumer interface. v2: - Fix kernel doc typo (checkpatch) - Remove comment around GT (Stuart) - Add explaination around reclaim (Francois) - Add comment around u8 vs enum (Francois) - Include engine instance (Stuart) v3: - Fix XE_PAGEFAULT_TYPE_ATOMIC_ACCESS_VIOLATION kernel doc (Stuart) Signed-off-by: Matthew Brost Reviewed-by: Lucas De Marchi Tested-by: Francois Dugast Link: https://patch.msgid.link/20251031165416.2871503-2-matthew.brost@intel.com Stable-dep-of: 6384271ac1ac ("drm/xe/pt: Reset current_op in xe_pt_update_ops_init()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/Makefile | 1 + drivers/gpu/drm/xe/xe_pagefault.c | 65 +++++++++++ drivers/gpu/drm/xe/xe_pagefault.h | 19 ++++ drivers/gpu/drm/xe/xe_pagefault_types.h | 136 ++++++++++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 drivers/gpu/drm/xe/xe_pagefault.c create mode 100644 drivers/gpu/drm/xe/xe_pagefault.h create mode 100644 drivers/gpu/drm/xe/xe_pagefault_types.h diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index ee52b751623f0..c5fc8cc71149c 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -94,6 +94,7 @@ xe-y += xe_bb.o \ xe_nvm.o \ xe_oa.o \ xe_observation.o \ + xe_pagefault.o \ xe_pat.o \ xe_pci.o \ xe_pcode.o \ diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c new file mode 100644 index 0000000000000..d509a80cb1f31 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_pagefault.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2025 Intel Corporation + */ + +#include "xe_pagefault.h" +#include "xe_pagefault_types.h" + +/** + * DOC: Xe page faults + * + * Xe page faults are handled in two layers. The producer layer interacts with + * hardware or firmware to receive and parse faults into struct xe_pagefault, + * then forwards them to the consumer. The consumer layer services the faults + * (e.g., memory migration, page table updates) and acknowledges the result back + * to the producer, which then forwards the results to the hardware or firmware. + * The consumer uses a page fault queue sized to absorb all potential faults and + * a multi-threaded worker to process them. Multiple producers are supported, + * with a single shared consumer. + * + * xe_pagefault.c implements the consumer layer. + */ + +/** + * xe_pagefault_init() - Page fault init + * @xe: xe device instance + * + * Initialize Xe page fault state. Must be done after reading fuses. + * + * Return: 0 on Success, errno on failure + */ +int xe_pagefault_init(struct xe_device *xe) +{ + /* TODO - implement */ + return 0; +} + +/** + * xe_pagefault_reset() - Page fault reset for a GT + * @xe: xe device instance + * @gt: GT being reset + * + * Reset the Xe page fault state for a GT; that is, squash any pending faults on + * the GT. + */ +void xe_pagefault_reset(struct xe_device *xe, struct xe_gt *gt) +{ + /* TODO - implement */ +} + +/** + * xe_pagefault_handler() - Page fault handler + * @xe: xe device instance + * @pf: Page fault + * + * Sink the page fault to a queue (i.e., a memory buffer) and queue a worker to + * service it. Safe to be called from IRQ or process context. Reclaim safe. + * + * Return: 0 on success, errno on failure + */ +int xe_pagefault_handler(struct xe_device *xe, struct xe_pagefault *pf) +{ + /* TODO - implement */ + return 0; +} diff --git a/drivers/gpu/drm/xe/xe_pagefault.h b/drivers/gpu/drm/xe/xe_pagefault.h new file mode 100644 index 0000000000000..bd0cdf9ed37f1 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_pagefault.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _XE_PAGEFAULT_H_ +#define _XE_PAGEFAULT_H_ + +struct xe_device; +struct xe_gt; +struct xe_pagefault; + +int xe_pagefault_init(struct xe_device *xe); + +void xe_pagefault_reset(struct xe_device *xe, struct xe_gt *gt); + +int xe_pagefault_handler(struct xe_device *xe, struct xe_pagefault *pf); + +#endif diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h new file mode 100644 index 0000000000000..d3b516407d600 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2025 Intel Corporation + */ + +#ifndef _XE_PAGEFAULT_TYPES_H_ +#define _XE_PAGEFAULT_TYPES_H_ + +#include + +struct xe_gt; +struct xe_pagefault; + +/** enum xe_pagefault_access_type - Xe page fault access type */ +enum xe_pagefault_access_type { + /** @XE_PAGEFAULT_ACCESS_TYPE_READ: Read access type */ + XE_PAGEFAULT_ACCESS_TYPE_READ = 0, + /** @XE_PAGEFAULT_ACCESS_TYPE_WRITE: Write access type */ + XE_PAGEFAULT_ACCESS_TYPE_WRITE = 1, + /** @XE_PAGEFAULT_ACCESS_TYPE_ATOMIC: Atomic access type */ + XE_PAGEFAULT_ACCESS_TYPE_ATOMIC = 2, +}; + +/** enum xe_pagefault_type - Xe page fault type */ +enum xe_pagefault_type { + /** @XE_PAGEFAULT_TYPE_NOT_PRESENT: Not present */ + XE_PAGEFAULT_TYPE_NOT_PRESENT = 0, + /** @XE_PAGEFAULT_TYPE_WRITE_ACCESS_VIOLATION: Write access violation */ + XE_PAGEFAULT_TYPE_WRITE_ACCESS_VIOLATION = 1, + /** @XE_PAGEFAULT_TYPE_ATOMIC_ACCESS_VIOLATION: Atomic access violation */ + XE_PAGEFAULT_TYPE_ATOMIC_ACCESS_VIOLATION = 2, +}; + +/** struct xe_pagefault_ops - Xe pagefault ops (producer) */ +struct xe_pagefault_ops { + /** + * @ack_fault: Ack fault + * @pf: Page fault + * @err: Error state of fault + * + * Page fault producer receives acknowledgment from the consumer and + * sends the result to the HW/FW interface. + */ + void (*ack_fault)(struct xe_pagefault *pf, int err); +}; + +/** + * struct xe_pagefault - Xe page fault + * + * Generic page fault structure for communication between producer and consumer. + * Carefully sized to be 64 bytes. Upon a device page fault, the producer + * populates this structure, and the consumer copies it into the page-fault + * queue for deferred handling. + */ +struct xe_pagefault { + /** + * @gt: GT of fault + */ + struct xe_gt *gt; + /** + * @consumer: State for the software handling the fault. Populated by + * the producer and may be modified by the consumer to communicate + * information back to the producer upon fault acknowledgment. + */ + struct { + /** @consumer.page_addr: address of page fault */ + u64 page_addr; + /** @consumer.asid: address space ID */ + u32 asid; + /** + * @consumer.access_type: access type, u8 rather than enum to + * keep size compact + */ + u8 access_type; + /** + * @consumer.fault_type: fault type, u8 rather than enum to + * keep size compact + */ + u8 fault_type; +#define XE_PAGEFAULT_LEVEL_NACK 0xff /* Producer indicates nack fault */ + /** @consumer.fault_level: fault level */ + u8 fault_level; + /** @consumer.engine_class: engine class */ + u8 engine_class; + /** @consumer.engine_instance: engine instance */ + u8 engine_instance; + /** consumer.reserved: reserved bits for future expansion */ + u8 reserved[7]; + } consumer; + /** + * @producer: State for the producer (i.e., HW/FW interface). Populated + * by the producer and should not be modified—or even inspected—by the + * consumer, except for calling operations. + */ + struct { + /** @producer.private: private pointer */ + void *private; + /** @producer.ops: operations */ + const struct xe_pagefault_ops *ops; +#define XE_PAGEFAULT_PRODUCER_MSG_LEN_DW 4 + /** + * @producer.msg: page fault message, used by producer in fault + * acknowledgment to formulate response to HW/FW interface. + * Included in the page-fault message because the producer + * typically receives the fault in a context where memory cannot + * be allocated (e.g., atomic context or the reclaim path). + */ + u32 msg[XE_PAGEFAULT_PRODUCER_MSG_LEN_DW]; + } producer; +}; + +/** + * struct xe_pagefault_queue: Xe pagefault queue (consumer) + * + * Used to capture all device page faults for deferred processing. Size this + * queue to absorb the device’s worst-case number of outstanding faults. + */ +struct xe_pagefault_queue { + /** + * @data: Data in queue containing struct xe_pagefault, protected by + * @lock + */ + void *data; + /** @size: Size of queue in bytes */ + u32 size; + /** @head: Head pointer in bytes, moved by producer, protected by @lock */ + u32 head; + /** @tail: Tail pointer in bytes, moved by consumer, protected by @lock */ + u32 tail; + /** @lock: protects page fault queue */ + spinlock_t lock; + /** @worker: to process page faults */ + struct work_struct worker; +}; + +#endif From b1a71151317c4682342bcaf543fc85d03cddcd9f Mon Sep 17 00:00:00 2001 From: Oak Zeng Date: Sat, 1 Aug 2026 21:49:14 -0400 Subject: [PATCH 3290/3297] drm/xe: Add page reclamation info to device info [ Upstream commit 9b1a0e0a15c97987fdf56a615f3d13995bafd042 ] Starting from Xe3p, HW adds a feature assisting range based page reclamation. Introduce a bit in device info to indicate whether device has such capability. Signed-off-by: Oak Zeng Signed-off-by: Brian Nguyen Reviewed-by: Shuicheng Lin Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20251212213225.3564537-15-brian3.nguyen@intel.com Stable-dep-of: 6384271ac1ac ("drm/xe/pt: Reset current_op in xe_pt_update_ops_init()") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_device_types.h | 2 ++ drivers/gpu/drm/xe/xe_pci.c | 1 + drivers/gpu/drm/xe/xe_pci_types.h | 1 + 3 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index 0e80f2940c996..2ed03fd074ff5 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -305,6 +305,8 @@ struct xe_device { * pcode mailbox commands. */ u8 has_mbx_power_limits:1; + /** @info.has_page_reclaim_hw_assist: Device supports page reclamation feature */ + u8 has_page_reclaim_hw_assist:1; /** @info.has_pxp: Device has PXP support */ u8 has_pxp:1; /** @info.has_range_tlb_inval: Has range based TLB invalidations */ diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 89cc6d32f041b..b787292117901 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -591,6 +591,7 @@ static int xe_info_init_early(struct xe_device *xe, xe->info.has_heci_cscfi = desc->has_heci_cscfi; xe->info.has_late_bind = desc->has_late_bind; xe->info.has_llc = desc->has_llc; + xe->info.has_page_reclaim_hw_assist = desc->has_page_reclaim_hw_assist; xe->info.has_pxp = desc->has_pxp; xe->info.has_sriov = desc->has_sriov; xe->info.skip_guc_pc = desc->skip_guc_pc; diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h index 9b9766a3baa34..a7b6576725edf 100644 --- a/drivers/gpu/drm/xe/xe_pci_types.h +++ b/drivers/gpu/drm/xe/xe_pci_types.h @@ -42,6 +42,7 @@ struct xe_device_desc { u8 has_late_bind:1; u8 has_llc:1; u8 has_mbx_power_limits:1; + u8 has_page_reclaim_hw_assist:1; u8 has_pxp:1; u8 has_sriov:1; u8 needs_scratch:1; From 157b1e3384d7d37f59c0c2b2ff2af8f557db1daa Mon Sep 17 00:00:00 2001 From: Zongyao Bai Date: Sat, 1 Aug 2026 21:49:15 -0400 Subject: [PATCH 3291/3297] drm/xe/pt: Reset current_op in xe_pt_update_ops_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6384271ac1ac0099198d15df79212a19ebdb929d ] xe_pt_update_ops_init() fails to reset current_op to 0. On the vm_bind path, ops_execute() calls xe_pt_update_ops_prepare() inside the xe_validation_guard() / drm_exec_until_all_locked() loop. When that loop retries due to lock contention or OOM eviction (drm_exec_retry_on_contention() / xe_validation_retry_on_oom()), xe_pt_update_ops_prepare() runs again on the same vops, and each call to bind_op_prepare() increments current_op without resetting it. After N retries current_op exceeds the array size allocated by xe_vma_ops_alloc(), causing an out-of-bounds write into SLUB-poisoned memory and a subsequent UAF crash in xe_migrate_update_pgtables_cpu() when reading the corrupted pt_op->bind. Also reset needs_svm_lock and needs_invalidation which are derived in the same prepare pass and would otherwise cause wrong migrate ops selection and redundant TLB invalidation on retry. Fix this by resetting current_op, needs_svm_lock and needs_invalidation in xe_pt_update_ops_init(). v2 (Matt): - Add details in commit message. - Add Fixes tag and Cc to stable@vger.kernel.org Fixes: e8babb280b5e ("drm/xe: Convert multiple bind ops into single job") Suggested-by: Matthew Auld Cc: stable@vger.kernel.org Assisted-by: GitHub-Copilot:claude-sonnet-4.6 Signed-off-by: Zongyao Bai Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260714232433.2737533-1-zongyao.bai@intel.com (cherry picked from commit 046045543e530605c441063535e7dca0075369a6) Signed-off-by: Thomas Hellström Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/xe/xe_pt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 845db2adf3b14..1d22af2145bf9 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -2153,8 +2153,11 @@ static void xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops) { init_llist_head(&pt_update_ops->deferred); + pt_update_ops->current_op = 0; pt_update_ops->start = ~0x0ull; pt_update_ops->last = 0x0ull; + pt_update_ops->needs_svm_lock = false; + pt_update_ops->needs_invalidation = false; } /** From 0902f06a6c0bbc1e4a08899d327eedc3fda4261d Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 7 Aug 2026 09:52:10 +0200 Subject: [PATCH 3292/3297] can: use skb hash instead of private variable in headroom commit d4fb6514ff8ed6912a71294e6b66a5d59ee88007 upstream. The can_skb_priv::skbcnt variable is used to identify CAN skbs in the RX path analogue to the skb->hash. As the skb hash is not filled in CAN skbs move the private skbcnt value to skb->hash and set skb->sw_hash accordingly. The skb->hash is a value used for RPS to identify skbs. Use it as intended. Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260201-can_skb_ext-v8-1-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni Signed-off-by: Oliver Hartkopp Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/dev/skb.c | 2 -- include/linux/can/core.h | 1 + include/linux/can/skb.h | 2 -- net/can/af_can.c | 14 +++++++++++--- net/can/bcm.c | 2 -- net/can/isotp.c | 3 --- net/can/j1939/socket.c | 1 - net/can/j1939/transport.c | 2 -- net/can/raw.c | 7 +++---- 9 files changed, 15 insertions(+), 19 deletions(-) diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index 3ebd4f779b9bd..0da615afa04de 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -202,7 +202,6 @@ static void init_can_skb_reserve(struct sk_buff *skb) skb_reset_transport_header(skb); can_skb_reserve(skb); - can_skb_prv(skb)->skbcnt = 0; } struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) @@ -312,7 +311,6 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb) if (skb->ip_summed == CHECKSUM_NONE) { /* init headroom */ can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb->ip_summed = CHECKSUM_UNNECESSARY; diff --git a/include/linux/can/core.h b/include/linux/can/core.h index 5fb8d0e3f9c1d..3287232e3cad9 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -58,6 +58,7 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev, void *data); extern int can_send(struct sk_buff *skb, int loop); +void can_set_skb_uid(struct sk_buff *skb); void can_sock_destruct(struct sock *sk); #endif /* !_CAN_CORE_H */ diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index 1abc25a8d144e..869ea574a40a1 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -49,13 +49,11 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb); /** * struct can_skb_priv - private additional data inside CAN sk_buffs * @ifindex: ifindex of the first interface the CAN frame appeared on - * @skbcnt: atomic counter to have an unique id together with skb pointer * @frame_len: length of CAN frame in data link layer * @cf: align to the following CAN frame at skb->data */ struct can_skb_priv { int ifindex; - int skbcnt; unsigned int frame_len; struct can_frame cf[]; }; diff --git a/net/can/af_can.c b/net/can/af_can.c index a624c04ed5c63..7a026f3a70a0e 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -641,6 +641,16 @@ static int can_rcv_filter(struct can_dev_rcv_lists *dev_rcv_lists, struct sk_buf return matches; } +void can_set_skb_uid(struct sk_buff *skb) +{ + /* create non-zero unique skb identifier together with *skb */ + while (!(skb->hash)) + skb->hash = atomic_inc_return(&skbcounter); + + skb->sw_hash = 1; +} +EXPORT_SYMBOL(can_set_skb_uid); + static void can_receive(struct sk_buff *skb, struct net_device *dev) { struct can_dev_rcv_lists *dev_rcv_lists; @@ -652,9 +662,7 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) atomic_long_inc(&pkg_stats->rx_frames); atomic_long_inc(&pkg_stats->rx_frames_delta); - /* create non-zero unique skb identifier together with *skb */ - while (!(can_skb_prv(skb)->skbcnt)) - can_skb_prv(skb)->skbcnt = atomic_inc_return(&skbcounter); + can_set_skb_uid(skb); rcu_read_lock(); diff --git a/net/can/bcm.c b/net/can/bcm.c index 76bc7f7274253..a819872fddeff 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -350,7 +350,6 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb_put_data(skb, cf, op->cfsiz); @@ -1621,7 +1620,6 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, } can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb->dev = dev; can_skb_set_owner(skb, sk); err = can_send(skb, 1); /* send with loopback */ diff --git a/net/can/isotp.c b/net/can/isotp.c index 95e14631a1eb9..86bfad36383fa 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -233,7 +233,6 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) can_skb_reserve(nskb); can_skb_prv(nskb)->ifindex = dev->ifindex; - can_skb_prv(nskb)->skbcnt = 0; nskb->dev = dev; can_skb_set_owner(nskb, sk); @@ -800,7 +799,6 @@ static void isotp_send_cframe(struct isotp_sock *so) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; cf = (struct canfd_frame *)skb->data; skb_put_zero(skb, so->ll.mtu); @@ -1106,7 +1104,6 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; so->tx.len = size; so->tx.idx = 0; diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index e3ba2e9fc0e9b..e56d8134938e8 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -897,7 +897,6 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, can_skb_reserve(skb); can_skb_prv(skb)->ifindex = ndev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb_reserve(skb, offsetof(struct can_frame, data)); ret = memcpy_from_msg(skb_put(skb, size), msg, size); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 72f6c06e1e5b0..9d9c342d26d58 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -613,7 +613,6 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; - can_skb_prv(skb)->skbcnt = 0; /* reserve CAN header */ skb_reserve(skb, offsetof(struct can_frame, data)); @@ -1549,7 +1548,6 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skcb = j1939_skb_to_cb(skb); memcpy(skcb, rel_skcb, sizeof(*skcb)); diff --git a/net/can/raw.c b/net/can/raw.c index 365559d19dc32..c397071bfa8b9 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -76,7 +76,7 @@ MODULE_ALIAS("can-proto-1"); struct uniqframe { const struct sk_buff *skb; - int skbcnt; + u32 hash; unsigned int join_rx_count; }; @@ -164,7 +164,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data) /* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr(ro->uniq)->skb == oskb && - this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) { + this_cpu_ptr(ro->uniq)->hash == oskb->hash) { if (!ro->join_filters) return; @@ -174,7 +174,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data) return; } else { this_cpu_ptr(ro->uniq)->skb = oskb; - this_cpu_ptr(ro->uniq)->skbcnt = can_skb_prv(oskb)->skbcnt; + this_cpu_ptr(ro->uniq)->hash = oskb->hash; this_cpu_ptr(ro->uniq)->join_rx_count = 1; /* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count > 1) @@ -954,7 +954,6 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; /* fill the skb before testing for valid CAN frames */ err = memcpy_from_msg(skb_put(skb, size), msg, size); From 10be509fa8fd95d1e47d40d1f68b0b26dfe9d572 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 7 Aug 2026 09:52:11 +0200 Subject: [PATCH 3293/3297] can: isotp: fix timer drain order, wakeup handling and tx_gen ordering commit 050f010f920da17c1044a4f174766ad553e770b6 upstream. This patch is a follow-up to commit cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") which addresses following sashiko-bot findings: - isotp_sendmsg(): drain so->txfrtimer first so a stale callback can't re-arm echotimer after the claim - isotp_release(): wake so->wait after forcing ISOTP_SHUTDOWN so a sleeping sendmsg() claim isn't stranded - isotp_sendmsg(): have both wait_event_interruptible() calls in isotp_sendmsg() also wake on ISOTP_SHUTDOWN and do not return claim to IDLE to avoid corrupting a concurrent isotp_release() process. - isotp_sendmsg(): handle potential claim of a new transfer when the wait_event_interruptible() call returns in CAN_ISOTP_WAIT_TX_DONE mode. Don't touch timers and states of the new transfer if a new thread incremented so->tx_gen before getting the lock at err_event_drop. - isotp_sendmsg(): handle a stuck can_send() and omit timer and state changes if a new transfer was claimed. wait_tx_done() returns the error recorded in so->tx_result[], tagged with the caller's own generation. - isotp_tx_timeout(): on a claimed timeout, record the ECOMM error for the timed-out transfer's own generation in so->tx_result[]; sk->sk_err is raised unconditionally, same as every other error path here. - isotp_tx_gen_done()/isotp_tx_timeout(): always read tx.state (acquire) before tx_gen - the reverse order let a weakly ordered CPU pair a fresh tx.state with a stale tx_gen/tx_result slot. - isotp_sendmsg(): wait_tx_done: drain sk_err via sock_error() once we have read the result from so->tx_result[], so an already-reported error doesn't stay latched for a later poll()/SO_ERROR. Also align the remaining lock-free so->tx.state/rx.state/cfecho accesses and use skb->hash as unique loopback echo frame indicator. Fixes: cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260724181525.43556-1-socketcan@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Greg Kroah-Hartman --- net/can/isotp.c | 317 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 230 insertions(+), 87 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 86bfad36383fa..720b911fe1ede 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -126,6 +126,15 @@ MODULE_PARM_DESC(max_pdu_size, "maximum isotp pdu size (default " #define ISOTP_FC_TIMEOUT 1 /* 1 sec */ #define ISOTP_ECHO_TIMEOUT 2 /* 2 secs */ +/* so->tx_result[so->tx_gen % ISOTP_TX_RESULT_SLOTS] holds the packed value + * (err << ISOTP_TX_RESULT_GEN_BITS | gen) for each tx generation slot, so it + * can be handled with a single READ_ONCE()/WRITE_ONCE() access. + */ +#define ISOTP_TX_RESULT_SLOTS 4 +#define ISOTP_TX_RESULT_GEN_BITS 24 +#define ISOTP_TX_RESULT_GEN_MASK ((1U << ISOTP_TX_RESULT_GEN_BITS) - 1) +#define ISOTP_TX_RESULT_ERR_MASK 0xFF + enum { ISOTP_IDLE = 0, ISOTP_WAIT_FIRST_FC, @@ -165,7 +174,8 @@ struct isotp_sock { u32 force_tx_stmin; u32 force_rx_stmin; u32 cfecho; /* consecutive frame echo tag */ - u32 tx_gen; /* generation, bumped per new tx transfer */ + u32 tx_gen; /* transfer generation, increased per new tx transfer */ + u32 tx_result[ISOTP_TX_RESULT_SLOTS]; /* per-generation result slots */ struct tpcon rx, tx; struct list_head notifier; wait_queue_head_t wait; @@ -176,6 +186,65 @@ static LIST_HEAD(isotp_notifier_list); static DEFINE_SPINLOCK(isotp_notifier_lock); static struct isotp_sock *isotp_busy_notifier; +/* increase (24 bit) tx generation value */ +static u32 isotp_inc_tx_gen(u32 gen) +{ + return (gen + 1) & ISOTP_TX_RESULT_GEN_MASK; +} + +/* store 8 bit error and 24 bit tx generation values in packed u32 element */ +static u32 isotp_pack_tx_result(u32 gen, int err) +{ + return gen | ((u32)err << ISOTP_TX_RESULT_GEN_BITS); +} + +/* get the 24 bit tx generation value from the tx result */ +static u32 isotp_get_tx_gen(u32 gen_err) +{ + return gen_err & ISOTP_TX_RESULT_GEN_MASK; +} + +/* get the 8 bit error value from the tx result */ +static u32 isotp_get_tx_err(u32 gen_err) +{ + return (gen_err >> ISOTP_TX_RESULT_GEN_BITS) & ISOTP_TX_RESULT_ERR_MASK; +} + +/* store transfer result in per-generation%4 so->tx_result[] slot */ +static void isotp_set_tx_result(struct isotp_sock *so, u32 gen, int err) +{ + WRITE_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS], + isotp_pack_tx_result(gen, err)); +} + +/* fetch the result recorded for 'gen', as a (negative) errno (0 for success) */ +static int isotp_get_tx_result(struct isotp_sock *so, u32 gen) +{ + u32 result = READ_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS]); + + if (isotp_get_tx_gen(result) != gen) { + pr_notice_once("can-isotp: tx_result[] slot reused before read\n"); + + /* report failure rather than risk a false success */ + return -ECOMM; + } + + return -(isotp_get_tx_err(result)); +} + +/* true if done, shut down or superseded ('gen' is no longer the active + * transfer). Reads tx.state first (acquire) so tx_gen/tx_result reads + * below see at least what that state write published (common sequence). + */ +static bool isotp_tx_gen_done(struct isotp_sock *so, u32 gen) +{ + /* read tx.state first for the common sequence */ + u32 state = smp_load_acquire(&so->tx.state); + + return state == ISOTP_IDLE || state == ISOTP_SHUTDOWN || + READ_ONCE(so->tx_gen) != gen; +} + static inline struct isotp_sock *isotp_sk(const struct sock *sk) { return (struct isotp_sock *)sk; @@ -198,7 +267,7 @@ static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer) rxtimer); struct sock *sk = &so->sk; - if (so->rx.state == ISOTP_WAIT_DATA) { + if (READ_ONCE(so->rx.state) == ISOTP_WAIT_DATA) { /* we did not get new data frames in time */ /* report 'connection timed out' */ @@ -207,7 +276,7 @@ static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer) sk_error_report(sk); /* reset rx state */ - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); } return HRTIMER_NORESTART; @@ -365,20 +434,19 @@ static void isotp_send_cframe(struct isotp_sock *so); static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) { struct sock *sk = &so->sk; + int tx_err = EBADMSG; /* default for unknown FC status */ - if (so->tx.state != ISOTP_WAIT_FC && - so->tx.state != ISOTP_WAIT_FIRST_FC) + if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC && + READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC) return 0; hrtimer_cancel(&so->txtimer); /* isotp_tx_timeout() may have given up on this job while - * hrtimer_cancel() above waited for it to finish; so->rx_lock - * (held by our caller isotp_rcv()) rules out a concurrent claim, - * so a plain recheck is enough here. + * hrtimer_cancel() above waited for it to finish => recheck */ - if (so->tx.state != ISOTP_WAIT_FC && - so->tx.state != ISOTP_WAIT_FIRST_FC) + if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC && + READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC) return 1; if ((cf->len < ae + FC_CONTENT_SZ) || @@ -389,13 +457,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) if (!sock_flag(sk, SOCK_DEAD)) sk_error_report(sk); - so->tx.state = ISOTP_IDLE; + isotp_set_tx_result(so, so->tx_gen, EBADMSG); + /* set to IDLE after publishing tx_result */ + smp_store_release(&so->tx.state, ISOTP_IDLE); wake_up_interruptible(&so->wait); return 1; } /* get static/dynamic communication params from first/every FC frame */ - if (so->tx.state == ISOTP_WAIT_FIRST_FC || + if (READ_ONCE(so->tx.state) == ISOTP_WAIT_FIRST_FC || so->opt.flags & CAN_ISOTP_DYN_FC_PARMS) { so->txfc.bs = cf->data[ae + 1]; so->txfc.stmin = cf->data[ae + 2]; @@ -419,13 +489,13 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) so->tx_gap = ktime_add_ns(so->tx_gap, (so->txfc.stmin - 0xF0) * 100000); - so->tx.state = ISOTP_WAIT_FC; + WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC); } switch (cf->data[ae] & 0x0F) { case ISOTP_FC_CTS: so->tx.bs = 0; - so->tx.state = ISOTP_SENDING; + WRITE_ONCE(so->tx.state, ISOTP_SENDING); /* send CF frame and enable echo timeout handling */ hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); @@ -440,14 +510,19 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) case ISOTP_FC_OVFLW: /* overflow on receiver side - report 'message too long' */ - sk->sk_err = EMSGSIZE; - if (!sock_flag(sk, SOCK_DEAD)) - sk_error_report(sk); + tx_err = EMSGSIZE; fallthrough; default: - /* stop this tx job */ - so->tx.state = ISOTP_IDLE; + /* reserved/unknown flow status (tx_err defaults to EBADMSG) */ + + sk->sk_err = tx_err; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); + + isotp_set_tx_result(so, so->tx_gen, tx_err); + /* set to IDLE after publishing tx_result */ + smp_store_release(&so->tx.state, ISOTP_IDLE); wake_up_interruptible(&so->wait); } return 0; @@ -460,7 +535,7 @@ static int isotp_rcv_sf(struct sock *sk, struct canfd_frame *cf, int pcilen, struct sk_buff *nskb; hrtimer_cancel(&so->rxtimer); - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); if (!len || len > cf->len - pcilen) return 1; @@ -494,7 +569,7 @@ static int isotp_rcv_ff(struct sock *sk, struct canfd_frame *cf, int ae) int ff_pci_sz; hrtimer_cancel(&so->rxtimer); - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); /* get the used sender LL_DL from the (first) CAN frame data length */ so->rx.ll_dl = padlen(cf->len); @@ -548,7 +623,7 @@ static int isotp_rcv_ff(struct sock *sk, struct canfd_frame *cf, int ae) /* initial setup for this pdu reception */ so->rx.sn = 1; - so->rx.state = ISOTP_WAIT_DATA; + WRITE_ONCE(so->rx.state, ISOTP_WAIT_DATA); /* no creation of flow control frames */ if (so->opt.flags & CAN_ISOTP_LISTEN_MODE) @@ -566,7 +641,7 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, struct sk_buff *nskb; int i; - if (so->rx.state != ISOTP_WAIT_DATA) + if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA) return 0; /* drop if timestamp gap is less than force_rx_stmin nano secs */ @@ -581,11 +656,9 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, hrtimer_cancel(&so->rxtimer); /* isotp_rx_timer_handler() may have raced us for so->rx.state - * while hrtimer_cancel() above waited for it to finish, already - * reporting ETIMEDOUT and resetting the reception; don't process - * this CF into a reassembly that has already been given up on. + * while hrtimer_cancel() above waited for it to finish => recheck */ - if (so->rx.state != ISOTP_WAIT_DATA) + if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA) return 1; /* CFs are never longer than the FF */ @@ -606,7 +679,7 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, sk_error_report(sk); /* reset rx state */ - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); return 1; } so->rx.sn++; @@ -620,7 +693,7 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, if (so->rx.idx >= so->rx.len) { /* we are done */ - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); if ((so->opt.flags & ISOTP_CHECK_PADDING) && check_pad(so, cf, i + 1, so->opt.rxpad_content)) { @@ -691,8 +764,10 @@ static void isotp_rcv(struct sk_buff *skb, void *data) if (so->opt.flags & CAN_ISOTP_HALF_DUPLEX) { /* check rx/tx path half duplex expectations */ - if ((so->tx.state != ISOTP_IDLE && n_pci_type != N_PCI_FC) || - (so->rx.state != ISOTP_IDLE && n_pci_type == N_PCI_FC)) + if ((READ_ONCE(so->tx.state) != ISOTP_IDLE && + n_pci_type != N_PCI_FC) || + (READ_ONCE(so->rx.state) != ISOTP_IDLE && + n_pci_type == N_PCI_FC)) goto out_unlock; } @@ -786,6 +861,7 @@ static void isotp_send_cframe(struct isotp_sock *so) struct canfd_frame *cf; int can_send_ret; int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; + u32 old_cfecho; dev = dev_get_by_index(sock_net(sk), so->ifindex); if (!dev) @@ -800,6 +876,9 @@ static void isotp_send_cframe(struct isotp_sock *so) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; + /* set uid in tx skb to identify CF echo frames */ + can_set_skb_uid(skb); + cf = (struct canfd_frame *)skb->data; skb_put_zero(skb, so->ll.mtu); @@ -816,12 +895,15 @@ static void isotp_send_cframe(struct isotp_sock *so) skb->dev = dev; can_skb_set_owner(skb, sk); - /* cfecho should have been zero'ed by init/isotp_rcv_echo() */ - if (so->cfecho) - pr_notice_once("can-isotp: cfecho is %08X != 0\n", so->cfecho); + /* zero'ed by init/isotp_rcv_echo(); reached lock-free via + * isotp_txfr_timer_handler() too, so use READ_ONCE()/WRITE_ONCE() + */ + old_cfecho = READ_ONCE(so->cfecho); + if (old_cfecho) + pr_notice_once("can-isotp: cfecho is %08X != 0\n", old_cfecho); /* set consecutive frame echo tag */ - so->cfecho = *(u32 *)cf->data; + WRITE_ONCE(so->cfecho, skb->hash); /* send frame with local echo enabled */ can_send_ret = can_send(skb, 1); @@ -873,7 +955,6 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) { struct sock *sk = (struct sock *)data; struct isotp_sock *so = isotp_sk(sk); - struct canfd_frame *cf = (struct canfd_frame *)skb->data; /* only handle my own local echo CF/SF skb's (no FF!) */ if (skb->sk != sk) @@ -885,32 +966,35 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) spin_lock(&so->rx_lock); /* so->cfecho may since belong to a new transfer; recheck under lock */ - if (so->cfecho != *(u32 *)cf->data) + if (READ_ONCE(so->cfecho) != skb->hash) goto out_unlock; /* cancel local echo timeout */ hrtimer_cancel(&so->echotimer); /* local echo skb with consecutive frame has been consumed */ - so->cfecho = 0; + WRITE_ONCE(so->cfecho, 0); /* claiming a transfer also takes so->rx_lock, so a plain recheck * is enough: so->tx.state can't have flipped to ISOTP_SENDING for * a new claim while we're still in here */ - if (so->tx.state != ISOTP_SENDING) + if (READ_ONCE(so->tx.state) != ISOTP_SENDING) goto out_unlock; if (so->tx.idx >= so->tx.len) { /* we are done */ - so->tx.state = ISOTP_IDLE; + + isotp_set_tx_result(so, so->tx_gen, 0); + /* set to IDLE after publishing tx_result */ + smp_store_release(&so->tx.state, ISOTP_IDLE); wake_up_interruptible(&so->wait); goto out_unlock; } if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { /* stop and wait for FC with timeout */ - so->tx.state = ISOTP_WAIT_FC; + WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC); hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); goto out_unlock; @@ -932,16 +1016,20 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) spin_unlock(&so->rx_lock); } -/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get - * cancelled under so->rx_lock elsewhere, so this must stay lock-free to - * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting - * a new transfer with an error from the one that just timed out. +/* isotp_tx_timeout: we did not get any flow control or echo frame in time + * + * Shared by so->txtimer's and so->echotimer's callbacks. Both timers get + * cancelled under so->rx_lock elsewhere, so this must stay lock-free. + * + * tx.state is acquired before tx_gen. Common sequence in isotp_tx_gen_done(). + * cmpxchg() only orders itself, not the two preceding loads. */ static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) { struct sock *sk = &so->sk; + /* read tx.state first for the common sequence */ + u32 old_state = smp_load_acquire(&so->tx.state); u32 gen = READ_ONCE(so->tx_gen); - u32 old_state = READ_ONCE(so->tx.state); /* don't handle timeouts in IDLE or SHUTDOWN state */ if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN) @@ -951,14 +1039,14 @@ static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) return HRTIMER_NORESTART; - /* we did not get any flow control or echo frame in time */ + /* detected timeout: report 'communication error on send' */ - if (READ_ONCE(so->tx_gen) == gen) { - /* report 'communication error on send' */ - sk->sk_err = ECOMM; - if (!sock_flag(sk, SOCK_DEAD)) - sk_error_report(sk); - } + /* a stale read of this slot by a waiter still falls back to ECOMM */ + isotp_set_tx_result(so, gen, ECOMM); + + sk->sk_err = ECOMM; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); wake_up_interruptible(&so->wait); @@ -993,7 +1081,7 @@ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer) HRTIMER_MODE_REL_SOFT); /* cfecho should be consumed by isotp_rcv_echo() here */ - if (so->tx.state == ISOTP_SENDING && !so->cfecho) + if (READ_ONCE(so->tx.state) == ISOTP_SENDING && !READ_ONCE(so->cfecho)) isotp_send_cframe(so); return HRTIMER_NORESTART; @@ -1011,10 +1099,12 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT; struct hrtimer *tx_hrt = &so->echotimer; u32 new_state = ISOTP_SENDING; + u32 my_gen; + u32 old_cfecho; int off; int err; - if (!so->bound || so->tx.state == ISOTP_SHUTDOWN) + if (!so->bound || READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) return -EADDRNOTAVAIL; /* claim the socket under so->rx_lock: this serializes the claim @@ -1031,29 +1121,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if (msg->msg_flags & MSG_DONTWAIT) return -EAGAIN; - if (so->tx.state == ISOTP_SHUTDOWN) + if (READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) return -EADDRNOTAVAIL; /* wait for complete transmission of current pdu */ err = wait_event_interruptible(so->wait, - so->tx.state == ISOTP_IDLE); + READ_ONCE(so->tx.state) == ISOTP_IDLE || + READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN); if (err) return err; } - /* new transfer: bump so->tx_gen and drain the old one's timers, - * still under the so->rx_lock we just claimed the socket with - */ - WRITE_ONCE(so->tx.state, ISOTP_SENDING); - WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1); + /* txfrtimer's callback re-arms echotimer lock-free: drain it first */ + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->echotimer); - hrtimer_cancel(&so->txfrtimer); - so->cfecho = 0; + + /* new transfer: increment so->tx_gen and set tx.state after barrier */ + my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen)); + isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot matching */ + WRITE_ONCE(so->tx_gen, my_gen); + smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */ + WRITE_ONCE(so->tx.state, ISOTP_SENDING); + WRITE_ONCE(so->cfecho, 0); spin_unlock_bh(&so->rx_lock); /* so->bound is only checked once above - a wakeup may have - * unbound/rebound the socket meanwhile, so re-validate it + * unbound/rebound the socket meanwhile => recheck */ if (!so->bound) { err = -EADDRNOTAVAIL; @@ -1105,6 +1199,9 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; + /* set uid in tx skb to identify CF echo frames */ + can_set_skb_uid(skb); + so->tx.len = size; so->tx.idx = 0; @@ -1112,8 +1209,9 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) skb_put_zero(skb, so->ll.mtu); /* cfecho should have been zero'ed by init / former isotp_rcv_echo() */ - if (so->cfecho) - pr_notice_once("can-isotp: uninit cfecho %08X\n", so->cfecho); + old_cfecho = READ_ONCE(so->cfecho); + if (old_cfecho) + pr_notice_once("can-isotp: uninit cfecho %08X\n", old_cfecho); /* check for single frame transmission depending on TX_DL */ if (size <= so->tx.ll_dl - SF_PCI_SZ4 - ae - off) { @@ -1141,7 +1239,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) cf->data[ae] |= size; /* set CF echo tag for isotp_rcv_echo() (SF-mode) */ - so->cfecho = *(u32 *)cf->data; + WRITE_ONCE(so->cfecho, skb->hash); } else { /* send first frame */ @@ -1158,7 +1256,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->txfc.bs = 0; /* set CF echo tag for isotp_rcv_echo() (CF-mode) */ - so->cfecho = *(u32 *)cf->data; + WRITE_ONCE(so->cfecho, skb->hash); } else { /* standard flow control check */ new_state = ISOTP_WAIT_FIRST_FC; @@ -1168,12 +1266,12 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) tx_hrt = &so->txtimer; /* no CF echo tag for isotp_rcv_echo() (FF-mode) */ - so->cfecho = 0; + WRITE_ONCE(so->cfecho, 0); } } spin_lock_bh(&so->rx_lock); - if (so->tx.state == ISOTP_SHUTDOWN) { + if (READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) { /* isotp_release() has since taken over and already drained * our timers - don't send into a socket that's going away */ @@ -1184,7 +1282,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return -EADDRNOTAVAIL; } /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ - so->tx.state = new_state; + WRITE_ONCE(so->tx.state, new_state); hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0), HRTIMER_MODE_REL_SOFT); spin_unlock_bh(&so->rx_lock); @@ -1201,20 +1299,49 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) __func__, ERR_PTR(err)); spin_lock_bh(&so->rx_lock); + + /* new transfer already claimed by a concurrent completion, + * timeout or sendmsg() while we were stuck in can_send()? + */ + if (READ_ONCE(so->tx_gen) != my_gen) { + /* don't touch timers and state of the new transfer */ + spin_unlock_bh(&so->rx_lock); + return err; + } + /* no transmission -> no timeout monitoring */ hrtimer_cancel(tx_hrt); goto err_out_drop_locked; } if (wait_tx_done) { - /* wait for complete transmission of current pdu */ - err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); + /* wake up for: + * - concurrent sendmsg() claiming a new transfer + * - complete transmission of current PDU + * - shutdown state change in isotp_release() + * isotp_tx_gen_done() uses common tx.state/tx_gen read sequence + */ + err = wait_event_interruptible(so->wait, + isotp_tx_gen_done(so, my_gen)); if (err) goto err_event_drop; - err = sock_error(sk); - if (err) - return err; + /* still our claim, but isotp_release() force-shut it down */ + if (smp_load_acquire(&so->tx.state) == ISOTP_SHUTDOWN && + READ_ONCE(so->tx_gen) == my_gen) { + err = -EADDRNOTAVAIL; + goto err_event_drop; + } + + /* own completion, or tx_gen moved on - either way this is + * what isotp_get_tx_result() recorded for my_gen + */ + err = isotp_get_tx_result(so, my_gen); + + /* drain to avoid stale error for a later poll()/SO_ERROR */ + sock_error(sk); + + return err ? err : size; } return size; @@ -1224,15 +1351,26 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) spin_lock_bh(&so->rx_lock); goto err_out_drop_locked; err_event_drop: - /* interrupted waiting on our own transfer - drain its timers */ + /* interrupted or shut down while waiting on our own transfer */ spin_lock_bh(&so->rx_lock); + + /* new transfer already started by concurrent sendmsg()? */ + if (READ_ONCE(so->tx_gen) != my_gen) { + /* don't touch timers and states of the new transfer */ + spin_unlock_bh(&so->rx_lock); + return err; + } + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->echotimer); err_out_drop_locked: /* release the claim; so->rx_lock still held from above */ - so->cfecho = 0; - so->tx.state = ISOTP_IDLE; + WRITE_ONCE(so->cfecho, 0); + + /* only claim to IDLE if isotp_release() has not taken over */ + if (READ_ONCE(so->tx.state) != ISOTP_SHUTDOWN) + WRITE_ONCE(so->tx.state, ISOTP_IDLE); spin_unlock_bh(&so->rx_lock); wake_up_interruptible(&so->wait); @@ -1298,8 +1436,9 @@ static int isotp_release(struct socket *sock) /* best-effort: wait for a running pdu to finish, but don't block on * it forever - give up after the first signal */ - while (so->tx.state != ISOTP_IDLE && - wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0) + while (READ_ONCE(so->tx.state) != ISOTP_IDLE && + wait_event_interruptible(so->wait, + READ_ONCE(so->tx.state) == ISOTP_IDLE) == 0) ; /* claim the socket under so->rx_lock like sendmsg() does, so its @@ -1307,9 +1446,12 @@ static int isotp_release(struct socket *sock) * unconditionally, even when a signal cut the wait above short */ spin_lock_bh(&so->rx_lock); - so->tx.state = ISOTP_SHUTDOWN; + WRITE_ONCE(so->tx.state, ISOTP_SHUTDOWN); spin_unlock_bh(&so->rx_lock); - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); + + /* forced SHUTDOWN may have skipped IDLE (gave up on a signal) */ + wake_up_interruptible(&so->wait); spin_lock(&isotp_notifier_lock); while (isotp_busy_notifier == so) { @@ -1425,7 +1567,8 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) * with so->bound in the same lock_sock() section above, so there is * no window in which a concurrent isotp_notify() could be missed. */ - if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) { + if (READ_ONCE(so->tx.state) != ISOTP_IDLE || + READ_ONCE(so->rx.state) != ISOTP_IDLE) { err = -EAGAIN; goto out; } @@ -1459,7 +1602,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr *uaddr, int len) isotp_rcv, sk, "isotp", sk); /* no consecutive frame echo skb in flight */ - so->cfecho = 0; + WRITE_ONCE(so->cfecho, 0); /* register for echo skb's */ can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), @@ -1825,7 +1968,7 @@ static __poll_t isotp_poll(struct file *file, struct socket *sock, poll_table *w poll_wait(file, &so->wait, wait); /* Check for false positives due to TX state */ - if ((mask & EPOLLWRNORM) && (so->tx.state != ISOTP_IDLE)) + if ((mask & EPOLLWRNORM) && (READ_ONCE(so->tx.state) != ISOTP_IDLE)) mask &= ~(EPOLLOUT | EPOLLWRNORM); return mask; From 7bc7af179916bd4964179d03fdd41e4aa46b7143 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Fri, 17 Jul 2026 10:46:14 +0000 Subject: [PATCH 3294/3297] usb: typec: ucsi: Correct teardown ordering in ucsi_init() error path commit fb0bf289f5d529336ef490c8273e88a8a8b29f69 upstream. The commit 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration") consolidated port teardown into the ucsi_unregister_port() helper. However, it introduced an ordering problem in the ucsi_init() error path. Fix this by ensuring ucsi_unregister_port() is called before we unregister their corresponding lockdep keys. Cc: stable@vger.kernel.org Fixes: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration") Reported-by: "Borah, Chaitanya Kumar" Closes: https://lore.kernel.org/all/22064276-6c56-411a-9f20-6917ceeb865f@intel.com/ Signed-off-by: Andrei Kuchynski Tested-by: Chaitanya Kumar Borah Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260717104614.325250-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 043a00a4f1c27..0d1062191951f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -2050,11 +2050,11 @@ static int ucsi_init(struct ucsi *ucsi) return 0; err_unregister: + for (con = connector; con->port; con++) + ucsi_unregister_port(con); for (i = 0; i < ucsi->cap.num_connectors; i++) lockdep_unregister_key(&connector[i].lock_key); - for (con = connector; con->port; con++) - ucsi_unregister_port(con); kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); From e7731507270c2c63f00cfbf210fe96b964e9b42e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 3 Apr 2026 13:53:54 -0700 Subject: [PATCH 3295/3297] drm/fb-helper: Fix a locking bug in an error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bd64240dc88caaf7b96dd869f36f165f51b52039 upstream. The name of the function __drm_fb_helper_initial_config_and_unlock() and also the comment above that function make it clear that all code paths in this function should unlock fb_helper->lock before returning. Add a mutex_unlock() call in the only code path where it is missing. This has been detected by the Clang thread-safety analyzer. Cc: Thomas Zimmermann Cc: Christian König # radeon Cc: Dmitry Baryshkov # msm Cc: Javier Martinez Canillas Fixes: 63c971af4036 ("drm/fb-helper: Allocate and release fb_info in single place") Signed-off-by: Bart Van Assche Signed-off-by: Thomas Zimmermann Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260403205355.1181984-1-bvanassche@acm.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_fb_helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 18a00de2ec1fd..df73433b46527 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1792,8 +1792,10 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper) drm_client_modeset_probe(&fb_helper->client, width, height); info = drm_fb_helper_alloc_info(fb_helper); - if (IS_ERR(info)) + if (IS_ERR(info)) { + mutex_unlock(&fb_helper->lock); return PTR_ERR(info); + } ret = drm_fb_helper_single_fb_probe(fb_helper); if (ret < 0) { From 358b5dcf1fd7f7e627d8d6fa3e6690a4b20562c7 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 Apr 2026 09:29:05 +0200 Subject: [PATCH 3296/3297] drm/tegra: fbdev: Do not assign to struct drm_fb_helper.info commit d23bd83f3e47a928e783c0d6a004737519dc77dc upstream. That field already contains the value being assigned. No need to do this twice. Signed-off-by: Thomas Zimmermann Fixes: 63c971af4036 ("drm/fb-helper: Allocate and release fb_info in single place") Cc: linux-tegra@vger.kernel.org Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-2-tzimmermann@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/tegra/fbdev.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index d4ef5d5e42641..50d3186241c6a 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -111,7 +111,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, helper->funcs = &tegra_fbdev_helper_funcs; helper->fb = fb; - helper->info = info; info->fbops = &tegra_fb_ops; From 1efe5d048a391de3ead2804b2e7f86376c356cc5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 9 Aug 2026 20:25:30 +0200 Subject: [PATCH 3297/3297] Linux 6.18.44 Link: https://lore.kernel.org/r/20260807143424.272339768@linuxfoundation.org Tested-by: Pavel Machek (CIP) Tested-by: Wentao Guan Tested-by: Shuah Khan Tested-by: Brett A C Sheffield Tested-by: Peter Schneider Tested-by: Ron Economos Tested-by: Miguel Ojeda 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 5976ab6f0128e..93ae28a0b88b2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 43 +SUBLEVEL = 44 EXTRAVERSION = NAME = Baby Opossum Posse